← ClaudeAtlas

padosoft-i18n-hygienelisted

Use this skill when working on a translation catalogue — adding a string, auditing for unused or missing keys, replacing hardcoded text, aligning locales, checking plural forms. Also when the user says a label shows as a raw key in the interface, that a language is missing strings, that the translation files have grown full of things nobody uses, that a text was never translated, or asks to clean up the localisation. It gives the search that actually finds a key's usages, the rule for deleting one safely, what counts as user-visible text, and the invariants a catalogue has to keep across every locale. Do not use it to choose a localisation library, to translate the copy itself, or for date and number formatting performance.
padosoft/skills · ★ 1 · AI & Automation · score 80
Install: claude install-skill padosoft/skills
# i18n catalogue hygiene A translation catalogue decays in two directions at once: it fills with keys nothing uses, and it misses the ones something does. Both are invisible in review — one adds noise nobody reads, the other shows a raw key to a user in a language nobody on the team speaks. **The rule: a key is only unused once you have searched for every form it can be written in. And a key exists in every locale, or in none.** --- ## 1. Start from a generated inventory, not from the files Extract the full list of defined keys with a script, in a flat dotted form. Reading the catalogue files by eye misses nested branches and gives you no way to diff the locales against each other. That inventory is the input to everything below, and it is also the thing to regenerate at the end to prove the state you claim. ## 2. Finding a key's usages is harder than it looks **Searching for the key as a string is the search that misses.** Depending on the library and its configuration, the same key appears in the code as: | Form | Looks like | |---|---| | A plain string | `t("account.header.welcome")` | | A typed selector | `t(($) => $.account.header.welcome)` — **no string anywhere** | | A dynamic leaf | `t(($) => $.account.header[variable])` — only the **parent** appears | | A runtime key | `t(buildKey(...))`, `tDynamic("account.header." + name)` | | A template literal | `` t(`tabs.${routeName}`) `` — only the prefix appears | | Deferred, in a config object | a thunk stored somew