docs(agents): pin the locale file layout and the stale-translation rule

Two things agents kept getting wrong while editing UI copy, both learned the
expensive way:

* `locales/*.json` come in two layouts: `en.json` (the reference, hand-maintained)
  is 2-space indented, the nine translated locales are 4-space (what
  `sync_translation_keys.py` writes). Loading one with `json.load` and dumping it
  back with the other indent reformats the whole file: changing two strings in the
  translated locales produced a 26k-line diff that had to be reverted and redone in
  place. The section now names both layouts, says to edit values in place, and adds
  the cheap guard that catches the whole class of mistake: after writing to an
  existing structured file, `git diff --stat` must match the change you meant to
  make.
* Changing an existing English string makes every other locale's value stale.
  Those values have to go back to `[TODO: Translate] <new English>` rather than
  keep describing the old behaviour until someone notices.
This commit is contained in:
Will Miao
2026-10-12 10:55:49 +08:00
parent b68af5400e
commit a750ca88ac
+14
View File
@@ -72,11 +72,25 @@ python scripts/sync_translation_keys.py
Locale files are in `locales/` (en, zh-CN, zh-TW, ja, ko, fr, de, es, ru, he).
`locales/*.json` keep a hand-maintained layout and **the two groups differ**: `en.json` (the
reference) uses **2-space indent**, the nine translated locales use **4-space indent** (what
`sync_translation_keys.py` writes). Both keep `ensure_ascii=False` and exactly one trailing newline.
Edit values **in place** (line/anchored text edit) and add keys with the sync script. Never
`json.load` + `json.dumps` a locale file just to change a string: the serializer rewrites the whole
layout, so a two-value edit lands as a several-thousand-line diff that has to be reverted and redone
(`tests/i18n/test_i18n.py::test_locale_files_keep_their_maintained_layout` now fails on that).
After writing to any existing structured file, check `git diff --stat`: the changed-line count must
match the change you meant to make.
After adding keys to `en.json` and syncing, **stop**: the `[TODO: Translate]` placeholders in
the other locales are the expected end state during feature development. Do NOT translate
proactively — translate only when the feature owner explicitly asks (see
`docs/i18n-translation-guidelines.md` §7).
Changing an **existing** English string is a translation event too: the other locales' value for
that key is now stale, so put it back in the queue (`[TODO: Translate] <new English>`) instead of
leaving a translation that describes the old behaviour.
**Before translating anything, read `docs/i18n-translation-guidelines.md`** — it defines the
term conventions (e.g. "Recipe" stays untranslated in French, 配方 in Chinese; model-type and
brand names are never translated), per-locale preferred renderings, placeholder rules, and