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.

locale/en.json
{
  "menu": {
    "home": "Home",
    "projects": "Projects"
  },
  "checkout": {
    "title": "Order summary",
    "items": "{count, plural, one {# item} other {# items}}"
  }
}
Nested keys and an ICU plural, the two things naive tooling usually breaks.
Download a sample: sample_de.json

Cheatsheet

JSON translation syntax in five rows

You writeWhat it means
"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.

nested
"menu": {
  "home": "Home",
  "projects": "Projects"
}
flat
"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.

exported empty
"title": "Bestellübersicht",
"confirm": ""
left out
"title": "Bestellübersicht"
source fallback
"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.

LibraryConventional path
i18next public/locales/{lng}/translation.json
next-intl messages/en.json
react-intl lang/en.json, flat keys with ICU values
vue-i18n src/locales/en.json
ngx-translate 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.

en · two keys
"item_one": "{{count}} item",
"item_other": "{{count}} items"
pl · four keys
"item_one": "{{count}} artykuł",
"item_few": "{{count}} artykuły",
"item_many": "{{count}} artykułów",
"item_other": "{{count}} artykułu"
ar · six keys
"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.

en · two slots
"cars": "car | cars"
pl · three slots, custom rule
"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.

en · two branches
"cartItems": "{count, plural,
  one {# item}
  other {# items}}"
pl · four branches
"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.

en · two forms
"items": "{count, plural, one {# item} other {# items}}"
pl · four forms
"items": "{count, plural,
  one {# artykuł}
  few {# artykuły}
  many {# artykułów}
  other {# artykułu}}"
ar · six forms
"items": "{count, plural,
  zero {لا عناصر} one {عنصر واحد}
  two {عنصران} few {# عناصر}
  many {# عنصرًا} other {# عنصر}}"

One string carries every branch; the file stays one key per message.

en
"items": {
  "one": "# item",
  "other": "# items"
}
pl
"items": {
  "one": "# artykuł",
  "few": "# artykuły",
  "many": "# artykułów",
  "other": "# artykułu"
}
ar
"items": {
  "zero": "لا عناصر",
  "one": "عنصر واحد",
  "two": "عنصران",
  "few": "# عناصر",
  "many": "# عنصرًا",
  "other": "# عنصر"
}

CLDR categories as subkeys, spelled out per language.

en
"items": ["# item", "# items"]
pl
"items": [
  "# artykuł",
  "# artykuły",
  "# artykułów",
  "# artykułu"
]
ar
"items": [
  "لا عناصر", "عنصر واحد", "عنصران",
  "# عناصر", "# عنصرًا", "# عنصر"
]

Forms in CLDR order with no labels; only position says which form is which.

en · the only shape it has
"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.

en · two keys
"item_one": "{{count}} item",
"item_other": "{{count}} items"
pl · four keys
"item_one": "{{count}} artykuł",
"item_few": "{{count}} artykuły",
"item_many": "{{count}} artykułów",
"item_other": "{{count}} artykułu"
ar · six keys
"item_zero": "لا عناصر",
"item_one": "عنصر واحد",
"item_two": "عنصران",
"item_few": "{{count}} عناصر",
"item_many": "{{count}} عنصرًا",
"item_other": "{{count}} عنصر"

Modern i18next: one suffixed key per CLDR category.

en
"items": "# item|# items"
pl
"items": "# artykuł|# artykuły|# artykułów|# artykułu"
ar
"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

nesting

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

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.

stability

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.

  1. Import the .xlf file; languages are read from its attributes.
  2. Review the translations that arrived with it.
  3. Export as JSON with your preferred nesting and plural style.

strings.xml to JSON

Android strings reused in a React Native or web build.

  1. Import strings.xml; plurals blocks become CLDR forms.
  2. Plurals convert to ICU messages automatically.
  3. Export as JSON, nested or flat.

JSON to YAML

The same tree, re-rooted for a Rails service.

  1. Import the JSON file.
  2. Nothing to adjust; the key model is identical.
  3. Export as YAML with stable indentation.

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.