td-web-cli 0.1.13 → 0.1.14

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 CHANGED
@@ -75,7 +75,7 @@ td-web-cli
75
75
 
76
76
  ### 功能说明
77
77
 
78
- - excel 表格转换为对应的 json 文件,简化数据处理流程。
78
+ - 多语言 excel json 工具。[功能说明](./docs/i18n/excel2json.md)
79
79
 
80
80
  ---
81
81
 
@@ -3,6 +3,7 @@ import { Command } from 'commander';
3
3
  * excel转json功能主函数
4
4
  * 读取用户输入的excel路径,解析内容,根据配置生成多语言json文件
5
5
  * 并对配置文件中所有语言对应的词条进行语言检测
6
+ * 如果有相同的json key,则在key前面加上6位编码,保证唯一性
6
7
  * @param program Commander命令行实例
7
8
  */
8
9
  export declare function excel2json(program: Command): Promise<void>;
@@ -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;AAyLpC;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,OAAO,iBA8ThD"}
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;AA0LpC;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,OAAO,iBAoVhD"}
@@ -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, } from '../../../utils/index.js';
6
+ import { getTimestamp, logger, loggerError, normalizeError, languageToolCheck, getLanguageTool, formatKey, } from '../../../utils/index.js';
7
7
  // 获取当前文件目录
8
8
  const __filename = fileURLToPath(import.meta.url);
9
9
  const __dirname = path.dirname(__filename);
