多语言(i18n)
内置插件怎样同时提供中文和英文。清单里的文字在 `i18n.en` 里写英文版,页面和小组件的文字放进 `defineStrings` 表,日期交给 SDK 的格式化函数,给模型看的文字不翻译。
App 支持简体中文(zh-Hans)和英文(en)。中文是源语言:每个字符串先用中文写,再补上英文版。用户在设置里选择「跟随系统」「简体中文」或「English」。
每个内置插件(bundled plugin)都必须把两种语言写全。插件仓库里的测试 plugins/i18n.test.ts 会逐个检查插件,缺少英文字符串就会失败。本页讲的是内置插件。插件文档的英文文字全部写在清单的 i18n.en 部分(见声明式插件)。
哪些要翻译
给人看的文字要翻译。只给模型看的文字,比如工具描述和插件说明,只写一份,不翻译。
| 文字 | 位置 | 是否翻译 |
|---|---|---|
插件 name、description | definePlugin(中文)+ i18n.en | 是 |
工具 title | tools.<name>.title + i18n.en.tools.<name>.title | 是 |
工具 summary(仅插件文档) | 工具文件里的 summary + i18n.en.tools.<name>.summary | 是 |
| 小组件标题和描述 | contributes.widgets + i18n.en.widgets | 是 |
| 页面标题 | contributes.screens + i18n.en.screens | 是 |
| 设置项 | contributes.settings + i18n.en.settings.<screen> | 是 |
| 快捷指令 | contributes.commands + i18n.en.commands.<name>(description、argHint、aliases、prompt) | 是 |
| 数据源标题 | dataSources + i18n.en.dataSources | 是 |
| 模型服务商名称、提示、模型名 | providers + i18n.en.providers | 是 |
| 页面和小组件里的文字 | src/strings.ts(defineStrings) | 是 |
工具 description、instructions、context、工具结果 | 给模型看 | 否 |
清单的英文版本
中文字符串照常写在 definePlugin 里,英文版写在 i18n.en 里。下面的例子来自 shuttle 插件:
definePlugin({
id: 'shuttle',
name: '校车',
description: '燕园—新燕园(昌平)通勤班车和新燕园—地铁北安河快速公交时刻表,算出下一班车。',
i18n: {
en: {
name: 'Shuttle bus',
description: 'Timetables for the Yanyuan–Xinyanyuan (Changping) commuter shuttle and the Xinyanyuan–Bei’anhe metro express bus, with the next departure worked out for you.',
tools: { next: { title: 'Next shuttle' }, timetable: { title: 'Shuttle timetable' } },
widgets: { next: { title: 'Next shuttle', description: '…' } },
commands: { shuttle: { description: 'Next shuttle (works offline)', argHint: 'e.g. Yanyuan to Xinyanyuan', aliases: ['bus'] } },
dataSources: { next: { title: 'Next shuttle' } },
},
},
// …
});运行时,en 里缺少的字段会回退到中文。但覆盖测试仍会失败,所以请全部填上。
字符串表
页面和小组件里的文字放在字符串表里,每个插件一张,位于 src/strings.ts。先用类型定义一次所有的键,再为每种语言分别给出值:
import { defineStrings } from '@pku-harness/sdk';
type Strings = {
none: string;
later: (list: string) => string;
};
export const strings = defineStrings<Strings>({
'zh-Hans': { none: '今明两天没有班次', later: (list) => `之后 ${list}` },
en: { none: 'No buses today or tomorrow', later: (list) => `Then ${list}` },
});const s = strings[host.useLocale()];
<Text>{s.none}</Text>在组件里,用 host.useLocale() 选出用户所用语言的那张表。
defineStrings 会拿 en 和 zh-Hans 做类型检查:两者的键必须相同,值的类型也必须相同。带参数的字符串写成函数。不要把翻译好的片段拼接成句子,因为两种语言的语序不同。
日期和数字
App 运行在 JavaScript 引擎 Hermes 上,而 Hermes 会忽略 Intl 的语言设置,toLocaleDateString 之类的函数因此会给出错误的格式。请改用 SDK 提供的格式化函数,其中大多数接收来自 host.useLocale() 的语言参数 l:
| 函数 | zh-Hans | en |
|---|---|---|
formatClock(d) | 21:04 | 21:04 |
formatWeekday(d, l) | 周一 | Mon |
formatLocalDate(d, l) | 10月18日 | Oct 18 |
formatLocalDateTime(d, l) | 10月18日 21:04 | Oct 18, 21:04 |
formatRelative(ms, l) | 刚刚, 3 分钟前, 昨天 21:04 | Just now, 3 min ago, Yesterday 21:04 |
formatDay(d, l) | 今天, 明天, 周三 | Today, Tomorrow, Wed |
formatCountdown(ms, l) | 2小时10分 | 2h 10m |
plural(n, one) | — | 1 book, 3 books |
详见 SDK → format。
英文文案规范
覆盖测试还会检查英文字符串的写法:
- 除了括号里的注释和引号内的内容,不能出现中文。
Treehole (树洞)是可以的。 - 用弯撇号(
’),不用直撇号(')。 - 不用感叹号。
- 简短,并使用句首大写(sentence case),即只有第一个单词和专有名词首字母大写。校园专有名词用官方英文名或拼音:Peking University、Treehole (树洞)、Course (教学网 / Blackboard)、Campus Card。
中文文字也一样:写得简短,不用感叹号。