Class PluralRules
Why this exists at all. A sentence with a number in it cannot be translated as one
string. Russian needs three forms for integers and picks between them by the last two
digits — 21 takes the same form as 1, 22 the same as 2, and 11 neither — and Arabic needs six.
Nothing in the JDK answers this: MessageFormat and ChoiceFormat select by
numeric range, which cannot express "ends in 1 but not 11", so a catalog cannot carry
the rule on its own however it is written. ICU has the data and is not an option here: this
toolkit has no runtime dependencies and gains none for one sentence.
So the rules are transcribed by hand — and the transcription is checked against CLDR
itself. scripts/i18n/dump-cldr-plurals.mjs reads Node's Intl.PluralRules,
which is the CLDR data as ICU carries it, and writes every answer for every integer
from 0 to 1000 plus spot checks past a hundred million;
PluralRulesTest.theTranscriptionAgreesWithCldr holds this class to that file, which is
checked in with the ICU version that produced it. Nothing in the build runs Node. Regenerate
the dump when a language is added, which is when a hand-written rule is most likely to be
wrong. Transcribed, the rules are still CLDR's data, under the Unicode License v3, whose text
the toolkit's jar carries at limn/i18n/internal/CLDR-LICENSE.txt.
The check earned its keep on the day it was written (2026-09-18, ICU 78.3): the first
transcription was wrong in five languages at once. Spanish, Italian, French, Portuguese and
Brazilian Portuguese have a many form for an exact multiple of a million — "un millón
de elementos" — which no rule written from the first few integers would ever suggest.
A language this class does not name falls back to English's PluralRules.Rule.ONE_OTHER, the
shape of the largest family, and that fallback stays a guess by construction: an application
shipping a language from another family declares its own rule by carrying only the forms it
needs (see integerCategories(java.util.Locale)).
Integers only, deliberately. Every count this toolkit speaks is a number of things —
rows, items, results — so select(java.util.Locale, long) takes a long. CLDR's fraction categories
(Czech's many, which is "2.5 položky") cannot arise, and integerCategories(java.util.Locale)
leaves them out rather than making a translator fill a form the language never reaches here.
- See Also:
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic enumCLDR's six cardinal categories. -
Method Summary
Modifier and TypeMethodDescriptionstatic Set<PluralRules.Category> integerCategories(Locale locale) Every formlocalecan reach with a whole number, which is exactly the set of keys a catalog for that language owes aPluralString.static PluralRules.CategoryThe formcounttakes inlocale.
-
Method Details
-
select
The formcounttakes inlocale.- Parameters:
locale- the language to ask; only its language subtag is read, as ICU reads the code, sofrais French andhebHebrewcount- how many, as a whole number; negative counts are read by their magnitude, because a language inflects for the quantity and not for the sign- Returns:
- the category, never
null
-
integerCategories
Every formlocalecan reach with a whole number, which is exactly the set of keys a catalog for that language owes aPluralString.Read by
ShippedTranslationsTest, which is why it is public: a catalog with a form its language never reaches is as wrong as one missing a form it does — the first invites a translator to invent a distinction, the second drops a number into the wrong sentence.- Parameters:
locale- the language to ask- Returns:
- its categories, in declaration order, never empty
-