td-web-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,302 @@
1
+ import { input } from '@inquirer/prompts';
2
+ import XLSX from 'xlsx';
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import { getTimestamp, logger, loggerError, normalizeError, languageToolCheck, getLanguageTool, } from '../../../utils/index.js';
6
+ /**
7
+ * 读取并解析配置文件
8
+ * @param configPath 配置文件路径
9
+ * @returns I18nConfig 配置对象
10
+ * @throws 配置文件不存在或格式错误时抛出异常
11
+ */
12
+ function loadConfig(configPath) {
13
+ if (!fs.existsSync(configPath)) {
14
+ throw new Error(`配置文件不存在:${configPath}`);
15
+ }
16
+ const content = fs.readFileSync(configPath, { encoding: 'utf-8' });
17
+ const json = JSON.parse(content);
18
+ if (!json.i18n) {
19
+ throw new Error('配置文件格式错误,缺少i18n');
20
+ }
21
+ if (!json.i18n.defaultKey) {
22
+ throw new Error('配置文件格式错误,缺少i18n.defaultKey');
23
+ }
24
+ if (!json.i18n.langs) {
25
+ throw new Error('配置文件格式错误,缺少i18n.langs');
26
+ }
27
+ if (!json.i18n.longCodes) {
28
+ throw new Error('配置文件格式错误,缺少i18n.longCodes');
29
+ }
30
+ return json.i18n;
31
+ }
32
+ /**
33
+ * 匹配excel表头列名对应的语言key,支持大小写不敏感匹配
34
+ * 先匹配语言key本身,再匹配语言名称数组(包含关系)
35
+ * @param colName 表头列名
36
+ * @param langs 语言映射
37
+ * @returns 匹配到的语言key,未匹配返回null
38
+ */
39
+ function matchLangKey(colName, langs) {
40
+ if (!colName)
41
+ return null;
42
+ const colNameLower = colName.toLowerCase();
43
+ // 先尝试匹配语言key
44
+ for (const langKey of Object.keys(langs)) {
45
+ if (langKey.toLowerCase() === colNameLower) {
46
+ return langKey;
47
+ }
48
+ }
49
+ // 再尝试匹配语言名称(包含关系)
50
+ for (const [langKey, names] of Object.entries(langs)) {
51
+ if (names.some((name) => name && colNameLower.includes(name.toLowerCase()))) {
52
+ return langKey;
53
+ }
54
+ }
55
+ return null;
56
+ }
57
+ /**
58
+ * 去除字符串首尾的单引号或双引号
59
+ * @param str 输入字符串
60
+ * @returns 去除引号后的字符串
61
+ */
62
+ function trimQuotes(str) {
63
+ if ((str.startsWith('"') && str.endsWith('"')) ||
64
+ (str.startsWith("'") && str.endsWith("'"))) {
65
+ return str.slice(1, -1);
66
+ }
67
+ return str;
68
+ }
69
+ /**
70
+ * 批量检测词条文本,返回所有检测结果
71
+ * @param texts 词条数组
72
+ * @param language 语言代码
73
+ * @returns 检测结果数组,顺序对应输入texts
74
+ *
75
+ * 说明:
76
+ * 这里将所有词条用换行符拼接成一个字符串,一次性调用语言检测接口,
77
+ * 以减少请求次数和提升性能。
78
+ * 返回结果数组中只包含一个元素,即合并检测的结果。
79
+ */
80
+ async function batchCheckTexts(texts, language) {
81
+ const results = [];
82
+ try {
83
+ // 将词条用换行符拼接,避免词条间干扰,推荐换行分隔
84
+ const joinedText = texts.join('\n');
85
+ const res = await languageToolCheck(joinedText, language);
86
+ results.push(res);
87
+ }
88
+ catch (error) {
89
+ loggerError(error, logger);
90
+ results.push(null);
91
+ }
92
+ return results;
93
+ }
94
+ /**
95
+ * excel转json功能主函数
96
+ * 读取用户输入的excel路径,解析内容,根据配置生成多语言json文件
97
+ * 并对配置文件中所有语言对应的词条进行语言检测
98
+ * @param program Commander命令行实例
99
+ */
100
+ export async function excel2json(program) {
101
+ var _a;
102
+ // 配置文件默认路径
103
+ const configPath = path.join(process.cwd(), 'src/config/setting.json');
104
+ let i18nConfig;
105
+ // 加载配置文件
106
+ try {
107
+ logger.info(`开始加载配置文件:${configPath}`);
108
+ i18nConfig = loadConfig(configPath);
109
+ logger.info('配置文件加载成功');
110
+ }
111
+ catch (error) {
112
+ const msg = `读取配置文件失败:${normalizeError(error).stack},程序已退出`;
113
+ logger.error(msg);
114
+ console.error('程序执行时发生异常,已记录日志,程序已退出');
115
+ process.exit(1);
116
+ }
117
+ // 尝试调用接口获取支持的语言列表,更新 longCodes
118
+ try {
119
+ logger.info('尝试获取在线支持的语言列表...');
120
+ const languageTools = await getLanguageTool();
121
+ logger.info(`成功获取语言列表,覆盖配置文件中的 longCodes`);
122
+ // 构建新的 longCodes 映射
123
+ const newLongCodes = {};
124
+ // 语言标识对应语言名称列表,方便匹配
125
+ const langNameToKey = {};
126
+ for (const [key, names] of Object.entries(i18nConfig.langs)) {
127
+ names.forEach((name) => {
128
+ langNameToKey[name.toLowerCase()] = key;
129
+ });
130
+ }
131
+ for (const lang of languageTools) {
132
+ // 尝试根据语言名称匹配配置中的语言key
133
+ const lowerName = lang.name.toLowerCase();
134
+ const matchedKey = langNameToKey[lowerName] ||
135
+ Object.keys(i18nConfig.langs).find((k) => k.toLowerCase() === lowerName);
136
+ if (matchedKey) {
137
+ newLongCodes[matchedKey] = lang.longCode;
138
+ }
139
+ }
140
+ // 替换旧的 longCodes,保留未匹配的旧值
141
+ i18nConfig.longCodes = { ...i18nConfig.longCodes, ...newLongCodes };
142
+ }
143
+ catch (error) {
144
+ logger.warn(`获取在线语言列表失败,使用本地配置 longCodes,错误:${normalizeError(error).stack}`);
145
+ }
146
+ // 交互式输入excel文件路径并校验
147
+ const answer = await input({
148
+ message: '请输入excel文件路径:',
149
+ validate: (value) => {
150
+ const cleaned = value.trim().replace(/^['"]|['"]$/g, '');
151
+ if (cleaned.length === 0)
152
+ return '路径不能为空';
153
+ if (!fs.existsSync(cleaned))
154
+ return '文件不存在,请输入有效路径';
155
+ if (!/\.(xls|xlsx)$/i.test(cleaned))
156
+ return '请输入有效的excel文件路径(.xls或.xlsx)';
157
+ return true;
158
+ },
159
+ });
160
+ // 规范化路径,支持相对路径转绝对路径,去除首尾引号
161
+ const excelPath = path.resolve(process.cwd(), answer.trim().replace(/^['"]|['"]$/g, ''));
162
+ try {
163
+ logger.info(`开始读取excel文件:${excelPath}`);
164
+ // 读取excel文件
165
+ const workbook = XLSX.readFile(excelPath);
166
+ const firstSheetName = workbook.SheetNames[0];
167
+ if (!firstSheetName) {
168
+ logger.error('excel文件没有任何工作表,程序已退出');
169
+ console.error('程序执行时发生异常,已记录日志,程序已退出');
170
+ process.exit(1);
171
+ }
172
+ // 读取第一个工作表的数据,按行读取,header=1表示返回二维数组
173
+ const sheet = workbook.Sheets[firstSheetName];
174
+ const rows = XLSX.utils.sheet_to_json(sheet, { header: 1 });
175
+ if (rows.length < 2) {
176
+ logger.error('工作表数据不足,至少需要两行(表头+数据),程序已退出');
177
+ console.error('程序执行时发生异常,已记录日志,程序已退出');
178
+ process.exit(1);
179
+ }
180
+ logger.info('开始解析表头');
181
+ // 处理表头行,去除空格,转成字符串
182
+ const headerRow = rows[0].map((cell) => (cell ? String(cell).trim() : ''));
183
+ // 根据表头匹配语言列,建立列索引到语言key的映射
184
+ const colIndexToLangKey = {};
185
+ headerRow.forEach((colName, idx) => {
186
+ const langKey = matchLangKey(colName, i18nConfig.langs);
187
+ if (langKey) {
188
+ colIndexToLangKey[idx] = langKey;
189
+ }
190
+ });
191
+ // 获取默认语言列索引
192
+ const defaultLang = i18nConfig.defaultKey;
193
+ const defaultColIndex = (_a = Object.entries(colIndexToLangKey).find(([, langKey]) => langKey === defaultLang)) === null || _a === void 0 ? void 0 : _a[0];
194
+ if (defaultColIndex === undefined) {
195
+ logger.error(`找不到默认语言列:${defaultLang},程序已退出`);
196
+ console.error('程序执行时发生异常,已记录日志,程序已退出');
197
+ process.exit(1);
198
+ }
199
+ const defaultColNum = Number(defaultColIndex);
200
+ // 初始化所有语言词条对象(包括默认语言)
201
+ const langTranslations = {};
202
+ Object.values(colIndexToLangKey).forEach((langKey) => {
203
+ langTranslations[langKey] = {};
204
+ });
205
+ logger.info('开始解析数据行');
206
+ // 遍历数据行,提取所有语言词条
207
+ // key统一用默认语言列的值,其他语言对应的列为翻译内容
208
+ const langKeysMap = {}; // 语言key => 词条数组
209
+ Object.keys(langTranslations).forEach((langKey) => {
210
+ langKeysMap[langKey] = [];
211
+ });
212
+ for (let i = 1; i < rows.length; i++) {
213
+ const row = rows[i];
214
+ const keyCell = row[defaultColNum];
215
+ if (keyCell === undefined || keyCell === null || keyCell === '')
216
+ continue;
217
+ let key = String(keyCell).trim();
218
+ key = trimQuotes(key); // 去除引号
219
+ // 跳过空key,避免写入无效数据
220
+ if (key.length === 0)
221
+ continue;
222
+ // 默认语言的词条即key本身
223
+ langTranslations[defaultLang][key] = key;
224
+ langKeysMap[defaultLang].push(key);
225
+ // 其他语言词条
226
+ for (const [colIdxStr, langKey] of Object.entries(colIndexToLangKey)) {
227
+ const colIdx = Number(colIdxStr);
228
+ if (langKey === defaultLang)
229
+ continue;
230
+ const valCell = row[colIdx];
231
+ if (valCell !== undefined && valCell !== null && valCell !== '') {
232
+ const valStr = String(valCell);
233
+ langTranslations[langKey][key] = valStr;
234
+ langKeysMap[langKey].push(valStr);
235
+ }
236
+ }
237
+ }
238
+ // 对所有语言词条批量进行语言检测(包括默认语言)
239
+ for (const [langKey, texts] of Object.entries(langKeysMap)) {
240
+ const longCode = i18nConfig.longCodes[langKey];
241
+ if (!longCode) {
242
+ logger.warn(`语言(${langKey})未配置 longCode,跳过检测`);
243
+ continue;
244
+ }
245
+ if (texts.length === 0) {
246
+ logger.info(`语言(${langKey})无词条,跳过检测`);
247
+ continue;
248
+ }
249
+ logger.info(`开始对语言(${langKey})词条进行语言检测,词条数量:${texts.length}`);
250
+ const checkResults = await batchCheckTexts(texts, longCode);
251
+ if (!checkResults || checkResults.length === 0 || !checkResults[0]) {
252
+ logger.error(`语言(${langKey})词条检测失败`);
253
+ continue;
254
+ }
255
+ const result = checkResults[0];
256
+ if (result.matches.length === 0) {
257
+ logger.info(`语言(${langKey})词条检测无错误`);
258
+ }
259
+ else {
260
+ logger.info(`语言(${langKey})词条检测发现问题,词条数量: ${result.matches.length}`);
261
+ for (const match of result.matches) {
262
+ logger.info(`- 错误: ${match.message}\n 出错句子: ${match.sentence}\n 建议替换: ${match.replacements
263
+ .map((r) => r.value)
264
+ .join(', ')}`);
265
+ }
266
+ }
267
+ }
268
+ // 输出目录:excel文件所在目录下的“lang_时间戳”文件夹
269
+ const excelDir = path.dirname(excelPath);
270
+ const timestamp = getTimestamp();
271
+ const outputRoot = path.join(excelDir, `lang_${timestamp}`);
272
+ if (!fs.existsSync(outputRoot)) {
273
+ fs.mkdirSync(outputRoot, { recursive: true });
274
+ }
275
+ logger.info(`开始生成语言文件,输出目录:${outputRoot}`);
276
+ // 按语言生成对应的json文件,默认语言的key=value不生成文件
277
+ for (const [langKey, translations] of Object.entries(langTranslations)) {
278
+ if (Object.keys(translations).length === 0)
279
+ continue;
280
+ if (langKey === defaultLang) {
281
+ logger.info(`跳过默认语言(${langKey})的json文件生成`);
282
+ continue; // 跳过默认语言文件生成
283
+ }
284
+ const langDir = path.join(outputRoot, langKey);
285
+ if (!fs.existsSync(langDir)) {
286
+ fs.mkdirSync(langDir, { recursive: true });
287
+ }
288
+ const filePath = path.join(langDir, 'translate.json');
289
+ fs.writeFileSync(filePath, JSON.stringify(translations, null, 2), {
290
+ encoding: 'utf-8',
291
+ });
292
+ logger.info(`已生成语言文件:${filePath}`);
293
+ }
294
+ logger.info('全部转换完成', true);
295
+ }
296
+ catch (error) {
297
+ // 记录错误日志,方便排查
298
+ loggerError(error, logger);
299
+ console.error('程序执行时发生异常,已记录日志,程序已退出');
300
+ process.exit(1);
301
+ }
302
+ }
@@ -0,0 +1,3 @@
1
+ export function run() {
2
+ console.log('提取词条');
3
+ }
@@ -0,0 +1,71 @@
1
+ import { select, Separator } from '@inquirer/prompts';
2
+ import { excel2json } from './excel2json/index.js';
3
+ import { logger, loggerError } from '../../utils/index.js';
4
+ /**
5
+ * 国际化模块主入口
6
+ * 提供多个国际化相关功能的交互式选择
7
+ * @param program Commander命令行实例,用于传递参数和配置
8
+ */
9
+ export async function i18n(program) {
10
+ try {
11
+ logger.info('国际化模块启动,等待用户选择功能');
12
+ // 定义可用功能选项
13
+ const moduleChoices = [
14
+ {
15
+ name: '提取词条',
16
+ value: 'extractEntry',
17
+ description: '从所给路径中提取词条信息',
18
+ },
19
+ {
20
+ name: 'json转excel',
21
+ value: 'json2excel',
22
+ description: '将json格式的词条信息转换为excel表格',
23
+ },
24
+ {
25
+ name: 'excel转json',
26
+ value: 'excel2json',
27
+ description: '将excel表格转换为json格式的词条信息',
28
+ },
29
+ {
30
+ name: 'json合并',
31
+ value: 'jsonMerge',
32
+ description: '合并多个json格式的词条信息文件',
33
+ },
34
+ ];
35
+ // 交互式选择需要执行的功能
36
+ const answer = await select({
37
+ message: '请选择要执行的功能:',
38
+ choices: [
39
+ ...moduleChoices,
40
+ new Separator(), // 分割线,方便未来扩展更多功能
41
+ ],
42
+ default: 'extractEntry', // 默认选项
43
+ pageSize: 10, // 最大显示选项数
44
+ loop: true, // 是否循环滚动选项
45
+ });
46
+ // 查找选择功能的名称,方便日志输出
47
+ const selectedModule = moduleChoices.find((item) => item.value === answer);
48
+ if (!selectedModule) {
49
+ logger.warn('未选择有效功能,程序已退出');
50
+ process.exit(0);
51
+ }
52
+ logger.info(`用户选择功能:${selectedModule.name}`);
53
+ // 根据选择执行对应功能
54
+ switch (answer) {
55
+ case 'excel2json':
56
+ logger.info(`${selectedModule.name}功能开始执行`);
57
+ await excel2json(program);
58
+ logger.info(`${selectedModule.name}功能执行完成`);
59
+ break;
60
+ default:
61
+ logger.warn(`${selectedModule.name}功能暂未实现,程序已退出`);
62
+ process.exit(0);
63
+ }
64
+ }
65
+ catch (error) {
66
+ // 记录错误日志,方便排查
67
+ loggerError(error, logger);
68
+ console.error('程序执行时发生异常,已记录日志,程序已退出');
69
+ process.exit(1);
70
+ }
71
+ }
@@ -0,0 +1,3 @@
1
+ export function run() {
2
+ console.log('json转excel');
3
+ }
@@ -0,0 +1,3 @@
1
+ export function run() {
2
+ console.log('json合并');
3
+ }
@@ -0,0 +1,240 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { getData, postData } from '../api/index.js';
5
+ import api from '../api/interface.js';
6
+ /**
7
+ * 获取当前时间戳字符串,格式为:YYYYMMDDHHMMSS
8
+ * 例如:20260123135500 表示 2026年01月23日 13点55分00秒
9
+ *
10
+ * @returns {string} 格式化后的时间戳字符串
11
+ */
12
+ export function getTimestamp() {
13
+ const now = new Date();
14
+ /**
15
+ * 辅助函数:将数字转换为长度为2的字符串,不足时左侧补0
16
+ * @param n 需要格式化的数字
17
+ * @returns {string} 补零后的字符串
18
+ */
19
+ const pad = (n) => n.toString().padStart(2, '0');
20
+ const year = now.getFullYear(); // 获取四位年份
21
+ const month = pad(now.getMonth() + 1); // 获取月份(0-11),需+1,补零
22
+ const day = pad(now.getDate()); // 获取日期,补零
23
+ const hour = pad(now.getHours()); // 获取小时,补零
24
+ const minute = pad(now.getMinutes()); // 获取分钟,补零
25
+ const second = pad(now.getSeconds()); // 获取秒钟,补零
26
+ // 拼接成完整时间戳字符串
27
+ return `${year}${month}${day}${hour}${minute}${second}`;
28
+ }
29
+ /**
30
+ * 获取程序入口文件路径(兼容 ES Module)
31
+ * @returns 程序入口文件的绝对路径
32
+ */
33
+ function getEntryFilePath() {
34
+ try {
35
+ // 当前模块文件路径
36
+ return fileURLToPath(import.meta.url);
37
+ }
38
+ catch {
39
+ // 兜底:使用 process.argv[1]
40
+ if (process.argv.length > 1) {
41
+ return path.resolve(process.cwd(), process.argv[1]);
42
+ }
43
+ // 最终兜底
44
+ return '';
45
+ }
46
+ }
47
+ /**
48
+ * 默认日志配置
49
+ * logsDir 默认设置为程序入口文件所在目录的上级目录的 logs 文件夹
50
+ */
51
+ const defaultOptions = {
52
+ logsDir: '',
53
+ filenameFormatter: (date) => date.toISOString().slice(0, 10).replace(/-/g, '') + '.txt',
54
+ env: process.env.NODE_ENV || 'production',
55
+ entryFilePath: getEntryFilePath(),
56
+ };
57
+ /**
58
+ * 格式化日志内容,支持字符串或对象
59
+ * @param level 日志级别
60
+ * @param message 日志内容,字符串或对象
61
+ * @param date 当前时间
62
+ * @returns 格式化后的日志字符串
63
+ */
64
+ function formatLogLine(level, message, date) {
65
+ const timeStr = date.toISOString();
66
+ let msgStr;
67
+ if (typeof message === 'string') {
68
+ msgStr = message;
69
+ }
70
+ else {
71
+ try {
72
+ msgStr = JSON.stringify(message, null, 2);
73
+ }
74
+ catch {
75
+ msgStr = String(message);
76
+ }
77
+ }
78
+ return `[${timeStr}] [${level}] ${msgStr}\n`;
79
+ }
80
+ /**
81
+ * 统一日志处理类
82
+ * 支持写入日志文件和控制台打印
83
+ */
84
+ export class Logger {
85
+ /**
86
+ * 构造函数,初始化日志配置
87
+ * @param options 配置项,可选
88
+ */
89
+ constructor(options) {
90
+ var _a, _b;
91
+ const opts = { ...defaultOptions, ...options };
92
+ // 如果未传 logsDir,则默认设置为入口文件所在目录的上级目录的 logs 文件夹
93
+ this.logsDir =
94
+ opts.logsDir ||
95
+ path.resolve(path.dirname(opts.entryFilePath), '..', 'logs');
96
+ this.filenameFormatter =
97
+ (_a = opts.filenameFormatter) !== null && _a !== void 0 ? _a : defaultOptions.filenameFormatter;
98
+ this.env = (_b = opts.env) !== null && _b !== void 0 ? _b : defaultOptions.env;
99
+ }
100
+ /**
101
+ * 写日志主函数
102
+ * @param level 日志级别
103
+ * @param message 日志内容,支持字符串或对象
104
+ * @param printConsole 是否打印到控制台,默认 false
105
+ */
106
+ log(level, message, printConsole = false) {
107
+ const now = new Date();
108
+ const logLine = formatLogLine(level, message, now);
109
+ // 根据调用时是否指定打印控制台,决定是否打印
110
+ if (printConsole) {
111
+ switch (level) {
112
+ case 'INFO':
113
+ console.info(logLine.trim());
114
+ break;
115
+ case 'WARN':
116
+ console.warn(logLine.trim());
117
+ break;
118
+ case 'ERROR':
119
+ console.error(logLine.trim());
120
+ break;
121
+ }
122
+ }
123
+ try {
124
+ // 确保日志目录存在
125
+ if (!fs.existsSync(this.logsDir)) {
126
+ fs.mkdirSync(this.logsDir, { recursive: true });
127
+ }
128
+ const filename = this.filenameFormatter(now);
129
+ const logFilePath = path.join(this.logsDir, filename);
130
+ // 追加写入日志文件
131
+ fs.appendFileSync(logFilePath, logLine, { encoding: 'utf8' });
132
+ }
133
+ catch (error) {
134
+ // 仅在开发环境打印写日志异常堆栈,生产环境静默失败,避免影响主程序
135
+ if (this.env === 'development') {
136
+ console.error('日志写入异常:', normalizeError(error).stack);
137
+ }
138
+ }
139
+ }
140
+ /**
141
+ * 记录信息级别日志
142
+ * @param message 日志内容
143
+ * @param printConsole 是否打印到控制台,默认 false
144
+ */
145
+ info(message, printConsole = false) {
146
+ this.log('INFO', message, printConsole);
147
+ }
148
+ /**
149
+ * 记录警告级别日志
150
+ * @param message 日志内容
151
+ * @param printConsole 是否打印到控制台,默认 false
152
+ */
153
+ warn(message, printConsole = false) {
154
+ this.log('WARN', message, printConsole);
155
+ }
156
+ /**
157
+ * 记录错误级别日志
158
+ * @param message 日志内容
159
+ * @param printConsole 是否打印到控制台,默认 false
160
+ */
161
+ error(message, printConsole = false) {
162
+ this.log('ERROR', message, printConsole);
163
+ }
164
+ }
165
+ /**
166
+ * 默认导出单例 logger,方便直接使用
167
+ * 默认不打印控制台日志
168
+ */
169
+ export const logger = new Logger({
170
+ env: process.env.NODE_ENV || 'development',
171
+ });
172
+ /**
173
+ * 通用错误日志记录函数
174
+ * @param error 捕获的错误对象
175
+ * @param logger 日志对象,需包含 error 方法
176
+ * @param prefix 日志前缀,方便区分来源,默认值为 '程序执行时发生错误'
177
+ * @param printConsole 是否打印错误日志到控制台,默认 false
178
+ */
179
+ export function loggerError(error, logger, prefix = '程序执行时发生错误', printConsole = false) {
180
+ logger.error(`${prefix}:${normalizeError(error).stack}`, printConsole);
181
+ }
182
+ /**
183
+ * 将任意错误对象规范化为 Error 类型。
184
+ * @param err - 可能是 Error、字符串或其他任意类型
185
+ * @returns 标准的 Error 对象
186
+ */
187
+ export const normalizeError = (err) => {
188
+ if (err instanceof Error) {
189
+ // 已经是 Error 类型,直接返回
190
+ return err;
191
+ }
192
+ else if (typeof err === 'string') {
193
+ // 如果是字符串,创建新的 Error
194
+ return new Error(err);
195
+ }
196
+ else {
197
+ // 其他情况,返回通用错误
198
+ return new Error('未知错误');
199
+ }
200
+ };
201
+ /**
202
+ * 获取支持的语言列表
203
+ * 调用语言工具接口,返回所有支持的语言信息数组
204
+ * @returns Promise<LanguageTool> 返回语言列表的 Promise
205
+ * @throws 接口请求失败时抛出错误,错误信息包含接口地址和异常堆栈
206
+ */
207
+ export async function getLanguageTool() {
208
+ const url = api.LANGUAGE_TOOL_V2_LANGUAGES;
209
+ try {
210
+ const res = await getData(url);
211
+ return res;
212
+ }
213
+ catch (error) {
214
+ // 捕获异常并包装错误信息,包含接口地址和堆栈信息
215
+ throw new Error(`${url}接口报错:${normalizeError(error).stack}`);
216
+ }
217
+ }
218
+ /**
219
+ * 调用语言检测接口,检测文本中的语言错误
220
+ * @param text 待检测文本
221
+ * @param language 语言代码,默认 'en-US'
222
+ * @returns Promise<CheckResult> 返回检测结果的 Promise
223
+ * @throws 接口请求失败时抛出错误,错误信息包含接口地址和异常堆栈
224
+ */
225
+ export async function languageToolCheck(text, language = 'en-US') {
226
+ const url = api.LANGUAGE_TOOL_V2_CHECK;
227
+ const params = new URLSearchParams();
228
+ params.append('text', text);
229
+ params.append('language', language);
230
+ try {
231
+ const res = await postData(url, params.toString(), {
232
+ 'Content-Type': 'application/x-www-form-urlencoded',
233
+ });
234
+ return res;
235
+ }
236
+ catch (error) {
237
+ // 捕获异常并包装错误信息,包含接口地址和堆栈信息
238
+ throw new Error(`${url}接口报错:${normalizeError(error).stack}`);
239
+ }
240
+ }
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "td-web-cli",
3
+ "version": "0.1.0",
4
+ "description": "A CLI tool for efficiency",
5
+ "main": "dist/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "td-web-cli": "./dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "format": "prettier --write \"src/**/*.{js,ts,tsx,json,md}\""
13
+ },
14
+ "keywords": [],
15
+ "author": "",
16
+ "license": "ISC",
17
+ "dependencies": {
18
+ "@inquirer/prompts": "^8.2.0",
19
+ "axios": "^1.13.3",
20
+ "commander": "^14.0.2",
21
+ "xlsx": "^0.18.5"
22
+ },
23
+ "devDependencies": {
24
+ "@types/commander": "^2.12.0",
25
+ "@types/node": "^25.0.10",
26
+ "prettier": "^3.8.1",
27
+ "typescript": "^5.9.3"
28
+ }
29
+ }