mirror of
https://github.com/luckyyzh/pi-agent-integrated.git
synced 2026-10-03 11:09:34 +00:00
173 lines
5.5 KiB
Markdown
173 lines
5.5 KiB
Markdown
# Internationalization
|
|
|
|
Pi Web's interface uses a small internal i18n layer. It is intentionally kept
|
|
inside the application instead of introducing another runtime dependency, so
|
|
the UI can support more languages without coupling the server, API responses,
|
|
or Pi's own output to a particular language.
|
|
|
|
## Current Languages
|
|
|
|
The built-in language packages are:
|
|
|
|
- `en` - English
|
|
- `zh-CN` - Simplified Chinese
|
|
|
|
The initial locale is inferred from the browser. Users can change the locale
|
|
from the language control in the top bar. The selected locale is persisted in
|
|
`localStorage` under `pi-locale`.
|
|
|
|
## Using Translations In A Component
|
|
|
|
Client components should get the translation function from `useI18n`:
|
|
|
|
```tsx
|
|
import { useI18n } from "@/hooks/useI18n";
|
|
|
|
export function ExampleButton() {
|
|
const { t } = useI18n();
|
|
|
|
return <button aria-label={t("example.open")}>{t("example.open")}</button>;
|
|
}
|
|
```
|
|
|
|
The component must be rendered below `I18nProvider`. The application root
|
|
already provides it in `app/page.tsx`.
|
|
|
|
Use parameters for values that change at runtime instead of concatenating
|
|
translated fragments:
|
|
|
|
```tsx
|
|
<span>{t("session.messageCount", { count: messageCount })}</span>
|
|
```
|
|
|
|
The corresponding message can use `{count}`:
|
|
|
|
```ts
|
|
"session.messageCount": "{count} messages"
|
|
```
|
|
|
|
This keeps the sentence structure under the control of each language package.
|
|
|
|
## Adding A New Language
|
|
|
|
### 1. Create The Message File
|
|
|
|
Add a file under `lib/i18n/messages/`, using a BCP 47-style identifier. For
|
|
example, a Japanese package would be `lib/i18n/messages/ja.ts`:
|
|
|
|
```ts
|
|
import type { LocalePlugin } from "../types";
|
|
|
|
/** Pi Web Japanese language package. */
|
|
export const jaLocale: LocalePlugin = {
|
|
id: "ja",
|
|
label: "日本語",
|
|
messages: {
|
|
"common.language": "言語",
|
|
// Add the rest of the shared keys here.
|
|
},
|
|
};
|
|
```
|
|
|
|
The message keys must remain stable and language-neutral. Copy the complete
|
|
key set from `lib/i18n/messages/en.ts` when starting a new package. Missing
|
|
keys fall back to English, but a complete package is preferred for a user
|
|
facing language.
|
|
|
|
### 2. Register The Package
|
|
|
|
Import and register the package in `lib/i18n/registry.ts`:
|
|
|
|
```ts
|
|
import { jaLocale } from "./messages/ja";
|
|
|
|
registerLocale(jaLocale);
|
|
```
|
|
|
|
`LocalePlugin.id` must be unique and non-empty. The label is displayed in the
|
|
language selector. `getSupportedLocales()` automatically exposes registered
|
|
packages to the UI.
|
|
|
|
### 3. Update Locale-Specific Types If Needed
|
|
|
|
The built-in `Locale` type currently lists `en` and `zh-CN` for compile-time
|
|
safety. When adding a built-in language, update that union in
|
|
`lib/i18n/types.ts` and update any locale validation that intentionally lists
|
|
the built-in identifiers.
|
|
|
|
### 4. Add Tests
|
|
|
|
Extend `lib/i18n/registry.test.mjs` when browser-language detection or registry
|
|
behavior changes. Extend `lib/i18n/format.test.mjs` when interpolation or
|
|
locale-aware formatting changes. A new language should at minimum verify its
|
|
registry id and a representative translation.
|
|
|
|
## Adding Or Changing UI Text
|
|
|
|
1. Add a stable key to both `lib/i18n/messages/en.ts` and
|
|
`lib/i18n/messages/zh-CN.ts`.
|
|
2. Put keys under a feature namespace such as `chat.*`, `files.*`,
|
|
`settings.*`, or `common.*`.
|
|
3. Use `t("namespace.key")` in visible text, `title`, `aria-label`, and
|
|
`placeholder` values.
|
|
4. Use interpolation for counts, names, paths, and other runtime values.
|
|
5. Keep product names, model names, provider names, commands, file paths,
|
|
user content, tool output, and server-provided error details unchanged.
|
|
6. Do not translate an API error by matching its English text. Translate only
|
|
the local UI fallback around it.
|
|
|
|
Keep established technical terms in English when translating them would make
|
|
the interface harder to map back to commands, configuration fields, or Pi
|
|
documentation. This includes terms such as `Agent`, `API Key`, `Provider`,
|
|
`Token`, `worktree`, `Diff`, `CWD`, `Shell`, `Git`, `HEAD`, `OAuth`, and
|
|
`Mermaid`; preserve their conventional casing. Translate the surrounding
|
|
actions and status text. Common concepts with clear localized forms, such as
|
|
model, plugin, skill, prompt, context, and cache, can remain localized.
|
|
|
|
For example:
|
|
|
|
```ts
|
|
// en.ts
|
|
"files.uploadedCount": "{count} uploaded"
|
|
|
|
// zh-CN.ts
|
|
"files.uploadedCount": "已上传 {count} 个文件"
|
|
```
|
|
|
|
```tsx
|
|
<span>{t("files.uploadedCount", { count: uploadedCount })}</span>
|
|
```
|
|
|
|
If a key is missing from the selected language, the formatter first tries the
|
|
English package and finally returns the key itself. Missing keys also produce
|
|
a development warning, which makes incomplete translations visible during
|
|
development.
|
|
|
|
## Verification
|
|
|
|
Run the following commands from the repository root:
|
|
|
|
```bash
|
|
node_modules/.bin/tsc --noEmit
|
|
npm run lint
|
|
node_modules/.bin/jiti lib/i18n/registry.test.mjs
|
|
node_modules/.bin/jiti lib/i18n/format.test.mjs
|
|
node --test lib/*.test.mjs
|
|
```
|
|
|
|
Do not run `next build` during normal development. See the repository
|
|
development notes for the reason.
|
|
|
|
## Design Principles
|
|
|
|
Pi Web is a user-facing web interface. Internationalization is therefore an
|
|
accessibility and usability feature: people should be able to understand and
|
|
operate the interface in a language they are comfortable with. Language
|
|
support should lower the barrier to using Pi Web, not be treated as a measure
|
|
of technical expertise or as a reason to exclude otherwise useful
|
|
contributions.
|
|
|
|
The i18n layer is deliberately incremental. Contributors can add a language
|
|
or improve a small group of UI messages without rewriting API contracts,
|
|
changing Pi's output, or introducing a large translation framework.
|