Documentation

File formats

Eight formats, and how plurals are handled in each.

FormatExtensionUsed by
JSON.jsonWeb, React, Vue, Node.js, i18next
ARB.arbFlutter, Dart
Android XML.xmlAndroid
Apple Strings.stringsiOS, macOS
Strings Dictionary.stringsdictiOS, macOS — plurals
String Catalog.xcstringsiOS 16+, macOS 13+
Gettext PO.poWordPress, Django, PHP
YAML.yaml / .ymlRuby on Rails, i18next

Plurals

Plural forms are not translated as ordinary strings. ParlanceKit extracts them, asks the model for every plural category the target language requires, and writes them back in the shape the format expects.

This matters because languages disagree about how many forms exist. English has two. Russian has four categories in CLDR: one, few, many, and other. Arabic has six. A source file with a singular and a plural will produce all four Russian forms, correctly inflected — not two.

Plural categories are generated for the target language, never assumed from the source. This holds regardless of what language your source file is written in.

This only works if your source expresses plurals as plurals

The generation above depends on your source file marking a string as a plural in the way your format supports: i18next _one / _other suffix keys, an Android <plurals> element, a .stringsdict entry, an ICU plural in an .arb file, or nested one: / other: keys in Rails YAML.

A flat string that merely contains a number — "{{count}} items", "%d files", "5 seats" — is not a plural to any tool, including this one. It is translated once, as a single string. For English-to-German that is fine; both have the same two forms. For a language with more forms than your source expresses, it is not: the one translation you get back is grammatically correct only for the counts it happens to fit, and wrong for the rest. Arabic reads correctly for some counts and wrong for others, and nothing in the output tells you which.

A flat count string cannot be turned into correct plural forms after the fact — the information the other forms need is not in it. Express countable strings as plurals in your source, using your format's plural syntax, before translating to a language that needs more forms than your source language has.

You do not have to catch this by eye. parlancekit lint flags a flat string that looks like a count when your target languages need more plural forms than your source expresses, and points you at the plural syntax for your format. The dashboard runs the same check before a translation and warns you before you spend anything on a run that would be wrong.

Format-specific behaviour

String Catalogs (.xcstrings)

A catalog contains every language in a single file. ParlanceKit updates it in place, adding each target language's localizations alongside your source. Plurals nested inside substitutions or device variations are handled too.

Gettext (.po)

PO files use positional plural slots — msgstr[0], msgstr[1] — and the number of slots comes from the language's gettext rule, which is not always the same as its CLDR category count. ParlanceKit writes the correct number of slots and the correct Plural-Forms header.

ARB (.arb)

ICU plural syntax is parsed, expanded to the target's categories, and rebuilt. Surrounding prose, offsets, and exact matches such as =0 are preserved.

Placeholders

Every common placeholder syntax is protected before the text reaches the model and restored afterwards: {name}, {{count}}, %d, %@, %lld, %1$s, %{name}, %(name)s, %<n>d, and Apple's %#@variable@. A literal %% is preserved as a literal percent.

If a translation comes back with a placeholder missing or altered, ParlanceKit retries that string once. If it is still wrong, the string is left untranslated rather than written broken.