Localization and Plurals

You're likely here because you're localizing a .properties file and it had a link to this page. This page is to help explain how to localize these strings so that the correct plural form is shown to the user. For example, "1 page" vs "2 pages".

If you're here to make your code (e.g., extensions) localizable for plural forms, you can jump straight to Developing with PluralForm. You may also need to localize the initial strings for your code, so it would be good to read through at least the Usage section as well.

Usage

Here are a couple terms used in this page to help keep things clear:

  • plural rule: For a given language, there is a grammatical rule on how to change words, depending on the number qualifying the word. Different languages can have different rules.
  • plural form: For a particular plural rule, there can be multiple forms of a word, such as "page" and "pages". In this case, there are 2 forms, but other languages can have 1 or many more.

If you're here for pluralRule in the chrome/global/intl.properties file, you'll need to figure out what plural rule to choose for your localization. This plural rule is used to determine how many plural forms are needed for each word that needs to be localized, with plurals in mind.

For all other properties files that link to this page, you'll need to provide enough plural forms of the desired word, and separate them with semi-colons (;). If you don't know how many plural forms you need, check the pluralRule number in chrome/global/intl.properties, and look up the corresponding entry in the following list of plural rules.

List of Plural Rules

This section contains a list of plural rules ordered by their plural rule number. Each entry indicates how many plural forms are needed when localizing a word. For each entry, there is a list of families and languages in those families, to help you figure out if it's the rule you should pick for pluralRule. Additionally, there is a brief description of each plural form, followed by some sample numbers that fall into that particular form.

For a given plural rule, the order in which the plural forms are listed is the same order you need to localize a word, separated by semi-colons. For example, English uses plural rule 1, and localizing plurals would require a string of "plural;plurals" where the first word is the singular form and the second is the general plural form.

Plural rule #0 (1 form)

Families: Asian (Chinese, Japanese, Korean), Persian, Turkic/Altaic (Turkish), Thai, Lao
everything: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, …

Plural rule #1 (2 forms)

Families: Germanic (Danish, Dutch, English, Faroese, Frisian, German, Norwegian, Swedish), Finno-Ugric (Estonian, Finnish, Hungarian), Language isolate (Basque), Latin/Greek (Greek), Semitic (Hebrew), Romanic (Italian, Portuguese, Spanish, Catalan), Vietnamese
is 1: 1
everything else: 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, …

Plural rule #2 (2 forms)

Families: Romanic (French, Brazilian Portuguese), Lingala
is 0 or 1: 0, 1
everything else: 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, …

Plural rule #3 (3 forms)

Families: Baltic (Latvian, Latgalian)
ends in 0: 0
ends in 1, excluding 11: 1, 21, 31, 41, 51, 61, 71, 81, 91, 101, 121, 131, 141, 151, 161, 171, 181, 191, 201, 221, 231, 241, 251, 261, 271, 281, 291, …
everything else: 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 22, 23, 24, 25, 26, 27, 28, 29, 32, 33, 34, 35, 36, 37, 38, 39, 42, 43, 44, 45, 46, 47, 48, 49, 52, 53, …

Plural rule #4 (4 forms)

Families: Celtic (Scottish Gaelic)
is 1 or 11: 1, 11
is 2 or 12: 2, 12
is 3-10 or 13-19: 3, 4, 5, 6, 7, 8, 9, 10, 13, 14, 15, 16, 17, 18, 19
everything else: 0, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, …

Plural rule #5 (3 forms)

Families: Romanic (Romanian)
is 1: 1
is 0 or ends in 01-19, excluding 1: 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, …
everything else: 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, …

Plural rule #6 (3 forms)

