import * as fs from "fs"; import { glob } from "glob"; import { CSCodeScanner } from "./CSCodeScanner"; import { CSVUtils } from "./CSVUtils"; import { TableScanner } from "./TableScanner"; export class LiteralCollector { async scanCodeInFolder(folder: string, literals: string[], unexpects: string[], trmethod: string, scanonly: boolean, verbose: boolean) { if (fs.existsSync(folder) == false) { console.warn(`代码目录不存在: ${folder}`); return; } let files = glob.sync("**/*.cs", { cwd: folder }); let testFullPath = "@"; // let testFullPath = "E:/DATA/Projects/ZhiYou/ProjectFClient/GameClient/Assets/Bundles/FGUI/Basics/Comp/InfoToastComp.cs"; // 限制并行处理的文件数量,避免内存占用过高 const batchSize = 10; for (let i = 0; i < files.length; i += batchSize) { const batchFiles = files.slice(i, i + batchSize); const batchPromises = batchFiles.map(async (filePath) => { let fullPath = folder + filePath; if (testFullPath != "@") { fullPath = testFullPath; } if (verbose) { console.log(`处理文件: ${filePath}, ${fullPath}`); } try { const content = await fs.promises.readFile(fullPath, "utf-8"); const snippets = CSCodeScanner.scanFile(fullPath, content, trmethod); if (snippets.length > 0) { if (!scanonly) { let convertedContent = CSCodeScanner.replaceInFile(content, snippets); if (convertedContent != content) { await fs.promises.writeFile(fullPath, convertedContent, "utf-8"); } } let filteredSnippets = [...snippets]; CSCodeScanner.filterSnippets(filteredSnippets); const fileLiterals: string[] = []; const fileUnexpects: string[] = []; for (const snippet of filteredSnippets) { fileLiterals.push(...snippet.literals); fileUnexpects.push(...snippet.unexpects); } return { literals: fileLiterals, unexpects: fileUnexpects }; } } catch (error) { console.error(`处理文件失败: ${fullPath}`, error); } return { literals: [], unexpects: [] }; }); const batchResults = await Promise.all(batchPromises); for (const result of batchResults) { literals.push(...result.literals); unexpects.push(...result.unexpects); } if (testFullPath != "@") { break; } } unexpects = [...new Set(unexpects)]; } async scanTablesInFolder(folder: string, literals: string[], verbose: boolean) { if (fs.existsSync(folder) == false) { console.warn(`表格目录不存在: ${folder}`); return; } let scanner = new TableScanner(); await scanner.scanTablesLiterals(folder + "Main/", literals, verbose); } static needTranslate(literal: string): boolean { let match = literal.match(/^[\da-zA-Z!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/? ]+$/m); return match == null; } /** * 去重 * @param literals */ filterLiterals(literals: string[]) { let literalsSet = new Set(literals); literalsSet.delete(""); for (let literal of literals) { let needTr = LiteralCollector.needTranslate(literal); if (!needTr) { literalsSet.delete(literal) } } literals.length = 0; literals.push(...literalsSet); } async convert(cscodeFolders: string[], gameConfigFolders: string[], outCsvFile: string, langs: string[], trmethod: string = "Tr.TR", scanonly: boolean = false, verbose: boolean = false) { let literals: string[] = []; let unexpects: string[] = []; // 并行处理代码文件夹 const codeFolderPromises = cscodeFolders.map(async (cscodeFolder) => { const folderLiterals: string[] = []; const folderUnexpects: string[] = []; await this.scanCodeInFolder(cscodeFolder, folderLiterals, folderUnexpects, trmethod, scanonly, verbose); return { literals: folderLiterals, unexpects: folderUnexpects }; }); const codeFolderResults = await Promise.all(codeFolderPromises); for (const result of codeFolderResults) { literals.push(...result.literals); unexpects.push(...result.unexpects); } // 并行处理表格文件夹 const tableFolderPromises = gameConfigFolders.map(async (gameConfigFolder) => { await this.scanTablesInFolder(gameConfigFolder, literals, verbose); }); await Promise.all(tableFolderPromises); // 去重和过滤 this.filterLiterals(literals); if (verbose) { console.log("扫描合并结果:", literals); } if (!scanonly) { await CSVUtils.updateToFile(outCsvFile, literals, langs); } for (const unexpect of unexpects) { console.error(unexpect); } } }