@robsun/create-keystone-app 0.2.7 → 0.2.9
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/dist/create-keystone-app.js +0 -0
- package/dist/create-module.js +0 -0
- package/package.json +7 -6
- package/template/.claude/skills/keystone-dev/SKILL.md +38 -13
- package/template/.claude/skills/keystone-dev/references/CAPABILITIES.md +80 -2
- package/template/.claude/skills/keystone-dev/references/TEMPLATES.md +220 -0
- package/template/.codex/skills/keystone-dev/SKILL.md +38 -13
- package/template/.codex/skills/keystone-dev/references/CAPABILITIES.md +80 -2
- package/template/.codex/skills/keystone-dev/references/TEMPLATES.md +220 -0
- package/template/apps/server/go.mod +1 -1
- package/template/apps/server/go.sum +1 -0
- package/template/apps/server/internal/modules/example/api/handler/item_handler.go +35 -32
- package/template/apps/server/internal/modules/example/domain/service/errors.go +13 -0
- package/template/apps/server/internal/modules/example/i18n/i18n.go +16 -0
- package/template/apps/server/internal/modules/example/i18n/keys.go +23 -0
- package/template/apps/server/internal/modules/example/i18n/locales/en-US.json +18 -0
- package/template/apps/server/internal/modules/example/i18n/locales/zh-CN.json +18 -0
- package/template/apps/server/internal/modules/example/module.go +9 -3
- package/template/apps/web/package.json +3 -1
- package/template/apps/web/src/app.config.ts +8 -1
- package/template/apps/web/src/modules/example/index.ts +7 -1
- package/template/apps/web/src/modules/example/locales/en-US/example.json +32 -0
- package/template/apps/web/src/modules/example/locales/zh-CN/example.json +32 -0
- package/template/apps/web/src/modules/example/pages/ExampleItemsPage.tsx +47 -45
- package/template/apps/web/src/modules/example/routes.tsx +6 -2
- package/template/docs/CONVENTIONS.md +73 -1
- package/template/docs/I18N.md +319 -0
- package/template/package.json +1 -0
- package/template/scripts/generate-i18n-types.js +154 -0
|
File without changes
|
package/dist/create-module.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
2
|
"name": "@robsun/create-keystone-app",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
|
+
"scripts": {
|
|
5
|
+
"build": "node scripts/build.js",
|
|
6
|
+
"prepublishOnly": "node scripts/build.js && node scripts/prune-template-deps.js"
|
|
7
|
+
},
|
|
4
8
|
"publishConfig": {
|
|
5
9
|
"access": "public"
|
|
6
10
|
},
|
|
@@ -15,8 +19,5 @@
|
|
|
15
19
|
],
|
|
16
20
|
"engines": {
|
|
17
21
|
"node": ">=18"
|
|
18
|
-
},
|
|
19
|
-
"scripts": {
|
|
20
|
-
"build": "node scripts/build.js"
|
|
21
22
|
}
|
|
22
|
-
}
|
|
23
|
+
}
|
|
@@ -10,11 +10,26 @@ description: 基于 Keystone 平台开发业务模块。当用户需要创建新
|
|
|
10
10
|
## 工作流程
|
|
11
11
|
|
|
12
12
|
1. **解析需求** → 识别所需能力
|
|
13
|
-
2. **生成前端模块** → routes.tsx, pages/, services/, types.ts
|
|
14
|
-
3. **生成后端模块** → handler/, service/, repository/, migrations/
|
|
15
|
-
4. **自动注册** → main.tsx + manifest.go + config.yaml
|
|
16
|
-
5.
|
|
17
|
-
6.
|
|
13
|
+
2. **生成前端模块** → routes.tsx, pages/, services/, types.ts, locales/
|
|
14
|
+
3. **生成后端模块** → handler/, service/, repository/, migrations/, i18n/
|
|
15
|
+
4. **自动注册** → main.tsx + manifest.go + config.yaml
|
|
16
|
+
5. **配置多语言** → 前端翻译文件 + 后端翻译键常量
|
|
17
|
+
6. **输出调整建议**
|
|
18
|
+
7. **补测试并执行** → 见 references/testing.md
|
|
19
|
+
|
|
20
|
+
## 多语言最佳实践
|
|
21
|
+
|
|
22
|
+
**前端**:
|
|
23
|
+
- 所有用户可见文本必须使用 `t()` 翻译函数
|
|
24
|
+
- 翻译键使用点记法:`{module}:{section}.{key}`
|
|
25
|
+
- 避免硬编码文本,确保国际化覆盖率 100%
|
|
26
|
+
|
|
27
|
+
**后端**:
|
|
28
|
+
- 错误消息使用翻译键常量(keys.go)
|
|
29
|
+
- API 响应消息根据请求语言返回
|
|
30
|
+
- 使用 `i18n.T(c, key)` 获取翻译文本
|
|
31
|
+
|
|
32
|
+
详见 [docs/I18N.md](../../docs/I18N.md)。
|
|
18
33
|
|
|
19
34
|
## 能力选择矩阵
|
|
20
35
|
|
|
@@ -27,13 +42,13 @@ description: 基于 Keystone 平台开发业务模块。当用户需要创建新
|
|
|
27
42
|
| 导入、批量 | DataImporter | ImportHandler + Job队列 |
|
|
28
43
|
| 导出、下载 | DataExporter | ExportHandler + Job队列 |
|
|
29
44
|
| 上传、附件 | FileUpload | UploadHandler + 存储服务 |
|
|
30
|
-
| 审批、流程 | ApprovalFlowEditor | 审批引擎 |
|
|
31
|
-
| 权限 | PermissionGuard | 权限中间件 |
|
|
32
|
-
|
|
33
|
-
审批流程与接入说明见 [references/approval.md](references/approval.md)。
|
|
34
|
-
测试清单见 [references/testing.md](references/testing.md)。
|
|
35
|
-
详细能力清单见 [references/CAPABILITIES.md](references/CAPABILITIES.md)。
|
|
36
|
-
代码模板见 [references/TEMPLATES.md](references/TEMPLATES.md)。
|
|
45
|
+
| 审批、流程 | ApprovalFlowEditor | 审批引擎 |
|
|
46
|
+
| 权限 | PermissionGuard | 权限中间件 |
|
|
47
|
+
|
|
48
|
+
审批流程与接入说明见 [references/approval.md](references/approval.md)。
|
|
49
|
+
测试清单见 [references/testing.md](references/testing.md)。
|
|
50
|
+
详细能力清单见 [references/CAPABILITIES.md](references/CAPABILITIES.md)。
|
|
51
|
+
代码模板见 [references/TEMPLATES.md](references/TEMPLATES.md)。
|
|
37
52
|
|
|
38
53
|
## 模块结构约定
|
|
39
54
|
|
|
@@ -48,7 +63,11 @@ apps/web/src/modules/{name}/
|
|
|
48
63
|
│ └── Form.tsx # ProForm
|
|
49
64
|
├── services/api.ts
|
|
50
65
|
├── types.ts
|
|
51
|
-
|
|
66
|
+
├── locales/ # 多语言翻译
|
|
67
|
+
│ ├── zh-CN/{module}.json
|
|
68
|
+
│ └── en-US/{module}.json
|
|
69
|
+
└── help/
|
|
70
|
+
└── index.md
|
|
52
71
|
```
|
|
53
72
|
|
|
54
73
|
### 后端模块
|
|
@@ -59,6 +78,12 @@ apps/server/internal/modules/{name}/
|
|
|
59
78
|
├── domain/models/
|
|
60
79
|
├── domain/service/
|
|
61
80
|
├── infra/repository/
|
|
81
|
+
├── i18n/ # 多语言翻译
|
|
82
|
+
│ ├── i18n.go
|
|
83
|
+
│ ├── keys.go
|
|
84
|
+
│ └── locales/
|
|
85
|
+
│ ├── zh-CN.json
|
|
86
|
+
│ └── en-US.json
|
|
62
87
|
└── bootstrap/migrations/
|
|
63
88
|
```
|
|
64
89
|
|
|
@@ -69,12 +69,46 @@ import { listNotifications, markNotificationRead, fetchUnreadCount } from '@robs
|
|
|
69
69
|
import { getJob, listJobs } from '@robsun/keystone-web-core'
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
-
### 8.
|
|
72
|
+
### 8. 多语言 (i18n)
|
|
73
|
+
```typescript
|
|
74
|
+
// 翻译 Hook
|
|
75
|
+
import { useTranslation } from 'react-i18next'
|
|
76
|
+
|
|
77
|
+
// 使用示例
|
|
78
|
+
const { t } = useTranslation()
|
|
79
|
+
const title = t('module:key') // 命名空间:键
|
|
80
|
+
const description = t('common:actions.save')
|
|
81
|
+
|
|
82
|
+
// 平台多语言配置
|
|
83
|
+
import { getKeystoneConfig } from '@robsun/keystone-web-core'
|
|
84
|
+
const locale = getKeystoneConfig().ui?.i18n?.defaultLocale // 'zh-CN' | 'en-US'
|
|
85
|
+
|
|
86
|
+
// dayjs 本地化同步(自动完成)
|
|
87
|
+
import dayjs from 'dayjs'
|
|
88
|
+
dayjs.locale(getKeystoneConfig().ui?.locale?.dayjs ?? 'zh-cn')
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**翻译文件组织**:
|
|
92
|
+
```
|
|
93
|
+
src/modules/{module}/locales/
|
|
94
|
+
├── zh-CN/
|
|
95
|
+
│ └── {namespace}.json
|
|
96
|
+
└── en-US/
|
|
97
|
+
└── {namespace}.json
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**命名空间约定**:
|
|
101
|
+
- `common` - 通用文案(按钮、操作、状态等)
|
|
102
|
+
- `auth` - 认证相关
|
|
103
|
+
- `system` - 系统管理
|
|
104
|
+
- `{module}` - 业务模块专属翻译
|
|
105
|
+
|
|
106
|
+
### 9. Hooks
|
|
73
107
|
```typescript
|
|
74
108
|
import { useListState } from '@robsun/keystone-web-core'
|
|
75
109
|
```
|
|
76
110
|
|
|
77
|
-
###
|
|
111
|
+
### 10. 类型
|
|
78
112
|
```typescript
|
|
79
113
|
import type { ApiResponse, PaginatedData, PaginatedResponse } from '@robsun/keystone-web-core'
|
|
80
114
|
import type { Permission, ModuleName, Action } from '@robsun/keystone-web-core'
|
|
@@ -126,3 +160,47 @@ storageService.Upload(...)
|
|
|
126
160
|
storageService.Download(...)
|
|
127
161
|
storageService.Delete(...)
|
|
128
162
|
```
|
|
163
|
+
|
|
164
|
+
### 7. 多语言 (i18n)
|
|
165
|
+
```go
|
|
166
|
+
// 获取当前请求语言环境
|
|
167
|
+
import "github.com/robsuncn/keystone/infra/i18n"
|
|
168
|
+
|
|
169
|
+
locale := i18n.GetLocale(c) // 从请求头或参数获取
|
|
170
|
+
|
|
171
|
+
// 翻译函数
|
|
172
|
+
i18n.T(c, "module.key") // 基础翻译
|
|
173
|
+
i18n.T(c, "module.greeting", "name", "张三") // 带变量
|
|
174
|
+
i18n.Tf(c, "module.count", count) // 复数形式
|
|
175
|
+
|
|
176
|
+
// 错误消息翻译
|
|
177
|
+
return errors.New(i18n.T(c, "module.errors.notFound"))
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**翻译文件组织**:
|
|
181
|
+
```
|
|
182
|
+
internal/modules/{module}/i18n/
|
|
183
|
+
├── i18n.go # 初始化
|
|
184
|
+
├── keys.go # 翻译键常量
|
|
185
|
+
└── locales/
|
|
186
|
+
├── zh-CN.json
|
|
187
|
+
└── en-US.json
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**翻译键常量定义**:
|
|
191
|
+
```go
|
|
192
|
+
package i18n
|
|
193
|
+
|
|
194
|
+
const (
|
|
195
|
+
KeyItemCreated = "example.item.created"
|
|
196
|
+
KeyItemNotFound = "example.item.notFound"
|
|
197
|
+
KeyItemInvalid = "example.item.invalid"
|
|
198
|
+
)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**模块初始化**:
|
|
202
|
+
```go
|
|
203
|
+
func init() {
|
|
204
|
+
i18n.MustLoadModuleTranslations("example", Translations)
|
|
205
|
+
}
|
|
206
|
+
```
|
|
@@ -157,6 +157,111 @@ export interface {Entity} {
|
|
|
157
157
|
}
|
|
158
158
|
```
|
|
159
159
|
|
|
160
|
+
### i18n 翻译文件
|
|
161
|
+
|
|
162
|
+
**locales/zh-CN/{module}.json**
|
|
163
|
+
```json
|
|
164
|
+
{
|
|
165
|
+
"title": "{模块标题}",
|
|
166
|
+
"list": {
|
|
167
|
+
"title": "{实体}列表",
|
|
168
|
+
"empty": "暂无数据",
|
|
169
|
+
"columns": {
|
|
170
|
+
"name": "名称",
|
|
171
|
+
"status": "状态",
|
|
172
|
+
"createdAt": "创建时间"
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
"form": {
|
|
176
|
+
"create": "创建{实体}",
|
|
177
|
+
"edit": "编辑{实体}",
|
|
178
|
+
"fields": {
|
|
179
|
+
"name": "名称",
|
|
180
|
+
"description": "描述"
|
|
181
|
+
},
|
|
182
|
+
"placeholders": {
|
|
183
|
+
"name": "请输入名称"
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
"actions": {
|
|
187
|
+
"create": "新建",
|
|
188
|
+
"edit": "编辑",
|
|
189
|
+
"delete": "删除",
|
|
190
|
+
"save": "保存",
|
|
191
|
+
"cancel": "取消"
|
|
192
|
+
},
|
|
193
|
+
"messages": {
|
|
194
|
+
"createSuccess": "创建成功",
|
|
195
|
+
"updateSuccess": "更新成功",
|
|
196
|
+
"deleteSuccess": "删除成功",
|
|
197
|
+
"deleteConfirm": "确定要删除吗?"
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**locales/en-US/{module}.json**
|
|
203
|
+
```json
|
|
204
|
+
{
|
|
205
|
+
"title": "{Module Title}",
|
|
206
|
+
"list": {
|
|
207
|
+
"title": "{Entity} List",
|
|
208
|
+
"empty": "No data",
|
|
209
|
+
"columns": {
|
|
210
|
+
"name": "Name",
|
|
211
|
+
"status": "Status",
|
|
212
|
+
"createdAt": "Created At"
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
"form": {
|
|
216
|
+
"create": "Create {Entity}",
|
|
217
|
+
"edit": "Edit {Entity}",
|
|
218
|
+
"fields": {
|
|
219
|
+
"name": "Name",
|
|
220
|
+
"description": "Description"
|
|
221
|
+
},
|
|
222
|
+
"placeholders": {
|
|
223
|
+
"name": "Enter name"
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
"actions": {
|
|
227
|
+
"create": "Create",
|
|
228
|
+
"edit": "Edit",
|
|
229
|
+
"delete": "Delete",
|
|
230
|
+
"save": "Save",
|
|
231
|
+
"cancel": "Cancel"
|
|
232
|
+
},
|
|
233
|
+
"messages": {
|
|
234
|
+
"createSuccess": "Created successfully",
|
|
235
|
+
"updateSuccess": "Updated successfully",
|
|
236
|
+
"deleteSuccess": "Deleted successfully",
|
|
237
|
+
"deleteConfirm": "Are you sure to delete?"
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### 使用翻译的 List.tsx
|
|
243
|
+
```tsx
|
|
244
|
+
import { useTranslation } from 'react-i18next'
|
|
245
|
+
import { ProTable } from '@robsun/keystone-web-core'
|
|
246
|
+
|
|
247
|
+
export function Component() {
|
|
248
|
+
const { t } = useTranslation() // 自动使用当前模块的命名空间
|
|
249
|
+
|
|
250
|
+
const columns = [
|
|
251
|
+
{ title: t('{module}:list.columns.name'), dataIndex: 'name' },
|
|
252
|
+
{ title: t('{module}:list.columns.status'), dataIndex: 'status' },
|
|
253
|
+
{ title: t('{module}:list.columns.createdAt'), dataIndex: 'createdAt' },
|
|
254
|
+
]
|
|
255
|
+
|
|
256
|
+
return (
|
|
257
|
+
<ProTable
|
|
258
|
+
columns={columns}
|
|
259
|
+
// ...
|
|
260
|
+
/>
|
|
261
|
+
)
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
160
265
|
## 后端模板
|
|
161
266
|
|
|
162
267
|
### module.go
|
|
@@ -310,3 +415,118 @@ func (r *Repository) Delete(ctx context.Context, id string) error {
|
|
|
310
415
|
return r.db.Delete(&models.{Entity}{}, "id = ?", id).Error
|
|
311
416
|
}
|
|
312
417
|
```
|
|
418
|
+
|
|
419
|
+
### i18n/i18n.go
|
|
420
|
+
```go
|
|
421
|
+
package i18n
|
|
422
|
+
|
|
423
|
+
import (
|
|
424
|
+
"embed"
|
|
425
|
+
"github.com/robsuncn/keystone/infra/i18n"
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
//go:embed locales/*.json
|
|
429
|
+
var Translations embed.FS
|
|
430
|
+
|
|
431
|
+
func init() {
|
|
432
|
+
// 注册模块翻译文件
|
|
433
|
+
i18n.MustLoadModuleTranslations("{module}", Translations)
|
|
434
|
+
}
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### i18n/keys.go
|
|
438
|
+
```go
|
|
439
|
+
package i18n
|
|
440
|
+
|
|
441
|
+
// 翻译键常量定义
|
|
442
|
+
const (
|
|
443
|
+
// 成功消息
|
|
444
|
+
KeyItemCreated = "{module}.item.created"
|
|
445
|
+
KeyItemUpdated = "{module}.item.updated"
|
|
446
|
+
KeyItemDeleted = "{module}.item.deleted"
|
|
447
|
+
|
|
448
|
+
// 错误消息
|
|
449
|
+
KeyItemNotFound = "{module}.item.notFound"
|
|
450
|
+
KeyItemInvalid = "{module}.item.invalid"
|
|
451
|
+
KeyItemAlreadyExists = "{module}.item.alreadyExists"
|
|
452
|
+
|
|
453
|
+
// 验证消息
|
|
454
|
+
KeyNameRequired = "{module}.validation.nameRequired"
|
|
455
|
+
KeyNameTooLong = "{module}.validation.nameTooLong"
|
|
456
|
+
)
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### i18n/locales/zh-CN.json
|
|
460
|
+
```json
|
|
461
|
+
{
|
|
462
|
+
"{module}": {
|
|
463
|
+
"item": {
|
|
464
|
+
"created": "{实体}创建成功",
|
|
465
|
+
"updated": "{实体}更新成功",
|
|
466
|
+
"deleted": "{实体}删除成功",
|
|
467
|
+
"notFound": "{实体}不存在",
|
|
468
|
+
"invalid": "{实体}数据无效",
|
|
469
|
+
"alreadyExists": "{实体}已存在"
|
|
470
|
+
},
|
|
471
|
+
"validation": {
|
|
472
|
+
"nameRequired": "名称不能为空",
|
|
473
|
+
"nameTooLong": "名称长度不能超过 {{max}} 个字符"
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
### i18n/locales/en-US.json
|
|
480
|
+
```json
|
|
481
|
+
{
|
|
482
|
+
"{module}": {
|
|
483
|
+
"item": {
|
|
484
|
+
"created": "{Entity} created successfully",
|
|
485
|
+
"updated": "{Entity} updated successfully",
|
|
486
|
+
"deleted": "{Entity} deleted successfully",
|
|
487
|
+
"notFound": "{Entity} not found",
|
|
488
|
+
"invalid": "Invalid {entity} data",
|
|
489
|
+
"alreadyExists": "{Entity} already exists"
|
|
490
|
+
},
|
|
491
|
+
"validation": {
|
|
492
|
+
"nameRequired": "Name is required",
|
|
493
|
+
"nameTooLong": "Name cannot exceed {{max}} characters"
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
### 使用翻译的 service.go
|
|
500
|
+
```go
|
|
501
|
+
package service
|
|
502
|
+
|
|
503
|
+
import (
|
|
504
|
+
"errors"
|
|
505
|
+
"github.com/gin-gonic/gin"
|
|
506
|
+
"github.com/robsuncn/keystone/infra/i18n"
|
|
507
|
+
modulei18n "app/internal/modules/{module}/i18n"
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
func (s *Service) Get(c *gin.Context, id string) (*models.{Entity}, error) {
|
|
511
|
+
item, err := s.repo.Get(c, id)
|
|
512
|
+
if err != nil {
|
|
513
|
+
// 使用翻译键返回错误
|
|
514
|
+
return nil, errors.New(i18n.T(c, modulei18n.KeyItemNotFound))
|
|
515
|
+
}
|
|
516
|
+
return item, nil
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
func (s *Service) Create(c *gin.Context, entity *models.{Entity}) error {
|
|
520
|
+
if entity.Name == "" {
|
|
521
|
+
return errors.New(i18n.T(c, modulei18n.KeyNameRequired))
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
err := s.repo.Create(c, entity)
|
|
525
|
+
if err != nil {
|
|
526
|
+
return err
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// 可以在日志或审计中使用翻译消息
|
|
530
|
+
return nil
|
|
531
|
+
}
|
|
532
|
+
```
|
|
@@ -10,11 +10,26 @@ description: 基于 Keystone 平台开发业务模块。当用户需要创建新
|
|
|
10
10
|
## 工作流程
|
|
11
11
|
|
|
12
12
|
1. **解析需求** → 识别所需能力
|
|
13
|
-
2. **生成前端模块** → routes.tsx, pages/, services/, types.ts
|
|
14
|
-
3. **生成后端模块** → handler/, service/, repository/, migrations/
|
|
15
|
-
4. **自动注册** → main.tsx + manifest.go + config.yaml
|
|
16
|
-
5.
|
|
17
|
-
6.
|
|
13
|
+
2. **生成前端模块** → routes.tsx, pages/, services/, types.ts, locales/
|
|
14
|
+
3. **生成后端模块** → handler/, service/, repository/, migrations/, i18n/
|
|
15
|
+
4. **自动注册** → main.tsx + manifest.go + config.yaml
|
|
16
|
+
5. **配置多语言** → 前端翻译文件 + 后端翻译键常量
|
|
17
|
+
6. **输出调整建议**
|
|
18
|
+
7. **补测试并执行** → 见 references/testing.md
|
|
19
|
+
|
|
20
|
+
## 多语言最佳实践
|
|
21
|
+
|
|
22
|
+
**前端**:
|
|
23
|
+
- 所有用户可见文本必须使用 `t()` 翻译函数
|
|
24
|
+
- 翻译键使用点记法:`{module}:{section}.{key}`
|
|
25
|
+
- 避免硬编码文本,确保国际化覆盖率 100%
|
|
26
|
+
|
|
27
|
+
**后端**:
|
|
28
|
+
- 错误消息使用翻译键常量(keys.go)
|
|
29
|
+
- API 响应消息根据请求语言返回
|
|
30
|
+
- 使用 `i18n.T(c, key)` 获取翻译文本
|
|
31
|
+
|
|
32
|
+
详见 [docs/I18N.md](../../docs/I18N.md)。
|
|
18
33
|
|
|
19
34
|
## 能力选择矩阵
|
|
20
35
|
|
|
@@ -27,13 +42,13 @@ description: 基于 Keystone 平台开发业务模块。当用户需要创建新
|
|
|
27
42
|
| 导入、批量 | DataImporter | ImportHandler + Job队列 |
|
|
28
43
|
| 导出、下载 | DataExporter | ExportHandler + Job队列 |
|
|
29
44
|
| 上传、附件 | FileUpload | UploadHandler + 存储服务 |
|
|
30
|
-
| 审批、流程 | ApprovalFlowEditor | 审批引擎 |
|
|
31
|
-
| 权限 | PermissionGuard | 权限中间件 |
|
|
32
|
-
|
|
33
|
-
审批流程与接入说明见 [references/approval.md](references/approval.md)。
|
|
34
|
-
测试清单见 [references/testing.md](references/testing.md)。
|
|
35
|
-
详细能力清单见 [references/CAPABILITIES.md](references/CAPABILITIES.md)。
|
|
36
|
-
代码模板见 [references/TEMPLATES.md](references/TEMPLATES.md)。
|
|
45
|
+
| 审批、流程 | ApprovalFlowEditor | 审批引擎 |
|
|
46
|
+
| 权限 | PermissionGuard | 权限中间件 |
|
|
47
|
+
|
|
48
|
+
审批流程与接入说明见 [references/approval.md](references/approval.md)。
|
|
49
|
+
测试清单见 [references/testing.md](references/testing.md)。
|
|
50
|
+
详细能力清单见 [references/CAPABILITIES.md](references/CAPABILITIES.md)。
|
|
51
|
+
代码模板见 [references/TEMPLATES.md](references/TEMPLATES.md)。
|
|
37
52
|
|
|
38
53
|
## 模块结构约定
|
|
39
54
|
|
|
@@ -48,7 +63,11 @@ apps/web/src/modules/{name}/
|
|
|
48
63
|
│ └── Form.tsx # ProForm
|
|
49
64
|
├── services/api.ts
|
|
50
65
|
├── types.ts
|
|
51
|
-
|
|
66
|
+
├── locales/ # 多语言翻译
|
|
67
|
+
│ ├── zh-CN/{module}.json
|
|
68
|
+
│ └── en-US/{module}.json
|
|
69
|
+
└── help/
|
|
70
|
+
└── index.md
|
|
52
71
|
```
|
|
53
72
|
|
|
54
73
|
### 后端模块
|
|
@@ -59,6 +78,12 @@ apps/server/internal/modules/{name}/
|
|
|
59
78
|
├── domain/models/
|
|
60
79
|
├── domain/service/
|
|
61
80
|
├── infra/repository/
|
|
81
|
+
├── i18n/ # 多语言翻译
|
|
82
|
+
│ ├── i18n.go
|
|
83
|
+
│ ├── keys.go
|
|
84
|
+
│ └── locales/
|
|
85
|
+
│ ├── zh-CN.json
|
|
86
|
+
│ └── en-US.json
|
|
62
87
|
└── bootstrap/migrations/
|
|
63
88
|
```
|
|
64
89
|
|
|
@@ -69,12 +69,46 @@ import { listNotifications, markNotificationRead, fetchUnreadCount } from '@robs
|
|
|
69
69
|
import { getJob, listJobs } from '@robsun/keystone-web-core'
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
-
### 8.
|
|
72
|
+
### 8. 多语言 (i18n)
|
|
73
|
+
```typescript
|
|
74
|
+
// 翻译 Hook
|
|
75
|
+
import { useTranslation } from 'react-i18next'
|
|
76
|
+
|
|
77
|
+
// 使用示例
|
|
78
|
+
const { t } = useTranslation()
|
|
79
|
+
const title = t('module:key') // 命名空间:键
|
|
80
|
+
const description = t('common:actions.save')
|
|
81
|
+
|
|
82
|
+
// 平台多语言配置
|
|
83
|
+
import { getKeystoneConfig } from '@robsun/keystone-web-core'
|
|
84
|
+
const locale = getKeystoneConfig().ui?.i18n?.defaultLocale // 'zh-CN' | 'en-US'
|
|
85
|
+
|
|
86
|
+
// dayjs 本地化同步(自动完成)
|
|
87
|
+
import dayjs from 'dayjs'
|
|
88
|
+
dayjs.locale(getKeystoneConfig().ui?.locale?.dayjs ?? 'zh-cn')
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**翻译文件组织**:
|
|
92
|
+
```
|
|
93
|
+
src/modules/{module}/locales/
|
|
94
|
+
├── zh-CN/
|
|
95
|
+
│ └── {namespace}.json
|
|
96
|
+
└── en-US/
|
|
97
|
+
└── {namespace}.json
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**命名空间约定**:
|
|
101
|
+
- `common` - 通用文案(按钮、操作、状态等)
|
|
102
|
+
- `auth` - 认证相关
|
|
103
|
+
- `system` - 系统管理
|
|
104
|
+
- `{module}` - 业务模块专属翻译
|
|
105
|
+
|
|
106
|
+
### 9. Hooks
|
|
73
107
|
```typescript
|
|
74
108
|
import { useListState } from '@robsun/keystone-web-core'
|
|
75
109
|
```
|
|
76
110
|
|
|
77
|
-
###
|
|
111
|
+
### 10. 类型
|
|
78
112
|
```typescript
|
|
79
113
|
import type { ApiResponse, PaginatedData, PaginatedResponse } from '@robsun/keystone-web-core'
|
|
80
114
|
import type { Permission, ModuleName, Action } from '@robsun/keystone-web-core'
|
|
@@ -126,3 +160,47 @@ storageService.Upload(...)
|
|
|
126
160
|
storageService.Download(...)
|
|
127
161
|
storageService.Delete(...)
|
|
128
162
|
```
|
|
163
|
+
|
|
164
|
+
### 7. 多语言 (i18n)
|
|
165
|
+
```go
|
|
166
|
+
// 获取当前请求语言环境
|
|
167
|
+
import "github.com/robsuncn/keystone/infra/i18n"
|
|
168
|
+
|
|
169
|
+
locale := i18n.GetLocale(c) // 从请求头或参数获取
|
|
170
|
+
|
|
171
|
+
// 翻译函数
|
|
172
|
+
i18n.T(c, "module.key") // 基础翻译
|
|
173
|
+
i18n.T(c, "module.greeting", "name", "张三") // 带变量
|
|
174
|
+
i18n.Tf(c, "module.count", count) // 复数形式
|
|
175
|
+
|
|
176
|
+
// 错误消息翻译
|
|
177
|
+
return errors.New(i18n.T(c, "module.errors.notFound"))
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**翻译文件组织**:
|
|
181
|
+
```
|
|
182
|
+
internal/modules/{module}/i18n/
|
|
183
|
+
├── i18n.go # 初始化
|
|
184
|
+
├── keys.go # 翻译键常量
|
|
185
|
+
└── locales/
|
|
186
|
+
├── zh-CN.json
|
|
187
|
+
└── en-US.json
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**翻译键常量定义**:
|
|
191
|
+
```go
|
|
192
|
+
package i18n
|
|
193
|
+
|
|
194
|
+
const (
|
|
195
|
+
KeyItemCreated = "example.item.created"
|
|
196
|
+
KeyItemNotFound = "example.item.notFound"
|
|
197
|
+
KeyItemInvalid = "example.item.invalid"
|
|
198
|
+
)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**模块初始化**:
|
|
202
|
+
```go
|
|
203
|
+
func init() {
|
|
204
|
+
i18n.MustLoadModuleTranslations("example", Translations)
|
|
205
|
+
}
|
|
206
|
+
```
|