const rules = new Intl.PluralRules("pl");
rules.select(1); // "one" → 1 plik
rules.select(3); // "few" → 3 pliki
rules.select(5); // "many" → 5 plików
rules.select(22); // "few" → 22 pliki
The ternary assumes every language has two forms, one and many. Many languages don't.
CLDR (the Unicode locale database) defines six plural categories: zero, one, two, few, many and other. Each language uses some of them:
- Japanese, Chinese, Korean: only
other. Nouns don't change with the number. - English:
oneandother. - French:
onecovers 0 and 1, so "0 fichier" is singular. - Polish:
one,fewandmany. As the code shows, 3 and 22 use one form but 5 uses another. - Arabic: all six. 0, 1, 2, 3–10, 11–99 and 100 each use a different category.
So a fixed 1 or not 1 rule gives wrong grammar for millions of users.
The fix:
new Intl.PluralRules(locale).select(n)returns the category for a number in that language.- The translator writes one full sentence per category the language needs.
- Your code picks the sentence for that category.
In practice, you rarely call PluralRules yourself. ICU MessageFormat strings ({count, plural, one {…} other {…}}) in react-intl, and i18next's _one and _other keys, use it for you.
There are also ordinal rules for "1st, 2nd, 3rd": new Intl.PluralRules("en", { type: "ordinal" }).
- Languages have different numbers of plural forms, from one to six.
- CLDR names the categories
zero,one,two,few,manyandother. Intl.PluralRules(locale).select(n)returns the category for a number.- The translator writes a full sentence for each category the language needs.
- Message libraries such as react-intl and i18next use these rules for you.