Why CLDR Plural Rules and Gettext Don’t Agree (and Why It Matters for AI Localization)
CLDR and Gettext both handle pluralization, but they model it differently. Using English, Cantonese, and Russian as examples, this article explores where those models diverge—and why that matters when AI is generating localization files.
My interests change from time to time, but one topic I have never lost interest in is how to properly internationalize the apps and websites that I build. I am always dissatisfied with the tools I have used and have written scripts to make things better. For years, I have wanted to create my own solution to this problem, and Forthwith is that solution.
One subtopic that fits nicely at the intersection of my interests in languages and software development is plural forms in languages.
Languages vary in how they handle plural forms. English is simple. We only have two forms: one and other. If you think about it for a few moments, you will see that it is true. Examples:
- "I have one pizza."
- "I have zero/two/infinity pizzas."
Other languages are more complicated, and there are different ways to represent these forms. In this article, I will briefly introduce you to CLDR and Gettext, after which I will explain why they don't always match and why this mismatch matters for AI translation.
CLDR
First up is CLDR. CLDR stands for Common Locale Data Repository, and it serves as a collection of data to be used by software to support languages and their regional variations. CLDR includes a Plural Rules specification that defines how languages handle quantities. The available categories are zero, one, two, few, many, and other.
We've already seen that English only needs the one and other categories. Cantonese requires only the other category in CLDR's cardinal plural rules:
- 15 本書
- 1.5 本書
Russian is more complicated than either and needs to support one, few, many, and other. one is applied to integers ending in 1, except for 11. Example: 1 книга (1 kniga - 1 book). few applies to integers ending in 2, 3, or 4, except for 12, 13, and 14. many applies to 0, integers ending in 0 or 5-9, as well as those whose last two digits are 11-14. other is used for values with visible fractional digits, such as 1.5—and even 1.0.
Gettext
Gettext, on the other hand, has a completely different model for representing plurals. While CLDR gives plural forms semantic names (zero, one, two, few, etc.), Gettext gives them numbered slots selected by a formula. These slots and the formula are defined in the translation file's Plural-Forms header.
English
The header for English is:
Plural-Forms: nplurals=2; plural=n != 1;
Let's break this down:
Plural-Formsis the header key that tells Gettext which plural rules the strings in this file follow.nplurals=2tells Gettext that there are two plural forms in this language.plural=n != 1;evaluates to false (or0) whennis1, so Gettext usesmsgstr[0], the first slot, forone. For every other integer,n != 1is true (or1), somsgstr[1]is used.
Cantonese
Cantonese is even easier. Because Gettext needs only one form for numeric quantities in Cantonese, its header can look like this:
Plural-Forms: nplurals=1; plural=0;
This forces Gettext to use msgstr[0] for every number.
Russian
Russian is where things get interesting. You might expect the Plural-Forms header to look like this:
Plural-Forms: nplurals=4; plural={complicated-rules-here}
But in fact, the standard Gettext plural rule for Russian uses only three forms, so the header looks like this:
Plural-Forms: nplurals=3;
plural=n%10==1 && n%100!=11 ? 0 :
n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 :
2;
If you don't love math and logic-based formulas, in English this boils down to the following:
msgstr[0] → numbers ending in 1, except 11
msgstr[1] → numbers ending in 2–4, except 12–14
msgstr[2] → everything else
If we line that up with the CLDR categories, the mismatch becomes obvious:
CLDR Gettext
one → msgstr[0]
few → msgstr[1]
many → msgstr[2]
other → no standard ngettext slot
So what happened to CLDR's other category?
The missing other category is where the difference between the two models becomes important. In CLDR, Russian uses other for values with visible fractional digits, such as 1.5 and even 1.0. Gettext's standard ngettext plural-selection model, on the other hand, is based on integer counts.
CLDR and Gettext aren't simply two different syntaxes for describing the same thing. They model pluralization differently. CLDR defines semantic categories and explicitly accounts for fractional values, while Gettext's standard plural-selection mechanism maps integer counts directly to numbered translation slots.
And this is where we get to fulfilling my next promise: why does this mismatch matter for AI translation?
While an LLM may be perfectly good at translating English to Russian, if you ask it to convert a CLDR-style localization containing one, few, many, and other into a Gettext catalog, a naïve conversion may try to create four Gettext plural forms, even though the standard Russian Gettext representation only has three.
And that is the important distinction. The LLM did not necessarily translate anything incorrectly. It may have produced perfectly good Russian. The problem is that a linguistically correct translation can still be structurally wrong for the localization system that has to consume it.
This problem extends far beyond plural forms. Localization files contain placeholders, interpolation variables, format specifiers, markup, escape sequences, and framework-specific structures that need to survive translation intact. An LLM can understand the meaning of a sentence while still producing output that violates one of those constraints.
That is one of the lessons I have learned while building Forthwith: AI makes translation dramatically easier, but it does not make localization correctness automatic.
The more localization systems I support, the more convinced I become that translating the words is only half of the problem. The other half is understanding the rules of the system those words have to live in—and validating that the translated output still follows them.