padosoft-i18n-hygienelisted
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