$npx -y skills add haoxiang-xu/PuPu --skill pupu-i18n-coverageUse when checking PuPu's translation coverage after adding or changing UI strings, or when asked "are translations complete / 漏翻了吗 / 检查 i18n". Audits all 11 locales in src/locales against en.json (source of truth), auto-fills missing keys by translating them, and reports orphan k
| 1 | # pupu-i18n-coverage |
| 2 | |
| 3 | Detects and fixes i18n drift in PuPu. `en.json` is the source of truth; the runtime silently |
| 4 | falls back to English, so missing translations are invisible without this check. |
| 5 | |
| 6 | ## When to run |
| 7 | After a feature or change that touched UI strings, or on request. Full-scan every time. |
| 8 | |
| 9 | ## Mechanism (Claude does only translation; scripts do all file mutation) |
| 10 | |
| 11 | ### Step 1 — Audit |
| 12 | ```bash |
| 13 | node .claude/skills/upkeep/pupu-i18n-coverage/audit.mjs --root <repo-root> > /tmp/i18n-report.json |
| 14 | ``` |
| 15 | Add `--strict` to also flag suspected-untranslated values (off by default — noisy). |
| 16 | Read the JSON. It contains, per locale: `missing`, `orphan`, `placeholderMismatch`; and a |
| 17 | `code` section: `missingInEn`, `deadKeys`, `dynamicCount`, `note`. |
| 18 | |
| 19 | ### Step 2 — Auto-fill missing keys (the only auto-apply action) |
| 20 | For each locale with a non-empty `missing` list: |
| 21 | 1. Look up each missing key's English value in `src/locales/en.json`. |
| 22 | 2. Translate each value into that locale's language. **Preserve every `{placeholder}` token |
| 23 | verbatim** — do not translate or reorder the token names. |
| 24 | 3. Write a flat JSON map `{ "key.path": "translated value", ... }` to a temp file. |
| 25 | 4. Apply it: |
| 26 | ```bash |
| 27 | node .claude/skills/upkeep/pupu-i18n-coverage/apply.mjs --root <repo-root> --locale <name> --translations /tmp/<name>.json |
| 28 | ``` |
| 29 | `apply.mjs` inserts each key in en order and never overwrites existing values. |
| 30 | |
| 31 | ### Step 3 — Report (do NOT act without confirmation) |
| 32 | Summarize for the owner: |
| 33 | - **Auto-filled:** per locale, which keys were added and their translations (he reviews the diff). |
| 34 | - **Needs confirmation — deletions:** `orphan` keys (per locale) and `deadKeys` (project-wide). |
| 35 | List them; delete only if he says so. |
| 36 | - **Needs confirmation — edits:** `placeholderMismatch` (the translation broke/changed a `{token}`). |
| 37 | - **Bugs to flag:** `missingInEn` — code calls `t("x")` but en.json lacks it (UI shows the raw key). |
| 38 | - **Blind spot:** state `dynamicCount` — that many `t(variable)` calls can't be statically |
| 39 | resolved, so dead-key/missing-in-en results exclude dynamically-composed keys. |
| 40 | |
| 41 | ### Step 4 — Stop |
| 42 | Leave the working tree dirty. **Never run `git commit`** — the owner reviews and commits himself. |
| 43 | |
| 44 | ## Safety rules |
| 45 | - Additions only are automatic. Deletions (orphan/dead) and edits (placeholder) require explicit OK. |
| 46 | - Dead-key detection is conservative (a key counts as live if its string appears anywhere in code). |
| 47 | - Never overwrite an existing translation via the auto-fill path. |