CodePark/ questions / Plurals & messages
START PRACTICINGPRACTICE
Frontend question · Internationalization › Plurals & messages

Why does count === 1 ? "item" : "items" break in other languages, and how does Intl.PluralRules solve it?

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: one and other.
  • French: one covers 0 and 1, so "0 fichier" is singular.
  • Polish: one, few and many. 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, many and other.
  • 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.