OpenBuddy
Download
Language简体中文· English only

Internationalization (i18n) Guide

How OpenBuddy localizes — single-file dict, locale switcher, SSR.

Source file: I18N.md7 min readUpdated 2018-10-20
On this page

This guide explains how OpenBuddy's UI strings are organized, how to add a new translation, and how to become a language maintainer.


Overview

OpenBuddy uses a custom in-house i18n system built on @openbuddy/ui-locale. It supports:

  • ICU MessageFormat (plurals, gender, selects)
  • Lazy locale loading (initial bundle stays small)
  • Plural-rule awareness (CLDR)
  • Right-to-left (RTL) layout (experimental)
  • Date / number / currency formatting via Intl.*

The current UI ships in:

Locale Code Status Coverage
English en ✅ Default 100%
简体中文 zh-CN ✅ Shipped 100%
日本語 ja 🚧 In progress 35%
한국어 ko 🚧 In progress 28%
Español es 🚧 In progress 22%
Deutsch de 🚧 In progress 18%

Coverage is measured nightly by pnpm i18n:coverage.

File layout

text
src/locales/
├── en-US.json              # English (default)
├── zh-CN.json              # 简体中文
└── …

Each locale is a single JSON file. Strings are organized into top-level keys by feature:

  • common — shared strings (buttons, errors)
  • composer — chat composer
  • settings — settings panels
  • conversation — chat history / messages
  • permission — permission prompts
  • auth — authentication flows
  • billing — billing & credits
  • tenant — tenant / org selector
  • marketplace — plugin marketplace
  • skill — skill library
  • scene — scene tabs
  • about — about / version info

Adding a new locale

  1. Open an issue with the i18n and locale: labels, naming your language and intended maintainer.
  2. Fork the repo.
  3. Create src/locales/<your-locale>.json (copy src/locales/en-US.json).
  4. Translate every string. Keep keys identical; only change values.
  5. Test locally: pnpm i18n:check <your-locale>.
  6. Open a PR with title i18n(<locale>): initial translation.
  7. Become maintainer — you'll be added to the @louloulin/i18n-<locale> team and granted merge rights for src/locales/<your-locale>.json only.

Translation guidelines

