【问题标题】:PHP NumberFormatter and Currency, can't set precisionPHP NumberFormatter 和 Currency,无法设置精度
【发布时间】:2012-09-02 14:04:14
【问题描述】:

在将NumberFormatter PHP 类(来自Intl extension)与货币一起使用时,我想将精度设置为0。但是我得到了一些奇怪的结果。这里:

$numberFormatter = new NumberFormatter('en-US', NumberFormatter::CURRENCY);
$numberFormatter->setAttribute(NumberFormatter::FRACTION_DIGITS, 0);

echo $numberFormatter->formatCurrency('45', 'USD');

它输出$45,这是我想要的。但是,如果我使用相同的设置将货币更改为 EUR

echo $numberFormatter->formatCurrency('45', 'EUR');

它输出€45.00(尽管我明确设置为零精度)。

更奇怪的是,如果我将语言环境设置为fr-FR,它会按预期输出数字:

$numberFormatter = new NumberFormatter('fr-FR', NumberFormatter::CURRENCY);
$numberFormatter->setAttribute(NumberFormatter::FRACTION_DIGITS, 0);

echo $numberFormatter->formatCurrency('45', 'EUR');

它输出45 €

这是一个错误吗?

【问题讨论】:

  • 你试过用其他货币吗?还是仅EUR 出现问题?
  • 某些货币会发生这种情况(例如,我们有几个单元测试在俄罗斯货币上失败)。但这也取决于区域设置,这就是为什么它真的很奇怪。
  • Michael,什么操作系统和php版本?
  • 在 Windows 7 上出现同样的问题,PHP 5.3.12:en-US + USD -> $45,fr-FR + EUR -> 45 €,en-US + EUR -> €45.00跨度>
  • 你是怎么解决这个问题的?

标签: php intl numberformatter


【解决方案1】:

我认为您必须使用支持目标货币的语言环境。所以 en_US 没有欧元。 de_DE 有它,fr_FR 也有。所以fr_FR支持欧元也就不足为奇了。

看来这不是bug。

【讨论】:

  • 嗯...这对我来说也是一个错误。这意味着如果语言环境设置为“fr_FR”,我无法格式化美元货币,因为我们这里没有美元?
  • 它被确认为libicu 中的一个错误。请参阅@LGT 的回答。
【解决方案2】:

您必须提供正确的区域设置和货币组合。例如,fr-FR / fr-bh / fr-ch support €,这是 formatCurrency 函数中必须提供的。

