schema-dsl 2.0.0 → 2.0.1

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.
Files changed (145) hide show
  1. package/CHANGELOG.md +130 -113
  2. package/LICENSE +21 -21
  3. package/README.md +628 -628
  4. package/dist/{DslBuilder-DkLaOo9Q.d.ts → DslBuilder-BIgQOAXp.d.ts} +2 -0
  5. package/dist/{DslBuilder-DQDN0ZxZ.d.cts → DslBuilder-CjHTucNQ.d.cts} +2 -0
  6. package/dist/{Validator-hFWKGxir.d.ts → Validator-CllRdrY0.d.ts} +1 -1
  7. package/dist/{Validator-C7GsVQOH.d.cts → Validator-D6okG9tr.d.cts} +1 -1
  8. package/dist/index.cjs +75 -29
  9. package/dist/index.d.cts +10 -4
  10. package/dist/index.d.ts +10 -4
  11. package/dist/index.js +75 -29
  12. package/dist/plugins/custom-format.cjs +33 -17
  13. package/dist/plugins/custom-format.d.cts +1 -1
  14. package/dist/plugins/custom-format.d.ts +1 -1
  15. package/dist/plugins/custom-format.js +33 -17
  16. package/dist/plugins/custom-type-example.cjs +33 -17
  17. package/dist/plugins/custom-type-example.d.cts +1 -1
  18. package/dist/plugins/custom-type-example.d.ts +1 -1
  19. package/dist/plugins/custom-type-example.js +33 -17
  20. package/dist/plugins/custom-validator.cjs +0 -2
  21. package/dist/plugins/custom-validator.d.cts +1 -1
  22. package/dist/plugins/custom-validator.d.ts +1 -1
  23. package/dist/plugins/custom-validator.js +0 -2
  24. package/docs/FEATURE-INDEX.md +553 -553
  25. package/docs/add-custom-locale.md +496 -496
  26. package/docs/add-keyword.md +24 -24
  27. package/docs/api-reference.md +1047 -1047
  28. package/docs/api.md +13 -13
  29. package/docs/best-practices-project-structure.md +417 -417
  30. package/docs/best-practices.md +712 -712
  31. package/docs/cache-manager.md +344 -344
  32. package/docs/compile.md +45 -45
  33. package/docs/conditional-api.md +1307 -1307
  34. package/docs/custom-extensions-guide.md +339 -339
  35. package/docs/design-philosophy.md +606 -606
  36. package/docs/doc-index.md +324 -324
  37. package/docs/dsl-syntax.md +714 -714
  38. package/docs/dynamic-locale.md +608 -608
  39. package/docs/enum.md +482 -482
  40. package/docs/error-handling.md +1975 -1975
  41. package/docs/export-guide.md +501 -501
  42. package/docs/export-limitations.md +567 -567
  43. package/docs/faq.md +596 -596
  44. package/docs/frontend-i18n-guide.md +307 -307
  45. package/docs/i18n-user-guide.md +487 -487
  46. package/docs/i18n.md +476 -476
  47. package/docs/index.md +48 -48
  48. package/docs/json-schema-basics.md +40 -40
  49. package/docs/label-vs-description.md +271 -271
  50. package/docs/markdown-exporter.md +406 -406
  51. package/docs/mongodb-exporter.md +302 -302
  52. package/docs/multi-language.md +26 -26
  53. package/docs/multi-type-support.md +322 -322
  54. package/docs/mysql-exporter.md +280 -280
  55. package/docs/number-operators.md +449 -449
  56. package/docs/optional-marker-guide.md +326 -326
  57. package/docs/performance-guide.md +49 -49
  58. package/docs/plugin-system.md +381 -381
  59. package/docs/plugin-type-registration.md +34 -34
  60. package/docs/postgresql-exporter.md +311 -311
  61. package/docs/public/favicon.svg +4 -4
  62. package/docs/quick-start.md +435 -435
  63. package/docs/runtime-locale-support.md +532 -532
  64. package/docs/schema-helper.md +345 -345
  65. package/docs/schema-utils-advanced-issues.md +23 -23
  66. package/docs/schema-utils-best-practices.md +20 -20
  67. package/docs/schema-utils-chaining.md +150 -150
  68. package/docs/schema-utils.md +524 -524
  69. package/docs/security-checklist.md +20 -20
  70. package/docs/string-extensions.md +488 -488
  71. package/docs/troubleshooting.md +486 -486
  72. package/docs/type-converter.md +310 -310
  73. package/docs/type-reference.md +242 -242
  74. package/docs/typescript-guide.md +584 -584
  75. package/docs/union-type-guide.md +157 -157
  76. package/docs/union-types.md +284 -284
  77. package/docs/validate-async.md +491 -491
  78. package/docs/validate-batch.md +49 -49
  79. package/docs/validate-dsl-object-support.md +578 -578
  80. package/docs/validate.md +506 -506
  81. package/docs/validation-guide.md +502 -502
  82. package/docs/validator.md +39 -39
  83. package/package.json +131 -131
  84. package/plugins/custom-format.cjs +8 -8
  85. package/plugins/custom-type-example.cjs +8 -8
  86. package/plugins/custom-validator.cjs +8 -8
  87. package/src/adapters/DslAdapter.ts +111 -111
  88. package/src/adapters/index.ts +1 -1
  89. package/src/config/constants.ts +83 -83
  90. package/src/config/index.ts +2 -2
  91. package/src/config/patterns.ts +77 -77
  92. package/src/core/CacheManager.ts +169 -159
  93. package/src/core/ConditionalBuilder.ts +382 -382
  94. package/src/core/ConditionalRuntime.ts +27 -27
  95. package/src/core/ConditionalValidator.ts +254 -254
  96. package/src/core/DslBuilder.ts +687 -677
  97. package/src/core/ErrorCodes.ts +38 -38
  98. package/src/core/ErrorFormatter.ts +271 -271
  99. package/src/core/JSONSchemaCore.ts +65 -65
  100. package/src/core/Locale.ts +187 -187
  101. package/src/core/MessageTemplate.ts +42 -42
  102. package/src/core/ObjectDslBuilder.ts +64 -64
  103. package/src/core/PluginManager.ts +326 -326
  104. package/src/core/StringExtensions.ts +140 -140
  105. package/src/core/TemplateEngine.ts +44 -44
  106. package/src/core/Validator.ts +448 -448
  107. package/src/errors/I18nError.ts +159 -159
  108. package/src/errors/ValidationError.ts +105 -105
  109. package/src/exporters/BaseExporter.ts +60 -60
  110. package/src/exporters/MarkdownExporter.ts +305 -305
  111. package/src/exporters/MongoDBExporter.ts +126 -126
  112. package/src/exporters/MySQLExporter.ts +156 -155
  113. package/src/exporters/PostgreSQLExporter.ts +222 -222
  114. package/src/exporters/index.ts +18 -18
  115. package/src/index.ts +651 -633
  116. package/src/locales/en-US.ts +160 -160
  117. package/src/locales/es-ES.ts +160 -160
  118. package/src/locales/fr-FR.ts +160 -160
  119. package/src/locales/index.ts +103 -103
  120. package/src/locales/ja-JP.ts +160 -160
  121. package/src/locales/types.ts +156 -156
  122. package/src/locales/zh-CN.ts +160 -160
  123. package/src/parser/ConstraintParser.ts +101 -101
  124. package/src/parser/DslParser.ts +470 -470
  125. package/src/parser/SchemaCompiler.ts +66 -66
  126. package/src/parser/TypeRegistry.ts +250 -250
  127. package/src/parser/index.ts +6 -6
  128. package/src/plugins/custom-format.ts +124 -126
  129. package/src/plugins/custom-type-example.ts +106 -108
  130. package/src/plugins/custom-validator.ts +138 -140
  131. package/src/types/conditional.ts +28 -28
  132. package/src/types/config.ts +59 -59
  133. package/src/types/dsl.ts +131 -131
  134. package/src/types/error.ts +60 -60
  135. package/src/types/index.ts +17 -17
  136. package/src/types/infer.ts +127 -127
  137. package/src/types/plugin.ts +58 -58
  138. package/src/types/safe-regex.d.ts +9 -9
  139. package/src/types/schema.ts +66 -66
  140. package/src/types/validate.ts +71 -71
  141. package/src/utils/SchemaHelper.ts +196 -196
  142. package/src/utils/SchemaUtils.ts +365 -346
  143. package/src/utils/TypeConverter.ts +215 -215
  144. package/src/utils/index.ts +10 -10
  145. package/src/validators/CustomKeywords.ts +477 -477
