schema-dsl 2.3.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.
Files changed (109) hide show
  1. package/.eslintignore +10 -0
  2. package/.eslintrc.json +27 -0
  3. package/.github/CODE_OF_CONDUCT.md +45 -0
  4. package/.github/ISSUE_TEMPLATE/bug_report.md +57 -0
  5. package/.github/ISSUE_TEMPLATE/config.yml +11 -0
  6. package/.github/ISSUE_TEMPLATE/feature_request.md +45 -0
  7. package/.github/ISSUE_TEMPLATE/question.md +31 -0
  8. package/.github/PULL_REQUEST_TEMPLATE.md +70 -0
  9. package/.github/SECURITY.md +184 -0
  10. package/.github/workflows/ci.yml +35 -0
  11. package/CHANGELOG.md +633 -0
  12. package/CONTRIBUTING.md +368 -0
  13. package/LICENSE +21 -0
  14. package/README.md +1122 -0
  15. package/STATUS.md +273 -0
  16. package/docs/FEATURE-INDEX.md +521 -0
  17. package/docs/INDEX.md +224 -0
  18. package/docs/api-reference.md +1098 -0
  19. package/docs/best-practices.md +672 -0
  20. package/docs/cache-manager.md +336 -0
  21. package/docs/design-philosophy.md +602 -0
  22. package/docs/dsl-syntax.md +654 -0
  23. package/docs/dynamic-locale.md +552 -0
  24. package/docs/error-handling.md +703 -0
  25. package/docs/export-guide.md +459 -0
  26. package/docs/faq.md +576 -0
  27. package/docs/frontend-i18n-guide.md +290 -0
  28. package/docs/i18n-user-guide.md +488 -0
  29. package/docs/label-vs-description.md +262 -0
  30. package/docs/markdown-exporter.md +398 -0
  31. package/docs/mongodb-exporter.md +279 -0
  32. package/docs/multi-type-support.md +319 -0
  33. package/docs/mysql-exporter.md +257 -0
  34. package/docs/plugin-system.md +542 -0
  35. package/docs/postgresql-exporter.md +290 -0
  36. package/docs/quick-start.md +761 -0
  37. package/docs/schema-helper.md +340 -0
  38. package/docs/schema-utils.md +492 -0
  39. package/docs/string-extensions.md +480 -0
  40. package/docs/troubleshooting.md +471 -0
  41. package/docs/type-converter.md +319 -0
  42. package/docs/type-reference.md +219 -0
  43. package/docs/validate.md +486 -0
  44. package/docs/validation-guide.md +484 -0
  45. package/examples/array-dsl-example.js +227 -0
  46. package/examples/custom-extension.js +85 -0
  47. package/examples/dsl-match-example.js +74 -0
  48. package/examples/dsl-style.js +118 -0
  49. package/examples/dynamic-locale-configuration.js +348 -0
  50. package/examples/dynamic-locale-example.js +287 -0
  51. package/examples/export-demo.js +130 -0
  52. package/examples/i18n-full-demo.js +310 -0
  53. package/examples/i18n-memory-safety.examples.js +268 -0
  54. package/examples/markdown-export.js +71 -0
  55. package/examples/middleware-usage.js +93 -0
  56. package/examples/password-reset/README.md +153 -0
  57. package/examples/password-reset/schema.js +26 -0
  58. package/examples/password-reset/test.js +101 -0
  59. package/examples/plugin-system.examples.js +205 -0
  60. package/examples/simple-example.js +122 -0
  61. package/examples/string-extensions.js +297 -0
  62. package/examples/user-registration/README.md +156 -0
  63. package/examples/user-registration/routes.js +92 -0
  64. package/examples/user-registration/schema.js +150 -0
  65. package/examples/user-registration/server.js +74 -0
  66. package/index.d.ts +1999 -0
  67. package/index.js +270 -0
  68. package/index.mjs +30 -0
  69. package/lib/adapters/DslAdapter.js +653 -0
  70. package/lib/adapters/index.js +20 -0
  71. package/lib/config/constants.js +286 -0
  72. package/lib/config/patterns/creditCard.js +9 -0
  73. package/lib/config/patterns/idCard.js +9 -0
  74. package/lib/config/patterns/index.js +8 -0
  75. package/lib/config/patterns/licensePlate.js +4 -0
  76. package/lib/config/patterns/passport.js +4 -0
  77. package/lib/config/patterns/phone.js +9 -0
  78. package/lib/config/patterns/postalCode.js +5 -0
  79. package/lib/core/CacheManager.js +376 -0
  80. package/lib/core/DslBuilder.js +740 -0
  81. package/lib/core/ErrorCodes.js +233 -0
  82. package/lib/core/ErrorFormatter.js +342 -0
  83. package/lib/core/JSONSchemaCore.js +347 -0
  84. package/lib/core/Locale.js +119 -0
  85. package/lib/core/MessageTemplate.js +89 -0
  86. package/lib/core/PluginManager.js +448 -0
  87. package/lib/core/StringExtensions.js +209 -0
  88. package/lib/core/Validator.js +316 -0
  89. package/lib/exporters/MarkdownExporter.js +420 -0
  90. package/lib/exporters/MongoDBExporter.js +162 -0
  91. package/lib/exporters/MySQLExporter.js +212 -0
  92. package/lib/exporters/PostgreSQLExporter.js +289 -0
  93. package/lib/exporters/index.js +24 -0
  94. package/lib/locales/en-US.js +65 -0
  95. package/lib/locales/es-ES.js +66 -0
  96. package/lib/locales/fr-FR.js +66 -0
  97. package/lib/locales/index.js +8 -0
  98. package/lib/locales/ja-JP.js +66 -0
  99. package/lib/locales/zh-CN.js +93 -0
  100. package/lib/utils/LRUCache.js +174 -0
  101. package/lib/utils/SchemaHelper.js +240 -0
  102. package/lib/utils/SchemaUtils.js +313 -0
  103. package/lib/utils/TypeConverter.js +245 -0
  104. package/lib/utils/index.js +13 -0
  105. package/lib/validators/CustomKeywords.js +203 -0
  106. package/lib/validators/index.js +11 -0
  107. package/package.json +70 -0
  108. package/plugins/custom-format.js +101 -0
  109. package/plugins/custom-validator.js +200 -0