@@ -138,6 +138,7 @@ function parseCheckResultPerEntry(checkResult, texts) {
138
138
  * excel转json功能主函数
139
139
  * 读取用户输入的excel路径,解析内容,根据配置生成多语言json文件
140
140
  * 并对配置文件中所有语言对应的词条进行语言检测
141
+ * 如果有相同的json key,则在key前面加上6位编码,保证唯一性
141
142
  * @param program Commander命令行实例
142
143
  */
143
144
  export async function excel2json(program) {
@@ -242,20 +243,26 @@ export async function excel2json(program) {
242
243
  }
243
244
  const defaultColNum = Number(defaultColIndex);
244
245
  // 初始化所有语言词条对象(包括默认语言)
246
+ // 用于存储最终的翻译key-value对,key可能会被重新编码以避免重复
245
247
  const langTranslations = {};
246
248
  Object.values(colIndexToLangKey).forEach((langKey) => {
247
249
  langTranslations[langKey] = {};
248
250
  });
249
251
  logger.info('开始解析数据行', true);
252
+ // 记录所有出现过的key,检测重复,格式:langKey => Set of keys
253
+ const langKeySets = {};
254
+ Object.keys(langTranslations).forEach((langKey) => {
255
+ langKeySets[langKey] = new Set();
256
+ });
250
257
  // 遍历数据行,提取所有语言词条
251
258
  // key统一用默认语言列的值,其他语言对应的列为翻译内容
252
- const langKeysMap = {}; // 语言key => 词条数组
259
+ const langKeysMap = {}; // 语言key => 词条数组(用于检测)
253
260
  Object.keys(langTranslations).forEach((langKey) => {
254
261
  langKeysMap[langKey] = [];
255
262
  });
256
263
  for (let i = 1; i < rows.length; i++) {
257
264
  const row = rows[i];
258
- const keyCell = row[defaultColNum];
265
+ let keyCell = row[defaultColNum];
259
266
  if (keyCell === undefined || keyCell === null || keyCell === '')
260
267
  continue;
261
268
  let key = String(keyCell).trim();
@@ -263,6 +270,11 @@ export async function excel2json(program) {
263
270
  // 跳过空key,避免写入无效数据
264
271
  if (key.length === 0)
265
272
  continue;
273
+ // 判断默认语言key是否重复,若重复则重新编码
274
+ if (langKeySets[defaultLang].has(key)) {
275
+ key = formatKey(key);
276
+ }
277
+ langKeySets[defaultLang].add(key);
266
278
  // 默认语言的词条即key本身
267
279
  langTranslations[defaultLang][key] = key;
268
280
  langKeysMap[defaultLang].push(key);
@@ -274,7 +286,13 @@ export async function excel2json(program) {
274
286
  const valCell = row[colIdx];
275
287
  if (valCell !== undefined && valCell !== null && valCell !== '') {
276
288
  const valStr = String(valCell);
277
- langTranslations[langKey][key] = valStr;
289
+ // 判断该语言的key是否重复,若重复则重新编码key
290
+ let finalKey = key;
291
+ if (langKeySets[langKey].has(finalKey)) {
292
+ finalKey = formatKey(finalKey);
293
+ }
294
+ langKeySets[langKey].add(finalKey);
295
+ langTranslations[langKey][finalKey] = valStr;
278
296
  langKeysMap[langKey].push(valStr);
279
297
  }
280
298
  else {
@@ -147,5 +147,18 @@ export interface CheckResult {
147
147
  * @throws 接口请求失败时抛出错误,错误信息包含接口地址和异常堆栈
148
148
  */
149
149
  export declare function languageToolCheck(text: string, language?: string): Promise<CheckResult>;
150
+ /**
151
+ * 对字符串重新编码,字符串前面加上6位的特殊编码(格式:字母#三个字母#)
152
+ * 例如:a#BCD#yourString
153
+ * @param key 需要进行重新编码的字符串
154
+ * @returns 带有特殊编码前缀的字符串
155
+ */
156
+ export declare function formatKey(key: string): string;
157
+ /**
158
+ * 对重新编码的字符串进行解码,去除前面6位特殊编码部分
159
+ * @param key 需要解码的字符串
160
+ * @returns 去除特殊编码前缀后的字符串
161
+ */
162
+ export declare function decodeKey(key: string): string;
150
163
  export {};
151
164
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAMA;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAmBrC;AAED;;GAEG;AACH,KAAK,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE1C;;GAEG;AACH,UAAU,aAAa;IACrB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC;IAE3C;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AA8ED;;;GAGG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,iBAAiB,CAAyB;IAClD,OAAO,CAAC,GAAG,CAAS;IAEpB;;;OAGG;gBACS,OAAO,CAAC,EAAE,aAAa;IAUnC;;;;;OAKG;IACH,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,UAAQ,GAAG,IAAI;IAsClE;;;;OAIG;IACH,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,UAAQ,GAAG,IAAI;IAIlD;;;;OAIG;IACH,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,UAAQ,GAAG,IAAI;IAIlD;;;;OAIG;IACH,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,UAAQ,GAAG,IAAI;CAGpD;AAED;;;GAGG;AACH,eAAO,MAAM,MAAM,QAEjB,CAAC;AAEH;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,OAAO,EACd,MAAM,EAAE;IAAE,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;CAAE,EAChE,MAAM,SAAc,EACpB,YAAY,UAAQ,GACnB,IAAI,CAEN;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,OAAO,KAAG,KAW7C,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB,EAAE,CAAC;AAEJ;;;;;GAKG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,YAAY,CAAC,CAS7D;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE;QAEJ,EAAE,EAAE,MAAM,CAAC;QACX,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,KAAK,EAAE,CAAC;IACjB,QAAQ,EAAE;QAER,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,QAAQ,EAAE;QAER,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,OAAO,CAAC;KAClB,CAAC;CACH;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,QAAQ,SAAU,GACjB,OAAO,CAAC,WAAW,CAAC,CAetB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"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"}
@@ -253,3 +253,38 @@ export async function languageToolCheck(text, language = 'en-US') {
253
253
  throw new Error(`${url}接口报错:${normalizeError(error).stack}`);
254
254
  }
255
255
  }
256
+ /**
257
+ * 对字符串重新编码,字符串前面加上6位的特殊编码(格式:字母#三个字母#)
258
+ * 例如:a#BCD#yourString
259
+ * @param key 需要进行重新编码的字符串
260
+ * @returns 带有特殊编码前缀的字符串
261
+ */
262
+ export function formatKey(key) {
263
+ const letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
264
+ const codeTemplate = '{0}#{1}{2}{3}#';
265
+ // 先解码,防止重复添加前缀
266
+ const decodedKey = decodeKey(key);
267
+ // 随机选取4个字母替换模板中的占位符
268
+ const getRandomLetter = () => letters[Math.floor(Math.random() * letters.length)];
269
+ const code = codeTemplate
270
+ .replace('{0}', getRandomLetter())
271
+ .replace('{1}', getRandomLetter())
272
+ .replace('{2}', getRandomLetter())
273
+ .replace('{3}', getRandomLetter());
274
+ return code + decodedKey;
275
+ }
276
+ /**
277
+ * 对重新编码的字符串进行解码,去除前面6位特殊编码部分
278
+ * @param key 需要解码的字符串
279
+ * @returns 去除特殊编码前缀后的字符串
280
+ */
281
+ export function decodeKey(key) {
282
+ if (!key) {
283
+ return '';
284
+ }
285
+ // 匹配开头格式:字母#三个字母#
286
+ // ^ 开头, [a-zA-Z] 一个字母, \# 字符 #, [a-zA-Z]{3} 三个字母, \# 字符 #
287
+ const prefixRegex = /^[a-zA-Z]\#[a-zA-Z]{3}\#/;
288
+ // 去除前缀
289
+ return key.replace(prefixRegex, '');
290
+ }
@@ -0,0 +1,67 @@
1
+ # 多语言 excel 转 json 工具
2
+
3
+ 该工具基于配置文件,实现将多语言 excel 文件转换为对应的 json 翻译文件,并支持语言检测和重复 key 处理,方便多语言项目的词条管理与质量控制。
4
+
5
+ ---
6
+
7
+ ## 主要功能
8
+
9
+ 1. **配置文件读取与解析**
10
+ - 从本地 `setting.json` 文件读取国际化配置,包括默认语言、语言映射和语言长代码。
11
+
12
+ 2. **excel 表头语言列匹配**
13
+ - 根据配置自动识别 excel 表头中的语言列,支持大小写不敏感匹配和语言名称模糊匹配。
14
+
15
+ 3. **excel 文件读取与数据解析**
16
+ - 读取用户指定路径的 excel 文件,提取第一个工作表的内容。
17
+ - 解析表头和数据行,提取多语言词条。
18
+
19
+ 4. **重复 json key 处理**
20
+ - 对默认语言列的 key 进行去重处理,若存在重复,则在 key 前添加随机生成的6位编码前缀,确保 json key 唯一。
21
+ - 其他语言的 key 也进行同样处理,避免冲突。
22
+
23
+ 5. **批量语言检测**
24
+ - 将所有语言词条拼接成字符串,一次性调用语言检测接口,减少请求次数,提高检测效率。
25
+ - 解析检测结果,拆分到每条词条对应的错误信息。
26
+
27
+ 6. **生成多语言 json 文件**
28
+ - 为每个语言生成对应的 json 翻译文件,默认语言文件不生成。
29
+
30
+ 7. **生成语言检测结果 excel 文件**
31
+ - 生成包含所有语言检测错误信息的 excel 文件,方便查看和修正。
32
+
33
+ 8. **命令行交互与日志记录**
34
+ - 通过命令行交互输入 excel 文件路径。
35
+ - 详细日志记录执行过程、错误和警告,便于排查问题。
36
+
37
+ ---
38
+
39
+ ## 使用说明
40
+
41
+ 1. 准备好符合格式的 excel 文件,第一行为表头,包含语言列(如 `en`, `zh-CN` 等)。
42
+ 2. 配置好 `setting.json`,包含国际化相关配置。
43
+ 3. 运行工具,输入 excel 文件路径。
44
+ 4. 程序将自动解析 excel,生成多语言 json 文件和语言检测结果 excel。
45
+ 5. 结果文件输出在 excel 文件所在目录的 `lang_时间戳` 文件夹中。
46
+
47
+ ---
48
+
49
+ ## 代码结构简述
50
+
51
+ - `formatKey` / `decodeKey`:处理重复 key,添加或去除随机编码前缀。
52
+ - `loadConfig`:读取并校验配置文件。
53
+ - `matchLangKey`:匹配 excel 表头语言列。
54
+ - `batchCheckTexts` / `parseCheckResultPerEntry`:批量调用语言检测并解析结果。
55
+ - `excel2json`:主函数,完成 excel 读取、解析、语言检测、文件生成等流程。
56
+
57
+ ---
58
+
59
+ ## 备注
60
+
61
+ - 默认语言的 json 文件不会生成,默认语言词条作为 key 使用。
62
+ - 语言检测依赖在线服务,若获取语言列表失败则使用本地配置。
63
+ - 重复 key 处理确保生成的 json 文件中不会有冲突的键名。
64
+
65
+ ---
66
+
67
+ 欢迎反馈和贡献!
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "td-web-cli",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "description": "A CLI tool for efficiency",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -9,7 +9,8 @@
9
9
  },
10
10
  "files": [
11
11
  "dist",
12
- "setting.json"
12
+ "setting.json",
13
+ "docs/**/*.md"
13
14
  ],
14
15
  "scripts": {
15
16
  "build": "tsc",