Families: Baltic (Lithuanian)
ends in 1, excluding 11: 1, 21, 31, 41, 51, 61, 71, 81, 91, 101, 121, 131, 141, 151, 161, 171, 181, 191, 201, 221, 231, 241, 251, 261, 271, 281, 291, …
ends in 0 or ends in 11-19: 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, …
everything else: 2, 3, 4, 5, 6, 7, 8, 9, 22, 23, 24, 25, 26, 27, 28, 29, 32, 33, 34, 35, 36, 37, 38, 39, 42, 43, 44, 45, 46, 47, 48, 49, 52, 53, 54, 55, 56, 57, 58, 59, 62, 63, 64, 65, 66, 67, 68, 69, 72, 73, …

Plural rule #7 (3 forms)

Families: Belarusian, Russian, Ukrainian
ends in 1, excluding 11: 1, 21, 31, 41, 51, 61, 71, 81, 91, 101, 121, 131, 141, 151, 161, 171, 181, 191, 201, 221, 231, 241, 251, 261, 271, 281, 291, …
ends in 2-4, excluding 12-14: 2, 3, 4, 22, 23, 24, 32, 33, 34, 42, 43, 44, 52, 53, 54, 62, 63, 64, 72, 73, 74, 82, 83, 84, 92, 93, 94, 102, 103, 104, 122, 123, 124, 132, 133, 134, 142, 143, 144, 152, 153, 154, 162, 163, 164, 172, 173, 174, 182, 183, …
everything else: 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 25, 26, 27, 28, 29, 30, 35, 36, 37, 38, 39, 40, 45, 46, 47, 48, 49, 50, 55, 56, 57, 58, 59, 60, 65, 66, 67, 68, 69, 70, 75, 76, 77, …, 112, 113, ..., 212, 213, ...

Plural rule #8 (3 forms)

Families: Slavic (Slovak, Czech)
is 1: 1
is 2-4: 2, 3, 4
everything else: 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, …

Plural rule #9 (3 forms)

Families: Slavic (Polish)
is 1: 1
ends in 2-4, excluding 12-14: 2, 3, 4, 22, 23, 24, 32, 33, 34, 42, 43, 44, 52, 53, 54, 62, 63, 64, 72, 73, 74, 82, 83, 84, 92, 93, 94, 102, 103, 104, 122, 123, 124, 132, 133, 134, 142, 143, 144, 152, 153, 154, 162, 163, 164, 172, 173, 174, 182, 183, …
everything else: 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 25, 26, 27, 28, 29, 30, 31, 35, 36, 37, 38, 39, 40, 41, 45, 46, 47, 48, 49, 50, 51, 55, 56, 57, 58, 59, 60, 61, 65, 66, 67, 68, …

Plural rule #10 (4 forms)

Families: Slavic (Slovenian, Sorbian)
ends in 01: 1, 101, 201, …
ends in 02: 2, 102, 202, …
ends in 03-04: 3, 4, 103, 104, 203, 204, …
everything else: 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, …

Plural rule #11 (5 forms)

Families: Celtic (Irish Gaelic)
is 1: 1
is 2: 2
is 3-6: 3, 4, 5, 6
is 7-10: 7, 8, 9, 10
everything else: 0, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, …

Plural rule #12 (6 forms)

Families: Semitic (Arabic)
is 1: 1
is 2: 2
ends in 03-10: 3, 4, 5, 6, 7, 8, 9, 10, 103, 104, 105, 106, 107, 108, 109, 110, 203, 204, 205, 206, 207, 208, 209, 210,
everything else but is 0 and ends in 00-02, excluding 0-2: 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, …
ends in 00-02, excluding 0-2: 100, 101, 102, 200, 201, 202, …
is 0: 0

Plural rule #13 (4 forms)

Families: Semitic (Maltese)
is 1: 1
is 0 or ends in 01-10, excluding 1: 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, …
ends in 11-19: 11, 12, 13, 14, 15, 16, 17, 18, 19, 111, 112, 113, 114, 115, 116, 117, 118, 119, 211, 212, 213, 214, 215, 216, 217, 218, 219, …
everything else: 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, …

Plural rule #14 (3 forms)

