Update Translations
Add new i18n keys to translation files following naming conventions, detect missing locales, and flag untranslated strings. Triggers on phrases like 'add translation', 'update i18n', 'missing translat
Files1
View on GitHubSKILL.md.claude/skills/skill-update-translations/SKILL.md
GitHubPurpose
Keep frontend/src/i18n/messages/<locale>/*.json files complete and consistent. Simpler-grants-gov ships English and Spanish today; new keys must be added to every locale file (with explicit TODO_TRANSLATE markers) so deploys never silently miss strings.
When to Invoke
- A developer adds a hard-coded string in JSX/TSX under
frontend/src/. - A developer asks to "add a translation key" or "update i18n".
- A reviewer asks for "missing translation" coverage.
- Before tagging a release, to sweep for drift.
When NOT to Invoke
- For backend strings — the API does not localize user-facing copy in this repo.
- For non-displayed strings (logs, error keys consumed only programmatically).
- As a substitute for human translation review — emits placeholders only.
Inputs
- target: an active file, an explicit path, or
git diffrange. - mode (optional):
add(default — add proposed keys),audit(only report drift), orprune(report unused keys).
Procedure
- Resolve target. If a file, scan for JSX text nodes and
t('...')calls. - Locate locale files:
frontend/src/i18n/messages/<locale>/*.json. Treat the English file as the source of truth. - Build the key universe:
- Keys referenced in code (
t('feature.subkey')calls). - Keys present in each locale file.
- Keys referenced in code (
- Compute three sets:
- MISSING_IN_LOCALE — present in English, missing in another locale.
- MISSING_IN_CODE — present in code, missing in English.
- UNUSED — present in any locale, not referenced in code.
- For new strings detected in JSX text nodes (literal text not wrapped in
t()), propose a key following the convention:- Namespace: file's component name in camelCase
- Subkey: kebab-case slug of the string, max 4 words
- Example:
searchBar.clearButtonfor "Clear" insideSearchBar.tsx
- Generate the patch:
- English: real string
- Other locales:
"TODO_TRANSLATE: <english>"
- Cross-reference
documentation/rules/frontend-*.mdfor namespace conventions. - Emit the Output Format. In
addmode, also emit a unified diff. Do not write files.
Outputs
Update Translations — frontend/src/components/search/SearchBar.tsx
Locales: en, es
Mode: add
Findings:
MISSING_IN_LOCALE (2):
feature.search.placeholder es
feature.search.clearButton es
MISSING_IN_CODE (0)
UNUSED (1):
feature.legacy.deprecatedTip en, es
Proposed new keys (1):
searchBar.helperText "Press Enter to search" (L42)
Patch:
--- frontend/src/i18n/messages/en/search.json
+++ frontend/src/i18n/messages/en/search.json
@@
+ "searchBar.helperText": "Press Enter to search",
--- frontend/src/i18n/messages/es/search.json
+++ frontend/src/i18n/messages/es/search.json
@@
+ "searchBar.helperText": "TODO_TRANSLATE: Press Enter to search",
Block merge: yes (untranslated key in `es`)
Safety
- Never writes files — emits a unified diff only.
- Never invents translations — non-English locales receive
TODO_TRANSLATEmarkers. - Never deletes keys in
addmode; pruning requires explicitmode=prune. - FedRAMP / accessibility: alt text and aria-labels are first-class i18n strings and must be wrapped in
t().
Examples
Example 1 — Add a button label
Developer types "Clear" inside SearchBar.tsx. Skill proposes searchBar.clearButton, generates the patch for both locales.
Example 2 — Audit drift
mode=audit over the whole frontend/src/ returns 12 keys missing from es and 3 unused keys in en.
Example 3 — Prune unused
mode=prune lists removable keys; developer reviews and applies manually.
Related
.cursor/skills/skill-accessibility-check/— flags missing aria-labels that should also be translated.documentation/rules/frontend-styles.mdc— namespace and key conventions.