yidaconnector 2026.6.11

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 (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +383 -0
  3. package/bin/yida.js +670 -0
  4. package/lib/app/form-navigation.js +58 -0
  5. package/lib/app/get-schema.js +538 -0
  6. package/lib/auth/auth.js +294 -0
  7. package/lib/auth/cdp-browser-login.js +390 -0
  8. package/lib/auth/codex-login.js +71 -0
  9. package/lib/auth/login.js +475 -0
  10. package/lib/auth/org.js +363 -0
  11. package/lib/auth/qr-login.js +1563 -0
  12. package/lib/core/chalk.js +384 -0
  13. package/lib/core/check-update.js +82 -0
  14. package/lib/core/cli-error.js +39 -0
  15. package/lib/core/command-manifest.js +106 -0
  16. package/lib/core/env-cmd.js +545 -0
  17. package/lib/core/env-manager.js +601 -0
  18. package/lib/core/env.js +287 -0
  19. package/lib/core/i18n.js +177 -0
  20. package/lib/core/locales/ar.js +805 -0
  21. package/lib/core/locales/de.js +805 -0
  22. package/lib/core/locales/en.js +1623 -0
  23. package/lib/core/locales/es.js +805 -0
  24. package/lib/core/locales/fr.js +805 -0
  25. package/lib/core/locales/hi.js +805 -0
  26. package/lib/core/locales/ja.js +1197 -0
  27. package/lib/core/locales/ko.js +807 -0
  28. package/lib/core/locales/pt.js +805 -0
  29. package/lib/core/locales/vi.js +805 -0
  30. package/lib/core/locales/zh-HK.js +1233 -0
  31. package/lib/core/locales/zh.js +1584 -0
  32. package/lib/core/query-data.js +781 -0
  33. package/lib/core/redact.js +100 -0
  34. package/lib/core/utils.js +799 -0
  35. package/lib/core/yida-client.js +117 -0
  36. package/package.json +94 -0
  37. package/project/config.json +4 -0
  38. package/project/pages/src/demo-birthday-game.oyd.jsx +832 -0
  39. package/project/pages/src/demo-chip-insight.oyd.jsx +983 -0
  40. package/project/pages/src/demo-compat-smoke.oyd.jsx +58 -0
  41. package/project/pages/src/demo-crm-batch-entry.oyd.jsx +805 -0
  42. package/project/pages/src/demo-crm-dashboard.oyd.jsx +677 -0
  43. package/project/pages/src/demo-future-vision-2026.oyd.jsx +1102 -0
  44. package/project/pages/src/demo-ppt.oyd.jsx +1192 -0
  45. package/project/pages/src/demo-salary-calculator.oyd.jsx +904 -0
  46. package/project/pages/src/yidaconnector-knowledge-doc.oyd.jsx +1714 -0
  47. package/project/prd/demo-birthday-game.md +39 -0
  48. package/project/prd/demo-crm.md +463 -0
  49. package/project/prd/demo-dingtalk-ai-solution-center.md +425 -0
  50. package/project/prd/demo-future-vision-2026.md +78 -0
  51. package/project/prd/demo-salary-calculator.md +101 -0
  52. package/scripts/build-skills-package.js +406 -0
  53. package/scripts/check-syntax.js +59 -0
  54. package/scripts/demo-dws.sh +106 -0
  55. package/scripts/e2e-real/cleanup.js +67 -0
  56. package/scripts/e2e-real/fixtures/form-fields.json +18 -0
  57. package/scripts/e2e-real/full-runner.js +1566 -0
  58. package/scripts/e2e-real/runner.js +293 -0
  59. package/scripts/e2e-real/skill-coverage.js +115 -0
  60. package/scripts/generate-command-docs.js +109 -0
  61. package/scripts/nightly-smoke.js +134 -0
  62. package/scripts/postinstall.js +545 -0
  63. package/scripts/solution-center-runner.js +368 -0
  64. package/scripts/validate-ci.sh +50 -0
  65. package/scripts/validate-command-manifest.js +119 -0
  66. package/scripts/validate-package-size.js +78 -0
  67. package/scripts/validate-skills.js +247 -0
  68. package/scripts/validate-structure.js +66 -0
  69. package/yida-skills/SKILL.md +163 -0
  70. package/yida-skills/references/yida-api.md +1309 -0
  71. package/yida-skills/skills/large-file-write/SKILL.md +91 -0
  72. package/yida-skills/skills/large-file-write/references/write-patterns.md +149 -0
  73. package/yida-skills/skills/large-file-write/scripts/write.js +157 -0
  74. package/yida-skills/skills/yida-data-management/SKILL.md +252 -0
  75. package/yida-skills/skills/yida-data-management/references/api-matrix.md +49 -0
  76. package/yida-skills/skills/yida-data-management/references/data-format-guide.md +159 -0
  77. package/yida-skills/skills/yida-data-management/references/verified-endpoints.md +62 -0
  78. package/yida-skills/skills/yida-login/SKILL.md +159 -0
  79. package/yida-skills/skills/yida-logout/SKILL.md +67 -0
@@ -0,0 +1,287 @@
1
+ /**
2
+ * env.js - 宜搭 CLI 环境检测
3
+ *
4
+ * 通过环境变量 + 文件特征检测当前运行环境,并输出环境信息。
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+ const os = require('os');
12
+ const {
13
+ detectActiveTool,
14
+ findProjectRoot,
15
+ loadCookieData,
16
+ resolveBaseUrl,
17
+ extractInfoFromCookies,
18
+ resolveWukongWorkspaceRoot,
19
+ } = require('./utils');
20
+ const { t } = require('./i18n');
21
+
22
+ const home = os.homedir();
23
+
24
+ /**
25
+ * 获取所有已安装的 AI 工具列表(用于展示)。
26
+ * 不判断当前是否活跃,只判断是否安装过。
27
+ * 使用 path.join 拼接路径,兼容 Windows 和 macOS/Linux。
28
+ *
29
+ * @returns {Array} 已安装工具列表
30
+ */
31
+ function getInstalledTools() {
32
+ const tools = [
33
+ { dirName: '.real', displayName: '悟空(Wukong)' },
34
+ { dirName: '.codex', displayName: 'Codex' },
35
+ // Windows 上配置目录为 ~/.config/opencode,macOS/Linux 为 ~/.opencode
36
+ {
37
+ dirName: process.platform === 'win32' ? path.join('.config', 'opencode') : '.opencode',
38
+ displayName: 'OpenCode',
39
+ },
40
+ { dirName: '.claude', displayName: 'Claude Code' },
41
+ { dirName: '.aone_copilot', displayName: 'Aone Copilot' },
42
+ { dirName: '.cursor', displayName: 'Cursor' },
43
+ { dirName: '.qoderwork', displayName: 'QoderWork' },
44
+ { dirName: '.qoder', displayName: 'Qoder' },
45
+ ];
46
+
47
+ return tools.filter(({ dirName }) => {
48
+ return fs.existsSync(path.join(home, dirName));
49
+ });
50
+ }
51
+
52
+ /**
53
+ * 检测当前 AI 工具环境。
54
+ * 返回当前活跃工具信息和所有已安装工具列表。
55
+ */
56
+ function detectEnvironment() {
57
+ const activeTool = detectActiveTool();
58
+ const installedTools = getInstalledTools();
59
+ const cwdProject = path.join(process.cwd(), 'project');
60
+
61
+ // 构建结果列表
62
+ const results = installedTools.map(({ dirName, displayName }) => {
63
+ const isWukong = dirName === '.real';
64
+ const isActive = activeTool && activeTool.dirName === dirName;
65
+ // path.join 在 Windows 上自动使用反斜杠,兼容所有平台
66
+ const workspaceRoot = isWukong
67
+ ? resolveWukongWorkspaceRoot(process.env.AGENT_WORK_ROOT || path.join(home, '.real', 'workspace'))
68
+ : cwdProject;
69
+ const hasProject = fs.existsSync(workspaceRoot);
70
+
71
+ return {
72
+ displayName,
73
+ dirName,
74
+ isActive: !!isActive,
75
+ hasProject,
76
+ workspaceRoot,
77
+ };
78
+ });
79
+
80
+ // 当前生效环境
81
+ const activeToolName = activeTool ? activeTool.displayName : null;
82
+ const activeProjectRoot = activeTool ? activeTool.workspaceRoot : null;
83
+
84
+ return { activeToolName, activeProjectRoot, results };
85
+ }
86
+ function getCookieDiagnostics(projectRoot) {
87
+ const { getCurrentEnvConfig, getCookieFilePath } = require('./env-manager');
88
+ const currentEnv = getCurrentEnvConfig(projectRoot);
89
+ const envCookieFile = getCookieFilePath(projectRoot);
90
+ const legacyCookieFile = path.join(projectRoot, '.cache', 'cookies.json');
91
+ const envCookieFileFound = fs.existsSync(envCookieFile);
92
+ const legacyCookieFileFound = fs.existsSync(legacyCookieFile);
93
+ const selectedCookieFile = envCookieFileFound ? envCookieFile : legacyCookieFile;
94
+
95
+ return {
96
+ currentEnv: currentEnv.name,
97
+ configuredCookieFile: envCookieFile,
98
+ legacyCookieFile,
99
+ cookieFile: selectedCookieFile,
100
+ cookieFileFound: envCookieFileFound || legacyCookieFileFound,
101
+ usedLegacyCookieFile: !envCookieFileFound && legacyCookieFileFound,
102
+ };
103
+ }
104
+
105
+ /**
106
+ * 检测登录态信息。
107
+ */
108
+ function detectLoginStatus(projectRoot) {
109
+ const cookieDiagnostics = getCookieDiagnostics(projectRoot);
110
+ const cookieData = loadCookieData(projectRoot);
111
+ const cookies = cookieData && Array.isArray(cookieData.cookies) ? cookieData.cookies : [];
112
+
113
+ if (!cookieData || !cookieData.cookies) {
114
+ return {
115
+ loggedIn: false,
116
+ canAutoUse: false,
117
+ csrfToken: null,
118
+ corpId: null,
119
+ userId: null,
120
+ baseUrl: null,
121
+ cookiesCount: 0,
122
+ diagnostics: {
123
+ ...cookieDiagnostics,
124
+ cacheReadable: false,
125
+ cookiesArrayFound: false,
126
+ csrfTokenFound: false,
127
+ corpIdFound: false,
128
+ userIdFound: false,
129
+ baseUrlFound: false,
130
+ failureReason: cookieDiagnostics.cookieFileFound ? 'cookie_cache_unreadable_or_invalid' : 'cookie_cache_missing',
131
+ },
132
+ };
133
+ }
134
+
135
+ const { csrfToken, corpId, userId } = extractInfoFromCookies(cookies);
136
+ const baseUrl = resolveBaseUrl(cookieData);
137
+ const loggedIn = !!csrfToken;
138
+
139
+ return {
140
+ loggedIn,
141
+ canAutoUse: loggedIn,
142
+ csrfToken,
143
+ corpId,
144
+ userId,
145
+ baseUrl,
146
+ cookiesCount: cookies.length,
147
+ diagnostics: {
148
+ ...cookieDiagnostics,
149
+ cacheReadable: true,
150
+ cookiesArrayFound: Array.isArray(cookieData.cookies),
151
+ csrfTokenFound: !!csrfToken,
152
+ corpIdFound: !!corpId,
153
+ userIdFound: !!userId,
154
+ baseUrlFound: !!baseUrl,
155
+ failureReason: loggedIn ? null : 'csrf_token_missing',
156
+ },
157
+ };
158
+ }
159
+
160
+ function maskSecret(value) {
161
+ if (!value) {return null;}
162
+ return `${value.slice(0, 16)}...`;
163
+ }
164
+
165
+ /**
166
+ * 构建适合 AI agent 读取的环境快照。
167
+ */
168
+ function buildEnvironmentSnapshot() {
169
+ const environment = detectEnvironment();
170
+ const projectRoot = (environment.activeProjectRoot && fs.existsSync(environment.activeProjectRoot))
171
+ ? environment.activeProjectRoot
172
+ : findProjectRoot();
173
+ const loginStatus = detectLoginStatus(projectRoot);
174
+
175
+ return {
176
+ ok: true,
177
+ system: {
178
+ platform: process.platform,
179
+ arch: os.arch(),
180
+ node: process.version,
181
+ home: os.homedir(),
182
+ cwd: process.cwd(),
183
+ },
184
+ active: {
185
+ tool: environment.activeToolName,
186
+ toolKey: environment.results.find((item) => item.isActive)?.dirName || null,
187
+ isWukong: environment.results.some((item) => item.isActive && item.dirName === '.real'),
188
+ projectRoot,
189
+ projectRootExists: fs.existsSync(projectRoot),
190
+ hasConfig: fs.existsSync(path.join(projectRoot, 'config.json')),
191
+ },
192
+ tools: environment.results,
193
+ login: {
194
+ loggedIn: loginStatus.loggedIn,
195
+ canAutoUse: loginStatus.canAutoUse,
196
+ baseUrl: loginStatus.baseUrl,
197
+ corpId: loginStatus.corpId,
198
+ userId: loginStatus.userId,
199
+ csrfToken: maskSecret(loginStatus.csrfToken),
200
+ cookiesCount: loginStatus.cookiesCount,
201
+ diagnostics: loginStatus.diagnostics,
202
+ },
203
+ };
204
+ }
205
+
206
+ /**
207
+ * 执行环境检测并打印结果。
208
+ */
209
+ function run(args = []) {
210
+ if (args.includes('--json')) {
211
+ console.log(JSON.stringify(buildEnvironmentSnapshot(), null, 2));
212
+ return;
213
+ }
214
+
215
+ const { c, sep, banner, success, warn, label } = require('./chalk');
216
+
217
+ banner(t('env.title'), { stderr: false });
218
+
219
+ // ── 系统信息 ──────────────────────────────────────
220
+ console.log(`\n ${c.bold}${c.cyan}${t('env.system_info').replace(/^[\s─=]+/, '').replace(/[\s─=]+$/, '')}${c.reset}`);
221
+ label('OS', `${process.platform} ${os.arch()}`, { stderr: false });
222
+ label('Node', process.version, { stderr: false });
223
+ label('Home', os.homedir(), { stderr: false });
224
+ label('CWD', process.cwd(), { stderr: false });
225
+
226
+ // ── AI 工具检测 ────────────────────────────────────
227
+ console.log(`\n ${c.bold}${c.cyan}${t('env.ai_tools').replace(/^[\s─=]+/, '').replace(/[\s─=]+$/, '')}${c.reset}`);
228
+ const { activeToolName, activeProjectRoot, results } = detectEnvironment();
229
+
230
+ if (results.length === 0) {
231
+ warn(t('env.no_tools'), false);
232
+ } else {
233
+ for (const { displayName, isActive, hasProject } of results) {
234
+ let statusIcon, note;
235
+ if (isActive && hasProject) {
236
+ statusIcon = `${c.green}✔${c.reset}`;
237
+ note = `${c.green}${t('env.tool_active_ready')}${c.reset}`;
238
+ } else if (isActive && !hasProject) {
239
+ statusIcon = `${c.yellow}●${c.reset}`;
240
+ note = `${c.yellow}${t('env.tool_active_no_project')}${c.reset}`;
241
+ } else if (!isActive && hasProject) {
242
+ statusIcon = `${c.dim}○${c.reset}`;
243
+ note = `${c.dim}${t('env.tool_installed_has_project')}${c.reset}`;
244
+ } else {
245
+ statusIcon = `${c.dim}○${c.reset}`;
246
+ note = `${c.dim}${t('env.tool_installed')}${c.reset}`;
247
+ }
248
+ console.log(` ${statusIcon} ${displayName.padEnd(18)} ${note}`);
249
+ }
250
+ }
251
+
252
+ // ── 当前生效环境 ───────────────────────────────────
253
+ console.log(`\n ${c.bold}${c.cyan}${t('env.active_env').replace(/^[\s─=]+/, '').replace(/[\s─=]+$/, '')}${c.reset}`);
254
+ if (activeToolName && activeProjectRoot) {
255
+ label('AI Tool', `${c.green}${activeToolName}${c.reset}`, { stderr: false });
256
+ label('Project', activeProjectRoot, { stderr: false });
257
+ } else {
258
+ const activeOnly = results.filter((r) => r.isActive);
259
+ if (activeOnly.length > 0) {
260
+ warn(t('env.active_no_project', activeOnly.map((r) => r.displayName).join(', ')), false);
261
+ } else {
262
+ warn(t('env.no_active_tool'), false);
263
+ }
264
+ label('Fallback', process.cwd(), { stderr: false });
265
+ }
266
+
267
+ // ── 登录态检测 ─────────────────────────────────────
268
+ console.log(`\n ${c.bold}${c.cyan}${t('env.login_status').replace(/^[\s─=]+/, '').replace(/[\s─=]+$/, '')}${c.reset}`);
269
+ const projectRoot = (activeProjectRoot && fs.existsSync(activeProjectRoot))
270
+ ? activeProjectRoot
271
+ : findProjectRoot();
272
+ const loginStatus = detectLoginStatus(projectRoot);
273
+
274
+ if (loginStatus.loggedIn) {
275
+ success(t('env.logged_in'), false);
276
+ label('Base URL', loginStatus.baseUrl, { stderr: false });
277
+ label('Corp ID', loginStatus.corpId || t('env.unknown'), { stderr: false });
278
+ label('User ID', loginStatus.userId || t('env.unknown'), { stderr: false });
279
+ label('CSRF', `${loginStatus.csrfToken ? loginStatus.csrfToken.slice(0, 16) : 'N/A'}…`, { stderr: false });
280
+ } else {
281
+ warn(t('env.not_logged_in'), false);
282
+ }
283
+
284
+ console.log(`\n ${sep()}\n`);
285
+ }
286
+
287
+ module.exports = { run, detectEnvironment, detectLoginStatus, buildEnvironmentSnapshot };
@@ -0,0 +1,177 @@
1
+ /**
2
+ * i18n.js - 国际化支持模块
3
+ *
4
+ * 支持语言:
5
+ * zh - 简体中文(默认)
6
+ * zh-HK - 繁體中文(香港)
7
+ * en - English
8
+ * ja - 日本語
9
+ * ko - 한국어
10
+ * fr - Français
11
+ * de - Deutsch
12
+ * es - Español
13
+ * pt - Português
14
+ * vi - Tiếng Việt
15
+ * hi - हिन्दी
16
+ * ar - العربية
17
+ *
18
+ * 语言检测优先级:
19
+ * 1. YIDACONNECTOR_LANG 环境变量(如:YIDACONNECTOR_LANG=en)
20
+ * 2. LANG / LC_ALL 系统环境变量
21
+ * 3. 默认:zh(简体中文)
22
+ *
23
+ * 用法:
24
+ * const { t } = require('./i18n');
25
+ * console.log(t('login.success'));
26
+ * console.log(t('create_app.usage', 'yidaconnector'));
27
+ */
28
+
29
+ 'use strict';
30
+
31
+ const SUPPORTED_LANGUAGES = ['zh', 'zh-HK', 'en', 'ja', 'ko', 'fr', 'de', 'es', 'pt', 'vi', 'hi', 'ar'];
32
+ const DEFAULT_LANGUAGE = 'zh';
33
+
34
+ /**
35
+ * 将系统 locale 代码映射到支持的语言代码。
36
+ * @param {string} code - 系统 locale 代码(小写)
37
+ * @returns {string|null}
38
+ */
39
+ function mapLocaleToLanguage(code) {
40
+ const localeMap = {
41
+ zh: 'zh',
42
+ 'zh-hk': 'zh-HK',
43
+ 'zh-mo': 'zh-HK',
44
+ 'zh-hant': 'zh-HK',
45
+ 'zh-tw': 'zh-HK',
46
+ en: 'en',
47
+ ja: 'ja',
48
+ ko: 'ko',
49
+ fr: 'fr',
50
+ de: 'de',
51
+ es: 'es',
52
+ pt: 'pt',
53
+ vi: 'vi',
54
+ hi: 'hi',
55
+ ar: 'ar',
56
+ };
57
+ return localeMap[code] || null;
58
+ }
59
+
60
+ /**
61
+ * 从环境变量或系统 locale 中检测当前语言。
62
+ * @returns {string} 支持的语言代码
63
+ */
64
+ function detectLanguage() {
65
+ // 优先级1:YIDACONNECTOR_LANG 环境变量(支持完整 locale,如 zh-HK、en-US)
66
+ const envLang = process.env.YIDACONNECTOR_LANG;
67
+ if (envLang) {
68
+ const normalized = envLang.toLowerCase().replace(/_/g, '-');
69
+ // 先尝试完整匹配(如 zh-HK)
70
+ const fullMatch = mapLocaleToLanguage(normalized);
71
+ if (fullMatch) {return fullMatch;}
72
+ // 再尝试主语言代码匹配(如 en-US → en)
73
+ const primaryCode = normalized.split('-')[0];
74
+ const primaryMatch = mapLocaleToLanguage(primaryCode);
75
+ if (primaryMatch) {return primaryMatch;}
76
+ }
77
+
78
+ // 优先级2:系统 LANG / LC_ALL 环境变量(如 zh_HK.UTF-8、en_US.UTF-8)
79
+ const systemLang = process.env.LC_ALL || process.env.LANG || '';
80
+ const normalizedSystem = systemLang.toLowerCase().replace(/_/g, '-').split('.')[0];
81
+ const systemMatch = mapLocaleToLanguage(normalizedSystem);
82
+ if (systemMatch) {return systemMatch;}
83
+ const systemPrimary = normalizedSystem.split('-')[0];
84
+ const systemPrimaryMatch = mapLocaleToLanguage(systemPrimary);
85
+ if (systemPrimaryMatch) {return systemPrimaryMatch;}
86
+
87
+ // 优先级3:默认简体中文
88
+ return DEFAULT_LANGUAGE;
89
+ }
90
+
91
+ let currentLanguage = detectLanguage();
92
+ let translations = null;
93
+
94
+ /**
95
+ * 懒加载翻译文件。
96
+ * @returns {object} 翻译字典
97
+ */
98
+ function loadTranslations() {
99
+ if (translations) {return translations;}
100
+ try {
101
+ translations = require(`./locales/${currentLanguage}`);
102
+ } catch {
103
+ // 加载失败时回退到中文
104
+ translations = require('./locales/zh');
105
+ }
106
+ return translations;
107
+ }
108
+
109
+ /**
110
+ * 翻译函数,支持 {0} {1} 占位符插值。
111
+ * @param {string} key - 翻译键(支持点号分隔的嵌套路径,如 "login.success")
112
+ * @param {...string} args - 插值参数
113
+ * @returns {string}
114
+ */
115
+ function t(key, ...args) {
116
+ const dict = loadTranslations();
117
+
118
+ // 支持嵌套路径:如 "login.success" → dict.login.success
119
+ const value = key.split('.').reduce((obj, segment) => {
120
+ return obj && typeof obj === 'object' ? obj[segment] : undefined;
121
+ }, dict);
122
+
123
+ if (typeof value !== 'string') {
124
+ // 找不到翻译时,尝试中文兜底
125
+ if (currentLanguage !== 'zh') {
126
+ const zhDict = require('./locales/zh');
127
+ const zhValue = key.split('.').reduce((obj, segment) => {
128
+ return obj && typeof obj === 'object' ? obj[segment] : undefined;
129
+ }, zhDict);
130
+ if (typeof zhValue === 'string') {
131
+ return interpolate(zhValue, args);
132
+ }
133
+ }
134
+ // 最终兜底:返回 key 本身
135
+ return key;
136
+ }
137
+
138
+ return interpolate(value, args);
139
+ }
140
+
141
+ /**
142
+ * 将 {0} {1} 占位符替换为实际参数。
143
+ * @param {string} template
144
+ * @param {string[]} args
145
+ * @returns {string}
146
+ */
147
+ function interpolate(template, args) {
148
+ if (!args || args.length === 0) {return template;}
149
+ return template.replace(/\{(\d+)\}/g, (match, index) => {
150
+ const argValue = args[parseInt(index, 10)];
151
+ return argValue !== undefined ? String(argValue) : match;
152
+ });
153
+ }
154
+
155
+ /**
156
+ * 获取当前语言。
157
+ * @returns {string}
158
+ */
159
+ function getLanguage() {
160
+ return currentLanguage;
161
+ }
162
+
163
+ /**
164
+ * 手动设置语言(主要用于测试)。
165
+ * @param {string} lang - 支持的语言代码(如 "zh"、"en"、"zh-HK"、"ko" 等)
166
+ */
167
+ function setLanguage(lang) {
168
+ const normalized = typeof lang === 'string'
169
+ ? mapLocaleToLanguage(lang.toLowerCase().replace(/_/g, '-'))
170
+ : null;
171
+ if (SUPPORTED_LANGUAGES.includes(normalized)) {
172
+ currentLanguage = normalized;
173
+ translations = null; // 清空缓存,下次 t() 时重新加载
174
+ }
175
+ }
176
+
177
+ module.exports = { t, getLanguage, setLanguage, detectLanguage, SUPPORTED_LANGUAGES };