国际化插件i18n和vuex的混合使用

缘起

前段时间测试对于项目切换中英文后页面会闪一下标为bug,想了想肯定是前任开发者在切换中英文后对页面进行了强制刷新。查看源码发现果不其然,并且前任仅将中英文状态存储在cookie中,然后在需要的组件中通过cookie获取中英文状态。发现接口都是同时有返回中英文,这样完全可以通过vuex全局状态管理中英文状态,达到在一处修改了中英文后全局自动修改。

本文以 vue+ElementUI 并且以和vue组合的 vue-i18n 为例

准备阶段

1、安装相关插件

1
2
3
4
5
6
// 安装 vue-i18n
npm install --save vue-i18n
// 安装 js-cookie
npm install --save js-cookie
// 安装 vuex
npm install --save vuex

2、目录结构
目录结构

国际化部分

i18n 封装

将i18n的引入与注册实例封装在了./langs/index.js中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import Cookies from 'js-cookie'
import elementEnLocale from 'element-ui/lib/locale/lang/en'
import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'
import enLocale from './en'
import zhLocale from './zh'

Vue.use(VueI18n)

const messages = {
  en: {
    ...enLocale,
    ...elementEnLocale
  },
  zh: {
    ...zhLocale,
    ...elementZhLocale
  }
}
export function getLanguage() {
  const chooseLanguage = Cookies.get('language')
  if (chooseLanguage) return chooseLanguage

  // if has not choose language
  const language = (navigator.language || navigator.browserLanguage).toLowerCase()
  const locales = Object.keys(messages)
  for (const locale of locales) {
    if (language.indexOf(locale) > -1) {
      return locale
    }
  }
  return 'en'
}
const i18n = new VueI18n({
  locale: getLanguage(),
  // set locale messages
  messages
})

export default i18n

语言配置样例

en.js

1
2
3
4
5
6
7
8
9
export default {
  settings: {
    title: 'Page style setting',
    theme: 'Theme Color',
    tagsView: 'Open Tags-View',
    fixedHeader: 'Fixed Header',
    sidebarLogo: 'Sidebar Logo'
  }
}

zh.js

1
2
3
4
5
6
7
8
9
export default {
  settings: {
    title: '系统布局配置',
    theme: '主题色',
    tagsView: '开启 Tags-View',
    fixedHeader: '固定 Header',
    sidebarLogo: '侧边栏 Logo'
  }
}

vuex

通过vuex实现国际化状态切换状态管理功能

index

./store/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
// 使用vuex
Vue.use(Vuex)

// 这种方式很好的将modules中的所有文件全部导出,这样以后修改modules不用再在此处进行添加导出
const modulesFiles = require.context('./modules', true, /\.js$/)
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
  const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
  const value = modulesFiles(modulePath)
  modules[moduleName] = value.default
  return modules
}, {})

// 创建vuex实例
const store = new Vuex.Store({
  modules,
  getters
})

export default store

getters

./store/getters.js

1
2
3
4
5
6
// 监听 vuex 中state下language的改变
// 外部通过 this.$store.getters.language 调用
const getters = {
  language: state => state.app.language,
}
export default getters

app.js

./store/modules/app.js
单独建立一个app.js的目的是以后如果需要扩展其他的全局状态,可以封装在其他独立的js中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 获取当前国际化状态封装在了上面的./langs/index.js中,相当于是初始化国际化
import { getLanguage } from '@/i18n/langs/index'
import Cookies from 'js-cookie'
// 数据,外部可以通过 this.$store.state.language 调用
const state = {
  language: getLanguage()
}
// 外部可以通过提交mutation来修改state中的值
// this.$store.commit("SET_LANGUAGE")
const mutations = {
  SET_LANGUAGE: (state, language) => {
    // 修改state并将其保存在Cookies中,这样用户下次打开仍然是上次选择的国际化状态
    state.language = language
    Cookies.set('language', language)
  },
}
// 官方推荐我们去提交一个actions,在actions中提交mutation再去修改state
// this.$store.dispatch("setLanguage")
const actions = {
  setLanguage({ commit }, language) {
    commit('SET_LANGUAGE', language)
  },
}
export default {
  namespaced: true,
  state,
  mutations,
  actions
}

main

main.js配置导入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Vue from 'vue'
import App from './App'
import router from './router'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
// 导入封装的i18n配置
import i18n from './i18n/langs';
import Cookies from 'js-cookie'
import store from './store'

// 应用
Vue.use(ElementUI, {
  i18n: (key, value) => i18n.t(key, value)
})

// 将i18n、store注入到vue实例中
new Vue({
  el: '#app',
  router,
  i18n,
  store,
  render: h => h(App)
})

应用

1、切换语言,向vuex的actions提交改变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<template>
  <el-dropdown trigger="click" class="international" @command="handleSetLanguage">
    <div>
      <svg-icon class-name="international-icon" icon-class="language" />
    </div>
    <el-dropdown-menu slot="dropdown">
      <el-dropdown-item :disabled="language==='zh'" command="zh">
        中文
      </el-dropdown-item>
      <el-dropdown-item :disabled="language==='en'" command="en">
        English
      </el-dropdown-item>
    </el-dropdown-menu>
  </el-dropdown>
</template>

<script>
export default {
  computed: {
    language() {
      return this.$store.getters.language
    }
  },
  methods: {
    handleSetLanguage(lang) {
      // 设置i18n,以便后面通过$t('settings.title')使用
      this.$i18n.locale = lang
      // 向vuex提交修改后的国际化,以便后面获取
      this.$store.dispatch('app/setLanguage', lang)
      this.$message({
        message: 'Switch Language Success',
        type: 'success'
      })
    }
  }
}
</script>

2、在需要的组件中使用与获取国际化状态

1
2
3
4
5
6
7
8
9
10
11
12
// html中使用
{{ $t('settings.title') }}

// js中使用
$t('settings.title')

// 通过computed获取vuex中的language状态,这样其他地方修改后这边可以监测到,并同步修改DOM中的值
computed: {
    language() {
      return this.$store.getters.language
    }
},