0x00 前言
本文将系统讲解 element
国际化方案实现,项目内置了 51 门语言。src/locale
目录下存放了语言配置文件、国际化处理方法、文本格式等,下面将逐一讲解,耐心读完,相信会对您有所帮助。
0x01 国际化方案
element
的国际化方案没有引入第三方插件如 vue-i18n
,通过自定义实现的。它兼容 vue-i18n
,搭配使用能十分方便。
📁 src/locale/lang
用于存放支持语言的配置文件,默认语言为简体中文语言(zh-CN),src/locale/lang/zh-CN.js
文件返回一个 JSON
格式的数据,内容为各组件相关描述翻译。
export default {
el: {
// 组件 select 选择器
select: {
loading: "加载中",
noMatch: "无匹配数据",
noData: "无数据",
placeholder: "请选择",
},
// ...
},
};
若需要使用其他的语言,在该目录下添加一个语言配置文件即可。
📃 src/locale/format.js
提供字符串模板函数,支持索引或命名关键字参数。 代码实现参考 npm/string-template。
// 核心方法
/**
* 字符串模板函数,支持索引或命名关键字参数
* @param {String} string 模板字符串
* @param {Array} ...args 参数
* @return {String} 格式化文本
*
*/
function template(string, ...args) {
const RE_NARGS = /(%|)\{([0-9a-zA-Z_]+)\}/g;
if (args.length === 1 && typeof args[0] === "object") {
args = args[0];
}
if (!args || !args.hasOwnProperty) {
args = {};
}
return string.replace(RE_NARGS, (match, prefix, i, index) => {
let result;
if (string[index - 1] === "{" && string[index + match.length] === "}") {
return i;
} else {
result = hasOwn(args, i) ? args[i] : null;
if (result === null || result === undefined) {
return "";
}
return result;
}
});
}
📃 src/locale/index.js
index.js
提供三个方法 use
、 t
、 i18n
用于语言切换设置、国际化处理、i18n
插件兼容以及自定义。详细内容请看注释源码。
use
方法参数为语言配置内容,用于语言切换。若参数为空,默认使用简体中文(zh-CN
)。
import defaultLang from "element-ui/src/locale/lang/zh-CN";
let lang = defaultLang; // 默认语言
// 语言切换
export const use = function (l) {
lang = l || lang;
};
i18n
方法用于外部插件方法的兼容和自定义处理方法引入。项目 i18nHandler
方法的实现就是用来兼容外部插件vue-i18n@5.x
。
若传入自定义处理方法, i18nHandler
方法实现将被覆盖。
// i18nHandler 是一个 i18n 的处理方法,这块逻辑就是用来兼容外部的 i18n 方案如 vue-i18n@5.x。
let i18nHandler = function () {
// 若引入插件 vue-i18n@5.x,会在 Vue 的原型上挂载 $t 方法。
// i18nHandler 默认会尝试去找 Vue 原型中的 $t 函数,
const vuei18n = Object.getPrototypeOf(this || Vue).$t;
// 方法vuei18n存在
if (typeof vuei18n === "function" && !!Vue.locale) {
// 合并语言配置内容
if (!merged) {
merged = true;
Vue.locale(
Vue.config.lang,
deepmerge(lang, Vue.locale(Vue.config.lang) || {}, { clone: true })
);
}
// 使用vuei18n进行国际化处理
return vuei18n.apply(this, arguments);
}
};
// i18n 的处理方法(支持引入自定义方法)
export const i18n = function (fn) {
i18nHandler = fn || i18nHandler;
};
t
方法用于组件国际化处理,它根据传入的 path
路径,从语言配置中找到对应的文案,然后在结合 options
进行格式化处理。
// 默认语言 中文简体
import defaultLang from "element-ui/src/locale/lang/zh-CN";
// 字符串模板函数
import Format from "./format";
// 等同于 template() 方法
const format = Format(Vue);
let lang = defaultLang; // 默认语言
let i18nHandler = function () {
// ...
};
// 国际化处理方法
export const t = function (path, options) {
// 用来兼容外部的 i18n 方案如 vue-i18n@5.x 或自定义方法
let value = i18nHandler.apply(this, arguments);
// 优先使用外部方法
if (value !== null && value !== undefined) return value;
// 路径截取 获取属性层级
const array = path.split(".");
// 翻译内容
let current = lang;
// 根据路径中属性,层级递归翻译内容
for (let i = 0, j = array.length; i < j; i++) {
const property = array[i];
value = current[property];
// console.log(property, value);
if (i === j - 1) {
// 格式化内容
// value 获取翻译内容 options 组件传入参数
return format(value, options);
}
if (!value) return "";
current = value;
}
return "";
};
0x02 组件国际化引用
接下来通过Select
、Pagination
组件的示例讲解下国际化功能引入使用。
功能引入
前文可知 src/mixins/locale.js
引入src/locale/index.js
中的 t
方法,通过 mixin
方式提供给组件页面使用。
import { t } from "element-ui/src/locale";
export default {
methods: {
t(...args) {
return t.apply(this, args);
},
},
};
Select
组件源码中可以看到国际化处理逻辑。
import Locale from "element-ui/src/mixins/locale";
export default {
mixins: [Locale],
name: "ElSelect",
componentName: "ElSelect",
computed: {
emptyText() {
if (this.options.length === 0) {
return this.noDataText || this.t("el.select.noData");
}
// ...
},
},
// ...
};
t(path)
this.t('el.select.noData')
调用 t
方法据传入的 path 路径值 el.select.noData
,参数options
为 undefined
,默认情况下没有引入第三方插件或自定义方法,直接从默认语言(简体中文)配置中找到对应的文案 。
此时 value
值为 “无数据”,参数options
为 “undefined”,使用 format
方法(即 src/locale/format.js
定义的 template
方法 )进行文本格式化。
format('无数据', undefined) => '无数据'
组件效果如下 👇。
t(path, options)
在Pagination
分页组件中, this.t('el.pagination.total', { total: this.$parent.total })
传入 path 路径值 el.pagination.total
,参数 options
为 {total: 1000}
。
根据 el.pagination.total
获取值为 共 {total} 条
,是一个模板字符串。
{
el: {
pagination: {
goto: "前往",
pagesize: "条/页",
total: "共 {total} 条",
pageClassifier: "页",
},
// ...
}
}
此时 value
值为 共 {total} 条
,参数options
为 {total: 1000}
, 使用 format
方法进行文本格式化。
format('共 {total} 条', {total: 1000}) => '共 1000 条'
不是所有的值都是纯文本,存在模板字符串。引入src/locale/format.js
定义的 format
方法用于字符串格式化操作。
组件效果如下 👇。
0x03 项目国际化设置
通过上述篇幅详细讨论组件内部国际化方案实现。接下来介绍组件的使用中如何使用设置多语言,引入第三方插件、手动扩展方法等操作。
完整引入
使用 Vue.use()
全局注册组件时,也可以传入一个可选的选项对象,locale
用于语言设置、i18n
自定义 i18n 的处理方法 。
import Vue from "vue";
import ElementUI from "element-ui";
import lang from "element-ui/lib/locale/lang/en";
import "element-ui/lib/theme-chalk/index.css";
Vue.use(ElementUI, {
locale: lang,
i18n: function (path, options) {
// ...
},
});
src/index.js
中的 install
方法中提供 locale.use()
、 locale.i18n()
,接收参数 opts
,用于国际化全局设置。
import locale from "element-ui/src/locale";
// ...
const install = function (Vue, opts = {}) {
locale.use(opts.locale);
locale.i18n(opts.i18n);
// ...
};
按需引入
引入部分组件比如 Button
和Select
,可以使用两种方式进行注册:插件注册 Vue.use()
或 组件全局注册Vue.component()
;引入 locale
方法设置语言;引入 i18n
方法用于处理方法;。
import Vue from "vue";
import { Button, Select, locale, i18n } from "element-ui";
import lang from "element-ui/lib/locale/lang/en";
// import locale from 'element-ui/lib/locale'
import "element-ui/lib/theme-chalk/index.css";
// 设置语言 等同于 locale.use()
locale(lang);
// 设置i18n处理方法 等同于 locale.i18n()
i18n(function (path, options) {
// ...
});
// 引入组件
Vue.use(Button);
Vue.component(Select.name, Select);
src/index.js
中导出的 locale
、 i18n
方法。 locale
等同于locale.use
, i18n
等同于locale.i18n
。
import locale from "element-ui/src/locale";
// ...
const install = function (Vue, opts = {}) {
// ...
};
export default {
locale: locale.use,
i18n: locale.i18n,
install,
Button,
Select,
// ...
};
搭配vue-i18n
使用
项目也可引入第三方插件,如vue-i18n
,最新版本为 8.x
,使用需要手动处理 (Element
兼容 5.x
) 。
import Vue from "vue";
import Element from "element-ui";
import VueI18n from "vue-i18n";
import enLocale from "element-ui/lib/locale/lang/en";
import zhLocale from "element-ui/lib/locale/lang/zh-CN";
Vue.use(VueI18n);
const messages = {
en: {
message: {
hello: "hello world",
},
...enLocale, // 或者用 Object.assign({ message: {hello: "hello world"}} }, enLocale)
},
zh: {
message: {
hello: "你好世界",
},
...zhLocale, // 或者用 Object.assign({ message: {hello: "你好世界"} }, zhLocale)
},
};
// 创建 VueI18n 实例
const i18n = new VueI18n({
locale: "zh", // 设置语言
messages, // 语言翻译内容
});
//注册Element 自定义i18n 的处理方法
Vue.use(Element, {
i18n: (key, value) => i18n.t(key, value),
});
new Vue({
i18n,
render: (h) => h(App),
}).$mount("#app");
测试页面
<template>
<div id="app">
<div>message.hello -- {{ $t("message.hello") }}</div>
<div>el.select.noData -- {{ $t("el.select.noData") }}</div>
</div>
</template>
<script>
export default {
name: "App",
};
</script>
设置语言为zh
, 页面输出内容:
设置语言为en
, 页面输出内容: