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,117 @@
1
+ # TRadioButton 单选按钮
2
+
3
+ 单选按钮组件,支持单个按钮和选项组两种使用方式。
4
+
5
+ ## 基础用法
6
+
7
+ ```vue
8
+ <template>
9
+ <TRadioButton v-model="value" value="option1">选项一</TRadioButton>
10
+ <TRadioButton v-model="value" value="option2">选项二</TRadioButton>
11
+ <TRadioButton v-model="value" value="option3">选项三</TRadioButton>
12
+ </template>
13
+
14
+ <script setup>
15
+ import { ref } from 'vue'
16
+ const value = ref('option1')
17
+ </script>
18
+ ```
19
+
20
+ ## 选项组模式
21
+
22
+ 通过 `options` 属性传入选项数组,自动渲染多个单选按钮。
23
+
24
+ ```vue
25
+ <template>
26
+ <TRadioButton
27
+ v-model="value"
28
+ :options="[
29
+ { label: '选项一', value: 'option1' },
30
+ { label: '选项二', value: 'option2' },
31
+ { label: '选项三', value: 'option3' }
32
+ ]"
33
+ />
34
+ </template>
35
+ ```
36
+
37
+ ## 禁用状态
38
+
39
+ ```vue
40
+ <TRadioButton v-model="value" value="option1" disabled>禁用选项</TRadioButton>
41
+ ```
42
+
43
+ ## 不同尺寸
44
+
45
+ ```vue
46
+ <TRadioButton v-model="value" value="small" size="small">小尺寸</TRadioButton>
47
+ <TRadioButton v-model="value" value="medium" size="medium">中等尺寸</TRadioButton>
48
+ <TRadioButton v-model="value" value="large" size="large">大尺寸</TRadioButton>
49
+ ```
50
+
51
+ ## 自定义颜色
52
+
53
+ ```vue
54
+ <TRadioButton
55
+ v-model="value"
56
+ value="option1"
57
+ activeColor="#f56c6c"
58
+ inactiveColor="#909399"
59
+ >
60
+ 自定义颜色
61
+ </TRadioButton>
62
+ ```
63
+
64
+ ## Props
65
+
66
+ | 参数 | 说明 | 类型 | 默认值 |
67
+ |------|------|------|--------|
68
+ | modelValue (v-model) | 绑定值 | `string \| number` | - |
69
+ | value | 单选按钮的值 | `string \| number` | - |
70
+ | label | 显示的标签文本 | `string` | - |
71
+ | options | 选项组模式的选项列表 | `FormOption[]` | `[]` |
72
+ | size | 按钮尺寸 | `'small' \| 'medium' \| 'large'` | `'medium'` |
73
+ | activeColor | 激活状态颜色 | `string` | `'#00bba7'` |
74
+ | inactiveColor | 非激活状态颜色 | `string` | `'#666666'` |
75
+ | disabled | 是否禁用 | `boolean` | `false` |
76
+ | checked | 是否选中(仅单个按钮模式) | `boolean` | `false` |
77
+ | name | 原生 name 属性 | `string` | - |
78
+
79
+ ## Events
80
+
81
+ | 事件名 | 说明 | 回调参数 |
82
+ |--------|------|----------|
83
+ | change | 选中值改变时触发 | `(value: string \| number)` |
84
+
85
+ ## FormOption 类型
86
+
87
+ ```typescript
88
+ type FormOption = {
89
+ label: string
90
+ value: string | number
91
+ }
92
+ ```
93
+
94
+ ## 在 TForm 中使用
95
+
96
+ TRadioButton 可以在 TForm 组件中使用:
97
+
98
+ ```vue
99
+ <TForm v-model="formData" :schemas="schemas" />
100
+
101
+ <script setup>
102
+ const schemas = [
103
+ {
104
+ field: 'gender',
105
+ label: '性别',
106
+ component: 'Radio',
107
+ required: true,
108
+ componentProps: {
109
+ options: [
110
+ { label: '男', value: 'male' },
111
+ { label: '女', value: 'female' }
112
+ ]
113
+ }
114
+ }
115
+ ]
116
+ </script>
117
+ ```
@@ -14,6 +14,8 @@ type Props = {
14
14
  options?: FormOption[]
15
15
  /** 单个按钮模式:选项值 */
16
16
  value?: string | number
17
+ /** 单个按钮模式:显示标签 */
18
+ label?: string
17
19
  /** 按钮尺寸 */
18
20
  size?: 'small' | 'medium' | 'large'
19
21
  /** 活动状态颜色 */
@@ -60,14 +62,16 @@ const isOptionsMode = computed(() => props.options && props.options.length > 0)
60
62
  * 单个按钮模式:判断是否被选中
61
63
  */
62
64
  const isChecked = computed(() => {
63
- if (isOptionsMode.value) {
65
+ // 优先使用 model.value 判断
66
+ if (model.value !== undefined && model.value !== null) {
64
67
  return model.value === props.value
65
68
  }
66
- return props.checked || model.value === props.value
69
+ // 如果没有 model.value,使用 checked 属性
70
+ return props.checked === true
67
71
  })
68
72
 
69
73
  /**
70
- 选择 * 处理变化
74
+ * 处理变化
71
75
  */
72
76
  const handleChange = (value: string | number) => {
73
77
  model.value = value
@@ -79,7 +83,9 @@ const handleChange = (value: string | number) => {
79
83
  */
80
84
  const handleClick = () => {
81
85
  if (props.disabled || isOptionsMode.value) return
82
- handleChange(props.value!)
86
+ if (props.value !== undefined) {
87
+ handleChange(props.value)
88
+ }
83
89
  }
84
90
 
85
91
  /**
@@ -99,26 +105,26 @@ const sizeClass = computed(() => {
99
105
  <!-- 选项组模式:使用 options 属性 -->
100
106
  <view v-if="isOptionsMode" class="radio-group" :style="cssVars">
101
107
  <view
102
- v-for="option in props.options"
108
+ v-for="option in options"
103
109
  :key="option.value"
104
110
  class="radio-item"
105
- :class="{ 'radio-disabled': props.disabled }"
106
- @click="!props.disabled && handleChange(option.value)"
111
+ :class="{ 'radio-disabled': disabled }"
112
+ @click="!disabled && handleChange(option.value)"
107
113
  >
108
- <view class="radio-icon" :class="{ active: model === option.value, 'radio-disabled': props.disabled }">
109
- <view v-if="model === option.value" class="radio-dot"></view>
114
+ <view class="radio-icon" :class="{ active: model.value === option.value, 'radio-disabled': disabled }">
115
+ <view v-if="model.value === option.value" class="radio-dot"></view>
110
116
  </view>
111
117
  <text class="radio-label">{{ option.label }}</text>
112
118
  </view>
113
119
  </view>
114
120
 
115
121
  <!-- 单个按钮模式:使用插槽或默认内容 -->
116
- <view v-else class="radio-item" :class="[sizeClass, { 'radio-disabled': props.disabled }]" @click="handleClick">
117
- <view class="radio-icon" :class="{ active: isChecked, 'radio-disabled': props.disabled }">
122
+ <view v-else class="radio-item" :class="[sizeClass, { active: isChecked, 'radio-disabled': disabled }]" :style="cssVars" @tap="handleClick">
123
+ <view class="radio-icon" :class="{ active: isChecked, 'radio-disabled': disabled }">
118
124
  <view v-if="isChecked" class="radio-dot"></view>
119
125
  </view>
120
126
  <slot>
121
- <text v-if="props.value !== undefined" class="radio-label">{{ props.value }}</text>
127
+ <text v-if="label || value !== undefined" class="radio-label">{{ label || value }}</text>
122
128
  </slot>
123
129
  </view>
124
130
  </template>
@@ -126,24 +132,29 @@ const sizeClass = computed(() => {
126
132
  <style lang="scss" scoped>
127
133
  /* 选项组容器 */
128
134
  .radio-group {
135
+ display: flex;
129
136
  flex-direction: column;
130
137
  gap: 16rpx;
131
138
  }
132
139
 
133
140
  /* 单个按钮容器 */
134
141
  .radio-item {
142
+ display: flex;
135
143
  flex-direction: row;
136
144
  align-items: center;
137
145
  gap: 16rpx;
146
+ padding: 8rpx 0;
147
+ cursor: pointer;
148
+ }
138
149
 
139
- &.radio-disabled {
140
- opacity: 0.5;
141
- cursor: not-allowed;
142
- }
150
+ .radio-item.radio-disabled {
151
+ opacity: 0.5;
152
+ cursor: not-allowed;
143
153
  }
144
154
 
145
155
  /* 单选按钮图标 */
146
156
  .radio-icon {
157
+ display: flex;
147
158
  justify-content: center;
148
159
  align-items: center;
149
160
  border-radius: 50%;
@@ -151,14 +162,14 @@ const sizeClass = computed(() => {
151
162
  border-style: solid;
152
163
  border-color: var(--radio-inactive-color);
153
164
  transition: all 0.2s ease;
165
+ }
154
166
 
155
- &.active {
156
- border-color: var(--radio-active-color);
157
- }
167
+ .radio-icon.active {
168
+ border-color: var(--radio-active-color);
169
+ }
158
170
 
159
- &.radio-disabled {
160
- border-color: #dcdfe6;
161
- }
171
+ .radio-icon.radio-disabled {
172
+ border-color: #dcdfe6;
162
173
  }
163
174
 
164
175
  /* 单选点 */
@@ -168,65 +179,59 @@ const sizeClass = computed(() => {
168
179
  }
169
180
 
170
181
  /* 尺寸样式 */
171
- .radio-size-small {
172
- .radio-icon {
173
- width: 28rpx;
174
- height: 28rpx;
175
- }
182
+ .radio-size-small .radio-icon {
183
+ width: 28rpx;
184
+ height: 28rpx;
185
+ }
176
186
 
177
- .radio-dot {
178
- width: 14rpx;
179
- height: 14rpx;
180
- }
187
+ .radio-size-small .radio-dot {
188
+ width: 14rpx;
189
+ height: 14rpx;
190
+ }
181
191
 
182
- .radio-label {
183
- font-size: 24rpx;
184
- }
192
+ .radio-size-small .radio-label {
193
+ font-size: 24rpx;
185
194
  }
186
195
 
187
- .radio-size-medium {
188
- .radio-icon {
189
- width: 36rpx;
190
- height: 36rpx;
191
- }
196
+ .radio-size-medium .radio-icon {
197
+ width: 36rpx;
198
+ height: 36rpx;
199
+ }
192
200
 
193
- .radio-dot {
194
- width: 20rpx;
195
- height: 20rpx;
196
- }
201
+ .radio-size-medium .radio-dot {
202
+ width: 20rpx;
203
+ height: 20rpx;
204
+ }
197
205
 
198
- .radio-label {
199
- font-size: 28rpx;
200
- }
206
+ .radio-size-medium .radio-label {
207
+ font-size: 28rpx;
201
208
  }
202
209
 
203
- .radio-size-large {
204
- .radio-icon {
205
- width: 44rpx;
206
- height: 44rpx;
207
- }
210
+ .radio-size-large .radio-icon {
211
+ width: 44rpx;
212
+ height: 44rpx;
213
+ }
208
214
 
209
- .radio-dot {
210
- width: 26rpx;
211
- height: 26rpx;
212
- }
215
+ .radio-size-large .radio-dot {
216
+ width: 26rpx;
217
+ height: 26rpx;
218
+ }
213
219
 
214
- .radio-label {
215
- font-size: 32rpx;
216
- }
220
+ .radio-size-large .radio-label {
221
+ font-size: 32rpx;
217
222
  }
218
223
 
219
224
  /* 文本标签 */
220
225
  .radio-label {
221
226
  color: var(--radio-inactive-color);
222
227
  transition: color 0.2s ease;
228
+ }
223
229
 
224
- .radio-item.active & {
225
- color: var(--radio-active-color);
226
- }
230
+ .radio-item.active .radio-label {
231
+ color: var(--radio-active-color);
232
+ }
227
233
 
228
- .radio-disabled & {
229
- color: #c0c4cc;
230
- }
234
+ .radio-item.radio-disabled .radio-label {
235
+ color: #c0c4cc;
231
236
  }
232
237
  </style>
@@ -1,6 +1,7 @@
1
1
  <script setup lang="uts">
2
2
  import { ref, computed } from 'vue'
3
3
  import type { TSearchBarProps } from './type.uts'
4
+ import { useI18n } from '../../composables/useI18n.uts'
4
5
 
5
6
  /**
6
7
  * TSearchBar 搜索框组件
@@ -11,13 +12,11 @@ import type { TSearchBarProps } from './type.uts'
11
12
  type Props = Omit<TSearchBarProps, 'modelValue'>
12
13
 
13
14
  const props = withDefaults(defineProps<Props>(), {
14
- placeholder: '请输入搜索关键词',
15
15
  shape: 'square',
16
16
  background: '#f5f5f5',
17
17
  maxlength: -1,
18
18
  clearable: true,
19
19
  showCancel: false,
20
- cancelText: '取消',
21
20
  disabled: false,
22
21
  readonly: false,
23
22
  autoFocus: false,
@@ -40,6 +39,22 @@ const emit = defineEmits<{
40
39
  blur: [event: Event]
41
40
  }>()
42
41
 
42
+ // 使用 i18n
43
+ const { $t } = useI18n()
44
+
45
+ // 优先使用用户传入的值,否则使用翻译
46
+ const displayPlaceholder = computed(() => {
47
+ return props.placeholder !== undefined && props.placeholder !== ''
48
+ ? props.placeholder
49
+ : $t('searchBar.placeholder')
50
+ })
51
+
52
+ const displayCancelText = computed(() => {
53
+ return props.cancelText !== undefined && props.cancelText !== ''
54
+ ? props.cancelText
55
+ : $t('searchBar.cancelText')
56
+ })
57
+
43
58
  // 状态管理
44
59
  const isFocused = ref<boolean>(false)
45
60
  let debounceTimer: number | null = null
@@ -149,7 +164,7 @@ const handleCancel = (): void => {
149
164
  class="t-search-bar__input"
150
165
  type="text"
151
166
  :value="modelValue"
152
- :placeholder="placeholder"
167
+ :placeholder="displayPlaceholder"
153
168
  :maxlength="maxlength"
154
169
  :disabled="disabled"
155
170
  :focus="autoFocus"
@@ -175,7 +190,7 @@ const handleCancel = (): void => {
175
190
  class="t-search-bar__cancel"
176
191
  @click="handleCancel"
177
192
  >
178
- {{ cancelText }}
193
+ {{ displayCancelText }}
179
194
  </text>
180
195
  </view>
181
196
  </template>
@@ -0,0 +1,82 @@
1
+ /**
2
+ * i18n 错误处理工具类
3
+ * 提供统一的错误和警告输出
4
+ */
5
+ export class I18nError {
6
+ /**
7
+ * 警告:语言代码不存在
8
+ * @param locale 语言代码
9
+ */
10
+ static warnLocaleNotFound(locale: string): void {
11
+ console.warn(`[Tang UI I18n] Locale "${locale}" not found, using fallback locale`)
12
+ }
13
+
14
+ /**
15
+ * 警告:翻译键不存在
16
+ * @param key 翻译键
17
+ * @param locale 语言代码
18
+ */
19
+ static warnKeyNotFound(key: string, locale: string): void {
20
+ // 仅在开发环境输出警告
21
+ if (process.env.NODE_ENV === 'development') {
22
+ console.warn(`[Tang UI I18n] Translation key "${key}" not found in locale "${locale}"`)
23
+ }
24
+ }
25
+
26
+ /**
27
+ * 错误:语言包格式无效
28
+ * @param locale 语言代码
29
+ */
30
+ static errorInvalidMessages(locale: string): void {
31
+ console.error(`[Tang UI I18n] Invalid messages format for locale "${locale}"`)
32
+ }
33
+
34
+ /**
35
+ * 警告:参数缺失
36
+ * @param key 翻译键
37
+ * @param param 参数名
38
+ */
39
+ static warnMissingParam(key: string, param: string): void {
40
+ // 仅在开发环境输出警告
41
+ if (process.env.NODE_ENV === 'development') {
42
+ console.warn(`[Tang UI I18n] Missing parameter "${param}" for key "${key}"`)
43
+ }
44
+ }
45
+
46
+ /**
47
+ * 警告:翻译键格式无效
48
+ * @param key 翻译键
49
+ */
50
+ static warnInvalidKey(key: string): void {
51
+ if (process.env.NODE_ENV === 'development') {
52
+ console.warn(`[Tang UI I18n] Invalid translation key format: "${key}". Expected format: "moduleName.key"`)
53
+ }
54
+ }
55
+
56
+ /**
57
+ * 错误:语言包不是有效的对象
58
+ * @param locale 语言代码
59
+ */
60
+ static errorMessagesNotObject(locale: string): void {
61
+ console.error(`[Tang UI I18n] Messages for locale "${locale}" must be a valid object`)
62
+ }
63
+
64
+ /**
65
+ * 错误:模块不是有效的对象
66
+ * @param locale 语言代码
67
+ * @param moduleName 模块名
68
+ */
69
+ static errorModuleNotObject(locale: string, moduleName: string): void {
70
+ console.error(`[Tang UI I18n] Module "${moduleName}" in locale "${locale}" must be a valid object`)
71
+ }
72
+
73
+ /**
74
+ * 错误:键值不是字符串
75
+ * @param locale 语言代码
76
+ * @param moduleName 模块名
77
+ * @param key 键名
78
+ */
79
+ static errorValueNotString(locale: string, moduleName: string, key: string): void {
80
+ console.error(`[Tang UI I18n] Value for key "${key}" in module "${moduleName}" of locale "${locale}" must be a string`)
81
+ }
82
+ }
@@ -0,0 +1,188 @@
1
+ /**
2
+ * i18n 全局导出函数
3
+ * 提供全局访问的便捷方法
4
+ */
5
+
6
+ import { I18nManager } from './manager.uts'
7
+ import type { ModularLocaleMessages } from './types.uts'
8
+
9
+ /**
10
+ * 设置语言(全局)
11
+ * 切换整个应用的显示语言
12
+ *
13
+ * @param locale 语言代码(如 'zh-CN', 'en-US', 'zh-TW')
14
+ * @returns 是否切换成功
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * import { setLanguage } from 'tang-ui-x'
19
+ *
20
+ * // 切换到英语
21
+ * const success = setLanguage('en-US')
22
+ * if (success) {
23
+ * console.log('语言切换成功')
24
+ * } else {
25
+ * console.log('语言切换失败,语言包未注册')
26
+ * }
27
+ * ```
28
+ */
29
+ export function setLanguage(locale: string): boolean {
30
+ const manager = I18nManager.getInstance()
31
+ return manager.setLocale(locale)
32
+ }
33
+
34
+ /**
35
+ * 注册语言包(默认使用合并模式)
36
+ * 添加新语言或覆盖现有语言的翻译
37
+ * 默认使用合并模式,新内容会与现有内容合并,新键覆盖旧键,未覆盖的键保留
38
+ *
39
+ * @param locale 语言代码
40
+ * @param messages 模块化语言包
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * import { registerLocale } from 'tang-ui-x'
45
+ *
46
+ * // 注册日语语言包
47
+ * registerLocale('ja-JP', {
48
+ * common: {
49
+ * confirm: '確認',
50
+ * cancel: 'キャンセル',
51
+ * ok: 'OK'
52
+ * },
53
+ * dialog: {
54
+ * title: 'ヒント',
55
+ * confirmText: '確認',
56
+ * cancelText: 'キャンセル'
57
+ * }
58
+ * })
59
+ *
60
+ * // 覆盖部分中文翻译(合并模式)
61
+ * registerLocale('zh-CN', {
62
+ * common: {
63
+ * confirm: '好的' // 只覆盖这个键,其他保持不变
64
+ * }
65
+ * })
66
+ * ```
67
+ */
68
+ export function registerLocale(locale: string, messages: ModularLocaleMessages): void {
69
+ const manager = I18nManager.getInstance()
70
+ manager.registerMessages(locale, messages)
71
+ }
72
+
73
+ /**
74
+ * 注册语言包(指定模式)
75
+ * 支持合并模式和替换模式
76
+ * - 合并模式:新内容与现有内容合并,新键覆盖旧键,未覆盖的键保留
77
+ * - 替换模式:完全替换现有语言包,不保留任何原有内容
78
+ *
79
+ * @param locale 语言代码
80
+ * @param messages 模块化语言包
81
+ * @param mode 注册模式:'merge' 合并(默认),'replace' 替换
82
+ *
83
+ * @example
84
+ * ```typescript
85
+ * import { registerLocaleWithMode } from 'tang-ui-x'
86
+ *
87
+ * // 使用合并模式(与 registerLocale 相同)
88
+ * registerLocaleWithMode('zh-CN', {
89
+ * common: {
90
+ * confirm: '好的' // 只覆盖这个键,其他保持不变
91
+ * }
92
+ * }, 'merge')
93
+ *
94
+ * // 使用替换模式(完全替换)
95
+ * registerLocaleWithMode('zh-CN', {
96
+ * common: {
97
+ * confirm: '好的',
98
+ * cancel: '取消'
99
+ * }
100
+ * // 必须提供所有模块,否则缺失的模块将为空
101
+ * }, 'replace')
102
+ * ```
103
+ */
104
+ export function registerLocaleWithMode(
105
+ locale: string,
106
+ messages: ModularLocaleMessages,
107
+ mode: 'merge' | 'replace'
108
+ ): void {
109
+ const manager = I18nManager.getInstance()
110
+ manager.registerMessagesWithMode(locale, messages, mode)
111
+ }
112
+
113
+ /**
114
+ * 替换语言包(完全替换)
115
+ * 完全替换指定语言的所有翻译内容,不保留任何原有内容
116
+ * 这是 registerLocaleWithMode(locale, messages, 'replace') 的便捷方法
117
+ *
118
+ * @param locale 语言代码
119
+ * @param messages 模块化语言包
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * import { replaceLocale } from 'tang-ui-x'
124
+ *
125
+ * // 完全替换中文翻译
126
+ * replaceLocale('zh-CN', {
127
+ * common: {
128
+ * confirm: '好的',
129
+ * cancel: '取消',
130
+ * ok: 'OK'
131
+ * },
132
+ * dialog: {
133
+ * title: '提示',
134
+ * confirmText: '好的',
135
+ * cancelText: '取消'
136
+ * }
137
+ * // 必须提供所有模块,否则缺失的模块将为空
138
+ * })
139
+ * ```
140
+ */
141
+ export function replaceLocale(locale: string, messages: ModularLocaleMessages): void {
142
+ const manager = I18nManager.getInstance()
143
+ manager.replaceMessages(locale, messages)
144
+ }
145
+
146
+ /**
147
+ * 获取当前语言
148
+ * 返回当前激活的语言代码
149
+ *
150
+ * @returns 当前语言代码
151
+ *
152
+ * @example
153
+ * ```typescript
154
+ * import { getCurrentLocale } from 'tang-ui-x'
155
+ *
156
+ * const current = getCurrentLocale()
157
+ * console.log('当前语言:', current) // 输出: '当前语言: zh-CN'
158
+ * ```
159
+ */
160
+ export function getCurrentLocale(): string {
161
+ const manager = I18nManager.getInstance()
162
+ return manager.getCurrentLocale()
163
+ }
164
+
165
+ /**
166
+ * 获取可用语言列表
167
+ * 返回所有已注册语言的语言代码数组
168
+ *
169
+ * @returns 可用语言代码数组
170
+ *
171
+ * @example
172
+ * ```typescript
173
+ * import { getAvailableLocales } from 'tang-ui-x'
174
+ *
175
+ * const locales = getAvailableLocales()
176
+ * console.log('可用语言:', locales) // 输出: ['zh-CN', 'en-US', 'zh-TW']
177
+ *
178
+ * // 在语言选择器中使用
179
+ * locales.forEach(locale => {
180
+ * console.log(`语言选项: ${locale}`)
181
+ * })
182
+ * ```
183
+ */
184
+ export function getAvailableLocales(): string[] {
185
+ const manager = I18nManager.getInstance()
186
+ // 注意:这里返回的是计算属性的当前值,而不是响应式引用
187
+ return manager.getAvailableLocales().value
188
+ }