Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Internationalization (i18n) workflow and standards for managing translations. Use when: (1) Adding new user-facing text, (2) Creating new components with user-facing text, (3) Reviewing code for i18n compliance, (4) Adding a new translation module.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-04 | ✗→✓ | ▲ Improved | 75% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 87% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 73% | 0% |
| case-17 | ✗→✓ | ▲ Improved | 93% | 0% |
| case-20 | ✓→✗ | ▼ Worse | 51% | 0% |
Standards and workflow for internationalization. All user-visible text must use i18n.
Announce at start: "I'm using i18n skill to ensure proper internationalization."
Before doing any i18n work, always read src/common/config/i18n-config.json to get the current list of supported languages and modules. Never assume a fixed number — languages and modules may have been added or removed since this skill was written.
bashcat src/common/config/i18n-config.json
This file is the single source of truth. All scripts, runtime code, and this workflow depend on it.
src/common/config/i18n-config.json # Single source of truth: languages, modules
src/renderer/i18n/
├── index.ts # i18next configuration
├── i18n-keys.d.ts # AUTO-GENERATED — do not edit manually
└── locales/
├── <lang>/ # One directory per language in i18n-config.json
│ ├── index.ts # Barrel import for all modules
│ ├── common.json # One JSON per module in i18n-config.json
│ ├── conversation.json
│ └── ...
└── ...referenceLanguage in i18n-config.json (currently en-US)supportedLanguages array — read the file to get the current listmodules array — read the file to get the current listKeys use namespaced dot notation in code: t('module.key') or t('module.nested.key').
Inside each module JSON file, keys can be flat or nested:
json// common.json — flat keys { "send": "Send", "cancel": "Cancel", "copySuccess": "Copied" } // cron.json — nested keys { "scheduledTasks": "Scheduled Tasks", "status": { "active": "Active", "paused": "Paused" } }
In code:
typescriptt('common.send'); // flat key in common.json t('cron.status.active'); // nested key in cron.json
copySuccess, scheduledTasksstatus.active, actions.pausecommon.json: save, cancel, delete, confirm, etc.| Suffix | Usage | | ------------------- | -------------------- | | title | Section/page titles | | placeholder | Input placeholders | | label | Form labels | | success / error | Status messages | | confirm | Confirmation dialogs | | empty | Empty state messages | | tooltip | Tooltip text |
src/common/config/i18n-config.jsonGet the current language list and module list. Do not skip this step.
Before adding a new key, search for similar existing keys:
bashgrep -r "keyword" src/renderer/i18n/locales/en-US/
Reuse common.* keys when possible.
Match the module to the feature area. If no module fits, consider whether a new module is needed (see "Adding a New Module" below).
CRITICAL: Every new key must be added to every locale in supportedLanguages. Use this checklist for each key:
en-US/<module>.json — reference language (added in Step 3)zh-CN/<module>.json — addedzh-TW/<module>.json — addedsrc/common/config/i18n-config.json → supportedLanguages — addedA key missing from even one locale will cause node scripts/check-i18n.js to fail in CI.
tsximport { useTranslation } from 'react-i18next'; function MyComponent() { const { t } = useTranslation(); return <button>{t('common.save')}</button>; }
Run these two commands in order — both must pass before committing:
bashbun run i18n:types # Step A: regenerate i18n-keys.d.ts from reference locale node scripts/check-i18n.js # Step B: validate structure, keys, and type sync
i18n:types must be run before check-i18n.js — the check validates the generated filecheck-i18n.js exits with errors (❌), fix them before proceedingcheck-i18n.js exits with warnings only (⚠️), review but may proceedi18n-keys.d.tssrc/common/config/i18n-config.json → modules array<module>.json in every locale directory (read supportedLanguages to know which)index.tsbun run i18n:types to regenerate type definitionsnode scripts/check-i18n.js to validateNever use hardcoded Chinese/English text in JSX:
tsx// Bad <span>重命名</span> <span>Delete</span> {name || '新对话'} // Good <span>{t('common.rename')}</span> <span>{t('common.delete')}</span> {name || t('conversation.newConversation')}
console.log() / debug outputjson{ "taskCount": "{{count}} task(s)", "greeting": "Hello, {{name}}!" }
tsxt('cron.taskCount', { count: 5 });
Use Trans component for complex markup:
tsximport { Trans } from 'react-i18next'; <Trans i18nKey='cron.countdown'> Task <strong>{{ taskName }}</strong> in <span>{{ countdown }}</span> </Trans>;
Most terms can be auto-converted from zh-CN, but some need manual review:
| zh-CN | zh-TW | Notes | | ----- | ----- | -------------- | | 视频 | 影片 | Different term | | 软件 | 軟體 | Different term | | 信息 | 訊息 | Different term | | 默认 | 預設 | Different term |
Before submitting code with new text:
src/common/config/i18n-config.json to get current languages and modulest() functionsupportedLanguagesbun run i18n:types ran first (regenerates i18n-keys.d.ts)node scripts/check-i18n.js passed after types regenerated (no errors)| Mistake | Correct | | ---------------------------------------------- | ---------------------------------------------------- | | Assuming a fixed number of languages | Always read i18n-config.json first | | Adding key to only some locales | Add to every locale in supportedLanguages | | Editing i18n-keys.d.ts manually | Run bun run i18n:types to generate | | Using t("New Chat") | Define key: t("conversation.newChat") | | Not updating i18n-config.json for new module | Update config first, then create files | | Adding module JSON but not updating index.ts | Must add import + export in each locale's index.ts |
Other measured skills in the registry, with their headline benchmark lift.