tang-ui-x 1.1.0 → 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.
Files changed (89) hide show
  1. package/README.md +1003 -0
  2. package/components/TActionSheet/index.uvue +15 -2
  3. package/components/TCollapse/index.uvue +1 -1
  4. package/components/TCollapse/type.uts +3 -1
  5. package/components/TCollapseItem/index.uvue +22 -26
  6. package/components/TDialog/index.uvue +19 -4
  7. package/components/TEmpty/index.uvue +28 -14
  8. package/components/TForm/index.uvue +60 -26
  9. package/components/TForm/type.uts +4 -0
  10. package/components/TInput/index.uvue +24 -5
  11. package/components/TInput/type.uts +10 -0
  12. package/components/TPicker/index.uvue +26 -6
  13. package/components/TRadioButton/README.md +117 -0
  14. package/components/TRadioButton/index.uvue +69 -64
  15. package/components/TSearchBar/index.uvue +19 -4
  16. package/composables/i18n/error.uts +82 -0
  17. package/composables/i18n/index.uts +188 -0
  18. package/composables/i18n/manager-demo.uts +104 -0
  19. package/composables/i18n/manager.test.uts +182 -0
  20. package/composables/i18n/manager.uts +336 -0
  21. package/composables/i18n/register-demo.uts +125 -0
  22. package/composables/i18n/task22-verification.uts +198 -0
  23. package/composables/i18n/task23-verification.uts +343 -0
  24. package/composables/i18n/task8-demo.uts +93 -0
  25. package/composables/i18n/task8-verification.uts +98 -0
  26. package/composables/i18n/test-task23.uts +9 -0
  27. package/composables/i18n/types.uts +46 -0
  28. package/composables/i18n/useI18n-verification.uts +105 -0
  29. package/composables/i18n/validation-demo.uts +45 -0
  30. package/composables/i18n/validation-test.uts +106 -0
  31. package/composables/useI18n.uts +77 -0
  32. package/index.uts +23 -0
  33. package/locales/cross-platform-verification.uts +510 -0
  34. package/locales/en-US/actionSheet.json +3 -0
  35. package/locales/en-US/common.json +10 -0
  36. package/locales/en-US/dialog.json +5 -0
  37. package/locales/en-US/empty.json +5 -0
  38. package/locales/en-US/errorState.json +5 -0
  39. package/locales/en-US/examplePages.json +1236 -0
  40. package/locales/en-US/examples.json +218 -0
  41. package/locales/en-US/form.json +11 -0
  42. package/locales/en-US/input.json +3 -0
  43. package/locales/en-US/list.json +5 -0
  44. package/locales/en-US/loading.json +3 -0
  45. package/locales/en-US/navBar.json +4 -0
  46. package/locales/en-US/noticeBar.json +3 -0
  47. package/locales/en-US/picker.json +5 -0
  48. package/locales/en-US/searchBar.json +4 -0
  49. package/locales/en-US/textarea.json +3 -0
  50. package/locales/en-US/toast.json +6 -0
  51. package/locales/index.uts +79 -0
  52. package/locales/init-verification.uts +101 -0
  53. package/locales/loader.uts +251 -0
  54. package/locales/run-verification.uts +16 -0
  55. package/locales/zh-CN/actionSheet.json +3 -0
  56. package/locales/zh-CN/common.json +10 -0
  57. package/locales/zh-CN/dialog.json +5 -0
  58. package/locales/zh-CN/empty.json +5 -0
  59. package/locales/zh-CN/errorState.json +5 -0
  60. package/locales/zh-CN/examplePages.json +1236 -0
  61. package/locales/zh-CN/examples.json +218 -0
  62. package/locales/zh-CN/form.json +11 -0
  63. package/locales/zh-CN/input.json +3 -0
  64. package/locales/zh-CN/list.json +5 -0
  65. package/locales/zh-CN/loading.json +3 -0
  66. package/locales/zh-CN/navBar.json +4 -0
  67. package/locales/zh-CN/noticeBar.json +3 -0
  68. package/locales/zh-CN/picker.json +5 -0
  69. package/locales/zh-CN/searchBar.json +4 -0
  70. package/locales/zh-CN/textarea.json +3 -0
  71. package/locales/zh-CN/toast.json +6 -0
  72. package/locales/zh-TW/actionSheet.json +3 -0
  73. package/locales/zh-TW/common.json +8 -0
  74. package/locales/zh-TW/dialog.json +5 -0
  75. package/locales/zh-TW/empty.json +5 -0
  76. package/locales/zh-TW/errorState.json +5 -0
  77. package/locales/zh-TW/examplePages.json +705 -0
  78. package/locales/zh-TW/examples.json +218 -0
  79. package/locales/zh-TW/form.json +11 -0
  80. package/locales/zh-TW/input.json +3 -0
  81. package/locales/zh-TW/list.json +5 -0
  82. package/locales/zh-TW/loading.json +3 -0
  83. package/locales/zh-TW/navBar.json +4 -0
  84. package/locales/zh-TW/noticeBar.json +3 -0
  85. package/locales/zh-TW/picker.json +5 -0
  86. package/locales/zh-TW/searchBar.json +4 -0
  87. package/locales/zh-TW/textarea.json +3 -0
  88. package/locales/zh-TW/toast.json +6 -0
  89. package/package.json +49 -47
@@ -0,0 +1,104 @@
1
+ /**
2
+ * I18nManager 功能演示
3
+ * 演示语言切换功能的使用
4
+ */
5
+
6
+ import { I18nManager } from './manager.uts'
7
+ import type { ModularLocaleMessages } from './types.uts'
8
+
9
+ // 获取管理器实例
10
+ const manager = I18nManager.getInstance()
11
+
12
+ // 注册测试语言包
13
+ const zhCN: ModularLocaleMessages = {
14
+ common: {
15
+ confirm: '确定',
16
+ cancel: '取消',
17
+ hello: '你好,{name}'
18
+ },
19
+ dialog: {
20
+ title: '提示'
21
+ }
22
+ }
23
+
24
+ const enUS: ModularLocaleMessages = {
25
+ common: {
26
+ confirm: 'Confirm',
27
+ cancel: 'Cancel',
28
+ hello: 'Hello, {name}'
29
+ },
30
+ dialog: {
31
+ title: 'Notice'
32
+ }
33
+ }
34
+
35
+ const zhTW: ModularLocaleMessages = {
36
+ common: {
37
+ confirm: '確定',
38
+ cancel: '取消',
39
+ hello: '你好,{name}'
40
+ }
41
+ }
42
+
43
+ // 注册语言包
44
+ manager.registerMessages('zh-CN', zhCN)
45
+ manager.registerMessages('en-US', enUS)
46
+ manager.registerMessages('zh-TW', zhTW)
47
+
48
+ console.log('=== 语言切换功能演示 ===\n')
49
+
50
+ // 测试 1: 检查语言包是否已注册
51
+ console.log('1. 检查语言包是否已注册:')
52
+ console.log(' hasLocale("zh-CN"):', manager.hasLocale('zh-CN'))
53
+ console.log(' hasLocale("en-US"):', manager.hasLocale('en-US'))
54
+ console.log(' hasLocale("zh-TW"):', manager.hasLocale('zh-TW'))
55
+ console.log(' hasLocale("ja-JP"):', manager.hasLocale('ja-JP'))
56
+ console.log('')
57
+
58
+ // 测试 2: 切换到有效的语言
59
+ console.log('2. 切换到有效的语言:')
60
+ console.log(' 当前语言:', manager.getCurrentLocale())
61
+ console.log(' 翻译 "common.confirm":', manager.translate('common.confirm'))
62
+
63
+ const success1 = manager.setLocale('en-US')
64
+ console.log(' setLocale("en-US") 返回:', success1)
65
+ console.log(' 当前语言:', manager.getCurrentLocale())
66
+ console.log(' 翻译 "common.confirm":', manager.translate('common.confirm'))
67
+ console.log('')
68
+
69
+ // 测试 3: 切换到另一个有效语言
70
+ console.log('3. 切换到繁体中文:')
71
+ const success2 = manager.setLocale('zh-TW')
72
+ console.log(' setLocale("zh-TW") 返回:', success2)
73
+ console.log(' 当前语言:', manager.getCurrentLocale())
74
+ console.log(' 翻译 "common.confirm":', manager.translate('common.confirm'))
75
+ console.log('')
76
+
77
+ // 测试 4: 尝试切换到无效的语言
78
+ console.log('4. 尝试切换到无效的语言:')
79
+ console.log(' 当前语言:', manager.getCurrentLocale())
80
+ const success3 = manager.setLocale('invalid-locale')
81
+ console.log(' setLocale("invalid-locale") 返回:', success3)
82
+ console.log(' 当前语言(应保持不变):', manager.getCurrentLocale())
83
+ console.log(' 翻译 "common.confirm"(应保持繁体中文):', manager.translate('common.confirm'))
84
+ console.log('')
85
+
86
+ // 测试 5: 验证响应式更新
87
+ console.log('5. 验证响应式状态:')
88
+ console.log(' currentLocale.value:', manager.currentLocale.value)
89
+ manager.setLocale('zh-CN')
90
+ console.log(' 切换后 currentLocale.value:', manager.currentLocale.value)
91
+ console.log('')
92
+
93
+ // 测试 6: 获取可用语言列表
94
+ console.log('6. 获取可用语言列表:')
95
+ const availableLocales = manager.getAvailableLocales()
96
+ console.log(' 可用语言:', availableLocales.value)
97
+ console.log('')
98
+
99
+ console.log('=== 演示完成 ===')
100
+ console.log('✅ setLocale() 方法已实现')
101
+ console.log('✅ hasLocale() 辅助方法已实现')
102
+ console.log('✅ 验证语言代码是否已注册')
103
+ console.log('✅ 更新 currentLocale ref 以触发响应式更新')
104
+ console.log('✅ 返回切换成功/失败状态')
@@ -0,0 +1,182 @@
1
+ /**
2
+ * I18nManager 单元测试
3
+ * 测试核心管理器的基本功能
4
+ */
5
+
6
+ import { describe, it, expect, beforeEach } from 'vitest'
7
+ import { I18nManager } from './manager.uts'
8
+ import type { ModularLocaleMessages } from './types.uts'
9
+
10
+ describe('I18nManager', () => {
11
+ let manager: I18nManager
12
+
13
+ beforeEach(() => {
14
+ // 获取单例实例
15
+ manager = I18nManager.getInstance()
16
+
17
+ // 注册测试语言包
18
+ const zhCN: ModularLocaleMessages = {
19
+ common: {
20
+ confirm: '确定',
21
+ cancel: '取消',
22
+ hello: '你好,{name}'
23
+ },
24
+ dialog: {
25
+ title: '提示'
26
+ }
27
+ }
28
+
29
+ const enUS: ModularLocaleMessages = {
30
+ common: {
31
+ confirm: 'Confirm',
32
+ cancel: 'Cancel',
33
+ hello: 'Hello, {name}'
34
+ }
35
+ }
36
+
37
+ manager.registerMessages('zh-CN', zhCN)
38
+ manager.registerMessages('en-US', enUS)
39
+ manager.setLocale('zh-CN')
40
+ })
41
+
42
+ describe('translate()', () => {
43
+ it('should translate simple keys', () => {
44
+ const result = manager.translate('common.confirm')
45
+ expect(result).toBe('确定')
46
+ })
47
+
48
+ it('should translate with module name', () => {
49
+ const result = manager.translate('dialog.title')
50
+ expect(result).toBe('提示')
51
+ })
52
+
53
+ it('should return key if translation not found', () => {
54
+ const result = manager.translate('common.notexist')
55
+ expect(result).toBe('common.notexist')
56
+ })
57
+
58
+ it('should return key if invalid format', () => {
59
+ const result = manager.translate('invalidkey')
60
+ expect(result).toBe('invalidkey')
61
+ })
62
+ })
63
+
64
+ describe('interpolate()', () => {
65
+ it('should interpolate parameters', () => {
66
+ const result = manager.translate('common.hello', { name: '张三' })
67
+ expect(result).toBe('你好,张三')
68
+ })
69
+
70
+ it('should handle missing parameters', () => {
71
+ const result = manager.translate('common.hello', {})
72
+ expect(result).toBe('你好,{name}')
73
+ })
74
+
75
+ it('should handle multiple parameters', () => {
76
+ // 注册带多个参数的消息
77
+ manager.registerMessages('zh-CN', {
78
+ test: {
79
+ multi: '{greeting}, {name}! 你有 {count} 条消息'
80
+ }
81
+ })
82
+
83
+ const result = manager.translate('test.multi', {
84
+ greeting: '你好',
85
+ name: '李四',
86
+ count: 5
87
+ })
88
+ expect(result).toBe('你好, 李四! 你有 5 条消息')
89
+ })
90
+ })
91
+
92
+ describe('setLocale()', () => {
93
+ it('should switch locale successfully', () => {
94
+ const success = manager.setLocale('en-US')
95
+ expect(success).toBe(true)
96
+ expect(manager.getCurrentLocale()).toBe('en-US')
97
+ })
98
+
99
+ it('should translate in new locale after switch', () => {
100
+ manager.setLocale('en-US')
101
+ const result = manager.translate('common.confirm')
102
+ expect(result).toBe('Confirm')
103
+ })
104
+
105
+ it('should return false for invalid locale', () => {
106
+ const success = manager.setLocale('invalid-locale')
107
+ expect(success).toBe(false)
108
+ expect(manager.getCurrentLocale()).toBe('zh-CN')
109
+ })
110
+ })
111
+
112
+ describe('fallback mechanism', () => {
113
+ it('should fallback to default locale when key not found', () => {
114
+ manager.setLocale('en-US')
115
+ // dialog.title 只在 zh-CN 中存在
116
+ const result = manager.translate('dialog.title')
117
+ expect(result).toBe('提示')
118
+ })
119
+ })
120
+
121
+ describe('registerMessages()', () => {
122
+ it('should register new locale', () => {
123
+ const zhTW: ModularLocaleMessages = {
124
+ common: {
125
+ confirm: '確定'
126
+ }
127
+ }
128
+
129
+ manager.registerMessages('zh-TW', zhTW)
130
+ manager.setLocale('zh-TW')
131
+ const result = manager.translate('common.confirm')
132
+ expect(result).toBe('確定')
133
+ })
134
+
135
+ it('should merge messages for existing locale', () => {
136
+ const additional: ModularLocaleMessages = {
137
+ common: {
138
+ ok: '好的'
139
+ },
140
+ newModule: {
141
+ test: '测试'
142
+ }
143
+ }
144
+
145
+ manager.registerMessages('zh-CN', additional)
146
+
147
+ // 原有的键应该保留
148
+ expect(manager.translate('common.confirm')).toBe('确定')
149
+ // 新增的键应该可用
150
+ expect(manager.translate('common.ok')).toBe('好的')
151
+ expect(manager.translate('newModule.test')).toBe('测试')
152
+ })
153
+
154
+ it('should override existing keys when merging', () => {
155
+ const override: ModularLocaleMessages = {
156
+ common: {
157
+ confirm: '好的'
158
+ }
159
+ }
160
+
161
+ manager.registerMessages('zh-CN', override)
162
+ const result = manager.translate('common.confirm')
163
+ expect(result).toBe('好的')
164
+ })
165
+ })
166
+
167
+ describe('getAvailableLocales()', () => {
168
+ it('should return all registered locales', () => {
169
+ const locales = manager.getAvailableLocales()
170
+ expect(locales.value).toContain('zh-CN')
171
+ expect(locales.value).toContain('en-US')
172
+ })
173
+ })
174
+
175
+ describe('singleton pattern', () => {
176
+ it('should return same instance', () => {
177
+ const instance1 = I18nManager.getInstance()
178
+ const instance2 = I18nManager.getInstance()
179
+ expect(instance1).toBe(instance2)
180
+ })
181
+ })
182
+ })
@@ -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
+ }