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/lib/lang.js CHANGED
@@ -1,6 +1,7 @@
1
+ // 语言实例由 mm_lang 提供,本文件的 Lang 只是语言包加载器,故以 BaseLang 别名引用
1
2
  const {
2
- BaseLang
3
- } = require('./base_lang.js');
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
- var lang = new BaseLang(dict);
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
- * BaseLang 内置规则仅覆盖 zh_cn、en,配置中的语言代码需显式登记才能按语言取复数形式
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
- * 过滤掉无词条的语言,Lang 内置的 zh_cn、en 空字典不计入
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
- var langs = this.lang.getLangs();
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
- var dict = this.lang ? this.lang.config[lang] : null;
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.7.4",
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
- "engines": {
49
- "node": ">=12.0.0"
50
- },
51
- "files": [
52
- "index.js",
53
- "hook.js",
54
- "tpl_rule.js",
55
- "lib/",
56
- "doc/",
57
- "README.md",
58
- "README_EN.md",
59
- "package.json"
60
- ],
61
- "devDependencies": {
62
- "eslint": "^9.39.5",
63
- "eslint-plugin-jsdoc": "^65.0.0"
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
- };