i18n Ally 插件帮你轻松搞定国际化需求-按模块划分

6,658 阅读16分钟

写在开头

i18n Ally 是一款 VS Code 插件,它能通过可视化操作的形式提高了开发者翻译多语言的效率,属于机器翻译,能省心不少;再去结合 vue-i18n 包(React 可以使用 react-i18next),咱们就能轻松完成项目国际化需求了。

i18n Ally 文档说明:传送门

i18n Ally 插件默认采用的是谷歌翻译,需要梯子、梯子、梯子❗

当然,这仅影响自动翻译的功能,插件还有其他小功能也很好用噢。

前置准备

首先,在 VS Code 中把 i18n Ally 插件安装上:

image.png

再创建一个 Vue 项目作为测试使用:

vue create your-project-name

再顺便把 vue-i18n 包给装上:

npm install vue-i18n@8 -S

Vue3项目可以安装 vue-i18n@10 版本

vue-i18n基本使用

先来创建不同语言对应的翻译文件,通常以 JSON 格式保存较为方便。

locales/zh-CN.json 文件:

{
    "message": "你好!",
    "description": "这是一个示例文本。"
}

locales/en.json 文件:

{
    "message": "Hello!",
    "description": "This is a sample text."
}

locales/zh-TW.json 文件:

(繁体使用 zh-HK.json 也可以的)

{
    "message": "你好!",
    "description": "這是一個示例文本。"
}

不同的语言文件命名应该遵循国际化语言模版规范,否则可能会导致插件自动翻译时无法正常识别,具体命名规则可参考最下方的"语言附录"。👇👇👇

再来一个 vue-i18n 初始化 locales/index.js 文件:

import Vue from 'vue';
import VueI18n from 'vue-i18n';
import zhCN from './zh-CN.json';
import en from './en.json';
import zhTW from './zh-TW.json';

Vue.use(VueI18n);

// 注意:属性名 是不同语言的标识,用于后续切换时传递使用。
const messages = {
    zhCN: zhCN,
    en: en,
    zhTW: zhTW,
};

const i18n = new VueI18n({
    // 设置默认语言为中文
    locale: 'zhCN',
    messages: messages
});

export default i18n;

main.js 文件中引入:

import Vue from 'vue'
import App from './App.vue'
import i18n from './locales'

Vue.config.productionTip = false

new Vue({
  // 注入i18n实例,后续通过this.$i18n访问实例
  i18n,
  render: h => h(App),
}).$mount('#app')

具体使用 App.vue 文件:

<template>
  <div id="app">
    <h1>橙某人</h1>
    <p>{{ $t('message') }}</p>
    <p>{{ $t('description') }}</p>
    <button @click="changeLanguage('zhCN')">中文</button>
    <button @click="changeLanguage('en')">英文</button>
    <button @click="changeLanguage('zhTW')">繁体</button>
  </div>
</template>

<script>
export default {
    methods: {
        changeLanguage(lang) {
            this.$i18n.locale = lang;
        }
    }
};
</script>

效果:

20241108-1.gif

这就完成了基础的第一步,接下来就能愉快的玩耍了。😋

问题❓

咱们来看一个问题,假设你现在想添加一个"名称"的文案,并让它具备国际化的功能,一般流程如下:

  1. 根据"名称"的文案,先取一个属性名称,就叫 name 吧。
  2. 手动提取简体中文文案到 zh-CN.json 文件中。
  3. 再利用第三方的翻译平台转译,手动补齐英文、繁体对应的文件。

每次你要新增文案,都要经历这么三小步,Em...就挺麻烦😑,少量还好,如果有大量文案那头就大了❗

这个问题当然是可以被解决的,解决方法就是咱们在本章要介绍的 i18n Ally 插件。