Do

  • ✅ Match the register of the original (formal/informal).
  • ✅ Use standard locale conventions (date formats, decimal separators, etc.).
  • ✅ Preserve placeholders verbatim: {name}, {count, plural, one {# item} other {# items}}.
  • ✅ Test plurals for one, few, many, other — singular/plural isn't 1:1 in many languages.
  • ✅ Keep brand names untranslated (OpenBuddy, Pi, Cordis, WorkBuddy, …).
  • ✅ Keep code identifiers untranslated (provider names, IPC channel names).

Don't

  • ❌ Translate brand names or technical proper nouns.
  • ❌ Add new keys that don't exist in en/. New keys must first be added to en/.
  • ❌ Use machine translation as the final word — always human-review.
  • ❌ Embed HTML or Markdown in translation values. Use the rich formatter instead.

Naming conventions

Language Native name Code
English English en
简体中文 简体中文 zh-CN
繁體中文 繁體中文 zh-TW
日本語 日本語 ja
한국어 한국어 ko
Español Español es
Français Français fr
Deutsch Deutsch de
Português Português pt
Русский Русский ru
العربية العربية ar
हिन्दी हिन्दी hi

Plural rules

ICU MessageFormat is used for plurals:

json
{
  "files.count": "{count, plural, one {# file} other {# files}}",
  "files.count_compact": "{count, plural, one {# file} other {# files}}",
  "minutes.ago": "{minutes, plural, one {# minute ago} other {# minutes ago}}"
}

Verify your plural forms against the CLDR plural rules table.

Right-to-left (RTL)

RTL is supported via dir="rtl" on the root element. The CSS uses logical properties (margin-inline-start, padding-block-end) which automatically flip.

For an RTL locale:

  1. Set direction: "rtl" in your locale config.
  2. Verify all icons (especially directional arrows) have RTL counterparts.
  3. Test on at least one RTL layout (Arabic, Hebrew).

Becoming a language maintainer

  1. Submit 3 substantial PRs of translation work.
  2. File a Discussion titled i18n: request maintainership for <locale>.
  3. Get 👍 from 2 current maintainers.
  4. A maintainer adds you to @louloulin/i18n-<locale>.
  5. You can now merge src/locales/<your-locale>.json directly.

Tools

Tool Command Purpose
Coverage report pnpm i18n:coverage Show % translated per locale
Missing keys pnpm i18n:missing <locale> List keys present in en/ but not in <locale>/
Stale keys pnpm i18n:stale Find keys in <locale>/ that aren't in en/ anymore
Format check pnpm i18n:format Validate JSON files
Lint pnpm i18n:lint Check for hardcoded strings in src/
Sync pnpm i18n:sync Pull new keys from en/ into all locales (mark as TODO)

概述

OpenBuddy 用自研的 @openbuddy/ui-locale 做国际化。支持:

  • ICU MessageFormat(复数、性别、select)
  • 按需加载 locale(初始 bundle 体积小)
  • CLDR 复数规则
  • RTL(右到左)布局(实验性)
  • Intl.* 日期/数字/货币格式化

当前 UI 已发布的语言:

Locale 代码 状态 覆盖率
English en ✅ 默认 100%
简体中文 zh-CN ✅ 出货 100%
日本語 ja 🚧 进行中 35%
한국어 ko 🚧 进行中 28%
Español es 🚧 进行中 22%
Deutsch de 🚧 进行中 18%

覆盖率每晚由 pnpm i18n:coverage 测算。

文件结构

text
src/locales/
├── en-US.json              # English(默认)
├── zh-CN.json              # 简体中文
└── …

每个 locale 一个 JSON 文件。字符串按 feature 划分顶层 key:

(同英文列表)

新增 locale

  1. 开 Issuei18nlocale: 标签,注明语言与计划维护者。
  2. Fork 仓库。
  3. 新建 src/locales/<你的-locale>.json(拷 src/locales/en-US.json)。
  4. 翻译 所有字符串。键保持不变,只改值。
  5. 本地测试:pnpm i18n:check <你的-locale>
  6. 提 PR,标题 i18n(<locale>): initial translation
  7. 成为维护者 —— 你会被加进 @louloulin/i18n-<locale> 团队,获得 src/locales/<你的-locale>/ 的合并权。

翻译规范

应该

  • ✅ 匹配原文语域(正式/非正式)
  • ✅ 使用本地习惯(日期格式、小数分隔符等)
  • ✅ 保留占位符原样:{name}{count, plural, one {# item} other {# items}}
  • ✅ 测 onefewmanyother —— 单复数在很多语言中不是 1:1
  • 品牌名保持原文(OpenBuddy、Pi、Cordis、WorkBuddy……)
  • 代码标识保持原文(Provider 名、IPC 通道名)

不要

  • ❌ 翻译品牌名或技术专有名词
  • ❌ 新增 en/ 中不存在的 key —— 必须先在 en/
  • ❌ 把机器翻译当最终结果 —— 始终人审
  • ❌ 在翻译值里塞 HTML 或 Markdown。用 rich 格式化器。

语言命名规范

语言 本地名称 代码
English English en
简体中文 简体中文 zh-CN
繁體中文 繁體中文 zh-TW
日本語 日本語 ja
한국어 한국어 ko
Español Español es
Français Français fr
Deutsch Deutsch de
Português Português pt
Русский Русский ru
العربية العربية ar
हिन्दी हिन्दी hi

复数规则

复数用 ICU MessageFormat:

json
{
  "files.count": "{count, plural, one {# file} other {# files}}",
  "minutes.ago": "{minutes, plural, one {# minute ago} other {# minutes ago}}"
}

对照 CLDR 复数规则表 校验。

RTL(右到左)

RTL 通过根元素 dir="rtl" 支持。CSS 用逻辑属性(margin-inline-startpadding-block-end),自动翻转。

RTL locale:

  1. 在 locale config 中设 direction: "rtl"
  2. 验证所有图标(尤其是方向箭头)有 RTL 对应版本。
  3. 至少在一个 RTL 布局(阿拉伯语、希伯来语)上测。

成为语言维护者

  1. 提交 3 个实质性翻译 PR
  2. Discussion 开贴,标题 i18n: request maintainership for <locale>
  3. 获得 2 位当前维护者点赞。
  4. 维护者把你加进 @louloulin/i18n-<locale>
  5. 你可以直接合并 src/locales/<你的-locale>/**

工具

工具 命令 用途
覆盖率报告 pnpm i18n:coverage 显示每个 locale 的翻译率
缺失 key pnpm i18n:missing <locale> 列出在 en/<locale>/ 没有的 key
过期 key pnpm i18n:stale <locale>/ 中已经不在 en/ 的 key
格式校验 pnpm i18n:format 验证 JSON 文件
Lint pnpm i18n:lint 检查 src/ 中的硬编码字符串
同步 pnpm i18n:sync en/ 拉新 key 到所有 locale(标记 TODO)

Help us reach every developer. / 帮我们触达每一位开发者。

认领一种语言?开 Discussion 标签 i18n