find-unused-locale-keyslisted
Install: claude install-skill zombico/mojulo
# /find-unused-locale-keys
Identify message keys in `control/messages/en.json` that no code under `control/**` references, then — only after you confirm — delete them from `en.json`. Deletion touches **en.json only**; the operator runs `/sync-locales` afterward to sweep the removals across every other locale.
Companion to `/sync-locales` (propagates en.json *edits*, including removals, to other locales) and `/translate-messages` (bootstraps a new locale). This skill is the *garbage collector* for the source catalog — it finds the dead keys and prunes the source; `/sync-locales` carries the deletions out to the translations.
## Why this needs more than grep
~16% of translator call sites in this repo build keys dynamically: `t(\`status.${x}\`)`, `t(\`preview.contexts.${ctx}.${k}\`)`, or `t(routeConfig.labelKey)`. A naïve "does the string `status.building` appear in source?" scan would delete those live keys. The helper script handles this by:
- binding each translator to its namespace (`const t = useTranslations('apps')` → `t('actions.stop')` is `apps.actions.stop`);
- treating a template literal's static prefix as a **protected subtree** (`t(\`status.${x}\`)` protects `<ns>.status.**` — the dynamic part is never resolved);
- protecting the **whole namespace** of a bare `t(var)` call on a namespaced translator;
- surfacing bare `t(var)` calls on a **root** translator (`useTranslations()`) as *blind spots* for human review, backed by a raw dotted-string-literal corpus that