Guides / 7 of 14

XLIFF plurals: how each format stores them

XLIFF 1.2 and 2.0 have no plural element. How gettext, Angular ICU messages and Xcode exports carry plural forms through XLIFF anyway, what XLIFF 2.2 adds, and what a translator has to check.

“1 file, 2 files” is one plural rule. Russian has more, Arabic has six, and Japanese makes no distinction at all. Every localisation format needs a way to carry those forms, and XLIFF, the format built for carrying translations, did not have one until very recently. What you find in exported files is a set of conventions layered on top, and each needs handling differently.

The categories every format is mapping to

Unicode’s CLDR names six plural categories: zero, one, two, few, many and other. Every language needs other. The categories are defined by how a sentence changes when the number changes, not by traditional grammar, so one is not always exactly 1. Arabic uses all six; some languages, such as Japanese, Korean, Vietnamese and Thai, use a single form.

That is why plural forms cannot be translated one to one. An English string has two forms; a Polish gettext file has three, and a Japanese one has one. Any format that fixes the number of forms to the source language’s count is wrong for most target languages.

XLIFF 1.2: no plural element

The XLIFF 1.2 standard defines no plural element or attribute; the word “plural” does not appear in it. It does let tools invent their own restype values, as long as they start with x-, and that is where the best-known convention comes from.

A separate OASIS committee guide for mapping gettext PO files to XLIFF, which only reached Committee Draft stage in 2006, turns each PO plural entry into a group with one unit per plural form of the target language. Simplified, for Polish:

<group restype="x-gettext-plurals">
  <trans-unit id="1[0]">
    <source>%d file</source>
    <target>%d plik</target>
  </trans-unit>
  <trans-unit id="1[1]">
    <source>%d files</source>
    <target>%d pliki</target>
  </trans-unit>
  <trans-unit id="1[2]">
    <source>%d files</source>
    <target>%d plików</target>
  </trans-unit>
</group>

It is widely used and still not part of the standard, so a tool that knows XLIFF 1.2 perfectly may treat those units as three unrelated strings.

XLIFF 2.0 and 2.1: still none

Neither 2.0 nor 2.1 has a plural module; their module lists contain no plural feature. Plural support arrives in XLIFF 2.2 as the Plural, Gender, and Select Module. At the time of writing, the latest 2.2 publication is a Committee Specification dated 13 March 2025, not yet a full OASIS Standard. It adds two attributes in the namespace urn:oasis:names:tc:xliff:pgs:1.0: pgs:switch on a <unit> names the variable, and pgs:case on each <segment> names the plural category or number that segment covers. In the module’s own example, an ICU plural message becomes one unit with one segment per case.

Angular: ICU inside the segment

Angular keeps the whole ICU plural expression inside a single unit’s source and target:

{count, plural, =0 {no files} one {one file} other {{{count}} files}}

Translators translate the text inside each branch and may add or remove plural cases for the target language. The keywords, count, plural, one, other, must stay as they are. Angular’s ng extract-i18n writes XLIFF 1.2 or XLIFF 2, both as .xlf. When a select or nested expression is extracted as a unit of its own, the surrounding unit keeps a placeholder for it, and deleting that placeholder removes the expression from the app.

Xcode: one unit per category

When Xcode exports localisations, it adds the right plural variants for each target language to the XLIFF file. Translators fill in one translation per plural category, and Xcode rebuilds the plural resource on import. From Xcode 15, string catalogs (.xcstrings) replace .stringsdict files for plurals; for Russian, Xcode adds One, Few, Many and Other variants. Apple’s older .stringsdict page lists a different set for Russian, which is a reminder to trust the variants the export actually contains, not a table in a document. The Xcode page covers translating these files.

gettext PO: forms counted by the header

A PO plural entry has msgid (singular), msgid_plural (plural) and one msgstr[n] line per plural form of the target language. How many forms there are, and the formula that picks one, live in the header’s Plural-Forms line, where nplurals gives the count. A language with a single form has nplurals=1.

What a translator has to check

  • The number of forms matches the target language, not the source. Two English forms becoming two Polish forms means one form is missing.
  • ICU keywords and selectors are never translated. plural stays plural and one stays one, and placeholders inside a branch stay exactly as written. Only the words inside the branches change.
  • Each form reads correctly with its numbers. Read few with 2, 3 or 4, and many with 5 or 11, in the languages that have them.
  • No form is left empty. In gettext an empty translation falls back to the source text at runtime, which is the bug users see.

How xliff.ai handles plurals

Being precise about this is more useful than a feature list:

  • PO files get one row per msgstr[n] form the entry has, each labelled with its form number in the details. The first form’s source is msgid; the others show msgid_plural. The editor reads nplurals from the header and warns when an entry has fewer forms than it says, but it cannot add forms. A .pot template always gives two forms, because a template does not know its language yet; to translate into Russian, Polish or Arabic, start from a .po whose header already declares the right nplurals, not from a renamed .pot. See PO translation.
  • XLIFF plural conventions (gettext groups, Xcode variants) open as ordinary rows, one per unit. They are not grouped or labelled as plural categories, so the unit IDs are your guide to which row is which form.
  • ICU messages are not parsed. QA does not check that the selector keywords survived, so check those by eye; the placeholder check still catches a missing {count} inside a branch.

Plurals are where “the file imported fine” and “the app reads correctly” part company. Ten minutes reading each form against real numbers is cheaper than the bug report.