Vue 结合后端接口数据实现多语言, 动态加载语言文件

3,653 阅读3分钟

需求背景

实现多语言切换,借助 vue-i18n 动态加载模块实现。但是前端过多语言文件或者与后端翻译保持不一致,开发效率低,于是约定共用一套语言文件并开放接口。后端实现多语言,前端只需要获取即可,不需要区分那种语言。

方案

方案 1

不需要后端接口数据实现多语言,可以使用 vue-i18n 实现需求。

方案 2

借助后端实现多语言,使用 vue-i18n, 路由拦截器去获取接口语言数据,然后更新语言文件,注意去重。此次只要针对此方案做详解。

方案 3

不使用 vue-i18n, 直接把接口数据保存在 localStorage / store 全局状态里,但是需要在每个组件再次赋值给组件 data,才能渲染到 template。

方案 1

如果不需要后端接口数据实现多语言,可以使用 vue-i18n 实现需求。如果有很多多语言文件,可以参考方案 2 中的动态加载。

参考官方链接

main.js

// 准备翻译的语言环境信息
const messages = {
  en: {
    message: {
      hello: 'hello world'
    }
  },
  ja: {
    message: {
      hello: 'こんにちは、世界'
    }
  }
}

// 通过选项创建 VueI18n 实例
const i18n = new VueI18n({
  locale: 'ja', // 设置地区
  messages, // 设置地区信息
})


// 通过 `i18n` 选项创建 Vue 实例
new Vue({ i18n }).$mount('#app')


Index.vue

<div id="app">
  <p>{{ $t("message.hello") }}</p>
</div>

方案 2

借助后端实现多语言,使用 vue-i18n, 路由拦截器去获取接口语言数据,然后更新语言文件,注意去重。

route.js // 路由文件

export default {
  {
    path: '/demo',
    component: BasicLayout,
    meta: {
      title: 'Demo'
    },
    children: [
      {
        path: 'index',
        name: 'demo-index',
        meta: {
          title: 'Home',

          // 标知符,代表访问该路由需要加载这个模块的多语言接口数据
          requiresLang: 'demo'
        },
        component: () => import('@/views/demo/Index.vue')
      }
    ]
  },
}
index.js // 路由入口文件

// 在路由拦截器处理
router.beforeEach((to, from, next) => {

  // 获取当前选中的语言项
  const lang = store.getters['user/lang'];

  if (to.matched.some(record => record.meta.requiresLang)) {
    
    // handler
    loadLanguageAsync(lang, to.meta.requiresLang)
      .then(() => next())
      .catch(() => {
        alert('Load multi-language fail, Please try to refresh or report to admin!');
      });
  } else {
    next();
  }
});

/lang/index.js 语言入口文件

import en from './en.json';

const DEFAULT_LANG = 'en';
const messages = {
  en
};

Vue.use(VueI18n);

const i18n = new VueI18n({
  locale: DEFAULT_LANG,
  messages,
  fallbackLocale: DEFAULT_LANG
});

export default i18n;

function setI18nLanguage(lang) {
  i18n.locale = lang;
  return lang;
}

// 传入当前选中的语言项和需要加载多语言的模块
export function loadLanguageAsync(lang, group) {

  // 优化,去重
  if (!i18n.getLocaleMessage(lang)[group]) {
    const params = {
      language: lang,
      group_keys: group
    };
    // 发起请求
    return languageApi.getGroupLanguage(params).then((data) => {

      // 处理数据,重要步骤是 getLocaleMessage, setLocaleMessage,set locale。这里不建议直接用 mergeLocaleMessage, 有些情况不起效果。

      const originMessages = i18n.getLocaleMessage(lang);
      i18n.setLocaleMessage(lang, { ...originMessages, ...toSnakeCase(data) });
      setI18nLanguage(lang);
    });
  }
  return Promise.resolve();
}

注意:如果不借助后端实现多语言,则需要在前端创建多个语言文件,可以使用动态加载多语言文件实现优化。代码如下:

const loadedLanguages = [DEFAULT_LANG]

function setI18nLanguage(lang) {
  i18n.locale = lang;
  return lang;
}

export function loadLanguageAsync(lang, group) {

  // 动态加载多语言模块

  if (i18n.locale !== lang) {
    if (!loadedLanguages.includes(lang)) {
      import(/* webpackChunkName: "lang-[request]" */ `@/lang/${lang}`).then(msgs => {
        i18n.setLocaleMessage(lang, msgs)
        loadedLanguages.push(lang)
        setI18nLanguage(lang)
      })
    }
  }

  return Promise.resolve();
}

心得

小小心得分享,有更加好的方案欢迎分享喔~