不过,在开始介绍这个插件之前,我们先来更细致地分析一下问题,要带着疑问去剖析。这样能让我们更好地理解 i18n Ally 插件是如何发挥作用的,以及它为什么能解决我们面临的问题。

  1. 需要手动提取文案,这点最麻烦,不符合咱们程序猿"追求高效"的原则(摸鱼时间不够呀)。
  2. 不能自动补齐其他语言,需要借助第三方翻译平台,这样子效率太低下了,最好能在 VS Code 里面直接就解决。
  3. 文案的属性名需要自己取,众所周知,取名字是一个令人头疼的事情。
  4. 无法有效保证不重复,当多人协作时,很有可能就会造成文案翻译重复,或者语义相近的文案重复翻译的情况。
  5. 无法直观的看到真实文案,只能看到 {{ $t('文案的属性名') }},下面会有详情介绍。
  6. 容易遗漏翻译,当页面HTML结构、JS逻辑非常复杂时,很容易会发生翻译遗漏的情况。
  7. 无法批量翻译,当整个文件都未翻译过,是否有一种方式能直接对整个文件进行批量翻译呢?

大致再细致分析出如上问题,下面咱们会通过 i18n Ally 插件来逐个解决,GO!

i18n Ally基本使用

首先,咱们在项目根目录下创建 .vscode/settings.json 文件,如果项目已经存在该文件,则不用。这个文件用于存储当前工作区或当前项目的"设置",可定制编辑器行为,如代码格式化、主题、自动保存方式等,当然,还有最重要的就是对于插件的设置。

先设置 .vscode/settings.json 文件内容如下:

{
    // 插件将以何种语言作为基准来进行翻译相关的操作,如果你正在开发一个多语言应用,并且原始文案是用英语编写的,你可以将`sourceLanguage`设置为`en`(代表英语)。这样,插件就知道在提取文案进行翻译或者在代码中关联不同语言的文案时,以英语文案为原始参考。
    "i18n-ally.sourceLanguage": "zh-CN",
    // 指定语言文件存储的位置,后续自动翻译的文案也会存到这个位置;数组的形式允许添加多个
    "i18n-ally.localesPaths": ["src/locales"],
    // 语言文件内部的数据结构,flat({"a.b.c": "..."}) or nested({"a": {"b": {"c": "..."}}}
    "i18n-ally.keystyle": "nested",
    // 设置当前显示的主体语言: zh-CN/en/zh-TW/...
    "i18n-ally.displayLanguage": "zh-CN"
}

保存后,查看你的 App.vue 文件:

image.png

😮 观察到效果没❓是不是就挺棒❗❗❗

这种显示何种主体语言由 i18n-ally.displayLanguage 属性来控制,你也可以改成显示英文或者繁体等。

当咱们将鼠标光标聚焦到某一行的国际化文案上:

image.png

插件又能将文案的属性名称给回显出来,Nice❗

注意🔉:

"i18n-ally.displayLanguage": "zh-CN" 属性的值取的是你翻译文件或者文件夹的名称。

比如你将简体语言文件叫 zh-CN.json,则值就为 zh-CN,英文语言文件叫 en.json,则值就为 en

自动翻译

上述的效果还不错吧😋,即使不使用插件的其他功能了,就这个小功能,也能极大提升咱们平时使用国际化时的开发体验了。

不过,这还仅是小能力,关键点来了,敲黑板。🙊

咱们来添加一个 "名称" 的文案:

image.png

打开插件:

image.png

在 VS Code 中安装了 i18n Ally 插件后,左边操作栏就会多出一个插件的图标哦。点击它后,在 Hard-coded strings 下就会列举对应需要翻译的文案,具体文案下也会显示一些黄色的波浪线。

注意:当前文件栏目下要有内容,需要你将 VS Code 编码区切换到对应文件上。

咱们可以来提取这个文案进行自动翻译,如将鼠标移动到想要翻译的文案上:

image.png

或者将鼠标移动到有黄色波浪线的文案上:

image.png

点击"快速修复":

image.png

其他两个选项分别是:

"在当前文件中忽略这个文案,不需要做翻译"。

