Vue-i18n异步加载翻译资源文件方式探讨

时间:2021-1-8 作者:admin

背景:

所在的项目是一个海外电商项目,涉及不同国家的翻译文件较多,有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里面获取语言信息,省去路由带参的操作。

总结

当翻译文件内容较少或者翻译文件较少,且部署的服务器需要全量支持语言的情况下,直接使用第一种方式;

当需要实现翻译文件异步加载,且翻译问题是通过发布补丁包发布时,可以采用第二种;

当需要实现翻译文件一步加载,同时在配置中心可以动态修改不同站点的翻译信息时,采用第三种。

如果有不足欢迎指正,有想法欢迎大家一起讨论。

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。