JSON
JSON translation files, from keys to plurals
JSON is the language file of the web. i18next, react-intl, Vue i18n and nearly every JavaScript framework read translations from it: a tree of keys and strings, one file per language. The format itself is minimal, which is exactly why the details matter: key structure, escaping, and how a plural fits into a format that has no plural syntax.
This page covers the syntax, the plural encodings that actually work, and the file conventions each JavaScript library expects.
{
"menu": {
"home": "Home",
"projects": "Projects"
},
"checkout": {
"title": "Order summary",
"items": "{count, plural, one {# item} other {# items}}"
}
} Cheatsheet
JSON translation syntax in five rows
"menu.home" or nested objects Both key styles import; the export setting decides which comes back. \" \\ \n \uXXXX Standard JSON escapes. A compliant writer emits them for you; nothing needs escaping by hand. {"one": "…", "other": "…"} A value that is a map of CLDR categories imports as a plural, decomposed into forms. Array encoded plurals import the same way. "{count, plural, …}" An ICU message in a value imports whole, every form intact in one string. Exports write plurals as ICU by default; category map and array styles are settings. // comments Not JSON. Files containing them are not valid JSON and comments cannot round trip; keep context in translator notes instead. Nested or flat, side by side
Same keys, two shapes; libraries differ on which they read.
"menu": {
"home": "Home",
"projects": "Projects"
} "menu.home": "Home",
"menu.projects": "Projects" Untranslated keys, three spellings
A file can represent a key German has not translated yet three ways: exported empty, left out entirely, or filled with the source language value.
"title": "Bestellübersicht",
"confirm": "" "title": "Bestellübersicht" "title": "Bestellübersicht",
"confirm": "Confirm purchase" Conventions
Where JSON translation files live
Every library has its own idea of the right folder. The names differ; the shape inside is the same.
public/locales/{lng}/translation.json messages/en.json lang/en.json, flat keys with ICU values src/locales/en.json src/assets/i18n/en.json
Typical flow: the repo holds locale/*.json, the
GitHub or
GitLab integration imports new keys on push,
and finished translations come back as a pull request touching only the strings
that changed.
i18next: CLDR suffixes on the key
One key per plural form, suffixed with the CLDR category. The file grows with the language: Polish gains _few and _many, Arabic uses all six.
"item_one": "{{count}} item",
"item_other": "{{count}} items" "item_one": "{{count}} artykuł",
"item_few": "{{count}} artykuły",
"item_many": "{{count}} artykułów",
"item_other": "{{count}} artykułu" "item_zero": "لا عناصر",
"item_one": "عنصر واحد",
"item_two": "عنصران",
"item_few": "{{count}} عناصر",
"item_many": "{{count}} عنصرًا",
"item_other": "{{count}} عنصر" i18next picks the key at runtime; to any tool reading the file these are ordinary separate keys. locamorph exports this dialect through the i18next-v4 plural format, one suffixed key per category.
vue-i18n: pipe slots
Forms separated by a pipe, selected by slot index rather than CLDR category.
"cars": "car | cars" "cars": "samochód | samochody | samochodów" Out of the box the slots mean 1 and everything else; languages like Polish or Russian only work after registering a custom pluralization rule that maps counts to slots.
ICU: the message carries the forms (react-intl, next-intl)
One key, one message, every CLDR branch inside the value. The only dialect where the file itself knows it holds a plural.
"cartItems": "{count, plural,
one {# item}
other {# items}}" "cartItems": "{count, plural,
one {# artykuł} few {# artykuły}
many {# artykułów} other {# artykułu}}" Because the plural lives in the value, it survives any tool that moves the file, which is why ICU is the interchange default.
Plurals
Plurals in JSON
The format has no plural syntax of its own, so files encode plurals inside plain JSON, and several encodings coexist in the wild. Pick an encoding and see the same plural in English, Polish and Arabic. Every encoding has a dedicated guide.
"items": "{count, plural, one {# item} other {# items}}" "items": "{count, plural,
one {# artykuł}
few {# artykuły}
many {# artykułów}
other {# artykułu}}" "items": "{count, plural,
zero {لا عناصر} one {عنصر واحد}
two {عنصران} few {# عناصر}
many {# عنصرًا} other {# عنصر}}" One string carries every branch; the file stays one key per message.
"items": {
"one": "# item",
"other": "# items"
} "items": {
"one": "# artykuł",
"few": "# artykuły",
"many": "# artykułów",
"other": "# artykułu"
} "items": {
"zero": "لا عناصر",
"one": "عنصر واحد",
"two": "عنصران",
"few": "# عناصر",
"many": "# عنصرًا",
"other": "# عنصر"
} CLDR categories as subkeys, spelled out per language.
"items": ["# item", "# items"] "items": [
"# artykuł",
"# artykuły",
"# artykułów",
"# artykułu"
] "items": [
"لا عناصر", "عنصر واحد", "عنصران",
"# عناصر", "# عنصرًا", "# عنصر"
] Forms in CLDR order with no labels; only position says which form is which.
"item": "{{count}} item",
"item_plural": "{{count}} items" The legacy v3 dialect: base key plus _plural. It only knows one and other, so Polish and Arabic cannot be expressed in it.
"item_one": "{{count}} item",
"item_other": "{{count}} items" "item_one": "{{count}} artykuł",
"item_few": "{{count}} artykuły",
"item_many": "{{count}} artykułów",
"item_other": "{{count}} artykułu" "item_zero": "لا عناصر",
"item_one": "عنصر واحد",
"item_two": "عنصران",
"item_few": "{{count}} عناصر",
"item_many": "{{count}} عنصرًا",
"item_other": "{{count}} عنصر" Modern i18next: one suffixed key per CLDR category.
"items": "# item|# items" "items": "# artykuł|# artykuły|# artykułów|# artykułu" "items": "لا عناصر|عنصر واحد|عنصران|# عناصر|# عنصرًا|# عنصر" Every form in one pipe separated string, in CLDR order.
The categories per language come from CLDR, not from the format: French counts 0 with one, Japanese has only other, Polish needs four forms and Arabic six.
See the same plural in every format, from Android plurals blocks to XLIFF groups.
In locamorph
How locamorph handles JSON
Flat or nested, your call
menu.home as a dotted key or as an object tree: both import
correctly, and the export setting decides which shape comes back out.
Plurals as ICU messages
JSON has no plural syntax, so locamorph carries all CLDR forms inside one ICU message. Nothing gets flattened to a single form on import.
Deterministic output
Indentation, key order and nesting follow project settings, not the last exporter. Diffs show changed strings, never a reshuffled file.
One honest limitation: JSON itself has no comments, so translator notes do not live in the file. Notes attached in the locamorph editor stay with the key and travel to formats that can hold them, like XLIFF or Android XML.
Convert
Convert JSON to and from other formats
Conversion is import plus export: locamorph maps everything onto one model of keys, values, plurals and notes, so any imported format exports as any other.
XLIFF to JSON
The classic agency handback into a web app.
- Import the
.xlffile; languages are read from its attributes. - Review the translations that arrived with it.
- Export as JSON with your preferred nesting and plural style.
strings.xml to JSON
Android strings reused in a React Native or web build.
- Import
strings.xml; plurals blocks become CLDR forms. - Plurals convert to ICU messages automatically.
- Export as JSON, nested or flat.
JSON to YAML
The same tree, re-rooted for a Rails service.
- Import the JSON file.
- Nothing to adjust; the key model is identical.
- Export as YAML with stable indentation.
Related
Keep going
YAML
The same key trees for Rails apps, with the same ICU plural handling.
ARB
Flutter’s JSON dialect with metadata and native ICU plurals.
GitHub integration
Keep locale JSON in the repo and let translations arrive as pull requests.
Further reading
- RFC 8259, the JSON specification
- ICU MessageFormat, the plural syntax carried in values
- i18next documentation, the most common consumer of these files
Drop in your en.json
Import a JSON file on the free plan and see your keys, plurals and nesting arrive intact. Exports match your formatting settings from the first byte.