跳转至

界面文本与语言

用户阅读的所有文本都来自 packages/i18n(@openbot/i18n)中的语言目录。桌面端、共享 UI、Web 客户端和移动端使用相同的目录。英文是源语言。巴西葡萄牙语使用 pt 目录。德语(de)、西班牙语(es)、法语(fr)、意大利语(it)、日语(ja)、韩语(ko)、荷兰语(nl)、波兰语(pl)、巴西葡萄牙语(pt)、俄语(ru)、土耳其语(tr)和简体中文(zh)在桌面端、Web 客户端和移动端翻译所有键。缺失翻译时显示其英文源文本。

文本存放位置

packages/i18n/src/messages/<locale>/<area>.ts 存放一个区域的内容。区域是读者查找文本的界面:settings、chat、mobile/settings、error/team。

  • common 保存没有自身上下文的短词:Cancel、Save、Retry。如果翻译可能随屏幕变化,请将键放在该屏幕的区域中,即使英文相同也是如此。
  • format 保存单位和列表词,createFormat 会使用它们。
  • language 保存语言名称,例如听写语言。
  • error/* 和 status/* 保存由主进程、后端、主机或团队客户端发送的英文文本。参见 来自其他进程的文本。
  • mobile/* 保存仅移动应用显示的文本。移动端包包含 common、format、language、error/*、status/* 和 mobile/*,不包含桌面端区域。

en/index.ts、en/mobile.ts、en/source.ts 和 en/shared.ts 仅用于拼接区域模块。不要在其中放入键。

添加键

  1. 将键添加到其区域的英文模块中:
export const messages = defineMessages("settings", {
  "settings.language.title": "Language",
  "settings.agents.count": { one: "{count} agent", other: "{count} agents" },
});

defineMessages 会在键不以模块前缀开头时使构建失败。带有 {count} 的消息具有复数形式。其他值是 {name} 占位符。 2. 渲染它。将键写成字面量,以便 bun run i18n:check 和搜索能找到它。 3. 如果可以,请向 de/、es/、fr/、it/、ja/、ko/、nl/、pl/、pt/、ru/、tr/ 和 zh/ 中的同一模块添加翻译。 如果无法添加,请省略该键:屏幕会显示英文,并且检查会将该键报告为未翻译。 按英文顺序放置每个已翻译的键。bun run i18n:check --fix 会为你执行此操作。 4. 保持按钮、标签页、菜单项或徽章的标签简短。按钮不会换行或缩小,因此过长的标签会撑开所在行;徽章或菜单项可能会截断标签。找到最短的自然词,而不是逐字翻译:例如,德语中用 "Wiederholen" 来表示 "Retry",而不是 "Erneut versuchen"。如果译文长度约超过英文的 1.5 倍,请寻找更短的词。

对于从值到文本的映射,请映射到键,而不是文本:

const STATUS_LABEL = {
  running: "agent.status.running",
  stopped: "agent.status.stopped",
} as const satisfies Record<AgentStatus, AppTextKey>;

使用文本

界面 API
桌面渲染器和 packages/ui const { t, format, errorMessage } = useText() from @openbot/ui/text
移动端屏幕 useText() from @/shared/lib/text
React 之外的移动端代码(stores、Alert.alert 回调) currentText() from @/shared/lib/text
主进程、原生 UI(对话框、菜单、窗口标题) language.translate from LanguageService
主进程、后端、团队客户端:错误和状态文本 sourceText(key, params) from @openbot/i18n/source
  • 在 packages/ui 中,调用 useText()。不要添加 t 属性。文本上下文是共享 UI 从上下文读取的唯一应用值。在 Provider 之外,它会是英文,因此测试或 story 无需设置即可工作。
  • errorMessage(error, fallback) 返回读者语言的 userErrorMessage。请为其提供已翻译的回退文本:errorMessage(err, t("skill.saveFailed"))。
  • 渲染器的 I18nProvider 在每个窗口中提供文本上下文。Web 客户端和 Storybook 使用带固定 locale 的 StaticI18nProvider。

来自其他进程的文本

错误仅以消息形式通过 IPC 传递,Team API 会向每个已发布版本的客户端发送 { error: message }。因此,发送方编写英文,屏幕再将其翻译:

// main, backend, team client
throw new Error(sourceText("error.skill.nameTaken", { name }));

源键的英文文本必须与之前代码发送的文本逐字节相同。旧客户端以及比较错误文本的代码都依赖此一致性。要更改 error.* 或 status.* 键的英文文本,请将其视为一次线上变更(wire change):找到每个比较位置以及所有读取它的旧客户端。

屏幕会调用 errorMessage 或 sourceText(text)。这些函数会找到英文模板与文本匹配的键,并以读者的语言渲染该键。{count} 匹配数字,其他占位符匹配任意文本。{reason} 和 {detail} 会被再次翻译。没有模板匹配的文本(例如来自较旧主机或提供者的文本)会原样显示。远程客户端读取的是它自己的语言,而不是主机的语言。

当两个源模板可能匹配同一文本时,bun run i18n:check 会失败。

不要翻译

  • 协议值、IPC 通道、标识符和已保存的值。在渲染时,将已保存的值映射到键;绝不要保存已翻译的文本。
  • 分析事件名称和属性,以及日志行。
  • Agent 提示词,以及应用发送给提供者的文本。
  • 用户内容:消息、文件名、Agent 名称。
  • 产品名称和品牌名称:OpenBot、提供者名称、ZIP、JSON。
  • 只有畸形负载、编程错误或开发工具才能导致的错误,例如 *-inputs.ts 文件中的结构检查失败。

数字、日期和大小

不要在没有 locale 的情况下调用 toLocaleString(),也不要使用固定的 "en-US":两者都会忽略用户选择的语言。请使用 useText() 中的 format:

方法 输出
format.number(value, options) 1,234.5
format.compact(value) 1.2K
format.percent(ratio) 25% for 0.25
format.currencyUsd(value) $1.50
format.date(value, options) Intl.DateTimeFormat 选项
format.list(items) a, b, and c
format.fileSize(bytes) 512 B, 12 KB, 3.4 MB

移动应用运行在 Hermes 上,它可能不支持 Intl.PluralRules、Intl.ListFormat 或紧凑数字(尚未在设备上确认)。packages/i18n 为每一项都提供了回退方案。

检查

  • tools/ui-foundation/no-hardcoded-ui-text.grit 会对 src/renderer/src、packages/ui/src 和 apps/mobile/src 中的字面文本报错:JSX 文本、字面量 aria-label、title、placeholder、alt、label、description、accessibilityLabel 或 accessibilityHint、字面量 toast 或 alert,以及字面量 errorMessage 回退文本。测试和 stories 不在检查范围内。
  • bun run i18n:check 会在以下情况失败:键前缀错误、一个键出现在两个模块中、英文没有的翻译键、英文没有的占位符、复数形式与英文不一致、有歧义的源模板,以及代码中没有引用的英文键。它会报告未翻译的键,以及键顺序与英文不一致的已翻译模块。bun run i18n:check --fix 会将这些键按英文顺序排列。每个键会连同其上方注释和空行一起移动,且不改变任何文本。bun run i18n:check --json 会写入 .openbot-build/i18n-report.json。
  • Storybook 的工具栏中有一个 Locale(语言)菜单。使用它可查看每种语言下的 story。
  • bun scripts/i18n-fit-e2e.ts --storybook=<url> 会打开每种语言下的每个 story,打开其菜单,并查找被截断、超出方框或换行的控件文本。--locales=de,pl 和 --stories=<part of an id> 可缩短运行时间。它使用一个浏览器;使用 --workers=2 时,所有语言的所有 stories 大约需要一小时。它会写入 .openbot-build/i18n-fit/report.json,并为每个英文中不会出现的失败项生成一张截图。
  • 在开发应用打开的情况下,bun scripts/i18n-e2e.ts --allow-mutations 会让应用依次切换每种语言。当屏幕上的目录键或占位符出现时,或当英文能适配但控件文本无法适配时,它会失败。

添加语言

  1. 将 BCP 47 标签添加到 packages/contracts/src/app-language.ts 中的 APP_LANGUAGES。切勿重命名已发布的标签:否则已保存的偏好设置会被读回为未知语言。
  2. 将其添加到 packages/i18n/src/locale.ts 中的 TRANSLATED_LOCALES。
  3. 将 packages/i18n/src/messages/fr/ 复制到 messages/<tag>/。将每个模块清空为 {} as const satisfies PartialTranslation<typeof source>,然后尽可能翻译。
  4. 将新目录添加到 packages/i18n/src/app-catalogs.ts、source-catalogs.ts、format-catalogs.ts 和 mobile-catalogs.ts 中的 catalogs 映射,添加到 mobile-catalogs.native.ts 中的 switch,以及 lazy-catalogs.ts 中的 loaders 记录。公共 Web 构建使用 lazy-catalogs.ts 代替前三个文件。编译器会提示每个需要修改的位置。
  5. 将 CLDR 复数规则添加到 packages/i18n/src/plural.ts 中的 BUILT_IN_RULES。
  6. 将语言自身的名称及其 lang 标签添加到 packages/i18n/src/languages.ts。
  7. 运行 bun run typecheck:i18n 和 bun run i18n:check。在开发应用打开的情况下,运行 bun scripts/i18n-e2e.ts --allow-mutations;它会写入 .openbot-build/i18n-e2e/report.json。

当标签位于 APP_LANGUAGES 中时,两个“设置”屏幕都会显示新语言。

术语

在每种目录中保持这些术语一致。德语使用 du。西班牙语使用 tú,并避免地区特定措辞。法语使用 vous。日语使用 です・ます。韩语句子使用 해요체,按钮、标签页和标签使用简短名词。简体中文使用“你”,动作保持动词原形;在按钮或菜单项已标明动作时,不使用“请”。巴西葡萄牙语使用 você。俄语使用 вы。波兰语对动作使用非正式的 ty 命令式。意大利语对动作使用非正式的 tu 和命令式。荷兰语对动作使用非正式的 je 和命令式。pt-BR 和 pt-PT 系统设置都选择 pt 目录;数字和日期保留系统的区域惯例。所有 zh 系统设置都会选择简体中文目录,因此设置为 zh-TW 或 zh-HK 的电脑也会读取该目录。

英语 法语 日语 葡萄牙语(巴西) 俄语 土耳其语
agent agent エージェント agente агент ajan
chat discussion チャット chat чат sohbet
conversation conversation 会話 conversa диалог konuşma
channel canal チャンネル canal канал kanal
server serveur サーバー servidor сервер sunucu
host hôte ホスト computador anfitrião хост ana makine
skill compétence スキル habilidade навык beceri
routine routine ルーティン rotina регулярная задача rutin
workspace espace de travail ワークスペース espaço de trabalho рабочее пространство çalışma alanı
provider fournisseur プロバイダー provedor провайдер sağlayıcı
Settings Réglages 設定 Configurações Настройки Ayarlar

德语、西班牙语、波兰语和意大利语术语:

英语 德语 西班牙语 波兰语 意大利语
agent Agent agente agent agente
chat Chat chat czat chat
conversation Unterhaltung conversación rozmowa conversazione
channel Kanal canal kanał canale
server Server servidor serwer server
host Host host host host
skill Fähigkeit habilidad umiejętność skill
routine Routine rutina rutyna routine
workspace Arbeitsbereich espacio de trabajo obszar roboczy workspace
provider Anbieter proveedor dostawca provider
Settings Einstellungen Ajustes Ustawienia Impostazioni
Computer Use Computersteuerung Uso del equipo Sterowanie komputerem Controllo del computer
Steer Steuern Orientar Pokieruj Indirizza

韩语术语:

英语 韩语
agent 에이전트
chat 채팅
conversation 대화
channel 채널
server 서버
host 호스트
skill 스킬
routine 루틴
workspace 워크스페이스
provider 제공자
Settings 설정
Computer Use 컴퓨터 제어
Steer 방향 지시

荷兰语术语:

英语 荷兰语
agent agent
chat chat
conversation gesprek
channel kanaal
server server
host host
skill vaardigheid
routine routine
workspace werkruimte
provider aanbieder
permissions machtigingen
Settings Instellingen
Computer Use Computerbesturing
Steer Bijsturen

设置标签页或设置行的空间有限,因此可以使用术语的短形式。页面标题保留完整术语。Computer Use 标签页:德语 PC-Steuerung、意大利语 Controllo PC、波兰语 Sterowanie PC、俄语 Управление ПК。自动化任务标签页或行:俄语 Автозадачи。

简体中文术语:

英语 简体中文
agent 助手
chat, of an agent 对话
chat, of a messaging connection 聊天
conversation 对话
channel 频道
server 服务器
host 主机
skill 技能
routine 自动化任务
workspace 工作区
provider 服务商
Settings 设置
Computer Use Computer Use
Steer 引导
session 会话

中文只有一种复数形式,因此每个数量都采用 other 文本,并且 plural.ts 中 zh 对每个数字都返回 other。agent 的 chat 和 conversation 都对应“对话”;消息连接(如 Telegram 聊天)上的 chat 对应“聊天”;session 对应“会话”。目录使用中国大陆的引号“ ”,更倾向于用“该”表示确定的事物(该服务器、该文件),而不是书面语中的“此”。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 openbo/openbot