"在全局中忽略这个文案,不需要做翻译"。

在选择提取文案后,VS Code 最顶部会弹出一个输入框,这里可以自己设置文案的属性名,默认会采用拼音(slug)的方式生成属性名。(你可以自定义改成 name 比较符合文案意思)

image.png

回车之后,会让你继续选择:

image.png

可以选择将原来的文案替换成期待的动态形式。

当你继续回车或者用鼠标选择,选择后:

image.png

你会瞧见 "名称" 的文案就被翻译好了,查看 zh-CN.json 文件:

image.png

当然,你此时再去查看 en.jsonzh-TW.json 文件会发现其中并没有 "名称" 文案的翻译内容。😅

这还需要咱们单独给不同语言继续添加翻译,如下:

image.png

在翻译进度的栏目下,会读取到你有多少种语言的翻译,咱们可以选择给对应语言进行翻译,点那个小地球🌐的图标就可以进行自动翻译了。

image.png image.png

不过,由于 i18n Ally 插件默认采用的是谷歌翻译,需要翻墙,都是程序猿相信这点难不倒你啦。😁

也可以指定其他翻译源:传送门

再去查看对应的语言文件内容:

image.pngimage.png

是不是还挺不错的,省心不少吧,这就彻底完成了一个文案的国际化翻译过程啦。👻

"自动翻译" 是本次的核心,整个过程说简洁也繁琐😂,但能用!但是,这个过程也还存在一些问题或者还有能优化的地方,咱们来慢慢继续唠。

"属性名称"生成规则

上面咱们采用了拼音(slug)的形式来生成文案的属性名,不过,拼音(ming-cheng)之间是通过短横杠来连接的,这形式其实还能再修改,如改采用驼峰的形式:

// ...
// 翻译文案的属性名称策略:slug-拼音、random-随机、empty-需要手动命名
"i18n-ally.extract.keygenStrategy": "slug",
// 属性名称的具体生成规则
"i18n-ally.extract.keygenStyle": "camelCase",

image.png

"i18n-ally.extract.keygenStyle" 规则的值有:

  • default/kebab-case;拼音+短横杠,如 ming-cheng
  • snake_case;拼音+下划线,如 ming_cheng
  • camelCase;拼音+驼峰,如 mingCheng
  • PascalCase;拼音+驼峰+首字符大写,如 MingCheng
  • ALL_CAPS;拼音+下划线+全大写,如 MING_CHENG

翻译文案的属性名称生成策略,一般拼音策略应该是足够咱们使用了,不过,插件还支持另外两种策略,咱们也来瞧瞧。

如随机策略:

{
    // ...
    "i18n-ally.extract.keygenStrategy": "random",
}

则在提取文案时会自动生成一个随机字符作为属性名称:

image.png

具体效果:

image.png

还有 empty 策略:

{
    // ...
    "i18n-ally.extract.keygenStrategy": "empty",
}

这个设置后文案的属性名就需要你自行来命名了,会简单的通过 keykey-0/key-1/... 形式提供默认值,但没有意义,还是需要你自行命名比较合适。

image.png

单文件批量翻译

上面咱们讲的是单个文案的翻译情况,如果是需要整个文件进行批量翻译呢?这情况 i18n Ally 插件考虑到了,继续来看。

咱们先来添加几个测试文案:

image.png

在 Hard-coded strings 下就会列举当前文件下所有需要翻译的文案,咱们右键它就会有两个选择出现,分别是:

  • 提取所有硬编码字符串(实验性)。
  • 启用硬编码字符串自动检测。
image.png

咱们选择第一个选项,虽然还写着实验性,但是,用着问题不大。😗

选择后,就会直接帮我们把整个文件未翻译的文案全给提取翻译了,如下:

image.png image.png

上面截图可以看到,即使是在 JS 中的文案翻译,也会智能的给你补上一个 this

之所以 i18n Ally 插件能自动补充一些框架语法,这是因为其本身内置对一些主流前端框架语法的支持,会自动开启检测,具体细节可以查看文档

