alepha@docs:~/docs/framework/cli/plugins$
cat 3-i18n.md | pretty
2 min read
Last commit:

#I18n Plugin

Find dead translation keys before they pile up. The i18n plugin statically scans your project for $dictionary(...) declarations and reports every key that is never referenced anywhere in the source tree - plus two placeholder checks (see below).

#Quick Start

Register the plugin in alepha.config.ts with the i18n() helper:

typescript
1import { defineConfig } from "alepha/cli/config";2import { i18n } from "alepha/cli/i18n";3 4export default defineConfig({5  plugins: [i18n()],6});
bash
alepha i18n check
txt
Checked 412 keys across 187 files (3 dictionary files).

✗ Unused translations (2):
  - profile.legacy.title
  - profile.legacy.subtitle

#What It Does

The check extracts every key declared in your $dictionary(...) calls, then looks for a quoted-literal reference to each key anywhere else in the scanned files. Keys with no reference are reported and the command exits non-zero - wire it into your verify pipeline so removing a feature can't silently strand its translations. The report also prints an exempt (dynamic prefixes) count so exemptions stay visible.

Two more checks run in the same pass:

  • Bad placeholders: entries using {0}-style placeholders that never interpolate; Alepha's interpolation syntax is $1, $2.
  • Missing arguments: tr() calls passing fewer arguments than the entry's $N placeholders expect.

If no translation keys are found at all, the command exits with code 2 and a warning - that usually means the dictionary location changed and the scan is looking in the wrong place.

#Configuration

i18n() accepts the following options:

Option Type Default Description
scan string[] ["src"] Directories (relative to the project root) to scan for dictionaries and key usage
dynamicPrefixes string[] [] Key prefixes constructed at runtime - exempted from the check
exclude string[] [] Extra path substrings to exclude, on top of the defaults (node_modules, dist, __tests__, .spec.*, .test.*, .alepha)
typescript
 1import { defineConfig } from "alepha/cli/config"; 2import { i18n } from "alepha/cli/i18n"; 3  4export default defineConfig({ 5  plugins: [ 6    i18n({ 7      scan: ["src", ".vendor/@alepha/ui"], 8      dynamicPrefixes: ["folio.type.", "feedback.filter."], 9    }),10  ],11});

#Dynamic Keys

A key built with a template literal has no quoted-literal reference:

typescript
1tr(`folio.type.${kind}`);

The static scan can't see it, so every folio.type.* key would be reported as unused. Add the prefix to dynamicPrefixes to exempt them.

Audit your prefixes

Keep the dynamicPrefixes list short and re-check it when a feature is removed - a stale prefix is a place where dead keys can hide.

#In the Verify Pipeline

Add a check:i18n script to your app and let CI run it:

json
1{2  "scripts": {3    "check:i18n": "alepha i18n check"4  }5}

The exit codes make it CI-friendly: 0 clean, 1 unused keys, bad placeholders, or missing arguments found, 2 no keys found at all.