scancscode 1.0.56 → 1.0.58

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,7 @@
1
+ # 验证清单
2
+
3
+ - [ ] 测试用例 "should handle assignment expression within if-else-end" 通过
4
+ - [ ] originalIndex 正确指向值表达式位置 (95),而不是整个语句
5
+ - [ ] originalCode 只包含值表达式部分,不包含赋值左侧
6
+ - [ ] convertedCode 正确添加 .TR() 后缀
7
+ - [ ] 完整测试套件无回归
@@ -0,0 +1,41 @@
1
+ # 修复 if-else 语句无花括号时的值表达式提取 Spec
2
+
3
+ ## 背景
4
+ 当 if 或 else 语句没有花括号包围时(如单行 if/else),当前代码提取的是整个赋值语句,而应该只提取赋值右侧的值表达式。
5
+
6
+ ## 测试用例分析
7
+
8
+ ### 输入代码
9
+ ```csharp
10
+ if (_eventIdx < _table.MainDescription.Length)
11
+ m_text_desc.text = _table.MainDescription[_eventIdx];
12
+ else
13
+ m_text_desc.text = _table.MainDescription[0];
14
+ ```
15
+
16
+ ### 期望输出
17
+ - 第一个 snippet:
18
+ - `originalIndex`: `_table.MainDescription[_eventIdx]` 的位置 (95)
19
+ - `originalCode`: `_table.MainDescription[_eventIdx]`
20
+ - `convertedCode`: `_table.MainDescription[_eventIdx].TR()`
21
+ - 第二个 snippet:
22
+ - `originalIndex`: `_table.MainDescription[0]` 的位置
23
+ - `originalCode`: `_table.MainDescription[0]`
24
+ - `convertedCode`: `_table.MainDescription[0].TR()`
25
+
26
+ ## 修改内容
27
+
28
+ ### 问题定位
29
+ 在 `CSharpStringExtractor.ts` 中,`extractValueExpression` 或相关方法处理 if-else 语句时:
30
+ - 当前:提取整个赋值语句 `m_text_desc.text = _table.MainDescription[_eventIdx]`
31
+ - 应该:只提取赋值右侧的值表达式 `_table.MainDescription[_eventIdx]`
32
+
33
+ ### 修复策略
34
+ 1. 识别 if/else 后没有花括号的单行语句
35
+ 2. 跳过赋值左侧部分(`m_text_desc.text = `)
36
+ 3. 只提取赋值右侧的表达式
37
+ 4. 对提取的表达式应用 `.TR()` 转换
38
+
39
+ ## 影响范围
40
+ - `CSharpStringExtractor.ts` 中的值提取逻辑
41
+ - 特别是在 `extractValueExpression` 或 `processStatementAndExtractValue` 方法中
@@ -0,0 +1,14 @@
1
+ # 任务列表
2
+
3
+ ## 任务 1: 分析代码定位问题
4
+ - [ ] 1.1 分析 CSharpStringExtractor.ts 中处理 if-else 单行语句的代码逻辑
5
+ - [ ] 1.2 定位需要修改的具体方法和代码位置
6
+
7
+ ## 任务 2: 实现修复
8
+ - [ ] 2.1 修改 if-else 单行语句的值表达式提取逻辑
9
+ - [ ] 2.2 确保只提取赋值右侧的表达式
10
+ - [ ] 2.3 确保添加 .TR() 转换
11
+
12
+ ## 任务 3: 验证测试
13
+ - [ ] 3.1 运行目标测试用例验证
14
+ - [ ] 3.2 运行完整测试套件确保无回归
package/README.md ADDED
@@ -0,0 +1,194 @@
1
+ # scancscode
2
+
3
+ C# 代码字符串提取与国际化处理工具集。
4
+
5
+ ## 功能简介
6
+
7
+ scancscode 是一个用于处理 C# 代码国际化的命令行工具集,主要功能包括:
8
+
9
+ - **扫描代码字符串**:从 C# 代码中自动提取需要国际化的字符串字面量, 生成 CSV 文件
10
+ - **CSV 文件处理**:合并、精简 CSV 语言文件
11
+ - **自动翻译**:调用翻译 API 批量翻译 CSV 文件中的内容
12
+
13
+ ## 安装
14
+
15
+ ```bash
16
+ # 克隆项目
17
+ git clone <repository-url>
18
+ cd scancscode
19
+
20
+ # 安装依赖
21
+ npm install
22
+
23
+ # 构建项目
24
+ npm run build
25
+ ```
26
+
27
+ ## 命令行工具
28
+
29
+ ### 1. scanliterals - 扫描代码字符串
30
+
31
+ 扫描 C# 代码中的字符串字面量,并输出到 CSV 文件。
32
+
33
+ ```bash
34
+ npx scanliterals --cscodedir <目录> --configdir <目录> --outcsv <文件> --langs <语言>
35
+ ```
36
+
37
+ **参数说明**:
38
+
39
+ | 参数 | 说明 |
40
+ | ------------- | --------------- |
41
+ | `--cscodedir` | C# 代码目录,支持多个目录 |
42
+ | `--configdir` | 配置文件目录,支持多个目录 |
43
+ | `--outcsv` | 输出 CSV 文件路径 |
44
+ | `--langs` | 语言列表(默认 zh\_cn) |
45
+ | `--verbose` | 显示详细日志 |
46
+
47
+ **示例**:
48
+
49
+ ```bash
50
+ npx scanliterals \
51
+ --cscodedir ./GameClient/Assets/Bundles/FGUI/ \
52
+ --configdir ./GameClient/Assets/Bundles/GameConfigs/ \
53
+ --outcsv ./output/Translation.csv \
54
+ --langs zh_cn
55
+ ```
56
+
57
+ ### 2. slimlangs - 精简 CSV 文件列
58
+
59
+ 将多个 CSV 文件合并,并按指定语言列表精简列。
60
+
61
+ ```bash
62
+ npx slimlangs --incsv <文件> --outcsv <文件> --langs <语言>
63
+ ```
64
+
65
+ **参数说明**:
66
+
67
+ | 参数 | 说明 |
68
+ | ---------- | ------------------ |
69
+ | `--incsv` | 输入 CSV 文件路径,支持多个文件 |
70
+ | `--outcsv` | 输出 CSV 文件路径 |
71
+ | `--langs` | 要保留的语言列表 |
72
+
73
+ **示例**:
74
+
75
+ ```bash
76
+ npx slimlangs \
77
+ --incsv ./Auto.csv ./Manual.csv \
78
+ --outcsv ./ScriptTrans.csv \
79
+ --langs zh_cn
80
+ ```
81
+
82
+ ### 3. transcsv - 翻译 CSV 文件
83
+
84
+ 使用翻译 API 将 CSV 文件中的内容从源语言翻译到目标语言。
85
+
86
+ - 暂时仅支持小牛翻译API: <https://niutrans.com/documents/contents/transapi_batch_v2#accessMode>
87
+
88
+ ```bash
89
+ npx transcsv --incsv <文件> --outcsv <文件> --fromLang <语言> --toLangs <语言> --apiKey <密钥> --appId <应用ID>
90
+ ```
91
+
92
+ **参数说明**:
93
+
94
+ | 参数 | 说明 |
95
+ | ------------ | ------------- |
96
+ | `--incsv` | 输入 CSV 文件路径 |
97
+ | `--outcsv` | 输出 CSV 文件路径 |
98
+ | `--fromLang` | 源语言(如 zh\_cn) |
99
+ | `--toLangs` | 目标语言列表 |
100
+ | `--apiKey` | 翻译 API 密钥 |
101
+ | `--appId` | 翻译 API 应用 ID |
102
+
103
+ **示例**:
104
+
105
+ ```bash
106
+ npx transcsv \
107
+ --incsv ./ScriptTrans.csv \
108
+ --outcsv ./ScriptTrans_translated.csv \
109
+ --fromLang zh_cn \
110
+ --toLangs zh_hk en_us \
111
+ --apiKey your_api_key \
112
+ --appId your_app_id
113
+ ```
114
+
115
+ ## 核心模块
116
+
117
+ ### CSharpStringExtractor
118
+
119
+ C# 代码字符串提取器,支持多种字符串格式:
120
+
121
+ - 普通字符串:`"Hello World"`
122
+ - 插值字符串:`$"Hello {name}"`
123
+ - 原生插值字符串:`$@"Hello {name}"`
124
+ - string.Format 调用:`string.Format("Hello {0}", name)`
125
+ - Tr.Format 调用:`Tr.Format("Hello {0}", name)`
126
+ - .text/.title 赋值:`label.text = "Hello"`
127
+ - 三元表达式:`condition ? "Yes" : "No"`
128
+ - 字符串拼接:`"Hello " + name`
129
+ - switch case:`case "value":`
130
+
131
+ ### CsvAutoTranslator
132
+
133
+ CSV 文件自动翻译器,基于百度翻译 API 实现批量翻译功能。
134
+
135
+ ### CSVUtils
136
+
137
+ CSV 文件处理工具集,提供 CSV 文件的读取、写入、合并和精简功能。
138
+
139
+ ## 工作流示例
140
+
141
+ ### 完整国际化处理流程
142
+
143
+ ```bash
144
+ # 步骤 1: 扫描代码中的字符串字面量
145
+ npx scanliterals \
146
+ --cscodedir ./GameClient/Assets/Bundles/FGUI/ \
147
+ --configdir ./GameClient/Assets/Bundles/GameConfigs/ \
148
+ --outcsv ./Translation/Auto.csv \
149
+ --langs zh_cn
150
+
151
+ # 步骤 2: 合并并精简 CSV(可选)
152
+ npx slimlangs \
153
+ --incsv ./Translation/Auto.csv ./Translation/Manual.csv \
154
+ --outcsv ./Translation/ScriptTrans.csv \
155
+ --langs zh_cn
156
+
157
+ # 步骤 3: 翻译到目标语言
158
+ npx transcsv \
159
+ --incsv ./Translation/ScriptTrans.csv \
160
+ --outcsv ./Translation/ScriptTrans.csv \
161
+ --fromLang zh_cn \
162
+ --toLangs zh_hk \
163
+ --apiKey your_api_key \
164
+ --appId your_app_id
165
+ ```
166
+
167
+ ## 开发
168
+
169
+ ### 运行测试
170
+
171
+ ```bash
172
+ npm test
173
+ ```
174
+
175
+ ### 构建项目
176
+
177
+ ```bash
178
+ npm run build
179
+ ```
180
+
181
+ ## 语言代码参考
182
+
183
+ | 语言 | 代码 |
184
+ | -------- | ------ |
185
+ | 简体中文 | zh\_cn |
186
+ | 繁体中文(香港) | zh\_hk |
187
+ | 繁体中文(台湾) | zh\_tw |
188
+ | 英语 | en\_us |
189
+ | 日语 | ja\_jp |
190
+ | 韩语 | ko\_kr |
191
+
192
+ ## 许可证
193
+
194
+ ISC
@@ -65,14 +65,15 @@ class CSVUtils {
65
65
  return content;
66
66
  }
