fix lang builder

This commit is contained in:
Km.Van
2026-07-11 16:55:38 +08:00
parent 4d7eb83c8b
commit 3fe0dbb5b2
+77 -48
View File
@@ -1,77 +1,106 @@
/**
* @version 1.0.1
*/
import { lstatSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
import { extname } from "node:path";
import { readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
import { extname, join } from "node:path";
const GETTEXT_REG =
/gettext\s*\(\s*(["'`])((?:(?!\1)[^\\]|\\.)*)\1\s*(?:,\s*(?:(["'`])((?:(?!\3)[^\\]|\\.)*)\3)?)?\s*\)/gms;
const GETTEXT_REG = /gettext\s*\(\s*(".+?")\s*,*\s*(".+?")*\s*\)/gm;
export class PotBuilder {
potPath = "";
sourceDir = "";
entries = {};
/**
* @type {string[]}
*/
filePaths = [];
constructor({ potPath, sourceDir }) {
this.potPath = potPath;
this.sourceDir = sourceDir;
}
build() {
this.filePaths = []; // 清空历史,防止重复调用时叠加
this.fetchDirOrFile(this.sourceDir);
for (const path of this.filePaths) {
this.buildEntries(path);
}
this.buildPotFile();
}
buildEntries(path) {
const code = readFileSync(path).toString();
const matches = code.matchAll(GETTEXT_REG);
if (matches) {
for (const match of matches) {
const msgid = match[1].slice(1, -1);
const msgctxt = (match[2] || "").slice(1, -1);
if (this.entries[`${msgid}${msgctxt}`]) {
continue;
}
this.entries[`${msgid}${msgctxt}`] = `
${msgctxt ? `msgctxt ${JSON.stringify(msgctxt)}` : ""}
msgid ${JSON.stringify(msgid)}
msgstr ""
`.trim();
const code = readFileSync(path, "utf8");
for (const match of code.matchAll(GETTEXT_REG)) {
const [, , msgid] = match;
const msgctxt = match[4] || "";
const cacheKey = `${msgid}____${msgctxt}`;
if (this.entries[cacheKey]) {
continue;
}
// 组装 entry,确保没有多余的空行
const lines = [];
if (msgctxt) {
lines.push(`msgctxt ${JSON.stringify(msgctxt)}`);
}
lines.push(`msgid ${JSON.stringify(msgid)}`);
lines.push('msgstr ""');
this.entries[cacheKey] = lines.join("\n");
}
}
buildPotFile() {
const toWriteData = `
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: \\n"
"POT-Creation-Date: \\n"
"PO-Revision-Date: \\n"
"Last-Translator: Km.Van\\n"
"Language-Team: INN STUDIO\\n"
"MIME-Version: 1.0\\n"
"Content-Type: text/plain; charset=UTF-8\\n"
"Content-Transfer-Encoding: 8bit\\n"
"X-Generator: Poedit 3.0.1\\n"
"X-Poedit-Basepath: ../src\\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\\n"
"X-Poedit-SourceCharset: UTF-8\\n"
"X-Poedit-KeywordsList: gettext\\n"
${Object.values(this.entries).join("\n\n")}
`.trim();
buildPotFile() {
const headers = [
"#, fuzzy",
'msgid ""',
'msgstr ""',
'"Project-Id-Version: \\n"',
'"POT-Creation-Date: \\n"',
'"PO-Revision-Date: \\n"',
'"Last-Translator: Km.Van\\n"',
'"Language-Team: INN STUDIO\\n"',
'"MIME-Version: 1.0\\n"',
'"Content-Type: text/plain; charset=UTF-8\\n"',
'"Content-Transfer-Encoding: 8bit\\n"',
'"X-Generator: Poedit 3.0.1\\n"',
'"X-Poedit-Basepath: ../src\\n"',
'"Plural-Forms: nplurals=2; plural=(n != 1);\\n"',
'"X-Poedit-SourceCharset: UTF-8\\n"',
'"X-Poedit-KeywordsList: gettext\\n"',
].join("\n");
const content = Object.values(this.entries).join("\n\n");
const toWriteData = `${headers}\n\n${content}\n`; // 标准文件末尾留一空行
writeFileSync(this.potPath, toWriteData, "utf8");
}
fetchDirOrFile(filePathOrDir) {
if (lstatSync(filePathOrDir).isDirectory()) {
for (const path of readdirSync(filePathOrDir)) {
this.fetchDirOrFile(`${filePathOrDir}/${path}`);
try {
const stat = statSync(filePathOrDir);
if (stat.isDirectory()) {
const entries = readdirSync(filePathOrDir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = join(filePathOrDir, entry.name);
if (entry.isDirectory()) {
this.fetchDirOrFile(fullPath);
} else if (
entry.isFile() &&
[".ts", ".tsx"].includes(extname(entry.name))
) {
this.filePaths.push(fullPath);
}
}
} else if (
stat.isFile() &&
[".ts", ".tsx"].includes(extname(filePathOrDir))
) {
this.filePaths.push(filePathOrDir);
}
} else if ([".ts", ".tsx"].includes(extname(filePathOrDir))) {
this.filePaths.push(filePathOrDir);
} catch (err) {
console.error(`无法读取路径: ${filePathOrDir}`, err);
}
}
}