PHP NumberFormatter и Currency, нельзя установить точность

Я хочу установить точность 0 при использовании класса PHP NumberFormatter (из Intl extension) с валютой. Однако у меня получился какой-то странный результат. Здесь:

$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 €.

Это ошибка?


person Michael Gallego    schedule 09.09.2012    source источник
comment
Вы пробовали это с другими валютами? Или проблема возникает только с EUR?   -  person Tchoupi    schedule 09.09.2012
comment
Это случается с некоторыми валютами (например, у нас есть пара юнит-тестов, которые не дали результатов с российской валютой). Но это тоже зависит от установленной локали, поэтому это действительно странно.   -  person Michael Gallego    schedule 09.09.2012
comment
Майкл, какая операционная система и какая версия php?   -  person taco    schedule 17.09.2012
comment
Та же проблема здесь в Windows 7, PHP 5.3.12: en-US + USD - ›45 $, fr-FR + EUR -› 45 €, en-US + EUR - ›45,00 €   -  person Maxence    schedule 16.11.2012
comment
Как вы решили эту проблему?   -  person goyote    schedule 20.11.2015


Ответы (6)


Майкл Галлего забыл обновить этот выпуск. Ознакомьтесь с его отчетом об ошибке и ответами на php.net.

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

Я подтверждаю, что это ошибка ICU в ветке 4.4.x.
Рассмотрите возможность обновления libicu, 4.8.x дает правильный результат

person LGT    schedule 15.06.2014
comment
У меня все еще возникает эта проблема с ICU 4.8.1 - person Attila Szeremi; 21.12.2015

Эта функция использует информацию о локали для форматирования числа в валюту, формат, который я использую, - '% # 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;
}
person Jaskaran Singh    schedule 04.01.2013

Вы должны указать правильную комбинацию языка и валюты. Например, fr-FR / fr-bh / fr-ch support €, это то, что должно быть указано в функции formatCurrency.

person Dave Meas    schedule 21.11.2012

Я думаю, вам нужно использовать регион, который поддерживает валюту назначения. Так что в en_US нет евро. Он есть у de_DE и у fr_FR тоже. Так что неудивительно, что fr_FR поддерживает евро.

Похоже, это не ошибка.

person Dirk    schedule 17.09.2012
comment
Хммм ... Это тоже ошибка меня. Это означает, что если для локали задано значение fr_FR, я не смогу отформатировать долларовую валюту, потому что у нас здесь нет долларов? - person Michael Gallego; 18.09.2012
comment
Это было подтверждено как ошибка в libicu. См. Ответ @ LGT. - person nobody; 20.06.2014

Установите значение локали по умолчанию в php ini, которое вы можете использовать:

ini_set ('intl.default_locale', 'де-DE');

Чтобы изменить числовой формат, используйте:

setlocale (LC_MONETARY, 'de-DE');

person Ganesan Murugesan    schedule 02.06.2014

Вы можете использовать следующую строку кода для форматирования денег: -

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

Он выведет вам:

USD 1,234.56
person Dinesh Rawat    schedule 04.06.2014
comment
Это не ответ на вопрос. - person nobody; 20.06.2014