组件国际化
# 前言
考虑到,开发者开发的系统的目标群体也有可能是面向国外的,很多组件库都提供了国际化的能力。MSSUI
也借鉴了 ElementUI
国际化实现的思想。
# 如何使用
# 使用vue-i18n
// @ 代表 src目录
import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";
import zh_CN from "@/locale/lang/zh-CN";
import en_US from "@/locale/lang/en-US";
import MlElement from "../lib/ml-element-ui.common.js";
import MlElement from "@/packages/index";
import "../lib/theme-chalk/index.css";
// 使用i18n
const messages = {
en: en_US,
zh: zh_CN,
};
Vue.use(VueI18n);
const i18n = new VueI18n({
locale: "zh", // 默认使用中文
messages,
});
Vue.use(MlElement, { i18n });
Vue.config.productionTip = false;
new Vue({
render: (h) => h(App),
i18n,
}).$mount("#app");
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
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
# 组件自带国际化
// @ 代表 src目录
import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";
import en_US from "@/locale/lang/en-US";
import MlElement from "../lib/ml-element-ui.common.js";
import MlElement from "@/packages/index";
import "../lib/theme-chalk/index.css";
// 在这里指定使用的语言
Vue.use(MlElement, { locale: en_US });
Vue.config.productionTip = false;
new Vue({
render: (h) => h(App),
}).$mount("#app");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 国际化实现原理
国际化的逻辑在 src/locale
目录下:
├── index.js
└── lang
├── en-US.js
└── zh-CN.js
1
2
3
4
2
3
4
解释下相关目录的作用:
- index.js 国际化主要的逻辑
- lang 提供的国际化的语言
- en-US.js 英文
- zh-CN.js 中文
打开 index.js
:
// @ 目录代表 MSSUI 源码中的src目录
import zh_CN from "@/locale/lang/zh-CN";
import { format } from "@/utils/format";
import Vue from "vue";
import { isDef } from "@/utils/index";
let lang = zh_CN;
let vuei18n = null;
let i18nHandler = function () {
vuei18n = (this || Vue).$i18n || vuei18n;
if (vuei18n && vuei18n.locale) {
return vuei18n.t(...arguments);
}
};
const t = function (path, options) {
let value;
// 如果 使用了i18n 组件的国际化就由i18n接管
value = i18nHandler.apply(this, arguments);
if (isDef(value)) return value;
// 否则国际化由组件内部接管
// ml.button.msg
const array = path.split(".");
let current = lang;
for (let i = 0, j = array.length; i < j; i++) {
const property = array[i];
value = current[property];
if (i === j - 1) return format(value, options);
if (!value) return "";
current = value;
}
return "";
};
const use = function (l) {
lang = l || lang;
};
const i18n = function (initI18n) {
vuei18n = initI18n;
};
export default {
use,
t,
i18n,
lang,
};
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
42
43
44
45
46
47
48
49
50
51
52
53
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
42
43
44
45
46
47
48
49
50
51
52
53
index.js
中主要暴露了 use
、t
、i18n
、lang
。
- use 函数用于指定使用的语言
- t 函数提供语言转换的能力
- i18n 函数用于兼容vue-i18n
- lang 变量记录了当前使用的语言
主要逻辑就在于 t
函数,可以这样使用 t
函数:
<template>
<ml-button type="info" @click="changeLang">{{ lang }}</ml-button>
</template>
<script>
import locale from "@/mixins/locale";
export default {
mixins: [locale],
data() {
return {
lang: "点击切换语言",
msg: "欢迎来到你的 Vue.js App",
confirm: "确定",
};
},
methods: {
changeLang() {
this.msg = this.t("ml.button.msg");
this.confirm = this.t("ml.button.confirm");
},
},
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
我们假如使用的是中文:
module.exports = {
lang: "zh-CN",
ml: {
button: {
msg: "欢迎来到你的 Vue.js App",
confirm: "确定",
cancle: "取消",
},
alert: {
title: "提示提示提示",
},
},
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
以上面的用法为例,t
函数的作用就是将 ml.button.msg
进行求值,最终将组件内所有包含 ml.button.msg
字符串 转变成 欢迎来到你的 Vue.js App
的中文。
那么是如何转换的呢?那我们就得去看下 t
函数了:
const t = function (path, options) {
let value;
// 如果 使用了i18n 组件的国际化就由i18n接管
value = i18nHandler.apply(this, arguments);
if (isDef(value)) return value;
// 否则国际化由组件内部接管
// ml.button.msg
const array = path.split(".");
let current = lang;
for (let i = 0, j = array.length; i < j; i++) {
const property = array[i];
value = current[property];
if (i === j - 1) return format(value, options);
if (!value) return "";
current = value;
}
return "";
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
思路就是如果使用了 vue-i18n
,组件的国际化工作就由 vue-i18n
接管。其实也就是i18nHandler
处理函数,如果该函数没有返回值,说明没有使用 vue-i18n
。那么国际化就由组件内部的国际化逻辑接管,就是下面这段代码:
const array = path.split(".");
let current = lang;
for (let i = 0, j = array.length; i < j; i++) {
const property = array[i];
value = current[property];
if (i === j - 1) return format(value, options);
if (!value) return "";
current = value;
}
return "";
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
这段逻辑就是递归找到求值字符串的最后一个变量,比如 ml.button.msg
这段字符串就是先拿到 msg
,然后利用 format
函数去获取 msg
变量的值。所以说,求值的逻辑真正是 format
函数做的,我们看下 format
函数中的代码,在源码中的src/utils/format.js
中。
/**
* String format template
* - Inspired:
* https://github.com/Matt-Esch/string-template
*/
// ml.button.msg
import { hasOwn, isUnDef, isPlainObject } from "./index";
const RE_NARGS = /(%|)\{([0-9a-zA-Z_]+)\}/g;
export function format(string, ...args) {
if (args.length === 1 && isPlainObject(args[0])) {
args = args[0];
}
if (!args || !args.hasOwnProperty) {
args = {};
}
if (isUnDef(string)) {
return "";
}
// match => 匹配到的字符串 p1 => 第一个捕获组 p2 => 第二个捕获组 index => 匹配到的字符串在原字符串中的索引值
return string.replace(RE_NARGS, (match, p1, p2, index) => {
let result;
if (string[index - 1] === "{" && string[index + match.length] === "}") {
result = p2;
} else {
result = hasOwn(args, p2) ? args[p2] : null;
if (isUnDef(result)) {
return "";
}
}
return result;
});
}
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
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
这其实是一个开源的字符串库,叫做string-template (opens new window), 利用正则表达式 /(%|)\{([0-9a-zA-Z_]+)\}/g
,匹配出需要求值的字符串,然后利用 replace
函数将求到的值 和 对应的变量 进行替换,如此就实现了国际化的工作。
编辑 (opens new window)
上次更新: 2021/10/16, 11:22:25