Families: unused
ends in 1: 1, 11, 21, 31, 41, 51, 61, 71, 81, 91, 101, 111, 121, 131, 141, 151, 161, 171, 181, 191, 201, 211, 221, 231, 241, 251, 261, 271, 281, 291, …
ends in 2: 2, 12, 22, 32, 42, 52, 62, 72, 82, 92, 102, 112, 122, 132, 142, 152, 162, 172, 182, 192, 202, 212, 222, 232, 242, 252, 262, 272, 282, 292, …
everything else: 0, 3, 4, 5, 6, 7, 8, 9, 10, 13, 14, 15, 16, 17, 18, 19, 20, 23, 24, 25, 26, 27, 28, 29, 30, 33, 34, 35, 36, 37, 38, 39, 40, 43, 44, 45, 46, 47, 48, 49, 50, 53, 54, 55, 56, 57, 58, 59, 60, 63, …

Plural rule #15 (2 forms)

Families: Icelandic, Macedonian
ends in 1, excluding 11: 1, 21, 31, 41, 51, 61, 71, 81, 91, 101, 121, 131, 141, 151, 161, 171, 181, 191, 201, 221, 231, 241, 251, 261, 271, 281, 291, …
everything else: 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 23, 24, 25, 26, 27, 28, 29, 30, 32, 33, 34, 35, 36, 37, 38, 39, 40, 42, 43, 44, 45, 46, 47, 48, 49, 50, 52, 53, 54, …

Plural rule #16 (5 forms)

Families: Celtic (Breton)
ends in 1, excluding 11, 71, 91: 21, 31, 41, 51, 61, 81, 101, 121, 131, 141, 151, 161, 181, 201, 221, 231, 241, 251, 261, 281, ...
ends in 2, excluding 12, 72, 92: 2, 22, 32, 42, 52, 62, 82, 102, 122, 132, 142, 152, 162, 182, 202, 222, 232, 242, 252, 262, 282, ...
ends in 3, 4 or 9 excluding 13, 14, 19, 73, 74, 79, 93, 94, 99: 3, 4, 9, 23, 24, 29, 33, 34, 39, 43, 44, 49, 53, 54, 59, ...
ends in 000000: 1000000, 2000000, 3000000, 4000000, 5000000, 6000000, 7000000, 8000000, 9000000, 10000000, ...
everything else: 0, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 25, 26, 27, 28, 30, 35, 36, 37, 38, 40, ...

Plural rule #17 (2 forms)

Families: Ecuador indigenous languages (Shuar)
is 0: 0
everything else: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, …

Plural rule #18 (6 forms)

Families: Welsh
is 0: 0
is 1: 1
is 2: 2
is 3: 3
is 6: 6
everything else: 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, …

Plural rule #19 (3 forms)

Families: Slavic (Bosnian, Croatian, Serbian)
ends in 1, excluding 11: 1, 21, 31, 41, 51, 61, 71, 81, 91, 101, 121, 131, 141, 151, 161, 171, 181, 191, 201, 221, 231, 241, 251, 261, 271, 281, 291, …
ends in 2-4, excluding 12-14: 2, 3, 4, 22, 23, 24, 32, 33, 34, 42, 43, 44, 52, 53, 54, 62, 63, 64, 72, 73, 74, 82, 83, 84, 92, 93, 94, 102, 103, 104, 122, 123, 124, 132, 133, 134, 142, 143, 144, 152, 153, 154, 162, 163, 164, 172, 173, 174, 182, 183, …
everything else: 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 25, 26, 27, 28, 29, 30, 35, 36, 37, 38, 39, 40, 45, 46, 47, 48, 49, 50, 55, 56, 57, 58, 59, 60, 65, 66, 67, 68, 69, 70, 75, 76, 77, …, 112, 113, ..., 212, 213, ...

Examples

The following are some examples for various languages and a brief thought processes.

French

Some French speaking places treat 0 as plural while others treat it as singular. The only other singular is 1 while everything else is plural. So pick either plural rule #1 or #2.

pluralRule=2
seconds=seconde;secondes
minutes=minute;minutes
hours=heure;heures
days=jour;jours

