VS Code插件国际化

使用vscode-nls做VS Code插件的国际化

最近项目需要参考VS Code的插件国际化方案,网上的资料比较少,所以研究了一下,算是有点成果,记录一下。

VS Code插件的国际化,官方提供了两个工具 vscode-nlsvscode-nls-dev ,vscode-nls在源码中引入,对字符串进行翻译,vscode-nls-dev用于提取源码中需要翻译的字符串,生成指定格式的国际化文件。本文参考了官方提供的国际化示例,同样使用gulp自动化构建工具。

个人没有做过VS Code插件开发,所以以最简单的插件做示例,对普通Javascript、Typescript、webpack三种插件开发或打包方式做国际化,三者的区别主要在gulpfile.js的编写。

一、准备工作

准备个VS Code插件项目,安装vscode-nls、vscode-nls-dev、gulp三个包,后两个只在开发环境中使用。

二、步骤

1. 源码中添加翻译代码

Typescript只是导入vscode-nls不同,其他一致。

const nls = require('vscode-nls');
const localize = nls.loadMessageBundle();
let text = localize('nlssample.hello', 'Hello!');
// 带有参数的翻译
text = localize('nlssample.time', 'Time: {0}!', Date.now());

或者使用config自定义配置:

const localize = nls.config({
    
    
	locale:'zh-cn',
	bundleFormat: nls.BundleFormat.standalone,
	messageFormat: nls.MessageFormat.both
})();

国际化文件有三种存在方式

  • VS Code语言包形式,主要是微软自己的出的一些插件
  • 所有文件的翻译信息合并到单一的bundle文件
  • 所有文件的翻译信息独立存在,与源码保持同样目录结构

config的参数主要就是配置如何使用国际化文件的。其中bundleFormat决定是使用语言包还是插件自己的国际化文件,由于vscode-nls的限制,两者不能共存,messageFormat用于配置使用单文件还是多文件或者都允许,但优先单文件。

nls.config一般只需要在插件入口调用一次,该接口实际返回的也是loadMessageBundle。其他文件中需要调用loadMessageBundle,该方法加载js文件相对应的翻译信息并返回一个方法。vscode-nls-dev会在loadMessageBundle参数插入当前文件路径作为上下文。

2. 编写gulpfile.js文件

Javascript 插件代码如下,其他类型文件参考个人的插件示例里的gulpfile.js:

const gulp = require('gulp');
const del = require('del');
const rename = require('gulp-rename');
const es = require('event-stream');
const nls = require('vscode-nls-dev');

// 支持的语言
const languages = [{
    
     folderName: 'zh-cn', id: 'zh-cn' }];

const cleanout = function () {
    
    
    return del(['out/**', 'package.nls.*.json']);
}

gulp.task('clean', cleanout);

// 源码
const sourcesNsl = function () {
    
    
    var r = gulp.src(['./**/*.js', '!node_modules/**', '!gulpfile.js'])
        .pipe(nls.rewriteLocalizeCalls())
        .pipe(nls.createAdditionalLanguageFiles(languages, 'i18n', ''))
        .pipe(nls.bundleMetaDataFiles('js-nls-sample', ''))
        .pipe(nls.bundleLanguageFiles());

    // 输出到out目录
    return r.pipe(gulp.dest("out"));
};

// package.json
const packageNls = function () {
    
    
    return gulp.src(['package.nls.json'], {
    
     allowEmpty: true })
        .pipe(nls.createAdditionalLanguageFiles(languages, 'i18n'))
        .pipe(gulp.dest('.'));
};

gulp.task('nls', gulp.series(cleanout, sourcesNsl, packageNls));

Typescript和Webpack参考 Git项目 。该文件有比较多的细节,如果项目有不一样的配置,可以自己修改看看每一步都做了什么。我简单说明一下单一国际化文件的生成过程,对应sourcesNsl定义的任务:

  • 读取项目下相关的js文件
  • 匹配localize的调用,获取消息id与默认的字符串,就是前两个参数,生成多个metadata文件
  • 查找翻译好的字符串,与上一步的metadata文件生成翻译文件
  • 合并所有metadata文件到单一文件
  • 合并所有翻译文件到单一的国际化文件

3. 翻译

上一步只是创建了任务用于生成单一国际化文件,执行任务生成的国际化文件仍旧是原始字符串,原因是第三步需要提前准备好与metadata文件目录结构一致的翻译文件。可以再建立一个任务用于提取生成。

