Get the Plural Rule for Your Locale with the JavaScript PluralRules Constructor

John Au-Yeung - Jan 23 '20 - - Dev Community

Subscribe to my email list now at http://jauyeung.net/subscribe/

Follow me on Twitter at https://twitter.com/AuMayeung

Many more articles at https://medium.com/@hohanga

Even more articles at http://thewebdev.info/

The plural rules for locales are different, and it’s difficult to be aware of all of them if we’re building apps for multiple languages. Luckily, there’s the Intl.PluralRules constructor in JavaScript where we can get the plural language rule for the locale you select and the type of plural rule for want to look up.

We can adjust the number of digits to use in our lookup, the number of whole number or fractional digits to use when we check the number. Also, we can select how many significant digits to use when looking up the plural rule for with the Intl.PluralRules constructor.

The Intl.NumberFormat constructor takes 2 arguments, first is the locales argument, which takes one locale string or an array of locale strings. This is an optional argument. It takes a BCP 47 language tag for the locale. An abridged list of BCP 47 language tags include:

  • ar — Arabic
  • bg — Bulgarian
  • ca — Catalan
  • zh-Hans — Chinese, Han (Simplified variant)
  • cs — Czech
  • da — Danish
  • de — German
  • el — Modern Greek (1453 and later)
  • en — English
  • es — Spanish
  • fi — Finnish
  • fr — French
  • he — Hebrew
  • hu — Hungarian
  • is — Icelandic
  • it — Italian
  • ja — Japanese
  • ko — Korean
  • nl — Dutch
  • no — Norwegian
  • pl — Polish
  • pt — Portuguese
  • rm — Romansh
  • ro — Romanian
  • ru — Russian
  • hr — Croatian
  • sk — Slovak
  • sq — Albanian
  • sv — Swedish
  • th — Thai
  • tr — Turkish
  • ur — Urdu
  • id — Indonesian
  • uk — Ukrainian
  • be — Belarusian
  • sl — Slovenian
  • et — Estonian
  • lv — Latvian
  • lt — Lithuanian
  • tg — Tajik
  • fa — Persian
  • vi — Vietnamese
  • hy — Armenian
  • az — Azerbaijani
  • eu — Basque
  • hsb — Upper Sorbian
  • mk — Macedonian
  • tn — Tswana
  • xh — Xhosa
  • zu — Zulu
  • af — Afrikaans
  • ka — Georgian
  • fo — Faroese
  • hi — Hindi
  • mt — Maltese
  • se — Northern Sami
  • ga — Irish
  • ms — Malay (macrolanguage)
  • kk — Kazakh
  • ky — Kirghiz
  • sw — Swahili (macrolanguage)
  • tk — Turkmen
  • uz — Uzbek
  • tt — Tatar
  • bn — Bengali
  • pa — Panjabi
  • gu — Gujarati
  • or — Oriya
  • ta — Tamil
  • te — Telugu
  • kn — Kannada
  • ml — Malayalam
  • as — Assamese
  • mr — Marathi
  • sa — Sanskrit
  • mn — Mongolian
  • bo — Tibetan
  • cy — Welsh
  • km — Central Khmer
  • lo — Lao
  • gl — Galician
  • kok — Konkani (macrolanguage)
  • syr — Syriac
  • si — Sinhala
  • iu — Inuktitut
  • am — Amharic
  • tzm — Central Atlas Tamazight
  • ne — Nepali
  • fy — Western Frisian
  • ps — Pushto
  • fil — Filipino
  • dv — Dhivehi
  • ha — Hausa
  • yo — Yoruba
  • quz — Cusco Quechua
  • nso — Pedi
  • ba — Bashkir
  • lb — Luxembourgish
  • kl — Kalaallisut
  • ig — Igbo
  • ii — Sichuan Yi
  • arn — Mapudungun
  • moh — Mohawk
  • br — Breton
  • ug — Uighur
  • mi — Maori
  • oc — Occitan (post 1500)
  • co — Corsican
  • gsw — Swiss German
  • sah — Yakut
  • qut — Guatemala
  • rw — Kinyarwanda
  • wo — Wolof
  • prs — Dari
  • gd — Scottish Gaelic

The second argument accepts an object with a few properties — localeMatcher, type, minimumIntegerDigits, minimumFractionDigits, maximumFractionDigits, minimumSignificantDigits, and maximumSignificantDigits .

The localeMatcher option specifies the locale matching algorithm to use. The possible values are lookup and best fit . The lookup algorithm search for the locale until it finds the one that fits the character set of the strings that are being compared. best fit finds the locale that is at least but possibly more suited that the lookup algorithm.

The type option let us select the type of plural rule to use. Possible values are cardinal and ordinal . cardinal refer to the quantity of things and is the default value. ordinal refers to ordinal numbers, which are for ordering and ranking items, like 1st, 2nd, and 3rd in English.

