Plural Rules & ICU MessageFormat

See how any language pluralises any number, plus why “n === 1 ? singular : plural” is wrong in most of them and why Russian treats 21 as singular.

Every category here is read from your browser's own Intl data, which follows CLDR — so it is the same table a translation framework would use, not a copy of one. Nothing is uploaded.

"n === 1 ? singular : plural" is wrong in most languages

Across the 37 languages below, the familiar one-or-many test reproduces the real rule in 13 and fails in 24. Every language it matches has exactly two categories; the rest need between one and six.

CategoriesLanguagesWhich
1 6 Indonesian, Japanese, Chinese, Korean, Thai, Vietnamese
2 18 English, German, Dutch, Swedish, Danish, Norwegian Bokmål, Italian, Spanish, Catalan, Portuguese, French, Hindi, Turkish, Hungarian, Finnish, Greek, Icelandic, Persian
3 3 Hebrew, Latvian, Romanian
4 6 Russian, Ukrainian, Polish, Czech, Lithuanian, Slovenian
5 2 Maltese, Irish
6 2 Arabic, Welsh

When the naive rule fails it fails early — every failing language disagrees at some number at or below 21, so this is not a corner case waiting in the tail. Japanese, Chinese, Korean, Thai and Vietnamese make no grammatical number distinction at all, which means an interface that pluralises by branching on the count is inventing a distinction those languages do not have.

The proportion is worth reading carefully rather than quoting. This list was chosen to span the plural systems, from one category to six, rather than sampled at random — so it says the naive rule covers one family shape and misses the others, not that some exact fraction of the world's languages defeat it.

The "one" category is not the number one

The categories are grammatical, not arithmetic. Russian uses its "one" form for 9 of the first hundred integers — every number ending in a single 1 except 11 itself. French and Hindi extend theirs to cover zero.

LanguageIntegers 0–100 in the "one" categoryHow many
English 1 1
Russian 1, 21, 31, 41, 51, 61, 71, 81, 91 9
Polish 1 1
French 0, 1 2
Hindi 0, 1 2
Arabic 1 1
Welsh 1 1

So a rule keyed on the value is the wrong shape however many branches you give it. In Russian, 21 takes the same form as 1 and 11 does not — a pattern no amount of if (n === 1) reproduces. The count has to be handed to the language's own classifier and the answer used to pick a branch.

Ordinals are a different rule set again

English has two cardinal categories and 4 ordinal ones — which is exactly the st/nd/rd/th pattern usually hand-written as a switch on the last digit. Reading it from the ordinal rules gets the teens right for free.

NumberCategoryCorrectLast-digit rule gives
1 one 1st
2 two 2nd
3 few 3rd
4 other 4th
11 other 11th 11st
12 other 12th 12nd
13 other 13th 13rd
21 one 21st
22 two 22nd
23 few 23rd
101 one 101st
111 other 111th 111st

A switch on the final digit gets 4 of these wrong: 11, 12, 13, 111. The first three are the well-known teens. The fourth is 111, which catches the same bug a second time at a different scale, because the real rule keys on the last two digits rather than the last one.

What a message actually needs

An ICU message needs one arm per category the target language uses, which means an English source string does not carry enough branches for its own Russian translation. This is the practical consequence: the count of branches is a property of the target, not the source.

LanguageMessage skeleton
English {count, plural, one {…} other {…}}
Russian {count, plural, one {…} few {…} many {…} other {…}}
Arabic {count, plural, zero {…} one {…} two {…} few {…} many {…} other {…}}
Japanese {count, plural, other {…}}

How to use

  1. Pick a language and type a number.
  2. See which grammatical category that number falls in.
  3. Compare it against the naive one-or-many rule.
  4. The table shows every integer in every category.

Frequently asked questions

Is “n === 1 ? singular : plural” good enough?

Not in most languages. Across the thirty-seven checked here it reproduces the real rule in thirteen and fails in twenty-four. Every language it matches has exactly two grammatical number categories, and the rest have between one and six.

How many plural forms can a language have?

Between one and six. Japanese, Chinese, Korean, Thai and Vietnamese make no grammatical number distinction at all, English and most of western Europe have two, Polish and Russian have four, and Arabic and Welsh have six.

Why does Russian treat 21 as singular?

Because the categories are grammatical rather than arithmetic. Russian uses its “one” form for every number ending in a single 1 except 11 itself, which is nine of the first hundred integers: 1, 21, 31, 41 and so on. No amount of checking whether the value equals one reproduces that.

Does any language treat zero as singular?

Yes. French and Hindi both put 0 in the “one” category, so “0 result” is correct French where “0 results” is correct English. This is why the count has to be handed to the language’s own classifier rather than compared against a number.

Are ordinals the same rules?

No, they are a separate rule set. English has two cardinal categories and four ordinal ones, which is exactly the st/nd/rd/th pattern. Reading it from the ordinal rules gets 11th, 12th and 13th right automatically.

What is wrong with switching on the last digit for ordinals?

It gets the teens wrong, and it gets them wrong at every scale. The real rule keys on the last two digits, so a last-digit switch produces 11st, 12nd and 13rd — and also 111st, which catches the same bug again a hundred later.

Why does my English source string not translate cleanly?

Because the number of message branches is a property of the target language, not the source. An English string carries two arms and a Russian translation needs four, so there is nowhere for the translator to put the missing forms unless the message format allows them.

Where does this data come from?

Your own browser, through the Intl.PluralRules API, which follows the CLDR data that translation frameworks use. Nothing here is a hand-maintained table, so it stays correct as that data is updated rather than drifting away from it.

Does this send anything anywhere?

No. Everything is computed in your browser, and nothing is uploaded.

🔒 This tool runs entirely in your browser. Nothing you enter is uploaded, logged, or stored.