scancscode 1.0.42 → 1.0.46

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.
@@ -1,3 +1,3 @@
1
- #!/usr/bin/env node
2
-
3
- require("../dist/src/RunConvert.js")
1
+ #!/usr/bin/env node
2
+
3
+ require("../dist/src/RunConvert.js")
package/bin/slimlangs.js CHANGED
@@ -1,3 +1,3 @@
1
- #!/usr/bin/env node
2
-
3
- require("../dist/src/RunSlimLangs.js")
1
+ #!/usr/bin/env node
2
+
3
+ require("../dist/src/RunSlimLangs.js")
@@ -1,3 +1,3 @@
1
- #!/usr/bin/env node
2
-
3
- require("../dist/src/RunTranslateCSV.js")
1
+ #!/usr/bin/env node
2
+
3
+ require("../dist/src/RunTranslateCSV.js")
@@ -123,7 +123,7 @@ class CmdExecutor {
123
123
  let toLangs = options.toLangs ?? undefined;
124
124
  let apiKey = options.apiKey;
125
125
  let appId = options.appId;
126
- console.log(`slim csv cmd options: `, incsv, outcsv, fromLang, toLangs);
126
+ console.log(`translate csv cmd options: `, incsv, outcsv, fromLang, toLangs, apiKey, appId);
127
127
  let argv = process.argv;
128
128
  if (incsv == null) {
129
129
  console.error(`incsv missing:`, argv);
@@ -149,7 +149,7 @@ class CmdExecutor {
149
149
  console.error(`appId missing:`, argv);
150
150
  return;
151
151
  }
152
- await CsvAutoTranslator_1.CsvAutoTranslator.translateCsvWithLangs(incsv, outcsv, fromLang, toLangs, apiKey, appId);
152
+ await CsvAutoTranslator_1.CsvAutoTranslator.translateCsvWithLangs(appId, apiKey, incsv, outcsv, fromLang, toLangs);
153
153
  console.log("translate csv with cmd options done.");
154
154
  }
155
155
  }
@@ -37,6 +37,31 @@ exports.CsvAutoTranslator = void 0;
37
37
  const crypto = __importStar(require("crypto"));
38
38
  const https = __importStar(require("https"));
39
39
  const CSVUtils_1 = require("./CSVUtils");
