前言
看了 vue-i18n
以及网上大部分相关文章,大多都是在讲 vue-i18n
在页面(项目)中的应用。至少目前,还没有看到哪篇文章是讲 vue-i18n
在组件库中的应用的,特别是再结合 element-ui
的国际化。
以下是个人在封装的组件库中使用 vue-i18n
的记录,也供有相关需求的同学参考。
项目中使用 i18n
不赘述,这里只是做个比较参考,直接抄官方文档
import Vue from 'vue' import VueI18n from 'vue-i18n' Vue.use(VueI18n) // 准备翻译的语言环境信息 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')
<template> <div id="app"> <p>{{ $t("message.hello") }}</p> </div> </template> 复制代码
组件库中使用 i18n
需求
- 组件库代码中配置
i18n
,并在组件中注册和使用 - 页面中若使用
i18n
,可以合并和覆盖组件库的i18n
配置 -
element-ui
的i18n
在组件库中注册,页面无需再注册 - 页面切换语言时,组件库和
element-ui
的语言也一起切换
未添加 i18n 前的组件库
正常组件库的入口文件,是一个很标准的 export
,供页面进行 Vue.use()
调用。
这个模块没有特殊意义,只是作为基础代码展示,以及与下面加入 i18n
的代码做对比。
const components = {}; // 具体封装的组件这里不做赘述 const install = function (Vue, options = {}) { Object.keys(components).forEach((key) => { Vue.component(key, components[key]); }); }; /* istanbul ignore if */ if (typeof window !== 'undefined' && window.Vue) { install(window.Vue); } export default { version, install, ...components };
组件库 i18n 的配置文件
先在 ./lang
路径下新建语言文件,如 en.js
、zh-CN.js
等(数据格式参考 vue-i18n
)
import Vue from 'vue'; import VueI18n from 'vue-i18n'; Vue.use(VueI18n) // 配置从文件读取 const req = require.context('./lang', false, /.js$/) const modules = req.keys().map(k => { let name = k.match(/./(.+).js/)[1] return { name: name, webLocale: req(k).default, elementLocale: require(`element-ui/lib/locale/lang/${name}`).default // 加入 element 的 i18n } }) // 注册 i18n let i18n = new VueI18n({ locale: 'zh-CN', messages: getMessages(), }) // 对外暴露的合并配置项的方法 export function i18nLocale(config, lang = 'zh-CN') { i18n = new VueI18n({ locale: lang, messages: getMessages(config), }) return i18n; } // 合并方法 function getMessages(config = []) { return modules.reduce((sum, item) => { let conf = config.find(m => m.locale === item.name) || {}; let locale = conf.locale || item.name; sum[locale] = { ...item.elementLocale, // element 的语言配置 ...item.webLocale, // 组件库的语言配置 ...conf.message, // 页面的语言配置 } return sum; }, {}) } export default i18n;
组件库的输出文件
import i18n, { vueI18nLocale } from './locale'; import element from 'element-ui'; const components = {}; // 具体封装的组件这里不做赘述 // 传入一个 options ,为了将 element 的国际化合并方法传入组件库 // 经实验,element 的国际化合并在页面触发有效,在组件库中触发无效,故此操作 const install = function (Vue, options = {}) { Object.keys(components).forEach((key) => { Vue.component(key, components[key]); }); // 在页面使用组件库,进行 Vue.use 的时候注册,能保证相同的 Vue 实例 // 这句是关键!不能使用 Vue.prototype.$i18n = i18n; 会报错,报错原因是与 vue-i18n 内部的变量重名,故这里使用 $i18n_ 代替,但这不影响页面使用 $i18n Object.defineProperty(Vue.prototype, '$i18n_', { get() { // 此 this 为页面 vue 实例,若页面配置了国际化,则使用页面的实例,否则用组件库的国际化 return this.$i18n || i18n; }, configurable: true }); // 在此注册 element,并将页面传入的国际化合并方法,继续传入到 element Vue.use(element, { i18n: options.i18n || ((key, value) => i18n.t(key, value)) }); }; /* istanbul ignore if */ if (typeof window !== 'undefined' && window.Vue) { install(window.Vue); } export default { version, vueI18nLocale, // 导出合并语言配置项的方法 install, ...components };
组件库中使用 i18n
因为命名问题,只能使用 this.$i18n_
而不是 this.$i18n
<div v-bind="$i18n_.t('textPart.textNum')"></div>
项目中使用组件库和 i18n
import Vue from 'vue'; import web from '../src/index'; // 组件库,无需再引用 element // 页面的国际化配置,同样需要新建对应的语言文件 const req = require.context('./lang', false, /.js$/); const localeConfig = req.keys().map((k) => { let name = k.match(/./(.+).js/)[1]; return { locale: name, // 需要与组件库的语言类型一一对应 message: req(k).default }; }); // 使用组件库暴露的合并配置项方法,获得新的 i18n 实例 // 该 i18n 包含了组件库的 i18n 和 页面的 i18n const i18n = web.i18nLocale(localeConfig, 'zh-CN'); // 注册组件库的同时,传入 element 的国际化合并方法 // 此时,该 i18n 包含了 element、组件库、页面 的 i18n Vue.use(web, { i18n: (key, value) => i18n.t(key, value) }); export default new Vue({ el: '#app', router, i18n, // 将 i18n 注册到页面 vue 实例 components: { App }, template: '<App/>' });
页面展示和切换 i18n
这里能展示 element
、组件库、页面 的语言变量,切换语言也是三者一起切换
<template> <div> <!-- 两种写法返回值相同 --> <div v-bind="$t('textPart.textNum')"></div> <div v-bind="$i18n.t('textPart.textNum')"></div> <div @click="changeI18n">切换语言</div> </div> </template> <script> export default { data() { return {}; }, methods: { changeI18n() { // 这里的 this.$i18n 包含了 element、组件库、页面 的 i18n // 然后这里还有一个 this.$i18n_ ,是单独组件库的 i18n this.$i18n.locale = 'en'; } } };
最后
至此,实现了 vue-i18n
在组件库中的应用,同时联合了 element-ui
实现了三方 i18n
的展示和切换。
最关键的是 Object.defineProperty
这个方法,以及 element
国际化的双层传参,前者解决了组件库与页面 i18n
的统一,后者将 element
的 i18n
纳入整个体系当中。事后回想,可谓精妙。
原文地址:https://juejin.cn/post/7226207194701627450