tang-ui-x 1.1.1 → 1.1.2
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/README.md +1003 -0
- package/components/TActionSheet/index.uvue +15 -2
- package/components/TCollapse/index.uvue +1 -1
- package/components/TCollapse/type.uts +3 -1
- package/components/TCollapseItem/index.uvue +22 -26
- package/components/TDialog/index.uvue +19 -4
- package/components/TEmpty/index.uvue +28 -14
- package/components/TForm/index.uvue +30 -9
- package/components/TForm/type.uts +4 -0
- package/components/TInput/index.uvue +24 -5
- package/components/TInput/type.uts +10 -0
- package/components/TPicker/index.uvue +26 -6
- package/components/TSearchBar/index.uvue +19 -4
- package/composables/i18n/error.uts +82 -0
- package/composables/i18n/index.uts +188 -0
- package/composables/i18n/manager-demo.uts +104 -0
- package/composables/i18n/manager.test.uts +182 -0
- package/composables/i18n/manager.uts +336 -0
- package/composables/i18n/register-demo.uts +125 -0
- package/composables/i18n/task22-verification.uts +198 -0
- package/composables/i18n/task23-verification.uts +343 -0
- package/composables/i18n/task8-demo.uts +93 -0
- package/composables/i18n/task8-verification.uts +98 -0
- package/composables/i18n/test-task23.uts +9 -0
- package/composables/i18n/types.uts +46 -0
- package/composables/i18n/useI18n-verification.uts +105 -0
- package/composables/i18n/validation-demo.uts +45 -0
- package/composables/i18n/validation-test.uts +106 -0
- package/composables/useI18n.uts +77 -0
- package/index.uts +23 -0
- package/locales/cross-platform-verification.uts +510 -0
- package/locales/en-US/actionSheet.json +3 -0
- package/locales/en-US/common.json +10 -0
- package/locales/en-US/dialog.json +5 -0
- package/locales/en-US/empty.json +5 -0
- package/locales/en-US/errorState.json +5 -0
- package/locales/en-US/examplePages.json +1236 -0
- package/locales/en-US/examples.json +218 -0
- package/locales/en-US/form.json +11 -0
- package/locales/en-US/input.json +3 -0
- package/locales/en-US/list.json +5 -0
- package/locales/en-US/loading.json +3 -0
- package/locales/en-US/navBar.json +4 -0
- package/locales/en-US/noticeBar.json +3 -0
- package/locales/en-US/picker.json +5 -0
- package/locales/en-US/searchBar.json +4 -0
- package/locales/en-US/textarea.json +3 -0
- package/locales/en-US/toast.json +6 -0
- package/locales/index.uts +79 -0
- package/locales/init-verification.uts +101 -0
- package/locales/loader.uts +251 -0
- package/locales/run-verification.uts +16 -0
- package/locales/zh-CN/actionSheet.json +3 -0
- package/locales/zh-CN/common.json +10 -0
- package/locales/zh-CN/dialog.json +5 -0
- package/locales/zh-CN/empty.json +5 -0
- package/locales/zh-CN/errorState.json +5 -0
- package/locales/zh-CN/examplePages.json +1236 -0
- package/locales/zh-CN/examples.json +218 -0
- package/locales/zh-CN/form.json +11 -0
- package/locales/zh-CN/input.json +3 -0
- package/locales/zh-CN/list.json +5 -0
- package/locales/zh-CN/loading.json +3 -0
- package/locales/zh-CN/navBar.json +4 -0
- package/locales/zh-CN/noticeBar.json +3 -0
- package/locales/zh-CN/picker.json +5 -0
- package/locales/zh-CN/searchBar.json +4 -0
- package/locales/zh-CN/textarea.json +3 -0
- package/locales/zh-CN/toast.json +6 -0
- package/locales/zh-TW/actionSheet.json +3 -0
- package/locales/zh-TW/common.json +8 -0
- package/locales/zh-TW/dialog.json +5 -0
- package/locales/zh-TW/empty.json +5 -0
- package/locales/zh-TW/errorState.json +5 -0
- package/locales/zh-TW/examplePages.json +705 -0
- package/locales/zh-TW/examples.json +218 -0
- package/locales/zh-TW/form.json +11 -0
- package/locales/zh-TW/input.json +3 -0
- package/locales/zh-TW/list.json +5 -0
- package/locales/zh-TW/loading.json +3 -0
- package/locales/zh-TW/navBar.json +4 -0
- package/locales/zh-TW/noticeBar.json +3 -0
- package/locales/zh-TW/picker.json +5 -0
- package/locales/zh-TW/searchBar.json +4 -0
- package/locales/zh-TW/textarea.json +3 -0
- package/locales/zh-TW/toast.json +6 -0
- package/package.json +2 -1
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* I18n 核心管理器
|
|
3
|
+
* 管理语言包、当前语言状态、执行翻译逻辑
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ref, computed } from 'vue'
|
|
7
|
+
import type { Ref, ComputedRef } from 'vue'
|
|
8
|
+
import type { ModularLocaleMessages, TranslateParams } from './types.uts'
|
|
9
|
+
import { I18nError } from './error.uts'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* I18nManager 单例类
|
|
13
|
+
* 提供多语言系统的核心功能
|
|
14
|
+
*/
|
|
15
|
+
export class I18nManager {
|
|
16
|
+
/** 单例实例 */
|
|
17
|
+
private static instance: I18nManager | null = null
|
|
18
|
+
|
|
19
|
+
/** 当前语言代码(响应式) */
|
|
20
|
+
public currentLocale: Ref<string>
|
|
21
|
+
|
|
22
|
+
/** 回退语言代码 */
|
|
23
|
+
public fallbackLocale: string
|
|
24
|
+
|
|
25
|
+
/** 语言包存储 */
|
|
26
|
+
private messages: Map<string, ModularLocaleMessages>
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 私有构造函数,确保单例模式
|
|
30
|
+
*/
|
|
31
|
+
private constructor() {
|
|
32
|
+
this.currentLocale = ref<string>('zh-CN')
|
|
33
|
+
this.fallbackLocale = 'zh-CN'
|
|
34
|
+
this.messages = new Map<string, ModularLocaleMessages>()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 获取单例实例
|
|
39
|
+
* @returns I18nManager 实例
|
|
40
|
+
*/
|
|
41
|
+
static getInstance(): I18nManager {
|
|
42
|
+
if (I18nManager.instance === null) {
|
|
43
|
+
I18nManager.instance = new I18nManager()
|
|
44
|
+
}
|
|
45
|
+
return I18nManager.instance
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 翻译函数
|
|
50
|
+
* 支持模块化键查询(moduleName.key)和参数插值
|
|
51
|
+
* @param key 翻译键(格式:moduleName.key)
|
|
52
|
+
* @param params 插值参数(可选)
|
|
53
|
+
* @returns 翻译后的文本
|
|
54
|
+
*/
|
|
55
|
+
translate(key: string, params?: TranslateParams): string {
|
|
56
|
+
// 1. 解析键:moduleName.key
|
|
57
|
+
const parts = key.split('.')
|
|
58
|
+
|
|
59
|
+
if (parts.length < 2) {
|
|
60
|
+
I18nError.warnInvalidKey(key)
|
|
61
|
+
return key
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const moduleName = parts[0]
|
|
65
|
+
const messageKey = parts.slice(1).join('.')
|
|
66
|
+
|
|
67
|
+
if (!moduleName || !messageKey) {
|
|
68
|
+
I18nError.warnInvalidKey(key)
|
|
69
|
+
return key
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 2. 在当前语言中查找
|
|
73
|
+
let message = this.getModuleMessage(this.currentLocale.value, moduleName, messageKey)
|
|
74
|
+
|
|
75
|
+
// 3. 如果未找到,尝试回退语言
|
|
76
|
+
if (message === undefined) {
|
|
77
|
+
message = this.getModuleMessage(this.fallbackLocale, moduleName, messageKey)
|
|
78
|
+
|
|
79
|
+
// 4. 如果仍未找到,返回键本身
|
|
80
|
+
if (message === undefined) {
|
|
81
|
+
I18nError.warnKeyNotFound(key, this.currentLocale.value)
|
|
82
|
+
return key
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 5. 如果有参数,执行插值
|
|
87
|
+
if (params && typeof message === 'string') {
|
|
88
|
+
return this.interpolate(message, params)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return message as string
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 获取模块中的消息(支持深层嵌套)
|
|
96
|
+
* @param locale 语言代码
|
|
97
|
+
* @param moduleName 模块名
|
|
98
|
+
* @param key 消息键(支持点号分隔的嵌套路径)
|
|
99
|
+
* @returns 消息文本或 undefined
|
|
100
|
+
*/
|
|
101
|
+
private getModuleMessage(locale: string, moduleName: string, key: string): string | undefined {
|
|
102
|
+
const localeMessages = this.messages.get(locale)
|
|
103
|
+
if (!localeMessages) {
|
|
104
|
+
return undefined
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const module = localeMessages[moduleName]
|
|
108
|
+
if (!module) {
|
|
109
|
+
return undefined
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 支持嵌套路径查询(例如:components.TButton.name)
|
|
113
|
+
const keys = key.split('.')
|
|
114
|
+
let current: any = module
|
|
115
|
+
|
|
116
|
+
for (const k of keys) {
|
|
117
|
+
if (current && typeof current === 'object' && k in current) {
|
|
118
|
+
current = current[k]
|
|
119
|
+
} else {
|
|
120
|
+
return undefined
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// 确保最终值是字符串
|
|
125
|
+
return typeof current === 'string' ? current : undefined
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 字符串插值
|
|
130
|
+
* 支持 {key} 占位符格式
|
|
131
|
+
* @param template 模板字符串(使用 {key} 作为占位符)
|
|
132
|
+
* @param params 参数对象
|
|
133
|
+
* @returns 插值后的字符串
|
|
134
|
+
*/
|
|
135
|
+
private interpolate(template: string, params: TranslateParams): string {
|
|
136
|
+
return template.replace(/\{(\w+)\}/g, (match: string, key: string): string => {
|
|
137
|
+
if (key in params) {
|
|
138
|
+
return String(params[key])
|
|
139
|
+
}
|
|
140
|
+
I18nError.warnMissingParam(template, key)
|
|
141
|
+
return match // 保留未匹配的占位符
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 设置当前语言
|
|
147
|
+
* @param locale 语言代码
|
|
148
|
+
* @returns 是否切换成功
|
|
149
|
+
*/
|
|
150
|
+
setLocale(locale: string): boolean {
|
|
151
|
+
if (!this.hasLocale(locale)) {
|
|
152
|
+
I18nError.warnLocaleNotFound(locale)
|
|
153
|
+
return false
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 更新 ref 会自动触发所有依赖的重新计算
|
|
157
|
+
this.currentLocale.value = locale
|
|
158
|
+
return true
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* 检查语言包是否已注册
|
|
163
|
+
* @param locale 语言代码
|
|
164
|
+
* @returns 是否存在
|
|
165
|
+
*/
|
|
166
|
+
hasLocale(locale: string): boolean {
|
|
167
|
+
return this.messages.has(locale)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* 验证语言包结构(支持嵌套对象)
|
|
172
|
+
* @param locale 语言代码
|
|
173
|
+
* @param messages 模块化语言包
|
|
174
|
+
* @returns 是否验证通过
|
|
175
|
+
*/
|
|
176
|
+
private validateMessages(locale: string, messages: ModularLocaleMessages): boolean {
|
|
177
|
+
// 1. 验证语言包是否为有效的对象
|
|
178
|
+
if (!messages || typeof messages !== 'object' || Array.isArray(messages)) {
|
|
179
|
+
I18nError.errorMessagesNotObject(locale)
|
|
180
|
+
return false
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// 2. 验证每个模块是否为对象
|
|
184
|
+
for (const moduleName in messages) {
|
|
185
|
+
const module = messages[moduleName]
|
|
186
|
+
|
|
187
|
+
if (!module || typeof module !== 'object' || Array.isArray(module)) {
|
|
188
|
+
I18nError.errorModuleNotObject(locale, moduleName)
|
|
189
|
+
return false
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// 3. 递归验证模块内容(支持嵌套对象)
|
|
193
|
+
if (!this.validateModuleContent(locale, moduleName, module)) {
|
|
194
|
+
return false
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return true
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* 递归验证模块内容(支持嵌套对象)
|
|
203
|
+
* @param locale 语言代码
|
|
204
|
+
* @param moduleName 模块名
|
|
205
|
+
* @param obj 要验证的对象
|
|
206
|
+
* @returns 是否验证通过
|
|
207
|
+
*/
|
|
208
|
+
private validateModuleContent(locale: string, moduleName: string, obj: any): boolean {
|
|
209
|
+
for (const key in obj) {
|
|
210
|
+
const value = obj[key]
|
|
211
|
+
|
|
212
|
+
if (typeof value === 'string') {
|
|
213
|
+
// 字符串值,验证通过
|
|
214
|
+
continue
|
|
215
|
+
} else if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
216
|
+
// 嵌套对象,递归验证
|
|
217
|
+
if (!this.validateModuleContent(locale, moduleName, value)) {
|
|
218
|
+
return false
|
|
219
|
+
}
|
|
220
|
+
} else {
|
|
221
|
+
// 其他类型(数组、null等),验证失败
|
|
222
|
+
I18nError.errorValueNotString(locale, moduleName, key)
|
|
223
|
+
return false
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return true
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* 注册或更新语言包(默认使用合并模式)
|
|
232
|
+
* @param locale 语言代码
|
|
233
|
+
* @param messages 模块化语言包
|
|
234
|
+
*/
|
|
235
|
+
registerMessages(locale: string, messages: ModularLocaleMessages): void {
|
|
236
|
+
this.registerMessagesWithMode(locale, messages, 'merge')
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* 注册语言包(指定模式)
|
|
241
|
+
* @param locale 语言代码
|
|
242
|
+
* @param messages 模块化语言包
|
|
243
|
+
* @param mode 注册模式:'merge' 合并,'replace' 替换
|
|
244
|
+
*/
|
|
245
|
+
registerMessagesWithMode(
|
|
246
|
+
locale: string,
|
|
247
|
+
messages: ModularLocaleMessages,
|
|
248
|
+
mode: 'merge' | 'replace'
|
|
249
|
+
): void {
|
|
250
|
+
// 验证语言包结构
|
|
251
|
+
if (!this.validateMessages(locale, messages)) {
|
|
252
|
+
// 验证失败,拒绝注册
|
|
253
|
+
return
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (mode === 'replace') {
|
|
257
|
+
// 替换模式:完全替换现有语言包(直接设置,不再次验证)
|
|
258
|
+
this.messages.set(locale, messages)
|
|
259
|
+
} else {
|
|
260
|
+
// 合并模式:合并现有语言包
|
|
261
|
+
const existingMessages = this.messages.get(locale)
|
|
262
|
+
|
|
263
|
+
if (existingMessages) {
|
|
264
|
+
// 合并现有语言包
|
|
265
|
+
const merged = this.mergeModularMessages(existingMessages, messages)
|
|
266
|
+
this.messages.set(locale, merged)
|
|
267
|
+
} else {
|
|
268
|
+
// 新增语言包
|
|
269
|
+
this.messages.set(locale, messages)
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* 替换语言包(完全替换)
|
|
276
|
+
* @param locale 语言代码
|
|
277
|
+
* @param messages 模块化语言包
|
|
278
|
+
*/
|
|
279
|
+
replaceMessages(locale: string, messages: ModularLocaleMessages): void {
|
|
280
|
+
// 验证语言包结构
|
|
281
|
+
if (!this.validateMessages(locale, messages)) {
|
|
282
|
+
// 验证失败,拒绝替换
|
|
283
|
+
return
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
this.messages.set(locale, messages)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* 合并模块化语言包
|
|
291
|
+
* @param target 目标语言包
|
|
292
|
+
* @param source 源语言包
|
|
293
|
+
* @returns 合并后的语言包
|
|
294
|
+
*/
|
|
295
|
+
private mergeModularMessages(
|
|
296
|
+
target: ModularLocaleMessages,
|
|
297
|
+
source: ModularLocaleMessages
|
|
298
|
+
): ModularLocaleMessages {
|
|
299
|
+
const result: ModularLocaleMessages = { ...target }
|
|
300
|
+
|
|
301
|
+
// 遍历源语言包的所有模块
|
|
302
|
+
for (const moduleName in source) {
|
|
303
|
+
const sourceModule = source[moduleName]
|
|
304
|
+
const targetModule = result[moduleName]
|
|
305
|
+
|
|
306
|
+
if (targetModule) {
|
|
307
|
+
// 如果目标中已有该模块,合并键值对
|
|
308
|
+
result[moduleName] = {
|
|
309
|
+
...targetModule,
|
|
310
|
+
...sourceModule // 源模块的键覆盖目标模块
|
|
311
|
+
}
|
|
312
|
+
} else {
|
|
313
|
+
// 如果目标中没有该模块,直接添加
|
|
314
|
+
result[moduleName] = sourceModule
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return result
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* 获取可用语言列表(响应式)
|
|
323
|
+
* @returns 可用语言代码数组
|
|
324
|
+
*/
|
|
325
|
+
getAvailableLocales(): ComputedRef<string[]> {
|
|
326
|
+
return computed(() => Array.from(this.messages.keys()))
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* 获取当前语言代码
|
|
331
|
+
* @returns 当前语言代码
|
|
332
|
+
*/
|
|
333
|
+
getCurrentLocale(): string {
|
|
334
|
+
return this.currentLocale.value
|
|
335
|
+
}
|
|
336
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 演示语言包注册和合并功能
|
|
3
|
+
* 用于验证 Task 7 的实现
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { I18nManager } from './manager.uts'
|
|
7
|
+
import type { ModularLocaleMessages } from './types.uts'
|
|
8
|
+
|
|
9
|
+
// 获取管理器实例
|
|
10
|
+
const manager = I18nManager.getInstance()
|
|
11
|
+
|
|
12
|
+
console.log('=== Task 7: 语言包注册和合并功能演示 ===\n')
|
|
13
|
+
|
|
14
|
+
// 1. 注册新语言包(日语)
|
|
15
|
+
console.log('1. 注册新语言包(日语)')
|
|
16
|
+
const jaJP: ModularLocaleMessages = {
|
|
17
|
+
common: {
|
|
18
|
+
confirm: '確認',
|
|
19
|
+
cancel: 'キャンセル',
|
|
20
|
+
ok: 'OK'
|
|
21
|
+
},
|
|
22
|
+
dialog: {
|
|
23
|
+
title: 'ヒント'
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
manager.registerMessages('ja-JP', jaJP)
|
|
28
|
+
console.log('✓ 日语语言包已注册')
|
|
29
|
+
|
|
30
|
+
// 验证新语言包可用
|
|
31
|
+
const hasJapanese = manager.hasLocale('ja-JP')
|
|
32
|
+
console.log(`✓ 日语语言包可用: ${hasJapanese}`)
|
|
33
|
+
|
|
34
|
+
// 切换到日语并测试翻译
|
|
35
|
+
manager.setLocale('ja-JP')
|
|
36
|
+
const japaneseConfirm = manager.translate('common.confirm')
|
|
37
|
+
console.log(`✓ 日语翻译测试: common.confirm = "${japaneseConfirm}"\n`)
|
|
38
|
+
|
|
39
|
+
// 2. 注册已存在语言的部分翻译(覆盖)
|
|
40
|
+
console.log('2. 测试语言包合并(覆盖现有键)')
|
|
41
|
+
manager.setLocale('zh-CN')
|
|
42
|
+
|
|
43
|
+
// 先注册初始中文语言包
|
|
44
|
+
const zhCNInitial: ModularLocaleMessages = {
|
|
45
|
+
common: {
|
|
46
|
+
confirm: '确定',
|
|
47
|
+
cancel: '取消',
|
|
48
|
+
close: '关闭'
|
|
49
|
+
},
|
|
50
|
+
dialog: {
|
|
51
|
+
title: '提示',
|
|
52
|
+
confirmText: '确定'
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
manager.registerMessages('zh-CN', zhCNInitial)
|
|
56
|
+
|
|
57
|
+
console.log('初始中文翻译:')
|
|
58
|
+
console.log(` common.confirm = "${manager.translate('common.confirm')}"`)
|
|
59
|
+
console.log(` common.cancel = "${manager.translate('common.cancel')}"`)
|
|
60
|
+
console.log(` common.close = "${manager.translate('common.close')}"`)
|
|
61
|
+
console.log(` dialog.title = "${manager.translate('dialog.title')}"`)
|
|
62
|
+
|
|
63
|
+
// 注册覆盖部分键的语言包
|
|
64
|
+
const zhCNOverride: ModularLocaleMessages = {
|
|
65
|
+
common: {
|
|
66
|
+
confirm: '好的', // 覆盖
|
|
67
|
+
ok: '行' // 新增
|
|
68
|
+
},
|
|
69
|
+
empty: { // 新模块
|
|
70
|
+
title: '暂无数据'
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
manager.registerMessages('zh-CN', zhCNOverride)
|
|
74
|
+
|
|
75
|
+
console.log('\n合并后的中文翻译:')
|
|
76
|
+
console.log(` common.confirm = "${manager.translate('common.confirm')}" (已覆盖)`)
|
|
77
|
+
console.log(` common.cancel = "${manager.translate('common.cancel')}" (保留)`)
|
|
78
|
+
console.log(` common.close = "${manager.translate('common.close')}" (保留)`)
|
|
79
|
+
console.log(` common.ok = "${manager.translate('common.ok')}" (新增)`)
|
|
80
|
+
console.log(` dialog.title = "${manager.translate('dialog.title')}" (保留)`)
|
|
81
|
+
console.log(` empty.title = "${manager.translate('empty.title')}" (新模块)\n`)
|
|
82
|
+
|
|
83
|
+
// 3. 验证可用语言列表更新
|
|
84
|
+
console.log('3. 验证可用语言列表')
|
|
85
|
+
const availableLocales = manager.getAvailableLocales()
|
|
86
|
+
console.log(`可用语言: ${availableLocales.value.join(', ')}`)
|
|
87
|
+
console.log(`✓ 语言列表已自动更新\n`)
|
|
88
|
+
|
|
89
|
+
// 4. 测试复杂合并场景
|
|
90
|
+
console.log('4. 测试复杂合并场景')
|
|
91
|
+
const frFR: ModularLocaleMessages = {
|
|
92
|
+
common: {
|
|
93
|
+
confirm: 'Confirmer',
|
|
94
|
+
cancel: 'Annuler'
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
manager.registerMessages('fr-FR', frFR)
|
|
98
|
+
|
|
99
|
+
// 添加更多模块到法语
|
|
100
|
+
const frFRAdditional: ModularLocaleMessages = {
|
|
101
|
+
common: {
|
|
102
|
+
ok: 'OK',
|
|
103
|
+
close: 'Fermer'
|
|
104
|
+
},
|
|
105
|
+
dialog: {
|
|
106
|
+
title: 'Conseil'
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
manager.registerMessages('fr-FR', frFRAdditional)
|
|
110
|
+
|
|
111
|
+
manager.setLocale('fr-FR')
|
|
112
|
+
console.log('法语翻译(多次合并后):')
|
|
113
|
+
console.log(` common.confirm = "${manager.translate('common.confirm')}" (第一次注册)`)
|
|
114
|
+
console.log(` common.cancel = "${manager.translate('common.cancel')}" (第一次注册)`)
|
|
115
|
+
console.log(` common.ok = "${manager.translate('common.ok')}" (第二次注册)`)
|
|
116
|
+
console.log(` common.close = "${manager.translate('common.close')}" (第二次注册)`)
|
|
117
|
+
console.log(` dialog.title = "${manager.translate('dialog.title')}" (第二次注册)\n`)
|
|
118
|
+
|
|
119
|
+
console.log('=== 所有测试通过!✓ ===')
|
|
120
|
+
console.log('\n需求验证:')
|
|
121
|
+
console.log('✓ 3.1: 新语言包可以注册并立即可用')
|
|
122
|
+
console.log('✓ 3.2: 已存在语言的语言包可以合并')
|
|
123
|
+
console.log('✓ 3.3: 合并时新内容覆盖旧内容,未覆盖的保留')
|
|
124
|
+
console.log('✓ 3.4: 注册完成后立即可用')
|
|
125
|
+
console.log('✓ 3.5: 接受任意语言代码')
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task 22 验证脚本
|
|
3
|
+
* 验证语言包注册模式功能
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { I18nManager } from './manager.uts'
|
|
7
|
+
import type { ModularLocaleMessages } from './types.uts'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 测试语言包注册模式功能
|
|
11
|
+
*/
|
|
12
|
+
export function testRegistrationModes(): void {
|
|
13
|
+
console.log('=== Task 22: 语言包注册模式功能验证 ===\n')
|
|
14
|
+
|
|
15
|
+
const manager = I18nManager.getInstance()
|
|
16
|
+
|
|
17
|
+
// 测试 1: 合并模式(默认)
|
|
18
|
+
console.log('测试 1: 合并模式(默认 registerMessages)')
|
|
19
|
+
const initialMessages: ModularLocaleMessages = {
|
|
20
|
+
common: {
|
|
21
|
+
confirm: '确定',
|
|
22
|
+
cancel: '取消',
|
|
23
|
+
ok: '好的'
|
|
24
|
+
},
|
|
25
|
+
dialog: {
|
|
26
|
+
title: '提示',
|
|
27
|
+
confirmText: '确定'
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
manager.registerMessages('test-locale', initialMessages)
|
|
32
|
+
console.log('✓ 初始语言包已注册')
|
|
33
|
+
|
|
34
|
+
// 部分覆盖
|
|
35
|
+
const partialMessages: ModularLocaleMessages = {
|
|
36
|
+
common: {
|
|
37
|
+
confirm: '好的', // 覆盖
|
|
38
|
+
close: '关闭' // 新增
|
|
39
|
+
},
|
|
40
|
+
empty: { // 新模块
|
|
41
|
+
title: '暂无数据'
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
manager.registerMessages('test-locale', partialMessages)
|
|
46
|
+
console.log('✓ 部分语言包已合并')
|
|
47
|
+
|
|
48
|
+
// 验证合并结果
|
|
49
|
+
manager.setLocale('test-locale')
|
|
50
|
+
const confirmText = manager.translate('common.confirm')
|
|
51
|
+
const cancelText = manager.translate('common.cancel')
|
|
52
|
+
const okText = manager.translate('common.ok')
|
|
53
|
+
const closeText = manager.translate('common.close')
|
|
54
|
+
const dialogTitle = manager.translate('dialog.title')
|
|
55
|
+
const emptyTitle = manager.translate('empty.title')
|
|
56
|
+
|
|
57
|
+
console.log(` common.confirm: ${confirmText} (应为 "好的")`)
|
|
58
|
+
console.log(` common.cancel: ${cancelText} (应为 "取消" - 保留)`)
|
|
59
|
+
console.log(` common.ok: ${okText} (应为 "好的" - 保留)`)
|
|
60
|
+
console.log(` common.close: ${closeText} (应为 "关闭" - 新增)`)
|
|
61
|
+
console.log(` dialog.title: ${dialogTitle} (应为 "提示" - 保留)`)
|
|
62
|
+
console.log(` empty.title: ${emptyTitle} (应为 "暂无数据" - 新模块)`)
|
|
63
|
+
|
|
64
|
+
const mergeSuccess =
|
|
65
|
+
confirmText === '好的' &&
|
|
66
|
+
cancelText === '取消' &&
|
|
67
|
+
okText === '好的' &&
|
|
68
|
+
closeText === '关闭' &&
|
|
69
|
+
dialogTitle === '提示' &&
|
|
70
|
+
emptyTitle === '暂无数据'
|
|
71
|
+
|
|
72
|
+
if (mergeSuccess) {
|
|
73
|
+
console.log('✅ 合并模式测试通过\n')
|
|
74
|
+
} else {
|
|
75
|
+
console.log('❌ 合并模式测试失败\n')
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 测试 2: 替换模式(registerMessagesWithMode)
|
|
79
|
+
console.log('测试 2: 替换模式(registerMessagesWithMode)')
|
|
80
|
+
const replaceMessages: ModularLocaleMessages = {
|
|
81
|
+
button: {
|
|
82
|
+
submit: '提交',
|
|
83
|
+
reset: '重置'
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
manager.registerMessagesWithMode('test-locale', replaceMessages, 'replace')
|
|
88
|
+
console.log('✓ 语言包已完全替换')
|
|
89
|
+
|
|
90
|
+
// 验证替换结果 - 旧内容应该不存在
|
|
91
|
+
const submitText = manager.translate('button.submit')
|
|
92
|
+
const oldConfirmText = manager.translate('common.confirm')
|
|
93
|
+
const oldDialogTitle = manager.translate('dialog.title')
|
|
94
|
+
|
|
95
|
+
console.log(` button.submit: ${submitText} (应为 "提交")`)
|
|
96
|
+
console.log(` common.confirm: ${oldConfirmText} (应为 "common.confirm" - 已被清除)`)
|
|
97
|
+
console.log(` dialog.title: ${oldDialogTitle} (应为 "dialog.title" - 已被清除)`)
|
|
98
|
+
|
|
99
|
+
const replaceSuccess =
|
|
100
|
+
submitText === '提交' &&
|
|
101
|
+
oldConfirmText === 'common.confirm' &&
|
|
102
|
+
oldDialogTitle === 'dialog.title'
|
|
103
|
+
|
|
104
|
+
if (replaceSuccess) {
|
|
105
|
+
console.log('✅ 替换模式测试通过\n')
|
|
106
|
+
} else {
|
|
107
|
+
console.log('❌ 替换模式测试失败\n')
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// 测试 3: replaceMessages 方法
|
|
111
|
+
console.log('测试 3: replaceMessages 方法')
|
|
112
|
+
const newMessages: ModularLocaleMessages = {
|
|
113
|
+
search: {
|
|
114
|
+
placeholder: '搜索',
|
|
115
|
+
cancel: '取消'
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
manager.replaceMessages('test-locale', newMessages)
|
|
120
|
+
console.log('✓ 使用 replaceMessages 方法替换')
|
|
121
|
+
|
|
122
|
+
const searchPlaceholder = manager.translate('search.placeholder')
|
|
123
|
+
const oldSubmitText = manager.translate('button.submit')
|
|
124
|
+
|
|
125
|
+
console.log(` search.placeholder: ${searchPlaceholder} (应为 "搜索")`)
|
|
126
|
+
console.log(` button.submit: ${oldSubmitText} (应为 "button.submit" - 已被清除)`)
|
|
127
|
+
|
|
128
|
+
const replaceMethodSuccess =
|
|
129
|
+
searchPlaceholder === '搜索' &&
|
|
130
|
+
oldSubmitText === 'button.submit'
|
|
131
|
+
|
|
132
|
+
if (replaceMethodSuccess) {
|
|
133
|
+
console.log('✅ replaceMessages 方法测试通过\n')
|
|
134
|
+
} else {
|
|
135
|
+
console.log('❌ replaceMessages 方法测试失败\n')
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// 测试 4: 合并模式使用 registerMessagesWithMode
|
|
139
|
+
console.log('测试 4: 合并模式使用 registerMessagesWithMode')
|
|
140
|
+
const baseMessages: ModularLocaleMessages = {
|
|
141
|
+
common: {
|
|
142
|
+
yes: '是',
|
|
143
|
+
no: '否'
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
manager.registerMessagesWithMode('test-locale-2', baseMessages, 'merge')
|
|
148
|
+
console.log('✓ 基础语言包已注册')
|
|
149
|
+
|
|
150
|
+
const additionalMessages: ModularLocaleMessages = {
|
|
151
|
+
common: {
|
|
152
|
+
yes: 'Yes', // 覆盖
|
|
153
|
+
maybe: 'Maybe' // 新增
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
manager.registerMessagesWithMode('test-locale-2', additionalMessages, 'merge')
|
|
158
|
+
console.log('✓ 额外语言包已合并')
|
|
159
|
+
|
|
160
|
+
manager.setLocale('test-locale-2')
|
|
161
|
+
const yesText = manager.translate('common.yes')
|
|
162
|
+
const noText = manager.translate('common.no')
|
|
163
|
+
const maybeText = manager.translate('common.maybe')
|
|
164
|
+
|
|
165
|
+
console.log(` common.yes: ${yesText} (应为 "Yes")`)
|
|
166
|
+
console.log(` common.no: ${noText} (应为 "否" - 保留)`)
|
|
167
|
+
console.log(` common.maybe: ${maybeText} (应为 "Maybe" - 新增)`)
|
|
168
|
+
|
|
169
|
+
const mergeWithModeSuccess =
|
|
170
|
+
yesText === 'Yes' &&
|
|
171
|
+
noText === '否' &&
|
|
172
|
+
maybeText === 'Maybe'
|
|
173
|
+
|
|
174
|
+
if (mergeWithModeSuccess) {
|
|
175
|
+
console.log('✅ registerMessagesWithMode 合并模式测试通过\n')
|
|
176
|
+
} else {
|
|
177
|
+
console.log('❌ registerMessagesWithMode 合并模式测试失败\n')
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// 总结
|
|
181
|
+
console.log('=== 测试总结 ===')
|
|
182
|
+
const allSuccess = mergeSuccess && replaceSuccess && replaceMethodSuccess && mergeWithModeSuccess
|
|
183
|
+
|
|
184
|
+
if (allSuccess) {
|
|
185
|
+
console.log('✅ 所有测试通过!')
|
|
186
|
+
console.log('\n实现的功能:')
|
|
187
|
+
console.log('1. registerMessages() - 默认使用合并模式')
|
|
188
|
+
console.log('2. registerMessagesWithMode() - 支持 merge 和 replace 模式')
|
|
189
|
+
console.log('3. replaceMessages() - 完全替换语言包')
|
|
190
|
+
console.log('4. 合并模式正确保留未覆盖的内容')
|
|
191
|
+
console.log('5. 替换模式完全清除旧内容')
|
|
192
|
+
} else {
|
|
193
|
+
console.log('❌ 部分测试失败,请检查实现')
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// 执行测试
|
|
198
|
+
testRegistrationModes()
|