Files
ppsspp/.claude/commands/add-string.md
T
Henrik RydgårdandClaude Opus 5 f6655d8b62 add-string: read the invocation instead of splitting it
The command took $1/$2/$3 off the front of whatever was typed, so it only got
the right section and key when called in exactly the documented shape. Called
with a sentence - which is the natural way to ask for this - it silently
produced three arbitrary words, and a paragraph warning about that is a poor
substitute for not doing it. It now gets $ARGUMENTS whole and works the three
values out, which is the part that needed a model anyway.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-18 13:50:25 -06:00

86 lines
4.9 KiB
Markdown

---
description: Translate a UI string into all the languages in assets/lang, using Tools/langtool
argument-hint: <Section> "<Key>" ["<English string>"]
---
Add and/or translate a PPSSPP UI string.
What was asked for: `$ARGUMENTS`
Work out three things from that, and **say which values you settled on before you touch anything**:
- **Section** - a `[Section]` that exists in `assets/lang/en_US.ini`. Check that it does.
- **Key** - for a new string this is the English text itself, which is how keys are written here; for
an existing one it is the key as `en_US.ini` spells it, character for character.
- **English string** - only for a new key. If the key is already in `en_US.ini` there is no English
string to pass, and the job is filling in the languages where it is still untranslated.
The invocation may be `<Section> "<Key>" ["<English string>"]`, or an ordinary sentence naming the
section and the string, or just the string. Read it, don't split it on whitespace and hope: three
words off the front of a sentence are three arbitrary words, and translating those quietly writes
rubbish into 47 files. When the section or key isn't stated outright, find them - grep the C++ for
the string to see which `GetI18NCategory` it belongs to, and `en_US.ini` for whether the key is
already there. Ask only if that leaves it genuinely ambiguous.
Follow the workflow in docs/translations.md. Run langtool from
`Tools/langtool`:
1. **Work out what the string actually means before translating it.** This is the part the tool's
own AI commands can't do, and the whole reason you're doing this instead of them:
- Grep the C++ for the key to find the call site. What widget is it? A button, a checkbox
label, a tooltip, a error message?
- What do any `%1` / `%d` placeholders get substituted with at that call site?
- How much room does the UI give it - is a long translation going to be clipped?
- How are neighbouring keys in the same section already phrased in each language? That's your
style guide, use it. Formality, terminology, whether English technical terms are kept or
translated - each language file has already made those choices, so follow them.
Say briefly what you found before you start translating.
2. Write the translations to a scratch file outside the repo, in this shape:
```ini
[Single]
en_US = Test string
sv_SE = Teststräng
lt-LT = Testeilutė
```
One line per language, named after the ini file minus the extension (`lt-LT`, `he_IL_invert`,
`zh_TW`, ...), plus an `en_US` line carrying the English string itself if you were given one -
that's what creates the key in `en_US.ini`. For a key that already exists, that line has to be
the existing English text character for character: it overwrites `en_US.ini` like any other
language, so a stray reword there silently changes the source string every other language was
translated from. Diff `en_US.ini` afterwards to confirm it didn't move.
No trailing `# comments` on those lines, they'd end up inside the translation. Placeholders like `%1` and `%d` have to appear verbatim in the
translation, in whatever position the target language needs them.
**If you don't know a language well enough to be confident, leave it out.** Step 4 gives those
languages the English string as a placeholder, which is much better than a confident guess that
nobody in the project can read well enough to catch.
If a language deliberately keeps the English string (a term like "Vsync" that language doesn't
translate), that's different from not knowing - include it with the English text. It gets written
with a `# same as English` comment, which is what stops langtool from trying to translate it
again on every later run.
3. `cargo run -- import-single <scratch-file> "<Section>" "<Key>"`
Note this overwrites any existing value for that key, so if the key already had human
translations, check what you're about to replace first. The section has to exist already.
4. `cargo run -- add-new-key "<Section>" "<Key>"` - **always, every language file ends up with the
key.** This writes `Key = Key` (plain English, no `# same as English` marker, so it still reads
as outstanding work) into every language you skipped, and leaves the ones you translated alone.
A missing key would fall back to the English string at runtime anyway, but then it looks exactly
like a translated one in the files, and translators can't see what's left to do.
Don't reach for `copy-missing-lines` to do this - it fills in placeholders repo-wide, and drags
several hundred lines of unrelated housekeeping (other missing keys, obsolete keys commented out)
into your diff.
5. `cargo run -- validate` - always, at the end. It must print `Found 0 problems.`
Finally, report which languages you translated and which you skipped and why, and leave the changes
uncommitted for review unless asked otherwise.