Locale Cleanup (pnpm locales:cleanup)
This command lives in apps/web/scripts/locales/cleanup/index.ts.
Why it exists (powerful cleanup)
As the app grows, translations can “drift” after you:
- rename/delete pages or components
- add/remove translation keys in
en.json - sync locale files and end up with stale or extra keys
locales:cleanup helps you detect and optionally remove that drift while treating apps/web/locales/en.json as the source of truth.
What it does (three-stage, interactive)
It runs three main stages and prints a plan first:
Stale linkage cleanup (writes immediately if you confirm)
- removes locale JSON files that are no longer linked in
src/i18n/config.ts+src/i18n/index.ts - removes config rows and index loader/import entries that no longer have a matching locale JSON file
- removes locale JSON files that are no longer linked in
Unused translation keys detection (interactive pruning, opt-in)
- scans TypeScript/TSX files under:
apps/web/src/**(full frontend scan for correctness)
- extracts translation-key candidates using static heuristics, including:
- direct
t("some.dot.key")/t(\some.dot.key`)` <Trans i18nKey="some.dot.key" />- key arrays like
const keys = ['${A}.x', '${A}.y'];and (when detectable) usage patterns like:t(k)insidekeys.map((k) => ...)t(k)insidefor (const k of keys) { ... }
- direct
- if it sees
${X}.something, it will try to resolve${X}only when${X}is a simpleconststring known at build-time. - then it compares the “used” set against the leaf paths from
en.jsonto compute which leaf keys look unused.
- scans TypeScript/TSX files under:
Extra key detection in non-EN locales (interactive pruning, opt-in)
- compares leaf paths in each non-English locale JSON vs
en.json - reports “extra” leaves (present in
zh-cn.json,ar.json, etc. but missing inen.json) - if you confirm, it can prune those extra leaves so other locales match
en.jsonshape.
- compares leaf paths in each non-English locale JSON vs
Safety / prompting behavior (how to use it correctly)
The command is interactive: it shows the planned actions and asks questions before writing any locale files. If you answer N, nothing will be removed for that stage.
en.json is always protected (it will never be deleted).
How to interpret the output
The key parts of the “plan” output:
arrays resolved= how many keys were successfully resolved from const key-arrays and detected as being used for translationsunresolved placeholders= how many${VAR}placeholders were found inside key strings but couldn’t be resolved staticallyunused leaf key count in en.json= leaf keys that look unused based on the static scan
If you see suspiciously high “unused” numbers, that usually means:
- the code uses dynamic key construction that the heuristic can’t fully resolve, or
- key arrays aren’t detected due to formatting/usage patterns
In that case, pick N first and rely on manual review.