咱们可以通过如下配置指定框架:

"i18n-ally.enabledFrameworks": ["vue", "react"] // 默认为 auto

最后,你可以再去"翻译进度"栏目下一键翻译其他语言的文案。这样子咱们就完成一个文件的批量翻译了。

文件夹批量翻译

当然,如果单文件批量翻译还不能满足你的话,i18n Ally 插件也支持目录/文件夹的形式进行批量翻译。

咱们创建两个文件如下:

image.pngimage.png

对着 components 文件夹右键,选择下图中红框框的选项:

image.png

选择后,插件会将整个文件夹的文件内相关文案全部提取出来进行翻译:

image.png

快速定位与修改

当我们的项目业务规模变得十分庞大,语言文件中充斥着海量的翻译文案时,修改和维护这些文案就可能会成为一个棘手的大问题。不过,插件为我们提供了一些有效的解决方式。

当我们把鼠标光标点击聚焦在某一行的国际化文案上之后,接着将鼠标移动到下图所示的绿色框框区域,此时便会弹出一个窗口,里面展示了一系列的选项;而当我们进一步点击最右侧黄色框框里的小箭头时,就能迅速跳转到与之对应的语言的文案所在位置了。

image.png

还有,当我们选择下图的小铅笔时,也能直接修改与之对应的语言的文案。😍

image.png

忽略某些文案的翻译

有时,咱们肯定会希望一些文案不被翻译,如下图这些红框框的文案:

image.png

你可以通过手动进行如下配置:

// ...
"i18n-ally.extract.ignored": ["橙某人", "中文", "英文", "繁体", "YYYY-MM-DD HH:mm:ss"],

也可以采用前面咱们说过的可视化操作:

image.png

效果:

image.png

配置后,目标文案就不会有黄色波浪线了,包括单文件批量翻译与文件夹批量翻译都不会翻译这些文案。

上面这种忽略是全局性,针对整个项目,但如果你想某文件中某个特定文案不被翻译,也可以限制更细致一点,如创建一个 Test.vue 文件:

image.png

设置:

// ...
"i18n-ally.extract.ignoredByFiles": {
    "src\\components\\Test.vue": [ "我是特定文案", "俺也不想被翻译"],
},

效果:

image.png

同样,也可以通过可视化操作:

image.png

不过,可视化操作好像有BUG😂,提取第一个文案是没有问题的,提取第二个文案就没有效果了。(全局性忽略的可视化操作倒是没有问题)

猜测是插件本身有问题,应该是 i18n-ally.extract.ignoredByFiles 属性逻辑处理时未考虑已经存在再追加的情况?

image.png

源码好像又看不出问题,Em...😅还是通过手动添加设置的形式吧。

按模块划分-命名空间

看完上述内容,再去实操一遍,相信你就能基本掌握 i18n Ally 插件的使用了。接下来,小编要讲的是 "命名空间" 的内容,可以先瞅瞅它的文档介绍:传送门1传送门2

在前面的内容中,我们把中文、英文和繁体这三种语言分别划分到了三个文件里:

image.png

对于小型项目而言,这样的划分方式基本上能够满足需求。然而,在实际的业务场景中,项目往往是极为复杂且规模庞大的。这就导致需要翻译的文案数量十分巨大,仅仅依靠一个文件来管理一种语言显然是不够的。

在很多情况下,为了更好地管理项目,我们通常会采用划分模块的方式。同理,对于国际化的语言文件,我们也需要对其进行模块化划分。通过这种方式,不仅可以方便我们在后期对语言文件进行管理和维护,而且有助于同类型项目在开发过程中快速复用这些语言模块,提高开发效率,降低开发成本。

那么如何来做这个事情❓

咱一步一步来,先进行一些配置:

