VS Code 扩展本地化(l10n)实战指南:以 l10n-sample 为例掌握vscode.l10n全流程
【免费下载链接】vscode-extension-samplesSample code illustrating the VS Code extension API.项目地址: https://gitcode.com/gh_mirrors/vs/vscode-extension-samples
本指南基于 vscode-extension-samples 仓库中的 l10n-sample 示例,系统讲解 VS Code 扩展国际化的完整技术栈:从静态清单(package.nls.json)的翻译、源码内字符串标记(vscode.l10n.t),到本地化字符串的提取工具(@vscode/l10n-dev),再到子进程中加载翻译(@vscode/l10n)。读完本文,你将能够为自己的扩展接入中、日等多语言支持,并能用伪本地化(Pseudolocalization)在不出海翻译团队的情况下自测本地化效果。
该示例实现了一个简单的扩展:注册Hello与Bye两条命令,在英文与日文两种 locale 下展示对应翻译,并演示了如何把本地化能力传递到扩展派生的子进程(Node CLI)中。
本地化的四大核心组成部分
VS Code 扩展源码级本地化由四个相互配合的部分组成(示例中全部有对应实现):
| 组件 | 作用 | 示例中的位置 |
|---|---|---|
package.nls.json | 翻译扩展package.json中的静态贡献(命令标题、菜单名等) | l10n-sample/package.nls.json、l10n-sample/package.nls.ja.json |
vscode.l10n.t | 官方 API,标记代码中"需要翻译"的字符串 | l10n-sample/src/extension.ts、l10n-sample/src/command/sayBye.ts |
@vscode/l10n-dev | 命令行工具,从扩展中提取 l10n 字符串、处理 XLF 文件 | 以npx方式调用(见下文) |
@vscode/l10n | 运行时库,在扩展的子进程中加载翻译 | l10n-sample/src/cli.ts |
其中@vscode/l10n-dev与@vscode/l10n分别声明在 l10n-sample/package.json 的devDependencies(^0.0.18)与dependencies(^0.0.10)中。
第一步:用package.nls.json翻译静态清单
扩展的package.json中所有面向用户展示的静态贡献(命令标题、配置项名称、菜单文案等)都无法通过源码 API 翻译,必须借助package.nls.json这一约定文件。
工作原理
package.nls.json中的键与package.json中的键一一对应,值为对应键的翻译。以示例为例,打开 l10n-sample/package.json 可以看到命令标题被%包裹:
{ "contributes": { "commands": [ { "command": "extension.sayHello", "title": "%extension.sayHello.title%" }, { "command": "extension.sayBye", "title": "%extension.sayBye.title%" } ] } }对应的 l10n-sample/package.nls.json(默认语言,即英文)为:
{ "extension.sayHello.title": "Hello", "extension.sayBye.title": "Bye" }而日文翻译 l10n-sample/package.nls.ja.json 为:
{ "extension.sayHello.title": "こんにちは", "extension.sayBye.title": "さようなら" }语言文件命名规则
package.nls.json:默认语言(通常是英文),不含语言代码后缀。package.nls.<LANG>.json:特定语言的翻译,例如package.nls.ja.json对应日文(ja为 BCP-47 语言代码)。- 当 VS Code 界面语言为对应 locale 时,会自动加载对应语言的
package.nls.*.json覆盖默认值;找不到对应语言文件时回退到package.nls.json。
第二步:用vscode.l10n.t翻译源码字符串
l10n是官方 VS Code API 中新增的命名空间(参见官方 vscode-api 文档中l10n部分),用于在扩展代码中标记"需要翻译"的字符串,取代了旧方案中的vscode-nls与vscode-nls-dev包。
三种函数签名
vscode.l10n.t()支持三种调用形式:
// 形式一:位置参数 function t(message: string, ...args: Array<string | number>): string; // 形式二:命名参数 function t(message: string, args: Record<string, any>): string; // 形式三:带翻译注释(推荐用于有占位符的字符串) function t(options: { message: string; args?: Array<string | number> | Record<string, any>; comment: string | string[] }): string;参数(占位符)机制
- 位置参数:字符串中的
{0}、{1}等占位符,会按索引被替换为对应实参。例如'Hello {0}'配合参数'CLI'会得到Hello CLI。 - 命名参数:字符串中的
{name}占位符,会从args对象中读取name属性填充。例如'Hello {done}'配合{ done: 'FINISHED' }会得到Hello FINISHED。
在 l10n-sample/src/extension.ts 中两种形式都有体现:
// 最简单的无参数形式 const message = vscode.l10n.t('Hello'); // 命名参数形式 const messageDone = vscode.l10n.t('Hello {done}', { done: 'FINISHED' });翻译注释(comment)
当字符串包含占位符时,译者往往不知道{0}代表什么。第三个签名中的comment字段正是用来给译者提供上下文说明的。在 l10n-sample/src/command/sayBye.ts 中可以看到完整示范:
import { l10n, window } from 'vscode'; export function sayByeCommand() { const message = l10n.t('Bye'); window.showInformationMessage(message); const message2 = l10n.t({ message: 'Bye {0}', args: ['Joey'], comment: ['{0} is a person\'s name'] }); window.showInformationMessage(message2); }这里comment数组说明{0}是"人名",译者便能据此给出符合语境的翻译。
翻译文件的加载规则
被vscode.l10n.t()标记的字符串,会在运行时从bundle.l10n.<LANG>.json文件中查找对应翻译(键为原始英文字符串,值为翻译)。本仓库提供了一份日文翻译 l10n-sample/l10n/bundle.l10n.ja.json:
{ "Bye": "さようなら", "Hello": "こんにちは", "Hello {0}": "こんにちは {0}", "Hello {done}": "こんにちは {done}" }注意键必须与源码中的t()调用字符串完全一致(包括占位符写法),翻译时占位符{0}、{done}需原样保留在目标语言字符串中,供运行时替换。
扩展清单中的l10n属性(必须配置)
要让上述机制生效,必须在扩展清单中声明l10n属性,告诉 VS Code 到哪里寻找本地化字符串文件。示例中 l10n-sample/package.json 配置如下:
{ // example "main": "./out/extension.js", // ... "l10n": "./l10n" }要点:
l10n的值必须是相对于扩展根目录的相对路径,指向存放bundle.l10n.<LANG>.json文件的目录。- 运行时 VS Code 会依据该属性加载与当前 locale 匹配的翻译文件,因此必须确保把翻译文件放在该目录下的正确位置。
- 路径可以自定义,但必须遵守"相对扩展根目录"这一约束。
第三步:用@vscode/l10n-dev提取与生成翻译文件
@vscode/l10n-dev是用于从扩展中提取本地化字符串、并处理 XLF 文件的命令行工具。示例中它作为 devDependency 引入,实际使用时通常通过npx直接运行。
导出bundle.l10n.json
从源码目录提取所有可本地化字符串,生成bundle.l10n.json:
npx @vscode/l10n-dev export -o ./l10n ./src-o ./l10n:指定输出目录。./src:指定扫描的源码目录。- 该命令会生成
l10n/bundle.l10n.json,其中包含扩展中所有可本地化的字符串(自动扫描vscode.l10n.t()调用)。之后即可为每种目标语言创建bundle.l10n.<LANG>.json并填写键值对。
伪本地化(Pseudolocalization)自测
如果不懂其他语言,又想验证本地化链路是否正常工作,可以使用@vscode/l10n-dev内置的伪本地化生成器,把字符串"翻译"成加了装饰性标记的伪文本:
npx @vscode/l10n-dev generate-pseudo -o ./l10n/ ./l10n/bundle.l10n.json ./package.nls.json该命令会生成package.nls.qps-ploc.json与bundle.l10n.qps-ploc.json两个文件。随后在 VS Code 中安装 Pseudo Language 语言包(qps-ploc是 VS Code 约定的伪本地化语言代码),并将 VS Code 界面语言切换为该 locale,扩展的字符串便会从对应的qps-ploc文件中读取。通过观察字符串是否被明显改写,可以快速确认哪些文案漏掉了翻译标记。
进阶:生成 XLF 文件对接翻译团队
XLF(XML Localisation Interchange File Format)是常见的翻译交付格式。官方团队通常将bundle.l10n.json与package.nls.json转换为 XLF 文件交给翻译团队:
npx @vscode/l10n-dev generate-xlf -o ./l10n-sample.xlf ./l10n/bundle.l10n.json ./package.nls.json-o ./l10n-sample.xlf:输出的 XLF 文件路径。- 后两个参数为输入:
bundle.l10n.json(源码字符串)与package.nls.json(静态清单字符串)。
l10n-dev工具同样支持将翻译完成的 XLF 文件反向转换回bundle.l10n.json与package.nls.json,从而把翻译结果落盘到仓库中(示例未涉及该反向流程,此处不做展开)。
第四步:用@vscode/l10n在子进程中加载翻译
扩展主进程中的vscode.l10n.t()由 VS Code 宿主负责加载翻译,但扩展可能通过child_process或任务派生 Node 子进程(如语言服务器、CLI 工具),这些子进程无法直接访问vscodeAPI,需要借助@vscode/l10n库在子进程内自行配置并加载翻译。
扩展侧:把翻译文件路径传给子进程
在 l10n-sample/src/extension.ts 中,扩展通过vscode.tasks.executeTask启动一个 shell 任务执行cli.js,并把vscode.l10n.uri的文件路径通过环境变量传给子进程:
await vscode.tasks.executeTask( new vscode.Task( { type: 'shell' }, vscode.TaskScope.Global, message, message, new vscode.ShellExecution(`node ${path.join(__dirname, 'cli.js')}`, { env: vscode.l10n.uri ? { EXTENSION_BUNDLE_PATH: vscode.l10n.uri?.fsPath } : undefined })));这里vscode.l10n.uri指向当前 locale 对应的bundle.l10n.<LANG>.json文件 URI,通过EXTENSION_BUNDLE_PATH环境变量注入子进程。
子进程侧:l10n.config()指定翻译文件
在 l10n-sample/src/cli.ts 中,子进程读取环境变量并调用@vscode/l10n的config()完成初始化:
import * as l10n from '@vscode/l10n'; if (process.env['EXTENSION_BUNDLE_PATH']) { l10n.config({ fsPath: process.env['EXTENSION_BUNDLE_PATH'] }); } const message = l10n.t('Hello {0}', 'CLI'); console.log(message + '\n');要点:
l10n.config({ fsPath })用于指定翻译 bundle 文件的本地路径;该库还支持通过uri传入远程 URI。- 配置完成后,
l10n.t()的调用行为与扩展主进程中的vscode.l10n.t()一致:有对应语言的翻译则返回翻译文本,否则回退到源字符串。 - 这类
l10n.t()调用同样会被@vscode/l10n-dev的提取工具识别,从而保证子进程中的字符串也不会漏出本地化流程之外。
端到端工作流总结
- 在
package.json中声明"l10n": "./l10n",并让所有静态贡献的标题使用%key%引用。 - 用
package.nls.json(+ 各语言的package.nls.<LANG>.json)提供静态文案翻译。 - 源码中用
vscode.l10n.t()标记动态字符串,占位符优先使用命名参数并辅以comment说明。 - 运行
npx @vscode/l10n-dev export -o ./l10n ./src提取字符串生成bundle.l10n.json。 - 为每种语言创建
bundle.l10n.<LANG>.json填写翻译;不想翻译时可先用generate-pseudo生成伪本地化文件自测。 - 若扩展派生子进程,通过环境变量把
vscode.l10n.uri传下去,子进程用@vscode/l10n的config()加载翻译。 - 对接翻译团队时,用
generate-xlf生成 XLF 交付,翻译完成后反向转换回仓库文件。
如需运行本示例,可在 l10n-sample 目录执行npm install与npm run compile(对应package.json中的vscode:prepublish/compile脚本),再按 VS Code 扩展调试(F5)方式启动扩展开发宿主,切换界面语言为日文即可看到两条命令的标题与弹窗文案随 locale 变化。
【免费下载链接】vscode-extension-samplesSample code illustrating the VS Code extension API.项目地址: https://gitcode.com/gh_mirrors/vs/vscode-extension-samples
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考