Stringalize
← All articles

Plural forms in app localization: why singular and plural are not enough

· 7 min read

"You have 1 new messages." Everyone has seen an app say something like this, and most developers know the fix: one string for a single item, another for several. That fix is correct for English. It is wrong for most of the world's languages, and the ways in which it is wrong are worth knowing before you translate an app.

English is the simple case

English has two forms: one for exactly 1, another for everything else, zero included ("0 messages"). So do German, Dutch, Spanish and many others, which is why the two-string approach feels universal. It is not.

Six categories, and each language picks its own

The Unicode CLDR project, which every major platform follows, describes plural rules with six named categories: zero, one, two, few, many and other. No language uses all of them for the same thing, and most use only a few.

LanguageCategoriesExamples
Japanese, Chinese, KoreanotherOne form for every number
English, Germanone, other1 file · 2 files
Frenchone, other0 and 1 are both one: 0 fichier · 2 fichiers
Russianone, few, many, other1 файл · 2 файла · 5 файлов
Polishone, few, many, other1 plik · 2 pliki · 5 plików
Arabicall sixSeparate forms for 0, 1, 2, 3–10, 11–99 and the rest

"one" does not mean 1

This is the part that surprises people. The category names are labels, not numbers. In Russian the one form is used for 1, but also for 21, 31, 101 and every other number ending in 1 except those ending in 11. The few form covers numbers ending in 2, 3 or 4, except 12, 13 and 14. Everything else that is a whole number is many.

So 21 files takes the same form as 1 file, and 5 takes the same form as 11 and 100. A check such as if (count == 1) cannot express this, and neither can any rule written by someone who does not speak the language. The rules are data. Let the platform apply them.

other is the fallback that every language has. In languages with several forms it is typically the one used for fractions ("1,5 файла"), and every platform requires it to be present.

How each platform stores plurals

Android

A plurals resource, one item per category:

<plurals name="messages">
    <item quantity="one">%d message</item>
    <item quantity="other">%d messages</item>
</plurals>

Note that the quantities follow the grammar of the language, not the number. An English resource with a zero item will never use it, because English has no zero category: 0 is other. To show "No messages", test for zero in code.

iOS

Plurals cannot live in a .strings file. They go in a .stringsdict file next to it, a property list with one entry per category, or in a String Catalog in recent versions of Xcode. Forgetting the .stringsdict when files are passed around is a common way to lose plurals entirely.

Gettext

A PO file holds the forms as a numbered list, msgstr[0], msgstr[1] and so on, and its header carries a formula that turns a number into an index. The number of forms and their order differ per language, and they do not always line up with the CLDR categories: a Russian PO file has three forms where Android and iOS expect four.

JSON for the web

There is no standard. A widespread convention, used by i18next, adds the category to the key: messages_one, messages_other.

Mistakes worth avoiding

  • Building the sentence from pieces. count + " " + word assumes the number comes first and that only one word changes. In many languages the verb or adjective changes too. Translate the whole sentence for each form.
  • Asking translators for "singular and plural". A Polish translator needs four boxes to fill in, not two. A Japanese translator needs one. The tool has to show each language its own set.
  • Leaving out other. It is the fallback everywhere. A plural without it is broken on every platform.
  • Using a plural for a count you do not show. "Delete these files?" depends on a number, but in some languages the form is chosen by rules that make no sense without the number in the sentence. Reword it, or show the count.
  • Dropping the number from every form. "One file" instead of "1 file" reads well in the one form of English. In Russian that same form also serves 21 and 31, where "one file" would be wrong.

Keeping it manageable

The work is in the bookkeeping: knowing which forms each language needs, noticing when one is missing, and writing them out in the shape each platform expects. That is mechanical, and it is the kind of thing a tool should do.

In Stringalize a string is marked as pluralised once. Each language then shows exactly the forms its grammar requires, a missing form is flagged, and exports produce Android plurals, an iOS .stringsdict, gettext plural entries or suffixed JSON keys from the same data.