Skip to main content

Pluralization

Plurals are essential when dealing with internationalization. LinguiJS uses CLDR Plural Rules. In general, there are 6 plural forms (taken from CLDR Plurals page):

  • zero
  • one (singular)
  • two (dual)
  • few (paucal)
  • many (also used for fractions if they have a separate class)
  • other (required — general plural form — also used if the language only has a single form)

Only the last one, other, is required because it's the only common plural form used in all languages.

All other plural forms depends on language. For example, English has only two: one and other (1 book vs. 2 books). In Czech, we have four: one, few, many and other (1 kniha, 2 knihy, 1,5 knihy, 5 knih). Some languages have even more, like Arabic.

Using plural forms

Good thing is that as developers, we have to know only plural forms for the source language.

If we use English in the source code, then we'll use only one and other:

plural(numBooks, {
one: "# book",
other: "# books",
});

When numBooks == 1, this will render as 1 book and for numBook == 2 it will be 2 books.

Interestingly, for numBooks == -1, it will be -1 book. This is because the "one" plural form also applies to -1. It is therefore important to remember that the plural forms (such as "one" or "two") do not represent the numbers themselves, but rather categories of numbers. If you want to specify a message for an exact number, use exact matches.

Funny fact for non-English speakers: In English, 0 uses plural form too, 0 books.

Under the hood, plural is replaced with low-level i18n._. For production, the above example will become:

i18n._({
id: "d1wX4r",
// stripped on production
// message: '{numBooks, plural, one {# book} other {# books}}',
values: { numBooks },
});

When we extract messages from source code using the CLI tool, we get:

{numBooks, plural, one {# book} other {# books}}

Now, we give it to our Czech translator, and they'll translate it as:

{numBooks, plural, one {# kniha} few {# knihy} many {# knihy} other {# knih}}

The important thing is that we don't need to change our code to support languages with different plural rules. Here's a step-by-step description of the process:

  1. In source code, we have:

    plural(numBooks, {
    one: "# book",
    other: "# books",
    });
  2. Code is compiled to:

    i18n._({
    id: "d1wX4r",
    // stripped on production
    // message: '{numBooks, plural, one {# book} other {# books}}',
    values: { numBooks },
    });
  3. Message {numBooks, plural, one {# book} other {# books}} is translated to:

    {numBooks, plural, one {# kniha} few {# knihy} many {# knihy} other {# knih}}
  4. Finally, message is formatted using Czech plural rules.

Source code in language other than English

As mentioned above, as developers, we have to know and use only plural forms for the source language. Go see what plural forms your languages has and then you can use them. Here's the example in Czech:

plural(numBooks, {
one: "# kniha",
few: "# knihy",
many: "# knihy",
other: "# knih",
});

This make LinguiJS useful also for unilingual projects, i.e: if you don't translate your app at all. Plurals, number and date formatting are common in every language.