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.
Files changed (87) 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 +30 -9
  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/TSearchBar/index.uvue +19 -4
  14. package/composables/i18n/error.uts +82 -0
  15. package/composables/i18n/index.uts +188 -0
  16. package/composables/i18n/manager-demo.uts +104 -0
  17. package/composables/i18n/manager.test.uts +182 -0
  18. package/composables/i18n/manager.uts +336 -0
  19. package/composables/i18n/register-demo.uts +125 -0
  20. package/composables/i18n/task22-verification.uts +198 -0
  21. package/composables/i18n/task23-verification.uts +343 -0
  22. package/composables/i18n/task8-demo.uts +93 -0
  23. package/composables/i18n/task8-verification.uts +98 -0
  24. package/composables/i18n/test-task23.uts +9 -0
  25. package/composables/i18n/types.uts +46 -0
  26. package/composables/i18n/useI18n-verification.uts +105 -0
  27. package/composables/i18n/validation-demo.uts +45 -0
  28. package/composables/i18n/validation-test.uts +106 -0
  29. package/composables/useI18n.uts +77 -0
  30. package/index.uts +23 -0
  31. package/locales/cross-platform-verification.uts +510 -0
  32. package/locales/en-US/actionSheet.json +3 -0
  33. package/locales/en-US/common.json +10 -0
  34. package/locales/en-US/dialog.json +5 -0
  35. package/locales/en-US/empty.json +5 -0
  36. package/locales/en-US/errorState.json +5 -0
  37. package/locales/en-US/examplePages.json +1236 -0
  38. package/locales/en-US/examples.json +218 -0
  39. package/locales/en-US/form.json +11 -0
  40. package/locales/en-US/input.json +3 -0
  41. package/locales/en-US/list.json +5 -0
  42. package/locales/en-US/loading.json +3 -0
  43. package/locales/en-US/navBar.json +4 -0
  44. package/locales/en-US/noticeBar.json +3 -0
  45. package/locales/en-US/picker.json +5 -0
  46. package/locales/en-US/searchBar.json +4 -0
  47. package/locales/en-US/textarea.json +3 -0
  48. package/locales/en-US/toast.json +6 -0
  49. package/locales/index.uts +79 -0
  50. package/locales/init-verification.uts +101 -0
  51. package/locales/loader.uts +251 -0
  52. package/locales/run-verification.uts +16 -0
  53. package/locales/zh-CN/actionSheet.json +3 -0
  54. package/locales/zh-CN/common.json +10 -0
  55. package/locales/zh-CN/dialog.json +5 -0
  56. package/locales/zh-CN/empty.json +5 -0
  57. package/locales/zh-CN/errorState.json +5 -0
  58. package/locales/zh-CN/examplePages.json +1236 -0
  59. package/locales/zh-CN/examples.json +218 -0
  60. package/locales/zh-CN/form.json +11 -0
  61. package/locales/zh-CN/input.json +3 -0
  62. package/locales/zh-CN/list.json +5 -0
  63. package/locales/zh-CN/loading.json +3 -0
  64. package/locales/zh-CN/navBar.json +4 -0
  65. package/locales/zh-CN/noticeBar.json +3 -0
  66. package/locales/zh-CN/picker.json +5 -0
  67. package/locales/zh-CN/searchBar.json +4 -0
  68. package/locales/zh-CN/textarea.json +3 -0
  69. package/locales/zh-CN/toast.json +6 -0
  70. package/locales/zh-TW/actionSheet.json +3 -0
  71. package/locales/zh-TW/common.json +8 -0
  72. package/locales/zh-TW/dialog.json +5 -0
  73. package/locales/zh-TW/empty.json +5 -0
  74. package/locales/zh-TW/errorState.json +5 -0
  75. package/locales/zh-TW/examplePages.json +705 -0
  76. package/locales/zh-TW/examples.json +218 -0
  77. package/locales/zh-TW/form.json +11 -0
  78. package/locales/zh-TW/input.json +3 -0
  79. package/locales/zh-TW/list.json +5 -0
  80. package/locales/zh-TW/loading.json +3 -0
  81. package/locales/zh-TW/navBar.json +4 -0
  82. package/locales/zh-TW/noticeBar.json +3 -0
  83. package/locales/zh-TW/picker.json +5 -0
  84. package/locales/zh-TW/searchBar.json +4 -0
  85. package/locales/zh-TW/textarea.json +3 -0
  86. package/locales/zh-TW/toast.json +6 -0
  87. package/package.json +2 -1