@@ -0,0 +1,488 @@
1
+ # 多语言支持用户指南
2
+
3
+ > **版本**: v2.3.0
4
+ > **更新日期**: 2025-12-29
5
+
6
+ ---
7
+
8
+ ## 📋 目录
9
+
10
+ 1. [快速开始](#快速开始)
11
+ 2. [配置方式](#配置方式)
12
+ 3. [Schema 定义](#schema-定义)
13
+ 4. [前端集成](#前端集成)
14
+ 5. [最佳实践](#最佳实践)
15
+ 6. [常见问题](#常见问题)
16
+
17
+ ---
18
+
19
+ ## 快速开始
20
+
21
+ ### 5 分钟上手
22
+
23
+ ```javascript
24
+ const { dsl, validate } = require('schema-dsl');
25
+
26
+ // 1. 配置用户语言包
27
+ dsl.config({
28
+ i18n: {
29
+ locales: {
30
+ 'zh-CN': {
31
+ 'username': '用户名',
32
+ 'email': '邮箱地址'
33
+ },
34
+ 'en-US': {
35
+ 'username': 'Username',
36
+ 'email': 'Email Address'
37
+ }
38
+ }
39
+ }
40
+ });
41
+
42
+ // 2. 定义 Schema(使用 key)
43
+ const schema = dsl({
44
+ username: 'string:3-32!'.label('username'),
45
+ email: 'email!'.label('email')
46
+ });
47
+
48
+ // 3. 验证(动态切换语言)
49
+ const result = validate(schema, data, { locale: 'zh-CN' });
50
+ ```
51
+
52
+ ---
53
+
54
+ ## 配置方式
55
+
56
+ ### 方式 1:直接传入对象(推荐小型项目)
57
+
58
+ ```javascript
59
+ dsl.config({
60
+ i18n: {
61
+ locales: {
62
+ 'zh-CN': {
63
+ 'username': '用户名',
64
+ 'email': '邮箱地址',
65
+ 'custom.invalidEmail': '邮箱格式不正确'
66
+ },
67
+ 'en-US': {
68
+ 'username': 'Username',
69
+ 'email': 'Email Address',
70
+ 'custom.invalidEmail': 'Invalid email format'
71
+ }
72
+ }
73
+ }
74
+ });
75
+ ```
76
+
77
+ **优点**:
78
+ - ✅ 简单直接
79
+ - ✅ 适合小型项目
80
+ - ✅ 无需额外文件
81
+
82
+ **缺点**:
83
+ - ❌ 语言包较大时代码臃肿
84
+ - ❌ 不利于维护
85
+
86
+ ---
87
+
88
+ ### 方式 2:从目录加载(推荐大型项目)
89
+
90
+ **目录结构**:
91
+ ```
92
+ project/
93
+ ├── i18n/
94
+ │ └── labels/
95
+ │ ├── zh-CN.js
96
+ │ ├── en-US.js
97
+ │ └── ja-JP.js
98
+ ├── app.js
99
+ └── routes/
100
+ ```
101
+
102
+ **配置**:
103
+ ```javascript
104
+ const path = require('path');
105
+
106
+ dsl.config({
107
+ i18n: {
108
+ localesPath: path.join(__dirname, 'i18n/labels')
109
+ }
110
+ });
111
+ ```
112
+
113
+ **语言包文件**(`i18n/labels/zh-CN.js`):
114
+ ```javascript
115
+ module.exports = {
116
+ // 字段标签
117
+ 'username': '用户名',
118
+ 'email': '邮箱地址',
119
+ 'password': '密码',
120
+ 'age': '年龄',
121
+
122
+ // 嵌套字段
123
+ 'address.city': '城市',
124
+ 'address.street': '街道',
125
+
126
+ // 自定义错误消息
127
+ 'custom.invalidEmail': '邮箱格式不正确',
128
+ 'custom.emailTaken': '该邮箱已被注册'
129
+ };
130
+ ```
131
+
132
+ **优点**:
133
+ - ✅ 清晰维护
134
+ - ✅ 支持大型项目
135
+ - ✅ 易于协作
136
+
137
+ ---
138
+
139
+ ### 缓存配置(可选)
140
+
141
+ ```javascript
142
+ dsl.config({
143
+ cache: {
144
+ maxSize: 10000, // 缓存最大条目数
145
+ ttl: 7200000 // 缓存过期时间(ms)
146
+ }
147
+ });
148
+ ```
149
+
150
+ **推荐配置**:
151
+
152
+ | 项目规模 | maxSize | 说明 |
153
+ |---------|---------|------|
154
+ | 小型(< 100 Schema) | 1000(默认) | 够用 |
155
+ | 中型(100-1000) | 5000(默认) | 推荐 |
156
+ | 大型(1000-5000) | 10000 | 推荐 |
157
+ | 超大型(> 5000) | 20000 | 推荐 |
158
+
159
+ ---
160
+
161
+ ## Schema 定义
162
+
163
+ ### 使用 key 引用语言包
164
+
165
+ ```javascript
166
+ const userSchema = dsl({
167
+ // label 使用 key
168
+ username: 'string:3-32!'.label('username'),
169
+ email: 'email!'.label('email'),
170
+
171
+ // messages 使用 key
172
+ password: 'string:8-32!'.label('password').messages({
173
+ 'minLength': 'custom.passwordWeak'
174
+ })
175
+ });
176
+ ```
177
+
178
+ ### 嵌套字段
179
+
180
+ ```javascript
181
+ const addressSchema = dsl({
182
+ address: dsl({
183
+ city: 'string!'.label('address.city'),
184
+ street: 'string!'.label('address.street'),
185
+ zipCode: 'string!'.label('address.zipCode')
186
+ })
187
+ });
188
+ ```
189
+
190
+ **语言包**:
191
+ ```javascript
192
+ {
193
+ 'address.city': '城市',
194
+ 'address.street': '街道',
195
+ 'address.zipCode': '邮编'
196
+ }
197
+ ```
198
+
199
+ ---
200
+
201
+ ## 前端集成
202
+
203
+ ### Express 中间件
204
+
205
+ ```javascript
206
+ const express = require('express');
207
+ const { validate } = require('schema-dsl');
208
+
209
+ const app = express();
210
+ app.use(express.json());
211
+
212
+ // 中间件:提取语言参数
213
+ app.use((req, res, next) => {
214
+ req.locale = req.headers['accept-language'] ||
215
+ req.query.lang ||
216
+ 'zh-CN';
217
+ next();
218
+ });
219
+
220
+ // API 路由
221
+ app.post('/api/register', (req, res) => {
222
+ // 使用全局 validate,传递 locale
223
+ const result = validate(userSchema, req.body, {
224
+ locale: req.locale
225
+ });
226
+
227
+ if (!result.valid) {
228
+ return res.status(400).json({
229
+ success: false,
230
+ errors: result.errors
231
+ });
232
+ }
233
+
234
+ res.json({ success: true });
235
+ });
236
+ ```
237
+
238
+ ---
239
+
240
+ ### React 集成
241
+
242
+ ```javascript
243
+ import { useState } from 'react';
244
+
245
+ function RegisterForm() {
246
+ const [locale, setLocale] = useState('zh-CN');
247
+ const [errors, setErrors] = useState([]);
248
+
249
+ const handleSubmit = async (formData) => {
250
+ const response = await fetch('/api/register', {
251
+ method: 'POST',
252
+ headers: {
253
+ 'Content-Type': 'application/json',
254
+ 'Accept-Language': locale // ← 传递语言
255
+ },
256
+ body: JSON.stringify(formData)
257
+ });
258
+
259
+ const result = await response.json();
260
+
261
+ if (!result.success) {
262
+ setErrors(result.errors); // 错误消息已经是对应语言
263
+ }
264
+ };
265
+
266
+ return (
267
+ <div>
268
+ {/* 语言切换 */}
269
+ <select value={locale} onChange={(e) => setLocale(e.target.value)}>
270
+ <option value="zh-CN">中文</option>
271
+ <option value="en-US">English</option>
272
+ <option value="ja-JP">日本語</option>
273
+ </select>
274
+
275
+ <form onSubmit={(e) => {
276
+ e.preventDefault();
277
+ handleSubmit({
278
+ username: e.target.username.value,
279
+ email: e.target.email.value
280
+ });
281
+ }}>
282
+ <input name="username" />
283
+ <input name="email" />
284
+ <button type="submit">提交</button>
285
+ </form>
286
+
287
+ {errors.map(err => (
288
+ <div key={err.path}>{err.message}</div>
289
+ ))}
290
+ </div>
291
+ );
292
+ }
293
+ ```
294
+
295
+ ---
296
+
297
+ ### Vue 集成
298
+
299
+ ```vue
300
+ <template>
301
+ <div>
302
+ <select v-model="locale">
303
+ <option value="zh-CN">中文</option>
304
+ <option value="en-US">English</option>
305
+ </select>
306
+
307
+ <form @submit.prevent="handleSubmit">
308
+ <input v-model="form.username" />
309
+ <input v-model="form.email" />
310
+ <button type="submit">提交</button>
311
+ </form>
312
+
313
+ <div v-for="error in errors" :key="error.path">
314
+ {{ error.message }}
315
+ </div>
316
+ </div>
317
+ </template>
318
+
319
+ <script setup>
320
+ import { ref, reactive } from 'vue';
321
+
322
+ const locale = ref('zh-CN');
323
+ const form = reactive({ username: '', email: '' });
324
+ const errors = ref([]);
325
+
326
+ const handleSubmit = async () => {
327
+ const response = await fetch('/api/register', {
328
+ method: 'POST',
329
+ headers: {
330
+ 'Content-Type': 'application/json',
331
+ 'Accept-Language': locale.value
332
+ },
333
+ body: JSON.stringify(form)
334
+ });
335
+
336
+ const result = await response.json();
337
+ errors.value = result.errors || [];
338
+ };
339
+ </script>
340
+ ```
341
+
342
+ ---
343
+
344
+ ## 最佳实践
345
+
346
+ ### 1. 语言包组织
347
+
348
+ **推荐结构**:
349
+ ```
350
+ i18n/
351
+ ├── labels/ # 字段标签
352
+ │ ├── zh-CN.js
353
+ │ ├── en-US.js
354
+ │ └── ja-JP.js
355
+ └── messages/ # 自定义消息(可选)
356
+ ├── zh-CN.js
357
+ └── en-US.js
358
+ ```
359
+
360
+ ### 2. 命名规范
361
+
362
+ **字段标签**:
363
+ ```javascript
364
+ {
365
+ 'username': '用户名', // 简单字段
366
+ 'address.city': '城市', // 嵌套字段
367
+ 'order.items[0].name': '商品名称' // 数组字段
368
+ }
369
+ ```
370
+
371
+ **自定义消息**:
372
+ ```javascript
373
+ {
374
+ 'custom.emailTaken': '邮箱已被注册',
375
+ 'custom.passwordWeak': '密码强度不够',
376
+ 'custom.orderExpired': '订单已过期'
377
+ }
378
+ ```
379
+
380
+ ### 3. 语言检测优先级
381
+
382
+ ```javascript
383
+ // 推荐优先级
384
+ const locale =
385
+ req.query.lang || // 1. URL 参数(最高优先级)
386
+ req.cookies.lang || // 2. Cookie
387
+ req.headers['accept-language'] || // 3. Accept-Language 头
388
+ 'zh-CN'; // 4. 默认语言
389
+ ```
390
+
391
+ ### 4. 语言持久化
392
+
393
+ **前端**:
394
+ ```javascript
395
+ // 保存用户语言偏好
396
+ localStorage.setItem('userLanguage', locale);
397
+
398
+ // 恢复语言偏好
399
+ const savedLang = localStorage.getItem('userLanguage') || 'zh-CN';
400
+ ```
401
+
402
+ ---
403
+
404
+ ## 常见问题
405
+
406
+ ### Q1: 如何添加新语言?
407
+
408
+ **A**: 创建新的语言包文件并重启应用
409
+
410
+ ```javascript
411
+ // i18n/labels/ko-KR.js(韩语)
412
+ module.exports = {
413
+ 'username': '사용자 이름',
414
+ 'email': '이메일 주소'
415
+ };
416
+ ```
417
+
418
+ ### Q2: 如何处理缺失的翻译?
419
+
420
+ **A**: 系统会自动回退
421
+
422
+ ```
423
+ 查找顺序:
424
+ 1. 用户语言包(i18n/labels/zh-CN.js)
425
+ 2. 系统语言包(lib/locales/zh-CN.js)
426
+ 3. 使用 key 本身
427
+ ```
428
+
429
+ ### Q3: 缓存配置对性能有多大影响?
430
+
431
+ **A**: 大型项目提升 3-10 倍
432
+
433
+ ```
434
+ 场景:3000 个 Schema
435
+ 原配置(1000):33% 命中率
436
+ 优化后(5000):100% 命中率
437
+ 性能提升:3 倍
438
+ ```
439
+
440
+ ### Q4: 是否支持动态加载语言包?
441
+
442
+ **A**: 支持,在应用启动后调用 `dsl.config()`
443
+
444
+ ```javascript
445
+ // 动态添加语言
446
+ dsl.config({
447
+ i18n: {
448
+ locales: {
449
+ 'fr-FR': require('./i18n/fr-FR.js')
450
+ }
451
+ }
452
+ });
453
+ ```
454
+
455
+ ### Q5: 如何与其他 i18n 库协同?
456
+
457
+ **A**: 保持语言同步
458
+
459
+ ```javascript
460
+ import i18next from 'i18next';
461
+ import { Locale } from 'schema-dsl';
462
+
463
+ // 同时切换两个库的语言
464
+ function changeLanguage(lang) {
465
+ i18next.changeLanguage(lang);
466
+ Locale.setLocale(lang);
467
+ }
468
+ ```
469
+
470
+ ---
471
+
472
+ ## 相关文档
473
+
474
+ - [API 参考](./api-reference.md)
475
+ - [完整示例](../examples/i18n-full-demo.js)
476
+ - [实际实现说明](./ACTUAL-MULTILANG-IMPLEMENTATION.md)
477
+
478
+ ---
479
+
480
+ ## 更新日志
481
+
482
+ ### v2.3.0 (2025-12-29)
483
+
484
+ - ✅ 新增 `dsl.config()` 支持用户语言包配置
485
+ - ✅ 优化缓存配置(1000 → 5000)
486
+ - ✅ 支持动态缓存配置
487
+ - ✅ 完善多语言文档和示例
488
+