Desktop i18n
Keep localization organized as:
apps/desktop/
├── plugins/static-i18n-plugin.ts
└── src/i18n/
├── I18nRoot.tsx
├── runtime.ts
├── store.ts
├── generated/catalog.json
└── locales/
├── zh-CN.json
└── en-US.json
Author messages
- Use the global
t()function with a static Simplified Chinese string literal:t("保存"). - Do not import or locally declare
t. - Keep calls inside render paths when the result must update after a language change. Do not translate module-level UI constants.
- Use named placeholders with a static object:
t("第 {page} 页", { page }). - Preserve every placeholder name exactly in all translations.
- Wrap all user-visible labels, descriptions, tooltips, empty states, validation messages, and accessibility labels. Do not wrap protocol tokens, model IDs, URLs, or product names that should remain unchanged.
Locale behavior
systemis the default preference.- Resolve supported operating-system languages to their locale and fall back to
en-USfor every unsupported language. - Persist only explicit locale selections. Removing the preference restores system-language behavior.
- Apply changes immediately and update the document
langattribute.
Update catalogs
From apps/desktop:
- Run
npm run i18n:scanafter adding or changingt()sources. - Translate every empty entry in
src/i18n/locales/en-US.json. - Never edit
src/i18n/generated/catalog.jsonorzh-CN.jsonmanually; scanning owns them. - Run
npm run check. A normal build must fail on a missing translation or mismatched placeholder.
When adding another locale, add it to the plugin's supported locales, runtime locale type and messages, locale resolver, settings options, and provide a complete locale JSON file.