// 提取需要翻译的字符串到i18n/base目录,方便翻译
const sourcesMsg = function () {
    
    
    const suffix = '.i18n.json';
    var r = gulp.src(['./**/*.js', '!node_modules/**', '!out/**', '!gulpfile.js'])
        .pipe(nls.rewriteLocalizeCalls())
        .pipe(nls.createKeyValuePairFile())
        .pipe(es.through(function (file) {
    
    
            // 仅处理.i18n.json
            if (file.path.indexOf(suffix, file.path.length - suffix.length) !== -1) {
    
    
                this.queue(file);
            }
        }))
        .pipe(gulp.dest(`./i18n/base`));
    return r;
};

// package.nls.json,结构一致,只需要拷贝一份
const packageMsg = function () {
    
    
    var r = gulp.src(['package.nls.json'], {
    
     allowEmpty: true })
        .pipe(rename({
    
     basename: "package", suffix: ".i18n" }))
        .pipe(gulp.dest(`./i18n/base`));
    return r;
};

const messageTask = gulp.series(sourcesMsg, packageMsg);
gulp.task('message', messageTask);

与上一步一样,涉及到package.nls.json文件,这个文件是package.json里需要做的翻译,需要手动编写,gulp任务也只是拷贝了一份。
格式如下:

{
    
    
    "jsnls.sample.description": "javascript extension nls example for VS Code"
}

4. 运行任务

运行gulp message(或者自己配置tasks.json)提取翻译信息到指定目录。上面的任务默认输出到i18n/base目录,拷贝一份该目录,重命名为步骤2中支持的语言的folderName:

const languages = [{
    
     folderName: 'zh-cn', id: 'zh-cn' }];

运行gulp nls,生成单一的翻译文件,同时会生成nls.metadata.json、nls.metadata.header.json、nls.bundle.json三个文件。


三、其他补充

1. 普通Javascript插件

  • 普通Javascript插件项目,gulp任务需要在js文件中插入上下文,重新输出到out目录,需要手动修改package.json的插件入口

2. Typescript插件

  • 个人对这类项目的Sourcemap概念不了解,gulp任务里涉及到gulp-sourcemaps相关的操作,开发者可以自己调整
  • 如果gulp nls执行成功,生成了nls.bundle.zh-cn.json且内容为中文,但运行时还是原始字符串,可能是VS Code重新编译了项目,而没有重写输出的js文件。需要更改配置,可以参考官方的示例。

3. Webpack插件

  • Webpack项目是通过在webpack.config.js中配置module.rules实现重写js和提取翻译,所以gulp任务中没有 nls.rewriteLocalizeCalls(),增加以下配置:
    {
          
          
       loader: 'vscode-nls-dev/lib/webpack-loader',
        options: {
          
          
            base: path.join(__dirname, 'src')
        } 
    }
    
    base为源码路径,拼接src是为了去掉翻译信息上下文的src
  • 上面的配置会在输出目录生成多个metadata文件,生成单一的国际化文件需要借助这些文件:
    const sourcesNsl = function () {
          
          
        return gulp.src(['**/*.nls.metadata.json'], {
          
           base: "./out" })
                .pipe(nls.createAdditionalLanguageFiles(languages, 'i18n', ''))
                .pipe(nls.bundleMetaDataFiles('webpack-nls-sample', ''))
                .pipe(nls.bundleLanguageFiles())
                .pipe(gulp.dest(`./out`));
    };
    
    其中gulp.src第二个参数设置了base,是因为metadata文件内容不包含上下文信息,通过路径计算需要以out为相对路径

4. nls.bundle文件格式

实际生成的国际化文件并不是键值对格式:

{
    
    
    "extension": [
        "你好!",
        "时间: {0}!"
    ],
    "hello/message": [
        "这是一个示例消息"
    ]
}

重写源码后,localize是通过索引定位的,metadata文件里可以看到固定的顺序。

5. 多文件独立国际化

上面提到国际化文件有三种存在方式,除了语言包还有一种是多个翻译文件,比如对于extension.js文件,可以生成extension.nls.json、extension.nls.zh-cn.json文件,通过messageFormat配置支持多文件方式即可。

以普通Javascript插件为例,不需要执行bundleMetaDataFiles和bundleLanguageFiles:

const sourcesNsl = function () {
    
    
    var r = gulp.src(['./**/*.js', '!node_modules/**', '!gulpfile.js'])
        .pipe(nls.rewriteLocalizeCalls())
        .pipe(nls.createAdditionalLanguageFiles(languages, 'i18n', ''));

    // 输出到out目录
    return r.pipe(gulp.dest("out"));
};

6. VS Code语言包方式

这种方式不太可能,可以测试。
vscode-nls-dev导出的接口中没有用于生成语言包国际化文件格式的接口,上面说到nls.bundle格式不是键值对,语言包中的是键值对,可以自己尝试生成。

四、总结

就这些,又研究了一个没太大用的知识……

猜你喜欢

转载自blog.csdn.net/eiilpux17/article/details/116808463