scancscode 1.0.28 → 1.0.29
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/CSCodeScanner.js +5 -4
- package/dist/LiteralCollector.js +2 -2
- package/jest.config.js +9 -0
- package/package.json +6 -3
- package/src/CSCodeScanner.ts +174 -150
- package/src/CSVUtils.ts +5 -1
- package/src/CSharpStringExtractor.ts +1069 -0
- package/src/CmdExecutor.ts +9 -1
- package/src/LiteralCollector.ts +74 -31
- package/src/TableScanner.ts +55 -31
- package/test/CSharpStringExtractor.test.ts +1029 -0
- package/test/GuildDonateDialog.cs +194 -0
- package/test/MainSutraDetailDialog.cs +362 -0
- package/test/TestConvert.test.ts +8 -0
- package/src/CodeSnippet.ts +0 -7
- package/src/TestConvert.test.ts +0 -6
- package/src/TestSlimCsv.test.ts +0 -6
- package/src/index.ts +0 -3
package/src/CmdExecutor.ts
CHANGED
|
@@ -14,7 +14,15 @@ export class CmdExecutor {
|
|
|
14
14
|
let outCsvFile = "E:/DATA/Projects/e-gbl-client/client/Assets/Bundles/GameConfigs/Translation/hello.csv"
|
|
15
15
|
let langs = ["zh_cn"];
|
|
16
16
|
let literalCollector = new LiteralCollector();
|
|
17
|
-
literalCollector.convert(cscodeFolders, gameConfigFolders, outCsvFile, langs)
|
|
17
|
+
// return literalCollector.convert(cscodeFolders, gameConfigFolders, outCsvFile, langs,undefined,false,true)
|
|
18
|
+
return literalCollector.convert(cscodeFolders,
|
|
19
|
+
// [
|
|
20
|
+
// "E:/DATA/Projects/ZhiYou/DXTSProject/Client/Assets/Bundles/UI/",
|
|
21
|
+
// "E:/DATA/Projects/ZhiYou/DXTSProject/Client/Assets/Bundles/Battle/",
|
|
22
|
+
// "E:/DATA/Projects/ZhiYou/DXTSProject/Client/Assets/Scripts/",
|
|
23
|
+
// "E:/DATA/Projects/ZhiYou/ProjectFClient/GameClient/Assets/Bundles/FGUI/",
|
|
24
|
+
// ],
|
|
25
|
+
gameConfigFolders, outCsvFile, langs)
|
|
18
26
|
// node . --cscodedir ../../../GameClient/Assets/Bundles/FGUI/ --configdir ../../../GameClient/Assets/Bundles/GameConfigs/ --outcsv E:/DATA/Projects/e-gbl-client/client/Assets/Bundles/GameConfigs/Translation/hello.csv --langs zh_cn
|
|
19
27
|
}
|
|
20
28
|
static async runConvertWithCmdOptions() {
|
package/src/LiteralCollector.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { TableScanner } from "./TableScanner";
|
|
|
7
7
|
|
|
8
8
|
export class LiteralCollector {
|
|
9
9
|
|
|
10
|
-
scanCodeInFolder(folder: string, literals: string[], unexpects: string[], trmethod: string, scanonly: boolean, verbose: boolean) {
|
|
10
|
+
async scanCodeInFolder(folder: string, literals: string[], unexpects: string[], trmethod: string, scanonly: boolean, verbose: boolean) {
|
|
11
11
|
if (fs.existsSync(folder) == false) {
|
|
12
12
|
console.warn(`代码目录不存在: ${folder}`);
|
|
13
13
|
return;
|
|
@@ -15,32 +15,54 @@ export class LiteralCollector {
|
|
|
15
15
|
|
|
16
16
|
let files = glob.sync("**/*.cs", { cwd: folder });
|
|
17
17
|
let testFullPath = "@";
|
|
18
|
-
// let testFullPath = "E:/DATA/Projects/ZhiYou/ProjectFClient/GameClient/Assets/Bundles/
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
let convertedContent = CSCodeScanner.replaceInFile(content, snippets);
|
|
32
|
-
if (convertedContent != content) {
|
|
33
|
-
fs.writeFileSync(fullPath, convertedContent, "utf-8");
|
|
34
|
-
}
|
|
18
|
+
// let testFullPath = "E:/DATA/Projects/ZhiYou/ProjectFClient/GameClient/Assets/Bundles/GameConfigs/Main/UnitAttributeTable-UnitAttributeTable.cs";
|
|
19
|
+
|
|
20
|
+
// 限制并行处理的文件数量,避免内存占用过高
|
|
21
|
+
const batchSize = 10;
|
|
22
|
+
for (let i = 0; i < files.length; i += batchSize) {
|
|
23
|
+
const batchFiles = files.slice(i, i + batchSize);
|
|
24
|
+
const batchPromises = batchFiles.map(async (filePath) => {
|
|
25
|
+
let fullPath = folder + filePath;
|
|
26
|
+
if (testFullPath != "@") {
|
|
27
|
+
fullPath = testFullPath;
|
|
28
|
+
}
|
|
29
|
+
if (verbose) {
|
|
30
|
+
console.log(`处理文件: ${filePath}, ${fullPath}`);
|
|
35
31
|
}
|
|
32
|
+
try {
|
|
33
|
+
const content = await fs.promises.readFile(fullPath, "utf-8");
|
|
34
|
+
const snippets = CSCodeScanner.scanFile(fullPath, content, trmethod);
|
|
35
|
+
CSCodeScanner.filterSnippets(snippets);
|
|
36
|
+
|
|
37
|
+
if (snippets.length > 0) {
|
|
38
|
+
if (!scanonly) {
|
|
39
|
+
let convertedContent = CSCodeScanner.replaceInFile(content, snippets);
|
|
40
|
+
if (convertedContent != content) {
|
|
41
|
+
await fs.promises.writeFile(fullPath, convertedContent, "utf-8");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
36
44
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
45
|
+
const fileLiterals: string[] = [];
|
|
46
|
+
const fileUnexpects: string[] = [];
|
|
47
|
+
for (const snippet of snippets) {
|
|
48
|
+
fileLiterals.push(...snippet.literals);
|
|
49
|
+
fileUnexpects.push(...snippet.unexpects);
|
|
50
|
+
}
|
|
51
|
+
return { literals: fileLiterals, unexpects: fileUnexpects };
|
|
52
|
+
}
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.error(`处理文件失败: ${fullPath}`, error);
|
|
40
55
|
}
|
|
56
|
+
return { literals: [], unexpects: [] };
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const batchResults = await Promise.all(batchPromises);
|
|
60
|
+
for (const result of batchResults) {
|
|
61
|
+
literals.push(...result.literals);
|
|
62
|
+
unexpects.push(...result.unexpects);
|
|
41
63
|
}
|
|
42
64
|
|
|
43
|
-
if (
|
|
65
|
+
if (testFullPath != "@") {
|
|
44
66
|
break;
|
|
45
67
|
}
|
|
46
68
|
}
|
|
@@ -49,13 +71,18 @@ export class LiteralCollector {
|
|
|
49
71
|
|
|
50
72
|
}
|
|
51
73
|
|
|
52
|
-
scanTablesInFolder(folder: string, literals: string[], verbose: boolean) {
|
|
74
|
+
async scanTablesInFolder(folder: string, literals: string[], verbose: boolean) {
|
|
53
75
|
if (fs.existsSync(folder) == false) {
|
|
54
76
|
console.warn(`表格目录不存在: ${folder}`);
|
|
55
77
|
return;
|
|
56
78
|
}
|
|
57
79
|
let scanner = new TableScanner();
|
|
58
|
-
scanner.scanTablesLiterals(folder + "Main/", literals, verbose);
|
|
80
|
+
await scanner.scanTablesLiterals(folder + "Main/", literals, verbose);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
static needTranslate(literal: string): boolean {
|
|
84
|
+
let match = literal.match(/^[\da-zA-Z!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/? ]+$/m);
|
|
85
|
+
return match == null;
|
|
59
86
|
}
|
|
60
87
|
|
|
61
88
|
/**
|
|
@@ -66,8 +93,8 @@ export class LiteralCollector {
|
|
|
66
93
|
let literalsSet = new Set(literals);
|
|
67
94
|
literalsSet.delete("");
|
|
68
95
|
for (let literal of literals) {
|
|
69
|
-
let
|
|
70
|
-
if (
|
|
96
|
+
let needTr = LiteralCollector.needTranslate(literal);
|
|
97
|
+
if (!needTr) {
|
|
71
98
|
literalsSet.delete(literal)
|
|
72
99
|
}
|
|
73
100
|
}
|
|
@@ -79,12 +106,28 @@ export class LiteralCollector {
|
|
|
79
106
|
|
|
80
107
|
let literals: string[] = [];
|
|
81
108
|
let unexpects: string[] = [];
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
109
|
+
|
|
110
|
+
// 并行处理代码文件夹
|
|
111
|
+
const codeFolderPromises = cscodeFolders.map(async (cscodeFolder) => {
|
|
112
|
+
const folderLiterals: string[] = [];
|
|
113
|
+
const folderUnexpects: string[] = [];
|
|
114
|
+
await this.scanCodeInFolder(cscodeFolder, folderLiterals, folderUnexpects, trmethod, scanonly, verbose);
|
|
115
|
+
return { literals: folderLiterals, unexpects: folderUnexpects };
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const codeFolderResults = await Promise.all(codeFolderPromises);
|
|
119
|
+
for (const result of codeFolderResults) {
|
|
120
|
+
literals.push(...result.literals);
|
|
121
|
+
unexpects.push(...result.unexpects);
|
|
87
122
|
}
|
|
123
|
+
|
|
124
|
+
// 并行处理表格文件夹
|
|
125
|
+
const tableFolderPromises = gameConfigFolders.map(async (gameConfigFolder) => {
|
|
126
|
+
await this.scanTablesInFolder(gameConfigFolder, literals, verbose);
|
|
127
|
+
});
|
|
128
|
+
await Promise.all(tableFolderPromises);
|
|
129
|
+
|
|
130
|
+
// 去重和过滤
|
|
88
131
|
this.filterLiterals(literals);
|
|
89
132
|
|
|
90
133
|
if (verbose) {
|
package/src/TableScanner.ts
CHANGED
|
@@ -14,55 +14,79 @@ export class TableScanner {
|
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
scanJsonWords(jsonContent: string, fields: string[], literals: string[]) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
for (const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
try {
|
|
18
|
+
let arr = JSON.parse(jsonContent);
|
|
19
|
+
for (const jsonRow of arr) {
|
|
20
|
+
for (const key of fields) {
|
|
21
|
+
let value = jsonRow[key];
|
|
22
|
+
if (typeof (value) == "string") {
|
|
23
|
+
literals.push(value);
|
|
24
|
+
} else if (Array.isArray(value)) {
|
|
25
|
+
literals.push(...value);
|
|
26
|
+
}
|
|
25
27
|
}
|
|
26
28
|
}
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error(`解析JSON失败: ${error}`);
|
|
27
31
|
}
|
|
28
32
|
}
|
|
29
|
-
scanTableLiterals(csFullPath: string, literals: string[], verbose: boolean) {
|
|
33
|
+
async scanTableLiterals(csFullPath: string, literals: string[], verbose: boolean) {
|
|
30
34
|
// csFullPath = "E:/DATA/Projects/ZhiYou/ProjectFClient/GameClient/Assets/Bundles/GameConfigs/Main/RefinerTable-RefinerEventTable.cs";
|
|
31
35
|
let jsonFullPath = csFullPath.replace("/Main/", "/Auto/").replace(/\.cs$/, ".json");
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
try {
|
|
37
|
+
const [csExists, jsonExists] = await Promise.all([
|
|
38
|
+
fs.promises.access(csFullPath).then(() => true).catch(() => false),
|
|
39
|
+
fs.promises.access(jsonFullPath).then(() => true).catch(() => false)
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
if (jsonExists && csExists) {
|
|
43
|
+
const [csContent, jsonContent] = await Promise.all([
|
|
44
|
+
fs.promises.readFile(csFullPath, "utf-8"),
|
|
45
|
+
fs.promises.readFile(jsonFullPath, "utf-8")
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
let trstrFields = this.parseTrStrFields(csContent);
|
|
49
|
+
if (trstrFields.length > 0) {
|
|
50
|
+
if (verbose) {
|
|
51
|
+
console.log(`扫描表格: ${csFullPath} , 需要翻译字段: ${trstrFields}`);
|
|
52
|
+
}
|
|
53
|
+
this.scanJsonWords(jsonContent, trstrFields, literals);
|
|
54
|
+
} else {
|
|
55
|
+
if (verbose) {
|
|
56
|
+
console.log(`跳过表格: ${csFullPath}`);
|
|
57
|
+
}
|
|
38
58
|
}
|
|
39
|
-
let jsonContent = fs.readFileSync(jsonFullPath, "utf-8");
|
|
40
|
-
this.scanJsonWords(jsonContent, trstrFields, literals);
|
|
41
59
|
} else {
|
|
42
60
|
if (verbose) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
console.warn(`表格 CS 文件不存在: ${jsonFullPath}`);
|
|
50
|
-
}
|
|
51
|
-
if (fs.existsSync(jsonFullPath) == false) {
|
|
52
|
-
console.warn(`表格 JSON 文件不存在: ${csFullPath}`);
|
|
61
|
+
if (!csExists) {
|
|
62
|
+
console.warn(`表格 CS 文件不存在: ${jsonFullPath}`);
|
|
63
|
+
}
|
|
64
|
+
if (!jsonExists) {
|
|
65
|
+
console.warn(`表格 JSON 文件不存在: ${csFullPath}`);
|
|
66
|
+
}
|
|
53
67
|
}
|
|
54
68
|
}
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error(`处理表格文件失败: ${csFullPath}`, error);
|
|
55
71
|
}
|
|
56
72
|
}
|
|
57
73
|
/**
|
|
58
74
|
* 扫描
|
|
59
75
|
*/
|
|
60
|
-
scanTablesLiterals(folder: string, literals: string[], verbose: boolean) {
|
|
76
|
+
async scanTablesLiterals(folder: string, literals: string[], verbose: boolean) {
|
|
61
77
|
let dir = folder;
|
|
62
78
|
let files = glob.sync("*.cs", { cwd: dir });
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
79
|
+
|
|
80
|
+
// 限制并行处理的文件数量,避免内存占用过高
|
|
81
|
+
const batchSize = 10;
|
|
82
|
+
for (let i = 0; i < files.length; i += batchSize) {
|
|
83
|
+
const batchFiles = files.slice(i, i + batchSize).reverse();
|
|
84
|
+
const batchPromises = batchFiles.map(async (filePath) => {
|
|
85
|
+
let csFullPath = folder + filePath;
|
|
86
|
+
await this.scanTableLiterals(csFullPath, literals, verbose);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
await Promise.all(batchPromises);
|
|
66
90
|
}
|
|
67
91
|
}
|
|
68
92
|
}
|