| name | lingui-i18n |
| description | The full lingui i18n workflow (setup / extract / translate / compile / adding a locale), with runnable scripts and ICU examples Adding or changing UI copy, adding a language, setting up lingui, catalogs out of sync, or an i18n check failing in CI |
| metadata | {"category":"i18n","stack":"lingui"} |
lingui i18n workflow
XID localizes everything through lingui: the React SPA (apps/server/src, TanStack Router) covering the Hosted UI and the org and platform consoles, the React SDK (packages/react), and user-facing Worker API errors. The standing coding conventions live in the i18n-lingui rule; this skill is the operational procedure. Email templates are out of scope (Mustache + R2, see chapter 07). The contributor-facing summary is docs/i18n.md.
1. Setup (already done -- reference only)
lingui v6 (6.2.0) dependencies are split across three places:
pnpm add -D -w @lingui/cli @lingui/conf @lingui/format-po
pnpm --filter @xid-kit/server add @lingui/core @lingui/react
pnpm --filter @xid-kit/server add -D @lingui/vite-plugin @lingui/babel-plugin-lingui-macro @rolldown/plugin-babel
lingui.config.ts sits at the repo root (see the i18n-lingui rule for the exact config block): sourceLocale: 'en', compileNamespace: 'es', catalog path packages/i18n/locales/{locale}/messages, po formatter with lineNumbers: false.
apps/server/vite.config.ts enables the macro transform with lingui() plus babel({ presets: [linguiTransformerBabelPreset()] }).
2. Write the copy
Follow the i18n-lingui rule:
- SPA:
<Trans> for JSX, t for imperative strings, plural for counts, msg for lazy messages. Do not hand-write ids.
- React SDK (
packages/react): no macros. Add a descriptor to sdkMessages in packages/react/src/i18n-runtime.tsx with an explicit sdk.* id and the /*i18n*/ marker comment, then render it with Rt (JSX) or rt(translate, descriptor) (string).
ICU usage: references/icu-examples.md. Catalog layout and naming: references/catalog-conventions.md.
3. Extract messages
bash .stdai/standards/skills/lingui-i18n/scripts/extract.sh
Equivalent to lingui extract --overwrite. Pulls macro and descriptor messages from the source into each locale's .po. New messages are filled in automatically for the source locale and left empty in the others. Check the Missing and Total counts in the output.
4. Translate
Edit packages/i18n/locales/<locale>/messages.po and fill in msgstr. ICU placeholders ({name}, {count, plural, ...}) MUST stay identical. Machine translation is acceptable as a starting point, but the source locale (en) requires human review.
5. Compile
bash .stdai/standards/skills/lingui-i18n/scripts/compile.sh
Equivalent to lingui compile. Turns each .po into the messages.mjs artifact the runtime imports. The runtime imports the compiled artifact, never the .po. Compiled artifacts are committed and MUST stay fresh.
6. Add a locale
A new locale has to be registered in every one of these places, or it will half-work:
lingui.config.ts -- add the BCP 47 tag (for example it, zh-Hant) to locales.
packages/i18n/src/i18n.ts -- SUPPORTED_LOCALES.
apps/server/src/lib/locale.ts -- SUPPORTED_LOCALES and the CATALOG_LOADERS dynamic-import map.
apps/server/worker/lib/locale.ts -- SUPPORTED_LOCALES; and apps/server/worker/middleware/i18n.ts -- CATALOG_LOADERS.
apps/server/src/components/LanguageSwitcher.tsx -- LOCALE_LABELS (native-language label).
packages/i18n/src/catalog.test.ts -- the LOCALES list checked by the audit gate.
Then run extract (creates the directory and empty .po), translate, compile, and run pnpm run i18n:audit.
7. Verification gates
pnpm run i18n:audit
pnpm run i18n:runtime
pnpm exec lingui compile --strict
pnpm check already includes pnpm run i18n:audit, and CI runs pnpm check. lingui v6 has no --fail-on-warning flag on extract -- do not put it in a script, the CLI rejects it.
Before opening a PR: no bypassed copy, catalogs complete, compiled artifacts in sync with the source (git diff clean after a fresh compile).
Checklist