i18n国际化,多语言的key如何处理更好

公司做海外业务,所有的对外项目都需要实现多语言,聊一些常见的多语言方案。

下面讨论的重点,不是在怎么去实现或者调用多语言,而是在对多语言 key 管理上的一些思考。

常见多语言方案

主要是两种方案,一种就是直接构建多份源文件,或者就是在运行时按需加载对应的语言包。

构建多份源文件

针对每种语言构建多份文件,假如现在需要 [zh-cn, en, ru],那会构建出来这三份代码

比如:index.zh-cn.jsindex.en.js index.ru.js

这种做单从文件冗余上来看是最纯粹的,但问题也很明显

  1. 对于我们需要后台配置语言包的场景无法实现。
  2. 分别每种语言构建,语言多了速度会很慢。
  3. 多个语言下资源无法缓存,增加网络费用成本。

运行时加载

这种方案使用相对广泛,目前我们也是采用这种方案,后台根据当前的语言同步或者异步返回当前的语言包,前端读取语言包即可。读取是通过 公共方法、或者直接属性访问也可以根据你的喜好。

弊端和遇到的问题结合下面的现状一起分析。

现状

目前多语言形式如下:

  1. 不区分模块,统一导入一个语言包,提供 trans 方法,或者新的项目做了模块,会按照模块加载然后也是在调用 trans 方法。

  2. 通过在线表格 + 后端数据库维护多语言key,新增的放在表格中,系统会定时抓取做同步,同时也支持后台修改已有的语言包 value

  3. 最后提供一个接口返回当前语言包信息,不过同时维护了一个字段来标识这个key 是否为给前端js使用,只有这种才会在接口返回。

接口返回语言包数据结构如下:(页面根据需要的语言同步或者异步加载)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// /lang?lang=en
langData = {
base: {
submit: 'Submit',
cancel: 'Cancel',
}
}

// /lang?lang=zh-cn
langData = {
base: {
submit: '提交',
cancel: '取消',
}
}

主要存在的问题:

乍一看,其实大家都是这么做,如果小站也不太会有大问题,但其实时间久了问题就会越来越明显。

  • 针对这种方案,语言包属于 只进不出 ,文件冗余无法排查

刚才看了下两个项目的 key 都在5000左右了,英语100kb,gzip后问题不太大,但俄语这种语言文件原大小已经达到了 500kb ,什么概念呢,就是一个语言包文件已经比整个页面 js + css 都要大了

  • 多语言的key划分了模块 导致复用变差

虽然配置了 base模块 但其实很难用,因为假如我先放在了 a 模块,后面发现 b 模块也需要,然后用的话你要提取到 base,那你就要去改原本的调用,并且这里到底值不值得放到 base 本来也是个问题

  • 浪费翻译资源

翻译都是要专门的人员来翻译,无法剔除无用的key,当需要翻译新语种或者变更都多余翻译那些没用的key。

  • 其它问题…

解决方案

结合上面的现状问题,解决痛点就是分析出当前页面(所有entry)分别使用的key有哪些,然后分析出对应entry所使用的key,按需加载,同时还可以反馈到翻译人员避免无用的翻译。

方案一:
按模块去加载,比如划分 goods user cart 等大模块的形式,但这样做只能解决文件太大的问题,对于文件冗余问题并未解决。

方案二:
在构建时基于提取出当前entry相关的所有 key,提了方案但并未去实现。

后面在这里 https://zhuanlan.zhihu.com/p/33880069 ,做到了类似的实现。

这里有介绍两种方案编译提取key的方法:

一种基于 webpack function call hook,通过配置一个调用的 functionName,然后获取到第一个参数既为多语言 key 名,关联到对应的entry,这里有个现成的库 grassator/webpack-extract-translation-keys

另一种是对作者来说是无奈之举,因为代码是直接通过了属性访问去读取了多语言的值,那只能通过 optimize-chunk-assets 获取所有的chunk的源码信息,然后正则匹配出来 key 的值关联到对应的entry

详细的可以看这里:https://zhuanlan.zhihu.com/p/33880069

使用编译提取的方案也需要同时做一些代码的约束,比如调用翻译方法必须直接传入 key 字符串,对于使用变量名 key,接口返回等,这类操作真的只能去修改代码。