{
    "i18n-ally.sourceLanguage": "zh-CN",
    "i18n-ally.localesPaths": ["src/locales"],
    "i18n-ally.displayLanguage": "zh-CN",
    "i18n-ally.extract.keygenStrategy": "slug",
    "i18n-ally.extract.keygenStyle": "camelCase",
    "i18n-ally.keystyle": "nested",
    // 使用空间命名
    "i18n-ally.namespace": true,
    "i18n-ally.pathMatcher": "{locale}/{namespace}.json",
}

然后,咱们创建一些业务模块做测试使用:

image.png

简单在 views 文件夹下创建了用户、商品、订单三个模块,内容文案可以随便写点不一样的就行。

再来把 locales 文件夹重新整理一下,删掉原来的语言文件,新建文件结构如下:

image.png

三种语言分别为三个文件夹,里面的文件情况基本相同,这里以中文(zh-CN)为例来瞧瞧。可以看到分别有 user.jsongoods.jsonorder.json 三个文件对应咱们上面创建的三个业务模块,还有一个 common.json 用于存储公共、全局的一些文案翻译,这个文件也是最容易被其他项目拿去复用的。

来看看 zh-CN/index.js 文件:

import common from './common.json';
import user from './user.json';
import goods from './goods.json';
import order from './order.json';

export default {
    common,
    user,
    goods,
    order
}

其他四个 .json 文件的都只需要添加一个空对象({})即可,否则导入会报错。

注意🔉:每当新建一个业务模块时,首先必须创建与之对应的语言文件。这是因为若没有语言文件,i18n Ally 插件将无法正常使用,它会找不到用于存储翻译内容的文件。

接下来就可以来尝试自动翻译的过程了,还是先以 App.vue 的内容为例子:

image.png

像上图中红框框的文案,一般可能我们会将其定义为公共性的文案,需要被存储到 common.json 文件中。

那么,要如何做呢?大致与前面讲的自动翻译过程差不多,但也有要注意的地方哦❗❗❗

如,点击提取文案:

image.png

这时,不是会弹出一个 "属性名称" 的输入框吗?插件默认会根据咱们的配置情况帮我们生成一个属性名称,我们需要把这个属性名称给复制(直接ctrl+x剪切也行)一下:

image.png

然后,删除这个属性名称,此时,输入框会变成这样子:

image.png

😲 插件能读取到咱们划分的语言模块情况,咱们再选择 common ,这步操作其实就是命名空间的体现,而后会变成:

image.png

再将刚刚复制的 xingMing 给黏贴上,形成 common.xingMing 形式,然后回车:

image.png

选择想要的形式替换原文案。

最后,插件会让你选择存储到哪个语言文件中:

image.png

选择存储后,这就完成了自动翻译的过程了。👏👏👏

你可以瞧瞧是否有正常存储到 zh-CN/common.json 文件中:

image.png

而那些国际化了的文案其属性名称在呈现时也会带上命名空间的体现:

image.png

这就非常棒了❗

其他业务模块内的文案自动翻译过程也和这差不多一样,只是选择的命名空间与存储到的语言文件不同罢了,就不过多阐述了。

使用命名空间了,则无法正常使用"批量"翻译的情况了。使用起来有些问题,你可以自己玩玩。😋

源码参考

i18n Ally 文档说明:传送门

仅仅依靠这份简单的文档说明,有时可能无法满足我们的参考需求。在这种情况下,咱们就可以深入研究一下源码,从中寻找是否存在隐藏的属性,或者某些未被列举出来的属性值。

如:

src/core/Config.ts:完整对外提供的属性情况。

package.json:可以找对象属性更加细致的值情况与默认参数情况。

语言附录

