Skip to content

谈谈国际化的使用姿势(i18n)

国际化(Internationalization,简称 i18n)的核心目标是让产品能够适应不同语言、文化和地区的用户需求。其必要性体现在以下几个方面:

  • 用户覆盖广度:全球用户语言环境多样,支持多语言可提升用户体验,扩大市场范围
  • 代码可维护性:通过统一管理多语言资源,避免硬编码文本分散在代码中,减少重复修改成本
  • 动态适配能力:结合浏览器语言检测或用户手动切换,实现内容实时更新,无需重新部署代码

通常一个中大型的项目都会实现国际化,开发人员只需要将对应语言的翻译根据映射到页面中就可以完成。在现代web框架以及国际化框架的加持下,整体功能的实现也越来越简单。如vue中通常使用 Vue-I18n、React可以用 React-I18next、Angular提供了 localize,这些都可以加速国际化开发

开发流程

在真实的项目开发中,常见的开发流程即:开发人员在编码的过程中,通常会将待翻译的文案提取到对应语言map中,最后通知翻译团队进行翻译,翻译完成后再更新到代码中,然后在测试过程中验证,有问题继续重复以上步骤

i18n几种方案

i18n是现代国际化实现必不可缺的帮手,它提供了相对灵活的方案,开发者可以根据使用场景选择合适的方案快速实现国际化

下面就来看看项目中i18n的多种使用方式,本文按照由简到繁的顺序一一介绍,以vue-i18n为演示框架

小贴士

本文只做国际化方案的演示介绍,不包括详细的配置步骤,读者可根据方案进行调整实现

代码集成实现

代码集成是i18n最常用的一种方案,它将所有需要翻译的文案统一到代码中,在编译时进行替换,从而实现国际化

首先创建i18n实例

js
// i18n实例
const i18n = createI18n({
  // 默认语音
  locale: 'zh',
  // 备选语音(默认语音不存在时使用)
  fallbackLocale: 'en',
  // 对应语言的翻译映射,以语言值为key,对应语言的翻译为value
  messages: {
    en: {
      message: {
        hello: 'hello world'
      }
    },
    zh: {
      message: {
        hello: '你好世界'
      }
    }
  }
})

在组件中使用

html
<h1>{{ $t('message.hello') }}</h1>

当语言环境改变时,修改i18n的语言值就完成了翻译文案的切换

js
i18n.value = 'en';

当翻译文件体量越来越大时,通常都会将翻译内容以语言划分文件夹,然后根据语义或者模块信息将内容分组后存储

sh
i18n  
└── en        
    ├── common.js  # 英文公共文件
    └── login.js   # 登录页面英文翻译
└── zh         
    ├── common.js  # 中文公共文件
    └── common.js  # 可能为重复或备用中文文件

📌 代码集成方式缺点:

  • 翻译同步维护难度大
  • 翻译体积文件过大时,加载性能问题

开发时通常都会用母语作为默认语言,然后其他的语言同步好对应的字段,交给翻译后进行翻译,当翻译内容好后再手动同步到对应的字段代码中,在后期多次需求开发时,如果翻译同步不及时会变得异常困难,而且手动同步也会变得很繁琐,成本太高开发体验很差

基于JSON实现

基于JSON的方案顾名思义就是将翻译内容保存到JSON文件中,然后根据语言加载对应的JSON文件,完成国际化工作

js
const i18n = createI18n({
  locale: 'zh',
  globalInjection: true,
  missingWarn: false,
  silentFallbackWarn: true,
  silentTranslationWarn: true,
});

export async function loadLangResource(i18n, locale) {
  const messages = await import(/* webpackChunkName: "locale-[request]" */ `/static/${locale}.json`);
  // 或者不参与打包
  // const messages = await fetch(`/static/${locale}.json?v=${app.version}`)

  i18n.global.setLocaleMessage(locale, messages.default);
}

从上述代码中可以看到在对应的语言环境下,加载对应的JSON翻译文件,此种方式可以很好的将翻译与代码解耦,实现翻译共享与复用,并且翻译资源文件拆分异步加载可以提高加载性能

📌 基于JSON方式缺点:

  • 翻译同步维护难度同样大
  • JSON中无法注释,需采用JSON5等方案

JSON方式虽然与代码进行了解耦,但仍然存在翻译同步维护的困难,如JSON字段的维护怎么维护?没有注释就像天书。当然,这并不是JSON的最佳使用方案,这里也是简单介绍下此种方案,但简单的翻译工作也可以使用这种简单的JSON方案

头疼的是JSON怎么维护问题,接下来看看如何解决这个问题

基于JSON与CMS实现

JSON方案虽然与代码解耦了,但开发者去修改JSON文件是万万不能接受的!!

如果可以将JSON字段用某个工具与翻译人员关联起来,那么就可以省不少事,并且翻译人员可以随时修改翻译内容,并通知更新。这里介绍2种方案:

  • 基于在线表格
  • 基于数据库(CMS)

来看看在线表格方案,什么意思呢:开发人员将待翻译内容呈现在在线表格中,翻译人员直接在表格中同步翻译内容,最后通知开发者更新即可。那么如何与JSON关联起来呢?要知道代码中i18n还是会以key为索引显示对应的翻译内容,所以表格中需要有对应的key字段,开发者需要创建key字段,最后将表格数据转换成json文件即可

