公司做海外业务,所有的对外项目都需要实现多语言,聊一些常见的多语言方案。
下面讨论的重点,不是在怎么去实现或者调用多语言,而是在对多语言 key
管理上的一些思考。
常见多语言方案
主要是两种方案,一种就是直接构建多份源文件,或者就是在运行时按需加载对应的语言包。
构建多份源文件
针对每种语言构建多份文件,假如现在需要 [zh-cn, en, ru],那会构建出来这三份代码
比如:index.zh-cn.js
、index.en.js
index.ru.js
这种做单从文件冗余上来看是最纯粹的,但问题也很明显
- 对于我们需要后台配置语言包的场景无法实现。
- 分别每种语言构建,语言多了速度会很慢。
- 多个语言下资源无法缓存,增加网络费用成本。
运行时加载
这种方案使用相对广泛,目前我们也是采用这种方案,后台根据当前的语言同步或者异步返回当前的语言包,前端读取语言包即可。读取是通过 公共方法、或者直接属性访问也可以根据你的喜好。
弊端和遇到的问题结合下面的现状一起分析。
现状
目前多语言形式如下:
不区分模块,统一导入一个语言包,提供
trans
方法,或者新的项目做了模块,会按照模块加载然后也是在调用trans
方法。通过在线表格 + 后端数据库维护多语言key,新增的放在表格中,系统会定时抓取做同步,同时也支持后台修改已有的语言包
value
。最后提供一个接口返回当前语言包信息,不过同时维护了一个字段来标识这个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
使用编译提取的方案也需要同时做一些代码的约束,比如调用翻译方法必须直接传入 key
字符串,对于使用变量名 key
,接口返回等,这类操作真的只能去修改代码。