Skip to content

Structured locale leaves (non-English) ​

locales/en.json stays plain strings at every leaf — it is the source of truth for copy and key shape.

Each non-English file (locales/<lang>.json) uses structured leaves at the same dot paths:

json
{
  "value": "Translated text",
  "status": "translated",
  "confidence": 0.88,
  "needsReview": false,
  "source": "google-translate",
  "updatedAt": "2026-04-04T12:00:00.000Z"
}

Fields ​

FieldTypeMeaning
valuestringThe string passed to i18next at runtime (after stripping; see below).
status"pending" | "translated"pending — synced from English or not yet machine-translated; translated — filled or generated with a completed translation step.
confidencenumber | nullHeuristic 0–1 from generate/fill (null until scored).
needsReviewbooleanFlag for QA (e.g. low confidence or EN-identical output).
sourcestringHow value was produced (see below).
updatedAtstring (optional)ISO-8601 when the leaf was last written by tooling.

source values ​

ValueWhen
google-translateMachine translation from generate/fill.
copy-enVerbatim English (preserve-as-en keys, or English copy from sync for new paths).
sync-defaultReserved for future use when sync only patches metadata.
manualHand-edited (convention for future editor flows).
unknownLegacy string wrapped by sync before meta was added.

Runtime ​

src/i18n/index.ts loads locale JSON and passes bundles through stripLocaleBundleToStrings() so i18next only sees plain strings at leaves. Legacy files that still use bare strings are unchanged by the strip step.

Tooling ​

CommandRole
pnpm locales:syncAligns key shape to en.json, copies English into new paths as structured leaves, and merges missing meta keys on existing leaves.
pnpm locales:generate / pnpm locales:fillWrites value + meta after each translation API call.
pnpm locales:reviewRead-only aggregates over meta (no en.json comparison).
pnpm locales:validate:qualityParity vs en.json and English-identical detection (uses string values from leaves).

Shared types and helpers live in apps/web/scripts/locales/shared/localeLeaf.ts.