- add SSR-safe I18nProvider, Translate, locale hooks, catalog activation, and typed runtime APIs - add consumer-owned Lingui configuration helpers and a CLI for locale creation, extraction, compilation, and project discovery - add catalog read/write services plus a Vite development API plugin scoped to each consuming application - add the portal-based I18nDevtool, message editor, dark-theme tracking, and standalone Message Studio - document the consumer workflow and cover runtime, configuration, catalog, CLI, and Devtool behavior with tests
4.4 KiB
@workspace/i18n
Reusable Lingui runtime, Devtool, Vite plugin, and CLI. This package does not own
application locales or catalogs: every consuming application keeps its own
i18n.config.json, lingui.config.ts, and generated message files.
Consumer setup
Install the package in an application and add scripts that invoke its CLI:
{
"dependencies": {
"@lingui/core": "^6",
"@workspace/i18n": "workspace:*"
},
"scripts": {
"i18n": "workspace-i18n",
"i18n:compile": "workspace-i18n compile",
"i18n:extract": "workspace-i18n extract",
"i18n:ui": "workspace-i18n ui"
}
}
Create i18n.config.json in that application:
{
"sourceLocale": "en",
"locales": ["en", "zh-Hans"],
"catalogPath": "src/locales/{locale}/messages",
"include": ["src"],
"exclude": ["**/*.test.{ts,tsx}"]
}
Then expose it as a Lingui configuration:
import project from "./i18n.config.json" with { type: "json" }
import { createLinguiConfig } from "@workspace/i18n/lingui-config"
export default createLinguiConfig(project)
Mount the API plugin in the consuming application's Vite configuration:
import { i18n } from "@workspace/i18n/vite"
import { defineConfig } from "vite"
export default defineConfig({
plugins: [i18n()],
})
Runtime
Compiled Lingui catalogs are passed to the provider synchronously, so the first client render and SSR render use the same locale.
import { messages as en } from "@/locales/en/messages"
import { messages as zhHans } from "@/locales/zh-Hans/messages"
import {
I18nProvider,
Translate,
useLocale,
useLocales,
useMessage,
useTranslate,
} from "@workspace/i18n"
import { I18nDevtool } from "@workspace/i18n/devtool"
function Root({ children, locale }: React.PropsWithChildren<{ locale: string }>) {
return (
<I18nProvider
locale={locale}
locales={[
{ locale: "en", label: "English" },
{ locale: "zh-Hans", label: "简体中文" },
]}
catalogs={{ en, "zh-Hans": zhHans }}
>
{children}
{import.meta.env.DEV && <I18nDevtool dark={"ui\\:dark"} />}
</I18nProvider>
)
}
function Greeting() {
const locale = useLocale()
const locales = useLocales()
const title = useMessage("dashboard.title")
const translate = useTranslate()
return (
<>
<Translate
id="welcome"
message="Welcome, {name}"
values={{ name: "Ada" }}
/>
<span>{title}</span>
</>
)
}
Translate is a thin wrapper around Lingui's runtime Trans component. It
does not add a DOM wrapper and retains values, components, formats,
component, and render.
The generated Lingui configuration points runtimeConfigModule.Trans at
Translate, so explicit <Translate id="…" message="…" /> usages are found
by lingui extract.
Devtool
I18nDevtool must be mounted inside I18nProvider. It reads the active locale
and available locales from the provider, while the Vite i18n() plugin serves
catalog reads, updates, extraction, and compilation.
<I18nProvider locale="zh-Hans" locales={["en", "zh-Hans"]}>
<App />
{import.meta.env.DEV && <I18nDevtool dark={"ui\\:dark"} />}
</I18nProvider>
The dark value can be a class name such as ui\:dark, or a CSS selector
such as [data-theme="dark"]. The Devtool observes document theme changes and
renders through a portal, so application overflow and stacking contexts do not
clip it.
Lower-level MessagePanel, MessageRepositoryProvider, and repository types
remain available from @workspace/i18n/devtool.
CLI
Run inside the consuming application so the CLI discovers that application's
i18n.config.json:
bun run i18n new ja
bun run i18n extract
bun run i18n compile
bun run i18n ui
Commands:
new <locale>validates and canonicalizes a BCP-47 locale, updatesi18n.config.json, and asks Lingui to extract its catalog.extractextracts application messages into its catalogs.compilecompiles application catalogs to TypeScript modules.uistarts Message Studio. It reads and writes PO catalogs through Lingui's catalog API, and provides Extract and Compile actions.
All commands accept --project <path> when invoked outside the application.
Catalog workflow
cd apps/web
bun run i18n:extract
bun run i18n:compile
The application's i18n.config.json is the machine-editable source of truth.
Its lingui.config.ts maps that manifest to Lingui's configuration format.