package/README.md CHANGED
@@ -88,6 +88,1009 @@ function showMessage() {
88
88
  </script>
89
89
  ```
90
90
 
91
+ ## 🌍 国际化 (i18n)
92
+
93
+ Tang UI X 内置了完整的多语言支持系统,让您的应用轻松支持多种语言。
94
+
95
+ ### 内置语言
96
+
97
+ 组件库默认支持以下语言:
98
+
99
+ - 🇨🇳 简体中文 (zh-CN) - 默认语言
100
+ - 🇺🇸 英语 (en-US)
101
+ - 🇭🇰 繁体中文 (zh-TW)
102
+
103
+ ### 基本使用
104
+
105
+ #### 在组件中使用翻译
106
+
107
+ ```vue
108
+ <script setup lang="uts">
109
+ import { useI18n } from 'tang-ui-x'
110
+
111
+ const { $t, locale, setLocale, availableLocales } = useI18n()
112
+
113
+ // 获取翻译文本
114
+ const confirmText = $t('common.confirm') // "确定"
115
+ const cancelText = $t('common.cancel') // "取消"
116
+
117
+ // 带参数的翻译
118
+ const greeting = $t('common.hello', { name: '张三' }) // "你好,张三"
119
+
120
+ // 切换语言
121
+ function switchLanguage() {
122
+ setLocale('en-US')
123
+ }
124
+ </script>
125
+
126
+ <template>
127
+ <view>
128
+ <text>{{ confirmText }}</text>
129
+ <text>当前语言: {{ locale }}</text>
130
+ <button @click="switchLanguage">Switch to English</button>
131
+ </view>
132
+ </template>
133
+ ```
134
+
135
+ #### useI18n API
136
+
137
+ `useI18n()` 返回以下属性和方法:
138
+
139
+ - `$t(key, params?)` - 翻译函数,获取指定键的翻译文本
140
+ - `key`: 翻译键,格式为 `moduleName.key`(如 `common.confirm`)
141
+ - `params`: 可选的插值参数对象
142
+ - `locale` - 当前语言代码(响应式)
143
+ - `setLocale(code)` - 切换语言函数
144
+ - `availableLocales` - 可用语言列表(响应式)
145
+
146
+ ### 全局语言切换
147
+
148
+ 在应用的任何位置使用全局函数切换语言:
149
+
150
+ ```vue
151
+ <script setup lang="uts">
152
+ import { setLanguage, getCurrentLocale, getAvailableLocales } from 'tang-ui-x'
153
+
154
+ // 切换到英语
155
+ const success = setLanguage('en-US')
156
+ console.log('切换成功:', success) // true
157
+
158
+ // 获取当前语言
159
+ const currentLang = getCurrentLocale() // 'en-US'
160
+
161
+ // 获取所有可用语言
162
+ const languages = getAvailableLocales() // ['zh-CN', 'en-US', 'zh-TW']
163
+ </script>
164
+
165
+ <template>
166
+ <view>
167
+ <button @click="setLanguage('zh-CN')">中文</button>
168
+ <button @click="setLanguage('en-US')">English</button>
169
+ <button @click="setLanguage('zh-TW')">繁體中文</button>
170
+ </view>
171
+ </template>
172
+ ```
173
+
174
+ ### 注册自定义语言包
175
+
176
+ #### 方式一:注册新语言
177
+
178
+ 添加组件库未内置的语言(如日语、韩语等):
179
+
180
+ ```typescript
181
+ import { registerLocale, setLanguage } from 'tang-ui-x'
182
+
183
+ // 注册日语语言包
184
+ registerLocale('ja-JP', {
185
+ common: {
186
+ confirm: '確認',
187
+ cancel: 'キャンセル',
188
+ ok: 'OK',
189
+ close: '閉じる',
190
+ loading: '読み込み中...',
191
+ noData: 'データなし'
192
+ },
193
+ dialog: {
194
+ title: 'ヒント',
195
+ confirmText: '確認',
196
+ cancelText: 'キャンセル'
197
+ },
198
+ empty: {
199
+ title: 'データなし',
200
+ description: '関連コンテンツがありません',
201
+ actionText: '更新'
202
+ },
203
+ searchBar: {
204
+ placeholder: '検索キーワードを入力',
205
+ cancelText: 'キャンセル'
206
+ }
207
+ // ... 其他组件模块
208
+ })
209
+
210
+ // 切换到日语
211
+ setLanguage('ja-JP')
212
+ ```
213
+
214
+ #### 方式二:覆盖默认翻译
215
+
216
+ 只覆盖部分翻译文本,其他保持默认:
217
+
218
+ ```typescript
219
+ import { registerLocale } from 'tang-ui-x'
220
+
221
+ // 自定义简体中文的部分翻译
222
+ registerLocale('zh-CN', {
223
+ common: {
224
+ confirm: '好的', // 覆盖默认的"确定"
225
+ cancel: '算了' // 覆盖默认的"取消"
226
+ // 其他键(ok, close, loading 等)保持默认值
227
+ }
228
+ // 其他模块(dialog, empty 等)保持默认翻译
229
+ })
230
+ ```
231
+
232
+ ### 语言包注册模式
233
+
234
+ Tang UI X 提供了两种语言包注册模式,以满足不同的使用场景:**合并模式(merge)** 和 **替换模式(replace)**。
235
+
236
+ #### 合并模式(默认)
237
+
238
+ 合并模式会将新语言包与现有语言包合并,新内容覆盖旧内容,但未覆盖的内容会被保留。这是 `registerLocale` 函数的默认行为。
239
+
240
+ **使用场景:**
241
+ - 只想覆盖部分翻译文本
242
+ - 添加新的翻译键而不影响现有翻译
243
+ - 逐步自定义组件库的翻译
244
+
245
+ **示例 1:覆盖部分通用文本**
246
+
247
+ ```typescript
248
+ import { registerLocale } from 'tang-ui-x'
249
+
250
+ // 只覆盖 common 模块的部分键
251
+ registerLocale('zh-CN', {
252
+ common: {
253
+ confirm: '好的', // 覆盖默认的"确定"
254
+ cancel: '算了' // 覆盖默认的"取消"
255
+ // ok, close, loading, noData 等其他键保持默认值
256
+ }
257
+ // dialog, empty, searchBar 等其他模块保持默认翻译
258
+ })
259
+
260
+ // 结果:
261
+ // - common.confirm → "好的"(已覆盖)
262
+ // - common.cancel → "算了"(已覆盖)
263
+ // - common.ok → "好的"(保持默认)
264
+ // - common.close → "关闭"(保持默认)
265
+ // - dialog.title → "提示"(保持默认)
266
+ // - empty.title → "暂无数据"(保持默认)
267
+ ```
268
+
269
+ **示例 2:添加新的翻译键**
270
+
271
+ ```typescript
272
+ import { registerLocale } from 'tang-ui-x'
273
+
274
+ // 为现有语言添加新的翻译键
275
+ registerLocale('zh-CN', {
276
+ common: {
277
+ save: '保存', // 新增键
278
+ delete: '删除', // 新增键
279
+ edit: '编辑' // 新增键
280
+ },
281
+ // 添加新的自定义模块
282
+ myComponent: {
283
+ title: '我的组件',
284
+ description: '这是自定义组件'
285
+ }
286
+ })
287
+
288
+ // 原有的所有翻译都会保留,只是新增了这些键
289
+ ```
290
+
291
+ **示例 3:使用 registerLocaleWithMode 显式指定合并模式**
292
+
293
+ ```typescript
294
+ import { registerLocaleWithMode } from 'tang-ui-x'
295
+
296
+ // 显式指定合并模式(与 registerLocale 效果相同)
297
+ registerLocaleWithMode('zh-CN', {
298
+ common: {
299
+ confirm: '好的'
300
+ }
301
+ }, 'merge')
302
+ ```
303
+
304
+ #### 替换模式
305
+
306
+ 替换模式会完全替换现有语言包,不保留任何原有内容。使用此模式时,您需要提供完整的语言包,否则缺失的模块和键将无法使用。
307
+
308
+ **使用场景:**
309
+ - 完全自定义某个语言的所有翻译
310
+ - 从外部 JSON 文件加载完整的语言包
311
+ - 确保语言包的完整性和一致性
312
+
313
+ **方式 1:使用 replaceLocale 函数**
314
+
315
+ ```typescript
316
+ import { replaceLocale } from 'tang-ui-x'
317
+
318
+ // 完全替换简体中文语言包
319
+ replaceLocale('zh-CN', {
320
+ common: {
321
+ confirm: '好的',
322
+ cancel: '取消',
323
+ ok: 'OK',
324
+ close: '关闭',
325
+ loading: '加载中...',
326
+ noData: '暂无数据'
327
+ },
328
+ dialog: {
329
+ title: '提示',
330
+ confirmText: '好的',
331
+ cancelText: '取消'
332
+ },
333
+ empty: {
334
+ title: '暂无数据',
335
+ description: '当前没有相关内容',
336
+ actionText: '刷新试试'
337
+ },
338
+ searchBar: {
339
+ placeholder: '请输入搜索关键词',
340
+ cancelText: '取消'
341
+ }
342
+ // 必须提供所有组件模块,否则缺失的模块将无法使用
343
+ })
344
+
345
+ // 注意:原有的所有翻译都被清除,只保留新提供的内容
346
+ ```
347
+
348
+ **方式 2:使用 registerLocaleWithMode 指定替换模式**
349
+
350
+ ```typescript
351
+ import { registerLocaleWithMode } from 'tang-ui-x'
352
+
353
+ // 使用替换模式注册语言包
354
+ registerLocaleWithMode('zh-CN', {
355
+ common: {
356
+ confirm: '好的',
357
+ cancel: '取消'
358
+ // ... 其他键
359
+ },
360
+ dialog: {
361
+ title: '提示'
362
+ // ... 其他键
363
+ }
364
+ // ... 其他模块
365
+ }, 'replace')
366
+ ```
367
+
368
+ #### 从 JSON 文件注册语言包
369
+
370
+ 您可以将语言包保存为 JSON 文件,然后导入并注册。这种方式特别适合管理大型语言包或从外部系统加载翻译。
371
+
372
+ **JSON 文件格式:**
373
+
374
+ ```json
375
+ // custom-zh-CN.json
376
+ {
377
+ "common": {
378
+ "confirm": "好的",
379
+ "cancel": "取消",
380
+ "ok": "OK",
381
+ "close": "关闭",
382
+ "loading": "加载中...",
383
+ "noData": "暂无数据"
384
+ },
385
+ "dialog": {
386
+ "title": "提示",
387
+ "confirmText": "好的",
388
+ "cancelText": "取消"
389
+ },
390
+ "empty": {
391
+ "title": "暂无数据",
392
+ "description": "当前没有相关内容",
393
+ "actionText": "刷新试试"
394
+ },
395
+ "searchBar": {
396
+ "placeholder": "请输入搜索关键词",
397
+ "cancelText": "取消"
398
+ }
399
+ }
400
+ ```
401
+
402
+ **使用合并模式加载 JSON 文件:**
403
+
404
+ ```typescript
405
+ import { registerLocale } from 'tang-ui-x'
406
+ import customZhCN from './locales/custom-zh-CN.json'
407
+
408
+ // 合并模式:保留未覆盖的默认翻译
409
+ registerLocale('zh-CN', customZhCN)
410
+ ```
411
+
412
+ **使用替换模式加载 JSON 文件:**
413
+
414
+ ```typescript
415
+ import { replaceLocale } from 'tang-ui-x'
416
+ import completeZhCN from './locales/complete-zh-CN.json'
417
+
418
+ // 替换模式:完全替换,不保留默认翻译
419
+ replaceLocale('zh-CN', completeZhCN)
420
+ ```
421
+
422
+ **从多个 JSON 文件组合语言包:**
423
+
424
+ ```typescript
425
+ import { registerLocale } from 'tang-ui-x'
426
+ import commonTranslations from './locales/common.json'
427
+ import dialogTranslations from './locales/dialog.json'
428
+ import emptyTranslations from './locales/empty.json'
429
+
430
+ // 组合多个 JSON 文件
431
+ const customLocale = {
432
+ common: commonTranslations,
433
+ dialog: dialogTranslations,
434
+ empty: emptyTranslations
435
+ }
436
+
437
+ // 注册组合后的语言包
438
+ registerLocale('zh-CN', customLocale)
439
+ ```
440
+
441
+ #### 两种模式的对比
442
+
443
+ | 特性 | 合并模式(merge) | 替换模式(replace) |
444
+ |------|------------------|-------------------|
445
+ | **默认行为** | ✓ 是(registerLocale) | ✗ 否(需显式使用) |
446
+ | **保留原有内容** | ✓ 是 | ✗ 否 |
447
+ | **覆盖指定键** | ✓ 是 | ✓ 是 |
448
+ | **需要完整语言包** | ✗ 否 | ✓ 是 |
449
+ | **适合部分自定义** | ✓ 是 | ✗ 否 |
450
+ | **适合完全自定义** | ✗ 否 | ✓ 是 |
451
+ | **使用函数** | registerLocale<br>registerLocaleWithMode(..., 'merge') | replaceLocale<br>registerLocaleWithMode(..., 'replace') |
452
+
453
+ #### 选择合适的模式
454
+
455
+ **使用合并模式的情况:**
456
+ - ✓ 只想修改少数几个翻译文本
457
+ - ✓ 想要添加新的翻译键
458
+ - ✓ 希望保留组件库的默认翻译作为后备
459
+ - ✓ 逐步自定义翻译,不想一次性提供所有内容
460
+
461
+ **使用替换模式的情况:**
462
+ - ✓ 需要完全控制语言包内容
463
+ - ✓ 从外部系统或 CMS 加载完整的翻译
464
+ - ✓ 确保翻译的完整性和一致性
465
+ - ✓ 不希望依赖组件库的默认翻译
466
+
467
+ #### 完整示例:动态语言包管理
468
+
469
+ ```vue
470
+ <script setup lang="uts">
471
+ import { registerLocale, replaceLocale, registerLocaleWithMode } from 'tang-ui-x'
472
+ import { ref } from 'vue'
473
+
474
+ // 场景 1:部分自定义(合并模式)
475
+ function customizePartial() {
476
+ registerLocale('zh-CN', {
477
+ common: {
478
+ confirm: '好的',
479
+ cancel: '算了'
480
+ }
481
+ })
482
+ console.log('已应用部分自定义翻译(合并模式)')
483
+ }
484
+
485
+ // 场景 2:完全替换(替换模式)
486
+ function replaceComplete() {
487
+ replaceLocale('zh-CN', {
488
+ common: {
489
+ confirm: '确认',
490
+ cancel: '取消',
491
+ ok: 'OK',
492
+ close: '关闭',
493
+ loading: '加载中...',
494
+ noData: '暂无数据'
495
+ },
496
+ dialog: {
497
+ title: '提示',
498
+ confirmText: '确认',
499
+ cancelText: '取消'
500
+ }
501
+ // ... 提供所有必需的模块
502
+ })
503
+ console.log('已完全替换语言包(替换模式)')
504
+ }
505
+
506
+ // 场景 3:从 JSON 文件加载
507
+ async function loadFromJSON() {
508
+ try {
509
+ // 假设从服务器获取语言包
510
+ const response = await fetch('/api/locales/zh-CN.json')
511
+ const localeData = await response.json()
512
+
513
+ // 使用合并模式
514
+ registerLocale('zh-CN', localeData)
515
+
516
+ // 或使用替换模式
517
+ // replaceLocale('zh-CN', localeData)
518
+
519
+ console.log('已从 JSON 加载语言包')
520
+ } catch (error) {
521
+ console.error('加载语言包失败:', error)
522
+ }
523
+ }
524
+
525
+ // 场景 4:显式指定模式
526
+ function explicitMode() {
527
+ // 合并模式
528
+ registerLocaleWithMode('zh-CN', {
529
+ common: { confirm: '好的' }
530
+ }, 'merge')
531
+
532
+ // 替换模式
533
+ registerLocaleWithMode('en-US', {
534
+ common: {
535
+ confirm: 'OK',
536
+ cancel: 'Cancel'
537
+ // ... 完整内容
538
+ }
539
+ }, 'replace')
540
+ }
541
+ </script>
542
+
543
+ <template>
544
+ <view class="locale-manager">
545
+ <button @click="customizePartial">部分自定义(合并)</button>
546
+ <button @click="replaceComplete">完全替换</button>
547
+ <button @click="loadFromJSON">从 JSON 加载</button>
548
+ <button @click="explicitMode">显式指定模式</button>
549
+ </view>
550
+ </template>
551
+ ```
552
+
553
+ #### 注意事项
554
+
555
+ 1. **替换模式需要完整语言包**:使用替换模式时,确保提供所有必需的模块和键,否则缺失的翻译将无法使用。
556
+
557
+ 2. **合并模式的覆盖规则**:在合并模式下,新键会覆盖旧键,但只在提供的模块内生效。未提供的模块完全保留。
558
+
559
+ 3. **JSON 结构验证**:系统会验证注册的语言包结构。如果结构无效(如不是对象、包含非字符串值等),注册会失败并输出错误信息。
560
+
561
+ 4. **模式选择建议**:如果不确定使用哪种模式,建议使用默认的合并模式(`registerLocale`),它更安全且易于使用。
562
+
563
+ ### 语言包结构
564
+
565
+ 语言包采用模块化结构,每个模块对应一组相关的翻译:
566
+
567
+ ```typescript
568
+ {
569
+ // 通用文本模块
570
+ common: {
571
+ confirm: '确定',
572
+ cancel: '取消',
573
+ ok: '好的',
574
+ close: '关闭',
575
+ loading: '加载中...',
576
+ noData: '暂无数据'
577
+ },
578
+
579
+ // 对话框模块
580
+ dialog: {
581
+ title: '提示',
582
+ confirmText: '确定',
583
+ cancelText: '取消'
584
+ },
585
+
586
+ // 空状态模块
587
+ empty: {
588
+ title: '暂无数据',
589
+ description: '当前没有相关内容',
590
+ actionText: '刷新试试'
591
+ },
592
+
593
+ // 搜索框模块
594
+ searchBar: {
595
+ placeholder: '请输入搜索关键词',
596
+ cancelText: '取消'
597
+ }
598
+
599
+ // ... 其他组件模块
600
+ }
601
+ ```
602
+
603
+ ### 翻译键命名规范
604
+
605
+ 翻译键采用 `模块名.键名` 的格式:
606
+
607
+ - `common.confirm` - 通用模块的确定按钮
608
+ - `dialog.title` - 对话框模块的标题
609
+ - `empty.description` - 空状态模块的描述文本
610
+ - `searchBar.placeholder` - 搜索框模块的占位符
611
+
612
+ ### 组件中使用自定义文本
613
+
614
+ 所有支持 i18n 的组件都允许通过 props 覆盖默认翻译:
615
+
616
+ ```vue
617
+ <template>
618
+ <view>
619
+ <!-- 使用默认翻译 -->
620
+ <TEmpty />
621
+
622
+ <!-- 使用自定义文本(优先级高于翻译) -->
623
+ <TEmpty
624
+ title="没有找到结果"
625
+ description="请尝试其他搜索条件"
626
+ actionText="重新搜索"
627
+ />
628
+
629
+ <!-- 使用默认翻译,但会随语言切换自动更新 -->
630
+ <TSearchBar />
631
+
632
+ <!-- 使用自定义占位符 -->
633
+ <TSearchBar placeholder="搜索商品" />
634
+ </view>
635
+ </template>
636
+ ```
637
+
638
+ ### 参数插值
639
+
640
+ 翻译文本支持动态参数替换:
641
+
642
+ ```typescript
643
+ // 定义带占位符的翻译
644
+ registerLocale('zh-CN', {
645
+ common: {
646
+ hello: '你好,{name}',
647
+ itemCount: '共 {count} 个项目',
648
+ welcome: '{user},欢迎回来!'
649
+ }
650
+ })
651
+
652
+ // 使用参数
653
+ const { $t } = useI18n()
654
+
655
+ const greeting = $t('common.hello', { name: '张三' })
656
+ // 结果: "你好,张三"
657
+
658
+ const count = $t('common.itemCount', { count: 5 })
659
+ // 结果: "共 5 个项目"
660
+
661
+ const welcome = $t('common.welcome', { user: 'Admin' })
662
+ // 结果: "Admin,欢迎回来!"
663
+ ```
664
+
665
+ ### 响应式语言切换
666
+
667
+ 语言切换会自动触发所有使用翻译的组件更新:
668
+
669
+ ```vue
670
+ <script setup lang="uts">
671
+ import { useI18n } from 'tang-ui-x'
672
+
673
+ const { $t, locale, setLocale } = useI18n()
674
+
675
+ // 这些文本会随语言切换自动更新
676
+ const title = computed(() => $t('common.confirm'))
677
+ const description = computed(() => $t('empty.description'))
678
+ </script>
679
+
680
+ <template>
681
+ <view>
682
+ <!-- 语言切换后,这些文本会自动更新 -->
683
+ <text>{{ title }}</text>
684
+ <text>{{ description }}</text>
685
+ <text>当前语言: {{ locale }}</text>
686
+
687
+ <!-- 组件内的文本也会自动更新 -->
688
+ <TEmpty />
689
+ <TSearchBar />
690
+
691
+ <button @click="setLocale('en-US')">English</button>
692
+ <button @click="setLocale('zh-CN')">中文</button>
693
+ </view>
694
+ </template>
695
+ ```
696
+
697
+ ### 完整示例:语言切换器
698
+
699
+ 创建一个完整的语言切换组件:
700
+
701
+ ```vue
702
+ <script setup lang="uts">
703
+ import { useI18n, setLanguage } from 'tang-ui-x'
704
+ import { ref } from 'vue'
705
+
706
+ const { locale, availableLocales } = useI18n()
707
+
708
+ // 语言显示名称映射
709
+ const languageNames = {
710
+ 'zh-CN': '简体中文',
711
+ 'en-US': 'English',
712
+ 'zh-TW': '繁體中文'
713
+ }
714
+
715
+ // 切换语言
716
+ function switchLanguage(code: string) {
717
+ const success = setLanguage(code)
718
+ if (success) {
719
+ console.log(`已切换到: ${languageNames[code]}`)
720
+ } else {
721
+ console.error(`切换失败: ${code}`)
722
+ }
723
+ }
724
+ </script>
725
+
726
+ <template>
727
+ <view class="language-switcher">
728
+ <text class="title">选择语言 / Select Language</text>
729
+
730
+ <view class="language-list">
731
+ <view
732
+ v-for="lang in availableLocales"
733
+ :key="lang"
734
+ class="language-item"
735
+ :class="{ active: locale === lang }"
736
+ @click="switchLanguage(lang)"
737
+ >
738
+ <text>{{ languageNames[lang] || lang }}</text>
739
+ <text v-if="locale === lang" class="check">✓</text>
740
+ </view>
741
+ </view>
742
+
743
+ <text class="current">当前语言: {{ languageNames[locale] }}</text>
744
+ </view>
745
+ </template>
746
+
747
+ <style>
748
+ .language-switcher {
749
+ padding: 20px;
750
+ }
751
+
752
+ .title {
753
+ font-size: 18px;
754
+ font-weight: bold;
755
+ margin-bottom: 15px;
756
+ }
757
+
758
+ .language-list {
759
+ border: 1px solid #e0e0e0;
760
+ border-radius: 8px;
761
+ overflow: hidden;
762
+ }
763
+
764
+ .language-item {
765
+ padding: 15px;
766
+ border-bottom: 1px solid #e0e0e0;
767
+ flex-direction: row;
768
+ justify-content: space-between;
769
+ align-items: center;
770
+ }
771
+
772
+ .language-item:last-child {
773
+ border-bottom: none;
774
+ }
775
+
776
+ .language-item.active {
777
+ background-color: #f0f9ff;
778
+ color: #0066cc;
779
+ }
780
+
781
+ .check {
782
+ color: #0066cc;
783
+ font-weight: bold;
784
+ }
785
+
786
+ .current {
787
+ margin-top: 15px;
788
+ color: #666;
789
+ font-size: 14px;
790
+ }
791
+ </style>
792
+ ```
793
+
794
+ ### 组件迁移指南
795
+
796
+ 如果您正在开发自定义组件并希望支持 i18n,请按以下步骤操作:
797
+
798
+ #### 步骤 1: 导入 useI18n
799
+
800
+ ```typescript
801
+ import { useI18n } from 'tang-ui-x'
802
+ ```
803
+
804
+ #### 步骤 2: 在 setup 中获取翻译函数
805
+
806
+ ```typescript
807
+ const { $t } = useI18n()
808
+ ```
809
+
810
+ #### 步骤 3: 替换硬编码文本
811
+
812
+ ```typescript
813
+ // 之前
814
+ const confirmText = '确定'
815
+
816
+ // 之后
817
+ const confirmText = $t('common.confirm')
818
+ ```
819
+
820
+ #### 步骤 4: 支持用户覆盖(推荐)
821
+
822
+ ```typescript
823
+ interface Props {
824
+ title?: string
825
+ description?: string
826
+ }
827
+
828
+ const props = defineProps<Props>()
829
+ const { $t } = useI18n()
830
+
831
+ // 优先使用用户传入的值,否则使用翻译
832
+ const displayTitle = computed(() => props.title || $t('empty.title'))
833
+ const displayDescription = computed(() => props.description || $t('empty.description'))
834
+ ```
835
+
836
+ #### 步骤 5: 更新模板
837
+
838
+ ```vue
839
+ <template>
840
+ <view>
841
+ <!-- 使用计算属性 -->
842
+ <text>{{ displayTitle }}</text>
843
+ <text>{{ displayDescription }}</text>
844
+ </view>
845
+ </template>
846
+ ```
847
+
848
+ #### 完整示例
849
+
850
+ ```vue
851
+ <script setup lang="uts">
852
+ import { useI18n } from 'tang-ui-x'
853
+ import { computed } from 'vue'
854
+
855
+ interface Props {
856
+ title?: string
857
+ description?: string
858
+ actionText?: string
859
+ }
860
+
861
+ const props = defineProps<Props>()
862
+ const { $t } = useI18n()
863
+
864
+ // 优先使用用户传入的值
865
+ const displayTitle = computed(() => props.title || $t('empty.title'))
866
+ const displayDescription = computed(() => props.description || $t('empty.description'))
867
+ const displayActionText = computed(() => props.actionText || $t('empty.actionText'))
868
+
869
+ function handleAction() {
870
+ console.log('Action clicked')
871
+ }
872
+ </script>
873
+
874
+ <template>
875
+ <view class="custom-component">
876
+ <text class="title">{{ displayTitle }}</text>
877
+ <text class="description">{{ displayDescription }}</text>
878
+ <button @click="handleAction">{{ displayActionText }}</button>
879
+ </view>
880
+ </template>
881
+ ```
882
+
883
+ ### API 参考
884
+
885
+ #### useI18n()
886
+
887
+ 组合式 API,用于在组件中使用 i18n 功能。
888
+
889
+ **返回值:**
890
+
891
+ ```typescript
892
+ {
893
+ $t: (key: string, params?: Record<string, any>) => string
894
+ locale: ComputedRef<string>
895
+ availableLocales: ComputedRef<string[]>
896
+ setLocale: (locale: string) => boolean
897
+ }
898
+ ```
899
+
900
+ #### 全局函数
901
+
902
+ ##### setLanguage(locale: string): boolean
903
+
904
+ 切换应用语言。
905
+
906
+ - **参数:**
907
+ - `locale`: 语言代码(如 'zh-CN', 'en-US')
908
+ - **返回值:** 切换是否成功
909
+
910
+ ##### registerLocale(locale: string, messages: object): void
911
+
912
+ 注册或更新语言包。
913
+
914
+ - **参数:**
915
+ - `locale`: 语言代码
916
+ - `messages`: 模块化语言包对象
917
+
918
+ ##### getCurrentLocale(): string
919
+
920
+ 获取当前语言代码。
921
+
922
+ - **返回值:** 当前语言代码
923
+
924
+ ##### getAvailableLocales(): string[]
925
+
926
+ 获取所有可用语言列表。
927
+
928
+ - **返回值:** 语言代码数组
929
+
930
+ ### 常见问题
931
+
932
+ #### Q: 如何添加新语言?
933
+
934
+ A: 使用 `registerLocale` 函数注册新语言包,然后使用 `setLanguage` 切换。
935
+
936
+ ```typescript
937
+ registerLocale('ja-JP', { /* 日语翻译 */ })
938
+ setLanguage('ja-JP')
939
+ ```
940
+
941
+ #### Q: 如何覆盖默认翻译?
942
+
943
+ A: 使用 `registerLocale` 注册同语言代码的语言包,只需包含要覆盖的键。
944
+
945
+ ```typescript
946
+ registerLocale('zh-CN', {
947
+ common: {
948
+ confirm: '好的' // 只覆盖这一个键
949
+ }
950
+ })
951
+ ```
952
+
953
+ #### Q: 组件的文本不随语言切换更新?
954
+
955
+ A: 确保使用 `computed` 包装翻译文本:
956
+
957
+ ```typescript
958
+ // ❌ 错误:不会更新
959
+ const title = $t('common.title')
960
+
961
+ // ✓ 正确:会自动更新
962
+ const title = computed(() => $t('common.title'))
963
+ ```
964
+
965
+ #### Q: 如何在组件外使用翻译?
966
+
967
+ A: 使用全局函数或直接访问 I18nManager:
968
+
969
+ ```typescript
970
+ import { getCurrentLocale } from 'tang-ui-x'
971
+
972
+ const locale = getCurrentLocale()
973
+ ```
974
+
975
+ #### Q: 支持哪些语言?
976
+
977
+ A: 内置支持简体中文、英语、繁体中文。您可以通过 `registerLocale` 添加任何语言。
978
+
979
+ ### 跨平台支持
980
+
981
+ Tang UI X 的国际化系统经过精心设计,确保在所有 uni-app x 支持的平台上都能正常工作。
982
+
983
+ #### 支持的平台
984
+
985
+ i18n 系统已在以下平台测试并验证:
986
+
987
+ - ✅ **Web H5** - 浏览器环境
988
+ - ✅ **微信小程序** - WeChat Mini Program
989
+ - ✅ **支付宝小程序** - Alipay Mini Program
990
+ - ✅ **App (Android/iOS)** - 原生应用
991
+
992
+ #### 跨平台兼容性设计
993
+
994
+ 为了确保在所有平台上的一致性和可靠性,i18n 系统采用了以下技术方案:
995
+
996
+ **1. 静态导入机制**
997
+
998
+ 所有语言包文件使用静态 `import` 语句导入,避免使用动态导入或平台特定 API(如 `import.meta.glob`),确保在小程序等受限环境中也能正常工作。
999
+
1000
+ ```typescript
1001
+ // ✓ 使用静态导入(跨平台兼容)
1002
+ import zhCNCommon from '../locales/zh-CN/common.json'
1003
+ import enUSCommon from '../locales/en-US/common.json'
1004
+
1005
+ // ✗ 避免动态导入(小程序不支持)
1006
+ // const module = await import(`../locales/${locale}/common.json`)
1007
+ ```
1008
+
1009
+ **2. 编译时确定性**
1010
+
1011
+ 所有文件路径在编译时可解析,确保打包工具能够正确处理语言包文件,无论目标平台是什么。
1012
+
1013
+ **3. 同步初始化**
1014
+
1015
+ i18n 系统使用同步初始化方式,避免异步操作可能带来的平台兼容性问题:
1016
+
1017
+ ```typescript
1018
+ // 同步初始化,所有平台都支持
1019
+ import { useI18n } from 'tang-ui-x'
1020
+ const { $t } = useI18n()
1021
+ ```
1022
+
1023
+ **4. JSON 文件处理**
1024
+
1025
+ 语言包使用标准 JSON 格式,确保在所有平台的编译和打包过程中都能被正确处理。
1026
+
1027
+ #### 平台特定注意事项
1028
+
1029
+ **小程序平台**
1030
+
1031
+ - ✓ 语言包会在编译时被打包到小程序包中
1032
+ - ✓ 所有 i18n API 都可以正常使用
1033
+ - ✓ 语言切换会立即生效,无需重启小程序
1034
+
1035
+ **App 平台**
1036
+
1037
+ - ✓ 语言包会被编译到原生应用中
1038
+ - ✓ 支持热更新后的语言切换
1039
+ - ✓ 性能与 Web 平台一致
1040
+
1041
+ **Web H5 平台**
1042
+
1043
+ - ✓ 语言包会被打包到 JavaScript bundle 中
1044
+ - ✓ 支持浏览器缓存优化
1045
+ - ✓ 可以与浏览器语言检测结合使用
1046
+
1047
+ #### 使用建议
1048
+
1049
+ 1. **统一的代码**: 在所有平台使用相同的 i18n 代码,无需平台判断
1050
+ 2. **提前测试**: 在目标平台上测试语言切换功能
1051
+ 3. **包体积优化**: 如果只需要部分语言,可以考虑按需注册语言包
1052
+
1053
+ #### 示例:跨平台语言切换
1054
+
1055
+ 以下代码在所有平台上都能正常工作:
1056
+
1057
+ ```vue
1058
+ <script setup lang="uts">
1059
+ import { useI18n, setLanguage } from 'tang-ui-x'
1060
+
1061
+ const { locale, availableLocales } = useI18n()
1062
+
1063
+ // 这段代码在所有平台都能正常运行
1064
+ function switchLanguage(lang: string) {
1065
+ const success = setLanguage(lang)
1066
+ if (success) {
1067
+ // 在所有平台上,UI 都会立即更新
1068
+ console.log(`语言已切换到: ${lang}`)
1069
+ }
1070
+ }
1071
+ </script>
1072
+
1073
+ <template>
1074
+ <view>
1075
+ <!-- 在所有平台上显示效果一致 -->
1076
+ <button @click="switchLanguage('zh-CN')">中文</button>
1077
+ <button @click="switchLanguage('en-US')">English</button>
1078
+ <button @click="switchLanguage('zh-TW')">繁體中文</button>
1079
+
1080
+ <text>当前语言: {{ locale }}</text>
1081
+ </view>
1082
+ </template>
1083
+ ```
1084
+
1085
+ #### 技术细节
1086
+
1087
+ 如果您对 i18n 系统的跨平台实现感兴趣,以下是关键技术点:
1088
+
1089
+ - **语言包加载器**: 使用静态导入映射表,在编译时确定所有语言包路径
1090
+ - **模块注册表**: 显式维护模块列表,避免运行时文件系统访问
1091
+ - **响应式系统**: 基于 Vue 3 的响应式系统,在所有平台上表现一致
1092
+ - **无外部依赖**: 不依赖任何平台特定的 API 或第三方库
1093
+
91
1094
  ## 📚 组件列表
92
1095
 
93
1096
  ### 基础组件 (5个)