【讨论】:

    【解决方案3】:

    此函数使用区域信息将数字格式化为货币, 我使用的格式是 '%#10n' for $ :

    /**
     * That it is an implementation of the function money_format for the
     * platforms that do not it bear.
     * The function accepts to same string of format accepts for the
     * original function of the PHP.
     * The function is tested using PHP 5.1.4 in Windows XP
     * and Apache WebServer.
     * 
     * format I am are using is '%#10n' for $;
     * 
    */
    public static function money_format( $format, $number )
    {
        $regex  = '/%((?:[\^!\-]|\+|\(|\=.)*)([0-9]+)?'.
                '(?:#([0-9]+))?(?:\.([0-9]+))?([in%])/';
        if (setlocale(LC_MONETARY, 0) == 'C') {
            setlocale(LC_MONETARY, '');
        }
        $locale = localeconv();
        preg_match_all($regex, $format, $matches, PREG_SET_ORDER);
        foreach ($matches as $fmatch) {
            $value = floatval($number);
            $flags = array(
                    'fillchar'  => preg_match('/\=(.)/', $fmatch[1], $match) ?
                    $match[1] : ' ',
                    'nogroup'   => preg_match('/\^/', $fmatch[1]) > 0,
                    'usesignal' => preg_match('/\+|\(/', $fmatch[1], $match) ?
                    $match[0] : '+',
                    'nosimbol'  => preg_match('/\!/', $fmatch[1]) > 0,
                    'isleft'    => preg_match('/\-/', $fmatch[1]) > 0
            );
            $width      = trim($fmatch[2]) ? (int)$fmatch[2] : 0;
            $left       = trim($fmatch[3]) ? (int)$fmatch[3] : 0;
            $right      = trim($fmatch[4]) ? (int)$fmatch[4] : $locale['int_frac_digits'];
            $conversion = $fmatch[5];
    
            $positive = true;
            if ($value < 0) {
                $positive = false;
                $value  *= -1;
            }
            $letter = $positive ? 'p' : 'n';
    
            $prefix = $suffix = $cprefix = $csuffix = $signal = '';
    
            $signal = $positive ? $locale['positive_sign'] : $locale['negative_sign'];
            switch (true) {
                case $locale["{$letter}_sign_posn"] == 1 && $flags['usesignal'] == '+':
                    $prefix = $signal;
                    break;
                case $locale["{$letter}_sign_posn"] == 2 && $flags['usesignal'] == '+':
                    $suffix = $signal;
                    break;
                case $locale["{$letter}_sign_posn"] == 3 && $flags['usesignal'] == '+':
                    $cprefix = $signal;
                    break;
                case $locale["{$letter}_sign_posn"] == 4 && $flags['usesignal'] == '+':
                    $csuffix = $signal;
                    break;
                case $flags['usesignal'] == '(':
                case $locale["{$letter}_sign_posn"] == 0:
                    $prefix = '(';
                    $suffix = ')';
                    break;
            }
            if (!$flags['nosimbol']) {
                $currency = $cprefix .
                ($conversion == 'i' ? $locale['int_curr_symbol'] : $locale['currency_symbol']) .
                $csuffix;
            } else {
                $currency = '';
            }
            $space  = $locale["{$letter}_sep_by_space"] ? ' ' : '';
    
            $value = number_format($value, $right, $locale['mon_decimal_point'],
                    $flags['nogroup'] ? '' : $locale['mon_thousands_sep']);
            $value = @explode($locale['mon_decimal_point'], $value);
    
            $n = strlen($prefix) + strlen($currency) + strlen($value[0]);
            if ($left > 0 && $left > $n) {
                $value[0] = str_repeat($flags['fillchar'], $left - $n) . $value[0];
            }
            $value = implode($locale['mon_decimal_point'], $value);
            if ($locale["{$letter}_cs_precedes"]) {
                $value = $prefix . $currency . $space . $value . $suffix;
            } else {
                $value = $prefix . $value . $space . $currency . $suffix;
            }
            if ($width > 0) {
                $value = str_pad($value, $width, $flags['fillchar'], $flags['isleft'] ?
                        STR_PAD_RIGHT : STR_PAD_LEFT);
            }
    
            $format = str_replace($fmatch[0], $value, $format);
        }
        return $format;
    }
    

    【讨论】:

      【解决方案4】:

      在你可以使用的php ini中设置默认的locale值:

      ini_set('intl.default_locale', 'de-DE');

      要更改数字格式,请使用:

      setlocale(LC_MONETARY, 'de-DE');

      【讨论】:

        【解决方案5】:

        您可以使用以下代码行格式化货币:-

        $number = 1234.56;
        setlocale(LC_MONETARY,"en_US"); // Sets the Default for money
        echo money_format("%i", $number);
        

        它会输出你:

        USD 1,234.56
        

        【讨论】:

        • 这不是回答问题。
        【解决方案6】:

        Michael Gallego 忘记更新此问题。查看他的错误报告和 php.net 上的回复。

        [2012-10-05 08:21 UTC] jpauli@email.com

        我确认这是 4.4.x 分支中的 ICU 错误。
        考虑升级 libicu,4.8.x 给出正确结果

        【讨论】:

        • 4.8.1 的 ICU 仍然存在这个问题
        猜你喜欢
        • 2017-01-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-13
        • 1970-01-01
        • 2018-11-19
        相关资源
        最近更新 更多