The command splits $1/$2/$3 off the invocation positionally and then states them as fact, so calling it with a sentence rather than `/add-string <Section> "<Key>"` yields three arbitrary words - and nothing downstream notices. Ask for them to be checked against en_US.ini first, and to re-derive the real section and key from the request if they don't hold up. Also spell out, in both the command and docs/translations.md, that the en_US line in the scratch file overwrites en_US.ini like any other language, so for an existing key it has to match the current English text exactly - otherwise it quietly rewords the string every other language was translated from. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GgACRqkQNpfJQ4fjwyoEup
2.9 KiB
Translated UI strings (assets/lang)
When implementing new UI, translations come last, in their own commit after everything else is done. Write the English strings, get the feature built and working, commit that - then stop and ask the user to check the English wording before translating anything. The English string is what all ~47 languages get derived from, so rewording it afterwards means redoing the whole sweep.
One .ini file per language, keyed by section and key against assets/lang/en_US.ini. Don't hand-edit
the ~47 files, and don't run the AI commands in Tools/langtool either - you can read the call site,
which its fixed prompt can't. Do the translating yourself and let the tool do the file surgery. Run it
from Tools/langtool:
- Work out what the string actually means before translating it: find where it's used in the C++,
what any
%1/%dplaceholders get substituted with, how long it can be without breaking the layout, and how neighboring keys are already phrased in each language (that's your style guide). - Write a scratch file, one line per language, named after the ini file minus the extension, with
the English string under
en_US:For a key that already exists, the[Single] en_US = Test string sv_SE = Teststräng lt-LT = Testeilutėen_USline has to be the current English text character for character - it overwritesen_US.inilike any other language, so a stray reword there quietly changes the string all the others were just translated from. Diffen_US.iniafterwards to check it didn't move. No trailing# commentson those lines, they'd end up inside the translation. Placeholders have to survive verbatim. If you don't know a language well enough, leave it out - a key that's missing from a language file falls back to the English string at runtime, which is much better than a confident guess. If a language deliberately keeps the English string (a term like "Vsync" that isn't translated), do include it with the English text - it gets written with a# same as Englishcomment, which stops langtool from trying to translate it again on every later run. cargo run -- import-single <scratch-file> <Section> "<Key>"writes them all in, including a new key in en_US.ini, tagged# AI translatedexcept for the en_US line, which is the string the rest were translated from. Note it overwrites existing values for that key, so take care with keys that already have human translations, and that the section has to exist already - langtool won't create one.cargo run -- validateat the end, always. It checks that placeholders survived and exits non-zero if anything is off.
Optionally follow up with cargo run -- copy-missing-lines to give the languages you skipped the
English string as a placeholder. The other mechanical jobs (renaming and moving keys, sorting
sections) are langtool commands too - prefer them over editing the ini files by hand.