背景:
所在的项目是一个海外电商项目,涉及不同国家的翻译文件较多,有20+,如果将所有的翻译文件都打包的部署包中,会导致页面首屏渲染较慢;
项目分多个区域进行部署,在不同的区域下,只服务单独几个国家的业务,如果将所有的翻译文件都打包到部署包中,会有写翻译文件不会被使用到的情况;
由于翻译内容经常出现不符合一线运营的要求情况,需要实现翻译文件的动态修改,即从配置台进行翻译词条的修改,然后生成对应的翻译文件发布到CDN上;
方案分析:
基于以上问题,在出包时,不能把翻译文件打到具体的部署包里面,需要在页面初始化时,根据cookie中的语言信息去异步加载对应的翻译文件;语言切换时,根据预期的语言信息去异步加载对应的翻译文件。
以下列出三种方式,均有各自的优缺点,可根据自身的项目情况进行选用:
一、同步加载版
按照vue-i18n插件官网的介绍方式,引用插件,在main.js中,引用i18n的配置文件,在创建Vue实例时,进行注册:
import Vue from 'vue'
import i18n from './language/index'new Vue({ i18n,}).$mount('#app')
在src/language/index.js文件中,引用vue-i18n插件,在i18n.messages中写全部需要支持的语言信息和对应的翻译内容
import Vue from 'vue'import VueI18n from 'vue-i18n'
Vue.use(VueI18n)const DEFAULT_LANG = 'en_PH'
const locales = {
en_PH: require('./cust/en_PH'),
ja_JP: require('./cust/ja_JP'),
...
}
const i18n = new VueI18n({ locale: DEFAULT_LANG, messages: locales,})
export default i18n
其中src/language/cust/en_PH.js文件格式如下,采用模块的方式导出翻译内容
export const msgObj = { "confirm":"\u0043\u006f\u006e\u0066\u0069\u0072\u006d", // Confirm
"cancel":"\u0043\u0061\u006e\u0063\u0065\u006c", // Cancel
...
}
其中src/language/cust/ja_JP.js文件格式如下,采用模块的方式导出翻译内容
export const msgObj = {
"confirm":"\u78ba\u8a8d", // 確認
"cancel":"\u30ad\u30e3\u30f3\u30bb\u30eb", // キャンセル
...}
以上方式,会将所有注册的翻译和内容全部打包到src/dist/js/index.js文件中。
当翻译文件内容较少或者翻译文件较少,且部署的服务器需要全量支持语言的情况下,使用。
二、异步加载版
这里官网给了一个简单的举例Vue I18n延迟加载翻译
参考官网的例子,修改成以下:
//index.js
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import axios from 'axios'
Vue.use(VueI18n)
const DEFAULT_LANG = 'en_PH'
const locales = {
en_PH: require('./cust/en_PH'),
...
}
const i18n = new VueI18n({
locale: DEFAULT_LANG, // 设置语言环境
messages: locales, // 设置语言环境信息
})
const loadedLanguages = [DEFAULT_LANG] // 我们的预装默认语言
function setI18nLanguage (lang) {
i18n.locale = lang
axios.defaults.headers.common['Accept-Language'] = lang
document.querySelector('html').setAttribute('lang', lang)
return lang
}
export function loadLanguageAsync (lang) {
if (i18n.locale !== lang) {
if (!loadedLanguages.includes(lang)) {
return import(`@/language/cust/${lang}`) // 这里由于新老项目共用一套翻译文件,在流水线出包时提供,不是使用webpack进行出包
.then(msgs => { //去引入这个值对应的翻译文件
i18n.setLocaleMessage(lang, msgs); // 扩展i18n.messages
//设置i18n的语言message切换成这个
loadedLanguages.push(lang); //本地已经加载的语言 加入 loadedLanguages
//设置语言
return setI18nLanguage(lang)
})
.catch( () => {
console.log(`async import language failed`)
})
}
return Promise.resolve(setI18nLanguage(lang))
}
return Promise.resolve(lang)
}
export default i18n
可以看到在调用i18n.setLocaleMessage时,是通过(语言标识,对应的翻译文件内容)的方式进行成对配置的,可以等效成去扩展i18n.messages这个对象
项目打包时,会将默认的语言信息打包到index.js文件中;
这里使用Webpack的require加载文件的方式去异步加载翻译文件;进行语言切换时,如果动态的信息比如商品相关的信息不涉及多语言,可以直接调用loadLanguageAsync去加载并设置对应的语言信息;如果动态信息涉及多语言,在调用loadLanguageAsync后还需要调用对应的数据接口刷新动态数据。
当需要实现翻译文件异步加载,且翻译问题是通过补丁方式发布时,可以采用以上方式。
export function loadLanguageAsync (lang) {
if (i18n.locale !== lang) {
if (!loadedLanguages.includes(lang)) {
// 动态从cdn引用资源翻译文件
const sdk = document.createElement('script')
sdk.type = 'text/javascript'
sdk.src = `${window.cdnPath}static/language/${lang}.js` // 请求到数据内容
sdk.onload = function() {
i18n.setLocaleMessage(lang, { // 扩展i18n.messages
msgObj: window.msgObj, // msgObj由对应的翻译文件提供,因此这里需要调整翻译文件,
});
//设置i18n的语言message切换成这个
loadedLanguages.push(lang); //本地已经加载的语言 加入 loadedLanguages
//设置语言
setI18nLanguage(lang);
// 清理数据
delete window.msgObj
// 返回语言
return Promise.resolve(lang)
}
sdk.onerror = function() {
console.log(`get language failed`)
return Promise.reject(lang)
}
document.body.appendChild(sdk)
}
return Promise.resolve(setI18nLanguage(lang))
}
return Promise.resolve(lang)
}
其中src/language/cust/en_PH.js等资源文件格式修改如下,采用JS代码方式
window.msgObj = {
"confirm":"\u0043\u006f\u006e\u0066\u0069\u0072\u006d", // Confirm
"cancel":"\u0043\u0061\u006e\u0063\u0065\u006c", // Cancel
...
}
这里也是等效成去扩展i18n.messages这个对象。
至于调用loadLanguageAsync的时机,官网给的建议是路由守卫里执行
router.beforeEach((to, from, next) => {
const lang = to.params.lang
loadLanguageAsync(lang).then(() => next())
})
这里需要目的路由上的参数带上语言信息,然后去加载和设置,适合页面初始化,或者路由信息变化的情况下使用,这里也可以从cookie里面获取语言信息,省去路由带参的操作。
总结
当翻译文件内容较少或者翻译文件较少,且部署的服务器需要全量支持语言的情况下,直接使用第一种方式;
当需要实现翻译文件异步加载,且翻译问题是通过发布补丁包发布时,可以采用第二种;
当需要实现翻译文件一步加载,同时在配置中心可以动态修改不同站点的翻译信息时,采用第三种。
如果有不足欢迎指正,有想法欢迎大家一起讨论。