@agentscope-ai/i18n 1.0.1 → 1.0.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.
- package/README.md +57 -135
- package/i18n.config.example.js +106 -0
- package/lib/cli.js +112 -221
- package/lib/core/ast-processor.js +17 -21
- package/lib/core/checker.js +544 -0
- package/lib/core/file-processor.js +44 -204
- package/lib/core/translator.js +95 -936
- package/lib/index.js +2 -8
- package/lib/parse-jsx.js +33 -23
- package/lib/utils/ast-utils.js +48 -25
- package/lib/utils/file-utils.js +50 -11
- package/package.json +10 -5
- package/skills/i18n-helper/SKILL.md +186 -82
package/lib/cli.js
CHANGED
|
@@ -2,10 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { program } = require('commander');
|
|
4
4
|
const {
|
|
5
|
-
|
|
6
|
-
initMT,
|
|
7
|
-
patch,
|
|
8
|
-
patchMT,
|
|
5
|
+
runTranslation,
|
|
9
6
|
check,
|
|
10
7
|
reverse,
|
|
11
8
|
translate,
|
|
@@ -15,11 +12,9 @@ const {
|
|
|
15
12
|
const path = require('path');
|
|
16
13
|
const fs = require('fs-extra');
|
|
17
14
|
|
|
18
|
-
// 读取配置文件
|
|
19
15
|
const getConfig = (options) => {
|
|
20
16
|
let configPath = options.config;
|
|
21
17
|
|
|
22
|
-
// 如果没有指定配置文件路径,按优先级查找
|
|
23
18
|
if (!configPath) {
|
|
24
19
|
const configFiles = ['i18n.config.js', 'i18n.config.cjs', 'i18n.config.mjs'];
|
|
25
20
|
|
|
@@ -35,31 +30,25 @@ const getConfig = (options) => {
|
|
|
35
30
|
throw new Error(`配置文件不存在,请在项目根目录创建 i18n.config.js 或 i18n.config.cjs`);
|
|
36
31
|
}
|
|
37
32
|
} else {
|
|
38
|
-
// 如果指定了配置文件路径,检查是否存在
|
|
39
33
|
if (!fs.existsSync(configPath)) {
|
|
40
34
|
throw new Error(`配置文件 ${configPath} 不存在`);
|
|
41
35
|
}
|
|
42
36
|
}
|
|
43
37
|
|
|
44
|
-
|
|
45
|
-
return config;
|
|
38
|
+
return require(configPath);
|
|
46
39
|
};
|
|
47
40
|
|
|
48
|
-
// 解析/覆盖 targetPath:命令行优先,其次配置文件
|
|
49
41
|
const resolveTargetPath = (options, config) => {
|
|
50
42
|
const fromCli = options.targetPath || options['target-path'];
|
|
51
|
-
const
|
|
52
|
-
const raw = fromCli || fromConfig;
|
|
43
|
+
const raw = fromCli || (config && config.targetPath);
|
|
53
44
|
|
|
54
45
|
if (!raw) {
|
|
55
46
|
throw new Error('targetPath 未配置:请在配置文件中提供 targetPath,或通过 --target-path 传入');
|
|
56
47
|
}
|
|
57
48
|
|
|
58
|
-
// 统一将相对路径转换为绝对路径(相对于当前工作目录)
|
|
59
49
|
return path.isAbsolute(raw) ? raw : path.resolve(process.cwd(), raw);
|
|
60
50
|
};
|
|
61
51
|
|
|
62
|
-
// 将命令行通用选项统一解析并合并到 config 中
|
|
63
52
|
const resolveCommonOptions = (options, config) => {
|
|
64
53
|
const targetPath = resolveTargetPath(options, config);
|
|
65
54
|
config.targetPath = targetPath;
|
|
@@ -67,187 +56,119 @@ const resolveCommonOptions = (options, config) => {
|
|
|
67
56
|
return targetPath;
|
|
68
57
|
};
|
|
69
58
|
|
|
70
|
-
// 为每个命令添加通用入参:config + target-path + skip-confirm
|
|
71
59
|
const withCommonOptions = (cmd) =>
|
|
72
60
|
cmd
|
|
73
61
|
.option('-c, --config <path>', 'Path to config file')
|
|
74
62
|
.option('-p, --target-path <path>', 'Override targetPath in config file')
|
|
75
63
|
.option('--skip-confirm', 'Skip all confirmation prompts, auto confirm (y/n) questions');
|
|
76
64
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
withCommonOptions(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
.description('Initialize translation for the project using Bailian Agent'),
|
|
86
|
-
).action(async (options) => {
|
|
87
|
-
try {
|
|
88
|
-
const config = getConfig(options);
|
|
89
|
-
const targetPath = resolveCommonOptions(options, config);
|
|
90
|
-
await init(targetPath, config);
|
|
91
|
-
} catch (error) {
|
|
92
|
-
console.error('初始化失败:', error.message);
|
|
93
|
-
process.exit(1);
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
withCommonOptions(
|
|
98
|
-
program
|
|
99
|
-
.command('init-mt')
|
|
100
|
-
.description('Initialize translation using MT (Machine Translation) method'),
|
|
101
|
-
).action(async (options) => {
|
|
102
|
-
try {
|
|
103
|
-
const config = getConfig(options);
|
|
104
|
-
const targetPath = resolveCommonOptions(options, config);
|
|
105
|
-
await initMT(targetPath, config);
|
|
106
|
-
} catch (error) {
|
|
107
|
-
console.error('MT初始化失败:', error.message);
|
|
108
|
-
process.exit(1);
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
withCommonOptions(
|
|
113
|
-
program
|
|
114
|
-
.command('init')
|
|
115
|
-
.description('Initialize translation using MT (Machine Translation) method'),
|
|
116
|
-
).action(async (options) => {
|
|
117
|
-
try {
|
|
118
|
-
const config = getConfig(options);
|
|
119
|
-
const targetPath = resolveCommonOptions(options, config);
|
|
120
|
-
await initMT(targetPath, config);
|
|
121
|
-
} catch (error) {
|
|
122
|
-
console.error('MT初始化失败:', error.message);
|
|
123
|
-
process.exit(1);
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
withCommonOptions(
|
|
128
|
-
program
|
|
129
|
-
.command('patch-agent')
|
|
130
|
-
.description('Update translations for new content using Bailian Agent'),
|
|
131
|
-
).action(async (options) => {
|
|
132
|
-
try {
|
|
133
|
-
const config = getConfig(options);
|
|
134
|
-
const targetPath = resolveCommonOptions(options, config);
|
|
135
|
-
await patch(targetPath, config);
|
|
136
|
-
} catch (error) {
|
|
137
|
-
console.error('补丁更新失败:', error.message);
|
|
138
|
-
process.exit(1);
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
withCommonOptions(
|
|
143
|
-
program
|
|
144
|
-
.command('patch-mt')
|
|
145
|
-
.description('Update translations for new content using MT (Machine Translation) method'),
|
|
146
|
-
).action(async (options) => {
|
|
147
|
-
try {
|
|
148
|
-
const config = getConfig(options);
|
|
149
|
-
const targetPath = resolveCommonOptions(options, config);
|
|
150
|
-
await patchMT(targetPath, config);
|
|
151
|
-
} catch (error) {
|
|
152
|
-
console.error('MT补丁更新失败:', error.message);
|
|
153
|
-
process.exit(1);
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
withCommonOptions(
|
|
158
|
-
program
|
|
159
|
-
.command('patch')
|
|
160
|
-
.description('Update translations for new content using MT (Machine Translation) method'),
|
|
161
|
-
).action(async (options) => {
|
|
162
|
-
try {
|
|
163
|
-
const config = getConfig(options);
|
|
164
|
-
const targetPath = resolveCommonOptions(options, config);
|
|
165
|
-
await patchMT(targetPath, config);
|
|
166
|
-
} catch (error) {
|
|
167
|
-
console.error('MT补丁更新失败:', error.message);
|
|
168
|
-
process.exit(1);
|
|
169
|
-
}
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
withCommonOptions(program.command('check').description('Check translation status'))
|
|
173
|
-
.option('--auto-delete-unused', 'Automatically delete unused translation keys')
|
|
174
|
-
.option(
|
|
175
|
-
'--summary-only',
|
|
176
|
-
'Only output missing key counts per language, skip deletion and file generation',
|
|
177
|
-
)
|
|
178
|
-
.action(async (options) => {
|
|
65
|
+
/**
|
|
66
|
+
* 注册一个带通用选项的命令,自动处理 config 加载、targetPath 解析和错误处理。
|
|
67
|
+
*/
|
|
68
|
+
const registerCommand = (name, description, handler, { extraOptions = [], useCommon = true } = {}) => {
|
|
69
|
+
const cmd = program.command(name).description(description);
|
|
70
|
+
if (useCommon) withCommonOptions(cmd);
|
|
71
|
+
extraOptions.forEach((opt) => cmd.option(...opt));
|
|
72
|
+
cmd.action(async (options) => {
|
|
179
73
|
try {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
74
|
+
if (useCommon) {
|
|
75
|
+
const config = getConfig(options);
|
|
76
|
+
resolveCommonOptions(options, config);
|
|
77
|
+
await handler(config, options);
|
|
78
|
+
} else {
|
|
79
|
+
await handler(options);
|
|
80
|
+
}
|
|
185
81
|
} catch (error) {
|
|
186
|
-
console.error(
|
|
82
|
+
console.error(`❌ ${description}失败:`, error.message);
|
|
187
83
|
process.exit(1);
|
|
188
84
|
}
|
|
189
85
|
});
|
|
86
|
+
return cmd;
|
|
87
|
+
};
|
|
190
88
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
)
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
if (options.file)
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
89
|
+
program
|
|
90
|
+
.name('i18n')
|
|
91
|
+
.description('A tool for translating Chinese content in frontend code repositories')
|
|
92
|
+
.version('1.0.1');
|
|
93
|
+
|
|
94
|
+
// --- 核心翻译命令 ---
|
|
95
|
+
|
|
96
|
+
registerCommand(
|
|
97
|
+
'init',
|
|
98
|
+
'初始化翻译',
|
|
99
|
+
(config, options) => runTranslation(config.targetPath, config, {
|
|
100
|
+
isUpdate: false,
|
|
101
|
+
engine: options.engine || 'mt',
|
|
102
|
+
}),
|
|
103
|
+
{ extraOptions: [['--engine <engine>', 'Translation engine: mt (default) or agent', 'mt']] },
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
registerCommand(
|
|
107
|
+
'patch',
|
|
108
|
+
'增量翻译',
|
|
109
|
+
(config, options) => runTranslation(config.targetPath, config, {
|
|
110
|
+
isUpdate: true,
|
|
111
|
+
engine: options.engine || 'mt',
|
|
112
|
+
}),
|
|
113
|
+
{ extraOptions: [['--engine <engine>', 'Translation engine: mt (default) or agent', 'mt']] },
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
registerCommand(
|
|
117
|
+
'check',
|
|
118
|
+
'检查翻译状态',
|
|
119
|
+
(config, options) => {
|
|
120
|
+
config.autoDeleteUnused = options.autoDeleteUnused;
|
|
121
|
+
config.summaryOnly = options.summaryOnly;
|
|
122
|
+
return check(config.targetPath, config);
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
extraOptions: [
|
|
126
|
+
['--auto-delete-unused', 'Automatically delete unused translation keys'],
|
|
127
|
+
['--summary-only', 'Only output missing key counts per language, skip deletion and file generation'],
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
registerCommand('reverse', '还原国际化代码为中文', (config) =>
|
|
133
|
+
reverse(config.targetPath, config),
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
registerCommand(
|
|
137
|
+
'translate',
|
|
138
|
+
'提取中文并翻译生成locale文件 (Step 1/3, 不修改源码)',
|
|
139
|
+
(config, options) => {
|
|
140
|
+
if (options.file) config.file = options.file;
|
|
141
|
+
return translate(config.targetPath, config);
|
|
142
|
+
},
|
|
143
|
+
{ extraOptions: [['-f, --file <path>', 'Process a single file instead of the entire targetPath directory']] },
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
registerCommand(
|
|
147
|
+
'replace',
|
|
148
|
+
'用翻译状态替换代码中的中文 (Step 3/3)',
|
|
149
|
+
(config, options) => {
|
|
150
|
+
if (options.file) config.file = options.file;
|
|
151
|
+
return replaceOnly(config.targetPath, config);
|
|
152
|
+
},
|
|
153
|
+
{ extraOptions: [['-f, --file <path>', 'Process a single file instead of the entire targetPath directory']] },
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
// --- 向后兼容别名(隐藏,不在 help 中显示) ---
|
|
157
|
+
|
|
158
|
+
const registerHiddenAlias = (name, target) => {
|
|
159
|
+
program.command(name, { hidden: true }).action(() => {
|
|
160
|
+
console.log(`⚠️ "${name}" 已弃用,请使用 "${target}" 代替`);
|
|
161
|
+
process.argv[2] = target;
|
|
162
|
+
program.parse(process.argv);
|
|
163
|
+
});
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
registerHiddenAlias('init-mt', 'init');
|
|
167
|
+
registerHiddenAlias('init-agent', 'init --engine agent');
|
|
168
|
+
registerHiddenAlias('patch-mt', 'patch');
|
|
169
|
+
registerHiddenAlias('patch-agent', 'patch --engine agent');
|
|
170
|
+
|
|
171
|
+
// --- 工具命令 ---
|
|
251
172
|
|
|
252
173
|
program
|
|
253
174
|
.command('init-config')
|
|
@@ -269,14 +190,12 @@ program
|
|
|
269
190
|
|
|
270
191
|
fs.copySync(templatePath, configPath);
|
|
271
192
|
|
|
272
|
-
// 检查 .env 文件是否存在
|
|
273
193
|
const envPath = path.resolve(cwd, '.env');
|
|
274
194
|
if (!fs.existsSync(envPath)) {
|
|
275
195
|
fs.writeFileSync(envPath, 'DASHSCOPE_APIKEY=\n', 'utf8');
|
|
276
196
|
console.log('📝 已创建 .env 文件,请填写 DASHSCOPE_APIKEY');
|
|
277
197
|
}
|
|
278
198
|
|
|
279
|
-
// 检查 .gitignore 中是否包含 .env
|
|
280
199
|
const gitignorePath = path.resolve(cwd, '.gitignore');
|
|
281
200
|
if (fs.existsSync(gitignorePath)) {
|
|
282
201
|
const gitignoreContent = fs.readFileSync(gitignorePath, 'utf8');
|
|
@@ -313,7 +232,6 @@ program
|
|
|
313
232
|
try {
|
|
314
233
|
const cwd = process.cwd();
|
|
315
234
|
|
|
316
|
-
// 读取配置
|
|
317
235
|
let config = {};
|
|
318
236
|
try {
|
|
319
237
|
config = getConfig(options);
|
|
@@ -339,13 +257,10 @@ program
|
|
|
339
257
|
process.exit(1);
|
|
340
258
|
}
|
|
341
259
|
if (!appId) {
|
|
342
|
-
console.error(
|
|
343
|
-
'❌ 缺少 appId,请通过 --app-id 参数传入,或在 i18n.config.js 的 medusa.appId 中配置',
|
|
344
|
-
);
|
|
260
|
+
console.error('❌ 缺少 appId,请通过 --app-id 参数传入,或在 i18n.config.js 的 medusa.appId 中配置');
|
|
345
261
|
process.exit(1);
|
|
346
262
|
}
|
|
347
263
|
|
|
348
|
-
// 各 IDE/Agent 的 skills 目录映射
|
|
349
264
|
const agentDirMap = {
|
|
350
265
|
cursor: '.cursor/skills',
|
|
351
266
|
qoder: '.qoder/skills',
|
|
@@ -354,10 +269,8 @@ program
|
|
|
354
269
|
opencode: '.opencode/skill',
|
|
355
270
|
};
|
|
356
271
|
|
|
357
|
-
// 确定目标 agents
|
|
358
272
|
let targetAgents = options.agent || [];
|
|
359
273
|
if (targetAgents.length === 0) {
|
|
360
|
-
// 自动检测:目录已存在的 IDE 默认安装,否则至少安装 cursor
|
|
361
274
|
targetAgents = Object.entries(agentDirMap)
|
|
362
275
|
.filter(([, dir]) => fs.existsSync(path.resolve(cwd, dir.split('/')[0])))
|
|
363
276
|
.map(([name]) => name);
|
|
@@ -366,7 +279,6 @@ program
|
|
|
366
279
|
}
|
|
367
280
|
}
|
|
368
281
|
|
|
369
|
-
// 读取模板
|
|
370
282
|
const skillSource = path.join(__dirname, '../skills/i18n-helper/SKILL.md');
|
|
371
283
|
if (!fs.existsSync(skillSource)) {
|
|
372
284
|
throw new Error(`SKILL.md 模板未找到: ${skillSource}`);
|
|
@@ -379,7 +291,6 @@ program
|
|
|
379
291
|
content = content.replace(/\{\{LOCALES_FILE_PATH\}\}/g, localesFilePath);
|
|
380
292
|
content = content.replace(/\{\{LANGUAGES\}\}/g, languages.join('、'));
|
|
381
293
|
|
|
382
|
-
// 写入各 IDE 目录
|
|
383
294
|
const installed = [];
|
|
384
295
|
for (const agent of targetAgents) {
|
|
385
296
|
const baseDir = agentDirMap[agent];
|
|
@@ -428,7 +339,6 @@ program
|
|
|
428
339
|
console.log('🚀 Medusa 国际化文件拉取工具');
|
|
429
340
|
console.log('='.repeat(50));
|
|
430
341
|
|
|
431
|
-
// 尝试读取配置文件
|
|
432
342
|
let configFileSettings = {};
|
|
433
343
|
try {
|
|
434
344
|
const configFromFile = getConfig(options);
|
|
@@ -441,50 +351,32 @@ program
|
|
|
441
351
|
|
|
442
352
|
const appName = options.app || configFileSettings.appName;
|
|
443
353
|
if (!appName) {
|
|
444
|
-
console.error(
|
|
445
|
-
'❌ 缺少 Medusa 应用名称 appName:请通过 --app 传入,或在 i18n.config.js 的 medusa.appName 中配置',
|
|
446
|
-
);
|
|
354
|
+
console.error('❌ 缺少 Medusa 应用名称 appName:请通过 --app 传入,或在 i18n.config.js 的 medusa.appName 中配置');
|
|
447
355
|
process.exit(1);
|
|
448
356
|
}
|
|
449
357
|
|
|
450
|
-
// tag 选填:命令行优先,其次配置文件,不设默认值
|
|
451
358
|
const tag = options.tag ?? configFileSettings.tag;
|
|
452
359
|
|
|
453
|
-
// 合并配置文件和命令行参数(命令行参数优先)
|
|
454
360
|
const config = {
|
|
455
|
-
medusa: {
|
|
456
|
-
appName,
|
|
457
|
-
tag,
|
|
458
|
-
},
|
|
361
|
+
medusa: { appName, tag },
|
|
459
362
|
type: options.type || configFileSettings.type || 'json',
|
|
460
363
|
cwd: process.cwd(),
|
|
461
364
|
path: options.output || configFileSettings.outputPath || './locales',
|
|
462
365
|
mergeExisting: options.merge !== false && configFileSettings.mergeExisting !== false,
|
|
463
366
|
};
|
|
464
367
|
|
|
465
|
-
// 显示配置信息
|
|
466
368
|
console.log('📝 配置信息:');
|
|
467
|
-
console.log(
|
|
468
|
-
` 应用名称: ${config.medusa.appName} ${options.app ? '(命令行)' : '(配置文件)'}`,
|
|
469
|
-
);
|
|
369
|
+
console.log(` 应用名称: ${config.medusa.appName} ${options.app ? '(命令行)' : '(配置文件)'}`);
|
|
470
370
|
const tagSource =
|
|
471
371
|
options.tag != null && options.tag !== ''
|
|
472
372
|
? '(命令行)'
|
|
473
373
|
: configFileSettings.tag != null && configFileSettings.tag !== ''
|
|
474
374
|
? '(配置文件)'
|
|
475
375
|
: '';
|
|
476
|
-
console.log(
|
|
477
|
-
|
|
478
|
-
);
|
|
479
|
-
console.log(
|
|
480
|
-
` 包类型: ${config.type} ${options.type ? '(命令行)' : configFileSettings.type ? '(配置文件)' : '(默认)'}`,
|
|
481
|
-
);
|
|
482
|
-
console.log(
|
|
483
|
-
` 输出目录: ${path.resolve(config.path)} ${options.output ? '(命令行)' : configFileSettings.outputPath ? '(配置文件)' : '(默认)'}`,
|
|
484
|
-
);
|
|
485
|
-
console.log(
|
|
486
|
-
` 合并现有文件: ${config.mergeExisting ? '是' : '否'} ${options.merge === false ? '(命令行)' : configFileSettings.mergeExisting !== undefined ? '(配置文件)' : '(默认)'}`,
|
|
487
|
-
);
|
|
376
|
+
console.log(` 标签名称: ${config.medusa.tag ?? '(未设置)'}${tagSource ? ` ${tagSource}` : ''}`);
|
|
377
|
+
console.log(` 包类型: ${config.type} ${options.type ? '(命令行)' : configFileSettings.type ? '(配置文件)' : '(默认)'}`);
|
|
378
|
+
console.log(` 输出目录: ${path.resolve(config.path)} ${options.output ? '(命令行)' : configFileSettings.outputPath ? '(配置文件)' : '(默认)'}`);
|
|
379
|
+
console.log(` 合并现有文件: ${config.mergeExisting ? '是' : '否'} ${options.merge === false ? '(命令行)' : configFileSettings.mergeExisting !== undefined ? '(配置文件)' : '(默认)'}`);
|
|
488
380
|
console.log('');
|
|
489
381
|
|
|
490
382
|
console.log('🔄 开始从 Medusa 平台拉取文件...');
|
|
@@ -495,7 +387,6 @@ program
|
|
|
495
387
|
} catch (error) {
|
|
496
388
|
console.error('❌ Medusa 拉取失败:', error.message);
|
|
497
389
|
|
|
498
|
-
// 提供更详细的错误信息
|
|
499
390
|
if (error.response) {
|
|
500
391
|
console.error(' HTTP 状态码:', error.response.status);
|
|
501
392
|
if (error.response.status === 404) {
|
|
@@ -2,30 +2,26 @@ const babylon = require('@babel/parser');
|
|
|
2
2
|
const traverse = require('@babel/traverse').default;
|
|
3
3
|
const generate = require('@babel/generator').default;
|
|
4
4
|
const t = require('@babel/types');
|
|
5
|
+
const { isMatchingCallee } = require('../utils/ast-utils');
|
|
5
6
|
|
|
6
7
|
// 检查i18n调用中id和dm是否匹配
|
|
7
|
-
const checkI18nCall = (node) => {
|
|
8
|
-
if (
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const args = node.arguments;
|
|
17
|
-
if (args.length > 0 && args[0].type === 'ObjectExpression') {
|
|
18
|
-
const idProperty = args[0].properties.find((prop) => prop.key && prop.key.name === 'id');
|
|
19
|
-
const dmProperty = args[0].properties.find((prop) => prop.key && prop.key.name === 'dm');
|
|
8
|
+
const checkI18nCall = (node, callExpr) => {
|
|
9
|
+
if (!isMatchingCallee(node, callExpr)) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const args = node.arguments;
|
|
14
|
+
if (args.length > 0 && args[0].type === 'ObjectExpression') {
|
|
15
|
+
const idProperty = args[0].properties.find((prop) => prop.key && prop.key.name === 'id');
|
|
16
|
+
const dmProperty = args[0].properties.find((prop) => prop.key && prop.key.name === 'dm');
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
18
|
+
if (
|
|
19
|
+
idProperty?.value?.type === 'StringLiteral' &&
|
|
20
|
+
dmProperty?.value?.type === 'StringLiteral'
|
|
21
|
+
) {
|
|
22
|
+
const id = idProperty.value.value;
|
|
23
|
+
const dm = dmProperty.value.value;
|
|
24
|
+
return { id, dm };
|
|
29
25
|
}
|
|
30
26
|
}
|
|
31
27
|
return null;
|