Localize numeric value with NumberFormatter
We don't need to create a framework to format localized numeric values. We just need to implement the Intl extension in PHP, either using the object-oriented methods or, even simpler, the procedural functions.
- https://www.php.net/manual/en/class.numberformatter.php
- https://en.wikipedia.org/wiki/Internationalization_and_localization
This extension has been available since PHP version 5.3.0.
Understanding localization in PHP
The NumberFormatter format()
method is like a combination of gettext()
and printf()
: it looks up a localized format and merges it with a provided value.
gettext() primer
gettext()
, with its alias _()
, is a simple lookup function: we feed it a key, normally a string in a default language, and it returns its associated value, the translation. The key/value pairs are located in a messages.po
files in a directory tree categorized by locale. These key/value pairs are cached by the web server for fast returns.
setlocale(LC_ALL, 'fr_CA');
echo _('This is text.'); //-> "Ceci est un texte."
printf() primer
printf()
is a function that merges values to a formatted string with placeholders.
printf("The string is %s and the number is %d.", 'simple', 25); //-> "The string is simple and the number is 25."
We can combine gettext()
and printf()
:
printf(_('The number is %d.'), 5); //-> "Le numéro est 5."
numfmt_format() primer
numfmt_format()
is a lookup function that merges a value to a locale-formatted string.
$fmt = numfmt_create('en_US', NumberFormatter::DEFAULT_STYLE);
echo numfmt_format($fmt, 2345234.234); //-> 2,345,234.234
numfmt_format()
has an OOP equivalent: NumberFormatter::format()
.
$fmt = new NumberFormatter('en_US', NumberFormatter::DEFAULT_STYLE);
echo $fmt->format(2345234.234); //-> 2,345,234.234
We can use the Facade version of the create()
method:
$fmt = NumberFormatter::create('en_US', NumberFormatter::DEFAULT_STYLE);
echo $fmt->format(2345234.234); //-> 2,345,234.234
As we can see, the numfmt_format()
function or the NumberFormatter::format()
method need to happen in two lines, unless we go with a very long line:
echo numfmt_format(numfmt_create('en_US', NumberFormatter::DEFAULT_STYLE), 2345234.234); //-> 2,345,234.234
Encapsulating NumberFormatter methods
We could encapsulate numfmt_create()
and numfmt_format()
in one practical custom function with default options that we can override:
function numeric_format($number, array $kwargs = []) {
extract($kwargs + [
'locale' => 'en_US',
'pattern' => NULL,
'style' => NumberFormatter::DEFAULT_STYLE,
'type' => NumberFormatter::TYPE_DEFAULT,
]);
$fmt = numfmt_create($locale, $style, $pattern);
return numfmt_format($fmt, $number, $type);
}
echo numeric_format(2345234.234); //-> 2,345,234.234
Using a default Locale
The Intl extension provides the Locale
class to get and set a default locale identifier.
Let's use it in a web application context. We set it in the bootstrap section of a web application:
Locale::setDefault('fr_FR');
We modify our custom function to use the default application locale.
function numeric_format($number, array $kwargs = []) {
extract($kwargs + [
- 'locale' => 'en_US',
+ 'locale' => substr(Locale::getDefault(), 0, 5) ?: NULL,
'pattern' => NULL,
'style' => NumberFormatter::DEFAULT_STYLE,
'type' => NumberFormatter::TYPE_DEFAULT,
]);
$fmt = numfmt_create($locale, $style, $pattern);
return numfmt_format($fmt, $number, $type);
}
Then, on a page, we localize an amount based on the application's locale:
echo numeric_format(2345234.234); //-> 2 345 234,234
Variants
Spellout
echo numeric_format(2345234.234, ['style'=>NumberFormatter::SPELLOUT]);
//-> two million three hundred forty-five thousand two hundred thirty-four point two three four
function spellout_format($number, array $kwargs = []) {
$kwargs += [
'style' => NumberFormatter::SPELLOUT,
];
return numeric_format($number, $kwargs);
}
echo spellout_format(2345234.234, ['locale'=>'de_DE']);
//-> zwei Millionen dreihundertfünfundvierzigtausendzweihundertvierunddreißig Komma zwei drei vier
Ordinal
echo numeric_format(31, ['style'=>NumberFormatter::ORDINAL]); //-> 31st
function ordinal_format($number, array $kwargs = []) {
$kwargs += [
'style' => NumberFormatter::ORDINAL,
];
return numeric_format($number, $kwargs);
}
echo ordinal_format(32); //-> 32nd
numfmt_create() arguments
numfmt_create(string $locale, int $style, ?string $pattern = null): ?NumberFormatter
$locale
If NULL
, it will lookup its default value in this order:
ini_get('intl.default_locale')
setlocale(LC_ALL, '0');
$LANG
value from the set locale of the operating system.
$style
-
The numeric default is
NumberFormatter::DEFAULT_STYLE
.
$pattern
-
https://unicode.org/reports/tr35/tr35-numbers.html#table-number-pattern-character-definitions
-
If not
NULL
, it will override the style argument. -
If
NULL
, it will defer to the style argument.