这里关键的步骤在于如何将在线表格数据转换成JSON,其实很简单,市面上的文档如:Google、Tencent等等都支持API操作,读者可前往对应的开发者文档查看

本文用腾讯在线表格演示,如下图所示,在线表格中包含了:翻译表标题、表头、翻译内容,开发者提供对应的翻译字段key,翻译人员将翻译内容填入即可

有了API数据后就可以转换成JSON了,可以用Node实现API请求然后生成对应的JSON文件

js
// 伪代码
async function getData() {
  const data = await fetch("https://domain.com/apii/sheet/uidlpsdfierersefadf")

  data.json().then(res => {
    // 转换逻辑
    // 根据不同的语言生成对应的JSON文件
    fs.writeFileSync(`${lang}.json`, JSON.stringify(res))`.)
  })
}

将对应的i18n生成脚本写入到项目的package.json中

json
{
  "scripts": {
    "i18n": "node i18n.js"
  }
}

这样每次在翻译表格更新后,直接走脚本就行了,开发者无需去关心JSON文件长啥样了。同样的再走完上文中JSON中i18n的json加载逻辑,即可完成多语言的切换

接着看看CMS(Content Management System)与JSON的结合使用,其实在线表格的方式已经减少了很大一部分工作,但对于企业来说维护自己的翻译系统还是必要的

设计这一套系统也非常简单,服务端只需要使用以下条件可快速实现:

  • 数据库:使用mongodb飞关系型数据库无需关注字段的数量,可以满足多语言的多样性数据
  • 数据表:项目语言表、具体语言表

总体来说使用mongodb创建一个不同项目的语言表和具体语言表就基本满足了项目使用了

CMS当然还需要实现在线表格的功能,或者说客户端页面使用功能,基本要满足:语言表创建、修改、查询等等,市面上有很多方式可以帮助到,这里就不细讲了

来看这是CMS中AI Chat项目的语言表

开发团队可以定制具体的规则来标准化开发流程,如:不同的颜色代表不同的含义

除此之外,可以根据RBAC权限来控制语言表的访问

那么在项目中如何拉取语言表呢?通常在项目中新增env配置:

sh
I18N_TABLE_KEY=xxxx

根据项目语言表的key就可以拉取数据了,同样的重复上文中的脚本操作完成json文件的生成

你是否在想如果有了CMS是不是可以直接跳过JSON文件载体,直接走API?其实也不是不可以,只不过JSON方式更稳妥,至于为什么读者可以多考虑考虑

📌 基于CMS与JSON方式缺点:

  • 架构复杂

基于插件自动化实现

话说回来国际化的实现过程确实很痛苦,要维护密密麻麻的翻译,何尝不相信自动化翻译呢?

我们可以借助翻译服务商提供翻译接口自动将文案翻译成目标语言,然后通过自动化插件将翻译后的文案自动填充到项目文件中,从而实现国际化

这种方式的维护成本很低,开发者根本不需要管翻译内容,一般实现步骤如下:

  1. 项目中直接用默认语言写入文案,然后标记当前位置需要翻译
html
<h1 m-translate>你好</h1>
  1. 编写翻译转换脚本,可以使用Babel自定义插件编译AST,找到标记的待翻译节点,然后为当前文案生成唯一的key,将文案替换为key后,再将文案翻译成多种目标语言
js
const translateAPI = require("google-translate-api");

export const AutoTranslatePlugin = declare(
  ({ assertVersion, types, template }) => {
    assertVersion(7);

    return {
      name: "AutoTranslatePlugin",
      visitor: {
        Expression(path) {
          // 走服务商翻译 translateAPI
        },
        // 补充...
      },
    };
  }
);
  1. 生成对应的最终的翻译JSON文件 在插件遍历AST以及翻译过程中将生成的文案key整理后生成到对应的JSON文件中,这样就实现了文案自动化处理

📌 基于插件自动化方式缺点:

  • 翻译质量不可控

方案对比

方案适用场景核心优势主要缺点
代码集成中小型项目实现简单,社区支持完善维护成本高,翻译同步困难
CMS 动态管理非技术团队协作内容可实时更新,灵活性高强依赖网络,架构复杂
自动化工具多语言快速迭代减少人工干预,提升效率翻译质量依赖 API,成本不可控

总结

本文通过不同角度介绍了国际化的实现方案,以及它们各自的优缺点,读者可根据项目要求或开发标准选择适合的方案。透露下本人工作中常使用CMS+JSON方式,如果你还有更好的方案,可留言分享

感谢支持

再次感谢您的支持,您的支持将鼓励我继续创作。文章通常首发公众号,可以关注我的公众号,获取最新优质文章;同时如果你有 珠宝首饰之类 的需求,也可以微信扫码光临小店,种类多多欢迎来选👏🏻
大卫talk
 
aphrodite-u

若您在阅读过程中发现一些错误:如语句不通、文字、逻辑等错误,可以在评论区指出,我会及时调整修改,感谢您的阅读。同时您觉得这篇文章对您有帮助的话,可以打赏作者一笔作为鼓励,金额不限,感谢支持🤝。
支付宝捐赠   微信支付捐赠

Released under the MIT License.