Like many other times when localizing words, gender agreement might force you to rearrange words in a way that the gender is always the same. (seconde vs jour)

Chinese

A word doesn't change if there is a different number in front of it, so all numbers use the same plural form. With just one plural form, it has to be plural rule #0. For each word to localize, it's just like localizing a single word with no semi-colons needed.

pluralRule=0
seconds=秒
minutes=分
hours=時
days=日

Polish

There's a singular form for 1, a plural form for 2-4, and another for 5-21 at which point 22 is the same as 2. Plural rule #7 has a "ends in 2-4, not 12-14" but the singular form includes everything ending in 1 except 11. Plural rule #9 has the correct singular form for only 1.

pluralRule=9
seconds=sekunda;sekundy;sekund
minutes=minuta;minuty;minut
hours=godzina;godziny;godzin
days=dzień;dni;dni

Even though the last 2 plural forms of "day" are the same, both are still needed, because there needs to be 3 plural forms for each word.

Sorbian

There are 4 plural forms: nominative singular, nominative dual, nominative plural, genitive plural. These match up with plural rule #10.

pluralRule=10
seconds=sekunda;sekundźe;sekundy;sekundow
minutes=mjeńšina;mjeńšinje;mjeńšiny;mjeńšin
hours=hodźina;hodźinje;hodźiny;hodźin
days=dźeń;dnjej;dny;dnjow

Testing Extension

To help make sure you pick the right plural rule and provide enough plural forms for strings, you should use the pluralForm Checker extension. After installing the extension, it should be available from the Tools menu.

To use it, list off the property files and properties that you want to check and click the button. The extension will load each property and display the plural forms in a table. Selecting a table entry will populate the bottom box with sample uses of the word for some numbers.

Image:pluralForm-checker.0.3.png

Install pluralForm Checker v0.3 extension

Extension input

It would be good to keep this list updated with all the words that need plural forms. People using the extension can then copy/paste this input.

chrome://mozapps/locale/downloads/do...tes,hours,days
chrome://mozapps/locale/downloads/do...dsTitlePercent
chrome://browser/locale/browser.prop...ausedDownloads

Version history

0.1: Initial version with pluralRule check, properties input loading, table generation, sample output display
0.2: Use PluralForm.numForms() to get the number of forms instead of figuring out locally to better support future rules - Requires build from 2007/01/27 or later
0.3: Generate a list of what numbers fall into which plural form to minimize the sample output to at most 3 of each form

Developing with PluralForm

The functionality for getting the correct plural forms is provided by a JavaScript Module, PluralForm.jsm. This module provides a couple methods for localizing to the browser's current locale as well as getting methods to localize to a desired plural rule. The latter ability of specifying a plural rule is useful for extensions because the extension doesn't necessarily have to be localized to the browser's locale.

Loading PluralForm.jsm

Loading the PluralForm module from JavaScript is simple with Components.utils.import. Just put the following line somewhere that will be evaluated before you want to use PluralForm. At the top of your JavaScript file is fine.

Components.utils.import("resource://gre/modules/PluralForm.jsm");

Methods: get

These methods make use of the browser's current locale specified by chrome://global/locale/intl.properties's pluralRule value.

/**
 * Get the correct plural form of a word based on the number
 *
 * @param aNum
 *        The number to decide which plural form to use
 * @param aWords
 *        A semi-colon (;) separated string of words to pick the plural form
 * @return The appropriate plural form of the word
 */
string pluralForm
get(int aNum, string aWords)

Here is an example of using this method:

// Load PluralForm and for this example, assume English
Components.utils.import("resource://gre/modules/PluralForm.jsm");

// PluralForm.get expects a semi-colon separated list of words
let downloads = "download;downloads";
// Pretend this number came from somewhere else
let num = 10;

// Display the correct plural form for 10 downloads: "You have 10 downloads.")
print("You have " + num + " " + PluralForm.get(num, downloads) + ".");

