I seem to be getting very inconsistent results when trying to format currency. In PHP, I’m using https://www.php.net/manual/en/class.numberformatter.php. I have the following demo code:
$number = 5125.99;
echo getInternationallyFormattedCurrency($number, 'tl-PH', 'PHP');
echo '<br/>';
echo getInternationallyFormattedCurrency($number, 'fil-PH', 'PHP');
echo '<br/>';
echo getInternationallyFormattedCurrency($number, 'en-US', 'PHP');
echo '<br/>';
echo '<br/>';
echo getInternationallyFormattedCurrency($number, 'tl_PH', 'USD');
echo '<br/>';
echo getInternationallyFormattedCurrency($number, 'fil_PH', 'USD');
echo '<br/>';
echo getInternationallyFormattedCurrency($number, 'en_US', 'USD');
echo '<br/>';
When I run this on my localhost (was PHP Version 7.3.7, but I updated to match my server’s PHP Version 7.3.12 – ICU version 64.2), I get this:
₱5,125.99
₱5,125.99
PHP 5,125.99
$5,125.99
$5,125.99
$5,125.99
However, when I run it on my server (PHP Version 7.3.12 – ICU version 4.2.1), I get this:
₱ 5125.99
₱ 5125.99
₱5,125.99
$ 5125.99
$ 5125.99
$5,125.99
Why the difference? And which one is actually correct? I’m guessing my local machine due to higher ICU version?
I need the exact same functionality from JS, too. So, on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat, I put the following equivalent code:
var number = 5125.99;
console.log(new Intl.NumberFormat('tl-PH', { style: 'currency', currency: 'PHP' }).format(number));
console.log(new Intl.NumberFormat('fil-PH', { style: 'currency', currency: 'PHP' }).format(number));
console.log(new Intl.NumberFormat('en-US', { style: 'currency', currency: 'PHP' }).format(number));
console.log(new Intl.NumberFormat('tl-PH', { style: 'currency', currency: 'USD' }).format(number));
console.log(new Intl.NumberFormat('fil-PH', { style: 'currency', currency: 'USD' }).format(number));
console.log(new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(number));
I get this:
> "PHP 5,125.99"
> "₱5,125.99"
> "PHP 5,125.99"
> "$5,125.99"
> "$5,125.99"
> "$5,125.99"
So – this is yet another result. I need a way to format currencies consistency between PHP and JS. How do I do this?
UPDATE 1:
My local machine’s ICU version is 64.2 while my server has 4.2.1. I’ll see if my hosting provider can update the ICU version to the latest. That might explain the discrepancy between what my local machine outputs versus what my server outputs.
Still not sure why JS behaves differently.
UPDATE 2:
My hosting company says that because of cPanel, I need to stick to ICU version 4.2.1. Although cPanel has some tips on how to upgrade ICU version, it is apparently not recommended to do so.
- https://forums.cpanel.net/threads/internationalization-extension-version-upgrade.612607/
- https://forums.cpanel.net/threads/install-php71-php-intl-with-a-more-recent-version-of-icu.620979/
UPDATE 3:
I’m now thinking to have my JS make an Ajax call to a PHP method that will handle number formatting, that way I can be sure I’m getting the same formatting output. Feels like a slow and expensive solution, though.
3
Answers
I tried this approach:
and got consistent results:
Thus, to be 100% safe, I would suggest creating a small server side component for the formatting purposes, so that you can pass the locale, value and currency and always have the same result regardless if it’s backend or frontend that needs the formatted string.
Also, note that
tl-PH
andtl_PH
is not the same. Usetl_PH
andfil_PH
anden_US
consistently.Just an idea… But to get consistent results accross two different languages, I would suggest using the exact same library code.
What if you used https://github.com/phpv8/v8js to run Intl.NumberFormat on PHP side? There’s also an example here https://github.com/phpv8/v8js/issues/182
I would probably go with this path to get everything consistent
From the php side, since php rely on underlying C libraries, this is from where comes inconsistencies (at compilation time):
Nothing much to do from php, we must takes extra cares of that.
Here are some notes following own tests!
Those snippets returns formatted numbers (won’t return scientific notation), but are rounding, which can be not desired. It takes care of grouped thousands, we can choose to display or not. From my view it’s a bad idea and wanted to avoid them.
From javascript, there is a lot of options:
Sample output:
List of supported codes
From php, to obtain something similar, I found easier to use associative arrays for the symbols. The code works then everywhere identically.
Sample output. Notice the rounding using NumberFormatter(). It’s in most cases unwanted, it’s more flexible to use just number_format().