Description
Describe the bug
When localizing my Flutter apps, I use the Google's ARB format, per recommendation from framework developers. It works great, but I've encountered a slight problem. I've added the following message:
"nthAddressLine": "{index}{index, plural, one{st} two{nd} few{rd} other{th}} address line",
"@nthAddressLine": {
"description": "Text field label. Accepts a single address line in an address form.",
"placeholders": {
"index": {
"type": "int",
"description": "Index of the address line, starting from one."
}
}
}
From which I would expect the following results:
1 => 1st address line
2 => 2nd address line
3 => 3rd address line
4 => 4th address line
...
But instead I was surprised to see the following:
1 => 1st address line
2 => 2nd address line
3 => 3th(!) address line
...
After some digging, I've found that the aforementioned ICU plural syntax results in the following code being generated:
@override
String nthAddressLine(num index) {
String _temp0 = intl.Intl.pluralLogic(
index,
locale: localeName,
other: 'th',
few: 'rd',
two: 'nd',
one: 'st',
);
return '$index$_temp0 address line';
}
And after some more digging, I've found that for locale = en
, the following closure is executed:
PluralCase _ast_rule() {
if ((_i == 1) && (_v == 0)) {
return ONE;
}
return OTHER;
}
Which makes it seem like the pluralLogic
function does cardinal pluralization, which is further corroborated by #425 (comment).
But according to language plural rules for English, there is only one
and other
cardinal plural forms, while pluralLogic
also returns zero
and two
:
// This is for backward compatibility.
// We interpret the presence of [precision] parameter as an "opt-in" to
// the new behavior, since [precision] did not exist before.
// For an English example: if the precision is 2 then the formatted string
// would not map to 'one' (for example "1.00 miles")
if (useExplicitNumberCases && (precision == null || precision == 0)) {
// If there's an explicit case for the exact number, we use it. This is
// not strictly in accord with the CLDR rules, but it seems to be the
// expectation. At least I see e.g. Russian translations that have a zero
// case defined. The rule for that locale will never produce a zero, and
// treats it as other. But it seems reasonable that, even if the language
// rules treat zero as other, we might want a special message for zero.
if (howMany == 0 && zero != null) return zero;
if (howMany == 1 && one != null) return one;
if (howMany == 2 && two != null) return two;
}
So this begs the question which pluralization does this function do: cardinal or ordinal? Because right now it seems to do a little bit of both, but doesn't seem spec compliant on neither.
Not sure how to approach fixing the logic, but the least we could do is mention this in the docs, both in this package and in Flutter's docs.
To Reproduce
test('Ordinal pluralization', () {
final output = old_intl.Intl.pluralLogic(
3,
locale: 'en',
other: 'other',
few: 'few',
two: 'two',
one: 'one',
);
// If this function was doing ordinal pluralization, we'd expect few for 3
expect(output, equals('few'));
});
test('Cardinal pluralization', () {
final output = old_intl.Intl.pluralLogic(
2,
locale: 'en',
other: 'other',
few: 'few',
two: 'two',
one: 'one',
);
// If this function was doing cardinal pluralization, we'd expect other for 2
expect(output, equals('other'));
});
System info
Dart SDK 3.7.2
Flutter SDK 3.29.2
intl 0.20.3-wip