phproundingnumberformatterformat-currency

PHP NumberFormatter, formatCurrency method rounds number for Serbian dinar


PHP NumberFormatter, formatCurrency method rounds numbers for Serbian dinar, but it works fine for other locales and currencies like Poland, Russia, and Turkey for instance.

echo (new NumberFormatter('sr_RS',NumberFormatter::CURRENCY))
        ->formatCurrency('127.43','RSD');
// 127 дин.

echo (new NumberFormatter('ro_RO',NumberFormatter::CURRENCY))
        ->formatCurrency('127.43','RON');
// 127,43 RON

echo (new NumberFormatter('pl_PL',NumberFormatter::CURRENCY))
        ->formatCurrency('127.43','PLN');
//127,43 zł

echo (new NumberFormatter('tr_TR',NumberFormatter::CURRENCY))
        ->formatCurrency('127.43','TRY');
//127,43 ₺

So I want to show the decimals in the first case as well, is this a normal behavior?


Solution

  • The default currency number format for Serbia does not include decimal places - likely they are not often used or are ignored in that locale.

    You can see the pattern used in this ICU data table, under the NumberFormatter Patterns header and CURRENCY side-header. If you enter other locales in the search box at the top you will notice most use a format of #,##0.00 whereas the sr_RS locale uses #,##0.

    While likely bad practice since the ICU patterns are generally what the populous expects for each locale, you can ensure 2 digits are included by modifying the pattern (not guaranteed to be safe with every locale, but should not change most), by using a regular expression:

    $nf = new NumberFormatter('sr_RS', NumberFormatter::CURRENCY);
    $nf->setPattern(preg_replace('/##0(?!\.)/', '##0.00', $nf->getPattern()));
    echo $nf->formatCurrency('127.43','RSD');
    // 127,43 RSD
    

    The pattern ##0(?!\.) says to look for a string that starts with two hashtag/pound symbols followed by a zero that is not followed by a period and replace it with a pattern that does include 2 decimal places (##0.00).