twigphp / Twig

Twig, the flexible, fast, and secure template language for PHP
https://twig.symfony.com/
BSD 3-Clause "New" or "Revised" License
8.19k stars 1.26k forks source link

IntlExtension::NUMBER_STYLES may be missing the PADDING_POSITION style #3850

Closed damienalexandre closed 11 months ago

damienalexandre commented 1 year ago

My goal is to format this price: 673000111.12 like this: 673 M € (known as short-form compact notation in ICU).

I came accros this issue when trying to replicate this code in Twig:

$nf = new \NumberFormatter('fr', \NumberFormatter::PADDING_POSITION);
$nf->setAttribute(\NumberFormatter::MAX_FRACTION_DIGITS, 0);
$formatted = $nf->format(673000111.12);
dump($formatted);

$formatted = $nf->formatCurrency(673000111.12, 'EUR');
dump($formatted);
"673 M"

"673 M €"

As you can see it looks like PHP has support for compact notation in both formatCurrency and format 🎉 (even if the "style" is not set to "currency", we can call formatCurrency, that's confusing).

With Twig IntlExtension format_number() method, you can only set one of those styles:

https://github.com/twigphp/Twig/blob/df75bfef370907f3842c321280b64c42f8507ecb/extra/intl-extra/IntlExtension.php#L72-L80

No trace of the one I used in PHP: \NumberFormatter::PADDING_POSITION. The IntlExtension treat that style as a number attribute:

https://github.com/twigphp/Twig/blob/df75bfef370907f3842c321280b64c42f8507ecb/extra/intl-extra/IntlExtension.php#L95

But when used as an attribute, it does nothing ❌

{{ bigNumber|format_number }}<br>
{{ bigNumber|format_number({fraction_digit: 0}) }}<br>
{{ bigNumber|format_number({padding_position: 'before_prefix'}) }}<br>
673 000 111,12
673 000 111
673 000 111,12

And forcing that style is not possible:

{{ bigNumber|format_number({padding_position: 'before_prefix'}, 'padding_position') }}<br>

The style "padding_position" does not exist, known styles are: "decimal", "currency", "percent", "scientific", "spellout", "ordinal", "duration".

What would be needed to fix this, is some kind of trick where, if a padding_position attribute is given, we set the style to \NumberFormatter::PADDING_POSITION and call either formatCurrency or format as usual on that "padding" formatter.

But I'm a bit torn appart because nothing in the documentation tells that \NumberFormatter::PADDING_POSITION is a "style" and it make no sense... It could even be a bug in PHP and it's working by accident.

Other implementations

In the PHP World I did not found a lot of usages of that "style".

https://github.com/akaunting/laravel-money/blob/37a56d8320a0a9ffa4dcecb218d2c55ba189ce06/src/Money.php#L586-L611

https://github.com/StarCitizenTools/mediawiki-skins-Citizen/blob/c793959416ce28fff0ddebebff6252f005b6aa52/includes/Partials/Drawer.php#L143-L149

In JavaScript it's known as the compact notation (much clearer name!):

new Intl.NumberFormat('en-GB', { 
    notation: "compact"
}).format(673000111.12);

I'm very curious about getting some feedback on that matter, I feel we can do a patch in Twig IntlExtension to get this nice feature, but also that there is something wrong in PHP itself about this as it supposed to be an attribute:

https://github.com/php/php-src/blob/30c5ae421940ed9597d838f3bcff6ea3c8a34047/ext/intl/formatter/formatter_attr.c#L55

stof commented 1 year ago

Well, the PHP documentation itself says that \NumberFormatter::PADDING_POSITION is a number attribute and not a style: https://www.php.net/manual/fr/class.numberformatter.php#numberformatter.constants.padding-position

So I guess the Twig implementation is wrong because it was done based on the PHP documentation. I suggest you to report an issue on PHP to fix things.