@skyfox2000/webui 1.4.27 → 1.5.0
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/lib/assets/modules/{baseLayout-DiqAUz2h.js → baseLayout-Ck74iWSW.js} +3 -3
- package/lib/assets/modules/{file-upload-CKMd0H11.js → file-upload-CYCbA0Go.js} +1 -1
- package/lib/assets/modules/{index-BpsqYPCw.js → index-C8QXmnsn.js} +1 -1
- package/lib/assets/modules/{index-DvozNhVM.js → index-DGfxQmRW.js} +2 -2
- package/lib/assets/modules/{index-gmbMtZa2.js → index-D_-xtDo9.js} +2 -2
- package/lib/assets/modules/{menuTabs-CvAiSVNW.js → menuTabs-Dx5VAF5K.js} +108 -111
- package/lib/assets/modules/{toolIcon-DWurTPaB.js → toolIcon-BD37QUAw.js} +1 -1
- package/lib/assets/modules/upload-template-DPaurj5G.js +6471 -0
- package/lib/assets/modules/{uploadList-De6KfTAV.js → uploadList-C0zjgyKg.js} +4 -4
- package/lib/es/AceEditor/index.js +3 -3
- package/lib/es/BasicLayout/index.js +2 -2
- package/lib/es/Error403/index.js +1 -1
- package/lib/es/Error404/index.js +1 -1
- package/lib/es/ExcelForm/index.js +5 -5
- package/lib/es/MenuLayout/index.js +2 -2
- package/lib/es/TemplateFile/index.js +4 -4
- package/lib/es/UploadForm/index.js +4 -4
- package/lib/index.d.ts +1 -0
- package/lib/locales/default.d.ts +130 -0
- package/lib/locales/index.d.ts +20 -0
- package/lib/webui.es.js +86 -80
- package/package.json +5 -2
- package/src/components/layout/header/headerExits.vue +8 -4
- package/src/index.ts +2 -0
- package/src/locales/default.ts +204 -0
- package/src/locales/index.ts +290 -0
- package/lib/assets/modules/upload-template-BuaWOXY_.js +0 -2584
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
export const defaultLocale = {
|
|
2
|
+
"webui": {
|
|
3
|
+
// 通用
|
|
4
|
+
"common": {
|
|
5
|
+
"confirm": "确定",
|
|
6
|
+
"cancel": "取消",
|
|
7
|
+
"yes": "是",
|
|
8
|
+
"no": "否",
|
|
9
|
+
"close": "关闭",
|
|
10
|
+
"save": "保存",
|
|
11
|
+
"saveAs": "另存为",
|
|
12
|
+
"delete": "删除",
|
|
13
|
+
"edit": "编辑",
|
|
14
|
+
"add": "新增",
|
|
15
|
+
"upload": "上传",
|
|
16
|
+
"download": "下载",
|
|
17
|
+
"preview": "预览",
|
|
18
|
+
"choose": "选择",
|
|
19
|
+
"placeholder": "请输入{0}"
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
// 布局相关
|
|
23
|
+
"layout": {
|
|
24
|
+
"header": {
|
|
25
|
+
"exitPlatform": "退出平台",
|
|
26
|
+
"exitSystem": "退出系统",
|
|
27
|
+
"confirmExit": "确定退出?",
|
|
28
|
+
"exitConfirmMessage": "是否退出平台,<br />清除用户缓存信息?"
|
|
29
|
+
},
|
|
30
|
+
"menu": {
|
|
31
|
+
// "close" 使用 common 中的定义
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
// 工具面板相关
|
|
36
|
+
"toolpanel": {
|
|
37
|
+
"tool": "工具",
|
|
38
|
+
"searchResult": "搜索结果",
|
|
39
|
+
"list": "列表",
|
|
40
|
+
"listView": "列表视图",
|
|
41
|
+
"grid": "网格",
|
|
42
|
+
"gridView": "网格视图",
|
|
43
|
+
"expand": "展开",
|
|
44
|
+
"collapse": "收起",
|
|
45
|
+
"expandPanel": "展开面板",
|
|
46
|
+
"collapsePanel": "收起面板",
|
|
47
|
+
"searchPlaceholder": "搜索工具..."
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
// 工具栏相关
|
|
51
|
+
"toolbar": {
|
|
52
|
+
// "add" 使用 common 中的定义
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
// 列表操作相关
|
|
56
|
+
"listOperate": {
|
|
57
|
+
"confirmDelete": "是否删除此记录?"
|
|
58
|
+
// "yes" 和 "no" 使用 common 中的定义
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
// 对话框相关
|
|
62
|
+
"dialog": {
|
|
63
|
+
"fileUpload": "文件上传",
|
|
64
|
+
"templateFileManager": "模板文件管理",
|
|
65
|
+
"batchImportUser": "批量导入用户",
|
|
66
|
+
// "close", "cancel", "saveAs", "save" 使用 common 中的定义
|
|
67
|
+
"uploadFile": "上传文件"
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
// 表格操作相关
|
|
71
|
+
"tableOperate": {
|
|
72
|
+
"confirmDelete": "是否删除此记录?"
|
|
73
|
+
// "edit", "delete", "yes" 和 "no" 使用 common 中的定义
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
// 抽屉相关
|
|
77
|
+
"drawer": {
|
|
78
|
+
// "cancel", "saveAs", "save" 使用 common 中的定义
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
// 上传组件相关
|
|
82
|
+
"upload": {
|
|
83
|
+
"unsupportedFileType": "文件类型不支持",
|
|
84
|
+
"fileSizeExceedsLimit": "文件大小超过 {0}MB 限制",
|
|
85
|
+
"maxFilesReached": "最多上传 {0} 个文件",
|
|
86
|
+
"deleteSuccess": "删除文件成功!",
|
|
87
|
+
"onlineOffline": "上线或下线",
|
|
88
|
+
"online": "上线",
|
|
89
|
+
"offline": "下线",
|
|
90
|
+
"uploading": "上传中",
|
|
91
|
+
"uploadComplete": "上传完成",
|
|
92
|
+
"uploadFailed": "上传失败",
|
|
93
|
+
"onlineStatus": "在线",
|
|
94
|
+
"offlineStatus": "已下线",
|
|
95
|
+
"pendingUpload": "待上传",
|
|
96
|
+
"chooseFile": "选择文件",
|
|
97
|
+
"chooseDirectory": "选择目录",
|
|
98
|
+
"download": "下载",
|
|
99
|
+
"preview": "预览",
|
|
100
|
+
"confirmDeleteFile": "确定删除该文件吗?",
|
|
101
|
+
"delete": "删除",
|
|
102
|
+
"fileMustBe": "文件必须为 {0}",
|
|
103
|
+
"singleFileMax": "单文件最大 {0}MB",
|
|
104
|
+
"maxFiles": "最多 {0} 个文件"
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
// 图片上传相关
|
|
108
|
+
"imageUpload": {
|
|
109
|
+
"loadImageFailed": "加载图片失败:"
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
// 时间选择器相关
|
|
113
|
+
"timePicker": {
|
|
114
|
+
// "placeholder" 使用 common 中的定义
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
// 选择器相关
|
|
118
|
+
"select": {
|
|
119
|
+
// "placeholder" 使用 common 中的定义
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
// 开关组件相关
|
|
123
|
+
"switch": {
|
|
124
|
+
"error": "Switch组件必须有且只有两个选项"
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
// 文本域相关
|
|
128
|
+
"textarea": {
|
|
129
|
+
// "placeholder" 使用 common 中的定义
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
// 代码编辑器相关
|
|
133
|
+
"aceEditor": {
|
|
134
|
+
"title": "代码编辑器"
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
// 穿梭框相关
|
|
138
|
+
"transfer": {
|
|
139
|
+
// "placeholder" 使用 common 中的定义
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
// 级联选择器相关
|
|
143
|
+
"cascader": {
|
|
144
|
+
// "placeholder" 使用 common 中的定义
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
// 日期选择器相关
|
|
148
|
+
"datePicker": {
|
|
149
|
+
// "placeholder" 使用 common 中的定义
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
// 属性编辑器相关
|
|
153
|
+
"propEditor": {
|
|
154
|
+
"configName": "配置名",
|
|
155
|
+
"configValue": "请输入配置值",
|
|
156
|
+
"inputConfigValue": "请输入{0}"
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
// 输入框相关
|
|
160
|
+
"input": {
|
|
161
|
+
// "placeholder" 使用 common 中的定义
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
// 自动完成相关
|
|
165
|
+
"autoComplete": {
|
|
166
|
+
"placeholder": "{0}并选择" // 使用 common.placeholder 并添加特定文本
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
// 单选框相关
|
|
170
|
+
"radio": {
|
|
171
|
+
"noData": "无数据",
|
|
172
|
+
"all": "全部"
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
// 树选择器相关
|
|
176
|
+
"treeSelect": {
|
|
177
|
+
// "placeholder" 使用 common 中的定义
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
// 错误页面相关
|
|
181
|
+
"error": {
|
|
182
|
+
"pageNotFound": "页面不存在或者没有权限访问页面!",
|
|
183
|
+
"forbidden": "您没有权限访问当前页面!"
|
|
184
|
+
},
|
|
185
|
+
|
|
186
|
+
// Excel导入相关
|
|
187
|
+
"excelForm": {
|
|
188
|
+
"chooseExcelFile": "请选择Excel文件",
|
|
189
|
+
"chooseCsvFile": "请选择CSV文件",
|
|
190
|
+
"chooseExcelOrCsvFile": "请选择Excel或CSV文件",
|
|
191
|
+
"validateDataRules": "待验证数据规则",
|
|
192
|
+
"validateDuplicateData": "待验证重复数据",
|
|
193
|
+
"uploadAddressNotConfigured": "未配置文件上传地址!",
|
|
194
|
+
"fileProcessingFailed": "文件处理失败,请检查文件格式!",
|
|
195
|
+
"dataSaveFailed": "数据保存失败,请稍后再试!",
|
|
196
|
+
"validationError": "验证异常",
|
|
197
|
+
"onlyExcelAllowed": "只能上传Excel文件!",
|
|
198
|
+
"onlyCsvAllowed": "只能上传CSV文件!",
|
|
199
|
+
"onlyExcelOrCsvAllowed": "只能上传Excel文件或CSV文件!",
|
|
200
|
+
"csvProcessingFailed": "CSV文件处理失败",
|
|
201
|
+
"followingMustBeUnique": "以下{0}必须唯一"
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import { createI18n } from "vue-i18n";
|
|
2
|
+
import { defaultLocale } from "./default";
|
|
3
|
+
|
|
4
|
+
// 定义语言包类型
|
|
5
|
+
export interface LocaleMessages {
|
|
6
|
+
[key: string]: any;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 深度合并两个对象
|
|
11
|
+
* @param target 目标对象
|
|
12
|
+
* @param source 源对象
|
|
13
|
+
* @returns 合并后的对象
|
|
14
|
+
*/
|
|
15
|
+
function deepMerge(target: any, source: any): any {
|
|
16
|
+
const result = { ...target };
|
|
17
|
+
|
|
18
|
+
for (const key in source) {
|
|
19
|
+
if (source.hasOwnProperty(key)) {
|
|
20
|
+
if (typeof source[key] === 'object' && source[key] !== null && !Array.isArray(source[key])) {
|
|
21
|
+
// 如果源值是对象且不是数组,则递归合并
|
|
22
|
+
result[key] = deepMerge(result[key] || {}, source[key]);
|
|
23
|
+
} else {
|
|
24
|
+
// 否则直接赋值
|
|
25
|
+
result[key] = source[key];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 语言显示名称映射(从 public/locales/languages.json 加载)
|
|
34
|
+
export let LOCALE_DISPLAY_NAMES: Record<string, string> = {};
|
|
35
|
+
|
|
36
|
+
// 支持的语言包映射
|
|
37
|
+
const loadedMessages: Record<string, LocaleMessages> = {
|
|
38
|
+
"zh-CN": deepMerge({}, defaultLocale)
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// 创建i18n实例
|
|
42
|
+
export const i18n = createI18n({
|
|
43
|
+
legacy: false, // 使用Composition API模式
|
|
44
|
+
locale: "zh-CN", // 默认语言
|
|
45
|
+
fallbackLocale: "en-US", // 回退语言
|
|
46
|
+
messages: loadedMessages,
|
|
47
|
+
silentTranslationWarn: true, // 禁用翻译警告
|
|
48
|
+
fallbackWarn: false, // 禁用回退警告
|
|
49
|
+
missingWarn: false, // 禁用缺失警告
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 从云端加载可用语言列表
|
|
54
|
+
*/
|
|
55
|
+
async function loadAvailableLanguages(): Promise<void> {
|
|
56
|
+
try {
|
|
57
|
+
const response = await fetch('/locales/languages.json');
|
|
58
|
+
if (!response.ok) {
|
|
59
|
+
throw new Error('Failed to load available languages');
|
|
60
|
+
}
|
|
61
|
+
const languages = await response.json();
|
|
62
|
+
LOCALE_DISPLAY_NAMES = languages;
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error('Error loading available languages:', error);
|
|
65
|
+
throw error; // 抛出错误,让调用方处理
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 从云端加载语言包
|
|
71
|
+
*/
|
|
72
|
+
async function loadLocaleFromCloud(locale: string): Promise<LocaleMessages> {
|
|
73
|
+
try {
|
|
74
|
+
const response = await fetch(`/locales/${locale}.json`);
|
|
75
|
+
if (!response.ok) {
|
|
76
|
+
throw new Error(`Failed to load locale ${locale}`);
|
|
77
|
+
}
|
|
78
|
+
const cloudMessages = await response.json();
|
|
79
|
+
|
|
80
|
+
// 使用深度合并更新语言包
|
|
81
|
+
if (loadedMessages[locale]) {
|
|
82
|
+
return deepMerge(loadedMessages[locale], cloudMessages);
|
|
83
|
+
}
|
|
84
|
+
return cloudMessages;
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.error(`Error loading locale ${locale}:`, error);
|
|
87
|
+
// 如果加载失败,返回现有语言包或空对象
|
|
88
|
+
return loadedMessages[locale] || {};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 从localStorage获取缓存的语言包
|
|
94
|
+
*/
|
|
95
|
+
function getCachedLocale(locale: string): LocaleMessages | null {
|
|
96
|
+
try {
|
|
97
|
+
const cached = localStorage.getItem(`locale_cache_${locale}`);
|
|
98
|
+
if (cached) {
|
|
99
|
+
const data = JSON.parse(cached);
|
|
100
|
+
return data.messages;
|
|
101
|
+
}
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.error(`Error reading cached locale ${locale}:`, error);
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* 缓存语言包到localStorage
|
|
110
|
+
*/
|
|
111
|
+
function cacheLocale(locale: string, messages: LocaleMessages): void {
|
|
112
|
+
try {
|
|
113
|
+
const data = {
|
|
114
|
+
messages,
|
|
115
|
+
timestamp: Date.now()
|
|
116
|
+
};
|
|
117
|
+
localStorage.setItem(`locale_cache_${locale}`, JSON.stringify(data));
|
|
118
|
+
} catch (error) {
|
|
119
|
+
console.error(`Error caching locale ${locale}:`, error);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* 获取所有可用语言代码
|
|
125
|
+
*/
|
|
126
|
+
export function getAvailableLocales(): string[] {
|
|
127
|
+
return Object.keys(LOCALE_DISPLAY_NAMES);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* 添加新的语言(预留扩展接口)
|
|
132
|
+
*/
|
|
133
|
+
export function addLocaleOption(locale: string, displayName: string): void {
|
|
134
|
+
LOCALE_DISPLAY_NAMES[locale] = displayName;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* 为插件提供的接口:修改指定语言的内容
|
|
139
|
+
* @param locale 语言代码
|
|
140
|
+
* @param key 翻译键
|
|
141
|
+
* @param value 翻译值
|
|
142
|
+
*/
|
|
143
|
+
export function setTranslation(
|
|
144
|
+
locale: string,
|
|
145
|
+
key: string,
|
|
146
|
+
value: string
|
|
147
|
+
): void {
|
|
148
|
+
if (!loadedMessages[locale]) {
|
|
149
|
+
loadedMessages[locale] = {};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// 将键转换为嵌套对象结构
|
|
153
|
+
const keyParts = key.split('.');
|
|
154
|
+
const nestedObj: any = {};
|
|
155
|
+
let current = nestedObj;
|
|
156
|
+
|
|
157
|
+
for (let i = 0; i < keyParts.length - 1; i++) {
|
|
158
|
+
current[keyParts[i]] = {};
|
|
159
|
+
current = current[keyParts[i]];
|
|
160
|
+
}
|
|
161
|
+
current[keyParts[keyParts.length - 1]] = value;
|
|
162
|
+
|
|
163
|
+
// 使用深度合并更新语言包
|
|
164
|
+
loadedMessages[locale] = deepMerge(loadedMessages[locale], nestedObj);
|
|
165
|
+
i18n.global.setLocaleMessage(locale, loadedMessages[locale]);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* 为插件提供的接口:批量修改指定语言的内容
|
|
170
|
+
* @param locale 语言代码
|
|
171
|
+
* @param translations 翻译对象
|
|
172
|
+
*/
|
|
173
|
+
export function setTranslations(
|
|
174
|
+
locale: string,
|
|
175
|
+
translations: Record<string, any>
|
|
176
|
+
): void {
|
|
177
|
+
if (!loadedMessages[locale]) {
|
|
178
|
+
loadedMessages[locale] = {};
|
|
179
|
+
}
|
|
180
|
+
// 使用深度合并而不是简单的 Object.assign
|
|
181
|
+
loadedMessages[locale] = deepMerge(loadedMessages[locale], translations);
|
|
182
|
+
i18n.global.setLocaleMessage(locale, loadedMessages[locale]);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* 为插件提供的接口:获取当前i18n实例的messages
|
|
187
|
+
* @param locale 语言代码(可选,不传则获取所有语言)
|
|
188
|
+
*/
|
|
189
|
+
export function getI18nMessages(
|
|
190
|
+
locale?: string
|
|
191
|
+
): Record<string, LocaleMessages> | LocaleMessages {
|
|
192
|
+
if (locale) {
|
|
193
|
+
return loadedMessages[locale] || {};
|
|
194
|
+
}
|
|
195
|
+
return loadedMessages;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* 加载语言包 - 使用缓存优先策略
|
|
200
|
+
*/
|
|
201
|
+
async function loadLocale(locale: string): Promise<void> {
|
|
202
|
+
// 优先使用缓存
|
|
203
|
+
const cachedMessages = getCachedLocale(locale);
|
|
204
|
+
if (cachedMessages) {
|
|
205
|
+
loadedMessages[locale] = cachedMessages;
|
|
206
|
+
i18n.global.setLocaleMessage(locale, cachedMessages);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// 异步后台更新:无论是否有缓存,都后台检查更新
|
|
210
|
+
setTimeout(async () => {
|
|
211
|
+
try {
|
|
212
|
+
// 后台异步加载最新语言包并直接更新缓存和内存
|
|
213
|
+
const freshMessages = await loadLocaleFromCloud(locale);
|
|
214
|
+
loadedMessages[locale] = freshMessages;
|
|
215
|
+
i18n.global.setLocaleMessage(locale, freshMessages);
|
|
216
|
+
cacheLocale(locale, freshMessages);
|
|
217
|
+
} catch (error) {
|
|
218
|
+
console.error("[Locale] Background update failed:", error);
|
|
219
|
+
}
|
|
220
|
+
}, 0); // 立即执行,不阻塞界面
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* 初始化语言 - 使用缓存优先+异步后台加载策略
|
|
225
|
+
*/
|
|
226
|
+
export async function initLang(): Promise<void> {
|
|
227
|
+
try {
|
|
228
|
+
// 先加载可用语言列表
|
|
229
|
+
await loadAvailableLanguages();
|
|
230
|
+
} catch (error) {
|
|
231
|
+
console.error('Failed to load available languages:', error);
|
|
232
|
+
// 如果加载语言列表失败,抛出错误让调用方处理
|
|
233
|
+
throw error;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const savedLocale = localStorage.getItem("language") || "zh-CN";
|
|
237
|
+
|
|
238
|
+
// 设置当前语言
|
|
239
|
+
i18n.global.locale.value = savedLocale;
|
|
240
|
+
localStorage.setItem("language", savedLocale);
|
|
241
|
+
|
|
242
|
+
// 统一调用loadLocale处理缓存,不加载,由后面代码
|
|
243
|
+
await loadLocale(savedLocale);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* 设置语言
|
|
248
|
+
*/
|
|
249
|
+
export async function setLang(locale: string): Promise<void> {
|
|
250
|
+
// 加载语言包
|
|
251
|
+
await loadLocale(locale);
|
|
252
|
+
|
|
253
|
+
// 设置当前语言
|
|
254
|
+
i18n.global.locale.value = locale;
|
|
255
|
+
localStorage.setItem("language", locale);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* 获取当前语言
|
|
260
|
+
*/
|
|
261
|
+
export function getLang(type: "code" | "display" = "code"): string {
|
|
262
|
+
const locale = i18n.global.locale.value;
|
|
263
|
+
return type === "display" ? LOCALE_DISPLAY_NAMES[locale] : locale;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* 获取可用语言列表(用于下拉选择)
|
|
268
|
+
*/
|
|
269
|
+
export function getAvailableLanguages(): Array<{ label: string; key: string }> {
|
|
270
|
+
return Object.keys(LOCALE_DISPLAY_NAMES).map((locale) => ({
|
|
271
|
+
label: LOCALE_DISPLAY_NAMES[locale],
|
|
272
|
+
key: locale,
|
|
273
|
+
}));
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// 翻译函数 - 优化回退逻辑
|
|
277
|
+
export function $t(key: string, params?: Record<string, any>): string {
|
|
278
|
+
try {
|
|
279
|
+
// @ts-ignore
|
|
280
|
+
const result = i18n.global.t(key, params);
|
|
281
|
+
// 如果返回的是翻译键本身,说明没有找到翻译,返回空字符串让fallback生效
|
|
282
|
+
return result;
|
|
283
|
+
} catch (error) {
|
|
284
|
+
// 如果翻译出错,返回空字符串让fallback生效
|
|
285
|
+
return key;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// 默认导出 i18n 实例
|
|
290
|
+
export default i18n;
|