localeconv() 返回當前 Locale 設置下的數字、貨幣格式化參數陣列,是構建多語言、多貨幣電商、財務系統的基礎工具。本文涵蓋返回值結構、關鍵字段含義、setlocale 聯動、跨平台差異、實戰格式化封裝。
localeconv(): array
返回關聯數組,包含以下核心鍵(部分平台可能缺失部分鍵):
| 鍵名 | 含義 | 示例 |
|---|---|---|
decimal_point | 小數點字符 | '.' (US) / ',' (DE) |
thousands_sep | 千位分隔符 | ',' (US) / '.' (DE) / ' ' (FR) |
int_curr_symbol | 國際貨幣符號 (ISO 4217) | 'USD ' / 'EUR ' / 'CNY ' |
currency_symbol | 本地貨幣符號 | '$' / '€' / '¥' |
mon_decimal_point | 貨幣小數點 | '.' / ',' |
mon_thousands_sep | 貨幣千位分隔符 | ',' / '.' / '' |
positive_sign | 正數符號 | '' / '+' |
negative_sign | 負數符號 | '-' / '()' |
int_frac_digits | 國際標準小數位數 | 2 (USD) / 0 (JPY) / 3 (BHD) |
frac_digits | 本地習慣小數位數 | 2 / 0 |
p_cs_precedes | 正數貨幣符號前置(1)/後置(0) | 1 ($1.00) / 0 (1,00 €) |
p_sep_by_space | 正數符號與數值空格分隔(1/0) | 1 (€ 1,00) / 0 (€1,00) |
n_cs_precedes | 負數貨幣符號前置(1)/後置(0) | 1 (-$1.00) / 0 (1,00- €) |
n_sep_by_space | 負數符號與數值空格分隔(1/0) | 1 / 0 |
p_sign_posn | 正數符號位置編碼 0-4 | 見下表 |
n_sign_posn | 負數符號位置編碼 0-4 | 見下表 |
grouping | 數字分組陣列 | [3, 3] 表示 1,000,000 |
mon_grouping | 貨幣分組陣列 | [3, 0] 表示 1,00,00 (印度制) |
p_sign_posn / n_sign_posn 編碼含義:
| 值 | 格式模式 |
|---|---|
| 0 | 括號包裹:(1.00) / ($1.00) |
| 1 | 符號前置:+1.00 / +$1.00 |
| 2 | 符號後置:1.00+ / $1.00+ |
| 3 | 緊貼貨幣符號前:+$1.00 |
| 4 | 緊貼貨幣符號後:$1.00+ |
class MoneyFormatter
{
private array $localeInfo;
public function __construct(string $locale = 'en_US.UTF-8')
{
setlocale(LC_MONETARY, $locale);
$this->localeInfo = localeconv();
}
public function format(float $amount, string $currency = null): string
{
$info = $this->localeInfo;
$symbol = $currency ? $currency : $info['currency_symbol'];
$decimals = $info['frac_digits'];
$point = $info['mon_decimal_point'];
$sep = $info['mon_thousands_sep'];
$grouping = $info['mon_grouping'];
// 1. 絕對值格式化
$abs = abs($amount);
$formatted = number_format($abs, $decimals, $point, $sep);
// 2. 符號位置
$isNegative = $amount < 0;
$precedes = $isNegative ? $info['n_cs_precedes'] : $info['p_cs_precedes'];
$space = $isNegative ? $info['n_sep_by_space'] : $info['p_sep_by_space'];
$signPosn = $isNegative ? $info['n_sign_posn'] : $info['p_sign_posn'];
$sign = $isNegative ? $info['negative_sign'] : $info['positive_sign'];
// 3. 組裝(簡化版,僅處理常見 0/1/3 模式)
$spacer = $space ? ' ' : '';
if ($precedes) {
// $1.00 / $ 1.00
$result = $symbol . $spacer . $formatted;
} else {
// 1.00$ / 1.00 $
$result = $formatted . $spacer . $symbol;
}
// 4. 正負號(簡化:前置/後置)
if ($signPosn === 1) { // 符號前置
$result = $sign . $result;
} elseif ($signPosn === 2) { // 符號後置
$result = $result . $sign;
} elseif ($signPosn === 0) { // 括號
$result = '(' . $result . ')';
}
// 3/4 需要區分貨幣符號與數值,此處省略
return $result;
}
}
// 使用
$us = new MoneyFormatter('en_US.UTF-8');
echo $us->format(1234.56); // $1,234.56
echo $us->format(-99.9); // -$99.90
$de = new MoneyFormatter('de_DE.UTF-8');
echo $de->format(1234.56); // 1.234,56 €
echo $de->format(-99.9); // -99,90 €
| 平台 | Locale 名稱格式 | 安裝檢查 |
|---|---|---|
| Linux (glibc) | en_US.UTF-8, zh_CN.UTF-8 | locale -a 列出已安裝 |
| Windows | English_United States.65001, Chinese_China.936 | 控制面板 → 區域 → 管理 → 更改系統區域設置 |
| Alpine (musl) | 需安裝 musl-locales 包 | locale -a |
最佳實踐:Docker 基礎鏡像預裝所需 Locale,代碼中使用標準 POSIX 名稱(
en_US.UTF-8),避免 Windows/ Linux 不一致。
| 坑點 | 現象 | 解決 |
|---|---|---|
| setlocale 未生效 | localeconv() 返回 C locale (點號小數、無貨幣符號) | 確認系統安裝該 Locale;PHP 線程安全版需在進程啟動前設置,運行時 setlocale 可能不生效 |
| Windows Locale 名稱不同 | Linux 代碼遷移到 Windows 報錯 | 維護平台映射表:['en_US.UTF-8' => 'English_United States.65001'] |
| 貨幣符號編碼亂碼 | € 顯示為 | 確保 PHP 文件 UTF-8 無 BOM,輸出頁面 <meta charset="UTF-8"> |
| grouping 陣列解析錯誤 | 印度制 [3,2] 解析為 1,00,000 | 遞歸解析:最後一組重複,[3,2] 表示 3,2,2,2... 位分組 |
$fmt = new NumberFormatter('zh_CN', NumberFormatter::CURRENCY);
echo $fmt->formatCurrency(1234.56, 'CNY'); // ¥1,234.56
echo $fmt->formatCurrency(1234.56, 'USD'); // US$1,234.56
// 獲取原始模式
echo $fmt->getPattern(); // ¤#,##0.00
NumberFormatter 基於 ICU 庫,數據更權威、更新更及時localeconv() 是 PHP 獲取本地化格式參數的底層接口,理解其返回陣列結構、setlocale 聯動機制、跨平台差異,能手寫兼容性極強的貨幣格式化器。但現代項目應優先使用 NumberFormatter (intl),享受 ICU 庫的權威數據與豐富 API。無論選擇哪種方案,Locale 數據必須隨系統/容器更新,確保貨幣符號、小數規則與實時標準同步。