gc_i18n 1.4.6 → 1.4.7

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.
@@ -2,48 +2,28 @@
2
2
 
3
3
  <cite>
4
4
  **本文档中引用的文件**
5
- - [packages/index.js](file://packages/index.js) - *核心I18n类完全重写*
6
- - [packages/libs/service.js](file://packages/libs/service.js) - *核心服务逻辑变更*
7
- - [packages/libs/utils.js](file://packages/libs/utils.js) - *工具函数库*
8
- - [packages/libs/i18nUtils.ts](file://packages/libs/i18nUtils.ts) - *翻译键提取工具*
9
- - [packages/components/earth.js](file://packages/components/earth.js) - *地球组件*
5
+ - [packages/index.js](file://packages/index.js) - *核心I18n类实现*
10
6
  - [packages/libs/textEditMode.js](file://packages/libs/textEditMode.js) - *文本编辑模式模块*
11
7
  - [packages/libs/textEditMode.css](file://packages/libs/textEditMode.css) - *文本编辑模式样式*
12
8
  - [packages/swal.css](file://packages/swal.css) - *SweetAlert2样式集成*
13
- - [lib/gc_i18n.css](file://lib/gc_i18n.css) - *样式文件*
9
+ - [packages/libs/service.js](file://packages/libs/service.js) - *HTTP服务封装*
14
10
  - [lib/gc_i18n.es.js](file://lib/gc_i18n.es.js) - *ES模块构建*
15
11
  - [lib/gc_i18n.umd.js](file://lib/gc_i18n.umd.js) - *UMD构建*
16
12
  - [lang/index.js](file://lang/index.js) - *语言包入口*
17
13
  - [lang/index.json](file://lang/index.json) - *语言资源文件*
18
- - [src/main.js](file://src/main.js) - *应用入口*
19
- - [src/App.vue](file://src/App.vue) - *根组件*
20
14
  - [package.json](file://package.json) - *项目配置*
21
15
  - [vite.config.js](file://vite.config.js) - *构建配置*
22
16
  </cite>
23
17
 
24
18
  ## 更新摘要
25
19
  **变更内容**
26
- - 核心I18n类完全重写,引入Vue 2/3兼容层和createI18nCompat函数
27
- - **新增单元代码支持功能**,在配置系统中引入unitCode属性,默认值为GREENCLOUD,增强了翻译数据结构和处理能力
28
- - **新增中文语言模式兼容性支持**,通过normalizeLocale方法支持zh-CN和zh-TW的自动规范化
29
- - **内部代码重构改进**,axios别名从Ve改为je,多个函数重命名等
30
- - 支持自动版本检测和适配,统一Vue 2和Vue 3的API差异
31
- - 增强路由守卫机制,支持动态组件加载和翻译键收集
32
- - 重构翻译查找机制,支持路由特定键和通用键的优先级处理
33
- - **新增翻译函数默认值参数支持**,当翻译不存在时返回默认值
34
- - **新增全局键集合跟踪机制**,通过__I18N_ALL_KEYS__全局变量记录所有调用过的键
35
- - **增强文本编辑模式核心功能**,改进键值映射和查找机制
36
- - 新增JWT令牌生成和管理功能
37
- - 完善缓存策略,支持本地存储和远程增量更新
38
- - **新增RTL方向管理功能**,支持从右到左语言的自动检测和文档方向切换
39
- - **新增文本编辑模式功能**,支持可视化翻译编辑和快捷键绑定
40
- - **新增editKeyboard配置选项**,允许自定义文本编辑模式快捷键
41
- - **集成$keyValueMap映射机制**,为文本编辑模式提供键值映射支持
42
- - **HTTP客户端现代化**,使用独立axios实例避免全局污染
43
- - **安全机制改进**,新增JWT令牌验证和权限控制
44
- - **SweetAlert2集成优化**,改进变量命名和代码组织结构,提升可读性和维护性
45
- - **新增智能语言列表缓存机制**,通过getLanguagesWithCache提供缓存和自动失效功能
46
- - **新增postMessage事件处理功能**,扩展了原有的updateLanguage事件处理机制,允许外部应用程序通过postMessage事件编程关闭模态框
20
+ - 版本从1.4.5升级到1.4.6,核心库实现重大重构
21
+ - 新增Axios请求拦截器,自动为所有HTTP请求添加Bearer令牌
22
+ - SweetAlert2集成得到改进,移除了复杂的模态框创建系统
23
+ - 简化为直接的SweetAlert2集成,提升可维护性
24
+ - 优化了事件通知机制,改进closeModal事件处理
25
+ - 增强了文本编辑模式的SweetAlert2集成
26
+ - 改进了HTTP客户端的集中式认证管理
47
27
 
48
28
  ## 目录
49
29
  1. [项目结构分析](#项目结构分析)
@@ -52,44 +32,33 @@
52
32
  4. [公共API接口文档](#公共api接口文档)
53
33
  5. [RTL方向管理功能](#rtl方向管理功能)
54
34
  6. [中文语言模式兼容性](#中文语言模式兼容性)
55
- 7. [单元代码支持功能](#单元代码支持功能)
56
- 8. [文本编辑模式功能](#文本编辑模式功能)
57
- 9. [非组件环境使用](#非组件环境使用)
58
- 10. [样式文件作用](#样式文件作用)
35
+ 7. [文本编辑模式功能](#文本编辑模式功能)
36
+ 8. [非组件环境使用](#非组件环境使用)
37
+ 9. [样式文件作用](#样式文件作用)
59
38
 
60
39
  ## 项目结构分析
61
40
 
62
41
  ```mermaid
63
42
  graph TB
64
43
  subgraph "核心模块"
65
- packages[index.js<br/>service.js<br/>utils.js<br/>i18nUtils.ts<br/>textEditMode.js]
44
+ packages[index.js<br/>textEditMode.js<br/>textEditMode.css<br/>swal.css<br/>service.js]
66
45
  end
67
46
  subgraph "构建产物"
68
47
  lib[lib]
69
48
  gc_i18n_es[gc_i18n.es.js]
70
49
  gc_i18n_umd[gc_i18n.umd.js]
71
- gc_i18n_css[gc_i18n.css]
72
- text_edit_css[textEditMode.css]
73
- swal_css[swal.css]
74
50
  end
75
51
  subgraph "语言资源"
76
52
  lang[index.js<br/>index.json]
77
53
  end
78
- subgraph "示例应用"
79
- src[src]
80
- main_js[main.js]
81
- app_vue[App.vue]
82
- end
83
54
  subgraph "依赖管理"
84
55
  package_json[package.json]
85
56
  vite_config[vite.config.js]
86
57
  dependencies[依赖配置]
87
- devDependencies[开发依赖]
88
58
  end
89
59
  packages --> lib
90
60
  lib --> packages
91
61
  lang --> packages
92
- src --> packages
93
62
  package_json --> dependencies
94
63
  vite_config --> dependencies
95
64
  ```
@@ -97,6 +66,7 @@ vite_config --> dependencies
97
66
  **图示来源**
98
67
  - [packages/index.js](file://packages/index.js)
99
68
  - [packages/libs/textEditMode.js](file://packages/libs/textEditMode.js)
69
+ - [packages/libs/service.js](file://packages/libs/service.js)
100
70
  - [lib/gc_i18n.es.js](file://lib/gc_i18n.es.js)
101
71
  - [lang/index.js](file://lang/index.js)
102
72
  - [package.json](file://package.json)
@@ -109,193 +79,27 @@ vite_config --> dependencies
109
79
 
110
80
  ## 核心逻辑实现
111
81
 
112
- ### Vue 2/3兼容层实现
113
-
114
- 核心I18n类引入了createI18nCompat函数来处理Vue 2和Vue 3的API差异:
115
-
116
- ```javascript
117
- function createI18nCompat(options) {
118
- if (vueI18n.createI18n) {
119
- return vueI18n.createI18n(options);
120
- }
121
- const VueI18n = vueI18n.default || vueI18n.VueI18n;
122
- // Vue 2 下必须先 Vue.use(VueI18n),否则 new VueI18n() 会报 config undefined
123
- const Vue = options.vue;
124
- if (Vue) {
125
- Vue.use(VueI18n);
126
- }
127
- const { vue, ...rest } = options;
128
- const opts = {
129
- locale: options.locale,
130
- messages: options.messages || {},
131
- silentTranslationWarn: options.silentTranslationWarn,
132
- silentFallbackWarn: options.silentFallbackWarn,
133
- missingWarn: options.missingWarn,
134
- fallbackWarn: options.fallbackWarn,
135
- ...rest
136
- };
137
- const instance = new VueI18n(opts);
138
- instance.global = {
139
- t: instance.t.bind(instance),
140
- setLocaleMessage: instance.setLocaleMessage.bind(instance),
141
- locale: {
142
- get value() {
143
- return instance.locale;
144
- },
145
- set value(v) {
146
- instance.locale = v;
147
- }
148
- }
149
- };
150
- return instance;
151
- }
152
- ```
153
-
154
- ### 语言切换机制
155
-
156
- 语言切换通过增强的翻译查找机制实现,支持路由特定键和通用键的优先级处理:
157
-
158
- ```javascript
159
- globalThis.$t = this.i18n.global.t = (key, comment) => {
160
- // 记录本次调用的原始 key(字符串场景)
161
- if (typeof key === "string") {
162
- this.translationKeySet.add(key);
163
- }
164
- // Vue Router 3 (Vue2) currentRoute 为普通对象,Vue Router 4 (Vue3) 为 ref
165
- const route = this.router?.currentRoute;
166
- const routeName = (route?.value ?? route)?.name;
167
- if (routeName) {
168
- const routerKey = `${routeName}.${key}`;
169
- const routeTranslation = originalT(routerKey);
170
- if (routeTranslation !== routerKey && !_.isEmpty(routeTranslation)) {
171
- return routeTranslation;
172
- } else {
173
- const commonKey = `common.${key}`;
174
- const commonTranslation = originalT(commonKey);
175
- if (commonTranslation !== commonKey) {
176
- return commonTranslation;
177
- } else {
178
- return originalT(key, comment);
179
- }
180
- }
181
- } else {
182
- return originalT(key, comment);
183
- }
184
- };
185
- ```
186
-
187
- ### 路由守卫机制
82
+ ### Axios请求拦截器集成
188
83
 
189
- 增强的路由守卫机制支持动态组件加载和翻译键的实时收集:
84
+ **更新** 核心I18n类现在集成了Axios请求拦截器,实现了集中式的认证管理:
190
85
 
191
86
  ```javascript
192
- initRouterGuards() {
193
- if (I18n._routerGuardRegistered) return;
194
- I18n._routerGuardRegistered = true;
195
- this.router.beforeEach(async (to, from, next) => {
196
- const { language, token } = to.query;
197
- this.configInstance && this.configInstance.closeModal();
198
- this.name = to.name;
199
- // 新路由开始前重置本次页面已收集到的 key
200
- this.translationKeySet = new Set();
201
- token && this.setToken(token);
202
-
203
- if (this.loadI18n) {
204
- await this.setLanguage(language || this.locale);
205
- }
206
- this.loadI18n = false;
207
-
208
- // 动态加载异步组件并提取翻译键
209
- if (to.matched.length > 0) {
210
- try {
211
- const allTranslationKeys = [];
212
- for (const match of to.matched) {
213
- let component = match.components?.default;
214
- if (
215
- typeof component === "function" &&
216
- component.toString().includes("import(")
217
- ) {
218
- component = await component();
219
- component = component.default;
220
- }
221
- if (component) {
222
- const keys = extractTranslationKeys(component);
223
- allTranslationKeys.push(...keys);
224
- }
225
- }
226
- this.translationKeys = allTranslationKeys;
227
- } catch (error) {
228
- console.error("加载异步组件时出错:", error);
229
- next();
230
- return;
231
- }
232
- }
233
- next();
234
- });
235
-
236
- // 在每次路由完成后,同步一次运行时收集到的 keys
237
- if (!I18n._afterEachGuardRegistered) {
238
- I18n._afterEachGuardRegistered = true;
239
- this.router.afterEach(async () => {
240
- await nextTick();
241
- // 合并静态扫描与运行时收集,去重
242
- const runtimeKeys = Array.from(this.translationKeySet);
243
- const merged = Array.from(
244
- new Set([...(this.translationKeys || []), ...runtimeKeys])
245
- );
246
- this.translationKeys = merged;
247
- });
248
- }
249
- }
250
- ```
251
-
252
- ### 翻译查找机制
253
-
254
- 翻译查找通过增强的消息解析器实现,支持嵌套键值查找和路由特定键处理:
87
+ // 独立 Axios 实例,避免宿主环境污染全局 axios
88
+ const http = axios.create();
255
89
 
256
- ```javascript
257
- function _r(e, t) {
258
- if (!M(e)) return null
259
- let n = yn.get(t)
260
- if (n || (n = ur(t), n && yn.set(t, n)), !n) return null
261
- const a = n.length
262
- let s = e, r = 0
263
- for (; r < a; ) {
264
- const o = n[r]
265
- if (ca.includes(o) && le(s)) return null
266
- const l = s[o]
267
- if (l === void 0 || x(s)) return null
268
- s = l, r++
90
+ // 添加请求拦截器,自动带上 token
91
+ http.interceptors.request.use((config) => {
92
+ // I18N_CONFIG 中获取 token
93
+ const i18nConfig = store2.get("I18N_CONFIG") || {};
94
+ const token = i18nConfig.token;
95
+ if (token) {
96
+ config.headers.Authorization = `Bearer ${token}`;
269
97
  }
270
- return s
271
- }
272
- ```
273
-
274
- ### 事件通知机制
275
-
276
- **更新** 事件通知通过自定义事件系统实现,使用mitt事件发射器,新增了closeModal事件处理功能:
277
-
278
- ```javascript
279
- install(app, opts = {}) {
280
- app.use(this.i18n);
281
- // 自动注册全局组件
282
- // app.component("i18n-earth", earthVue);
283
-
284
- window.addEventListener("message", (event) => {
285
- if (event.data.type === "updateLanguage") {
286
- location.reload();
287
- }
288
- // 新增:监听关闭弹窗消息
289
- if (event.data.type === "closeModal") {
290
- this.configInstance && this.configInstance.closeModal();
291
- }
292
- });
293
- }
98
+ return config;
99
+ });
294
100
  ```
295
101
 
296
- ### HTTP客户端现代化
297
-
298
- **更新** 新增独立axios实例,避免宿主环境污染全局axios配置:
102
+ **更新** HTTP客户端现代化,新增独立axios实例,避免宿主环境污染全局axios配置:
299
103
 
300
104
  ```javascript
301
105
  // 独立 Axios 实例,避免宿主环境污染全局 axios
@@ -309,7 +113,6 @@ export const getTranslate = async ({
309
113
  routerName
310
114
  }) => {
311
115
  return new Promise(async (resolve, reject) => {
312
- // const appCodeStore = i18nStore.get(appCode);
313
116
  const languageStore = store2.namespace(`I18N_${_.toUpper(appCode)}`);
314
117
  const options = {
315
118
  baseUrl,
@@ -357,36 +160,71 @@ export const getTranslate = async ({
357
160
  };
358
161
  ```
359
162
 
360
- ### 安全机制改进
163
+ ### SweetAlert2集成重构
361
164
 
362
- **更新** 新增JWT令牌生成和验证机制:
165
+ **更新** 核心I18n类现在使用直接的SweetAlert2集成,移除了复杂的模态框创建系统:
363
166
 
364
167
  ```javascript
365
- // 生成 JWT 的函数
366
- function generateJWT(orgCode) {
367
- const header = {
368
- alg: "HS512" // 算法
369
- };
370
- const payload = {
371
- orgCode
372
- };
373
- const sHeader = JSON.stringify(header);
374
- const sPayload = JSON.stringify(payload);
375
- const secret = "";
376
- // 同步生成 JWT
377
- return jws.JWS.sign("HS512", sHeader, sPayload, secret);
378
- }
168
+ // 直接使用 SweetAlert2 创建配置弹窗
169
+ keyboardJS.bind(this.keyboard, (e) => {
170
+ const iframeSrc = `${this.baseUrl}/i18n-admin-front/#/config?name=${
171
+ this.name
172
+ }&appCode=${this.appCode}&env=${
173
+ this.env
174
+ }&translationKeys=${this.translationKeys.join(",")}`;
175
+ this.swalInstance = Swal.fire({
176
+ titleText: "多语言管理中心",
177
+ padding: "0px",
178
+ showCloseButton: true,
179
+ showConfirmButton: false,
180
+ width: "1000px",
181
+ customClass: {
182
+ container: "gc_i18n_container",
183
+ title: "gc_i18n_title",
184
+ closeButton: "gc_i18n_close_button",
185
+ htmlContainer: "gc_i18n_html_container"
186
+ },
187
+ html: `<iframe src="${iframeSrc}" style="width: 100%; height: 100%;border: none;min-height: 480px;"></iframe>`
188
+ });
189
+ });
190
+ ```
379
191
 
380
- // 设置令牌
381
- setToken(token) {
382
- this.token = token;
383
- store2.set("I18N_TOKEN", token);
192
+ ### 事件通知机制改进
193
+
194
+ **更新** 事件通知通过自定义事件系统实现,使用mitt事件发射器,改进了closeModal事件处理功能:
195
+
196
+ ```javascript
197
+ install(app, opts = {}) {
198
+ app.use(this.i18n);
199
+ // 自动注册全局组件
200
+ app.component("i18n-earth", {
201
+ ...earthVue,
202
+ props: {
203
+ ...earthVue.props,
204
+ baseUrl: {
205
+ type: String,
206
+ default: this.baseUrl
207
+ }
208
+ }
209
+ });
210
+
211
+ window.addEventListener("message", (event) => {
212
+ if (event.data.type === "updateLanguage") {
213
+ location.reload();
214
+ }
215
+ // 新增:监听关闭弹窗消息
216
+ if (event.data.type === "closeModal") {
217
+ if (this.swalInstance) {
218
+ this.swalInstance.close();
219
+ }
220
+ }
221
+ });
384
222
  }
385
223
  ```
386
224
 
387
- ### 翻译函数增强
225
+ ### 翻译查找机制
388
226
 
389
- **更新** 翻译函数现在支持默认值参数,当翻译不存在时返回默认值:
227
+ 翻译查找通过增强的消息解析器实现,支持嵌套键值查找和路由特定键处理:
390
228
 
391
229
  ```javascript
392
230
  globalThis.$t = this.i18n.global.t = (key, defaultValue) => {
@@ -441,88 +279,38 @@ globalThis.$t = this.i18n.global.t = (key, defaultValue) => {
441
279
  };
442
280
  ```
443
281
 
444
- ### 键跟踪机制改进
445
-
446
- **更新** 新增全局键集合跟踪机制,通过__I18N_ALL_KEYS__全局变量记录所有调用过的键:
447
-
448
- ```javascript
449
- // 用于存储所有调用过的 key,供文本编辑模式使用
450
- globalThis.__I18N_ALL_KEYS__ = new Set();
451
- ```
452
-
453
- ### 智能语言列表缓存机制
282
+ ### 安全机制改进
454
283
 
455
- **新增** I18n类新增了智能语言列表缓存机制,通过getLanguagesWithCache函数提供缓存和自动失效功能:
284
+ 新增JWT令牌生成和验证机制:
456
285
 
457
286
  ```javascript
458
- /**
459
- * 获取语言列表(带缓存)
460
- * @param {Object} options - 配置选项
461
- * @param {string} options.baseUrl - API 基础 URL
462
- * @param {string} options.token - 认证 token
463
- * @param {string} options.appCode - 应用代码
464
- * @param {boolean} options.isRemote - 是否强制从远程获取
465
- * @returns {Promise<Array>} 语言列表
466
- */
467
- export const getLanguagesWithCache = async ({
468
- baseUrl,
469
- token,
470
- appCode,
471
- isRemote = false
472
- }) => {
473
- // 先从缓存获取
474
- const cached = store2.get("I18N_LANGUAGES");
475
- if (cached && Array.isArray(cached) && cached.length > 0 && !isRemote) {
476
- return cached.map((l) => ({
477
- langCode: l.language || l.langCode || l,
478
- langName: l.langName || l.language || l
479
- }));
480
- }
481
-
482
- // 从 API 获取
483
- if (!appCode) {
484
- return [];
485
- }
486
-
487
- try {
488
- const res = await http.get(`${baseUrl}/i18n-web/language/languages`, {
489
- headers: {
490
- appCode,
491
- Authorization: token
492
- }
493
- });
494
- if (res.data && res.data.result === 0) {
495
- const langs = _.get(res.data, "retVal", []);
496
- const formattedLangs = langs.map((l) => ({
497
- langCode: l.language || l.langCode || l,
498
- langName: l.langName || l.language || l
499
- }));
500
- store2.set("I18N_LANGUAGES", langs);
501
- return formattedLangs;
502
- }
503
- } catch (error) {
504
- console.error("获取语言列表失败:", error);
505
- }
287
+ // 生成 JWT 的函数
288
+ function generateJWT(orgCode) {
289
+ const header = {
290
+ alg: "HS512" // 算法
291
+ };
292
+ const payload = {
293
+ orgCode
294
+ };
295
+ const sHeader = JSON.stringify(header);
296
+ const sPayload = JSON.stringify(payload);
297
+ const secret = "";
298
+ // 同步生成 JWT
299
+ return jws.JWS.sign("HS512", sHeader, sPayload, secret);
300
+ }
506
301
 
507
- return [];
508
- };
302
+ // 设置令牌
303
+ setToken(token) {
304
+ this.token = token;
305
+ store2.set("I18N_TOKEN", token);
306
+ }
509
307
  ```
510
308
 
511
- ### 缓存策略和自动失效
512
-
513
- 智能缓存机制具有以下特点:
514
-
515
- 1. **本地缓存优先**:首次调用时从store2本地存储获取缓存数据
516
- 2. **智能判断**:检查缓存数据的有效性(数组类型、长度大于0)
517
- 3. **强制刷新**:通过isRemote参数强制从远程API获取最新数据
518
- 4. **自动存储**:成功获取后自动存储到本地缓存
519
- 5. **格式化输出**:统一返回标准化的语言列表格式
520
-
521
309
  **本节来源**
522
- - [packages/index.js:67-411](file://packages/index.js#L67-L411)
523
- - [packages/libs/i18nUtils.ts:1-54](file://packages/libs/i18nUtils.ts#L1-L54)
524
- - [packages/libs/service.js:1-187](file://packages/libs/service.js#L1-L187)
525
- - [packages/swal.css:1-18](file://packages/swal.css#L1-L18)
310
+ - [packages/index.js:51-62](file://packages/index.js#L51-L62)
311
+ - [packages/index.js:133-152](file://packages/index.js#L133-L152)
312
+ - [packages/index.js:519-530](file://packages/index.js#L519-L530)
313
+ - [packages/index.js:327-376](file://packages/index.js#L327-L376)
526
314
 
527
315
  ## Vue插件安装机制
528
316
 
@@ -532,15 +320,26 @@ I18n类作为Vue插件,通过install方法进行安装:
532
320
  install(app, opts = {}) {
533
321
  app.use(this.i18n);
534
322
  // 自动注册全局组件
535
- // app.component("i18n-earth", earthVue);
323
+ app.component("i18n-earth", {
324
+ ...earthVue,
325
+ props: {
326
+ ...earthVue.props,
327
+ baseUrl: {
328
+ type: String,
329
+ default: this.baseUrl
330
+ }
331
+ }
332
+ });
536
333
 
537
334
  window.addEventListener("message", (event) => {
538
335
  if (event.data.type === "updateLanguage") {
539
336
  location.reload();
540
337
  }
541
- // 新增:监听关闭弹窗消息
338
+ // 监听关闭弹窗消息
542
339
  if (event.data.type === "closeModal") {
543
- this.configInstance && this.configInstance.closeModal();
340
+ if (this.swalInstance) {
341
+ this.swalInstance.close();
342
+ }
544
343
  }
545
344
  });
546
345
  }
@@ -565,7 +364,7 @@ this.i18n = createI18nCompat({
565
364
 
566
365
  const originalT = this.i18n.global.t;
567
366
 
568
- globalThis.$t = this.i18n.global.t = (key, comment) => {
367
+ globalThis.$t = this.i18n.global.t = (key, defaultValue) => {
569
368
  // 记录本次调用的原始 key(字符串场景)
570
369
  if (typeof key === "string") {
571
370
  this.translationKeySet.add(key);
@@ -573,29 +372,37 @@ globalThis.$t = this.i18n.global.t = (key, comment) => {
573
372
  // Vue Router 3 (Vue2) currentRoute 为普通对象,Vue Router 4 (Vue3) 为 ref
574
373
  const route = this.router?.currentRoute;
575
374
  const routeName = (route?.value ?? route)?.name;
375
+ let result;
576
376
  if (routeName) {
577
377
  const routerKey = `${routeName}.${key}`;
578
378
  const routeTranslation = originalT(routerKey);
579
379
  if (routeTranslation !== routerKey && !_.isEmpty(routeTranslation)) {
580
- return routeTranslation;
380
+ result = routeTranslation;
581
381
  } else {
582
382
  const commonKey = `common.${key}`;
583
383
  const commonTranslation = originalT(commonKey);
584
384
  if (commonTranslation !== commonKey) {
585
- return commonTranslation;
385
+ result = commonTranslation;
586
386
  } else {
587
- return originalT(key, comment);
387
+ result = originalT(key);
588
388
  }
589
389
  }
590
390
  } else {
591
- return originalT(key, comment);
391
+ result = originalT(key);
592
392
  }
393
+
394
+ // 如果翻译不存在(返回的是 key 本身),则使用默认值
395
+ if (result === key && typeof defaultValue === "string" && defaultValue) {
396
+ result = defaultValue;
397
+ }
398
+
399
+ return result;
593
400
  };
594
401
  ```
595
402
 
596
403
  **本节来源**
597
- - [packages/index.js:400-411](file://packages/index.js#L400-L411)
598
- - [packages/index.js:242-307](file://packages/index.js#L242-L307)
404
+ - [packages/index.js:505-530](file://packages/index.js#L505-L530)
405
+ - [packages/index.js:306-408](file://packages/index.js#L306-L408)
599
406
 
600
407
  ## 公共API接口文档
601
408
 
@@ -735,9 +542,8 @@ const latestLanguages = await $getLanguages(true)
735
542
  ```
736
543
 
737
544
  **本节来源**
738
- - [packages/index.js:257-307](file://packages/index.js#L257-L307)
739
- - [packages/index.js:369-371](file://packages/index.js#L369-L371)
740
- - [packages/index.js:412-420](file://packages/index.js#L412-L420)
545
+ - [packages/index.js:395-408](file://packages/index.js#L395-L408)
546
+ - [packages/index.js:440-448](file://packages/index.js#L440-L448)
741
547
 
742
548
  ## RTL方向管理功能
743
549
 
@@ -913,15 +719,13 @@ async setLanguage(language = "zh-CN") {
913
719
  ```
914
720
 
915
721
  **本节来源**
916
- - [packages/index.js:68-136](file://packages/index.js#L68-L136)
917
- - [packages/index.js:310-343](file://packages/index.js#L310-L343)
918
- - [packages/libs/utils.js:3-22](file://packages/libs/utils.js#L3-L22)
722
+ - [packages/index.js:410-435](file://packages/index.js#L410-L435)
919
723
 
920
724
  ## 中文语言模式兼容性
921
725
 
922
726
  ### 语言代码规范化机制
923
727
 
924
- **新增** I18n类引入了normalizeLocale方法,专门处理中文语言模式的兼容性支持:
728
+ I18n类引入了normalizeLocale方法,专门处理中文语言模式的兼容性支持:
925
729
 
926
730
  ```javascript
927
731
  /**
@@ -931,7 +735,19 @@ async setLanguage(language = "zh-CN") {
931
735
  */
932
736
  normalizeLocale(locale) {
933
737
  if (!locale) return "zh-CN";
934
- // 处理简写形式:zh -> zh-CN, en -> en-US
738
+ // 保留繁体中文变体
739
+ if (locale === "zh-TW" || locale === "zh-HK" || locale === "zh-MO") {
740
+ return locale;
741
+ }
742
+ // zh-SG 等其它 zh- 开头映射为 zh-CN
743
+ if (locale.startsWith("zh-")) {
744
+ return "zh-CN";
745
+ }
746
+ // en-* 全部映射为 en-US
747
+ if (locale.startsWith("en-")) {
748
+ return "en-US";
749
+ }
750
+ // 处理完全匹配形式
935
751
  const localeMap = {
936
752
  zh: "zh-CN",
937
753
  en: "en-US"
@@ -942,7 +758,7 @@ normalizeLocale(locale) {
942
758
 
943
759
  ### 中文简体和繁体支持
944
760
 
945
- **新增** 完整的中文语言模式支持,包括简体中文(zh-CN)和繁体中文(zh-TW):
761
+ 完整的中文语言模式支持,包括简体中文(zh-CN)和繁体中文(zh-TW):
946
762
 
947
763
  ```javascript
948
764
  // 语言资源文件包含简体和繁体中文翻译
@@ -968,7 +784,7 @@ normalizeLocale(locale) {
968
784
 
969
785
  ### 自动语言检测和回退机制
970
786
 
971
- **更新** 语言加载时的智能回退机制:
787
+ 语言加载时的智能回退机制:
972
788
 
973
789
  ```javascript
974
790
  async setLanguage(language = "zh-CN") {
@@ -1003,7 +819,7 @@ async setLanguage(language = "zh-CN") {
1003
819
 
1004
820
  ### 语言资源文件结构
1005
821
 
1006
- **新增** 语言资源文件采用JSON格式,支持多语言键值对:
822
+ 语言资源文件采用JSON格式,支持多语言键值对:
1007
823
 
1008
824
  ```json
1009
825
  {
@@ -1021,7 +837,7 @@ async setLanguage(language = "zh-CN") {
1021
837
 
1022
838
  ### 中文语言模式最佳实践
1023
839
 
1024
- **新增** 使用中文语言模式的推荐做法:
840
+ 使用中文语言模式的推荐做法:
1025
841
 
1026
842
  ```javascript
1027
843
  // 1. 使用规范化的语言代码
@@ -1039,135 +855,8 @@ await $changeLocale('zh-CN'); // 切换到简体中文
1039
855
  ```
1040
856
 
1041
857
  **本节来源**
1042
- - [packages/index.js:168-176](file://packages/index.js#L168-L176)
1043
- - [packages/index.js:437-492](file://packages/index.js#L437-L492)
1044
- - [lang/index.json:1-130](file://lang/index.json#L1-L130)
1045
-
1046
- ## 单元代码支持功能
1047
-
1048
- ### 单位代码配置机制
1049
-
1050
- **新增** I18n类引入了unitCode属性,用于支持单元代码(组织代码)的配置和管理:
1051
-
1052
- ```javascript
1053
- constructor(options = {}) {
1054
- // ... 其他初始化代码
1055
- this.unitCode = options.unitCode || "GREENCLOUD";
1056
- // ... 其他初始化代码
1057
- }
1058
- ```
1059
-
1060
- ### 默认值设置和环境检测
1061
-
1062
- **更新** 单元代码的默认值设置,结合环境检测机制:
1063
-
1064
- ```javascript
1065
- // 在语言包入口中,根据环境模式设置单元代码
1066
- const i18n = new gc_i18n({
1067
- // ... 其他配置
1068
- orgCode: import.meta.env.MODE === "unit" ? "DD" : "GREENCLOUD",
1069
- // ...
1070
- });
1071
- ```
1072
-
1073
- ### 文本编辑模式中的单元代码集成
1074
-
1075
- **新增** 文本编辑模式中集成了unitCode支持,用于翻译数据的保存和检索:
1076
-
1077
- ```javascript
1078
- async saveTranslations(key, values, originalData) {
1079
- const original = originalData[0];
1080
- const unitCode = this.unitCode || "GREENCLOUD";
1081
-
1082
- const requestData = [];
1083
- Object.keys(values).forEach(lang => {
1084
- requestData.push({
1085
- appCode: this.appCode,
1086
- page: original.page,
1087
- key: key,
1088
- unitCode: unitCode,
1089
- lang: lang,
1090
- value: values[lang],
1091
- fromAi: "F"
1092
- });
1093
- });
1094
-
1095
- const response = await saveTranslate({
1096
- data: { data: requestData, isLocal: "F" },
1097
- token: this.token,
1098
- baseUrl: this.baseUrl
1099
- });
1100
-
1101
- return response;
1102
- }
1103
- ```
1104
-
1105
- ### 单元代码在配置系统中的应用
1106
-
1107
- **新增** 单元代码在配置系统中的完整应用流程:
1108
-
1109
- ```javascript
1110
- /**
1111
- * 初始化配置
1112
- */
1113
- async initConfig() {
1114
- const i18nConfig = store2.get("I18N_CONFIG") || {};
1115
- const env = i18nConfig.env || "local";
1116
-
1117
- // 根据环境设置 baseUrl
1118
- this.baseUrl =
1119
- !env || env === "local"
1120
- ? ""
1121
- : env === "dev"
1122
- ? "https://test.ihotel.cn"
1123
- : "https://trans.ihotel.cn";
1124
-
1125
- this.appCode = i18nConfig.appCode || "";
1126
- this.unitCode = i18nConfig.unitCode || "GREENCLOUD";
1127
- this.token = i18nConfig.token || store2.get("I18N_TOKEN") || "";
1128
- this.currentLocale = i18nConfig.locale || store2.get("I18N_LANGUAGE") || "zh-CN";
1129
-
1130
- await this.loadLanguages();
1131
- }
1132
- ```
1133
-
1134
- ### 单元代码的默认值策略
1135
-
1136
- **新增** 单元代码的默认值策略,确保在不同环境下的一致性:
1137
-
1138
- ```javascript
1139
- // 默认单元代码为 GREENCLOUD
1140
- this.unitCode = options.unitCode || "GREENCLOUD";
1141
-
1142
- // 在语言包入口中,根据模式覆盖默认值
1143
- const unitCode = import.meta.env.MODE === "unit" ? "DD" : "GREENCLOUD";
1144
- ```
1145
-
1146
- ### 单元代码支持的最佳实践
1147
-
1148
- **新增** 使用单元代码支持的推荐做法:
1149
-
1150
- ```javascript
1151
- // 1. 在创建I18n实例时显式设置unitCode
1152
- const i18n = new I18n({
1153
- unitCode: 'CUSTOM_UNIT_CODE',
1154
- appCode: 'MY_APP',
1155
- orgCode: 'ORG123'
1156
- });
1157
-
1158
- // 2. 在不同环境使用不同的unitCode
1159
- const unitCode = import.meta.env.MODE === "unit" ? "DD" : "GREENCLOUD";
1160
-
1161
- // 3. 在文本编辑模式中自动使用配置的unitCode
1162
- await textEditMode.toggle();
1163
- // 编辑的翻译数据将使用配置的unitCode保存
1164
- ```
1165
-
1166
- **本节来源**
1167
- - [packages/index.js:70-125](file://packages/index.js#L70-L125)
1168
- - [packages/libs/textEditMode.js:29-50](file://packages/libs/textEditMode.js#L29-L50)
1169
- - [lang/index.js:10](file://lang/index.js#L10)
1170
- - [lib/gc_i18n.es.js:1964-1966](file://lib/gc_i18n.es.js#L1964-L1966)
858
+ - [packages/index.js:168-188](file://packages/index.js#L168-L188)
859
+ - [packages/index.js:449-504](file://packages/index.js#L449-L504)
1171
860
 
1172
861
  ## 文本编辑模式功能
1173
862
 
@@ -1192,6 +881,7 @@ handleElementClick[处理元素点击]
1192
881
  openEditModal[打开编辑弹窗]
1193
882
  fetchTranslations[获取翻译数据]
1194
883
  saveTranslations[保存翻译数据]
884
+ updateTranslatedText[更新翻译文本]
1195
885
  end
1196
886
  subgraph "快捷键绑定"
1197
887
  keyboardJS[keyboardJS库]
@@ -1214,6 +904,7 @@ textEditMode --> handleElementClick
1214
904
  textEditMode --> openEditModal
1215
905
  textEditMode --> fetchTranslations
1216
906
  textEditMode --> saveTranslations
907
+ textEditMode --> updateTranslatedText
1217
908
  keyboardJS --> editKeyboard
1218
909
  textEditModeCSS --> i18nEditableText
1219
910
  textEditModeCSS --> i18nEditModeActive
@@ -1221,9 +912,9 @@ swalCSS --> swal2Container
1221
912
  ```
1222
913
 
1223
914
  **图示来源**
1224
- - [packages/libs/textEditMode.js:14-623](file://packages/libs/textEditMode.js#L14-L623)
1225
- - [packages/index.js:109-156](file://packages/index.js#L109-L156)
1226
- - [packages/swal.css:1-18](file://packages/swal.css#L1-L18)
915
+ - [packages/libs/textEditMode.js:14-624](file://packages/libs/textEditMode.js#L14-L624)
916
+ - [packages/index.js:154-158](file://packages/index.js#L154-L158)
917
+ - [packages/swal.css:1-23](file://packages/swal.css#L1-L23)
1227
918
 
1228
919
  ### 文本编辑模式快捷键配置
1229
920
 
@@ -1269,7 +960,7 @@ enableEditMode() {
1269
960
  // 显示提示
1270
961
  Swal.fire({
1271
962
  title: "文本编辑模式已开启",
1272
- text: "点击带蓝色虚线外框的文本即可编辑翻译内容,按 Ctrl+Shift+E 退出",
963
+ text: "点击带外框的文本即可编辑翻译内容,按 Ctrl+Shift+E 退出",
1273
964
  icon: "info",
1274
965
  timer: 2000,
1275
966
  showConfirmButton: false,
@@ -1310,6 +1001,9 @@ scanTextNodes(rootElement) {
1310
1001
  const key = this.findKeyByValue(text);
1311
1002
  if (key) {
1312
1003
  this.wrapTextNode(textNode, key);
1004
+ } else {
1005
+ // 尝试从文本中提取可识别的翻译部分
1006
+ this.wrapPartialTextNode(textNode);
1313
1007
  }
1314
1008
  });
1315
1009
  }
@@ -1317,7 +1011,7 @@ scanTextNodes(rootElement) {
1317
1011
 
1318
1012
  #### 查找翻译键
1319
1013
 
1320
- **更新** 增强的键查找机制,支持更全面的键值映射:
1014
+ 增强的键查找机制,支持更全面的键值映射:
1321
1015
 
1322
1016
  ```javascript
1323
1017
  findKeyByValue(value) {
@@ -1344,6 +1038,15 @@ findKeyByValue(value) {
1344
1038
  }
1345
1039
  }
1346
1040
 
1041
+ // 从当前路由的翻译中查找(如果存在)
1042
+ const routeMessagesKey = `I18N_MESSAGES_${this.currentLocale}`;
1043
+ const routeMessages = store2.get(routeMessagesKey) || {};
1044
+ for (const [key, val] of Object.entries(routeMessages)) {
1045
+ if (val === value) {
1046
+ return key;
1047
+ }
1048
+ }
1049
+
1347
1050
  // 如果找不到,返回 null
1348
1051
  return null;
1349
1052
  }
@@ -1351,6 +1054,8 @@ findKeyByValue(value) {
1351
1054
 
1352
1055
  #### 打开编辑弹窗
1353
1056
 
1057
+ **更新** 使用SweetAlert2直接集成,简化了弹窗创建过程:
1058
+
1354
1059
  ```javascript
1355
1060
  async openEditModal(key) {
1356
1061
  try {
@@ -1358,7 +1063,7 @@ async openEditModal(key) {
1358
1063
  if (key.startsWith("i.") || key.startsWith("pro.")) {
1359
1064
  // 显示 tips 提示
1360
1065
  Swal.fire({
1361
- text: "框架内部固定翻译,不支持修改",
1066
+ text: "系统字段不支持修改",
1362
1067
  icon: "info",
1363
1068
  timer: 2000,
1364
1069
  showConfirmButton: false,
@@ -1372,6 +1077,20 @@ async openEditModal(key) {
1372
1077
  const translations = await this.fetchTranslations(key);
1373
1078
  console.log("translations", translations);
1374
1079
  const value = translations[0] || "";
1080
+
1081
+ // 如果没有数据,显示提示
1082
+ if (!value) {
1083
+ Swal.fire({
1084
+ text: "暂无翻译数据",
1085
+ icon: "info",
1086
+ timer: 2000,
1087
+ showConfirmButton: false,
1088
+ position: "top",
1089
+ toast: true
1090
+ });
1091
+ return;
1092
+ }
1093
+
1375
1094
  // 构建编辑表单
1376
1095
  const langInputs =
1377
1096
  value &&
@@ -1393,9 +1112,10 @@ async openEditModal(key) {
1393
1112
  .join("");
1394
1113
 
1395
1114
  const { value: formValues } = await Swal.fire({
1396
- title: `编辑翻译 - ${key}`,
1115
+ title: `原值参考:${value["zh-CN_raw"]}`,
1397
1116
  html: `
1398
1117
  <div class="i18n-edit-form">
1118
+ <div id="i18n-save-error" style="display: none; margin-bottom: 15px; padding: 10px 15px; background: #fff1f0; border: 1px solid #ffa39e; border-radius: 4px; color: #cf1322; font-size: 13px;"></div>
1399
1119
  ${langInputs}
1400
1120
  </div>
1401
1121
  `,
@@ -1409,18 +1129,50 @@ async openEditModal(key) {
1409
1129
  title: "i18n-edit-modal-title",
1410
1130
  htmlContainer: "i18n-edit-modal-content"
1411
1131
  },
1412
- preConfirm: () => {
1132
+ preConfirm: async () => {
1413
1133
  const inputs = document.querySelectorAll(".i18n-edit-input");
1414
1134
  const values = {};
1415
1135
  inputs.forEach((input) => {
1416
1136
  values[input.getAttribute("data-lang")] = input.value;
1417
1137
  });
1418
- return values;
1138
+
1139
+ // 隐藏之前的错误信息
1140
+ const errorDiv = document.getElementById("i18n-save-error");
1141
+ if (errorDiv) {
1142
+ errorDiv.style.display = "none";
1143
+ errorDiv.textContent = "";
1144
+ }
1145
+
1146
+ // 执行保存
1147
+ try {
1148
+ await this.saveTranslations(key, values, translations);
1149
+ return values;
1150
+ } catch (error) {
1151
+ // 显示错误信息在弹窗顶部
1152
+ if (errorDiv) {
1153
+ errorDiv.textContent =
1154
+ error.message || "保存失败,请检查网络连接或权限";
1155
+ errorDiv.style.display = "block";
1156
+ }
1157
+ // 返回 false 阻止弹窗关闭
1158
+ return false;
1159
+ }
1419
1160
  }
1420
1161
  });
1421
1162
 
1422
1163
  if (formValues) {
1423
- await this.saveTranslations(key, formValues, translations);
1164
+ // 保存成功后会自动关闭弹窗
1165
+ this.updateTranslatedText(key, formValues);
1166
+
1167
+ // 显示成功提示
1168
+ Swal.fire({
1169
+ text: "保存成功",
1170
+ icon: "success",
1171
+ timer: 1500,
1172
+ showConfirmButton: false,
1173
+ position: "top",
1174
+ toast: true
1175
+ });
1424
1176
  }
1425
1177
  } catch (error) {
1426
1178
  console.error("打开编辑弹窗失败:", error);
@@ -1446,8 +1198,7 @@ async openEditModal(key) {
1446
1198
  /* 可编辑文本的外框样式 */
1447
1199
  .i18n-editable-text {
1448
1200
  position: relative;
1449
- outline: 2px dashed #2d8cf0 !important;
1450
- outline-offset: 2px;
1201
+ outline: 1px dashed #2d8cf0 !important;
1451
1202
  cursor: pointer !important;
1452
1203
  background-color: rgba(45, 140, 240, 0.05);
1453
1204
  border-radius: 2px;
@@ -1461,8 +1212,8 @@ async openEditModal(key) {
1461
1212
  box-shadow: 0 0 8px rgba(45, 140, 240, 0.3);
1462
1213
  }
1463
1214
 
1464
- /* 编辑模式提示标签 */
1465
- .i18n-editable-text::after {
1215
+ /* 编辑模式提示标签 - 已禁用 */
1216
+ /* .i18n-editable-text::after {
1466
1217
  content: attr(data-i18n-key);
1467
1218
  position: absolute;
1468
1219
  top: -20px;
@@ -1483,12 +1234,12 @@ async openEditModal(key) {
1483
1234
  .i18n-editable-text:hover::after {
1484
1235
  opacity: 1;
1485
1236
  visibility: visible;
1486
- }
1237
+ } */
1487
1238
  ```
1488
1239
 
1489
1240
  ### SweetAlert2样式集成优化
1490
1241
 
1491
- **更新** SweetAlert2样式集成经过优化,提供更好的视觉体验:
1242
+ SweetAlert2样式集成经过优化,提供更好的视觉体验:
1492
1243
 
1493
1244
  ```css
1494
1245
  /* SweetAlert2 容器样式 */
@@ -1515,6 +1266,11 @@ async openEditModal(key) {
1515
1266
  div:where(.swal2-container) button:where(.swal2-close):focus-visible {
1516
1267
  box-shadow: none !important;
1517
1268
  }
1269
+
1270
+ /* 全局 SweetAlert2 弹窗 z-index 设置,确保不被其他弹窗遮盖 */
1271
+ .swal2-container {
1272
+ z-index: 99999 !important;
1273
+ }
1518
1274
  ```
1519
1275
 
1520
1276
  ### 非组件环境使用
@@ -1552,10 +1308,10 @@ if ($isRTL()) {
1552
1308
  ```
1553
1309
 
1554
1310
  **本节来源**
1555
- - [packages/libs/textEditMode.js:1-623](file://packages/libs/textEditMode.js#L1-L623)
1311
+ - [packages/libs/textEditMode.js:14-624](file://packages/libs/textEditMode.js#L14-L624)
1556
1312
  - [packages/libs/textEditMode.css:1-166](file://packages/libs/textEditMode.css#L1-L166)
1557
- - [packages/index.js:109-156](file://packages/index.js#L109-L156)
1558
- - [packages/swal.css:1-18](file://packages/swal.css#L1-L18)
1313
+ - [packages/index.js:154-158](file://packages/index.js#L154-L158)
1314
+ - [packages/swal.css:1-23](file://packages/swal.css#L1-L23)
1559
1315
 
1560
1316
  ## 非组件环境使用
1561
1317
 
@@ -1684,8 +1440,7 @@ div:where(.swal2-container) button:where(.swal2-close):focus-visible {
1684
1440
  /* 可编辑文本的外框样式 */
1685
1441
  .i18n-editable-text {
1686
1442
  position: relative;
1687
- outline: 2px dashed #2d8cf0 !important;
1688
- outline-offset: 2px;
1443
+ outline: 1px dashed #2d8cf0 !important;
1689
1444
  cursor: pointer !important;
1690
1445
  background-color: rgba(45, 140, 240, 0.05);
1691
1446
  border-radius: 2px;
@@ -1698,35 +1453,11 @@ div:where(.swal2-container) button:where(.swal2-close):focus-visible {
1698
1453
  background-color: rgba(45, 140, 240, 0.1);
1699
1454
  box-shadow: 0 0 8px rgba(45, 140, 240, 0.3);
1700
1455
  }
1701
-
1702
- /* 编辑模式提示标签 */
1703
- .i18n-editable-text::after {
1704
- content: attr(data-i18n-key);
1705
- position: absolute;
1706
- top: -20px;
1707
- left: 0;
1708
- background: #2d8cf0;
1709
- color: white;
1710
- font-size: 11px;
1711
- padding: 2px 6px;
1712
- border-radius: 3px;
1713
- white-space: nowrap;
1714
- opacity: 0;
1715
- visibility: hidden;
1716
- transition: all 0.2s ease;
1717
- z-index: 9999;
1718
- pointer-events: none;
1719
- }
1720
-
1721
- .i18n-editable-text:hover::after {
1722
- opacity: 1;
1723
- visibility: visible;
1724
- }
1725
1456
  ```
1726
1457
 
1727
1458
  ### SweetAlert2样式集成
1728
1459
 
1729
- **更新** SweetAlert2样式集成优化,提供更好的用户体验:
1460
+ SweetAlert2样式集成优化,提供更好的用户体验:
1730
1461
 
1731
1462
  ```css
1732
1463
  /* SweetAlert2 容器样式 */
@@ -1753,6 +1484,11 @@ div:where(.swal2-container) button:where(.swal2-close):focus-visible {
1753
1484
  div:where(.swal2-container) button:where(.swal2-close):focus-visible {
1754
1485
  box-shadow: none !important;
1755
1486
  }
1487
+
1488
+ /* 全局 SweetAlert2 弹窗 z-index 设置,确保不被其他弹窗遮盖 */
1489
+ .swal2-container {
1490
+ z-index: 99999 !important;
1491
+ }
1756
1492
  ```
1757
1493
 
1758
1494
  ### 样式特性
@@ -1768,12 +1504,11 @@ div:where(.swal2-container) button:where(.swal2-close):focus-visible {
1768
1504
  **本节来源**
1769
1505
  - [lib/gc_i18n.css](file://lib/gc_i18n.css)
1770
1506
  - [packages/libs/textEditMode.css:1-166](file://packages/libs/textEditMode.css#L1-L166)
1771
- - [packages/swal.css:1-18](file://packages/swal.css#L1-L18)
1772
- - [packages/index.js:338-343](file://packages/index.js#L338-L343)
1507
+ - [packages/swal.css:1-23](file://packages/swal.css#L1-L23)
1773
1508
 
1774
1509
  ## PostMessage事件处理功能
1775
1510
 
1776
- **新增** I18n类现在支持通过postMessage事件编程控制模态框的关闭,扩展了原有的updateLanguage事件处理机制。
1511
+ I18n类现在支持通过postMessage事件编程控制模态框的关闭,扩展了原有的updateLanguage事件处理机制。
1777
1512
 
1778
1513
  ### 事件处理机制
1779
1514
 
@@ -1786,7 +1521,9 @@ window.addEventListener("message", (event) => {
1786
1521
  }
1787
1522
  // 新增:监听关闭弹窗消息
1788
1523
  if (event.data.type === "closeModal") {
1789
- this.configInstance && this.configInstance.closeModal();
1524
+ if (this.swalInstance) {
1525
+ this.swalInstance.close();
1526
+ }
1790
1527
  }
1791
1528
  });
1792
1529
  ```
@@ -1803,7 +1540,7 @@ window.addEventListener("message", (event) => {
1803
1540
 
1804
1541
  - **用途**: 编程式关闭模态框
1805
1542
  - **触发条件**: 接收到type为"closeModal"的消息
1806
- - **行为**: 调用configInstance.closeModal()关闭当前打开的配置模态框
1543
+ - **行为**: 调用this.swalInstance.close()关闭当前打开的配置模态框
1807
1544
  - **适用场景**: 外部应用程序需要程序化控制模态框的关闭
1808
1545
 
1809
1546
  ### 外部应用程序集成
@@ -1843,6 +1580,138 @@ const handleMessage = (event) => {
1843
1580
  4. **时机控制**: 在模态框真正打开后再发送closeModal事件
1844
1581
 
1845
1582
  **本节来源**
1846
- - [packages/index.js:546-555](file://packages/index.js#L546-L555)
1847
- - [packages/components/earth.js:122-128](file://packages/components/earth.js#L122-L128)
1848
- - [lib/gc_i18n.es.js:2868-2870](file://lib/gc_i18n.es.js#L2868-L2870)
1583
+ - [packages/index.js:519-530](file://packages/index.js#L519-L530)
1584
+
1585
+ ## HTTP客户端集中式认证管理
1586
+
1587
+ **新增** I18n类集成了Axios请求拦截器,实现了HTTP请求的集中式认证管理:
1588
+
1589
+ ### 请求拦截器实现
1590
+
1591
+ ```javascript
1592
+ // 独立 Axios 实例,避免宿主环境污染全局 axios
1593
+ const http = axios.create();
1594
+
1595
+ // 添加请求拦截器,自动带上 token
1596
+ http.interceptors.request.use((config) => {
1597
+ // 从 I18N_CONFIG 中获取 token
1598
+ const i18nConfig = store2.get("I18N_CONFIG") || {};
1599
+ const token = i18nConfig.token;
1600
+ if (token) {
1601
+ config.headers.Authorization = `Bearer ${token}`;
1602
+ }
1603
+ return config;
1604
+ });
1605
+ ```
1606
+
1607
+ ### 认证令牌管理
1608
+
1609
+ **更新** 新增了独立的Axios实例,避免宿主环境对全局axios配置的污染:
1610
+
1611
+ ```javascript
1612
+ // 独立 Axios 实例,避免宿主环境污染全局 axios
1613
+ const http = axios.create();
1614
+
1615
+ export const getTranslate = async ({
1616
+ baseUrl,
1617
+ appCode,
1618
+ language = "zh-CN",
1619
+ token,
1620
+ routerName
1621
+ }) => {
1622
+ return new Promise(async (resolve, reject) => {
1623
+ const languageStore = store2.namespace(`I18N_${_.toUpper(appCode)}`);
1624
+ const options = {
1625
+ baseUrl,
1626
+ appCode,
1627
+ language,
1628
+ token
1629
+ };
1630
+ const lastData = languageStore.get(language);
1631
+
1632
+ if (!lastData || !lastData.lastPullDate) {
1633
+ // 如果是第一次获取
1634
+ const res = await fetchTranslate(options);
1635
+ if (res) {
1636
+ languageStore.set(language, res);
1637
+ resolve(res.translatesDTOs);
1638
+ }
1639
+ } else {
1640
+ const { lastPullDate } = lastData;
1641
+ const res = await fetchTranslate({
1642
+ ...options,
1643
+ lastPullDate
1644
+ });
1645
+ if (res) {
1646
+ // 合并增量数据
1647
+ const langData = _.get(lastData, "translatesDTOs");
1648
+ if (!_.isEmpty(res.translatesDTOs)) {
1649
+ const data = mergeArraysByKey(
1650
+ langData,
1651
+ res.translatesDTOs,
1652
+ routerName
1653
+ );
1654
+ const saveData = {
1655
+ lastPullDate: res.lastPullDate,
1656
+ translatesDTOs: data
1657
+ };
1658
+ const languageStore = store2.namespace(`I18N_${_.toUpper(appCode)}`);
1659
+ languageStore.set(language, saveData, ":"); // Only update the specific key
1660
+ resolve(data);
1661
+ } else {
1662
+ resolve(langData);
1663
+ }
1664
+ }
1665
+ }
1666
+ });
1667
+ };
1668
+ ```
1669
+
1670
+ ### 服务层HTTP封装
1671
+
1672
+ **更新** service.js文件也采用了相同的集中式认证管理模式:
1673
+
1674
+ ```javascript
1675
+ // 独立 Axios 实例,避免宿主环境拦截或修改全局 axios
1676
+ const http = axios.create();
1677
+
1678
+ export const getLanguagesWithCache = async ({
1679
+ baseUrl,
1680
+ token,
1681
+ appCode,
1682
+ isRemote = false
1683
+ }) => {
1684
+ // 先从缓存获取
1685
+ const cached = store2.get("I18N_LANGUAGES");
1686
+ if (cached && Array.isArray(cached) && cached.length > 0 && !isRemote) {
1687
+ return cached.map((l) => ({
1688
+ langCode: l.language || l.langCode || l,
1689
+ langName: l.langName || l.language || l
1690
+ }));
1691
+ }
1692
+
1693
+ // 从 API 获取
1694
+ if (!appCode) {
1695
+ return [];
1696
+ }
1697
+
1698
+ try {
1699
+ const res = await http.get(`${baseUrl}/i18n-web//app/getsupportedlangs`, {
1700
+ headers: {
1701
+ appCode,
1702
+ Authorization: token
1703
+ }
1704
+ });
1705
+ ```
1706
+
1707
+ ### 认证流程优势
1708
+
1709
+ 1. **集中管理**: 所有HTTP请求都通过统一的拦截器处理认证
1710
+ 2. **自动添加**: 无需在每个请求中手动添加Authorization头
1711
+ 3. **环境隔离**: 独立的axios实例避免了宿主环境的干扰
1712
+ 4. **令牌持久化**: 通过store2存储和管理认证令牌
1713
+ 5. **错误处理**: 统一的认证错误处理机制
1714
+
1715
+ **本节来源**
1716
+ - [packages/index.js:51-62](file://packages/index.js#L51-L62)
1717
+ - [packages/libs/service.js:7-45](file://packages/libs/service.js#L7-L45)