mm_tpl 2.7.4 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1082 -1083
- package/README_EN.md +1066 -1067
- package/lib/lang.js +52 -5
- package/lib/tpl.js +4 -11
- package/package.json +66 -65
- package/lib/base_lang.js +0 -200
package/lib/lang.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
// 语言实例由 mm_lang 提供,本文件的 Lang 只是语言包加载器,故以 BaseLang 别名引用
|
|
1
2
|
const {
|
|
2
|
-
BaseLang
|
|
3
|
-
} = require('
|
|
3
|
+
Lang: BaseLang
|
|
4
|
+
} = require('mm_lang');
|
|
4
5
|
const {
|
|
5
6
|
TplUtil
|
|
6
7
|
} = require('./util.js');
|
|
@@ -153,24 +154,70 @@ Lang.prototype.buildDict = function (force) {
|
|
|
153
154
|
return Object.assign({}, dict);
|
|
154
155
|
};
|
|
155
156
|
|
|
157
|
+
/**
|
|
158
|
+
* 将嵌套语言包字典规范化为 mm_lang 的扁平词条表
|
|
159
|
+
* mm_lang 的 Lang 只接受 `语言.层级键 → 文本` 的扁平入参,控制键(default、silent)原样保留
|
|
160
|
+
* @param {Object} dict 语言包字典,语言代码对应嵌套词条对象
|
|
161
|
+
* @returns {Object} 扁平词条表
|
|
162
|
+
*/
|
|
163
|
+
function flattenLangPack(dict) {
|
|
164
|
+
var flat = {};
|
|
165
|
+
for (var key in dict) {
|
|
166
|
+
if (!Object.prototype.hasOwnProperty.call(dict, key)) {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
var val = dict[key];
|
|
170
|
+
if (val && typeof (val) === 'object' && !Array.isArray(val)) {
|
|
171
|
+
flattenInto(val, key, flat);
|
|
172
|
+
} else {
|
|
173
|
+
flat[key] = val;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return flat;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* 递归展开嵌套词条为点号路径键 - 内部函数
|
|
181
|
+
* @param {Object} obj 嵌套词条对象
|
|
182
|
+
* @param {string} prefix 键前缀
|
|
183
|
+
* @param {Object} flat 输出容器
|
|
184
|
+
* @returns {void}
|
|
185
|
+
*/
|
|
186
|
+
function flattenInto(obj, prefix, flat) {
|
|
187
|
+
for (var key in obj) {
|
|
188
|
+
if (!Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
var val = obj[key];
|
|
192
|
+
var path = prefix + '.' + key;
|
|
193
|
+
if (val && typeof (val) === 'object' && !Array.isArray(val)) {
|
|
194
|
+
flattenInto(val, path, flat);
|
|
195
|
+
} else {
|
|
196
|
+
flat[path] = val;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
156
201
|
/**
|
|
157
202
|
* 创建语言实例
|
|
158
203
|
* 语言包字典在实例间共享,仅当前语言各自独立
|
|
159
204
|
* @param {Object} config - 配置参数
|
|
160
205
|
* @param {boolean} force - 是否跳过语言包缓存强制重新加载
|
|
161
|
-
* @returns {BaseLang}
|
|
206
|
+
* @returns {BaseLang} 语言实例(mm_lang 的 Lang)
|
|
162
207
|
*/
|
|
163
208
|
Lang.prototype.create = function (config, force) {
|
|
164
209
|
var dict = this.buildDict(force);
|
|
165
210
|
dict.default = config.default_lang || dict.default || 'zh_cn';
|
|
166
|
-
|
|
211
|
+
// 词条缺失时静默返回 undefined,日志由模板引擎自身的 log 体系统一输出
|
|
212
|
+
dict.silent = true;
|
|
213
|
+
var lang = new BaseLang(flattenLangPack(dict));
|
|
167
214
|
this.registerPlural(lang, config.plural_rules);
|
|
168
215
|
return lang;
|
|
169
216
|
};
|
|
170
217
|
|
|
171
218
|
/**
|
|
172
219
|
* 登记复数规则
|
|
173
|
-
*
|
|
220
|
+
* mm_lang 的 Lang 内置规则仅覆盖 zh_cn、en,配置中的语言代码需显式登记才能按语言取复数形式
|
|
174
221
|
* @param {BaseLang} lang - 语言实例
|
|
175
222
|
* @param {Object} plural_rules - 复数规则,键为语言代码
|
|
176
223
|
*/
|
package/lib/tpl.js
CHANGED
|
@@ -537,22 +537,14 @@ Tpl.prototype.tc = function (key, count, ...args) {
|
|
|
537
537
|
|
|
538
538
|
/**
|
|
539
539
|
* 获取支持的语言列表
|
|
540
|
-
*
|
|
540
|
+
* 语言列表来自 mm_lang 的扁平词条表,无词条的语言不会出现,无需过滤空字典
|
|
541
541
|
* @returns {Array<string>} 语言代码数组
|
|
542
542
|
*/
|
|
543
543
|
Tpl.prototype.getLangs = function () {
|
|
544
544
|
if (!this.lang) {
|
|
545
545
|
return [];
|
|
546
546
|
}
|
|
547
|
-
|
|
548
|
-
var list = [];
|
|
549
|
-
for (var i = 0; i < langs.length; i++) {
|
|
550
|
-
var dict = this.lang.config[langs[i]];
|
|
551
|
-
if (dict && Object.keys(dict).length) {
|
|
552
|
-
list.push(langs[i]);
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
return list;
|
|
547
|
+
return this.lang.getLangs();
|
|
556
548
|
};
|
|
557
549
|
|
|
558
550
|
/**
|
|
@@ -580,7 +572,8 @@ Tpl.prototype.loadLang = function (lang) {
|
|
|
580
572
|
return null;
|
|
581
573
|
}
|
|
582
574
|
this.reloadLangs();
|
|
583
|
-
|
|
575
|
+
// 通过 getDict 导出该语言的嵌套词条字典(形态与语言包文件一致)
|
|
576
|
+
var dict = this.lang ? this.lang.getDict(lang) : null;
|
|
584
577
|
if (!dict) {
|
|
585
578
|
this.log('warn', '语言包不存在:' + lang);
|
|
586
579
|
return null;
|
package/package.json
CHANGED
|
@@ -1,65 +1,66 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "mm_tpl",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Super Meimei Template Engine - High-performance template engine based on art-template with MVC pattern, hook system, theme switching and caching mechanism",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"io": true,
|
|
7
|
-
"priority": 100,
|
|
8
|
-
"scripts": {
|
|
9
|
-
"dev": "nodemon test.js",
|
|
10
|
-
"demo": "node example/demo.js",
|
|
11
|
-
"test": "node test.js",
|
|
12
|
-
"coverage": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage"
|
|
13
|
-
},
|
|
14
|
-
"repository": {
|
|
15
|
-
"type": "git",
|
|
16
|
-
"url": "git+https://gitee.com/qiuwenwu91/mm_tpl.git"
|
|
17
|
-
},
|
|
18
|
-
"keywords": [
|
|
19
|
-
"mm_mod",
|
|
20
|
-
"mm_infra",
|
|
21
|
-
"mm_common",
|
|
22
|
-
"mm_web",
|
|
23
|
-
"template",
|
|
24
|
-
"engine",
|
|
25
|
-
"mvc",
|
|
26
|
-
"web",
|
|
27
|
-
"view",
|
|
28
|
-
"render",
|
|
29
|
-
"art-template",
|
|
30
|
-
"hook",
|
|
31
|
-
"theme",
|
|
32
|
-
"cache",
|
|
33
|
-
"nodejs",
|
|
34
|
-
"javascript"
|
|
35
|
-
],
|
|
36
|
-
"layer": "infra",
|
|
37
|
-
"group": "common",
|
|
38
|
-
"author": "邱文武",
|
|
39
|
-
"license": "MIT",
|
|
40
|
-
"bugs": {
|
|
41
|
-
"url": "https://gitee.com/qiuwenwu91/mm_tpl/issues"
|
|
42
|
-
},
|
|
43
|
-
"homepage": "https://gitee.com/qiuwenwu91/mm_tpl#readme",
|
|
44
|
-
"dependencies": {
|
|
45
|
-
"art-template": "4.13.4",
|
|
46
|
-
"mm_corejs": "^1.1.6"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
"eslint
|
|
64
|
-
|
|
65
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "mm_tpl",
|
|
3
|
+
"version": "2.9.0",
|
|
4
|
+
"description": "Super Meimei Template Engine - High-performance template engine based on art-template with MVC pattern, hook system, theme switching and caching mechanism",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"io": true,
|
|
7
|
+
"priority": 100,
|
|
8
|
+
"scripts": {
|
|
9
|
+
"dev": "nodemon test.js",
|
|
10
|
+
"demo": "node example/demo.js",
|
|
11
|
+
"test": "node test.js",
|
|
12
|
+
"coverage": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage"
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://gitee.com/qiuwenwu91/mm_tpl.git"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"mm_mod",
|
|
20
|
+
"mm_infra",
|
|
21
|
+
"mm_common",
|
|
22
|
+
"mm_web",
|
|
23
|
+
"template",
|
|
24
|
+
"engine",
|
|
25
|
+
"mvc",
|
|
26
|
+
"web",
|
|
27
|
+
"view",
|
|
28
|
+
"render",
|
|
29
|
+
"art-template",
|
|
30
|
+
"hook",
|
|
31
|
+
"theme",
|
|
32
|
+
"cache",
|
|
33
|
+
"nodejs",
|
|
34
|
+
"javascript"
|
|
35
|
+
],
|
|
36
|
+
"layer": "infra",
|
|
37
|
+
"group": "common",
|
|
38
|
+
"author": "邱文武",
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"bugs": {
|
|
41
|
+
"url": "https://gitee.com/qiuwenwu91/mm_tpl/issues"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://gitee.com/qiuwenwu91/mm_tpl#readme",
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"art-template": "4.13.4",
|
|
46
|
+
"mm_corejs": "^1.1.6",
|
|
47
|
+
"mm_lang": "^3.0.0"
|
|
48
|
+
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=12.0.0"
|
|
51
|
+
},
|
|
52
|
+
"files": [
|
|
53
|
+
"index.js",
|
|
54
|
+
"hook.js",
|
|
55
|
+
"tpl_rule.js",
|
|
56
|
+
"lib/",
|
|
57
|
+
"doc/",
|
|
58
|
+
"README.md",
|
|
59
|
+
"README_EN.md",
|
|
60
|
+
"package.json"
|
|
61
|
+
],
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"eslint": "^9.39.5",
|
|
64
|
+
"eslint-plugin-jsdoc": "^65.0.0"
|
|
65
|
+
}
|
|
66
|
+
}
|
package/lib/base_lang.js
DELETED
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 基础语言类
|
|
3
|
-
* @description 提供语言包字典存储、翻译取词、复数形式选择与占位符插值能力
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* 基础语言类
|
|
8
|
-
* @class BaseLang
|
|
9
|
-
*/
|
|
10
|
-
class BaseLang {
|
|
11
|
-
/**
|
|
12
|
-
* 创建语言实例
|
|
13
|
-
* @param {Object} config 语言包字典,形如 { zh_cn: { key: 值 }, default: 'zh_cn' }
|
|
14
|
-
*/
|
|
15
|
-
constructor(config) {
|
|
16
|
-
/**
|
|
17
|
-
* 语言包字典
|
|
18
|
-
* @type {Object}
|
|
19
|
-
*/
|
|
20
|
-
this.config = config || {};
|
|
21
|
-
/**
|
|
22
|
-
* 当前语言代码
|
|
23
|
-
* @type {String}
|
|
24
|
-
*/
|
|
25
|
-
this.now = this.config.default || 'zh_cn';
|
|
26
|
-
/**
|
|
27
|
-
* 复数规则表,键为语言代码,值为根据数量返回复数索引的函数
|
|
28
|
-
* @type {Object}
|
|
29
|
-
*/
|
|
30
|
-
this.rules = {
|
|
31
|
-
zh_cn: function (count) {
|
|
32
|
-
return 0;
|
|
33
|
-
},
|
|
34
|
-
en: function (count) {
|
|
35
|
-
return count === 1 ? 0 : 1;
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* 注册语言复数规则
|
|
42
|
-
* @param {String} code 语言代码
|
|
43
|
-
* @param {Function} fn 复数规则函数,入参为数量,返回复数索引
|
|
44
|
-
* @returns {BaseLang} 当前实例
|
|
45
|
-
*/
|
|
46
|
-
registerPlural(code, fn) {
|
|
47
|
-
if (code && typeof fn === 'function') {
|
|
48
|
-
this.rules[code] = fn;
|
|
49
|
-
}
|
|
50
|
-
return this;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* 设置当前语言
|
|
55
|
-
* 请求的语言不支持时回退到默认语言
|
|
56
|
-
* @param {String} lang 语言代码
|
|
57
|
-
* @returns {String} 设置后的语言代码
|
|
58
|
-
*/
|
|
59
|
-
setLang(lang) {
|
|
60
|
-
if (this.isSupported(lang)) {
|
|
61
|
-
this.now = lang;
|
|
62
|
-
} else if (this.isSupported(this.config.default)) {
|
|
63
|
-
this.now = this.config.default;
|
|
64
|
-
}
|
|
65
|
-
return this.now;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* 获取支持的语言列表
|
|
70
|
-
* 过滤掉配置中的默认语言标记
|
|
71
|
-
* @returns {Array<String>} 语言代码数组
|
|
72
|
-
*/
|
|
73
|
-
getLangs() {
|
|
74
|
-
return Object.keys(this.config).filter(function (key) {
|
|
75
|
-
return key !== 'default';
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* 判断语言是否支持
|
|
81
|
-
* @param {String} lang 语言代码
|
|
82
|
-
* @returns {Boolean} 支持返回true,否则返回false
|
|
83
|
-
*/
|
|
84
|
-
isSupported(lang) {
|
|
85
|
-
return !!lang && lang !== 'default' && typeof this.config[lang] === 'object' && this.config[lang] !== null;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* 获取翻译词条
|
|
90
|
-
* 键支持 a.b 层级形式,词条缺失时回退到默认语言
|
|
91
|
-
* @param {String} key 词条键名
|
|
92
|
-
* @param {...any} args 位置占位符替换参数,依次替换 {0}、{1} 等
|
|
93
|
-
* @returns {*} 翻译文本;数组词条不带数量时原样返回
|
|
94
|
-
*/
|
|
95
|
-
get(key, ...args) {
|
|
96
|
-
var val = this._getValue(key);
|
|
97
|
-
if (typeof val !== 'string' || !args.length) {
|
|
98
|
-
return val;
|
|
99
|
-
}
|
|
100
|
-
for (var i = 0; i < args.length; i++) {
|
|
101
|
-
val = val.split('{' + i + '}').join(args[i]);
|
|
102
|
-
}
|
|
103
|
-
return val;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* 获取带复数形式的翻译文本
|
|
108
|
-
* @param {String} key 词条键名
|
|
109
|
-
* @param {Number} count 数量,用于选择复数形式并替换 {count} 占位符
|
|
110
|
-
* @param {...any} args 位置占位符替换参数
|
|
111
|
-
* @returns {String} 翻译文本
|
|
112
|
-
*/
|
|
113
|
-
tc(key, count, ...args) {
|
|
114
|
-
var val = this._getValue(key);
|
|
115
|
-
var rule = this.rules[this.now] || this.rules.zh_cn;
|
|
116
|
-
var index = typeof rule === 'function' ? rule(count) : 0;
|
|
117
|
-
var text = Array.isArray(val) ? (val[index] || val[0] || '') : (typeof val === 'string' ? val : '');
|
|
118
|
-
text = String(text).split('{count}').join(count);
|
|
119
|
-
for (var i = 0; i < args.length; i++) {
|
|
120
|
-
text = text.split('{' + i + '}').join(args[i]);
|
|
121
|
-
}
|
|
122
|
-
return text;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* 写入词条
|
|
127
|
-
* 写入当前语言字典,键支持 a.b 层级形式
|
|
128
|
-
* @param {String} key 词条键名
|
|
129
|
-
* @param {*} value 词条值
|
|
130
|
-
* @returns {BaseLang} 当前实例
|
|
131
|
-
*/
|
|
132
|
-
set(key, value) {
|
|
133
|
-
var lang = this.config[this.now];
|
|
134
|
-
if (!lang || typeof lang !== 'object') {
|
|
135
|
-
lang = {};
|
|
136
|
-
this.config[this.now] = lang;
|
|
137
|
-
}
|
|
138
|
-
setByPath(lang, key, value);
|
|
139
|
-
return this;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* 从当前语言字典取值,词条缺失时回退到默认语言
|
|
144
|
-
* @param {String} key 词条键名
|
|
145
|
-
* @returns {*} 词条值
|
|
146
|
-
* @private
|
|
147
|
-
*/
|
|
148
|
-
_getValue(key) {
|
|
149
|
-
var now_dict = this.config[this.now];
|
|
150
|
-
var val = now_dict ? getByPath(now_dict, key) : undefined;
|
|
151
|
-
if (val === undefined && this.now !== this.config.default) {
|
|
152
|
-
var def_dict = this.config[this.config.default];
|
|
153
|
-
if (def_dict) {
|
|
154
|
-
val = getByPath(def_dict, key);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
return val;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* 按路径从对象中取值
|
|
163
|
-
* @param {Object} obj 对象
|
|
164
|
-
* @param {String} path 点号路径
|
|
165
|
-
* @returns {*} 路径对应的值
|
|
166
|
-
*/
|
|
167
|
-
function getByPath(obj, path) {
|
|
168
|
-
var keys = String(path).split('.');
|
|
169
|
-
var val = obj;
|
|
170
|
-
for (var i = 0; i < keys.length; i++) {
|
|
171
|
-
if (val === null || val === undefined || typeof val !== 'object') {
|
|
172
|
-
return undefined;
|
|
173
|
-
}
|
|
174
|
-
val = val[keys[i]];
|
|
175
|
-
}
|
|
176
|
-
return val;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* 按路径向对象写入值
|
|
181
|
-
* @param {Object} obj 对象
|
|
182
|
-
* @param {String} path 点号路径
|
|
183
|
-
* @param {*} value 写入的值
|
|
184
|
-
* @returns {void}
|
|
185
|
-
*/
|
|
186
|
-
function setByPath(obj, path, value) {
|
|
187
|
-
var keys = String(path).split('.');
|
|
188
|
-
var cur = obj;
|
|
189
|
-
for (var i = 0; i < keys.length - 1; i++) {
|
|
190
|
-
if (typeof cur[keys[i]] !== 'object' || cur[keys[i]] === null) {
|
|
191
|
-
cur[keys[i]] = {};
|
|
192
|
-
}
|
|
193
|
-
cur = cur[keys[i]];
|
|
194
|
-
}
|
|
195
|
-
cur[keys[keys.length - 1]] = value;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
module.exports = {
|
|
199
|
-
BaseLang
|
|
200
|
-
};
|