swap-translations
Serve translations from somewhere other than the bundled JSON (a database, a translation service) so copy changes without a deploy. Use when the user wants editable copy, a CMS for strings, or transla
SKILL.md.claude/skills/swap-translations/SKILL.md
GitHubSwap the translation source
- Interface:
MessageSource(load(locale): Promise<Messages>) inpackages/i18n/src/index.ts. - Today:
bundledMessages, the JSON inpackages/i18n/messages/, type-checked against English. Changing copy needs a deploy. - Env: none today.
Swap
- Implement
MessageSourceover the new store, e.g. a tablei18n.message(locale, key, value)(db-change skill), cached in Redis and dropped on write. Validate loaded messages against English's shape and fall back tobundledMessagesfor missing keys, so a bad row never renders a raw key. - Wire it where messages are loaded:
- Nest services:
I18nModule.forRoot({ source })(packages/nest-common/src/i18n.ts), registered inapps/api/src/app.module.tsandapps/notifications/src/app.module.ts(everything that renders copy injects it with@InjectI18n()). Calli18n.invalidate(locale)on every replica when copy changes (a Redis pub/sub message). - apps/web:
bundledMessages.loadinapps/web/src/i18n/request.ts. - apps/mobile:
bundledMessages.loadinapps/mobile/src/lib/i18n.tsx(keep the bundle as the offline fallback).
- Nest services:
- New variables (the store's URL, cache TTL) in each service's
src/env.ts,.env.exampleanddocs/environment.md.
Tests
packages/i18n/src/index.test.ts (loading, caching, invalidation, catalog completeness)
with a fake source; packages/email/src/render.test.tsx renders emails through a
translator; integration tests for the store in the owning service.
Finish
- The verify skill.
- Ask the
revieweragent to review the change, and thesecurity-revieweragent: a new implementation brings its own credentials and sends data somewhere new. If you wrote a migration, themigration-revieweragent too. - Update the seam's row in the README's "Scaling path" table if what's "Now" changed.