// Try again with a different value: "You have 1 download."
num = 1;
print("You have " + num + " " + PluralForm.get(num, downloads) + ".")

The above example works, but is still difficult to localize, because we're concatenating strings assuming a particular grammatical structure. The following would be better:

Components.utils.import("resource://gre/modules/PluralForm.jsm");

let downloads = "You have one download.;You have #1 downloads.";
let num = 10;

// For English, this would display "You have 10 downloads."
print(PluralForm.get(num, downloads).replace("#1", num);

Notice in the above example that the code can be written to support placeholders or not use placeholders in some forms of the string. Additionally, the localizer has control over where the placeholder is, in relation to the rest of the text.

Of course, the strings to be localized will be placed in a separate file, such as yourextension.properties, instead of being hardcoded in the JavaScript code file.

The following 3 file snippets show how to use PluralForm with your .xul, .properties, .js files.

downloads.xul:

<stringbundleset>
  <stringbundle id="strings" src="chrome://downloads.properties"/>
</stringbundleset>

downloads.properties:

# LOCALIZATION NOTE (downloadsTitleFiles): Semi-colon list of plural forms.
# See: https://developer.mozilla.org/docs/Mozilla/Localization/Localization_and_Plurals
# #1 number of files
# example: 111 files - Downloads
downloadsTitleFiles=#1 file - Downloads;#1 files - Downloads

# LOCALIZATION NOTE (timePair): #1 time number; #2 time unit
# example: 1 second; 11 seconds
timePair=#1 #2
seconds=second;seconds
minutes=minute;minutes
hours=hour;hours

downloads.js:

Components.utils.import("resource://gre/modules/PluralForm.jsm");
let getStr = function(string) document.getElementById("strings").getString(string);

// Get the correct plural form for the title
let numDownloads = 3;
let title = PluralForm.get(numDownloads, getStr("downloadsTitleFiles"));
// Put in the correct number of downloads
print(title.replace("#1", numDownloads));

// Get the correct plural form of seconds
let timeLeft = 55;
let seconds = PluralForm.get(timeLeft, getStr("seconds"));
// Print the localized string for "55 seconds"
print(getStr("timePair").replace("#1", timeLeft).replace("#2", seconds));

Method: makeGetter

If you're writing an extension, you'll want to use makeGetter instead of PluralForm.get() or PluralForm.numForms(). This is because someone installing the extension on a different locale will be using the strings provided by your default extension locale. For example, your extension localized for English with plural rule #1, which expects 2 plural forms, is installed on a localized version of Firefox with plural rule #4, which expects 3 forms:

/**
 * Create a pair of plural form functions for the given plural rule number.
 *
 * @param aRuleNum
 *        The plural rule number to create functions
 * @return A pair: [function that gets the right plural form,
 *                  function that returns the number of plural forms]
 */
[string pluralForm get(int aNum, string aWords), int numForms numForms()]
makeGetter(int aRuleNum)

Here is an example usage of makeGetter:

Components.utils.import("resource://gre/modules/PluralForm.jsm");

// Let's get Irish (plural rule #11)
let [get, numForms] = PluralForm.makeGetter(11);

// Make up some values to use with "get"
let dummyText = "form 1;form 2;form 3;form 4;form 5";
let dummyNum = 10;

// In the case of Irish, the value 10 uses plural form #4, so "form 4" is printed
print(get(dummyNum, dummyText));

In this example, the Irish plural rule was hardcoded, but this could be a value specified in the .properties file. So for your extension, specify a pluralRule value in the .properties, and call PluralForm.makeGetter(pluralRuleFromProperties), making sure to save the 2 returned functions. (You can use destructured assignment in JavaScript 1.7 to keep things clean.) The returned functions act just like PluralForm.get() and PluralForm.numForms(), except for the specified plural rule instead of the default plural rule.

Credits

Plural Form code first implemented for bug 394516 - Figure out a remaining-time rounding scheme for minutes -> hours/days
Plural rules and families derived from GNU gettext documentation.