td-web-cli 0.1.22 → 0.1.24
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/dist/modules/i18n/excel2json/index.d.ts.map +1 -1
- package/dist/modules/i18n/excel2json/index.js +19 -37
- package/dist/modules/i18n/index.d.ts.map +1 -1
- package/dist/modules/i18n/index.js +12 -0
- package/dist/modules/i18n/json2excel/index.d.ts +6 -1
- package/dist/modules/i18n/json2excel/index.d.ts.map +1 -1
- package/dist/modules/i18n/json2excel/index.js +156 -2
- package/dist/modules/i18n/jsonMerge/config.d.ts +20 -0
- package/dist/modules/i18n/jsonMerge/config.d.ts.map +1 -0
- package/dist/modules/i18n/jsonMerge/config.js +40 -0
- package/dist/modules/i18n/jsonMerge/index.d.ts +6 -1
- package/dist/modules/i18n/jsonMerge/index.d.ts.map +1 -1
- package/dist/modules/i18n/jsonMerge/index.js +195 -2
- package/dist/modules/tools/getHolidayTime/index.js +2 -2
- package/dist/utils/index.d.ts +12 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +27 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/i18n/excel2json/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/i18n/excel2json/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiLpC;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,OAAO,iBAqWhD"}
|
|
@@ -3,7 +3,7 @@ import XLSX from 'xlsx';
|
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
import path from 'path';
|
|
6
|
-
import { getTimestamp, logger, loggerError, normalizeError, languageToolCheck, getLanguageTool, formatKey, } from '../../../utils/index.js';
|
|
6
|
+
import { getTimestamp, logger, loggerError, normalizeError, languageToolCheck, getLanguageTool, formatKey, normalizeGitBashPath, trimQuotes, } from '../../../utils/index.js';
|
|
7
7
|
// 获取当前文件目录
|
|
8
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
9
|
const __dirname = path.dirname(__filename);
|
|
@@ -41,8 +41,9 @@ function loadConfig(configPath) {
|
|
|
41
41
|
* @returns 匹配到的语言KEY,未匹配返回null
|
|
42
42
|
*/
|
|
43
43
|
function matchLangKey(colName, langs) {
|
|
44
|
-
if (!colName)
|
|
44
|
+
if (!colName) {
|
|
45
45
|
return null;
|
|
46
|
+
}
|
|
46
47
|
const colNameLower = colName.toLowerCase();
|
|
47
48
|
// 先尝试匹配语言KEY
|
|
48
49
|
for (const langKey of Object.keys(langs)) {
|
|
@@ -58,33 +59,6 @@ function matchLangKey(colName, langs) {
|
|
|
58
59
|
}
|
|
59
60
|
return null;
|
|
60
61
|
}
|
|
61
|
-
/**
|
|
62
|
-
* 去除字符串首尾的单引号或双引号
|
|
63
|
-
* @param str 输入字符串
|
|
64
|
-
* @returns 去除引号后的字符串
|
|
65
|
-
*/
|
|
66
|
-
function trimQuotes(str) {
|
|
67
|
-
if ((str.startsWith('"') && str.endsWith('"')) ||
|
|
68
|
-
(str.startsWith("'") && str.endsWith("'"))) {
|
|
69
|
-
return str.slice(1, -1);
|
|
70
|
-
}
|
|
71
|
-
return str;
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* 将Git Bash风格的路径(如 /d/...)转换成Windows风格路径(D:/...)
|
|
75
|
-
* @param inputPath 用户输入的路径
|
|
76
|
-
* @returns 转换后的绝对路径
|
|
77
|
-
*/
|
|
78
|
-
function normalizeGitBashPath(inputPath) {
|
|
79
|
-
let cleaned = inputPath.trim().replace(/^['"]|['"]$/g, '');
|
|
80
|
-
// 如果路径是 /d/... 格式,转换成 D:/...
|
|
81
|
-
if (/^\/[a-zA-Z]\//.test(cleaned)) {
|
|
82
|
-
cleaned = cleaned.replace(/^\/([a-zA-Z])\//, '$1:/');
|
|
83
|
-
}
|
|
84
|
-
// 使用 path.resolve 转成绝对路径(相对于当前工作目录)
|
|
85
|
-
const absolutePath = path.resolve(process.cwd(), cleaned);
|
|
86
|
-
return absolutePath;
|
|
87
|
-
}
|
|
88
62
|
/**
|
|
89
63
|
* 批量检测词条文本,返回所有检测结果
|
|
90
64
|
* @param texts 词条数组
|
|
@@ -133,8 +107,9 @@ function parseCheckResultPerEntry(checkResult, texts) {
|
|
|
133
107
|
const errorOffset = match.offset;
|
|
134
108
|
// 找出错误所在的词条索引
|
|
135
109
|
const idx = positions.findIndex((range) => errorOffset >= range.start && errorOffset < range.end);
|
|
136
|
-
if (idx === -1)
|
|
110
|
+
if (idx === -1) {
|
|
137
111
|
continue;
|
|
112
|
+
}
|
|
138
113
|
// 生成错误信息字符串
|
|
139
114
|
const errMsg = `错误: ${match.message}\n出错句子: ${match.sentence}\n建议替换: ${match.replacements
|
|
140
115
|
.map((r) => r.value)
|
|
@@ -208,14 +183,17 @@ export async function excel2json(program) {
|
|
|
208
183
|
message: '请输入Excel文件路径:',
|
|
209
184
|
validate: (value) => {
|
|
210
185
|
const cleaned = value.trim().replace(/^['"]|['"]$/g, '');
|
|
211
|
-
if (cleaned.length === 0)
|
|
186
|
+
if (cleaned.length === 0) {
|
|
212
187
|
return '路径不能为空';
|
|
188
|
+
}
|
|
213
189
|
// 这里调用路径转换确保校验时路径正确
|
|
214
190
|
const normalizedPath = normalizeGitBashPath(cleaned);
|
|
215
|
-
if (!fs.existsSync(normalizedPath))
|
|
191
|
+
if (!fs.existsSync(normalizedPath)) {
|
|
216
192
|
return '文件不存在,请输入有效路径';
|
|
217
|
-
|
|
193
|
+
}
|
|
194
|
+
if (!/\.(xls|xlsx)$/i.test(normalizedPath)) {
|
|
218
195
|
return '请输入有效的Excel文件路径(.xls或.xlsx)';
|
|
196
|
+
}
|
|
219
197
|
return true;
|
|
220
198
|
},
|
|
221
199
|
});
|
|
@@ -280,13 +258,15 @@ export async function excel2json(program) {
|
|
|
280
258
|
for (let i = 1; i < rows.length; i++) {
|
|
281
259
|
const row = rows[i];
|
|
282
260
|
let keyCell = row[defaultColNum];
|
|
283
|
-
if (keyCell === undefined || keyCell === null || keyCell === '')
|
|
261
|
+
if (keyCell === undefined || keyCell === null || keyCell === '') {
|
|
284
262
|
continue;
|
|
263
|
+
}
|
|
285
264
|
let key = String(keyCell).trim();
|
|
286
265
|
key = trimQuotes(key); // 去除引号
|
|
287
266
|
// 跳过空KEY,避免写入无效数据
|
|
288
|
-
if (key.length === 0)
|
|
267
|
+
if (key.length === 0) {
|
|
289
268
|
continue;
|
|
269
|
+
}
|
|
290
270
|
// 判断默认语言KEY是否重复,若重复则重新编码
|
|
291
271
|
if (langKeySets[defaultLang].has(key)) {
|
|
292
272
|
key = formatKey(key);
|
|
@@ -298,8 +278,9 @@ export async function excel2json(program) {
|
|
|
298
278
|
// 其他语言词条
|
|
299
279
|
for (const [colIdxStr, langKey] of Object.entries(colIndexToLangKey)) {
|
|
300
280
|
const colIdx = Number(colIdxStr);
|
|
301
|
-
if (langKey === defaultLang)
|
|
281
|
+
if (langKey === defaultLang) {
|
|
302
282
|
continue;
|
|
283
|
+
}
|
|
303
284
|
const valCell = row[colIdx];
|
|
304
285
|
if (valCell !== undefined && valCell !== null && valCell !== '') {
|
|
305
286
|
const valStr = String(valCell);
|
|
@@ -370,8 +351,9 @@ export async function excel2json(program) {
|
|
|
370
351
|
logger.info(`开始生成语言文件,输出目录:${outputRoot}`, true);
|
|
371
352
|
// 按语言生成对应的JSON文件,默认语言的KEY=VALUE不生成文件
|
|
372
353
|
for (const [langKey, translations] of Object.entries(langTranslations)) {
|
|
373
|
-
if (Object.keys(translations).length === 0)
|
|
354
|
+
if (Object.keys(translations).length === 0) {
|
|
374
355
|
continue;
|
|
356
|
+
}
|
|
375
357
|
if (langKey === defaultLang) {
|
|
376
358
|
logger.info(`跳过默认语言(${langKey})的JSON文件生成`, true);
|
|
377
359
|
continue; // 跳过默认语言文件生成
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/modules/i18n/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/modules/i18n/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC;;;;GAIG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,OAAO,iBA6E1C"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { select, Separator } from '@inquirer/prompts';
|
|
2
2
|
import { logger, loggerError } from '../../utils/index.js';
|
|
3
3
|
import { excel2json } from './excel2json/index.js';
|
|
4
|
+
import { json2excel } from './json2excel/index.js';
|
|
5
|
+
import { jsonMerge } from './jsonMerge/index.js';
|
|
4
6
|
/**
|
|
5
7
|
* 国际化模块主入口
|
|
6
8
|
* 提供多个国际化相关功能的交互式选择
|
|
@@ -57,6 +59,16 @@ export async function i18n(program) {
|
|
|
57
59
|
await excel2json(program);
|
|
58
60
|
logger.info(`${selectedModule.name}功能执行完成`);
|
|
59
61
|
break;
|
|
62
|
+
case 'json2excel':
|
|
63
|
+
logger.info(`${selectedModule.name}功能开始执行`);
|
|
64
|
+
await json2excel(program);
|
|
65
|
+
logger.info(`${selectedModule.name}功能执行完成`);
|
|
66
|
+
break;
|
|
67
|
+
case 'jsonMerge':
|
|
68
|
+
logger.info(`${selectedModule.name}功能开始执行`);
|
|
69
|
+
await jsonMerge(program);
|
|
70
|
+
logger.info(`${selectedModule.name}功能执行完成`);
|
|
71
|
+
break;
|
|
60
72
|
default:
|
|
61
73
|
logger.warn(`${selectedModule.name}功能暂未实现,程序已退出`);
|
|
62
74
|
process.exit(0);
|
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
/**
|
|
3
|
+
* JSON 转 Excel 主函数
|
|
4
|
+
* 将多语言 JSON 文件合并为一个 Excel 文件,表头使用语言名称(从配置的 langs 中取第一个)
|
|
5
|
+
*/
|
|
6
|
+
export declare function json2excel(program: Command): Promise<void>;
|
|
2
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/i18n/json2excel/index.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/i18n/json2excel/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+CpC;;;GAGG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,OAAO,iBAsIhD"}
|
|
@@ -1,3 +1,157 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { input } from '@inquirer/prompts';
|
|
2
|
+
import XLSX from 'xlsx';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { getTimestamp, logger, loggerError, normalizeError, normalizeGitBashPath, } from '../../../utils/index.js';
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
/**
|
|
10
|
+
* 读取并解析配置文件(同 excel2json)
|
|
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
|
+
* JSON 转 Excel 主函数
|
|
34
|
+
* 将多语言 JSON 文件合并为一个 Excel 文件,表头使用语言名称(从配置的 langs 中取第一个)
|
|
35
|
+
*/
|
|
36
|
+
export async function json2excel(program) {
|
|
37
|
+
var _a;
|
|
38
|
+
// 配置文件默认路径
|
|
39
|
+
const configPath = path.join(__dirname, '../../../../setting.json');
|
|
40
|
+
let i18nConfig;
|
|
41
|
+
// 加载配置文件
|
|
42
|
+
try {
|
|
43
|
+
logger.info(`开始加载配置文件:${configPath}`, true);
|
|
44
|
+
i18nConfig = loadConfig(configPath);
|
|
45
|
+
logger.info('配置文件加载成功', true);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
const msg = `读取配置文件失败:${normalizeError(error).stack},程序已退出`;
|
|
49
|
+
logger.error(msg);
|
|
50
|
+
console.error('程序执行时发生异常,已记录日志,程序已退出');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
// 交互式输入 JSON 根目录
|
|
54
|
+
const answer = await input({
|
|
55
|
+
message: '请输入存放多语言 JSON 的根目录:',
|
|
56
|
+
validate: (value) => {
|
|
57
|
+
const cleaned = value.trim().replace(/^['"]|['"]$/g, '');
|
|
58
|
+
if (cleaned.length === 0) {
|
|
59
|
+
return '路径不能为空';
|
|
60
|
+
}
|
|
61
|
+
const normalizedPath = normalizeGitBashPath(cleaned);
|
|
62
|
+
if (!fs.existsSync(normalizedPath)) {
|
|
63
|
+
return '目录不存在,请输入有效路径';
|
|
64
|
+
}
|
|
65
|
+
if (!fs.statSync(normalizedPath).isDirectory()) {
|
|
66
|
+
return '请输入一个目录路径';
|
|
67
|
+
}
|
|
68
|
+
return true;
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
const rootDir = normalizeGitBashPath(answer);
|
|
72
|
+
try {
|
|
73
|
+
logger.info(`开始扫描目录:${rootDir}`, true);
|
|
74
|
+
// 获取根目录下所有子目录(作为语言 KEY)
|
|
75
|
+
const subDirs = fs.readdirSync(rootDir).filter((name) => {
|
|
76
|
+
const fullPath = path.join(rootDir, name);
|
|
77
|
+
return fs.statSync(fullPath).isDirectory();
|
|
78
|
+
});
|
|
79
|
+
// 加载每个语言的翻译
|
|
80
|
+
const translations = {};
|
|
81
|
+
const availableLangs = [];
|
|
82
|
+
for (const langKey of subDirs) {
|
|
83
|
+
const jsonPath = path.join(rootDir, langKey, 'translate.json');
|
|
84
|
+
if (!fs.existsSync(jsonPath)) {
|
|
85
|
+
logger.warn(`跳过 ${langKey},不存在 translate.json`, true);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const content = fs.readFileSync(jsonPath, 'utf-8');
|
|
90
|
+
const data = JSON.parse(content);
|
|
91
|
+
translations[langKey] = data;
|
|
92
|
+
availableLangs.push(langKey);
|
|
93
|
+
logger.info(`已加载语言:${langKey},词条数量:${Object.keys(data).length}`, true);
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
logger.error(`读取 ${langKey} 的 translate.json 失败:${normalizeError(err).message}`, true);
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (availableLangs.length === 0) {
|
|
101
|
+
logger.error('未找到任何有效的 translate.json 文件,程序退出', true);
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
// 默认语言
|
|
105
|
+
const defaultLang = i18nConfig.defaultKey;
|
|
106
|
+
if (!availableLangs.includes(defaultLang)) {
|
|
107
|
+
logger.warn(`默认语言 ${defaultLang} 不存在于已加载的语言中,将使用所有语言的 KEY 并集作为基准`, true);
|
|
108
|
+
}
|
|
109
|
+
// 收集所有 KEY 的并集(作为 Excel 第一列)
|
|
110
|
+
const allKeysSet = new Set();
|
|
111
|
+
for (const lang of availableLangs) {
|
|
112
|
+
Object.keys(translations[lang]).forEach((key) => allKeysSet.add(key));
|
|
113
|
+
}
|
|
114
|
+
const sortedKeys = Array.from(allKeysSet).sort();
|
|
115
|
+
logger.info(`共收集到 ${sortedKeys.length} 个唯一 KEY`, true);
|
|
116
|
+
// 构建表头:第一列使用默认语言的名称(从配置中获取),后面列使用其他语言的名称
|
|
117
|
+
const header = [];
|
|
118
|
+
// 第一列名称:优先使用默认语言的第一个名称,否则用 defaultKey
|
|
119
|
+
const defaultLangNames = i18nConfig.langs[defaultLang];
|
|
120
|
+
const firstColName = (defaultLangNames && defaultLangNames.length > 0)
|
|
121
|
+
? defaultLangNames[0]
|
|
122
|
+
: defaultLang;
|
|
123
|
+
header.push(firstColName);
|
|
124
|
+
const nonDefaultLangs = availableLangs.filter((lang) => lang !== defaultLang);
|
|
125
|
+
for (const lang of nonDefaultLangs) {
|
|
126
|
+
const names = i18nConfig.langs[lang];
|
|
127
|
+
const colName = (names && names.length > 0) ? names[0] : lang;
|
|
128
|
+
header.push(colName);
|
|
129
|
+
}
|
|
130
|
+
// 构建 Excel 数据行
|
|
131
|
+
const rows = [header];
|
|
132
|
+
for (const key of sortedKeys) {
|
|
133
|
+
const row = [key]; // 第一列是 KEY
|
|
134
|
+
for (const lang of nonDefaultLangs) {
|
|
135
|
+
const val = (_a = translations[lang]) === null || _a === void 0 ? void 0 : _a[key];
|
|
136
|
+
row.push(val !== undefined ? val : null); // 无翻译时留空
|
|
137
|
+
}
|
|
138
|
+
rows.push(row);
|
|
139
|
+
}
|
|
140
|
+
// 生成 Excel 文件
|
|
141
|
+
const timestamp = getTimestamp();
|
|
142
|
+
const outputFileName = `lang_merged_${timestamp}.xlsx`;
|
|
143
|
+
const outputPath = path.join(rootDir, outputFileName);
|
|
144
|
+
const wb = XLSX.utils.book_new();
|
|
145
|
+
const ws = XLSX.utils.aoa_to_sheet(rows);
|
|
146
|
+
XLSX.utils.book_append_sheet(wb, ws, 'Translations');
|
|
147
|
+
XLSX.writeFile(wb, outputPath);
|
|
148
|
+
logger.info(`Excel 文件已生成:${outputPath}`, true);
|
|
149
|
+
logger.info(`共处理语言:${availableLangs.join(', ')}`, true);
|
|
150
|
+
logger.info('转换完成', true);
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
loggerError(error, logger);
|
|
154
|
+
console.error('程序执行时发生异常,已记录日志,程序已退出');
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
3
157
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 进度显示管理器
|
|
3
|
+
* 用于在同一行显示进度,避免被其他日志干扰
|
|
4
|
+
*/
|
|
5
|
+
export declare class ProgressDisplay {
|
|
6
|
+
private isActive;
|
|
7
|
+
/**
|
|
8
|
+
* 更新进度显示
|
|
9
|
+
*/
|
|
10
|
+
update(progressBar: string, current: number, total: number, key: string): void;
|
|
11
|
+
/**
|
|
12
|
+
* 完成进度显示
|
|
13
|
+
*/
|
|
14
|
+
complete(progressBar: string): void;
|
|
15
|
+
/**
|
|
16
|
+
* 强制清除当前行
|
|
17
|
+
*/
|
|
18
|
+
clearLine(): void;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../../src/modules/i18n/jsonMerge/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,eAAe;IACxB,OAAO,CAAC,QAAQ,CAAkB;IAElC;;OAEG;IACH,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM;IAWvE;;OAEG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM;IAW5B;;OAEG;IACH,SAAS;CAMV"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 进度显示管理器
|
|
3
|
+
* 用于在同一行显示进度,避免被其他日志干扰
|
|
4
|
+
*/
|
|
5
|
+
export class ProgressDisplay {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.isActive = false;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 更新进度显示
|
|
11
|
+
*/
|
|
12
|
+
update(progressBar, current, total, key) {
|
|
13
|
+
const message = ` ${progressBar} 正在处理: ${key}`;
|
|
14
|
+
if (this.isActive) {
|
|
15
|
+
process.stdout.write('\x1b[2K\r'); // 清除整行,回到行首
|
|
16
|
+
}
|
|
17
|
+
process.stdout.write(message);
|
|
18
|
+
this.isActive = true;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 完成进度显示
|
|
22
|
+
*/
|
|
23
|
+
complete(progressBar) {
|
|
24
|
+
const message = ` ${progressBar} 处理完成`;
|
|
25
|
+
if (this.isActive) {
|
|
26
|
+
process.stdout.write('\x1b[2K\r');
|
|
27
|
+
}
|
|
28
|
+
console.log(message); // 换行显示完成信息
|
|
29
|
+
this.isActive = false;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 强制清除当前行
|
|
33
|
+
*/
|
|
34
|
+
clearLine() {
|
|
35
|
+
if (this.isActive) {
|
|
36
|
+
process.stdout.write('\x1b[2K\r');
|
|
37
|
+
this.isActive = false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
/**
|
|
3
|
+
* 主函数:合并两个目录下相同语言文件夹的JSON文件
|
|
4
|
+
* @param program commander命令行实例(暂未使用,可扩展)
|
|
5
|
+
*/
|
|
6
|
+
export declare function jsonMerge(program: Command): Promise<void>;
|
|
2
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/i18n/jsonMerge/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/i18n/jsonMerge/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuGpC;;;GAGG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,OAAO,iBA4J/C"}
|
|
@@ -1,3 +1,196 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { input, select, Separator } from '@inquirer/prompts';
|
|
4
|
+
import { logger, loggerError, normalizeError, normalizeGitBashPath, } from '../../../utils/index.js';
|
|
5
|
+
/**
|
|
6
|
+
* 读取JSON文件内容,返回对象,文件不存在返回空对象
|
|
7
|
+
* @param filePath JSON文件路径
|
|
8
|
+
* @returns 解析后的对象,出错或不存在返回空对象
|
|
9
|
+
*/
|
|
10
|
+
function readJsonFile(filePath) {
|
|
11
|
+
if (!fs.existsSync(filePath)) {
|
|
12
|
+
return {};
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
16
|
+
return JSON.parse(content);
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
logger.error(`读取JSON文件失败: ${filePath},错误: ${normalizeError(error).message}`);
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 写入JSON文件,格式化缩进2格
|
|
25
|
+
* @param filePath 写入文件路径
|
|
26
|
+
* @param data 写入的对象数据
|
|
27
|
+
*/
|
|
28
|
+
function writeJsonFile(filePath, data) {
|
|
29
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
30
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* 合并两个JSON对象,检测重复key,重复时通过交互让用户选择保留哪个值
|
|
34
|
+
* @param baseObj 源JSON对象(被合并到此对象)
|
|
35
|
+
* @param mergeObj 待合并JSON对象
|
|
36
|
+
* @param langKey 当前语言标识,用于日志提示
|
|
37
|
+
* @returns 合并后的JSON对象
|
|
38
|
+
*/
|
|
39
|
+
async function mergeJsonObjects(baseObj, mergeObj, langKey) {
|
|
40
|
+
const entries = Object.entries(mergeObj);
|
|
41
|
+
for (const [key, val] of entries) {
|
|
42
|
+
if (key in baseObj) {
|
|
43
|
+
if (baseObj[key] === val) {
|
|
44
|
+
// 值相同,无需处理,继续下一个键
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
// 值不同,需要用户交互选择保留哪个值
|
|
48
|
+
logger.info(`发现冲突: 键【${key}】`, true);
|
|
49
|
+
const choice = await select({
|
|
50
|
+
message: `请选择要保留的值:`,
|
|
51
|
+
choices: [
|
|
52
|
+
{ name: `源文件值: ${baseObj[key]}`, value: 'base' },
|
|
53
|
+
{ name: `合并文件值: ${val}`, value: 'merge' },
|
|
54
|
+
new Separator(),
|
|
55
|
+
],
|
|
56
|
+
default: 'base',
|
|
57
|
+
pageSize: 10,
|
|
58
|
+
loop: true,
|
|
59
|
+
});
|
|
60
|
+
if (choice === 'merge') {
|
|
61
|
+
baseObj[key] = val;
|
|
62
|
+
}
|
|
63
|
+
// 如果选择保留base,则保持不变
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
// 新键,直接添加
|
|
67
|
+
baseObj[key] = val;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return baseObj;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 获取指定语言文件夹下的所有JSON文件名列表
|
|
74
|
+
* @param dirPath 语言文件夹路径
|
|
75
|
+
* @returns JSON文件名数组
|
|
76
|
+
*/
|
|
77
|
+
function getJsonFilesInLangDir(dirPath) {
|
|
78
|
+
if (!fs.existsSync(dirPath)) {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
return fs.readdirSync(dirPath).filter((fileName) => {
|
|
82
|
+
const fullPath = path.join(dirPath, fileName);
|
|
83
|
+
return fs.statSync(fullPath).isFile() && fileName.endsWith('.json');
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* 主函数:合并两个目录下相同语言文件夹的JSON文件
|
|
88
|
+
* @param program commander命令行实例(暂未使用,可扩展)
|
|
89
|
+
*/
|
|
90
|
+
export async function jsonMerge(program) {
|
|
91
|
+
try {
|
|
92
|
+
// 交互输入源目录路径
|
|
93
|
+
const srcDir = await input({
|
|
94
|
+
message: '请输入源JSON文件夹路径(含语言子文件夹,如cn/translate.json):',
|
|
95
|
+
validate: (value) => {
|
|
96
|
+
const cleaned = value.trim().replace(/^['"]|['"]$/g, '');
|
|
97
|
+
if (cleaned.length === 0) {
|
|
98
|
+
return '路径不能为空';
|
|
99
|
+
}
|
|
100
|
+
const normalizedPath = normalizeGitBashPath(cleaned);
|
|
101
|
+
if (!fs.existsSync(normalizedPath)) {
|
|
102
|
+
return '文件不存在,请输入有效路径';
|
|
103
|
+
}
|
|
104
|
+
return true;
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
// 交互输入待合并目录路径
|
|
108
|
+
const mergeDir = await input({
|
|
109
|
+
message: '请输入待合并JSON文件夹路径(含语言子文件夹,如cn/translate.json):',
|
|
110
|
+
validate: (value) => {
|
|
111
|
+
const cleaned = value.trim().replace(/^['"]|['"]$/g, '');
|
|
112
|
+
if (cleaned.length === 0) {
|
|
113
|
+
return '路径不能为空';
|
|
114
|
+
}
|
|
115
|
+
const normalizedPath = normalizeGitBashPath(cleaned);
|
|
116
|
+
if (!fs.existsSync(normalizedPath)) {
|
|
117
|
+
return '文件不存在,请输入有效路径';
|
|
118
|
+
}
|
|
119
|
+
return true;
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
const srcPath = normalizeGitBashPath(srcDir);
|
|
123
|
+
const mergePath = normalizeGitBashPath(mergeDir);
|
|
124
|
+
logger.info(`源目录: ${srcPath}`);
|
|
125
|
+
logger.info(`合并目录: ${mergePath}`);
|
|
126
|
+
// 获取两个目录下的一级语言子文件夹列表
|
|
127
|
+
const srcLangDirs = fs.readdirSync(srcPath).filter((f) => {
|
|
128
|
+
const fullPath = path.join(srcPath, f);
|
|
129
|
+
return fs.statSync(fullPath).isDirectory();
|
|
130
|
+
});
|
|
131
|
+
const mergeLangDirs = fs.readdirSync(mergePath).filter((f) => {
|
|
132
|
+
const fullPath = path.join(mergePath, f);
|
|
133
|
+
return fs.statSync(fullPath).isDirectory();
|
|
134
|
+
});
|
|
135
|
+
// 找出两个目录下都存在的语言文件夹
|
|
136
|
+
const commonLangDirs = srcLangDirs.filter((lang) => mergeLangDirs.includes(lang));
|
|
137
|
+
if (commonLangDirs.length === 0) {
|
|
138
|
+
logger.info('没有发现相同语言文件夹,无需合并');
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
logger.info(`发现 ${commonLangDirs.length} 个共同语言文件夹: ${commonLangDirs.join(', ')}`, true);
|
|
142
|
+
// 遍历每个共同语言文件夹进行合并
|
|
143
|
+
for (const langKey of commonLangDirs) {
|
|
144
|
+
logger.info(`${'='.repeat(60)}`, true);
|
|
145
|
+
logger.info(`处理语言: ${langKey}`, true);
|
|
146
|
+
const srcLangPath = path.join(srcPath, langKey);
|
|
147
|
+
const mergeLangPath = path.join(mergePath, langKey);
|
|
148
|
+
const srcJsonFiles = getJsonFilesInLangDir(srcLangPath);
|
|
149
|
+
const mergeJsonFiles = getJsonFilesInLangDir(mergeLangPath);
|
|
150
|
+
// 找出两个语言文件夹中共同存在的JSON文件
|
|
151
|
+
const commonJsonFiles = srcJsonFiles.filter((file) => mergeJsonFiles.includes(file));
|
|
152
|
+
if (commonJsonFiles.length === 0) {
|
|
153
|
+
logger.info(`语言【${langKey}】下没有共同的JSON文件,跳过`, true);
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
logger.info(`发现 ${commonJsonFiles.length} 个共同JSON文件: ${commonJsonFiles.join(', ')}`, true);
|
|
157
|
+
// 逐个文件合并
|
|
158
|
+
for (const jsonFile of commonJsonFiles) {
|
|
159
|
+
logger.info(`处理文件: ${jsonFile}`, true);
|
|
160
|
+
const srcFile = path.join(srcLangPath, jsonFile);
|
|
161
|
+
const mergeFile = path.join(mergeLangPath, jsonFile);
|
|
162
|
+
if (!fs.existsSync(srcFile)) {
|
|
163
|
+
logger.warn(`源目录语言文件缺失: ${srcFile},跳过`, true);
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (!fs.existsSync(mergeFile)) {
|
|
167
|
+
logger.warn(`合并目录语言文件缺失: ${mergeFile},跳过`, true);
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
logger.info(`开始合并文件 ${jsonFile}...`, true);
|
|
171
|
+
const srcJson = readJsonFile(srcFile);
|
|
172
|
+
const mergeJson = readJsonFile(mergeFile);
|
|
173
|
+
// 打印合并前后键数量
|
|
174
|
+
const srcKeyCount = Object.keys(srcJson).length;
|
|
175
|
+
const mergeKeyCount = Object.keys(mergeJson).length;
|
|
176
|
+
logger.info(`源文件键数: ${srcKeyCount}, 合并文件键数: ${mergeKeyCount}`, true);
|
|
177
|
+
const merged = await mergeJsonObjects(srcJson, mergeJson, langKey);
|
|
178
|
+
const finalKeyCount = Object.keys(merged).length;
|
|
179
|
+
logger.info(`合并后键数: ${finalKeyCount}`, true);
|
|
180
|
+
writeJsonFile(srcFile, merged);
|
|
181
|
+
logger.info(`文件 ${jsonFile} 合并完成`, true);
|
|
182
|
+
}
|
|
183
|
+
logger.info(`语言【${langKey}】全部处理完成`, true);
|
|
184
|
+
}
|
|
185
|
+
logger.info(`${'='.repeat(60)}`, true);
|
|
186
|
+
logger.info(`所有合并操作完成!`, true);
|
|
187
|
+
logger.info(`源目录: ${srcPath}`, true);
|
|
188
|
+
logger.info(`合并目录: ${mergePath}`, true);
|
|
189
|
+
logger.info(`所有修改已写回源目录`, true);
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
loggerError(error, logger);
|
|
193
|
+
console.error('程序执行时发生异常,已记录日志,程序已退出');
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
3
196
|
}
|
|
@@ -29,7 +29,7 @@ function getNearestHolidays(data, count = 3) {
|
|
|
29
29
|
// 初始化第一个节日组
|
|
30
30
|
currentGroup = {
|
|
31
31
|
name: h.name,
|
|
32
|
-
daysUntil: Math.
|
|
32
|
+
daysUntil: Math.ceil((hd.getTime() - today.getTime()) / (1000 * 60 * 60 * 24)),
|
|
33
33
|
holidayDates: [h.date],
|
|
34
34
|
hasTransferWorkday: false,
|
|
35
35
|
transferWorkdays: [],
|
|
@@ -47,7 +47,7 @@ function getNearestHolidays(data, count = 3) {
|
|
|
47
47
|
grouped.push(currentGroup);
|
|
48
48
|
currentGroup = {
|
|
49
49
|
name: h.name,
|
|
50
|
-
daysUntil: Math.
|
|
50
|
+
daysUntil: Math.ceil((hd.getTime() - today.getTime()) / (1000 * 60 * 60 * 24)),
|
|
51
51
|
holidayDates: [h.date],
|
|
52
52
|
hasTransferWorkday: false,
|
|
53
53
|
transferWorkdays: [],
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -160,5 +160,17 @@ export declare function formatKey(key: string): string;
|
|
|
160
160
|
* @returns 去除特殊编码前缀后的字符串
|
|
161
161
|
*/
|
|
162
162
|
export declare function decodeKey(key: string): string;
|
|
163
|
+
/**
|
|
164
|
+
* 将Git Bash风格的路径(如 /d/...)转换成Windows风格路径(D:/...)
|
|
165
|
+
* @param inputPath 用户输入的路径
|
|
166
|
+
* @returns 转换后的绝对路径
|
|
167
|
+
*/
|
|
168
|
+
export declare function normalizeGitBashPath(inputPath: string): string;
|
|
169
|
+
/**
|
|
170
|
+
* 去除字符串首尾的单引号或双引号
|
|
171
|
+
* @param str 输入字符串
|
|
172
|
+
* @returns 去除引号后的字符串
|
|
173
|
+
*/
|
|
174
|
+
export declare function trimQuotes(str: string): string;
|
|
163
175
|
export {};
|
|
164
176
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/index.js
CHANGED
|
@@ -288,3 +288,30 @@ export function decodeKey(key) {
|
|
|
288
288
|
// 去除前缀
|
|
289
289
|
return key.replace(prefixRegex, '');
|
|
290
290
|
}
|
|
291
|
+
/**
|
|
292
|
+
* 将Git Bash风格的路径(如 /d/...)转换成Windows风格路径(D:/...)
|
|
293
|
+
* @param inputPath 用户输入的路径
|
|
294
|
+
* @returns 转换后的绝对路径
|
|
295
|
+
*/
|
|
296
|
+
export function normalizeGitBashPath(inputPath) {
|
|
297
|
+
let cleaned = inputPath.trim().replace(/^['"]|['"]$/g, '');
|
|
298
|
+
// 如果路径是 /d/... 格式,转换成 D:/...
|
|
299
|
+
if (/^\/[a-zA-Z]\//.test(cleaned)) {
|
|
300
|
+
cleaned = cleaned.replace(/^\/([a-zA-Z])\//, '$1:/');
|
|
301
|
+
}
|
|
302
|
+
// 使用 path.resolve 转成绝对路径(相对于当前工作目录)
|
|
303
|
+
const absolutePath = path.resolve(process.cwd(), cleaned);
|
|
304
|
+
return absolutePath;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* 去除字符串首尾的单引号或双引号
|
|
308
|
+
* @param str 输入字符串
|
|
309
|
+
* @returns 去除引号后的字符串
|
|
310
|
+
*/
|
|
311
|
+
export function trimQuotes(str) {
|
|
312
|
+
if ((str.startsWith('"') && str.endsWith('"')) ||
|
|
313
|
+
(str.startsWith("'") && str.endsWith("'"))) {
|
|
314
|
+
return str.slice(1, -1);
|
|
315
|
+
}
|
|
316
|
+
return str;
|
|
317
|
+
}
|