@@ -1,496 +1,496 @@
1
- # 添加自定义语言包指南
2
-
3
- **版本**: v2.0.0-beta.1
4
- **最后更新**: 2026-04-30
5
-
6
- ---
7
-
8
- ## 📖 概述
9
-
10
- 本指南将教你如何为 schema-dsl 添加自定义语言包或扩展现有语言。
11
-
12
- > **Node.js 要求**:`>=18.0.0`
13
- >
14
- > **目录加载(Node >=18)默认支持的语言文件格式**:`.js`(CommonJS)、`.cjs`、`.json`、`.jsonc`、`.json5`。
15
- > **推荐**:如果你的应用是 `type: module` / ESM 项目,优先使用 `.cjs`、`.json`、`.jsonc`、`.json5`。
16
-
17
- ---
18
-
19
- ## 🏗️ 多人协作:子目录拆分语言包(v1.2.3 新增)⭐
20
-
21
- > **适用场景**:多人/多模块开发,避免所有语言 key 堆在同一文件产生 Git 冲突和 code 码冲突。
22
-
23
- ### 目录结构
24
-
25
- ```bash
26
- project/
27
- ├── locales/
28
- │ ├── core/ # 公共 code 段:1000-1999(框架层维护)
29
- │ │ ├── zh-CN.cjs
30
- │ │ └── en-US.jsonc
31
- │ ├── account/ # 账户模块 code 段:10000-10999(开发者A)
32
- │ │ ├── zh-CN.cjs
33
- │ │ └── en-US.jsonc
34
- │ ├── order/ # 订单模块 code 段:20000-20999(开发者B)
35
- │ │ ├── zh-CN.json5
36
- │ │ └── en-US.json5
37
- │ └── payment/ # 支付模块 code 段:30000-30999(开发者C)
38
- │ ├── zh-CN.cjs
39
- │ └── en-US.cjs
40
- └── app.js
41
- ```
42
-
43
- ### 每个模块独立维护自己的语言文件
44
-
45
- ```javascript
46
- // locales/account/zh-CN.cjs — 开发者A 独立维护,互不干扰
47
- module.exports = {
48
- 'account.notFound': { code: 10001, message: '账户不存在' },
49
- 'account.locked': { code: 10002, message: '账户已锁定' },
50
- };
51
-
52
- // locales/order/zh-CN.json5 — 开发者B 独立维护
53
- const orderZhCN = {
54
- 'order.notFound': { code: 20001, message: '订单不存在' },
55
- 'order.notPaid': { code: 20002, message: '订单未支付' },
56
- }
57
- ```
58
-
59
- ### 应用启动:一行配置,自动递归合并
60
-
61
- ```javascript
62
- // app.js
63
- const { dsl, validate } = require('schema-dsl');
64
- const path = require('path');
65
-
66
- // 自动递归扫描 locales/ 下所有子目录,同语言文件合并为一个完整语言包
67
- dsl.config({
68
- i18n: path.join(__dirname, 'locales')
69
- });
70
- ```
71
-
72
- > - 子目录名(`account/`、`order/`)仅作为**模块组织层**,不影响最终语言 key 命名
73
- > - 加载顺序:按文件系统字母序递归扫描
74
- > - 同语言 key 出现重复时:默认打 `WARN` 日志,可开启严格模式阻断启动
75
-
76
- ### 严格模式:key 冲突时阻断启动(推荐 CI 环境)
77
-
78
- ```javascript
79
- dsl.config({
80
- i18n: path.join(__dirname, 'locales'),
81
- strict: true // 同名 key 冲突时直接抛 Error,防止静默覆盖
82
- });
83
-
84
- // 冲突示例输出:
85
- // Error: [schema-dsl] i18n key 冲突 in locale 'zh-CN'
86
- // 冲突 key: account.notFound
87
- // 来源文件: /project/locales/account/zh-CN.cjs
88
- ```
89
-
90
- ### Code 段划分建议
91
-
92
- 多人开发时建议在项目根目录维护一份 `locales/CODE-SEGMENTS.md`,约定各模块的 code 号段:
93
-
94
- | 模块 | code 范围 | 负责人 |
95
- |------|----------|--------|
96
- | core(公共) | 1000–1999 | 框架组 |
97
- | account | 10000–10999 | 开发者A |
98
- | order | 20000–20999 | 开发者B |
99
- | payment | 30000–30999 | 开发者C |
100
-
101
- > `CODE-SEGMENTS.md` / `CODE-SEGMENTS.js` 等非语言文件会被自动跳过,无需担心被误加载。
102
-
103
- ---
104
-
105
-
106
-
107
- ## 🚀 快速开始
108
-
109
- ### 推荐方式:配置语言包目录(一次性加载所有语言)⭐
110
-
111
- **正确的使用方式**:在应用启动时一次性加载所有语言包,运行时直接切换。
112
-
113
- #### 第1步:创建语言包文件
114
-
115
-
116
- #### 第1步:创建语言包文件
117
-
118
- ```bash
119
- # 项目结构
120
- my-project/
121
- ├── locales/ # 语言包目录
122
- │ ├── zh-CN.cjs # 中文(CommonJS / ESM 项目都稳定)
123
- │ ├── en-US.jsonc # 英文(带注释 / 末尾逗号)
124
- │ └── pt-BR.json5 # 葡萄牙语(JSON5 风格)
125
- └── app.js
126
- ```
127
-
128
- #### 第2步:定义语言包(`locales/pt-BR.json5`)
129
-
130
- ```javascript
131
- module.exports = {
132
- // 通用验证错误
133
- 'required': '{{#label}} é obrigatório',
134
- 'type': '{{#label}} deve ser do tipo {{#expected}}',
135
- 'min': '{{#label}} deve ter pelo menos {{#limit}} caracteres',
136
- 'max': '{{#label}} não pode exceder {{#limit}} caracteres',
137
- 'length': '{{#label}} deve ter exatamente {{#limit}} caracteres',
138
- 'pattern': '{{#label}} formato inválido',
139
- 'enum': '{{#label}} deve ser um dos seguintes valores: {{#allowed}}',
140
-
141
- // 格式验证
142
- 'format.email': '{{#label}} deve ser um e-mail válido',
143
- 'format.url': '{{#label}} deve ser uma URL válida',
144
- 'format.uuid': '{{#label}} deve ser um UUID válido',
145
- 'format.date': '{{#label}} deve ser uma data válida (YYYY-MM-DD)',
146
- 'format.datetime': '{{#label}} deve ser uma data/hora válida (ISO 8601)',
147
-
148
- // 字符串验证
149
- 'string.minLength': '{{#label}} deve ter pelo menos {{#limit}} caracteres',
150
- 'string.maxLength': '{{#label}} não pode exceder {{#limit}} caracteres',
151
- 'string.pattern': '{{#label}} formato inválido',
152
- 'string.alphanum': '{{#label}} deve conter apenas letras e números',
153
-
154
- // 数字验证
155
- 'number.base': '{{#label}} deve ser um número',
156
- 'number.min': '{{#label}} não pode ser menor que {{#limit}}',
157
- 'number.max': '{{#label}} não pode ser maior que {{#limit}}',
158
- 'number.integer': '{{#label}} deve ser um inteiro',
159
- 'number.positive': '{{#label}} deve ser um número positivo',
160
- 'number.negative': '{{#label}} deve ser um número negativo',
161
-
162
- // 布尔验证
163
- 'boolean.base': '{{#label}} deve ser um booleano',
164
-
165
- // 对象验证
166
- 'object.base': '{{#label}} deve ser um objeto',
167
-
168
- // 数组验证
169
- 'array.base': '{{#label}} deve ser um array',
170
- 'array.min': '{{#label}} deve ter pelo menos {{#limit}} itens',
171
- 'array.max': '{{#label}} não pode ter mais de {{#limit}} itens',
172
-
173
- // 日期验证
174
- 'date.base': '{{#label}} deve ser uma data válida',
175
- 'date.min': '{{#label}} não pode ser anterior a {{#limit}}',
176
- 'date.max': '{{#label}} não pode ser posterior a {{#limit}}',
177
-
178
- // 自定义模式
179
- 'pattern.phone.cn': 'Número de telefone inválido',
180
- 'pattern.idCard.cn': 'Número de identidade inválido',
181
- 'pattern.creditCard': 'Número de cartão de crédito inválido',
182
- 'pattern.objectId': 'ObjectId inválido',
183
- 'pattern.hexColor': 'Código de cor hexadecimal inválido',
184
- 'pattern.macAddress': 'Endereço MAC inválido',
185
- 'pattern.cron': 'Expressão Cron inválida',
186
- 'pattern.slug': 'Slug deve conter apenas letras minúsculas, números e hífens'
187
- };
188
- ```
189
-
190
- #### 第3步:应用启动时一次性加载所有语言
191
-
192
- ```javascript
193
- const { dsl, validate } = require('schema-dsl');
194
- const path = require('path');
195
-
196
- // ========== 应用启动时配置(只执行一次)==========
197
- dsl.config({
198
- i18n: path.join(__dirname, 'locales') // 自动加载目录下所有语言文件
199
- });
200
-
201
- // 说明:
202
- // 1. 自动扫描 locales/ 目录下的 `.js`(CommonJS)、`.cjs`、`.json`、`.jsonc`、`.json5`
203
- // 2. 从文件名提取语言代码(如 pt-BR.cjs → pt-BR)
204
- // 3. 自动加载并注册所有语言包
205
- // 4. 用户自定义的语言包会与系统默认语言包合并,用户的优先
206
-
207
- // ========== 运行时直接切换语言(无需重新加载)==========
208
- const schema = dsl({ username: 'string:3-32!' });
209
-
210
- // 使用葡萄牙语
211
- const result1 = validate(schema, { username: 'ab' }, { locale: 'pt-BR' });
212
- // 错误消息: "username deve ter pelo menos 3 caracteres"
213
-
214
- // 使用中文
215
- const result2 = validate(schema, { username: 'ab' }, { locale: 'zh-CN' });
216
- // 错误消息: "username长度不能少于3个字符"
217
-
218
- // 使用英文
219
- const result3 = validate(schema, { username: 'ab' }, { locale: 'en-US' });
220
- // 错误消息: "username length must be at least 3"
221
- ```
222
-
223
- #### 语言包合并策略
224
-
225
- ```javascript
226
- // 系统内置的 zh-CN 语言包
227
- const systemZhCN = {
228
- 'required': '{{#label}}是必填项',
229
- 'string.minLength': '{{#label}}长度不能少于{{#limit}}个字符'
230
- };
231
-
232
- // 用户自定义的 locales/zh-CN.cjs
233
- const userZhCN = {
234
- 'required': '{{#label}}必须填写', // 覆盖系统默认
235
- 'custom.myError': '自定义错误' // 新增自定义消息
236
- };
237
-
238
- // 最终合并结果(深度合并)
239
- const finalZhCN = {
240
- 'required': '{{#label}}必须填写', // ✅ 用户的优先
241
- 'string.minLength': '{{#label}}长度不能少于{{#limit}}个字符', // 保留系统默认
242
- 'custom.myError': '自定义错误' // 新增自定义消息
243
- };
244
- ```
245
-
246
- ---
247
-
248
- ### 方式2:直接传入对象(适合动态配置)
249
-
250
- ```javascript
251
- const { dsl } = require('schema-dsl');
252
-
253
- // 应用启动时配置
254
- dsl.config({
255
- i18n: {
256
- 'pt-BR': require('./locales/pt-BR'),
257
- 'de-DE': require('./locales/de-DE'),
258
- 'ko-KR': require('./locales/ko-KR')
259
- }
260
- });
261
-
262
- // 运行时直接切换
263
- validate(schema, data, { locale: 'pt-BR' });
264
- validate(schema, data, { locale: 'de-DE' });
265
- ```
266
-
267
- ---
268
-
269
- ## ⚠️ 错误示例(不推荐)
270
-
271
- ### ❌ 错误:运行时单个加载语言包
272
-
273
- ```javascript
274
- const { Locale } = require('schema-dsl');
275
-
276
- // ❌ 不推荐:在每次验证前动态加载
277
- function validateUser(data, locale) {
278
- if (locale === 'pt-BR') {
279
- Locale.addLocale('pt-BR', require('./locales/pt-BR')); // 每次都加载,性能差
280
- }
281
- return validate(schema, data, { locale });
282
- }
283
- ```
284
-
285
- ```javascript
286
- // ✅ 正确:应用启动时一次性加载
287
- // app.js 启动入口
288
- dsl.config({ i18n: './locales' }); // 只加载一次
289
-
290
- // 运行时直接切换,无需重新加载
291
- function validateUser(data, locale) {
292
- return validate(schema, data, { locale }); // ✅ 直接切换,性能好
293
- }
294
- ```
295
-
296
- ### 为什么推荐"首次加载,运行时切换"?
297
-
298
- | 方式 | 加载次数 | 性能 | 内存 | 推荐度 |
299
- |------|---------|------|------|--------|
300
- | **首次加载所有** | 1次 | ⭐⭐⭐⭐⭐ 极快 | 低 | ✅ 强烈推荐 |
301
- | 运行时单个加载 | N次 | ⭐⭐ 慢 | 中 | ❌ 不推荐 |
302
-
303
- ---
304
-
305
- ## 🎯 完整示例
306
-
307
- ```javascript
308
- // ========== app.js(应用启动入口)==========
309
- const express = require('express');
310
- const { dsl, validate } = require('schema-dsl');
311
- const path = require('path');
312
-
313
- // 应用启动时一次性加载所有语言包
314
- dsl.config({
315
- i18n: path.join(__dirname, 'locales')
316
- });
317
-
318
- const app = express();
319
-
320
- // ========== routes/user.js(业务路由)==========
321
- const userSchema = dsl({
322
- username: 'string:3-32!',
323
- email: 'email!',
324
- age: 'number:18-120'
325
- });
326
-
327
- app.post('/api/users', (req, res) => {
328
- // 从请求头获取用户语言偏好
329
- const locale = req.headers['accept-language']?.split(',')[0]?.trim() || 'en-US';
330
-
331
- // 验证(直接切换语言,无需加载)
332
- const result = validate(userSchema, req.body, { locale });
333
-
334
- if (!result.valid) {
335
- return res.status(400).json({
336
- errors: result.errors // 自动使用用户偏好的语言
337
- });
338
- }
339
-
340
- // 处理请求...
341
- });
342
- ```
343
-
344
- ---
345
-
346
- ## 📋 完整的消息键列表
347
-
348
- ### 通用键
349
-
350
- | 键名 | 说明 | 示例 |
351
- |-----|------|------|
352
- | `required` | 必填字段 | `{{#label}} é obrigatório` |
353
- | `type` | 类型错误 | `{{#label}} deve ser do tipo {{#expected}}` |
354
- | `min` | 最小长度(通用) | `{{#label}} deve ter pelo menos {{#limit}} caracteres` |
355
- | `max` | 最大长度(通用) | `{{#label}} não pode exceder {{#limit}} caracteres` |
356
- | `length` | 精确长度 | `{{#label}} deve ter exatamente {{#limit}} caracteres` |
357
- | `pattern` | 模式匹配 | `{{#label}} formato inválido` |
358
- | `enum` | 枚举值 | `{{#label}} deve ser um dos seguintes: {{#allowed}}` |
359
-
360
- ### 字符串验证键
361
-
362
- | 键名 | 说明 | 可用变量 |
363
- |-----|------|---------|
364
- | `string.minLength` | 最小长度 | `{{#label}}`, `{{#limit}}` |
365
- | `string.maxLength` | 最大长度 | `{{#label}}`, `{{#limit}}` |
366
- | `string.length` | 精确长度 | `{{#label}}`, `{{#limit}}` |
367
- | `string.pattern` | 模式匹配 | `{{#label}}` |
368
- | `string.alphanum` | 字母数字 | `{{#label}}` |
369
- | `string.enum` | 枚举值 | `{{#label}}`, `{{#valids}}` |
370
-
371
- ### 数字验证键
372
-
373
- | 键名 | 说明 | 可用变量 |
374
- |-----|------|---------|
375
- | `number.base` | 类型错误 | `{{#label}}` |
376
- | `number.min` | 最小值 | `{{#label}}`, `{{#limit}}` |
377
- | `number.max` | 最大值 | `{{#label}}`, `{{#limit}}` |
378
- | `number.integer` | 整数 | `{{#label}}` |
379
- | `number.positive` | 正数 | `{{#label}}` |
380
- | `number.negative` | 负数 | `{{#label}}` |
381
- | `number.precision` | 小数精度 | `{{#label}}`, `{{#limit}}` |
382
- | `number.port` | 端口号 | `{{#label}}` |
383
-
384
- ### 格式验证键
385
-
386
- | 键名 | 说明 |
387
- |-----|------|
388
- | `format.email` | 邮箱格式 |
389
- | `format.url` | URL格式 |
390
- | `format.uuid` | UUID格式 |
391
- | `format.date` | 日期格式 |
392
- | `format.datetime` | 日期时间格式 |
393
- | `format.time` | 时间格式 |
394
- | `format.ipv4` | IPv4地址 |
395
- | `format.ipv6` | IPv6地址 |
396
- | `format.binary` | Base64编码 |
397
-
398
- ### 自定义模式键
399
-
400
- | 键名 | 说明 |
401
- |-----|------|
402
- | `pattern.phone.cn` | 中国手机号 |
403
- | `pattern.phone.us` | 美国电话号 |
404
- | `pattern.idCard.cn` | 中国身份证 |
405
- | `pattern.creditCard` | 信用卡号 |
406
- | `pattern.objectId` | MongoDB ObjectId |
407
- | `pattern.hexColor` | 十六进制颜色 |
408
- | `pattern.macAddress` | MAC地址 |
409
- | `pattern.cron` | Cron表达式 |
410
- | `pattern.slug` | URL别名 |
411
- | `pattern.username` | 用户名 |
412
- | `pattern.password.weak` | 弱密码 |
413
- | `pattern.password.medium` | 中等密码 |
414
- | `pattern.password.strong` | 强密码 |
415
- | `pattern.password.veryStrong` | 超强密码 |
416
-
417
- ---
418
-
419
- ## 🎨 模板变量
420
-
421
- 所有错误消息支持以下模板变量:
422
-
423
- | 变量 | 说明 | 使用示例 |
424
- |------|------|---------|
425
- | `{{#label}}` | 字段标签 | `{{#label}} é obrigatório` |
426
- | `{{#limit}}` | 限制值(长度/大小) | `deve ter pelo menos {{#limit}} caracteres` |
427
- | `{{#allowed}}` | 允许的值列表 | `deve ser um dos seguintes: {{#allowed}}` |
428
- | `{{#expected}}` | 期望的类型 | `deve ser do tipo {{#expected}}` |
429
- | `{{#valids}}` | 有效值列表(数组) | `deve ser: {{#valids}}` |
430
- | `{{#path}}` | 字段路径 | `Erro no campo {{#path}}` |
431
-
432
- ---
433
-
434
- ## 📚 参考内置语言包
435
-
436
- 你可以参考内置的语言包作为模板:
437
-
438
- ```javascript
439
- const { Locale } = require('schema-dsl');
440
-
441
- // 查看中文语言包
442
- const zhCN = Locale.getMessages('zh-CN');
443
- console.log(zhCN);
444
-
445
- // 查看英文语言包
446
- const enUS = Locale.getMessages('en-US');
447
- console.log(enUS);
448
- ```
449
-
450
- 或者直接查看源码:
451
- - 中文:`src/locales/zh-CN.ts`
452
- - 英文:`src/locales/en-US.ts`
453
- - 日语:`src/locales/ja-JP.ts`
454
- - 西班牙语:`src/locales/es-ES.ts`
455
- - 法语:`src/locales/fr-FR.ts`
456
-
457
- ---
458
-
459
- ## ✅ 最佳实践
460
-
461
- 1. **完整性**:确保翻译所有常用的错误消息键
462
- 2. **一致性**:保持错误消息风格统一
463
- 3. **模板变量**:正确使用 `{{#label}}`、`{{#limit}}` 等变量
464
- 4. **测试**:添加语言包后进行测试,确保所有消息正确显示
465
- 5. **文档**:为自定义语言包编写使用说明
466
-
467
- ---
468
-
469
- ## 🤝 贡献语言包
470
-
471
- 如果你为 schema-dsl 添加了新语言包,欢迎提交 Pull Request:
472
-
473
- 1. Fork 项目
474
- 2. 在 `src/locales/` 目录创建新语言文件(如 `pt-BR.ts`)
475
- 3. 完整翻译所有消息键
476
- 4. 在 `src/locales/index.ts` 中注册新语言
477
- 5. 添加测试用例(在 `test/unit/locales/` 目录)
478
- 6. 提交 Pull Request
479
-
480
- ---
481
-
482
- ## 📞 支持
483
-
484
- 如果你在添加语言包时遇到问题:
485
-
486
- - 查看 [多语言配置指南](./i18n.md)
487
- - 查看 [动态多语言配置指南](./dynamic-locale.md)
488
- - 提交 Issue: https://github.com/vextjs/schema-dsl/issues
489
-
490
- ---
491
-
492
- ## 对应示例文件
493
-
494
- **示例入口**: [add-custom-locale.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/add-custom-locale.ts)
495
- **说明**: 覆盖 `Locale.addLocale()` 注册新语言、读取消息文本,以及在自定义 locale 下执行验证的最小工作流。
496
-
1
+ # 添加自定义语言包指南
2
+
3
+ **版本**: v2.0.0-beta.1
4
+ **最后更新**: 2026-04-30
5
+
6
+ ---
7
+
8
+ ## 📖 概述
9
+
10
+ 本指南将教你如何为 schema-dsl 添加自定义语言包或扩展现有语言。
11
+
12
+ > **Node.js 要求**:`>=18.0.0`
13
+ >
14
+ > **目录加载(Node >=18)默认支持的语言文件格式**:`.js`(CommonJS)、`.cjs`、`.json`、`.jsonc`、`.json5`。
15
+ > **推荐**:如果你的应用是 `type: module` / ESM 项目,优先使用 `.cjs`、`.json`、`.jsonc`、`.json5`。
16
+
17
+ ---
18
+
19
+ ## 🏗️ 多人协作:子目录拆分语言包(v1.2.3 新增)⭐
20
+
21
+ > **适用场景**:多人/多模块开发,避免所有语言 key 堆在同一文件产生 Git 冲突和 code 码冲突。
22
+
23
+ ### 目录结构
24
+
25
+ ```bash
26
+ project/
27
+ ├── locales/
28
+ │ ├── core/ # 公共 code 段:1000-1999(框架层维护)
29
+ │ │ ├── zh-CN.cjs
30
+ │ │ └── en-US.jsonc
31
+ │ ├── account/ # 账户模块 code 段:10000-10999(开发者A)
32
+ │ │ ├── zh-CN.cjs
33
+ │ │ └── en-US.jsonc
34
+ │ ├── order/ # 订单模块 code 段:20000-20999(开发者B)
35
+ │ │ ├── zh-CN.json5
36
+ │ │ └── en-US.json5
37
+ │ └── payment/ # 支付模块 code 段:30000-30999(开发者C)
38
+ │ ├── zh-CN.cjs
39
+ │ └── en-US.cjs
40
+ └── app.js
41
+ ```
42
+
43
+ ### 每个模块独立维护自己的语言文件
44
+
45
+ ```javascript
46
+ // locales/account/zh-CN.cjs — 开发者A 独立维护,互不干扰
47
+ module.exports = {
48
+ 'account.notFound': { code: 10001, message: '账户不存在' },
49
+ 'account.locked': { code: 10002, message: '账户已锁定' },
50
+ };
51
+
52
+ // locales/order/zh-CN.json5 — 开发者B 独立维护
53
+ const orderZhCN = {
54
+ 'order.notFound': { code: 20001, message: '订单不存在' },
55
+ 'order.notPaid': { code: 20002, message: '订单未支付' },
56
+ }
57
+ ```
58
+
59
+ ### 应用启动:一行配置,自动递归合并
60
+
61
+ ```javascript
62
+ // app.js
63
+ const { dsl, validate } = require('schema-dsl');
64
+ const path = require('path');
65
+
66
+ // 自动递归扫描 locales/ 下所有子目录,同语言文件合并为一个完整语言包
67
+ dsl.config({
68
+ i18n: path.join(__dirname, 'locales')
69
+ });
70
+ ```
71
+
72
+ > - 子目录名(`account/`、`order/`)仅作为**模块组织层**,不影响最终语言 key 命名
73
+ > - 加载顺序:按文件系统字母序递归扫描
74
+ > - 同语言 key 出现重复时:默认打 `WARN` 日志,可开启严格模式阻断启动
75
+
76
+ ### 严格模式:key 冲突时阻断启动(推荐 CI 环境)
77
+
78
+ ```javascript
79
+ dsl.config({
80
+ i18n: path.join(__dirname, 'locales'),
81
+ strict: true // 同名 key 冲突时直接抛 Error,防止静默覆盖
82
+ });
83
+
84
+ // 冲突示例输出:
85
+ // Error: [schema-dsl] i18n key 冲突 in locale 'zh-CN'
86
+ // 冲突 key: account.notFound
87
+ // 来源文件: /project/locales/account/zh-CN.cjs
88
+ ```
89
+
90
+ ### Code 段划分建议
91
+
92
+ 多人开发时建议在项目根目录维护一份 `locales/CODE-SEGMENTS.md`,约定各模块的 code 号段:
93
+
94
+ | 模块 | code 范围 | 负责人 |
95
+ |------|----------|--------|
96
+ | core(公共) | 1000–1999 | 框架组 |
97
+ | account | 10000–10999 | 开发者A |
98
+ | order | 20000–20999 | 开发者B |
99
+ | payment | 30000–30999 | 开发者C |
100
+
101
+ > `CODE-SEGMENTS.md` / `CODE-SEGMENTS.js` 等非语言文件会被自动跳过,无需担心被误加载。
102
+
103
+ ---
104
+
105
+
106
+
107
+ ## 🚀 快速开始
108
+
109
+ ### 推荐方式:配置语言包目录(一次性加载所有语言)⭐
110
+
111
+ **正确的使用方式**:在应用启动时一次性加载所有语言包,运行时直接切换。
112
+
113
+ #### 第1步:创建语言包文件
114
+
115
+
116
+ #### 第1步:创建语言包文件
117
+
118
+ ```bash
119
+ # 项目结构
120
+ my-project/
121
+ ├── locales/ # 语言包目录
122
+ │ ├── zh-CN.cjs # 中文(CommonJS / ESM 项目都稳定)
123
+ │ ├── en-US.jsonc # 英文(带注释 / 末尾逗号)
124
+ │ └── pt-BR.json5 # 葡萄牙语(JSON5 风格)
125
+ └── app.js
126
+ ```
127
+
128
+ #### 第2步:定义语言包(`locales/pt-BR.json5`)
129
+
130
+ ```javascript
131
+ module.exports = {
132
+ // 通用验证错误
133
+ 'required': '{{#label}} é obrigatório',
134
+ 'type': '{{#label}} deve ser do tipo {{#expected}}',
135
+ 'min': '{{#label}} deve ter pelo menos {{#limit}} caracteres',
136
+ 'max': '{{#label}} não pode exceder {{#limit}} caracteres',
137
+ 'length': '{{#label}} deve ter exatamente {{#limit}} caracteres',
138
+ 'pattern': '{{#label}} formato inválido',
139
+ 'enum': '{{#label}} deve ser um dos seguintes valores: {{#allowed}}',
140
+
141
+ // 格式验证
142
+ 'format.email': '{{#label}} deve ser um e-mail válido',
143
+ 'format.url': '{{#label}} deve ser uma URL válida',
144
+ 'format.uuid': '{{#label}} deve ser um UUID válido',
145
+ 'format.date': '{{#label}} deve ser uma data válida (YYYY-MM-DD)',
146
+ 'format.datetime': '{{#label}} deve ser uma data/hora válida (ISO 8601)',
147
+
148
+ // 字符串验证
149
+ 'string.minLength': '{{#label}} deve ter pelo menos {{#limit}} caracteres',
150
+ 'string.maxLength': '{{#label}} não pode exceder {{#limit}} caracteres',
151
+ 'string.pattern': '{{#label}} formato inválido',
152
+ 'string.alphanum': '{{#label}} deve conter apenas letras e números',
153
+
154
+ // 数字验证
155
+ 'number.base': '{{#label}} deve ser um número',
156
+ 'number.min': '{{#label}} não pode ser menor que {{#limit}}',
157
+ 'number.max': '{{#label}} não pode ser maior que {{#limit}}',
158
+ 'number.integer': '{{#label}} deve ser um inteiro',
159
+ 'number.positive': '{{#label}} deve ser um número positivo',
160
+ 'number.negative': '{{#label}} deve ser um número negativo',
161
+
162
+ // 布尔验证
163
+ 'boolean.base': '{{#label}} deve ser um booleano',
164
+
165
+ // 对象验证
166
+ 'object.base': '{{#label}} deve ser um objeto',
167
+
168
+ // 数组验证
169
+ 'array.base': '{{#label}} deve ser um array',
170
+ 'array.min': '{{#label}} deve ter pelo menos {{#limit}} itens',
171
+ 'array.max': '{{#label}} não pode ter mais de {{#limit}} itens',
172
+
173
+ // 日期验证
174
+ 'date.base': '{{#label}} deve ser uma data válida',
175
+ 'date.min': '{{#label}} não pode ser anterior a {{#limit}}',
176
+ 'date.max': '{{#label}} não pode ser posterior a {{#limit}}',
177
+
178
+ // 自定义模式
179
+ 'pattern.phone.cn': 'Número de telefone inválido',
180
+ 'pattern.idCard.cn': 'Número de identidade inválido',
181
+ 'pattern.creditCard': 'Número de cartão de crédito inválido',
182
+ 'pattern.objectId': 'ObjectId inválido',
183
+ 'pattern.hexColor': 'Código de cor hexadecimal inválido',
184
+ 'pattern.macAddress': 'Endereço MAC inválido',
185
+ 'pattern.cron': 'Expressão Cron inválida',
186
+ 'pattern.slug': 'Slug deve conter apenas letras minúsculas, números e hífens'
187
+ };
188
+ ```
189
+
190
+ #### 第3步:应用启动时一次性加载所有语言
191
+
192
+ ```javascript
193
+ const { dsl, validate } = require('schema-dsl');
194
+ const path = require('path');
195
+
196
+ // ========== 应用启动时配置(只执行一次)==========
197
+ dsl.config({
198
+ i18n: path.join(__dirname, 'locales') // 自动加载目录下所有语言文件
199
+ });
200
+
201
+ // 说明:
202
+ // 1. 自动扫描 locales/ 目录下的 `.js`(CommonJS)、`.cjs`、`.json`、`.jsonc`、`.json5`
203
+ // 2. 从文件名提取语言代码(如 pt-BR.cjs → pt-BR)
204
+ // 3. 自动加载并注册所有语言包
205
+ // 4. 用户自定义的语言包会与系统默认语言包合并,用户的优先
206
+
207
+ // ========== 运行时直接切换语言(无需重新加载)==========
208
+ const schema = dsl({ username: 'string:3-32!' });
209
+
210
+ // 使用葡萄牙语
211
+ const result1 = validate(schema, { username: 'ab' }, { locale: 'pt-BR' });
212
+ // 错误消息: "username deve ter pelo menos 3 caracteres"
213
+
214
+ // 使用中文
215
+ const result2 = validate(schema, { username: 'ab' }, { locale: 'zh-CN' });
216
+ // 错误消息: "username长度不能少于3个字符"
217
+
218
+ // 使用英文
219
+ const result3 = validate(schema, { username: 'ab' }, { locale: 'en-US' });
220
+ // 错误消息: "username length must be at least 3"
221
+ ```
222
+
223
+ #### 语言包合并策略
224
+
225
+ ```javascript
226
+ // 系统内置的 zh-CN 语言包
227
+ const systemZhCN = {
228
+ 'required': '{{#label}}是必填项',
229
+ 'string.minLength': '{{#label}}长度不能少于{{#limit}}个字符'
230
+ };
231
+
232
+ // 用户自定义的 locales/zh-CN.cjs
233
+ const userZhCN = {
234
+ 'required': '{{#label}}必须填写', // 覆盖系统默认
235
+ 'custom.myError': '自定义错误' // 新增自定义消息
236
+ };
237
+
238
+ // 最终合并结果(深度合并)
239
+ const finalZhCN = {
240
+ 'required': '{{#label}}必须填写', // ✅ 用户的优先
241
+ 'string.minLength': '{{#label}}长度不能少于{{#limit}}个字符', // 保留系统默认
242
+ 'custom.myError': '自定义错误' // 新增自定义消息
243
+ };
244
+ ```
245
+
246
+ ---
247
+
248
+ ### 方式2:直接传入对象(适合动态配置)
249
+
250
+ ```javascript
251
+ const { dsl } = require('schema-dsl');
252
+
253
+ // 应用启动时配置
254
+ dsl.config({
255
+ i18n: {
256
+ 'pt-BR': require('./locales/pt-BR'),
257
+ 'de-DE': require('./locales/de-DE'),
258
+ 'ko-KR': require('./locales/ko-KR')
259
+ }
260
+ });
261
+
262
+ // 运行时直接切换
263
+ validate(schema, data, { locale: 'pt-BR' });
264
+ validate(schema, data, { locale: 'de-DE' });
265
+ ```
266
+
267
+ ---
268
+
269
+ ## ⚠️ 错误示例(不推荐)
270
+
271
+ ### ❌ 错误:运行时单个加载语言包
272
+
273
+ ```javascript
274
+ const { Locale } = require('schema-dsl');
275
+
276
+ // ❌ 不推荐:在每次验证前动态加载
277
+ function validateUser(data, locale) {
278
+ if (locale === 'pt-BR') {
279
+ Locale.addLocale('pt-BR', require('./locales/pt-BR')); // 每次都加载,性能差
280
+ }
281
+ return validate(schema, data, { locale });
282
+ }
283
+ ```
284
+
285
+ ```javascript
286
+ // ✅ 正确:应用启动时一次性加载
287
+ // app.js 启动入口
288
+ dsl.config({ i18n: './locales' }); // 只加载一次
289
+
290
+ // 运行时直接切换,无需重新加载
291
+ function validateUser(data, locale) {
292
+ return validate(schema, data, { locale }); // ✅ 直接切换,性能好
293
+ }
294
+ ```
295
+
296
+ ### 为什么推荐"首次加载,运行时切换"?
297
+
298
+ | 方式 | 加载次数 | 性能 | 内存 | 推荐度 |
299
+ |------|---------|------|------|--------|
300
+ | **首次加载所有** | 1次 | ⭐⭐⭐⭐⭐ 极快 | 低 | ✅ 强烈推荐 |
301
+ | 运行时单个加载 | N次 | ⭐⭐ 慢 | 中 | ❌ 不推荐 |
302
+
303
+ ---
304
+
305
+ ## 🎯 完整示例
306
+
307
+ ```javascript
308
+ // ========== app.js(应用启动入口)==========
309
+ const express = require('express');
310
+ const { dsl, validate } = require('schema-dsl');
311
+ const path = require('path');
312
+
313
+ // 应用启动时一次性加载所有语言包
314
+ dsl.config({
315
+ i18n: path.join(__dirname, 'locales')
316
+ });
317
+
318
+ const app = express();
319
+
320
+ // ========== routes/user.js(业务路由)==========
321
+ const userSchema = dsl({
322
+ username: 'string:3-32!',
323
+ email: 'email!',
324
+ age: 'number:18-120'
325
+ });
326
+
327
+ app.post('/api/users', (req, res) => {
328
+ // 从请求头获取用户语言偏好
329
+ const locale = req.headers['accept-language']?.split(',')[0]?.trim() || 'en-US';
330
+
331
+ // 验证(直接切换语言,无需加载)
332
+ const result = validate(userSchema, req.body, { locale });
333
+
334
+ if (!result.valid) {
335
+ return res.status(400).json({
336
+ errors: result.errors // 自动使用用户偏好的语言
337
+ });
338
+ }
339
+
340
+ // 处理请求...
341
+ });
342
+ ```
343
+
344
+ ---
345
+
346
+ ## 📋 完整的消息键列表
347
+
348
+ ### 通用键
349
+
350
+ | 键名 | 说明 | 示例 |
351
+ |-----|------|------|
352
+ | `required` | 必填字段 | `{{#label}} é obrigatório` |
353
+ | `type` | 类型错误 | `{{#label}} deve ser do tipo {{#expected}}` |
354
+ | `min` | 最小长度(通用) | `{{#label}} deve ter pelo menos {{#limit}} caracteres` |
355
+ | `max` | 最大长度(通用) | `{{#label}} não pode exceder {{#limit}} caracteres` |
356
+ | `length` | 精确长度 | `{{#label}} deve ter exatamente {{#limit}} caracteres` |
357
+ | `pattern` | 模式匹配 | `{{#label}} formato inválido` |
358
+ | `enum` | 枚举值 | `{{#label}} deve ser um dos seguintes: {{#allowed}}` |
359
+
360
+ ### 字符串验证键
361
+
362
+ | 键名 | 说明 | 可用变量 |
363
+ |-----|------|---------|
364
+ | `string.minLength` | 最小长度 | `{{#label}}`, `{{#limit}}` |
365
+ | `string.maxLength` | 最大长度 | `{{#label}}`, `{{#limit}}` |
366
+ | `string.length` | 精确长度 | `{{#label}}`, `{{#limit}}` |
367
+ | `string.pattern` | 模式匹配 | `{{#label}}` |
368
+ | `string.alphanum` | 字母数字 | `{{#label}}` |
369
+ | `string.enum` | 枚举值 | `{{#label}}`, `{{#valids}}` |
370
+
371
+ ### 数字验证键
372
+
373
+ | 键名 | 说明 | 可用变量 |
374
+ |-----|------|---------|
375
+ | `number.base` | 类型错误 | `{{#label}}` |
376
+ | `number.min` | 最小值 | `{{#label}}`, `{{#limit}}` |
377
+ | `number.max` | 最大值 | `{{#label}}`, `{{#limit}}` |
378
+ | `number.integer` | 整数 | `{{#label}}` |
379
+ | `number.positive` | 正数 | `{{#label}}` |
380
+ | `number.negative` | 负数 | `{{#label}}` |
381
+ | `number.precision` | 小数精度 | `{{#label}}`, `{{#limit}}` |
382
+ | `number.port` | 端口号 | `{{#label}}` |
383
+
384
+ ### 格式验证键
385
+
386
+ | 键名 | 说明 |
387
+ |-----|------|
388
+ | `format.email` | 邮箱格式 |
389
+ | `format.url` | URL格式 |
390
+ | `format.uuid` | UUID格式 |
391
+ | `format.date` | 日期格式 |
392
+ | `format.datetime` | 日期时间格式 |
393
+ | `format.time` | 时间格式 |
394
+ | `format.ipv4` | IPv4地址 |
395
+ | `format.ipv6` | IPv6地址 |
396
+ | `format.binary` | Base64编码 |
397
+
398
+ ### 自定义模式键
399
+
400
+ | 键名 | 说明 |
401
+ |-----|------|
402
+ | `pattern.phone.cn` | 中国手机号 |
403
+ | `pattern.phone.us` | 美国电话号 |
404
+ | `pattern.idCard.cn` | 中国身份证 |
405
+ | `pattern.creditCard` | 信用卡号 |
406
+ | `pattern.objectId` | MongoDB ObjectId |
407
+ | `pattern.hexColor` | 十六进制颜色 |
408
+ | `pattern.macAddress` | MAC地址 |
409
+ | `pattern.cron` | Cron表达式 |
410
+ | `pattern.slug` | URL别名 |
411
+ | `pattern.username` | 用户名 |
412
+ | `pattern.password.weak` | 弱密码 |
413
+ | `pattern.password.medium` | 中等密码 |
414
+ | `pattern.password.strong` | 强密码 |
415
+ | `pattern.password.veryStrong` | 超强密码 |
416
+
417
+ ---
418
+
419
+ ## 🎨 模板变量
420
+
421
+ 所有错误消息支持以下模板变量:
422
+
423
+ | 变量 | 说明 | 使用示例 |
424
+ |------|------|---------|
425
+ | `{{#label}}` | 字段标签 | `{{#label}} é obrigatório` |
426
+ | `{{#limit}}` | 限制值(长度/大小) | `deve ter pelo menos {{#limit}} caracteres` |
427
+ | `{{#allowed}}` | 允许的值列表 | `deve ser um dos seguintes: {{#allowed}}` |
428
+ | `{{#expected}}` | 期望的类型 | `deve ser do tipo {{#expected}}` |
429
+ | `{{#valids}}` | 有效值列表(数组) | `deve ser: {{#valids}}` |
430
+ | `{{#path}}` | 字段路径 | `Erro no campo {{#path}}` |
431
+
432
+ ---
433
+
434
+ ## 📚 参考内置语言包
435
+
436
+ 你可以参考内置的语言包作为模板:
437
+
438
+ ```javascript
439
+ const { Locale } = require('schema-dsl');
440
+
441
+ // 查看中文语言包
442
+ const zhCN = Locale.getMessages('zh-CN');
443
+ console.log(zhCN);
444
+
445
+ // 查看英文语言包
446
+ const enUS = Locale.getMessages('en-US');
447
+ console.log(enUS);
448
+ ```
449
+
450
+ 或者直接查看源码:
451
+ - 中文:`src/locales/zh-CN.ts`
452
+ - 英文:`src/locales/en-US.ts`
453
+ - 日语:`src/locales/ja-JP.ts`
454
+ - 西班牙语:`src/locales/es-ES.ts`
455
+ - 法语:`src/locales/fr-FR.ts`
456
+
457
+ ---
458
+
459
+ ## ✅ 最佳实践
460
+
461
+ 1. **完整性**:确保翻译所有常用的错误消息键
462
+ 2. **一致性**:保持错误消息风格统一
463
+ 3. **模板变量**:正确使用 `{{#label}}`、`{{#limit}}` 等变量
464
+ 4. **测试**:添加语言包后进行测试,确保所有消息正确显示
465
+ 5. **文档**:为自定义语言包编写使用说明
466
+
467
+ ---
468
+
469
+ ## 🤝 贡献语言包
470
+
471
+ 如果你为 schema-dsl 添加了新语言包,欢迎提交 Pull Request:
472
+
473
+ 1. Fork 项目
474
+ 2. 在 `src/locales/` 目录创建新语言文件(如 `pt-BR.ts`)
475
+ 3. 完整翻译所有消息键
476
+ 4. 在 `src/locales/index.ts` 中注册新语言
477
+ 5. 添加测试用例(在 `test/unit/locales/` 目录)
478
+ 6. 提交 Pull Request
479
+
480
+ ---
481
+
482
+ ## 📞 支持
483
+
484
+ 如果你在添加语言包时遇到问题:
485
+
486
+ - 查看 [多语言配置指南](./i18n.md)
487
+ - 查看 [动态多语言配置指南](./dynamic-locale.md)
488
+ - 提交 Issue: https://github.com/vextjs/schema-dsl/issues
489
+
490
+ ---
491
+
492
+ ## 对应示例文件
493
+
494
+ **示例入口**: [add-custom-locale.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/add-custom-locale.ts)
495
+ **说明**: 覆盖 `Locale.addLocale()` 注册新语言、读取消息文本,以及在自定义 locale 下执行验证的最小工作流。
496
+