schema-dsl 1.1.3 → 1.1.5
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/CHANGELOG.md +86 -1271
- package/README.md +366 -2
- package/STATUS.md +69 -3
- package/changelogs/v1.0.0.md +328 -0
- package/changelogs/v1.0.9.md +367 -0
- package/changelogs/v1.1.0.md +389 -0
- package/changelogs/v1.1.1.md +308 -0
- package/changelogs/v1.1.2.md +183 -0
- package/changelogs/v1.1.3.md +161 -0
- package/changelogs/v1.1.4.md +432 -0
- package/changelogs/v1.1.5.md +493 -0
- package/docs/dsl-syntax.md +14 -3
- package/docs/error-handling.md +247 -2
- package/docs/optional-marker-guide.md +321 -0
- package/docs/runtime-locale-support.md +470 -0
- package/index.d.ts +203 -12
- package/index.js +6 -3
- package/index.mjs +2 -2
- package/lib/core/DslBuilder.js +11 -2
- package/lib/core/Locale.js +28 -22
- package/lib/core/MessageTemplate.js +30 -21
- package/lib/core/Validator.js +6 -1
- package/lib/errors/I18nError.js +59 -15
- package/lib/locales/en-US.js +13 -3
- package/lib/locales/zh-CN.js +14 -3
- package/lib/validators/CustomKeywords.js +10 -2
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -1,9 +1,64 @@
|
|
|
1
|
-
// Type definitions for schema-dsl v1.1.
|
|
1
|
+
// Type definitions for schema-dsl v1.1.5
|
|
2
2
|
// Project: https://github.com/vextjs/schema-dsl
|
|
3
3
|
// Definitions by: schema-dsl Team
|
|
4
4
|
|
|
5
5
|
// ========== 核心类型 ==========
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* 错误消息配置(字符串或对象)
|
|
9
|
+
*
|
|
10
|
+
* @description v1.1.5 新增:支持对象格式配置错误代码和消息
|
|
11
|
+
*
|
|
12
|
+
* @example 字符串格式(向后兼容)
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const messages = {
|
|
15
|
+
* 'user.notFound': '用户不存在'
|
|
16
|
+
* };
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @example 对象格式(v1.1.5 新增)
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const messages = {
|
|
22
|
+
* 'account.notFound': {
|
|
23
|
+
* code: 'ACCOUNT_NOT_FOUND',
|
|
24
|
+
* message: '账户不存在'
|
|
25
|
+
* }
|
|
26
|
+
* };
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @since v1.1.5
|
|
30
|
+
*/
|
|
31
|
+
export type ErrorMessageConfig =
|
|
32
|
+
| string // 向后兼容:'账户不存在'
|
|
33
|
+
| { // 新格式:{ code, message }
|
|
34
|
+
/** 错误代码(可选,默认使用 key) */
|
|
35
|
+
code?: string;
|
|
36
|
+
/** 错误消息(必需) */
|
|
37
|
+
message: string;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 语言包定义
|
|
42
|
+
*
|
|
43
|
+
* @description 语言包对象,key 为错误代码,value 为错误消息配置
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* const zhCN: LocaleMessages = {
|
|
48
|
+
* 'user.notFound': '用户不存在',
|
|
49
|
+
* 'account.notFound': {
|
|
50
|
+
* code: 'ACCOUNT_NOT_FOUND',
|
|
51
|
+
* message: '账户不存在'
|
|
52
|
+
* }
|
|
53
|
+
* };
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* @since v1.1.5
|
|
57
|
+
*/
|
|
58
|
+
export interface LocaleMessages {
|
|
59
|
+
[key: string]: ErrorMessageConfig;
|
|
60
|
+
}
|
|
61
|
+
|
|
7
62
|
/**
|
|
8
63
|
* JSON Schema 对象
|
|
9
64
|
*
|
|
@@ -285,13 +340,24 @@ export class DslBuilder {
|
|
|
285
340
|
|
|
286
341
|
/**
|
|
287
342
|
* 构造函数
|
|
288
|
-
* @param dslString - DSL字符串(如 'email!', 'string:3-32!', 'types:string|number')
|
|
343
|
+
* @param dslString - DSL字符串(如 'email!', 'string:3-32!', 'string?', 'types:string|number')
|
|
289
344
|
*
|
|
290
345
|
* @example 基础类型
|
|
291
346
|
* ```typescript
|
|
292
|
-
* const builder = new DslBuilder('email!');
|
|
293
|
-
* const builder2 = new DslBuilder('string:3-32');
|
|
294
|
-
* const builder3 = new DslBuilder('
|
|
347
|
+
* const builder = new DslBuilder('email!'); // 必填邮箱
|
|
348
|
+
* const builder2 = new DslBuilder('string:3-32'); // 可选字符串(默认)
|
|
349
|
+
* const builder3 = new DslBuilder('string?'); // 显式可选字符串
|
|
350
|
+
* const builder4 = new DslBuilder('email?'); // 显式可选邮箱
|
|
351
|
+
* const builder5 = new DslBuilder('types:string|number'); // 联合类型
|
|
352
|
+
* ```
|
|
353
|
+
*
|
|
354
|
+
* @example 必填与可选标记
|
|
355
|
+
* ```typescript
|
|
356
|
+
* new DslBuilder('string!') // 必填字符串
|
|
357
|
+
* new DslBuilder('string') // 可选字符串(默认)
|
|
358
|
+
* new DslBuilder('string?') // 显式可选字符串
|
|
359
|
+
* new DslBuilder('email?') // 可选邮箱
|
|
360
|
+
* new DslBuilder('string:3-32?') // 可选字符串,长度3-32
|
|
295
361
|
* ```
|
|
296
362
|
*
|
|
297
363
|
* @example 数字类型比较运算符 (v1.1.2+)
|
|
@@ -317,6 +383,7 @@ export class DslBuilder {
|
|
|
317
383
|
* // 配合必填标记
|
|
318
384
|
* new DslBuilder('number:>=18!') // 必填且 >= 18
|
|
319
385
|
* new DslBuilder('number:>0!') // 必填且 > 0
|
|
386
|
+
* new DslBuilder('number:>0?') // 可选且 > 0(当有值时)
|
|
320
387
|
* ```
|
|
321
388
|
*/
|
|
322
389
|
constructor(dslString: string);
|
|
@@ -1254,12 +1321,26 @@ export namespace dsl {
|
|
|
1254
1321
|
* @param code - 错误代码(多语言 key)
|
|
1255
1322
|
* @param params - 错误参数
|
|
1256
1323
|
* @param statusCode - HTTP 状态码
|
|
1324
|
+
* @param locale - 语言环境(可选,不传则使用全局语言)
|
|
1257
1325
|
* @returns 错误实例
|
|
1326
|
+
*
|
|
1327
|
+
* @example 全局语言
|
|
1328
|
+
* ```typescript
|
|
1329
|
+
* Locale.setLocale('zh-CN');
|
|
1330
|
+
* const error = dsl.error.create('account.notFound');
|
|
1331
|
+
* ```
|
|
1332
|
+
*
|
|
1333
|
+
* @example 运行时指定语言
|
|
1334
|
+
* ```typescript
|
|
1335
|
+
* const error1 = dsl.error.create('account.notFound', {}, 404, 'zh-CN');
|
|
1336
|
+
* const error2 = dsl.error.create('account.notFound', {}, 404, 'en-US');
|
|
1337
|
+
* ```
|
|
1258
1338
|
*/
|
|
1259
1339
|
create(
|
|
1260
1340
|
code: string,
|
|
1261
1341
|
params?: Record<string, any>,
|
|
1262
|
-
statusCode?: number
|
|
1342
|
+
statusCode?: number,
|
|
1343
|
+
locale?: string
|
|
1263
1344
|
): I18nError;
|
|
1264
1345
|
|
|
1265
1346
|
/**
|
|
@@ -1267,12 +1348,25 @@ export namespace dsl {
|
|
|
1267
1348
|
* @param code - 错误代码(多语言 key)
|
|
1268
1349
|
* @param params - 错误参数
|
|
1269
1350
|
* @param statusCode - HTTP 状态码
|
|
1351
|
+
* @param locale - 语言环境(可选,不传则使用全局语言)
|
|
1270
1352
|
* @throws I18nError
|
|
1353
|
+
*
|
|
1354
|
+
* @example 全局语言
|
|
1355
|
+
* ```typescript
|
|
1356
|
+
* Locale.setLocale('zh-CN');
|
|
1357
|
+
* dsl.error.throw('account.notFound');
|
|
1358
|
+
* ```
|
|
1359
|
+
*
|
|
1360
|
+
* @example 运行时指定语言
|
|
1361
|
+
* ```typescript
|
|
1362
|
+
* dsl.error.throw('account.notFound', {}, 404, 'en-US');
|
|
1363
|
+
* ```
|
|
1271
1364
|
*/
|
|
1272
1365
|
throw(
|
|
1273
1366
|
code: string,
|
|
1274
1367
|
params?: Record<string, any>,
|
|
1275
|
-
statusCode?: number
|
|
1368
|
+
statusCode?: number,
|
|
1369
|
+
locale?: string
|
|
1276
1370
|
): never;
|
|
1277
1371
|
|
|
1278
1372
|
/**
|
|
@@ -1281,13 +1375,26 @@ export namespace dsl {
|
|
|
1281
1375
|
* @param code - 错误代码(多语言 key)
|
|
1282
1376
|
* @param params - 错误参数
|
|
1283
1377
|
* @param statusCode - HTTP 状态码
|
|
1378
|
+
* @param locale - 语言环境(可选,不传则使用全局语言)
|
|
1284
1379
|
* @throws I18nError 条件为 false 时抛出
|
|
1380
|
+
*
|
|
1381
|
+
* @example 全局语言
|
|
1382
|
+
* ```typescript
|
|
1383
|
+
* Locale.setLocale('zh-CN');
|
|
1384
|
+
* dsl.error.assert(account, 'account.notFound');
|
|
1385
|
+
* ```
|
|
1386
|
+
*
|
|
1387
|
+
* @example 运行时指定语言
|
|
1388
|
+
* ```typescript
|
|
1389
|
+
* dsl.error.assert(account, 'account.notFound', {}, 404, 'en-US');
|
|
1390
|
+
* ```
|
|
1285
1391
|
*/
|
|
1286
1392
|
assert(
|
|
1287
1393
|
condition: any,
|
|
1288
1394
|
code: string,
|
|
1289
1395
|
params?: Record<string, any>,
|
|
1290
|
-
statusCode?: number
|
|
1396
|
+
statusCode?: number,
|
|
1397
|
+
locale?: string
|
|
1291
1398
|
): asserts condition;
|
|
1292
1399
|
};
|
|
1293
1400
|
}
|
|
@@ -1648,7 +1755,10 @@ export class I18nError extends Error {
|
|
|
1648
1755
|
/** 错误消息(已翻译) */
|
|
1649
1756
|
message: string;
|
|
1650
1757
|
|
|
1651
|
-
/**
|
|
1758
|
+
/** 原始 key(v1.1.5 新增) */
|
|
1759
|
+
originalKey: string;
|
|
1760
|
+
|
|
1761
|
+
/** 错误代码(从对象提取或使用 key) */
|
|
1652
1762
|
code: string;
|
|
1653
1763
|
|
|
1654
1764
|
/** 错误参数(用于插值) */
|
|
@@ -1679,12 +1789,30 @@ export class I18nError extends Error {
|
|
|
1679
1789
|
* @param code - 错误代码
|
|
1680
1790
|
* @param params - 错误参数
|
|
1681
1791
|
* @param statusCode - HTTP 状态码
|
|
1792
|
+
* @param locale - 语言环境(可选,不传则使用全局语言)
|
|
1682
1793
|
* @returns 错误实例
|
|
1794
|
+
*
|
|
1795
|
+
* @example 全局语言
|
|
1796
|
+
* ```typescript
|
|
1797
|
+
* Locale.setLocale('zh-CN');
|
|
1798
|
+
* const error = I18nError.create('account.notFound');
|
|
1799
|
+
* // message: "账户不存在"
|
|
1800
|
+
* ```
|
|
1801
|
+
*
|
|
1802
|
+
* @example 运行时指定语言
|
|
1803
|
+
* ```typescript
|
|
1804
|
+
* const error1 = I18nError.create('account.notFound', {}, 404, 'zh-CN');
|
|
1805
|
+
* // message: "账户不存在"
|
|
1806
|
+
*
|
|
1807
|
+
* const error2 = I18nError.create('account.notFound', {}, 404, 'en-US');
|
|
1808
|
+
* // message: "Account not found"
|
|
1809
|
+
* ```
|
|
1683
1810
|
*/
|
|
1684
1811
|
static create(
|
|
1685
1812
|
code: string,
|
|
1686
1813
|
params?: Record<string, any>,
|
|
1687
|
-
statusCode?: number
|
|
1814
|
+
statusCode?: number,
|
|
1815
|
+
locale?: string
|
|
1688
1816
|
): I18nError;
|
|
1689
1817
|
|
|
1690
1818
|
/**
|
|
@@ -1692,12 +1820,25 @@ export class I18nError extends Error {
|
|
|
1692
1820
|
* @param code - 错误代码
|
|
1693
1821
|
* @param params - 错误参数
|
|
1694
1822
|
* @param statusCode - HTTP 状态码
|
|
1823
|
+
* @param locale - 语言环境(可选,不传则使用全局语言)
|
|
1695
1824
|
* @throws I18nError
|
|
1825
|
+
*
|
|
1826
|
+
* @example 全局语言
|
|
1827
|
+
* ```typescript
|
|
1828
|
+
* Locale.setLocale('zh-CN');
|
|
1829
|
+
* I18nError.throw('account.notFound');
|
|
1830
|
+
* ```
|
|
1831
|
+
*
|
|
1832
|
+
* @example 运行时指定语言
|
|
1833
|
+
* ```typescript
|
|
1834
|
+
* I18nError.throw('account.notFound', {}, 404, 'en-US');
|
|
1835
|
+
* ```
|
|
1696
1836
|
*/
|
|
1697
1837
|
static throw(
|
|
1698
1838
|
code: string,
|
|
1699
1839
|
params?: Record<string, any>,
|
|
1700
|
-
statusCode?: number
|
|
1840
|
+
statusCode?: number,
|
|
1841
|
+
locale?: string
|
|
1701
1842
|
): never;
|
|
1702
1843
|
|
|
1703
1844
|
/**
|
|
@@ -1706,13 +1847,46 @@ export class I18nError extends Error {
|
|
|
1706
1847
|
* @param code - 错误代码
|
|
1707
1848
|
* @param params - 错误参数
|
|
1708
1849
|
* @param statusCode - HTTP 状态码
|
|
1850
|
+
* @param locale - 语言环境(可选,不传则使用全局语言)
|
|
1709
1851
|
* @throws I18nError 条件为 false 时抛出
|
|
1852
|
+
*
|
|
1853
|
+
* @example 全局语言
|
|
1854
|
+
* ```typescript
|
|
1855
|
+
* Locale.setLocale('zh-CN');
|
|
1856
|
+
* I18nError.assert(account, 'account.notFound');
|
|
1857
|
+
* ```
|
|
1858
|
+
*
|
|
1859
|
+
* @example 运行时指定语言
|
|
1860
|
+
* ```typescript
|
|
1861
|
+
* I18nError.assert(account, 'account.notFound', {}, 404, 'en-US');
|
|
1862
|
+
* ```
|
|
1863
|
+
*/
|
|
1864
|
+
/**
|
|
1865
|
+
* 断言方法 - 条件不满足时抛错
|
|
1866
|
+
* @param condition - 条件表达式
|
|
1867
|
+
* @param code - 错误代码
|
|
1868
|
+
* @param params - 错误参数
|
|
1869
|
+
* @param statusCode - HTTP 状态码
|
|
1870
|
+
* @param locale - 语言环境(可选,不传则使用全局语言)
|
|
1871
|
+
* @throws I18nError 条件为 false 时抛出
|
|
1872
|
+
*
|
|
1873
|
+
* @example 全局语言
|
|
1874
|
+
* ```typescript
|
|
1875
|
+
* Locale.setLocale('zh-CN');
|
|
1876
|
+
* I18nError.assert(account, 'account.notFound');
|
|
1877
|
+
* ```
|
|
1878
|
+
*
|
|
1879
|
+
* @example 运行时指定语言
|
|
1880
|
+
* ```typescript
|
|
1881
|
+
* I18nError.assert(account, 'account.notFound', {}, 404, 'en-US');
|
|
1882
|
+
* ```
|
|
1710
1883
|
*/
|
|
1711
1884
|
static assert(
|
|
1712
1885
|
condition: any,
|
|
1713
1886
|
code: string,
|
|
1714
1887
|
params?: Record<string, any>,
|
|
1715
|
-
statusCode?: number
|
|
1888
|
+
statusCode?: number,
|
|
1889
|
+
locale?: string
|
|
1716
1890
|
): asserts condition;
|
|
1717
1891
|
|
|
1718
1892
|
/**
|
|
@@ -1725,9 +1899,26 @@ export class I18nError extends Error {
|
|
|
1725
1899
|
/**
|
|
1726
1900
|
* 转为 JSON 格式(用于 API 响应)
|
|
1727
1901
|
* @returns JSON 对象
|
|
1902
|
+
*
|
|
1903
|
+
* @example
|
|
1904
|
+
* ```typescript
|
|
1905
|
+
* const json = error.toJSON();
|
|
1906
|
+
* // {
|
|
1907
|
+
* // error: 'I18nError',
|
|
1908
|
+
* // originalKey: 'account.notFound', // v1.1.5 新增
|
|
1909
|
+
* // code: 'ACCOUNT_NOT_FOUND',
|
|
1910
|
+
* // message: '账户不存在',
|
|
1911
|
+
* // params: {},
|
|
1912
|
+
* // statusCode: 400,
|
|
1913
|
+
* // locale: 'zh-CN'
|
|
1914
|
+
* // }
|
|
1915
|
+
* ```
|
|
1916
|
+
*
|
|
1917
|
+
* @since v1.1.5 - 新增 originalKey 字段
|
|
1728
1918
|
*/
|
|
1729
1919
|
toJSON(): {
|
|
1730
1920
|
error: string;
|
|
1921
|
+
originalKey: string; // v1.1.5 新增
|
|
1731
1922
|
code: string;
|
|
1732
1923
|
message: string;
|
|
1733
1924
|
params: Record<string, any>;
|
package/index.js
CHANGED
|
@@ -59,18 +59,20 @@ dsl.error = {
|
|
|
59
59
|
* @param {string} code - 错误代码(多语言 key)
|
|
60
60
|
* @param {Object} params - 错误参数
|
|
61
61
|
* @param {number} statusCode - HTTP 状态码
|
|
62
|
+
* @param {string} locale - 语言环境(可选,不传则使用全局语言)
|
|
62
63
|
* @returns {I18nError} 错误实例
|
|
63
64
|
*/
|
|
64
|
-
create: (code, params, statusCode) => I18nError.create(code, params, statusCode),
|
|
65
|
+
create: (code, params, statusCode, locale) => I18nError.create(code, params, statusCode, locale),
|
|
65
66
|
|
|
66
67
|
/**
|
|
67
68
|
* 抛出多语言错误
|
|
68
69
|
* @param {string} code - 错误代码(多语言 key)
|
|
69
70
|
* @param {Object} params - 错误参数
|
|
70
71
|
* @param {number} statusCode - HTTP 状态码
|
|
72
|
+
* @param {string} locale - 语言环境(可选,不传则使用全局语言)
|
|
71
73
|
* @throws {I18nError} 直接抛出错误
|
|
72
74
|
*/
|
|
73
|
-
throw: (code, params, statusCode) => I18nError.throw(code, params, statusCode),
|
|
75
|
+
throw: (code, params, statusCode, locale) => I18nError.throw(code, params, statusCode, locale),
|
|
74
76
|
|
|
75
77
|
/**
|
|
76
78
|
* 断言方法 - 条件不满足时抛错
|
|
@@ -78,8 +80,9 @@ dsl.error = {
|
|
|
78
80
|
* @param {string} code - 错误代码(多语言 key)
|
|
79
81
|
* @param {Object} params - 错误参数
|
|
80
82
|
* @param {number} statusCode - HTTP 状态码
|
|
83
|
+
* @param {string} locale - 语言环境(可选,不传则使用全局语言)
|
|
81
84
|
*/
|
|
82
|
-
assert: (condition, code, params, statusCode) => I18nError.assert(condition, code, params, statusCode)
|
|
85
|
+
assert: (condition, code, params, statusCode, locale) => I18nError.assert(condition, code, params, statusCode, locale)
|
|
83
86
|
};
|
|
84
87
|
|
|
85
88
|
/**
|
package/index.mjs
CHANGED
package/lib/core/DslBuilder.js
CHANGED
|
@@ -123,11 +123,20 @@ class DslBuilder {
|
|
|
123
123
|
processedDsl = trimmed.replace(/^array!/, 'array:') + '!';
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
// 🔴 处理必填标记 ! 和可选标记 ?
|
|
127
|
+
// 优先级:! > ?(如果同时存在,! 优先)
|
|
126
128
|
this._required = processedDsl.endsWith('!');
|
|
127
|
-
|
|
129
|
+
this._optional = processedDsl.endsWith('?') && !this._required;
|
|
130
|
+
|
|
131
|
+
let dslWithoutMarker = processedDsl;
|
|
132
|
+
if (this._required) {
|
|
133
|
+
dslWithoutMarker = processedDsl.slice(0, -1);
|
|
134
|
+
} else if (this._optional) {
|
|
135
|
+
dslWithoutMarker = processedDsl.slice(0, -1);
|
|
136
|
+
}
|
|
128
137
|
|
|
129
138
|
// 简单解析为基础Schema(避免循环依赖)
|
|
130
|
-
this._baseSchema = this._parseSimple(
|
|
139
|
+
this._baseSchema = this._parseSimple(dslWithoutMarker);
|
|
131
140
|
|
|
132
141
|
// 扩展属性
|
|
133
142
|
this._customMessages = {};
|
package/lib/core/Locale.js
CHANGED
|
@@ -62,11 +62,12 @@ class Locale {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
|
-
*
|
|
65
|
+
* 获取错误消息配置
|
|
66
66
|
* @param {string} type - 错误类型或消息字符串
|
|
67
67
|
* @param {Object} [customMessages] - 自定义消息
|
|
68
68
|
* @param {string} [locale] - 指定语言(可选,默认使用当前语言)
|
|
69
|
-
* @returns {string}
|
|
69
|
+
* @returns {Object|string} 消息配置对象 { code, message } 或字符串(向后兼容)
|
|
70
|
+
* @version 1.1.5 - 支持对象格式
|
|
70
71
|
*/
|
|
71
72
|
static getMessage(type, customMessages = {}, locale = null) {
|
|
72
73
|
// 使用指定的语言或当前全局语言
|
|
@@ -74,32 +75,37 @@ class Locale {
|
|
|
74
75
|
|
|
75
76
|
// 优先级: 自定义消息 > 全局自定义消息 > 语言包 > ErrorCodes > 原字符串
|
|
76
77
|
|
|
77
|
-
// 1.
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
// 1. 查找消息配置
|
|
79
|
+
let messageConfig = customMessages[type]
|
|
80
|
+
|| this.customMessages[type]
|
|
81
|
+
|| (this.locales[targetLocale] && this.locales[targetLocale][type]);
|
|
82
|
+
|
|
83
|
+
// 2. 如果未找到,尝试从 ErrorCodes 获取
|
|
84
|
+
if (!messageConfig) {
|
|
85
|
+
const errorInfo = getErrorInfo(type);
|
|
86
|
+
if (errorInfo.code === 'UNKNOWN_ERROR') {
|
|
87
|
+
// ✅ 向后兼容:直接返回原字符串(支持硬编码消息)
|
|
88
|
+
return type;
|
|
89
|
+
}
|
|
90
|
+
messageConfig = errorInfo.message;
|
|
80
91
|
}
|
|
81
92
|
|
|
82
|
-
//
|
|
83
|
-
if (
|
|
84
|
-
|
|
93
|
+
// 3. 规范化为对象格式(v1.1.5 新增)
|
|
94
|
+
if (typeof messageConfig === 'string') {
|
|
95
|
+
// 字符串格式 → 转换为对象格式(向后兼容)
|
|
96
|
+
return { code: type, message: messageConfig };
|
|
85
97
|
}
|
|
86
98
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
99
|
+
if (typeof messageConfig === 'object' && messageConfig !== null && messageConfig.message) {
|
|
100
|
+
// 对象格式 → 直接使用
|
|
101
|
+
return {
|
|
102
|
+
code: messageConfig.code || type, // 无 code 时使用 type 作为 code
|
|
103
|
+
message: messageConfig.message
|
|
104
|
+
};
|
|
91
105
|
}
|
|
92
106
|
|
|
93
|
-
// 4.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
// 5. 如果是未知错误,说明不是预定义的错误码
|
|
97
|
-
if (errorInfo.code === 'UNKNOWN_ERROR') {
|
|
98
|
-
// ✅ 向后兼容:直接返回原字符串(支持硬编码消息)
|
|
99
|
-
return type;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return errorInfo.message;
|
|
107
|
+
// 4. 降级处理(边界情况)
|
|
108
|
+
return { code: type, message: type };
|
|
103
109
|
}
|
|
104
110
|
|
|
105
111
|
/**
|
|
@@ -29,32 +29,41 @@ class MessageTemplate {
|
|
|
29
29
|
render(context = {}) {
|
|
30
30
|
let message = this.template;
|
|
31
31
|
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
// 定义支持的模板格式(按优先级)
|
|
33
|
+
const patterns = [
|
|
34
|
+
/\{\{#(\w+)\}\}/g, // {{#variable}} - 优先级1(现有格式)
|
|
35
|
+
/\{\{(\w+)\}\}/g, // {{variable}} - 优先级2(无井号)
|
|
36
|
+
/\{(\w+)\}/g // {variable} - 优先级3(单花括号)
|
|
37
|
+
];
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
// 按优先级依次替换
|
|
40
|
+
for (const pattern of patterns) {
|
|
41
|
+
message = message.replace(pattern, (match, key) => {
|
|
42
|
+
const value = context[key];
|
|
40
43
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
// 特殊处理
|
|
45
|
+
if (value === undefined || value === null) {
|
|
46
|
+
return match; // 保留原样
|
|
47
|
+
}
|
|
45
48
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
// 数组转字符串
|
|
50
|
+
if (Array.isArray(value)) {
|
|
51
|
+
return value.join(', ');
|
|
52
|
+
}
|
|
50
53
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
// RegExp转字符串
|
|
55
|
+
if (value instanceof RegExp) {
|
|
56
|
+
return value.toString();
|
|
57
|
+
}
|
|
55
58
|
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
// Date转字符串
|
|
60
|
+
if (value instanceof Date) {
|
|
61
|
+
return value.toISOString();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return String(value);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
58
67
|
|
|
59
68
|
return message;
|
|
60
69
|
}
|
package/lib/core/Validator.js
CHANGED
|
@@ -477,7 +477,12 @@ class Validator {
|
|
|
477
477
|
const errorMsg = failedMessage || cond.message;
|
|
478
478
|
// 支持多语言:如果 message 是 key(如 'conditional.underAge'),从语言包获取翻译
|
|
479
479
|
// 传递 locale 参数以支持动态语言切换
|
|
480
|
-
const
|
|
480
|
+
const messageConfig = Locale.getMessage(errorMsg, options.messages || {}, locale);
|
|
481
|
+
|
|
482
|
+
// v1.1.5: Locale.getMessage 返回对象 { code, message },需要提取 message
|
|
483
|
+
const errorMessage = typeof messageConfig === 'object' && messageConfig.message
|
|
484
|
+
? messageConfig.message
|
|
485
|
+
: messageConfig;
|
|
481
486
|
|
|
482
487
|
return {
|
|
483
488
|
valid: false,
|