参考资料:传送门

  • 英文 : en
  • 美国 : en_US
  • 中国 : zh_CN
  • 台湾:zh_TW
  • 香港 : zh_HK
  • 日本 : ja_JP
  • 秘鲁 : es_PE
  • 巴拿马 : es_PA
  • 波斯尼亚和黑山共和国 : sr_BA
  • 危地马拉 : es_GT
  • 阿拉伯联合酋长国 : ar_AE
  • 挪威 : no_NO
  • 阿尔巴尼亚 : sq_AL
  • 伊拉克 : ar_IQ
  • 也门 : ar_YE
  • 葡萄牙 : pt_PT
  • 塞浦路斯 : el_CY
  • 卡塔尔 : ar_QA
  • 马其顿王国 : mk_MK
  • 瑞士 : de_CH
  • 芬兰 : fi_FI
  • 马耳他 : en_MT
  • 斯洛文尼亚 : sl_SI
  • 斯洛伐克 : sk_SK
  • 土耳其 : tr_TR
  • 沙特阿拉伯 : ar_SA
  • 英国 : en_GB
  • 塞尔维亚及黑山 : sr_CS
  • 新西兰 : en_NZ
  • 挪威 : no_NO
  • 立陶宛 : lt_LT
  • 尼加拉瓜 : es_NI
  • 爱尔兰 : ga_IE
  • 比利时 : fr_BE
  • 西班牙 : es_ES
  • 黎巴嫩 : ar_LB
  • 加拿大 : fr_CA
  • 爱沙尼亚 : et_EE
  • 科威特 : ar_KW
  • 塞尔维亚 : sr_RS
  • 美国 : es_US
  • 墨西哥 : es_MX
  • 苏丹 : ar_SD
  • 印度尼西亚 : in_ID
  • 乌拉圭 : es_UY
  • 拉脱维亚 : lv_LV
  • 巴西 : pt_BR
  • 叙利亚 : ar_SY
  • 多米尼加共和国 : es_DO
  • 瑞士 : fr_CH
  • 印度 : hi_IN
  • 委内瑞拉 : es_VE
  • 巴林 : ar_BH
  • 菲律宾 : en_PH
  • 突尼斯 : ar_TN
  • 奥地利 : de_AT
  • 荷兰 : nl_NL
  • 厄瓜多尔 : es_EC
  • 台湾地区 : zh_TW
  • 约旦 : ar_JO
  • 冰岛 : is_IS
  • 哥伦比亚 : es_CO
  • 哥斯达黎加 : es_CR
  • 智利 : es_CL
  • 埃及 : ar_EG
  • 南非 : en_ZA
  • 泰国 : th_TH
  • 希腊 : el_GR
  • 意大利 : it_IT
  • 匈牙利 : hu_HU
  • 爱尔兰 : en_IE
  • 乌克兰 : uk_UA
  • 波兰 : pl_PL
  • 卢森堡 : fr_LU
  • 比利时 : nl_BE
  • 印度 : en_IN
  • 西班牙 : ca_ES
  • 摩洛哥 : ar_MA
  • 玻利维亚 : es_BO
  • 澳大利亚 : en_AU
  • 新加坡 : zh_SG
  • 萨尔瓦多 : es_SV
  • 俄罗斯 : ru_RU
  • 韩国 : ko_KR
  • 阿尔及利亚 : ar_DZ
  • 越南 : vi_VN
  • 黑山 : sr_ME
  • 利比亚 : ar_LY
  • 白俄罗斯 : be_BY
  • 以色列 : iw_IL
  • 保加利亚 : bg_BG
  • 马耳他 : mt_MT
  • 巴拉圭 : es_PY
  • 法国 : fr_FR
  • 捷克共和国 : cs_CZ
  • 瑞士 : it_CH
  • 罗马尼亚 : ro_RO
  • 波多黎哥 : es_PR
  • 加拿大 : en_CA
  • 德国 : de_DE
  • 卢森堡 : de_LU
  • 阿根廷 : es_AR
  • 马来西亚 : ms_MY
  • 克罗地亚 : hr_HR
  • 新加坡 : en_SG
  • 阿曼 : ar_OM
  • 泰国 : th_TH
  • 瑞典 : sv_SE
  • 丹麦 : da_DK
  • 洪都拉斯 : es_HN




至此,本篇文章就写完啦,撒花撒花。

image.png