Files
simple-react-app-kit/packages/i18n/README.md
T

173 lines
4.4 KiB
Markdown
Raw Normal View History

# @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:
```json
{
"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:
```json
{
"sourceLocale": "en",
"locales": ["en", "zh-Hans"],
"catalogPath": "src/locales/{locale}/messages",
"include": ["src"],
"exclude": ["**/*.test.{ts,tsx}"]
}
```
Then expose it as a Lingui configuration:
```ts
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:
```ts
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.
```tsx
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.
```tsx
<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`:
```sh
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, updates
`i18n.config.json`, and asks Lingui to extract its catalog.
- `extract` extracts application messages into its catalogs.
- `compile` compiles application catalogs to TypeScript modules.
- `ui` starts 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
```sh
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.