简体   繁体   English

PHP NumberFormatter和Currency,无法设置精度

[英]PHP NumberFormatter and Currency, can't set precision

I want to set a precision of 0 when using the NumberFormatter PHP class (from Intl extension ) with currency. 我想在使用带货币的NumberFormatter PHP类(来自Intl扩展名 )时设置精度为0。 However I've got some strange result. 但是我得到了一些奇怪的结果。 Here: 这里:

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

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

It outputs $45 , which is what I want. 它输出$45 ,这是我想要的。 However, if I change the currency to EUR with the same settings: 但是,如果我使用相同的设置将货币更改为EUR

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

It outputs €45.00 (although I explicitly set to have a precision of zero). 它输出€45.00 (虽然我明确设置精度为零)。

Even more strange, if I set the locale to fr-FR , it outputs the number as expected: 更奇怪的是,如果我将语言环境设置为fr-FR ,它会按预期输出数字:

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

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

It outputs 45 € . 它输出45 €

Is this a bug? 这是一个错误吗?

Michael Gallego forgot to update this issue. Michael Gallego忘了更新这个问题 Check out his bug report and the replies at php.net. 查看他的错误报告和php.net上的回复。

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

I confirm this is an ICU bug in 4.4.x branch. 我确认这是4.4.x分支中的ICU错误。
Consider upgrading libicu, 4.8.x gives correct result 考虑升级libicu,4.8.x给出正确的结果

You must provide a locale AND currency combination that is correct. 您必须提供正确的区域设置和货币组合。 For example, fr-FR / fr-bh / fr-ch support €, which is what must be provided in the formatCurrency function. 例如,fr-FR / fr-bh / fr-ch支持€,这是formatCurrency函数中必须提供的。

This function uses locale information to format number into currency, format I am are using is '%#10n' for $ : 此函数使用区域设置信息将数字格式化为货币,我正在使用的格式为'%#10n'为$:

/**
 * 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;
}

I think you have to use a locale which supports the destination currency. 我认为你必须使用支持目标货币的区域设置。 So en_US don't have the Euro. 所以en_US没有欧元。 de_DE have it and fr_FR also. de_DE也有它和fr_FR。 So it is not strange that fr_FR supports Euro. 所以fr_FR支持欧元并不奇怪。

It seems that this is not a bug. 看来这不是一个bug。

Set the default locale value in the php ini you can use: 在您可以使用的php ini中设置默认语言环境值:

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

To change the number format use: 要更改数字格式,请使用:

setlocale(LC_MONETARY, 'de-DE'); setlocale(LC_MONETARY,'de-DE');

You can us the following line of code for formatting the money:- 您可以使用以下代码行格式化资金: -

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

It will output you: 它会输出你:

USD 1,234.56

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM