fluent.js
fluent.js copied to clipboard
Number parsing overrides minimumFractionDigits
The way we store number's precision has been rewritten in https://github.com/projectfluent/fluent.js/commit/2bd94bc33a555c12ce176ed8b519159d3cd28e48 to store a value as a number and precision as minimumFractionDigits option to intl number formatter.
This has an unfortunate consequence in that the number of fraction digits specified in the input hardcodes the mfd option, which is different from how Intl.NumberFormat works.
Here's an example (ignore for now that we wanted to limit currency setting from L10n, same argument applies for partial arguments or other forms of default precision):
(5).toLocaleString("en"); // "5"
(5.00).toLocaleString("en"); // "5"
(5).toLocaleString("en", {style: "currency", currency: "USD"}); // "$5.00"
(5.00).toLocaleString("en", {style: "currency", currency: "USD"}); // "$5.00"
But in Fluent, this will happen:
key1 = Hello { 5 } World
key2 = Hello { 5.00 } World
key3 = Hello { NUMBER(5, style: "currency", currency: "USD") } World
key4 = Hello { NUMBER(5.00, style: "currency", currency: "USD") } World
output:
Hello 5 World
Hello 5.00 World
Hello $5 World
Hello $5.00 World
I aligned fluent-rs with fluent.js here https://github.com/projectfluent/fluent-rs/commit/df9c489d6a451e181d939096883eea708efd3b87 but am a bit concerned about this behavior difference.
@stasm, @pike - do you think this is an issue or a feature?
I recall a whiteboard in Berlin .... that made it sound like a feature. But I don't remember.
This has an unfortunate consequence in that the number of fraction digits specified in the input hardcodes the mfd option, which is different from how Intl.NumberFormat works.
Why do you think this is unfortunate? The behavior of Intl.NumberFormat is due to how number literals work in JS. Fluent doesn't have to be bound by the same rules.
In Fluent, number literals carry the information about their precision. The following expressions produce the same result:
key1 = {3.00}
key2 = {NUMBER(3, minimumFractionDigits:2)}
Do you think it shouldn't work this way?
Related spec issue: https://github.com/projectfluent/fluent/issues/268
I think the issue is with this:
key3 = Hello { NUMBER(5, style: "currency", currency: "USD") } World
vs.
(5).toLocaleString("en", {style: "currency", currency: "USD"}); // "$5.00"
In JS case I said take 5 and format it as currency using default fraction digits for the number because I have not specificied MFD manually.
In Fluent I said take 5 and format it as currency enforcing zero MFD.
There's no way to say the former in Fluent scenario because not specifying fractionals is equivalent of setting minimumFractionDigits: 0 explicitly. There's no way to say auto or None.
Ah, I see what you mean, thanks.
My current thinking is to approach this with a pragmatic mindset. I'd expect localizers to just write key = $5,00 if they know the value a priori. Otherwise this isn't a use-case for number literals. Number literals are mostly useful as variant keys and values of named arguments.
We still need to specify their different behaviors, but I'd like to also keep the larger picture in mind when doing it.
Yeah, as I said, currency may not be the best example, but with the growth of Intl.NumberFormat I can see this becoming a limitation at some point.
For example it may be there default formatting of some units will be different or percent in different locales.
And that formatting may by default have some MFD, while the way Fluent works now will force the MFD: 0 in all of them.
Here's an example of how we could limit the impact:
The minimumFractionDigits is set based on the number of fraction digits in the input number string only if:
- there's a fraction separator (
.) - the last digit of the fraction is
0
This means that 5.20 has MFD: 2, 5.0 has MFD: 1, but 5.2, 5 and 5.3231 has auto.
Do you expect number literals to be used with intl formatters? I'd argue that if a) locale is known and b) the value is known, the right solution is to just format the value by typing it as a string.
This means that 5.20 has MFD: 2, 5.0 has MFD: 1, but 5.2, 5 and 5.3231 has auto.
How would you express 5.2 which is supposed to have MFD equal to exactly 1?
How would you express 5.2 which is supposed to have MFD equal to exactly 1?
If you want to enforce MFD: 1, you set it via minimumFractionDigits: 1. If you don't care, you just pass and we set is as "auto".