--- name: i18n-check layer: stack description: Pre-commit i18n check. Scans changed files for untranslated strings, missing locale keys, and locale sync issues. Fast and focused — only checks what changed. Use before committing or as a review add-on. argument-hint: "[--scope | --fix]" allowed-tools: Bash, Read, Grep, Glob, Edit, Agent --- # i18n Pre-Commit Check Fast, focused translation check for changed files only. Catches the 4 most common i18n mistakes before they ship. ## Modes | Invocation | What it checks | |------------|---------------| | `/i18n-check` | All uncommitted changed `.vue` files + their locale files | | `/i18n-check --scope ` | Only files under `apps//` | | `/i18n-check --fix` | Check + auto-fix all findings | ## What It Catches ### 1. Hardcoded Labels (Most Common) Scan changed `.vue` files for user-visible text NOT wrapped in `$t()` or `t()`: **Must be translated:** - `label="..."` on `UFormField` (should be `:label="$t(...)"`) - `title="..."` on `CroutonTableHeader` (should be `:title="$t(...)"`) - `add-label="..."` on `CroutonFormRepeater` (should be `:add-label="$t(...)"`) - `placeholder="..."` on inputs (should be `:placeholder="$t(...)"`) - `dependent-label="..."` on dependent field loaders - Static `{ label: 'Text', value: '...' }` arrays in select options (should be computed with `$t()`) - Raw text in `

`, `

`-`

`, `` that isn't interpolation or a component - Button text content that isn't `{{ t(...) }}` or `{{ $t(...) }}` - Tab/navigation labels in JS arrays (e.g., `{ label: 'Scheduling', value: 'scheduling' }`) **Skip (not translatable):** - CSS class strings, icon names (`i-lucide-*`) - Route paths, collection names, field names in `name="..."` attributes - Component names, event handlers - Boolean/number props - Dynamic `:label` already using `$t()` or `t()` - Comments, `console.log()`, error messages in catch blocks - Test files ### 2. Raw PascalCase/camelCase Titles Detect table headers or page titles using raw collection names: ```vue title="BookingsLocations" title="PagesPages" :title="$t('bookings.collections.locations.title')" :title="useFormatCollections().collectionWithCapital('pagesPages')" ``` ### 3. Locale Key Sync For each app with changed locale files, or apps whose components were changed: - Compare key structure across `en.json`, `nl.json`, `fr.json` - Flag keys present in `en` but missing from `nl` or `fr` - Flag values in `nl`/`fr` that are identical to `en` (likely untranslated copy-paste) ### 4. Missing Locale Infrastructure If a changed `.vue` file uses `$t()` or `t()` but the layer has no locale files, flag it. ## Workflow ### Step 1: Gather changed files ```bash # Get all changed .vue files (staged + unstaged) git diff --name-only --diff-filter=ACMR HEAD | grep '\.vue$' git diff --name-only --cached --diff-filter=ACMR | grep '\.vue$' # Also check untracked .vue files git ls-files --others --exclude-standard | grep '\.vue$' ``` If `--scope` is set, filter to `apps//**`. ### Step 2: Scan templates for hardcoded text For each changed `.vue` file, read it and check: **Pattern matching (in `