Skip to content

Localization Overview

Architecture

Translations live in packages/i18n/locales/ as JSON files, organized by language-country directory:

locales/
├── en/ ← Source locale (ground truth)
│ ├── common.json
│ ├── commerce.json
│ ├── account.json
│ └── legal.json
├── nl-NL/
│ ├── common.json
│ └── ...
├── de-DE/
└── ...

Source locale: en

English (en/) is the universal source. All other locales are translations FROM English. The source directory has no country suffix — it’s the canonical reference.

English variants like en-GB or en-IE are treated as regular target locales (not source).

Namespaces

NamespaceContent
commonUI chrome: buttons, navigation, labels, errors, empty states
commerceCart, checkout, shipping, payment, orders, returns
accountLogin, register, profile, addresses, preferences
legalCookie consent, privacy policy, terms & conditions

Each namespace is a separate JSON file per locale.

Placeholder syntax

One format: {name} (curly braces).

{
"empty.no_results": "No results found for \"{query}\""
}

Forbidden formats: {{name}}, %s, %d. The check script validates placeholder parity between source and target.

Workflow

  1. Add or change keys in en/ (the source)
  2. Translate to target locales
  3. Run pnpm i18n:check to validate
  4. Run pnpm i18n:report to update the coverage page

See Add a String for step-by-step instructions.

Tooling

CommandPurpose
pnpm i18n:checkValidate all locales (missing keys, placeholders, empty values)
pnpm i18n:reportGenerate coverage report for docs