67
67
  tryGetEntry(content, map01, map02) {
68
- if (map02.has(content)) {
69
- let row2 = map02.get(content);
70
- return [row2 != null, content, row2];
68
+ let key = content.replaceAll('\r\n', '\n');
69
+ if (map02.has(key)) {
70
+ let row2 = map02.get(key);
71
+ return [row2 != null, key, row2];
71
72
  }
72
- let shortKey = this.getShortKey(content);
73
+ let shortKey = this.getShortKey(key);
73
74
  if (map01.has(shortKey)) {
74
75
  let row1 = map01.get(shortKey);
75
- if (row1 != null && (row1[0] == shortKey || row1[1] == content)) {
76
+ if (row1 != null && (row1[0] == shortKey || row1[1] == key)) {
76
77
  return [true, shortKey, row1];
77
78
  }
78
79
  else {
@@ -82,6 +83,7 @@ class CSVUtils {
82
83
  return [false, shortKey, undefined];
83
84
  }
84
85
  merge(rows0, rows1, langs) {
86
+ let indexsxse = rows1.findIndex(x => x.includes("完成每日的显圣任务,获得经验值提升显圣等级"));
85
87
  // 短key
86
88
  let map01 = new Map();
87
89
  // 全文key
@@ -21,7 +21,7 @@ class CmdExecutor {
21
21
  // "E:/DATA/Projects/ZhiYou/DXTSProject/Client/Assets/Scripts/",
22
22
  ];
23
23
  let gameConfigFolders = [cwd + "Assets/Bundles/GameConfigs/"];
24
- let outCsvFile = "E:/DATA/Projects/e-gbl-client/client/Assets/Bundles/GameConfigs/Translation/hello.csv";
24
+ let outCsvFile = "test/Auto.csv";
25
25
  let langs = ["zh_cn"];
26
26
  let literalCollector = new LiteralCollector_1.LiteralCollector();
27
27
  // return literalCollector.convert(cscodeFolders, gameConfigFolders, outCsvFile, langs,undefined,false,true)
@@ -37,6 +37,7 @@ 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
+ const LanguageMap_1 = require("./LanguageMap");
40
41
  class OnlineTranslateResult {
41
42
  errorCode = "";
42
43
  errorMsg = "";
@@ -223,11 +224,6 @@ class CsvAutoTranslator {
223
224
  let result = new OnlineTranslateResult(firstErrorCode, firstErrorMsg, isOk, translatedTexts);
224
225
  return result;
225
226
  }
226
- langMap = new Map([
227
- ["zh_cn", "zh"],
228
- ["zh_hk", "cht"],
229
- ["en_us", "en"],
230
- ]);
231
227
  async translateCsvRows(rows, fromLang, toLangs) {
232
228
  if (rows.length === 0) {
233
229
  console.log("CSV文件为空");
@@ -239,11 +235,11 @@ class CsvAutoTranslator {
239
235
  console.error(`未找到 ${fromLang} 列`);
240
236
  return TranslateCSVResult.Empty;
241
237
  }
242
- if (!this.langMap.has(fromLang)) {
238
+ if (!LanguageMap_1.languageMap.has(fromLang)) {
243
239
  console.error(`未找到 ${fromLang} 的目标语言`);
244
240
  return TranslateCSVResult.Empty;
245
241
  }
246
- let fromLang2 = this.langMap.get(fromLang);
242
+ let fromLang2 = LanguageMap_1.languageMap.get(fromLang);
247
243
  if (fromLang2 == null) {
248
244
  console.error(`未找到 ${fromLang} 的目标语言`);
249
245
  return TranslateCSVResult.Empty;
@@ -261,7 +257,7 @@ class CsvAutoTranslator {
261
257
  if (toLangs != undefined && !toLangs.includes(lang)) {
262
258
  continue;
263
259
  }
264
- if (!(lang != fromLang && this.langMap.has(lang))) {
260
+ if (!(lang != fromLang && LanguageMap_1.languageMap.has(lang))) {
265
261
  continue;
266
262
  }
267
263
  const needTranslateIndices = [];
@@ -289,7 +285,7 @@ class CsvAutoTranslator {
289
285
  return TranslateCSVResult.Empty;
290
286
  }
291
287
  console.log(`开始翻译 ${needTranslateTexts.length} 条内容...`);
292
- const toLang = this.langMap.get(lang);
288
+ const toLang = LanguageMap_1.languageMap.get(lang);
293
289
  if (toLang == null) {
294
290
  console.error(`未找到 ${lang} 的目标语言`);
295
291
  continue;
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.languageMap = void 0;
4
+ exports.languageMap = new Map([
5
+ // 中文相关
6
+ ["zh_cn", "zh"], // 简体中文
7
+ ["zh_hk", "cht"], // 香港繁体中文
8
+ ["zh_tw", "cht"], // 台湾繁体中文
9
+ // 英语相关
10
+ ["en_us", "en"], // 美式英语
11
+ ["en_gb", "en"], // 英式英语
12
+ ["en_au", "en"], // 澳大利亚英语
13
+ ["en_ca", "en"], // 加拿大英语
14
+ ["en_nz", "en"], // 新西兰英语
15
+ // 日语
16
+ ["ja_jp", "ja"], // 日语
17
+ // 韩语
18
+ ["ko_kr", "ko"], // 韩语
19
+ // 法语
20
+ ["fr_fr", "fr"], // 法语
21
+ ["fr_ca", "fr"], // 加拿大法语
22
+ // 德语
23
+ ["de_de", "de"], // 德语
24
+ // 西班牙语
25
+ ["es_es", "es"], // 西班牙语
26
+ ["es_mx", "es"], // 墨西哥西班牙语
27
+ // 俄语
28
+ ["ru_ru", "ru"], // 俄语
29
+ // 葡萄牙语
30
+ ["pt_pt", "pt"], // 葡萄牙语
31
+ ["pt_br", "pt"], // 巴西葡萄牙语
32
+ // 意大利语
33
+ ["it_it", "it"], // 意大利语
34
+ // 阿拉伯语
35
+ ["ar_sa", "ar"], // 阿拉伯语
36
+ // 荷兰语
37
+ ["nl_nl", "nl"], // 荷兰语
38
+ // 瑞典语
39
+ ["sv_se", "sv"], // 瑞典语
40
+ // 丹麦语
41
+ ["da_dk", "da"], // 丹麦语
42
+ // 挪威语
43
+ ["no_no", "no"], // 挪威语
44
+ ["nb_no", "no"], // 挪威语(博克马尔语)
45
+ // 芬兰语
46
+ ["fi_fi", "fi"], // 芬兰语
47
+ // 波兰语
48
+ ["pl_pl", "pl"], // 波兰语
49
+ // 匈牙利语
50
+ ["hu_hu", "hu"], // 匈牙利语
51
+ // 捷克语
52
+ ["cs_cz", "cs"], // 捷克语
53
+ // 斯洛伐克语
54
+ ["sk_sk", "sk"], // 斯洛伐克语
55
+ // 罗马尼亚语
56
+ ["ro_ro", "ro"], // 罗马尼亚语
57
+ // 希腊语
58
+ ["el_gr", "el"], // 希腊语
59
+ // 土耳其语
60
+ ["tr_tr", "tr"], // 土耳其语
61
+ // 希伯来语
62
+ ["he_il", "he"], // 希伯来语
63
+ // 印地语
64
+ ["hi_in", "hi"], // 印地语
65
+ // 泰语
66
+ ["th_th", "th"], // 泰语
67
+ // 越南语
68
+ ["vi_vn", "vi"], // 越南语
69
+ // 印尼语
70
+ ["id_id", "id"], // 印尼语
71
+ // 马来语
72
+ ["ms_my", "ms"], // 马来语
73
+ // 菲律宾语
74
+ ["tl_ph", "tl"], // 菲律宾语
75
+ // 乌克兰语
76
+ ["uk_ua", "uk"], // 乌克兰语
77
+ // 保加利亚语
78
+ ["bg_bg", "bg"], // 保加利亚语
79
+ // 克罗地亚语
80
+ ["hr_hr", "hr"], // 克罗地亚语
81
+ // 塞尔维亚语
82
+ ["sr_rs", "sr"], // 塞尔维亚语
83
+ // 斯洛文尼亚语
84
+ ["sl_si", "sl"], // 斯洛文尼亚语
85
+ // 爱沙尼亚语
86
+ ["et_ee", "et"], // 爱沙尼亚语
87
+ // 拉脱维亚语
88
+ ["lv_lv", "lv"], // 拉脱维亚语
89
+ // 立陶宛语
90
+ ["lt_lt", "lt"], // 立陶宛语
91
+ // 波斯语
92
+ ["fa_ir", "fa"], // 波斯语
93
+ // 乌尔都语
94
+ ["ur_pk", "ur"], // 乌尔都语
95
+ // 孟加拉语
96
+ ["bn_in", "bn"], // 孟加拉语
97
+ // 泰米尔语
98
+ ["ta_in", "ta"], // 泰米尔语
99
+ // 泰卢固语
100
+ ["te_in", "te"], // 泰卢固语
101
+ // 卡纳达语
102
+ ["kn_in", "kn"], // 卡纳达语
103
+ // 马拉雅拉姆语
104
+ ["ml_in", "ml"], // 马拉雅拉姆语
105
+ // 古吉拉特语
106
+ ["gu_in", "gu"], // 古吉拉特语
107
+ // 马拉地语
108
+ ["mr_in", "mr"], // 马拉地语
109
+ // 奥里亚语
110
+ ["or_in", "or"], // 奥里亚语
111
+ // 阿萨姆语
112
+ ["as_in", "as"] // 阿萨姆语
113
+ ]);