daodou-command 1.4.8 → 1.4.9
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/.daodourc +85 -0
- package/README.md +174 -157
- package/bin/daodou.js +9 -13
- package/lib/commands/config.js +149 -231
- package/lib/commands/lang.js +71 -115
- package/lib/commands/upgrade.js +47 -87
- package/lib/translation/TranslationService.js +6 -24
- package/lib/translation/engines/microsoft/services/EdgeAuthService.js +0 -2
- package/lib/utils/translation.js +4 -15
- package/lib/utils/update-checker.js +34 -113
- package/package.json +2 -2
package/lib/commands/lang.js
CHANGED
|
@@ -19,152 +19,106 @@ class LangCommand {
|
|
|
19
19
|
*/
|
|
20
20
|
async add(key, value, options = {}) {
|
|
21
21
|
if (!key) {
|
|
22
|
-
console.error(chalk.red('
|
|
22
|
+
console.error(chalk.red(' ✖ 请提供键名'));
|
|
23
23
|
process.exit(1);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
// 如果没有指定 value,则使用 key 作为 value
|
|
27
26
|
const finalValue = value || key;
|
|
28
|
-
|
|
29
27
|
const defaultLang = this.config.defaultLang || 'en';
|
|
30
28
|
const dir = options.dir || this.config.defaultDir || './public/locales';
|
|
31
29
|
const fileName = this.config.fileName || 'common.json';
|
|
32
30
|
|
|
33
|
-
console.log(
|
|
31
|
+
console.log('');
|
|
32
|
+
console.log(chalk.bold(' 🌐 多语言管理工具'));
|
|
33
|
+
console.log(chalk.dim(' ─────────────────────────'));
|
|
34
34
|
|
|
35
35
|
try {
|
|
36
|
-
// 获取语言列表
|
|
37
36
|
const languages = await this.getLanguages(dir, options.lang);
|
|
38
|
-
|
|
37
|
+
|
|
39
38
|
if (languages.length === 0) {
|
|
40
|
-
console.log(chalk.yellow('
|
|
39
|
+
console.log(chalk.yellow(' ⚠ 未找到任何语言目录'));
|
|
41
40
|
return;
|
|
42
41
|
}
|
|
43
42
|
|
|
44
|
-
console.log(chalk.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
console.log(chalk.
|
|
48
|
-
console.log(
|
|
43
|
+
console.log(' ' + chalk.dim('键名') + ' ' + chalk.cyan(key));
|
|
44
|
+
console.log(' ' + chalk.dim('原文') + ' ' + chalk.cyan(finalValue));
|
|
45
|
+
console.log(' ' + chalk.dim('语言') + ' ' + chalk.cyan(languages.join(', ')));
|
|
46
|
+
console.log(chalk.dim(' ─────────────────────────'));
|
|
47
|
+
console.log('');
|
|
49
48
|
|
|
50
|
-
// 统计变量
|
|
51
49
|
let successCount = 0;
|
|
52
50
|
let skipCount = 0;
|
|
53
51
|
let failCount = 0;
|
|
54
52
|
const results = {};
|
|
55
53
|
|
|
56
|
-
// 顺序处理每个语言
|
|
57
54
|
for (let i = 0; i < languages.length; i++) {
|
|
58
55
|
const langCode = languages[i];
|
|
59
56
|
const filePath = path.join(dir, langCode, fileName);
|
|
60
|
-
|
|
61
|
-
// 显示分隔线
|
|
62
|
-
console.log(chalk.gray('────────────────────────────────────────'));
|
|
63
|
-
|
|
64
|
-
// 显示当前处理的语言
|
|
65
57
|
const isDefault = this.translationService.isDefaultLanguage(langCode, defaultLang);
|
|
66
|
-
const
|
|
67
|
-
|
|
58
|
+
const langLabel = isDefault ? `${langCode} ${chalk.dim('(默认)')}` : langCode;
|
|
59
|
+
const spinner = ora({ text: `${langLabel} 处理中...`, indent: 2 }).start();
|
|
68
60
|
|
|
69
61
|
try {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (fs.existsSync(filePath)) {
|
|
74
|
-
const fileContent = fs.readFileSync(filePath, 'utf8');
|
|
75
|
-
const data = JSON.parse(fileContent);
|
|
76
|
-
|
|
77
|
-
if (data[key] !== undefined) {
|
|
78
|
-
console.log(chalk.yellow(` ⚠️ key "${key}" 已存在,跳过该语言`));
|
|
79
|
-
console.log(chalk.gray(` 📄 结果: 跳过`));
|
|
80
|
-
results[langCode] = { status: 'skipped', reason: 'key 已存在' };
|
|
81
|
-
skipCount++;
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
} else {
|
|
85
|
-
console.log(chalk.yellow(` ⚠️ 文件 ${filePath} 不存在,跳过该语言`));
|
|
86
|
-
console.log(chalk.gray(` 📄 结果: 跳过`));
|
|
62
|
+
if (!fs.existsSync(filePath)) {
|
|
63
|
+
spinner.warn(`${langLabel} ${chalk.dim('文件不存在,跳过')}`);
|
|
87
64
|
results[langCode] = { status: 'skipped', reason: '文件不存在' };
|
|
88
65
|
skipCount++;
|
|
89
66
|
continue;
|
|
90
67
|
}
|
|
91
68
|
|
|
92
|
-
|
|
69
|
+
const fileContent = fs.readFileSync(filePath, 'utf8');
|
|
70
|
+
const data = JSON.parse(fileContent);
|
|
71
|
+
|
|
72
|
+
if (data[key] !== undefined) {
|
|
73
|
+
spinner.warn(`${langLabel} ${chalk.dim('key 已存在,跳过')}`);
|
|
74
|
+
results[langCode] = { status: 'skipped', reason: 'key 已存在' };
|
|
75
|
+
skipCount++;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
93
78
|
|
|
94
79
|
let translatedValue = finalValue;
|
|
95
80
|
|
|
96
|
-
if (isDefault) {
|
|
97
|
-
|
|
98
|
-
console.log(chalk.blue(` ⚡ 步骤 2/3: 默认语言直接写入...`));
|
|
99
|
-
console.log(chalk.green(` ✅ 步骤 3/3: 成功写入文件`));
|
|
100
|
-
console.log(chalk.gray(` 📄 结果: "${key}": "${translatedValue}"`));
|
|
101
|
-
} else {
|
|
102
|
-
// 其他语言需要翻译
|
|
81
|
+
if (!isDefault) {
|
|
82
|
+
spinner.text = `${langLabel} 翻译中...`;
|
|
103
83
|
const translationResult = await this.translationService.translateText(finalValue, langCode, defaultLang);
|
|
104
|
-
|
|
105
84
|
if (translationResult.success) {
|
|
106
85
|
translatedValue = translationResult.result;
|
|
107
|
-
console.log(chalk.green(` ✅ 步骤 4/4: 成功写入文件`));
|
|
108
|
-
console.log(chalk.gray(` 📄 结果: "${key}": "${translatedValue}"`));
|
|
109
86
|
} else {
|
|
110
|
-
|
|
87
|
+
spinner.fail(`${langLabel} ${chalk.dim('翻译失败')}`);
|
|
111
88
|
results[langCode] = { status: 'skipped', reason: '翻译失败' };
|
|
112
89
|
skipCount++;
|
|
113
90
|
continue;
|
|
114
91
|
}
|
|
115
92
|
}
|
|
116
93
|
|
|
117
|
-
// 写入文件
|
|
118
94
|
await this.langFileManager.addKey(filePath, key, translatedValue);
|
|
119
|
-
|
|
120
|
-
results[langCode] = {
|
|
121
|
-
status: 'success',
|
|
122
|
-
value: translatedValue,
|
|
123
|
-
originalValue: finalValue
|
|
124
|
-
};
|
|
125
|
-
successCount++;
|
|
95
|
+
spinner.succeed(`${langLabel} ${chalk.dim(translatedValue)}`);
|
|
126
96
|
|
|
97
|
+
results[langCode] = { status: 'success', value: translatedValue, originalValue: finalValue };
|
|
98
|
+
successCount++;
|
|
127
99
|
} catch (error) {
|
|
128
|
-
|
|
129
|
-
console.log(chalk.gray(` 📄 结果: 失败`));
|
|
100
|
+
spinner.fail(`${langLabel} ${chalk.dim(error.message)}`);
|
|
130
101
|
results[langCode] = { status: 'failed', error: error.message };
|
|
131
102
|
failCount++;
|
|
132
103
|
}
|
|
133
104
|
|
|
134
|
-
|
|
135
|
-
console.log(chalk.blue(`📊 进度: ${i + 1}/${languages.length} 语言完成`));
|
|
136
|
-
|
|
137
|
-
// 等待 0.5 秒后处理下一个语言(除了最后一个)
|
|
138
|
-
if (i < languages.length - 1) {
|
|
139
|
-
console.log(chalk.gray('\n⏳ 等待 0.5s 后处理下一个语言...\n'));
|
|
105
|
+
if (i < languages.length - 1 && !isDefault) {
|
|
140
106
|
await this.translationService.delay(500);
|
|
141
107
|
}
|
|
142
108
|
}
|
|
143
109
|
|
|
144
|
-
//
|
|
145
|
-
console.log(
|
|
146
|
-
console.log(chalk.
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
console.log(chalk.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
console.log(chalk.blue('\n📋 翻译结果汇总:'));
|
|
155
|
-
console.log(chalk.gray(`原文: "${finalValue}"`));
|
|
156
|
-
for (const [lang, result] of Object.entries(results)) {
|
|
157
|
-
if (result.status === 'success') {
|
|
158
|
-
console.log(chalk.green(` ${lang}: ${result.value}`));
|
|
159
|
-
} else if (result.status === 'skipped') {
|
|
160
|
-
console.log(chalk.yellow(` ${lang}: 跳过 (${result.reason})`));
|
|
161
|
-
} else {
|
|
162
|
-
console.log(chalk.red(` ${lang}: 失败 (${result.error})`));
|
|
163
|
-
}
|
|
164
|
-
}
|
|
110
|
+
// 统计
|
|
111
|
+
console.log('');
|
|
112
|
+
console.log(chalk.dim(' ─────────────────────────'));
|
|
113
|
+
const parts = [];
|
|
114
|
+
if (successCount > 0) parts.push(chalk.green(`${successCount} 成功`));
|
|
115
|
+
if (skipCount > 0) parts.push(chalk.yellow(`${skipCount} 跳过`));
|
|
116
|
+
if (failCount > 0) parts.push(chalk.red(`${failCount} 失败`));
|
|
117
|
+
console.log(' ' + parts.join(chalk.dim(' / ')));
|
|
118
|
+
console.log('');
|
|
165
119
|
|
|
166
120
|
} catch (error) {
|
|
167
|
-
console.error(chalk.red('
|
|
121
|
+
console.error(chalk.red(' ✖ ' + error.message));
|
|
168
122
|
process.exit(1);
|
|
169
123
|
}
|
|
170
124
|
}
|
|
@@ -174,59 +128,66 @@ class LangCommand {
|
|
|
174
128
|
*/
|
|
175
129
|
async remove(key, options = {}) {
|
|
176
130
|
if (!key) {
|
|
177
|
-
console.error(chalk.red('
|
|
131
|
+
console.error(chalk.red(' ✖ 请提供键名'));
|
|
178
132
|
process.exit(1);
|
|
179
133
|
}
|
|
180
134
|
|
|
181
|
-
const lang = options.lang || this.config.defaultLang || 'en';
|
|
182
135
|
const dir = options.dir || this.config.defaultDir || './public/locales';
|
|
183
136
|
const fileName = this.config.fileName || 'common.json';
|
|
184
137
|
|
|
185
|
-
console.log(
|
|
138
|
+
console.log('');
|
|
139
|
+
console.log(chalk.bold(' 🌐 多语言管理工具'));
|
|
140
|
+
console.log(chalk.dim(' ─────────────────────────'));
|
|
141
|
+
console.log(' ' + chalk.dim('操作') + ' ' + chalk.cyan('删除'));
|
|
142
|
+
console.log(' ' + chalk.dim('键名') + ' ' + chalk.cyan(key));
|
|
186
143
|
|
|
187
144
|
try {
|
|
188
|
-
// 获取语言列表
|
|
189
145
|
const languages = await this.getLanguages(dir, options.lang);
|
|
190
|
-
|
|
146
|
+
|
|
191
147
|
if (languages.length === 0) {
|
|
192
|
-
console.log(chalk.yellow('
|
|
148
|
+
console.log(chalk.yellow(' ⚠ 未找到任何语言目录'));
|
|
193
149
|
return;
|
|
194
150
|
}
|
|
195
151
|
|
|
196
|
-
console.log(chalk.
|
|
152
|
+
console.log(' ' + chalk.dim('语言') + ' ' + chalk.cyan(languages.join(', ')));
|
|
153
|
+
console.log(chalk.dim(' ─────────────────────────'));
|
|
154
|
+
console.log('');
|
|
197
155
|
|
|
198
|
-
// 批量处理每个语言
|
|
199
156
|
let successCount = 0;
|
|
200
157
|
let skipCount = 0;
|
|
201
158
|
|
|
202
159
|
for (const langCode of languages) {
|
|
203
160
|
const filePath = path.join(dir, langCode, fileName);
|
|
204
|
-
|
|
161
|
+
const spinner = ora({ text: `${langCode} 处理中...`, indent: 2 }).start();
|
|
162
|
+
|
|
205
163
|
try {
|
|
206
164
|
const result = await this.langFileManager.removeKey(filePath, key);
|
|
207
165
|
if (result.success) {
|
|
208
|
-
|
|
166
|
+
spinner.succeed(`${langCode} ${chalk.dim('已删除')}`);
|
|
209
167
|
successCount++;
|
|
210
168
|
} else if (result.notFound) {
|
|
211
|
-
|
|
169
|
+
spinner.warn(`${langCode} ${chalk.dim('未找到,跳过')}`);
|
|
212
170
|
skipCount++;
|
|
213
171
|
}
|
|
214
172
|
} catch (error) {
|
|
215
|
-
if (error.message.includes('目录不存在')) {
|
|
216
|
-
|
|
217
|
-
} else if (error.message.includes('文件不存在')) {
|
|
218
|
-
console.log(chalk.yellow(`⚠️ 跳过 ${langCode} (文件不存在)`));
|
|
173
|
+
if (error.message.includes('目录不存在') || error.message.includes('文件不存在')) {
|
|
174
|
+
spinner.warn(`${langCode} ${chalk.dim('文件不存在,跳过')}`);
|
|
219
175
|
} else {
|
|
220
|
-
|
|
176
|
+
spinner.fail(`${langCode} ${chalk.dim(error.message)}`);
|
|
221
177
|
}
|
|
222
178
|
}
|
|
223
179
|
}
|
|
224
180
|
|
|
225
|
-
|
|
226
|
-
console.log(chalk.
|
|
181
|
+
console.log('');
|
|
182
|
+
console.log(chalk.dim(' ─────────────────────────'));
|
|
183
|
+
const parts = [];
|
|
184
|
+
if (successCount > 0) parts.push(chalk.green(`${successCount} 删除`));
|
|
185
|
+
if (skipCount > 0) parts.push(chalk.yellow(`${skipCount} 跳过`));
|
|
186
|
+
console.log(' ' + parts.join(chalk.dim(' / ')));
|
|
187
|
+
console.log('');
|
|
227
188
|
|
|
228
189
|
} catch (error) {
|
|
229
|
-
console.error(chalk.red('
|
|
190
|
+
console.error(chalk.red(' ✖ ' + error.message));
|
|
230
191
|
process.exit(1);
|
|
231
192
|
}
|
|
232
193
|
}
|
|
@@ -235,28 +196,23 @@ class LangCommand {
|
|
|
235
196
|
* 获取语言列表
|
|
236
197
|
*/
|
|
237
198
|
async getLanguages(dir, specifiedLang) {
|
|
238
|
-
// 如果指定了语言,只处理该语言
|
|
239
199
|
if (specifiedLang) {
|
|
240
200
|
return [specifiedLang];
|
|
241
201
|
}
|
|
242
202
|
|
|
243
|
-
// 否则扫描目录下的所有语言文件夹
|
|
244
203
|
try {
|
|
245
204
|
if (!fs.existsSync(dir)) {
|
|
246
|
-
console.log(chalk.yellow(
|
|
205
|
+
console.log(chalk.yellow(` ⚠ 目录不存在: ${dir}`));
|
|
247
206
|
return [];
|
|
248
207
|
}
|
|
249
208
|
|
|
250
209
|
const items = fs.readdirSync(dir);
|
|
251
|
-
|
|
210
|
+
return items.filter(item => {
|
|
252
211
|
const itemPath = path.join(dir, item);
|
|
253
212
|
return fs.statSync(itemPath).isDirectory();
|
|
254
213
|
});
|
|
255
|
-
|
|
256
|
-
console.log(chalk.blue(`🔍 扫描目录 ${dir}: 发现 ${languages.length} 个语言`));
|
|
257
|
-
return languages;
|
|
258
214
|
} catch (error) {
|
|
259
|
-
console.log(chalk.red(
|
|
215
|
+
console.log(chalk.red(` ✖ 扫描目录失败: ${error.message}`));
|
|
260
216
|
return [];
|
|
261
217
|
}
|
|
262
218
|
}
|
package/lib/commands/upgrade.js
CHANGED
|
@@ -6,7 +6,6 @@ const packageJson = require('../../package.json');
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* 获取npm上的最新版本号
|
|
9
|
-
* @returns {Promise<string>} 最新版本号
|
|
10
9
|
*/
|
|
11
10
|
async function getLatestVersion() {
|
|
12
11
|
try {
|
|
@@ -21,7 +20,6 @@ async function getLatestVersion() {
|
|
|
21
20
|
|
|
22
21
|
/**
|
|
23
22
|
* 获取当前安装的版本号
|
|
24
|
-
* @returns {string} 当前版本号
|
|
25
23
|
*/
|
|
26
24
|
function getCurrentVersion() {
|
|
27
25
|
return packageJson.version;
|
|
@@ -29,117 +27,79 @@ function getCurrentVersion() {
|
|
|
29
27
|
|
|
30
28
|
/**
|
|
31
29
|
* 比较版本号
|
|
32
|
-
* @param {string} current 当前版本
|
|
33
|
-
* @param {string} latest 最新版本
|
|
34
|
-
* @returns {boolean} 是否有更新
|
|
35
30
|
*/
|
|
36
31
|
function hasUpdate(current, latest) {
|
|
37
32
|
const currentParts = current.split('.').map(Number);
|
|
38
33
|
const latestParts = latest.split('.').map(Number);
|
|
39
|
-
|
|
34
|
+
|
|
40
35
|
for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) {
|
|
41
36
|
const currentPart = currentParts[i] || 0;
|
|
42
37
|
const latestPart = latestParts[i] || 0;
|
|
43
|
-
|
|
44
|
-
if (latestPart > currentPart) {
|
|
45
|
-
return true;
|
|
46
|
-
} else if (latestPart < currentPart) {
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
38
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
* @param {string} version 要更新的版本
|
|
57
|
-
*/
|
|
58
|
-
function updatePackage(version) {
|
|
59
|
-
try {
|
|
60
|
-
console.log(chalk.blue(`正在更新到版本 ${version}...`));
|
|
61
|
-
execSync(`npm install -g ${packageJson.name}@${version} --force`, {
|
|
62
|
-
stdio: 'inherit',
|
|
63
|
-
timeout: 300000 // 5分钟超时
|
|
64
|
-
});
|
|
65
|
-
console.log(chalk.green(`✅ 成功更新到版本 ${version}`));
|
|
66
|
-
} catch (error) {
|
|
67
|
-
throw new Error(`更新失败: ${error.message}`);
|
|
39
|
+
if (latestPart > currentPart) return true;
|
|
40
|
+
if (latestPart < currentPart) return false;
|
|
68
41
|
}
|
|
69
|
-
}
|
|
70
42
|
|
|
71
|
-
|
|
72
|
-
* 显示更新信息
|
|
73
|
-
* @param {string} current 当前版本
|
|
74
|
-
* @param {string} latest 最新版本
|
|
75
|
-
* @param {boolean} hasUpdateAvailable 是否有更新
|
|
76
|
-
*/
|
|
77
|
-
function displayUpdateInfo(current, latest, hasUpdateAvailable) {
|
|
78
|
-
console.log(chalk.cyan('📦 daodou-command 版本信息:'));
|
|
79
|
-
console.log(` 当前版本: ${chalk.yellow(current)}`);
|
|
80
|
-
console.log(` 最新版本: ${chalk.yellow(latest)}`);
|
|
81
|
-
|
|
82
|
-
if (hasUpdateAvailable) {
|
|
83
|
-
console.log(chalk.red(' ⚠️ 有新版本可用!'));
|
|
84
|
-
console.log(chalk.gray(' 使用 "dao update" 命令进行更新'));
|
|
85
|
-
} else {
|
|
86
|
-
console.log(chalk.green(' ✅ 已是最新版本'));
|
|
87
|
-
}
|
|
43
|
+
return false;
|
|
88
44
|
}
|
|
89
45
|
|
|
90
46
|
/**
|
|
91
47
|
* 执行更新命令
|
|
92
|
-
* @param {Object} options 命令选项
|
|
93
48
|
*/
|
|
94
49
|
async function execute(options) {
|
|
95
|
-
|
|
96
|
-
|
|
50
|
+
console.log('');
|
|
51
|
+
console.log(chalk.bold(' 🔄 版本更新'));
|
|
52
|
+
console.log(chalk.dim(' ─────────────────────────'));
|
|
53
|
+
|
|
54
|
+
const spinner = ora({ text: '检查最新版本...', indent: 2 }).start();
|
|
55
|
+
|
|
97
56
|
try {
|
|
98
|
-
// 获取版本信息
|
|
99
57
|
const currentVersion = getCurrentVersion();
|
|
100
58
|
const latestVersion = await getLatestVersion();
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
spinner.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
59
|
+
const updateAvailable = hasUpdate(currentVersion, latestVersion);
|
|
60
|
+
|
|
61
|
+
spinner.succeed('版本检查完成');
|
|
62
|
+
|
|
63
|
+
console.log('');
|
|
64
|
+
console.log(' ' + chalk.dim('当前') + ' v' + currentVersion);
|
|
65
|
+
console.log(' ' + chalk.dim('最新') + ' v' + latestVersion);
|
|
66
|
+
console.log(chalk.dim(' ─────────────────────────'));
|
|
67
|
+
|
|
109
68
|
if (options.check) {
|
|
69
|
+
if (updateAvailable) {
|
|
70
|
+
console.log('');
|
|
71
|
+
console.log(chalk.yellow(' ⚠ 有新版本可用,运行 ') + chalk.cyan('dao upgrade') + chalk.yellow(' 更新'));
|
|
72
|
+
} else {
|
|
73
|
+
console.log(chalk.green(' ✔ 已是最新版本'));
|
|
74
|
+
}
|
|
75
|
+
console.log('');
|
|
110
76
|
return;
|
|
111
77
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
console.log(
|
|
78
|
+
|
|
79
|
+
if (!updateAvailable && !options.force) {
|
|
80
|
+
console.log(chalk.green(' ✔ 已是最新版本'));
|
|
81
|
+
console.log('');
|
|
116
82
|
return;
|
|
117
83
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const newVersion = execSync('dao --version', { encoding: 'utf8' }).trim();
|
|
134
|
-
console.log(chalk.green(`✅ 更新完成!当前版本: ${newVersion}`));
|
|
135
|
-
} catch (error) {
|
|
136
|
-
console.log(chalk.yellow('⚠️ 更新完成,但无法验证版本号'));
|
|
137
|
-
}
|
|
84
|
+
|
|
85
|
+
if (options.force && !updateAvailable) {
|
|
86
|
+
console.log(chalk.yellow(' ⚠ 强制重新安装'));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
console.log('');
|
|
90
|
+
const installSpinner = ora({ text: `安装 v${latestVersion}...`, indent: 2 }).start();
|
|
91
|
+
try {
|
|
92
|
+
execSync(`npm install -g ${packageJson.name}@${latestVersion} --force`, {
|
|
93
|
+
stdio: 'pipe',
|
|
94
|
+
timeout: 300000
|
|
95
|
+
});
|
|
96
|
+
installSpinner.succeed(`已更新到 v${latestVersion}`);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
installSpinner.fail('安装失败 ' + chalk.dim(error.message));
|
|
138
99
|
}
|
|
139
|
-
|
|
100
|
+
console.log('');
|
|
140
101
|
} catch (error) {
|
|
141
|
-
spinner.
|
|
142
|
-
throw error;
|
|
102
|
+
spinner.fail('检查失败 ' + chalk.dim(error.message));
|
|
143
103
|
}
|
|
144
104
|
}
|
|
145
105
|
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
*/
|
|
5
5
|
const { getEnginesByPriority, getEnabledEngines } = require('./engines');
|
|
6
6
|
const { ConfigManager } = require('../utils/config');
|
|
7
|
-
const chalk = require('chalk');
|
|
8
7
|
|
|
9
8
|
class TranslationService {
|
|
10
9
|
constructor() {
|
|
@@ -39,24 +38,17 @@ class TranslationService {
|
|
|
39
38
|
// 按优先级尝试各个引擎
|
|
40
39
|
for (const engine of this.engines) {
|
|
41
40
|
try {
|
|
42
|
-
console.log(chalk.blue(`尝试使用 ${engine.getName()} 翻译...`));
|
|
43
|
-
|
|
44
|
-
// 检查引擎是否可用
|
|
45
41
|
const isAvailable = await engine.isAvailable();
|
|
46
42
|
if (!isAvailable) {
|
|
47
|
-
console.log(chalk.yellow(`${engine.getName()} 不可用,跳过`));
|
|
48
43
|
continue;
|
|
49
44
|
}
|
|
50
45
|
|
|
51
|
-
// 执行翻译
|
|
52
46
|
const result = await engine.translate(text, sourceLang, targetLang, options);
|
|
53
|
-
|
|
47
|
+
|
|
54
48
|
if (result.success) {
|
|
55
|
-
console.log(chalk.green(`翻译成功,使用引擎: ${engine.getName()}`));
|
|
56
49
|
return result;
|
|
57
50
|
}
|
|
58
51
|
} catch (error) {
|
|
59
|
-
console.log(chalk.red(`${engine.getName()} 翻译失败: ${error.message}`));
|
|
60
52
|
lastError = error;
|
|
61
53
|
continue;
|
|
62
54
|
}
|
|
@@ -80,10 +72,9 @@ class TranslationService {
|
|
|
80
72
|
|
|
81
73
|
if (microsoftEngine && await microsoftEngine.isAvailable()) {
|
|
82
74
|
try {
|
|
83
|
-
console.log(chalk.blue('使用微软翻译文档功能...'));
|
|
84
75
|
return await microsoftEngine.translateDocumentation(text, sourceLang, targetLang, options);
|
|
85
76
|
} catch (error) {
|
|
86
|
-
|
|
77
|
+
// 回退到普通翻译
|
|
87
78
|
}
|
|
88
79
|
}
|
|
89
80
|
|
|
@@ -105,27 +96,18 @@ class TranslationService {
|
|
|
105
96
|
// 按优先级尝试各个引擎
|
|
106
97
|
for (const engine of this.engines) {
|
|
107
98
|
try {
|
|
108
|
-
console.log(chalk.blue(`尝试使用 ${engine.getName()} 翻译HTML...`));
|
|
109
|
-
|
|
110
|
-
// 检查引擎是否可用
|
|
111
99
|
if (!(await engine.isAvailable())) {
|
|
112
|
-
console.log(chalk.yellow(`引擎 ${engine.getName()} 不可用,跳过`));
|
|
113
100
|
continue;
|
|
114
101
|
}
|
|
115
|
-
|
|
116
|
-
// 执行HTML翻译
|
|
102
|
+
|
|
117
103
|
const result = await engine.translateHtml(htmlContent, sourceLang, targetLang, options);
|
|
118
|
-
|
|
104
|
+
|
|
119
105
|
if (result && result.success) {
|
|
120
|
-
console.log(chalk.green(`HTML翻译成功,使用引擎: ${engine.getName()}`));
|
|
121
106
|
return result;
|
|
122
107
|
}
|
|
123
|
-
|
|
124
108
|
} catch (error) {
|
|
125
109
|
lastError = error;
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
// 如果是内容长度限制错误,直接抛出,不尝试其他引擎
|
|
110
|
+
|
|
129
111
|
if (error.message.includes('内容过长') || error.message.includes('Content too long')) {
|
|
130
112
|
throw error;
|
|
131
113
|
}
|
|
@@ -148,7 +130,7 @@ class TranslationService {
|
|
|
148
130
|
const languages = await engine.getSupportedLanguages();
|
|
149
131
|
languages.forEach(lang => allLanguages.add(lang));
|
|
150
132
|
} catch (error) {
|
|
151
|
-
|
|
133
|
+
// 获取失败不影响其他引擎
|
|
152
134
|
}
|
|
153
135
|
}
|
|
154
136
|
|
|
@@ -60,8 +60,6 @@ class MicrosoftEdgeAuthService {
|
|
|
60
60
|
*/
|
|
61
61
|
updateAccessToken(token) {
|
|
62
62
|
const expirationTime = this.getExpirationTimeFromToken(token);
|
|
63
|
-
console.log(`更新访问令牌: ********, 过期时间: ${new Date(expirationTime)}`);
|
|
64
|
-
|
|
65
63
|
this.accessToken = token;
|
|
66
64
|
this.expireAt = expirationTime - this.PRE_EXPIRATION;
|
|
67
65
|
this.tokenPromise = null;
|
package/lib/utils/translation.js
CHANGED
|
@@ -20,20 +20,14 @@ class TranslationService {
|
|
|
20
20
|
* @returns {Promise<{success: boolean, result: string, error: string}>}
|
|
21
21
|
*/
|
|
22
22
|
async translateText(text, targetLang, sourceLang = 'en') {
|
|
23
|
-
console.log(chalk.blue(` 🌐 步骤 2/4: 开始翻译 "${text}"...`));
|
|
24
|
-
|
|
25
23
|
try {
|
|
26
24
|
const result = await this.translationService.translate(text, sourceLang, targetLang);
|
|
27
|
-
|
|
28
|
-
console.log(chalk.green(` 🔄 步骤 3/4: 翻译完成 "${text}" -> "${result.text}"`));
|
|
29
|
-
|
|
30
25
|
return {
|
|
31
26
|
success: true,
|
|
32
27
|
result: result.text,
|
|
33
28
|
error: null
|
|
34
29
|
};
|
|
35
30
|
} catch (error) {
|
|
36
|
-
console.log(chalk.red(` ❌ 翻译失败: ${error.message}`));
|
|
37
31
|
return {
|
|
38
32
|
success: false,
|
|
39
33
|
result: null,
|
|
@@ -50,20 +44,14 @@ class TranslationService {
|
|
|
50
44
|
* @returns {Promise<{success: boolean, result: string, error: string}>}
|
|
51
45
|
*/
|
|
52
46
|
async translateHtml(htmlContent, targetLang, sourceLang = 'en') {
|
|
53
|
-
console.log(chalk.blue(` 🌐 步骤 2/4: 开始翻译HTML内容...`));
|
|
54
|
-
|
|
55
47
|
try {
|
|
56
48
|
const result = await this.translationService.translateHtml(htmlContent, sourceLang, targetLang);
|
|
57
|
-
|
|
58
|
-
console.log(chalk.green(` 🔄 步骤 3/4: HTML翻译完成`));
|
|
59
|
-
|
|
60
49
|
return {
|
|
61
50
|
success: true,
|
|
62
51
|
result: result.text,
|
|
63
52
|
error: null
|
|
64
53
|
};
|
|
65
54
|
} catch (error) {
|
|
66
|
-
console.log(chalk.red(` ❌ HTML翻译失败: ${error.message}`));
|
|
67
55
|
return {
|
|
68
56
|
success: false,
|
|
69
57
|
result: null,
|
|
@@ -128,10 +116,11 @@ class TranslationService {
|
|
|
128
116
|
*/
|
|
129
117
|
async showEngineStatus() {
|
|
130
118
|
const status = await this.getEngineStatus();
|
|
131
|
-
console.log(
|
|
119
|
+
console.log('');
|
|
120
|
+
console.log(chalk.dim(' 翻译引擎状态:'));
|
|
132
121
|
status.forEach(engine => {
|
|
133
|
-
const
|
|
134
|
-
console.log(
|
|
122
|
+
const icon = engine.available ? chalk.green('✔') : chalk.red('✖');
|
|
123
|
+
console.log(` ${icon} ${engine.name} ${chalk.dim(`(优先级 ${engine.priority})`)}`);
|
|
135
124
|
});
|
|
136
125
|
}
|
|
137
126
|
}
|