minimumIntegerDigits, minimumFractionDigits, and maximumFractionDigits are considered one group of options. minimumIntegerDigits specifies the minimum number of integer digits to use, ranging from 1 to 21, with 1 being the default option. minimumFractionDigits is the minimum number of fraction digits to use, ranging from 0 to 20. The default is 0 for plain number and percent formatting. The default for currency formatting is specified by the ISO 4217 currency code list, and 2 if it’s not specified in the list. maximumFractionDigits is the maximum number of fraction digits to use, with possible values ranging from 0 to 20. The default for a plain number is the maximum between minimumFractionDigits and 3. The default for currency formatting is the maximum between minimumFractionDigits and the number of fractional unit digits provided by the ISO 4217 currency code list or 2 if the list doesn't provide that information. The default for percent formatting is the maximum between minimumFractionDigits and 0.

minimumSignificantDigits and maximumSignificantDigits are considered as another group of options. If at least one of the options in this group is defined, then the first group is ignored. minimumSignificantDigits is the minimum number of significant digits to use, with possible values ranging from 1 to 21 with the default being 1. maximumSignificantDigits is the maximum number of significant digits to use, with possible values ranging from 1 to 21 with the default being 21.

Methods

The constructed object has a few methods. There are the select and resolvedOptions methods. The select method returns a string with the plural rule and takes a number as an argument. The returned rule is computed according to the values we set in the constructor like the locale, the type of plural rule to get and the number of digit options that are set. The resolvedOptions method returns an object with the options we selected for computing the plural rule with the locale and collation options during the initialization of the object.

For example, we can use the select method to get the plural rule for English as follows:

const rule = new Intl.PluralRules('en', {
  type: 'ordinal'
}).select(1);
console.log(rule);

If the code above is run, we get ‘one’ logged. We can do this for other numbers like in the following code:

const rule0 = new Intl.PluralRules('en', {
  type: 'ordinal'
}).select(0);
console.log(rule0);
// other

const rule2 = new Intl.PluralRules('en', {
  type: 'ordinal'
}).select(2);
console.log(rule2);
// two

const rule3 = new Intl.PluralRules('en', {
  type: 'ordinal'
}).select(3);
console.log(rule3);
// few

const rule4 = new Intl.PluralRules('en', {
  type: 'ordinal'
}).select(4);
console.log(rule4);
// other

const rule5 = new Intl.PluralRules('en', {
  type: 'ordinal'
}).select(5);
console.log(rule5);
// other

const rule6 = new Intl.PluralRules('en', {
  type: 'ordinal'
}).select(6);
console.log(rule6);
// other

We can also use this for non-English locales, like in the following code:

const rule0 = new Intl.PluralRules('fr', {
  type: 'cardinal'
}).select(0);
console.log(rule0);
// one

const rule1 = new Intl.PluralRules('fr', {
  type: 'cardinal'
}).select(1);
console.log(rule);
// one

const rule2 = new Intl.PluralRules('fr', {
  type: 'cardinal'
}).select(2);
console.log(rule2);
// two

const rule3 = new Intl.PluralRules('fr', {
  type: 'cardinal'
}).select(3);
console.log(rule3);
// few

const rule4 = new Intl.PluralRules('fr', {
  type: 'cardinal'
}).select(4);
console.log(rule4);
// other

const rule5 = new Intl.PluralRules('fr', {
  type: 'cardinal'
}).select(5);
console.log(rule5);
// other

const rule6 = new Intl.PluralRules('fr', {
  type: 'cardinal'
}).select(6);
console.log(rule6);
// other

With the returned string, we can map it to the suffix for the corresponding plural rule if needed so we can append the correct suffix to the given word or number.

To use the resolvedOptions method, we can write something like the following:

const options = new Intl.PluralRules('en', {
  type: 'ordinal',
  maxiumSignificantDigits: 1
}).resolvedOptions()

console.log(options);

When we run the above, we get the following from the console.log statement above:

{
  "locale": "en",
  "type": "ordinal",
  "minimumIntegerDigits": 1,
  "minimumFractionDigits": 0,
  "maximumFractionDigits": 3,
  "pluralCategories": [
    "few",
    "one",
    "two",
    "other"
  ]
}

We see all the options we set in the constructor and the pluralCategories property for the possible plural rules for the locale.

The plural rules are different for different locales. It’s hard to be aware of all of them if we’re building apps that are aware of different locales.

Fortunately, there’s the Intl.PluralRules constructor in JavaScript where we can get the plural language rule for the locale you select and the type of plural rule for want to look up.

We can adjust the number of digits to use in our lookup, the number of a whole number or fractional digits to use when we check the number. Also, we can select how many significant digits to use when looking up the plural rule for the Intl.PluralRules constructor.

We can use the plural rules to map to the correct suffix for words with the given quantity.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .