schema-dsl 1.1.3 → 1.1.4
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 +75 -1275
- package/README.md +221 -2
- package/STATUS.md +34 -2
- 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/docs/dsl-syntax.md +14 -3
- package/docs/optional-marker-guide.md +321 -0
- package/docs/runtime-locale-support.md +443 -0
- package/index.d.ts +126 -10
- package/index.js +6 -3
- package/index.mjs +2 -2
- package/lib/core/DslBuilder.js +11 -2
- package/lib/errors/I18nError.js +21 -6
- package/package.json +1 -1
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
# v1.0.9 变更日志
|
|
2
|
+
|
|
3
|
+
> **发布日期**: 2026-01-04
|
|
4
|
+
> **版本号**: v1.0.9
|
|
5
|
+
> **类型**: 🎉 重大改进
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 📋 变更概览
|
|
10
|
+
|
|
11
|
+
| 类型 | 数量 | 说明 |
|
|
12
|
+
|------|------|------|
|
|
13
|
+
| 🎉 新功能 | 2 | 完整多语言支持、I18nError类 |
|
|
14
|
+
| ⚡ 功能增强 | 1 | TypeScript类型定义完善 |
|
|
15
|
+
| 📚 文档更新 | 5 | 多语言相关文档 |
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 🎉 新功能
|
|
20
|
+
|
|
21
|
+
### 1. 完整的多语言支持
|
|
22
|
+
|
|
23
|
+
**内置5种语言,开箱即用的国际化方案**
|
|
24
|
+
|
|
25
|
+
#### 支持的语言
|
|
26
|
+
|
|
27
|
+
| 语言代码 | 语言名称 | 完整度 | 说明 |
|
|
28
|
+
|---------|---------|--------|------|
|
|
29
|
+
| zh-CN | 简体中文 | 100% | 默认语言 |
|
|
30
|
+
| en-US | 英文(美国) | 100% | 完整翻译 |
|
|
31
|
+
| ja-JP | 日语 | 95% | 主要功能已翻译 |
|
|
32
|
+
| es-ES | 西班牙语 | 90% | 核心功能已翻译 |
|
|
33
|
+
| fr-FR | 法语 | 90% | 核心功能已翻译 |
|
|
34
|
+
|
|
35
|
+
#### 快速配置
|
|
36
|
+
|
|
37
|
+
**方式1:使用内置语言**
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
const { dsl, validate, Locale } = require('schema-dsl');
|
|
41
|
+
|
|
42
|
+
// 设置全局语言
|
|
43
|
+
Locale.setLocale('zh-CN');
|
|
44
|
+
|
|
45
|
+
const schema = dsl({ username: 'string:3-32!' });
|
|
46
|
+
|
|
47
|
+
// 中文错误消息
|
|
48
|
+
const result = validate(schema, { username: 'ab' });
|
|
49
|
+
console.log(result.errors[0].message);
|
|
50
|
+
// => "username长度不能少于3个字符"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**方式2:配置自定义语言包目录**
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
const path = require('path');
|
|
57
|
+
|
|
58
|
+
// 应用启动时配置一次
|
|
59
|
+
dsl.config({
|
|
60
|
+
i18n: path.join(__dirname, 'locales')
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// 自动加载目录下所有语言文件
|
|
64
|
+
// locales/zh-CN.js
|
|
65
|
+
// locales/en-US.js
|
|
66
|
+
// locales/custom-lang.js
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
#### 自定义语言包
|
|
70
|
+
|
|
71
|
+
```javascript
|
|
72
|
+
// locales/zh-CN.js
|
|
73
|
+
module.exports = {
|
|
74
|
+
'required': '{{#path}} 是必填项',
|
|
75
|
+
'string.min': '{{#path}} 长度不能少于 {{#min}} 个字符',
|
|
76
|
+
'string.max': '{{#path}} 长度不能超过 {{#max}} 个字符',
|
|
77
|
+
'email': '{{#path}} 必须是有效的邮箱地址',
|
|
78
|
+
'account.notFound': '账户不存在',
|
|
79
|
+
'account.insufficientBalance': '余额不足,当前余额{{balance}},需要{{required}}'
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// locales/en-US.js
|
|
83
|
+
module.exports = {
|
|
84
|
+
'required': '{{#path}} is required',
|
|
85
|
+
'string.min': '{{#path}} must be at least {{#min}} characters',
|
|
86
|
+
'string.max': '{{#path}} must not exceed {{#max}} characters',
|
|
87
|
+
'email': '{{#path}} must be a valid email',
|
|
88
|
+
'account.notFound': 'Account not found',
|
|
89
|
+
'account.insufficientBalance': 'Insufficient balance, current: {{balance}}, required: {{required}}'
|
|
90
|
+
};
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
#### 运行时切换语言
|
|
94
|
+
|
|
95
|
+
```javascript
|
|
96
|
+
// 全局切换
|
|
97
|
+
Locale.setLocale('en-US');
|
|
98
|
+
|
|
99
|
+
// 验证时指定语言
|
|
100
|
+
validate(schema, data, { locale: 'ja-JP' });
|
|
101
|
+
|
|
102
|
+
// 获取当前语言
|
|
103
|
+
const currentLocale = Locale.getLocale(); // 'en-US'
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### 参数插值
|
|
107
|
+
|
|
108
|
+
```javascript
|
|
109
|
+
// 自动插值
|
|
110
|
+
const schema = dsl({ age: 'number:18-120!' });
|
|
111
|
+
validate(schema, { age: 15 });
|
|
112
|
+
// => "age must be at least 18" (en-US)
|
|
113
|
+
// => "age 必须至少为 18" (zh-CN)
|
|
114
|
+
|
|
115
|
+
// 自定义参数插值
|
|
116
|
+
const error = new I18nError('account.insufficientBalance', {
|
|
117
|
+
balance: 50,
|
|
118
|
+
required: 100
|
|
119
|
+
});
|
|
120
|
+
console.log(error.message);
|
|
121
|
+
// => "余额不足,当前余额50,需要100" (zh-CN)
|
|
122
|
+
// => "Insufficient balance, current: 50, required: 100" (en-US)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
### 2. I18nError 多语言错误类
|
|
128
|
+
|
|
129
|
+
**统一的多语言错误处理类**
|
|
130
|
+
|
|
131
|
+
#### 基础用法
|
|
132
|
+
|
|
133
|
+
```javascript
|
|
134
|
+
const { I18nError } = require('schema-dsl');
|
|
135
|
+
|
|
136
|
+
// 创建错误
|
|
137
|
+
const error = new I18nError(
|
|
138
|
+
'account.notFound', // 错误代码
|
|
139
|
+
{}, // 参数(可选)
|
|
140
|
+
404 // HTTP状态码(可选)
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
console.log(error.code); // 'account.notFound'
|
|
144
|
+
console.log(error.message); // '账户不存在' (zh-CN)
|
|
145
|
+
console.log(error.statusCode); // 404
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
#### 静态方法
|
|
149
|
+
|
|
150
|
+
```javascript
|
|
151
|
+
// 直接抛出错误
|
|
152
|
+
I18nError.throw('user.noPermission');
|
|
153
|
+
|
|
154
|
+
// 断言风格
|
|
155
|
+
I18nError.assert(user, 'user.notFound');
|
|
156
|
+
I18nError.assert(
|
|
157
|
+
user.role === 'admin',
|
|
158
|
+
'user.noPermission'
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
// 创建错误对象
|
|
162
|
+
const error = I18nError.create('account.notFound', {}, 404);
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
#### 错误对象属性
|
|
166
|
+
|
|
167
|
+
```javascript
|
|
168
|
+
const error = new I18nError('account.insufficientBalance', {
|
|
169
|
+
balance: 50,
|
|
170
|
+
required: 100
|
|
171
|
+
}, 400);
|
|
172
|
+
|
|
173
|
+
console.log(error.code); // 'account.insufficientBalance'
|
|
174
|
+
console.log(error.message); // '余额不足,当前余额50,需要100'
|
|
175
|
+
console.log(error.statusCode); // 400
|
|
176
|
+
console.log(error.params); // { balance: 50, required: 100 }
|
|
177
|
+
|
|
178
|
+
// JSON序列化
|
|
179
|
+
console.log(error.toJSON());
|
|
180
|
+
// {
|
|
181
|
+
// code: 'account.insufficientBalance',
|
|
182
|
+
// message: '余额不足,当前余额50,需要100',
|
|
183
|
+
// statusCode: 400,
|
|
184
|
+
// params: { balance: 50, required: 100 }
|
|
185
|
+
// }
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
#### Express/Koa 集成
|
|
189
|
+
|
|
190
|
+
```javascript
|
|
191
|
+
// Express 错误处理中间件
|
|
192
|
+
app.use((error, req, res, next) => {
|
|
193
|
+
if (error instanceof I18nError) {
|
|
194
|
+
return res.status(error.statusCode).json({
|
|
195
|
+
success: false,
|
|
196
|
+
code: error.code,
|
|
197
|
+
message: error.message,
|
|
198
|
+
params: error.params
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
next(error);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// 业务代码
|
|
205
|
+
app.post('/api/withdraw', async (req, res, next) => {
|
|
206
|
+
try {
|
|
207
|
+
const account = await getAccount(req.user.id);
|
|
208
|
+
I18nError.assert(account, 'account.notFound', {}, 404);
|
|
209
|
+
|
|
210
|
+
I18nError.assert(
|
|
211
|
+
account.balance >= req.body.amount,
|
|
212
|
+
'account.insufficientBalance',
|
|
213
|
+
{ balance: account.balance, required: req.body.amount },
|
|
214
|
+
400
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
// 处理提现...
|
|
218
|
+
res.json({ success: true });
|
|
219
|
+
} catch (error) {
|
|
220
|
+
next(error);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## ⚡ 功能增强
|
|
228
|
+
|
|
229
|
+
### TypeScript 类型定义完善
|
|
230
|
+
|
|
231
|
+
**完整的类型定义,100% 类型安全**
|
|
232
|
+
|
|
233
|
+
#### 新增类型
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
// Locale 类型
|
|
237
|
+
interface Locale {
|
|
238
|
+
setLocale(locale: string): void;
|
|
239
|
+
getLocale(): string;
|
|
240
|
+
getMessage(code: string, params?: Record<string, any>): string;
|
|
241
|
+
setMessages(messages: Record<string, string>): void;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// I18nError 类型
|
|
245
|
+
class I18nError extends Error {
|
|
246
|
+
code: string;
|
|
247
|
+
statusCode: number;
|
|
248
|
+
params: Record<string, any>;
|
|
249
|
+
|
|
250
|
+
constructor(
|
|
251
|
+
code: string,
|
|
252
|
+
params?: Record<string, any>,
|
|
253
|
+
statusCode?: number
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
static throw(code: string, params?: Record<string, any>, statusCode?: number): never;
|
|
257
|
+
static assert(condition: any, code: string, params?: Record<string, any>, statusCode?: number): asserts condition;
|
|
258
|
+
static create(code: string, params?: Record<string, any>, statusCode?: number): I18nError;
|
|
259
|
+
|
|
260
|
+
toJSON(): object;
|
|
261
|
+
is(code: string): boolean;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// ValidationOptions 扩展
|
|
265
|
+
interface ValidationOptions {
|
|
266
|
+
locale?: string; // 新增:验证时指定语言
|
|
267
|
+
// ...其他选项
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
#### 使用示例
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
import { dsl, validate, I18nError, Locale } from 'schema-dsl';
|
|
275
|
+
|
|
276
|
+
// 完整的类型推导
|
|
277
|
+
const schema = dsl({ email: 'email!' });
|
|
278
|
+
|
|
279
|
+
// 验证时指定语言
|
|
280
|
+
const result = validate(schema, data, { locale: 'en-US' });
|
|
281
|
+
|
|
282
|
+
// I18nError 类型安全
|
|
283
|
+
try {
|
|
284
|
+
I18nError.throw('account.notFound', {}, 404);
|
|
285
|
+
} catch (error) {
|
|
286
|
+
if (error instanceof I18nError) {
|
|
287
|
+
console.log(error.code); // string
|
|
288
|
+
console.log(error.statusCode); // number
|
|
289
|
+
console.log(error.params); // Record<string, any>
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## 📚 文档更新
|
|
297
|
+
|
|
298
|
+
1. ✅ **多语言用户指南**: [docs/i18n-user-guide.md](../docs/i18n-user-guide.md)
|
|
299
|
+
2. ✅ **I18n 完整文档**: [docs/i18n.md](../docs/i18n.md)
|
|
300
|
+
3. ✅ **I18nError API 文档**: [docs/error-handling.md](../docs/error-handling.md)
|
|
301
|
+
4. ✅ **自定义语言包指南**: [docs/add-custom-locale.md](../docs/add-custom-locale.md)
|
|
302
|
+
5. ✅ **前端 i18n 集成**: [docs/frontend-i18n-guide.md](../docs/frontend-i18n-guide.md)
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## 🔄 破坏性变更
|
|
307
|
+
|
|
308
|
+
**无破坏性变更** - 所有新功能都是可选的,默认行为保持不变。
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## 🎯 升级指南
|
|
313
|
+
|
|
314
|
+
### 从 v1.0.8 升级到 v1.0.9
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
npm install schema-dsl@1.0.9
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
**可选:启用多语言**
|
|
321
|
+
|
|
322
|
+
```javascript
|
|
323
|
+
const { Locale } = require('schema-dsl');
|
|
324
|
+
|
|
325
|
+
// 设置全局语言(可选)
|
|
326
|
+
Locale.setLocale('zh-CN');
|
|
327
|
+
|
|
328
|
+
// 或在验证时指定(可选)
|
|
329
|
+
validate(schema, data, { locale: 'en-US' });
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
**可选:使用 I18nError**
|
|
333
|
+
|
|
334
|
+
```javascript
|
|
335
|
+
const { I18nError } = require('schema-dsl');
|
|
336
|
+
|
|
337
|
+
// 替代普通 Error
|
|
338
|
+
I18nError.throw('account.notFound');
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## 📦 依赖变更
|
|
344
|
+
|
|
345
|
+
**无依赖变更**
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
## 🙏 致谢
|
|
350
|
+
|
|
351
|
+
感谢社区贡献者提供的多语言翻译和反馈。
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
## 🔗 相关链接
|
|
356
|
+
|
|
357
|
+
- [GitHub Repository](https://github.com/vextjs/schema-dsl)
|
|
358
|
+
- [npm Package](https://www.npmjs.com/package/schema-dsl)
|
|
359
|
+
- [完整多语言文档](../docs/i18n.md)
|
|
360
|
+
- [I18nError 示例](../examples/i18n-error.examples.js)
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
**发布者**: schema-dsl Team
|
|
365
|
+
**发布时间**: 2026-01-04
|
|
366
|
+
**下一版本**: v1.1.0
|
|
367
|
+
|