Skip to content

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:

  1. 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
  2. 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 = ['&#36;{A}.x', '&#36;{A}.y']; and (when detectable) usage patterns like:
        • t(k) inside keys.map((k) => ...)
        • t(k) inside for (const k of keys) { ... }
    • if it sees &#36;{X}.something, it will try to resolve &#36;{X} only when &#36;{X} is a simple const string known at build-time.
    • then it compares the “used” set against the leaf paths from en.json to compute which leaf keys look unused.
  3. 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 in en.json)
    • if you confirm, it can prune those extra leaves so other locales match en.json shape.

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 translations
  • unresolved placeholders = how many &#36;{VAR} placeholders were found inside key strings but couldn’t be resolved statically
  • unused 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.