add-translation
Add or change user-facing text (a UI string, email or error message), or add a new language. Use whenever a change shows new words to a user, or the user asks for another locale.
SKILL.md.claude/skills/add-translation/SKILL.md
GitHubAdd a string or a locale
Every user-facing string is a key in packages/i18n/messages/<locale>.json (en.json
only today). .claude/rules/i18n.md has the rules.
A string
- Pick the namespace (top-level key) the surface uses:
auth,settings,shorts,director,email,errors, … and put the key next to its siblings. Reusecommon.*for shared words (Save, Cancel). - Add it to every catalog, at the same path, in the same change. Write real translations; if you can't, say so in the pull request rather than copying English.
- ICU for anything variable:
{name},{count, plural, one {# item} other {# items}}. Never concatenate translated fragments. Arguments aren't type-checked (the catalog is imported as JSON), so pass exactly the ones the message names. - Use it:
useTranslations("<ns>")(web client),getTranslationsfromnext-intl/server(web server components), the translator inapps/notificationsfor emails. Dates, numbers and money throughuseFormatter()/getFormatter(). - Keys the code checks for: an error code needs
errors.<CODE>; an eventworkspace.audit.events.<domain>.<fact>.v<N>(nested, likeauth.signed_up.v1).
A locale
- Copy
packages/i18n/messages/en.jsonto<code>.jsonand translate it. - Add the code to
localesand tocatalogsinpackages/i18n/src/index.ts(thesatisfiesclause makes a missing key a type error). - Right-to-left: add it to
RTLinapps/web/src/app/layout.tsx. - The profile settings' language list reads
locales, so it picks it up. With more than one language, the header's language switcher (removed while English was the only one) and the saved-language half ofPreferenceSynccome back.
Done when
bun run check-typespasses (web keys are checked against English).bun run --filter @repo/i18n testpasses (every catalog complete; codes, events, categories and scopes all have their keys).bun run --filter @repo/email testif an email changed.- The
i18n-checkeragent reports nothing blocking.