插件国际化
本页说明插件如何提供多语言文本:哪些字段会被解析、资源文件放在哪里、宿主如何选择语言与回退、运行时消息如何格式化,以及如何用 Devkit 校验。
国际化只影响展示。请求参数、返回的歌曲元数据、歌词内容、写入音频的标签值和插件行为都不随语言变化。
文本值:@ 引用与字面量
Manifest 中显示给用户的文本(插件名称、描述、配置项标题、选项文本等)都写成普通字符串,用前缀区分两种含义:
| 值 | 含义 | 界面显示 |
|---|---|---|
"@plugin.name" | 引用语言资源里的键 plugin.name | 当前语言的文本,例如“示例搜索源” |
"@@price" | 以 @ 开头的普通文本 | 去掉一个 @,显示 @price |
"Example Source" | 普通文本 | 原样显示,不去查资源 |
三条限制:
- 只解析一层。资源文件里的值不会再被当成引用,所以
"tip": "@plugin.name"会原样显示@plugin.name。 - 引用不能为空,
"@"不合法。 - 用到引用就必须声明
i18n,且minHostApiVersion不低于 4,否则安装会失败。
哪些字段会按上面的规则解析
| 位置 | 解析 | 说明 |
|---|---|---|
manifest.name | 是 | 插件名称 |
manifest.description | 是 | 插件描述 |
配置项 title | 是 | 配置项标题 |
配置项 summary | 是 | 标题下的说明 |
配置项 group | 是 | 界面按原始值归组,卡片标题用解析结果,见下方说明 |
选项 label、选项 summary | 是 | 下拉选项的文本 |
markdown 配置项的 defaultValue | 是 | 说明正文,通常把整段正文放在资源里 |
其他配置项的 defaultValue | 否 | 业务默认值,不能翻译,例如 "cn"、"true" |
配置项 key、选项 value、dependency | 否 | 稳定标识,必须保持原样 |
id、entry、icon、author、versionCode、versionName、apiVersion、capabilities | 否 | 不参与界面文本 |
group 既是分组身份也是卡片标题:界面按 group 的原始字符串把配置项归到同一张卡片,卡片标题显示它解析后的文本。所以同一组要写完全相同的 group 值(通常是同一个 @group.xxx 引用),不要每种语言写一个值。留空 group 的配置项归入应用自带的“基础”分组。
添加一种语言
以新增简体中文为例。
1. 在插件根目录建资源文件 locales/zh-Hans.json:
{
"plugin.description": "示例搜索源插件",
"config.region.title": "地区",
"config.region.summary": "接口使用的地区代码",
"group.request": "请求",
"config.region.option.cn": "中国大陆",
"config.region.option.us": "美国"
}2. 在 Manifest 中把要翻译的文本换成引用,并声明资源:
{
"id": "com.example.source",
"name": "Example Source",
"description": "@plugin.description",
"versionCode": 2,
"versionName": "1.1.0",
"apiVersion": 4,
"minHostApiVersion": 4,
"configFields": [
{
"key": "region",
"title": "@config.region.title",
"summary": "@config.region.summary",
"group": "@group.request",
"type": "dropdown",
"defaultValue": "cn",
"options": [
{ "value": "cn", "label": "@config.region.option.cn" },
{ "value": "us", "label": "@config.region.option.us" }
]
}
],
"i18n": {
"defaultLocale": "en",
"resources": {
"en": "locales/en.json",
"zh-Hans": "locales/zh-Hans.json"
}
}
}上面每个字段的值本身还是字符串:要翻译就写 @ 引用,不需要翻译就写字面量。引用写法不用再留一份原文,缺少某种语言的翻译时会自动用默认资源的文本;而品牌名、尺寸这类与语言无关的文本(例如 Example Source、500 × 500)直接写字面量。
3. 建默认资源 locales/en.json。defaultLocale 指向的这份文件必须包含所有被引用的键,其他语言找不到翻译时都用它:
{
"plugin.description": "Example source plugin",
"config.region.title": "Region",
"config.region.summary": "Region code used by the API",
"group.request": "Requests",
"config.region.option.cn": "Mainland China",
"config.region.option.us": "United States"
}4. 校验:
node tools/plugin-devkit/src/cli.js validate ./my-plugin
node tools/plugin-devkit/src/cli.js inspect ./my-plugin --locales zh-CNinspect --locales 会按给定语言偏好显示解析后的插件名称;加 --json 还能看到描述与完整的配置项文本,可以直接确认翻译是否生效。
资源文件约定
<plugin-root>/
├── manifest.json
├── source.js
├── locales/
│ ├── en.json
│ ├── zh-Hans.json
│ └── zh-Hant.json
└── icon.png- 一种语言一个 UTF-8 JSON 文件,内容是「资源键 → 字符串」的字典,值必须是字符串。
- 语言标签用规范的 BCP 47 写法,例如
en、ja、pt-BR、zh-Hans、zh-Hant;它就是resources的键。 - 每个文件不超过 512 KiB;每个插件 1 到 64 种语言。
- 路径必须是插件目录内的相对
.json路径,不能用绝对路径、\或..。 - 资源随插件一起打包,不要写进
includeDirs:它不是脚本。
键名没有强制格式,按引用位置分组便于维护:
| 前缀 | 用途 |
|---|---|
plugin.* | 名称与描述 |
config.<配置项 key>.* | 配置项标题、说明、选项(建议 config.<key>.option.<value>、正文用 config.<key>.content) |
group.<分组标识> | 分组标题 |
error.*、warn.*、status.* | 运行时消息,见下文 |
键的规则:
- 默认资源要包含两类键:所有被
@引用的键,以及任一语言资源里出现过的键。 - 翻译资源可以少写键,缺少时去父级资源和默认资源里找;但不能多出默认资源里没有的键。
- 带占位符的运行时消息,各语言中
%s、%d的编号和类型必须一致。 - 界面文本里的
%、%s、%20只是普通字符,不参与占位符校验。
语言匹配与回退
宿主按应用当前的语言偏好列表依次尝试:完整标签匹配 → 同语言同文字体系的资源 → 同文字体系地区资源 → defaultLocale。
以只提供 en、zh-Hans、zh-Hant 的插件为例:
| 应用语言 | 选中的资源 | 原因 |
|---|---|---|
zh-CN | zh-Hans | 同语言、同文字体系 |
zh-TW、zh-HK | zh-Hant | 同语言、同文字体系 |
en-US、en-GB | en | 完整标签或同语言匹配 |
de-DE、ja-JP | en | 没有匹配,回退到默认资源 |
文字体系(简体 Hans、繁体 Hant 等)由系统 ICU 数据补全,匹配结果只取决于资源的语言标签;少见语言在不同系统上可能有差异。
选中语言后如果缺键,依次查找:
- 选中语言的资源
- 父级资源:例如选中的是
zh-Hant-TW,就用zh-Hant - 默认资源
界面在展示时才解析引用,数据库里保存的仍是 Manifest 原始文本,所以切换语言不需要重新安装或迁移插件。
运行时消息
脚本里需要展示给用户的消息同样走资源,通过 Platform.i18n 读取:
| 函数 | 说明 |
|---|---|
Platform.i18n.getLocale() | 返回当前选中的语言标签,例如 "zh-Hans";插件没有 i18n 时返回 "und" |
Platform.i18n.t(key, ...args) | 取回 key 的文本;传入参数时按占位符格式化,不传参数时原样返回 |
// locales/en.json: "error.candidateFailed": "Candidate %1$s failed: %2$s"
// locales/zh-Hans.json: "error.candidateFailed": "候选 %1$s 获取失败:%2$s"
try {
// ...
} catch (e) {
Platform.log.warn("Example", Platform.i18n.t(
"error.candidateFailed",
String(song.title || song.id || ""),
String(e && e.message ? e.message : e)
));
}占位符规则:
- 支持
%s、%d、%1$s、%2$d和%%,最多 64 个参数。 - 只有一个参数时可以省略编号;多个参数必须写从 1 开始连续的位置编号,不能混用带编号与不带编号的写法。
%s只接受字符串;%d只接受 JavaScript 安全范围内的整数,小数、数字字符串、空值都会抛错。- 参数按可变参数传入,不传数组;数量必须与资源中声明的位置数一致。
- 译文可以换序或重复使用同一个编号,但不能增删参数、不能改变类型。
- 不支持浮点、宽度、日期格式和复数规则。
调用注意:
- 键在选中语言、父级资源和默认资源里都不存在时抛出
Unknown plugin string: <key>;参数数量或类型不符也会抛错,插件可以自行捕获。 - 运行时键只在脚本里引用,Manifest 无法检查它们,因此要保证脚本中的键名与资源一致。任一语言声明了某个键,默认资源里必须也有这个键;带占位符的键还要求各语言签名一致。
- 运行时不参与业务判断:不要用
getLocale()决定请求参数(例如接口的语言参数)。这类需求应做成独立的配置项,否则用户切换界面语言会改变搜索结果与缓存键。 Platform.i18n属于宿主 API 4,调用它的插件应声明minHostApiVersion: 4;版本字段的区别见 API 版本沿革。
用 Devkit 校验
在插件仓库根目录执行:
node tools/plugin-devkit/src/cli.js validate ./my-plugin
node tools/plugin-devkit/src/cli.js inspect ./my-plugin --locales zh-CN,en
node tools/plugin-devkit/src/cli.js test ./my-plugin searchSongs --keyword "晴天" --locales zh-CN,en
node tools/plugin-devkit/src/cli.js pack ./my-plugin| 命令 | 会检查什么 |
|---|---|
validate | 资源路径、语言标签规范、defaultLocale 是否存在、默认资源是否覆盖全部引用键、翻译是否多出默认资源没有的键、运行时键的占位符签名是否一致、引用是否满足 minHostApiVersion >= 4 |
inspect --locales | 按语言偏好显示解析后的插件名称;加 --json 可以看到解析后的描述与完整配置项文本 |
test --locales | 用真实脚本运行,确认运行时消息在目标语言下的输出 |
pack | 把 locales/ 一起打进安装包 |
Devkit 的完整命令与限制见本地调试插件。
发布前检查
- 需要翻译的文本字段都换成了
@引用,不需要翻译的保持字面量。 - 默认资源覆盖全部引用键,且没有多余的、默认资源未声明的键。
- 同组配置项使用完全相同的
group值。 - 运行时消息键在每种语言中的占位符编号与类型一致。
- 脚本里用到的运行时键与资源文件中的键一一对应。
- 使用引用或
Platform.i18n的插件已声明minHostApiVersion: 4。 validate通过,并用inspect --locales、test --locales检查过目标语言。
常见问题
Q:界面还是显示 @config.region.title?
说明这个键没有解析成功。检查 i18n 是否声明、defaultLocale 是否在 resources 里,以及默认资源是否有这个键——正常情况下默认资源缺键会在安装阶段就报错,页面上不会漏出 @。如果字符串本来就该以 @ 开头,请写成 @@...。
Q:为什么有几个字段不能翻译?
key、选项 value、dependency 和普通配置项的 defaultValue 都是稳定标识或业务值,宿主不会解析它们。需要多语言的默认值请改成下拉选项,用 label 提供文本。
Q:markdown 配置项怎么翻译?
把正文当作普通资源:"defaultValue": "@config.help.content",然后在各语言资源里给 config.help.content 写正文。正文里的 #、-、链接等 Markdown 语法保留在资源值里。
Q:翻译可以缺键吗?
可以。缺键会依次回退到父级资源、默认资源。但不能新增默认资源里没有的键——这通常是键名拼写错误,validate 会直接报出来。
Q:粘贴带 % 的文本会不会报错?
界面文本不会:只有含 %s/%d 这类占位符的运行时资源才做签名校验,100%、%20 会被当成普通字符。运行时消息里如果要显示字面百分号,请写 %%。
Q:怎么调试“某个键没生效”?
先跑 validate,它会给出具体的键名与原因;再用 inspect --locales <标签> 看解析结果,最后用 test --locales 看运行时消息。日志里出现 Unknown plugin string: <key> 表示脚本引用的键在资源里不存在。
Q:用户改了插件名称,翻译会被覆盖吗?
用户自定义名称优先于翻译,其他文本仍按语言解析。
Q:旧插件没有 i18n 会怎样?
继续使用 Manifest 中的原始文本,行为不变。只有当字段里出现 @ 引用时,才必须声明 i18n。