登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  php教程

PHP Intl NumberFormatter 的本地化数字输出

来源:17golang原创

时间:2026-10-28 11:07:06 351浏览 收藏

如果同一个金额或统计数字要面向不同地区展示,直接拼接千分位和小数点很快就会失控。PHP 的 Intl\NumberFormatter 把 locale、数字样式和精度规则放进一个格式器:同一个数值可以按 en_US、de_DE 等地区输出不同的分隔符;货币和百分比也能使用对应的展示语义。

官方手册:https://www.php.net/manual/en/class.numberformatter.php

这篇文章把范围收敛到“本地化数字输出”:先说明 Intl 的变化点,再处理旧代码的默认精度、货币调用和错误返回,最后给一个可复用的输出函数。格式化只负责展示,不能替代金额和统计数据的原始存储策略。

先把 locale 从服务器环境里拿出来

Intl 是 PHP 对 ICU 的封装,NumberFormatter 是其中负责本地化数字显示的类。它是 locale-sensitive 的对象,因此不要让一个隐含的系统 locale 决定所有用户看到的结果;在请求层先确定用户或业务目标 locale,再创建对应的格式器。

format(1234567.89);
if ($display === false) {
    // 格式化失败时先停止拼接,避免把 false 当成正常文本输出。
    throw new RuntimeException($formatter->getErrorMessage());
}

echo $display;
?>

NumberFormatter 的构造参数是 locale、style 和可选 pattern。DECIMAL 适合普通数字;locale 只决定怎样显示,不会把业务数值本身转换成另一种存储格式。

普通数字、货币和百分比要选对 style

之前常见的写法是统一使用 number_format(),然后在业务代码里手工拼货币符号或百分号。迁移到 Intl 后,应该让 style 表达展示目的:普通数字调用 format(),货币调用 formatCurrency(),百分比则以小数比例交给 PERCENT 格式器。

format(1234567.89), PHP_EOL;

// CURRENCY 使用三字母 ISO 4217 代码表达要显示的货币。
$money = new NumberFormatter('zh_CN', NumberFormatter::CURRENCY);
echo $money->formatCurrency(1234567.89, 'CNY'), PHP_EOL;

// PERCENT 把 0.875 这样的比例按百分比语义显示。
$percent = new NumberFormatter('en_US', NumberFormatter::PERCENT);
echo $percent->format(0.875), PHP_EOL;
?>

货币 locale 和货币代码是两个不同维度:前者影响分组符号、货币位置等展示规则,后者明确金额属于哪一种货币。NumberFormatter 负责格式化,不负责汇率换算;不要因为换了 USD 或 CNY 就认为金额已经发生兑换。

PHP NumberFormatter 的 locale、数字样式和输出文本之间的静态关系图
图1:locale、DECIMAL/CURRENCY/PERCENT 样式与输出文本的关系;这是原创静态说明图,不是截图或运行证据。

默认小数位不是业务精度

数字显示“看起来少了几位”,通常不是 locale 丢失数据,而是格式器的默认小数位和舍入规则在起作用。需要固定展示位数时,用 setAttribute() 设置 MIN_FRACTION_DIGITS、MAX_FRACTION_DIGITS 或 FRACTION_DIGITS;需要截断而不是四舍五入时,再设置 ROUNDING_MODE。

setAttribute(NumberFormatter::MIN_FRACTION_DIGITS, 2);
$formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 2);

// 默认会按格式器规则舍入;需要截断时显式选择 ROUND_DOWN。
$formatter->setAttribute(NumberFormatter::ROUNDING_MODE, NumberFormatter::ROUND_DOWN);

echo $formatter->format(3.789), PHP_EOL;
?>

这里的两位小数只是展示契约,不应被当成数据精度。金额、计量值或统计结果仍应按业务要求保存原始值;如果业务需要精确的金额运算,应先解决数值存储和计算,再把最终结果交给 NumberFormatter 显示。

把失败处理和格式器职责收进一个函数

format() 和 formatCurrency() 在失败时返回 false。不要只判断输出是否为空,也不要把错误值直接交给字符串拼接;可以把 locale、style、货币代码和错误信息集中到一个小函数,调用方只接收成功文本或明确异常。

format($value)
        : $formatter->formatCurrency((float) $value, $currency);

    if ($result === false) {
        // 同时保留错误码和消息,方便记录配置或 ICU 数据问题。
        throw new RuntimeException(sprintf(
            'NumberFormatter failed (%d): %s',
            $formatter->getErrorCode(),
            $formatter->getErrorMessage()
        ));
    }

    return $result;
}

echo localizedAmount(1234.5, 'fr_FR'), PHP_EOL;
echo localizedAmount(1234.5, 'zh_CN', NumberFormatter::CURRENCY, 'CNY');
?>

这个封装没有把 locale 写死,也没有把货币符号拼在字符串外面。要注意的是,输入是 float 时仍然受浮点表示影响;函数解决的是本地化展示和失败处理,不是高精度金额计算。

PHP NumberFormatter 的精度属性、舍入模式和错误返回关系静态结构图
图2:精度属性与格式化结果、错误诊断之间的职责边界;这是原创静态结构图,不是截图或运行证据。

发布前用四项检查固定结果

  • 扩展:确认运行环境启用了 intl,否则类和常量不可用。
  • locale:确认 locale 来自明确的用户偏好或业务配置,不要依赖服务器默认值。
  • 样式:普通数字、货币和百分比分别选择 DECIMAL、CURRENCY、PERCENT。
  • 精度与失败:显式设置小数位和舍入规则,并检查返回值是否为 false;失败时记录 getErrorCode() 与 getErrorMessage()。

因此,PHP Intl NumberFormatter 的最小迁移路径是:先确定 locale,再选择 style,按展示契约设置精度,最后把失败返回变成可诊断的异常或错误。这样本地化只影响输出层,原始数值、货币语义和业务计算仍保持清晰边界。

相关问题

为什么同一个数字在不同地区显示不同?

因为 NumberFormatter 按 locale 使用当地的分组符号、小数符号和相关展示规则;这是显示差异,不是数值被改写。

货币代码能不能直接写中文名称?

推荐传入三字母 ISO 4217 代码,例如 CNY 或 USD,不要把展示名称当成货币代码。

为什么设置了两位小数还要区分最小和最大位数?

最小位数控制不足位数时是否补零,最大位数控制最多保留多少位;两者一起设置才能形成稳定的展示宽度。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>