Lingui framework setup
Set up Lingui in a React framework. Use when adding Lingui to a Next.js App Router, Vite, React Router 7, Remix, or TanStack Start project, when wiring locale detection, locale-prefixed URLs, or SSR locale resolution, or when a working setup breaks after a framework or build-tool upgrade.From its SKILL.md
npx -y skills add lingui/skills --skill lingui-framework-setupAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 9 stars9 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
7.2 KB, as published. Nobody here has run it
Lingui Framework Setup
Correct Lingui setup is framework-shaped: what works in a Vite SPA breaks in React Server Components, and a module-level i18n singleton that is fine in the browser bleeds locales across requests under SSR. This skill routes to a framework-native recipe instead of a generic I18nProvider answer.
Work in this order: detect the stack → apply the common steps → follow the framework reference → run the verification sequence.
Step 1 — Detect the stack
Read package.json and the build config before recommending anything. Never guess the compiler — the macro transform silently does nothing when wired into the wrong one.
| Signal | Stack | Reference |
|---|---|---|
next in deps, app/ or src/app/ with layout.tsx | Next.js App Router | nextjs-app-router.md |
next in deps, pages/ with _app.tsx, no app/ | Next.js Pages Router | Supported by Lingui, brief notes in nextjs-app-router.md |
@tanstack/react-start in deps | TanStack Start (SSR) | tanstack-start.md |
@react-router/dev in devDeps + react-router.config.* | React Router 7 framework mode (SSR) | react-router-remix.md |
@remix-run/dev ≥ 2.7 in devDeps + vite | Remix v2 (Vite build, SSR) | react-router-remix.md |
vite + react, none of the above | Vite SPA (incl. declarative React Router, TanStack Router) | vite-spa.md |
Compiler detection (decides which macro-transform package to install):
| Signal | Compiler | Macro transform |
|---|---|---|
@vitejs/plugin-react-swc | SWC | @lingui/swc-plugin (pin exactly — see the swc-plugin-compatibility skill) |
@vitejs/plugin-react ^5 or lower | Babel | @lingui/babel-plugin-lingui-macro via the plugin's babel option |
@vitejs/plugin-react ^6+ | Babel plugin unusable | v6 removed the babel option — switch to the SWC variant (see swc-plugin-compatibility) |
Next.js, no .babelrc | SWC | @lingui/swc-plugin via experimental.swcPlugins |
Next.js with .babelrc | Babel | @lingui/babel-plugin-lingui-macro in .babelrc (a Babel config disables Next's SWC) |
One more distinction that changes everything: react-router in deps without @react-router/dev is a declarative SPA (<Routes> in JSX) — that is the Vite SPA reference, not the framework-mode one. Route-module loader/+types code written into a declarative SPA is dead on arrival.
Step 2 — Common steps (every framework)
Version gate. Lingui 6 is current: ESM-only, requires Node ≥ 22.19 (or ≥ 24). Install @lingui/core@^6 @lingui/react@^6 and dev @lingui/cli@^6. If the project cannot meet the Node requirement, pin every @lingui/* package to ^5 — do not mix majors. Do not install @lingui/macro: since v5 the macros live at @lingui/react/macro and @lingui/core/macro.
Per-stack extras (details in each reference):
| Stack | Extra runtime | Extra dev |
|---|---|---|
| Next.js App Router | — | @lingui/swc-plugin (exact pin) |
| Vite SPA | @lingui/detect-locale | @lingui/vite-plugin + transform plugin per compiler |
| React Router 7 / Remix | — | @lingui/vite-plugin, @lingui/format-po, transform plugin |
| TanStack Start | — | @lingui/vite-plugin, transform plugin |
@lingui/detect-locale is browser-only (navigator, localStorage, window.location). Never install it on an SSR stack — it throws on the server or desyncs hydration; SSR stacks resolve locale from the request (cookie / Accept-Language) instead.
Config and catalogs. Create lingui.config.ts with sourceLocale, locales, and one catalogs entry; the lingui-best-practices skill owns catalog hygiene (build-script wiring, gitignore rules, CI drift check) and the single-sourced locale module (locales.ts with resolveLocale, getDirection, localeDisplayName) — reuse both, do not re-derive them.
Two catalog-loading models. On Vite-based stacks, @lingui/vite-plugin compiles .po on import — await import('./locales/en/messages.po') just works, there are no compiled artifacts to script or gitignore. Next.js has no Vite pipeline: run lingui compile prepended to dev/build scripts and import the compiled catalogs (@lingui/loader is webpack-only and Turbopack is the Next 15/16 default, so a loader-based setup is a trap).
Step 3 — Framework reference
Read the matching reference file fully before editing the project. Every reference follows the same section contract, in order:
packages → build-tool integration → lingui.config → locale resolution → provider/layout wiring → routing strategies → language switcher → gotchas → verification
If the project needs locale-prefixed URLs, every reference offers the same three strategies — unprefixed source locale, all locales prefixed, or no URL locale (cookie/storage only). Restructuring routes is invasive: ask the user which strategy they want before moving files.
Step 4 — Verify
Run in this order; each step must pass before the next:
npx lingui extract --clean # catalogs regenerate; count matches expectations
npx lingui compile # only on stacks with compiled catalogs (Next.js)
npx tsc --noEmit # types resolve, incl. catalog imports
npm run build # macro wiring errors surface here
Then prove the transform ran. A green build does not: a mis-wired macro transform is silently a no-op and the app ships in the source language. Translate one string in a non-source catalog, run the app in that locale, and confirm the translation reaches the browser (each reference gives the stack's exact check — curl the server-rendered HTML on SSR stacks, hard-load a deep link on the SPA). The setup is done when you have seen a translated string, not when the build is green.
Raw untranslated text at that point is a compiler wiring problem — wrong plugin, bare-string SWC entry, or the @vitejs/plugin-react@6 babel trap; see the swc-plugin-compatibility skill.
Adding a new framework
Another stack (a new meta-framework, a new build tool) costs two edits: one row in the Step 1 detection table, and one file under references/ following the Step 3 section contract. Framework-specific facts go in the reference; anything true of every stack — the version gate, the two catalog-loading models, the SSR detect-locale rule — stays here, stated once. Every non-obvious rule carries a one-sentence why.
Related skills
- lingui-best-practices — macro selection, catalog hygiene, the shared locale module. This skill assumes it; don't duplicate it.
- swc-plugin-compatibility — SWC plugin version pinning and the silent-failure modes of a mis-wired transform.
- migrate-i18next-to-lingui — if the project already has i18next, migrate first, then return here for framework wiring.