40
+ class OnlineTranslateResult {
41
+ errorCode = "";
42
+ errorMsg = "";
43
+ isOk = false;
44
+ translatedTexts;
45
+ constructor(errorCode, errorMsg, isOk, translatedTexts) {
46
+ this.errorCode = errorCode;
47
+ this.errorMsg = errorMsg;
48
+ this.isOk = isOk;
49
+ this.translatedTexts = translatedTexts;
50
+ }
51
+ }
52
+ class TranslateCSVResult {
53
+ translateCount = 0;
54
+ isOk = false;
55
+ firstErrorCode = "";
56
+ firstErrorMsg = "";
57
+ constructor(translateCount, isOk, firstErrorCode, firstErrorMsg) {
58
+ this.translateCount = translateCount;
59
+ this.isOk = isOk;
60
+ this.firstErrorCode = firstErrorCode;
61
+ this.firstErrorMsg = firstErrorMsg;
62
+ }
63
+ static Empty = new TranslateCSVResult(0, false, "", "");
64
+ }
40
65
  class CsvAutoTranslator {
41
66
  apiKey;
42
67
  appId;
@@ -44,7 +69,7 @@ class CsvAutoTranslator {
44
69
  maxBatchSize = 5;
45
70
  MAX_BATCH_TEXT_COUNT = 50;
46
71
  MAX_BATCH_JSON_SIZE = 4900;
47
- constructor(apiKey, appId) {
72
+ constructor(appId, apiKey) {
48
73
  this.apiKey = apiKey;
49
74
  this.appId = appId;
50
75
  }
@@ -107,9 +132,14 @@ class CsvAutoTranslator {
107
132
  async translateBatch(texts, from, to) {
108
133
  const response = await this.requestTranslate(texts, from, to);
109
134
  if (response.resultCode !== "200") {
110
- throw new Error(`翻译失败: ${response.resultMsg}`);
135
+ let errTip = `翻译失败: ${response.errorCode}, ${response.errorMsg}, ${response.resultCode}, ${response.resultMsg}`;
136
+ console.error(errTip);
137
+ console.error("翻译失败内容:", texts);
138
+ let result = new OnlineTranslateResult(response.errorCode, response.errorMsg, false, new Array(texts.length).fill(""));
139
+ return result;
111
140
  }
112
- return response.tgtList.map(item => item.tgtText || "");
141
+ let result = new OnlineTranslateResult(response.resultCode, response.resultMsg, true, response.tgtList.map(item => item.tgtText || ""));
142
+ return result;
113
143
  }
114
144
  estimateSingleTextJsonSize(text, isSingle) {
115
145
  return Buffer.byteLength(JSON.stringify(text)) + (isSingle ? 0 : 1);
@@ -159,18 +189,28 @@ class CsvAutoTranslator {
159
189
  const results = new Array(batches.length);
160
190
  const concurrencyLimit = this.maxBatchSize;
161
191
  let index = 0;
192
+ let isOk = true;
162
193
  let batchCount = 0;
194
+ let firstErrorCode = "";
195
+ let firstErrorMsg = "";
163
196
  const processBatch = async () => {
164
197
  while (index < batches.length) {
165
198
  const batchIndex = index++;
166
199
  const batch = batches[batchIndex];
167
200
  batchCount++;
168
201
  console.log(`batchCount++: ${batchCount}`);
169
- const batchResults = await this.translateBatch(batch, from, to);
202
+ const batchResult = await this.translateBatch(batch, from, to);
203
+ if (!batchResult.isOk) {
204
+ isOk = false;
205
+ if (firstErrorCode === "") {
206
+ firstErrorCode = batchResult.errorCode;
207
+ firstErrorMsg = batchResult.errorMsg;
208
+ }
209
+ }
170
210
  await new Promise(resolve => setTimeout(resolve, 1000));
171
211
  batchCount--;
172
212
  console.log(`batchCount--: ${batchCount}`);
173
- results[batchIndex] = batchResults;
213
+ results[batchIndex] = batchResult.translatedTexts;
174
214
  }
175
215
  };
176
216
  const workers = [];
@@ -179,7 +219,9 @@ class CsvAutoTranslator {
179
219
  }
180
220
  await Promise.all(workers);
181
221
  // await processBatch()
182
- return results.flat();
222
+ let translatedTexts = results.flat();
223
+ let result = new OnlineTranslateResult(firstErrorCode, firstErrorMsg, isOk, translatedTexts);
224
+ return result;
183
225
  }
184
226
  langMap = new Map([
185
227
  ["zh_cn", "zh"],
@@ -189,45 +231,27 @@ class CsvAutoTranslator {
189
231
  async translateCsvRows(rows, fromLang, toLangs) {
190
232
  if (rows.length === 0) {
191
233
  console.log("CSV文件为空");
192
- return 0;
234
+ return TranslateCSVResult.Empty;
193
235
  }
194
236
  let header = rows[0];
195
237
  let fromLangIndex = header.indexOf(fromLang);
196
238
  if (fromLangIndex === -1) {
197
239
  console.error(`未找到 ${fromLang} 列`);
198
- return 0;
240
+ return TranslateCSVResult.Empty;
199
241
  }
200
242
  if (!this.langMap.has(fromLang)) {
201
243
  console.error(`未找到 ${fromLang} 的目标语言`);
202
- return 0;
244
+ return TranslateCSVResult.Empty;
203
245
  }
204
246
  let fromLang2 = this.langMap.get(fromLang);
205
247
  if (fromLang2 == null) {
206
248
  console.error(`未找到 ${fromLang} 的目标语言`);
207
- return 0;
249
+ return TranslateCSVResult.Empty;
208
250
  }
209
- const needTranslateIndices = [];
210
- const needTranslateTexts = [];
211
- for (let i = 1; i < rows.length; i++) {
212
- const row = rows[i];
213
- if (!row[0] || row[0].trim() === "") {
214
- continue;
215
- }
216
- if (row[fromLangIndex] && row[fromLangIndex].trim() !== "") {
217
- continue;
218
- }
219
- needTranslateIndices.push(i);
220
- let text = row[fromLangIndex];
221
- if (text == null || text == "") {
222
- text = row[0];
223
- }
224
- needTranslateTexts.push(text);
225
- }
226
- if (needTranslateTexts.length === 0) {
227
- console.log("没有需要翻译的内容");
228
- return 0;
229
- }
230
- console.log(`开始翻译 ${needTranslateTexts.length} 条内容...`);
251
+ let isOk = true;
252
+ let translateCount = 0;
253
+ let firstErrorCode = "";
254
+ let firstErrorMsg = "";
231
255
  for (let curLangIndex = 0; curLangIndex < header.length; curLangIndex++) {
232
256
  let lang = header[curLangIndex];
233
257
  lang = lang.trim().toLowerCase();
@@ -237,36 +261,79 @@ class CsvAutoTranslator {
237
261
  if (toLangs != undefined && !toLangs.includes(lang)) {
238
262
  continue;
239
263
  }
240
- if (lang != fromLang && this.langMap.has(lang)) {
241
- const toLang = this.langMap.get(lang);
242
- if (toLang == null) {
243
- console.error(`未找到 ${lang} 的目标语言`);
264
+ if (!(lang != fromLang && this.langMap.has(lang))) {
265
+ continue;
266
+ }
267
+ const needTranslateIndices = [];
268
+ const needTranslateTexts = [];
269
+ for (let i = 1; i < rows.length; i++) {
270
+ const row = rows[i];
271
+ if (!row[0] || row[0].trim() === "") {
244
272
  continue;
245
273
  }
246
- const translations = await this.translateAll(needTranslateTexts, fromLang2, toLang);
247
- for (let j = 0; j < needTranslateIndices.length; j++) {
248
- const rowIndex = needTranslateIndices[j];
249
- while (rows[rowIndex].length < header.length) {
250
- rows[rowIndex].push("");
251
- }
252
- rows[rowIndex][curLangIndex] = translations[j];
274
+ if (row[curLangIndex] && row[curLangIndex].trim() !== "") {
275
+ continue;
276
+ }
277
+ let text = row[fromLangIndex];
278
+ if (text == null || text == "") {
279
+ text = row[0];
280
+ }
281
+ if (text == null || text === "") {
282
+ continue;
283
+ }
284
+ needTranslateIndices.push(i);
285
+ needTranslateTexts.push(text);
286
+ }
287
+ if (needTranslateTexts.length === 0) {
288
+ console.log("没有需要翻译的内容");
289
+ return TranslateCSVResult.Empty;
290
+ }
291
+ console.log(`开始翻译 ${needTranslateTexts.length} 条内容...`);
292
+ const toLang = this.langMap.get(lang);
293
+ if (toLang == null) {
294
+ console.error(`未找到 ${lang} 的目标语言`);
295
+ continue;
296
+ }
297
+ const translateResult = await this.translateAll(needTranslateTexts, fromLang2, toLang);
298
+ isOk = isOk && translateResult.isOk;
299
+ if (!translateResult.isOk) {
300
+ if (firstErrorCode === "") {
301
+ firstErrorCode = translateResult.errorCode;
302
+ firstErrorMsg = translateResult.errorMsg;
303
+ }
304
+ console.error(`翻译 ${lang} 失败: ${translateResult.errorCode} ${translateResult.errorMsg}`);
305
+ continue;
306
+ }
307
+ let translations = translateResult.translatedTexts;
308
+ for (let j = 0; j < needTranslateIndices.length; j++) {
309
+ const rowIndex = needTranslateIndices[j];
310
+ while (rows[rowIndex].length < header.length) {
311
+ rows[rowIndex].push("");
253
312
  }
313
+ rows[rowIndex][curLangIndex] = translations[j];
254
314
  }
315
+ translateCount += needTranslateTexts.length;
255
316
  }
256
- return needTranslateTexts.length;
317
+ let result = new TranslateCSVResult(translateCount, isOk, firstErrorCode, firstErrorMsg);
318
+ return result;
257
319
  }
258
320
  async translateCsv(filePath, outFilePath, fromLang = "auto", toLangs) {
259
321
  const csvUtils = new CSVUtils_1.CSVUtils(filePath);
260
322
  const rows = await csvUtils.parseCsv();
261
- const translatedCount = await this.translateCsvRows(rows, fromLang, toLangs);
262
- if (translatedCount > 0) {
323
+ const translatedResult = await this.translateCsvRows(rows, fromLang, toLangs);
324
+ if (translatedResult.isOk && translatedResult.translateCount > 0) {
263
325
  await CSVUtils_1.CSVUtils.writeCsv(outFilePath, rows);
264
- console.log(`翻译完成,已更新 ${translatedCount} 条内容到 ${outFilePath}`);
326
+ console.log(`翻译完成,已更新 ${translatedResult.translateCount} 条内容到 ${outFilePath}`);
265
327
  }
328
+ else {
329
+ console.log(`翻译完成,未更新任何内容到 ${outFilePath}`);
330
+ }
331
+ return translatedResult;
266
332
  }
267
- static async translateCsvWithLangs(filePath, outFilePath, fromLang, langs, apiKey, appId) {
268
- const translator = new CsvAutoTranslator(apiKey, appId);
269
- await translator.translateCsv(filePath, outFilePath, fromLang, langs);
333
+ static async translateCsvWithLangs(appId, apiKey, filePath, outFilePath, fromLang, langs) {
334
+ const translator = new CsvAutoTranslator(appId, apiKey);
335
+ let result = await translator.translateCsv(filePath, outFilePath, fromLang, langs);
336
+ return result;
270
337
  }
271
338
  }
272
339
  exports.CsvAutoTranslator = CsvAutoTranslator;
@@ -27,7 +27,7 @@ describe("CsvAutoTranslator", () => {
27
27
  expect(true).toBe(true);
28
28
  return;
29
29
  }
30
- const translator = new CsvAutoTranslator_1.CsvAutoTranslator(apiKey, appId);
30
+ const translator = new CsvAutoTranslator_1.CsvAutoTranslator(appId, apiKey);
31
31
  const rows = JSON.parse(JSON.stringify(testRows));
32
32
  const translatedCount = await translator.translateCsvRows(rows, "zh_cn");
33
33
  expect(translatedCount).toBe(10);
@@ -45,7 +45,7 @@ describe("CsvAutoTranslator", () => {
45
45
  expect(true).toBe(true);
46
46
  return;
47
47
  }
48
- const translator = new CsvAutoTranslator_1.CsvAutoTranslator(apiKey, appId);
48
+ const translator = new CsvAutoTranslator_1.CsvAutoTranslator(appId, apiKey);
49
49
  const rows = JSON.parse(JSON.stringify(testRows));
50
50
  rows[1][2] = "已有译文";
51
51
  const translatedCount = await translator.translateCsvRows(rows, "zh_cn");
@@ -53,12 +53,12 @@ describe("CsvAutoTranslator", () => {
53
53
  expect(rows[10][2]).toBe("歡迎來到中國");
54
54
  }, 120000);
55
55
  test("translateCsvRows - 空CSV测试", async () => {
56
- const translator = new CsvAutoTranslator_1.CsvAutoTranslator("dummy-key", "dummy-id");
56
+ const translator = new CsvAutoTranslator_1.CsvAutoTranslator(NIUTRANS_APP_ID, NIUTRANS_API_KEY);
57
57
  const translatedCount = await translator.translateCsvRows([], "zh_cn");
58
58
  expect(translatedCount).toBe(0);
59
59
  });
60
60
  test("translateCsvRows - 没有需要翻译的内容", async () => {
61
- const translator = new CsvAutoTranslator_1.CsvAutoTranslator("dummy-key", "dummy-id");
61
+ const translator = new CsvAutoTranslator_1.CsvAutoTranslator(NIUTRANS_APP_ID, NIUTRANS_API_KEY);
62
62
  const rows = [
63
63
  ["key", "en_us", "zh_hk"],
64
64
  ["Hello", "", "已有译文1"],
@@ -68,7 +68,7 @@ describe("CsvAutoTranslator", () => {
68
68
  expect(translatedCount).toBe(0);
69
69
  });
70
70
  describe("smartBatch - 边界回归测试", () => {
71
- const translator = new CsvAutoTranslator_1.CsvAutoTranslator("dummy-key", "dummy-id");
71
+ const translator = new CsvAutoTranslator_1.CsvAutoTranslator(NIUTRANS_APP_ID, NIUTRANS_API_KEY);
72
72
  test("空文本数组", () => {
73
73
  const batches = translator.smartBatch([]);
74
74
  expect(batches.length).toBe(0);
@@ -157,4 +157,14 @@ describe("CsvAutoTranslator", () => {
157
157
  }
158
158
  }, 300000);
159
159
  });
160
+ describe("translateCSV", () => {
161
+ test("翻译 Auto.csv 并验证输出", async () => {
162
+ const incsv = "test/Auto.csv";
163
+ const outcsv = "temp/Auto-Out.csv";
164
+ const fromLang = "zh_cn";
165
+ let result = await CsvAutoTranslator_1.CsvAutoTranslator.translateCsvWithLangs("kDr1772519780125", "4cfb5525d3be1e45003910059cd7ea9b", incsv, outcsv, fromLang);
166
+ expect(result.isOk).toBe(true);
167
+ expect(result.translateCount).toBeGreaterThan(0);
168
+ });
169
+ });
160
170
  });
@@ -1,73 +1,73 @@
1
- 对文件进行TypeScript代码重构,重点优化字符串匹配方法设计,将原有的整句C#语句捕获逻辑精确调整为仅捕获字符串值表达式部分,通过90%的测试用例。开发过程中必须严格遵循以下规范:采用原生TypeScript开发方式,严禁使用反编译JavaScript的方法;禁止使用`git show`等需要用户手动确认的操作;所有中间生成的临时文件仅在最终结束时,以清单形式提示用户需要清理的文件列表,不得自动执行清理操作。不要使用 `&lt;`、 `&amp;`、`&gt;` 等html代码中才有的符号,严格使用 typescript 语言语法, 不使用 `arguments` 等保留字段作为局部变量名。
2
- 其中CodeSnippet类成员定义如下:
3
-
4
- ```typescript
5
- export class CodeSnippet {
6
- /**
7
- * 要替换的原始内容在代码全文中的起始位置,从0开始
8
- */
9
- originalIndex: number;
10
- /**
11
- * 从originalIndex开始长度至少30个字符的原始代码文本,如果从originalIndex开始后续全文中包含`;`符号,那么originalContext必须包含一个`;`号
12
- */
13
- originalContext: string;
14
- /**
15
- * 标记是否改变了原始代码内容,如果需要替换文件内容则为true,否则为false; 具体定义为: isChanged = originalCode!=convertedCode
16
- */
17
- isChanged: boolean;
18
- /**
19
- * 要替换的C#语句整句原始内容
20
- */
21
- originalCode: string;
22
- /**
23
- * originalCode转换后的代码
24
- */
25
- convertedCode: string;
26
- /**
27
- * 匹配出的所有字符串列表, 包括转换出来的字符串模板,同一个位置匹配出的字符串会合并到一个元素中
28
- */
29
- literals: string[];
30
- /**
31
- * 无法识别的疑似字符串列表
32
- */
33
- unexpects: string[];
34
- }
35
- ```
36
-
37
-
38
- 代码实现需满足以下具体技术要求:
39
- 1. 重构originalCode的解析逻辑,实现精确的字符串操作表达式定位与提取,严格排除非字符串表达式的代码部分
40
- 2. 基于调整后的originalCode提取逻辑,同步更新convertedCode的转换逻辑和originalIndex的位置映射关系,确保代码转换的准确性和位置对应关系
41
- 3. 完整实现CSharpStringExtractor类,该类必须包含名为`extractStrings`的公共成员方法。此方法接收C#代码字符串作为输入参数,提取其中的字符串文本转换信息,并以CodeSnippet[]数组形式返回处理结果
42
-
43
- CodeSnippet数据结构必须严格符合以下规范:
44
- - CodeSnippet.originalCode:存储从C#代码中提取的完整字符串值表达式文本,确保不包含多余的代码内容
45
- - CodeSnippet.convertedCode:存储按照"规则A"转换后的字符串值表达式文本,保持语法正确性
46
- - CodeSnippet.literals:存储从字符串值表达式中提取的所有字符串文本组成的字符串数组,确保包含所有嵌套和连接的字符串片段,包括转义字符和特殊符号
47
-
48
- "规则A"转换逻辑的详细实现规范:
49
- 1. 对于赋值表达式中形如`xxx.text = 字符串值表达式`的模式:
50
- - 非内插字符串(不以$@或$开头):在原始字符串值表达式末尾添加`.TR()`方法调用,保持原有字符串内容不变
51
- - 内插字符串(以$@或$开头):将整个字符串表达式转换为`Tr.Format(...)`格式,保持原有占位符结构和表达式逻辑
52
- 2. 对于使用`+`运算符连接的字符串表达式:
53
- - 对每个独立的字符串子表达式应用与规则1相同的转换处理,保持原有的连接结构和运算顺序
54
- 3. 其他所有不符合上述模式的情况:
55
- - CodeSnippet.convertedCode字段保持原始字符串值表达式不变
56
- - CodeSnippet.literals字段仍需准确提取所有字符串文本内容,包括转义字符和特殊符号
57
- 4. string.Format(...) 形式包含的字符串表达式, 需要特殊处理, 连带 `string.Format()` 一起捕获存入 originalCode 成员
58
- 5. 需要捕获类对象成员初始化赋值表达式中, 出现在赋值操作符`=`右侧的字符串值表达式
59
- 6. 需要能捕获 switch 语句中包含普通字符串的情况
60
- 7. 需要正确识别字符串包含各种特殊符号和转译符号组合的情况, 能正确识别这种情况下的字符串边界
61
- 8. 函数参数需要拆分, 逐个参数捕获
62
- 9. string.Format(...) 形式包含的字符串表达式, 需要特殊处理, 连带 `string.Format()` 一起捕获存入 originalCode 成员
63
-
64
-
65
-
66
- 实现过程中必须特别注意以下技术要点:
67
- - 实现精确的字符串边界识别算法,正确处理单引号和双引号字符串的开始与结束位置
68
- - 设计转义字符处理机制,妥善处理字符串中的转义字符(如\"、\'、\\等),避免错误识别字符串边界
69
- - 开发代码结构分析逻辑,正确区分字符串内的代码内容与字符串外的代码结构
70
- - 确保提取的字符串文本的准确性和完整性,包括多行字符串、复杂嵌套结构和特殊编码字符
71
- - 全面处理各种边缘情况,如空字符串、仅包含空格的字符串、包含特殊字符的字符串、零长度字符串等
72
-
1
+ 对文件进行TypeScript代码重构,重点优化字符串匹配方法设计,将原有的整句C#语句捕获逻辑精确调整为仅捕获字符串值表达式部分,通过90%的测试用例。开发过程中必须严格遵循以下规范:采用原生TypeScript开发方式,严禁使用反编译JavaScript的方法;禁止使用`git show`等需要用户手动确认的操作;所有中间生成的临时文件仅在最终结束时,以清单形式提示用户需要清理的文件列表,不得自动执行清理操作。不要使用 `&lt;`、 `&amp;`、`&gt;` 等html代码中才有的符号,严格使用 typescript 语言语法, 不使用 `arguments` 等保留字段作为局部变量名。
2
+ 其中CodeSnippet类成员定义如下:
3
+
4
+ ```typescript
5
+ export class CodeSnippet {
6
+ /**
7
+ * 要替换的原始内容在代码全文中的起始位置,从0开始
8
+ */
9
+ originalIndex: number;
10
+ /**
11
+ * 从originalIndex开始长度至少30个字符的原始代码文本,如果从originalIndex开始后续全文中包含`;`符号,那么originalContext必须包含一个`;`号
12
+ */
13
+ originalContext: string;
14
+ /**
15
+ * 标记是否改变了原始代码内容,如果需要替换文件内容则为true,否则为false; 具体定义为: isChanged = originalCode!=convertedCode
16
+ */
17
+ isChanged: boolean;
18
+ /**
19
+ * 要替换的C#语句整句原始内容
20
+ */
21
+ originalCode: string;
22
+ /**
23
+ * originalCode转换后的代码
24
+ */
25
+ convertedCode: string;
26
+ /**
27
+ * 匹配出的所有字符串列表, 包括转换出来的字符串模板,同一个位置匹配出的字符串会合并到一个元素中
28
+ */
29
+ literals: string[];
30
+ /**
31
+ * 无法识别的疑似字符串列表
32
+ */
33
+ unexpects: string[];
34
+ }
35
+ ```
36
+
37
+
38
+ 代码实现需满足以下具体技术要求:
39
+ 1. 重构originalCode的解析逻辑,实现精确的字符串操作表达式定位与提取,严格排除非字符串表达式的代码部分
40
+ 2. 基于调整后的originalCode提取逻辑,同步更新convertedCode的转换逻辑和originalIndex的位置映射关系,确保代码转换的准确性和位置对应关系
41
+ 3. 完整实现CSharpStringExtractor类,该类必须包含名为`extractStrings`的公共成员方法。此方法接收C#代码字符串作为输入参数,提取其中的字符串文本转换信息,并以CodeSnippet[]数组形式返回处理结果
42
+
43
+ CodeSnippet数据结构必须严格符合以下规范:
44
+ - CodeSnippet.originalCode:存储从C#代码中提取的完整字符串值表达式文本,确保不包含多余的代码内容
45
+ - CodeSnippet.convertedCode:存储按照"规则A"转换后的字符串值表达式文本,保持语法正确性
46
+ - CodeSnippet.literals:存储从字符串值表达式中提取的所有字符串文本组成的字符串数组,确保包含所有嵌套和连接的字符串片段,包括转义字符和特殊符号
47
+
48
+ "规则A"转换逻辑的详细实现规范:
49
+ 1. 对于赋值表达式中形如`xxx.text = 字符串值表达式`的模式:
50
+ - 非内插字符串(不以$@或$开头):在原始字符串值表达式末尾添加`.TR()`方法调用,保持原有字符串内容不变
51
+ - 内插字符串(以$@或$开头):将整个字符串表达式转换为`Tr.Format(...)`格式,保持原有占位符结构和表达式逻辑
52
+ 2. 对于使用`+`运算符连接的字符串表达式:
53
+ - 对每个独立的字符串子表达式应用与规则1相同的转换处理,保持原有的连接结构和运算顺序
54
+ 3. 其他所有不符合上述模式的情况:
55
+ - CodeSnippet.convertedCode字段保持原始字符串值表达式不变
56
+ - CodeSnippet.literals字段仍需准确提取所有字符串文本内容,包括转义字符和特殊符号
57
+ 4. string.Format(...) 形式包含的字符串表达式, 需要特殊处理, 连带 `string.Format()` 一起捕获存入 originalCode 成员
58
+ 5. 需要捕获类对象成员初始化赋值表达式中, 出现在赋值操作符`=`右侧的字符串值表达式
59
+ 6. 需要能捕获 switch 语句中包含普通字符串的情况
60
+ 7. 需要正确识别字符串包含各种特殊符号和转译符号组合的情况, 能正确识别这种情况下的字符串边界
61
+ 8. 函数参数需要拆分, 逐个参数捕获
62
+ 9. string.Format(...) 形式包含的字符串表达式, 需要特殊处理, 连带 `string.Format()` 一起捕获存入 originalCode 成员
63
+
64
+
65
+
66
+ 实现过程中必须特别注意以下技术要点:
67
+ - 实现精确的字符串边界识别算法,正确处理单引号和双引号字符串的开始与结束位置
68
+ - 设计转义字符处理机制,妥善处理字符串中的转义字符(如\"、\'、\\等),避免错误识别字符串边界
69
+ - 开发代码结构分析逻辑,正确区分字符串内的代码内容与字符串外的代码结构
70
+ - 确保提取的字符串文本的准确性和完整性,包括多行字符串、复杂嵌套结构和特殊编码字符
71
+ - 全面处理各种边缘情况,如空字符串、仅包含空格的字符串、包含特殊字符的字符串、零长度字符串等
72
+
73
73
  完成实现后,需进行全面测试验证:确保代码通过90%以上的测试用例,对未通过的测试用例进行针对性分析和优化,提供详细的测试报告和优化方案。
package/jest.config.js CHANGED
@@ -1,9 +1,9 @@
1
- module.exports = {
2
- preset: 'ts-jest',
3
- testEnvironment: 'node',
4
- testPathIgnorePatterns: ['/node_modules/', '/dist/'],
5
- moduleFileExtensions: ['ts', 'js'],
6
- transform: {
7
- '^.+\.ts$': 'ts-jest'
8
- }
9
- };
1
+ module.exports = {
2
+ preset: 'ts-jest',
3
+ testEnvironment: 'node',
4
+ testPathIgnorePatterns: ['/node_modules/', '/dist/'],
5
+ moduleFileExtensions: ['ts', 'js'],
6
+ transform: {
7
+ '^.+\.ts$': 'ts-jest'
8
+ }
9
+ };
package/package.json CHANGED
@@ -1,15 +1,17 @@
1
1
  {
2
2
  "name": "scancscode",
3
- "version": "1.0.42",
3
+ "version": "1.0.46",
4
4
  "description": "",
5
5
  "main": "./dist/index.js",
6
6
  "bin": {
7
7
  "scanliterals": "bin/scanliterals.js",
8
- "slimlangs": "bin/slimlangs.js"
8
+ "slimlangs": "bin/slimlangs.js",
9
+ "translatecsvfile": "bin/translatecsvfile.js"
9
10
  },
10
11
  "scripts": {
11
12
  "scanliterals":"node bin/scanliterals.js",
12
13
  "slimlangs":"node bin/slimlangs.js",
14
+ "translatecsvfile": "node bin/translatecsvfile.js",
13
15
  "test": "jest"
14
16
  },
15
17
  "keywords": [],
@@ -124,7 +124,7 @@ export class CmdExecutor {
124
124
  let apiKey: string = options.apiKey;
125
125
  let appId: string = options.appId;
126
126
 
127
- console.log(`slim csv cmd options: `, incsv, outcsv, fromLang, toLangs);
127
+ console.log(`translate csv cmd options: `, incsv, outcsv, fromLang, toLangs, apiKey, appId);
128
128
  let argv = process.argv;
129
129
  if (incsv == null) {
130
130
  console.error(`incsv missing:`, argv);
@@ -149,8 +149,8 @@ export class CmdExecutor {
149
149
  if (appId == null) {
150
150
  console.error(`appId missing:`, argv);
151
151
  return;
152
- }
153
- await CsvAutoTranslator.translateCsvWithLangs(incsv, outcsv, fromLang, toLangs, apiKey, appId);
152
+ }
153
+ await CsvAutoTranslator.translateCsvWithLangs(appId, apiKey, incsv, outcsv, fromLang, toLangs);
154
154
  console.log("translate csv with cmd options done.");
155
155
  }
156
156
  }