mirror of
https://github.com/kmvan/x-prober.git
synced 2026-08-13 01:59:26 +08:00
fix lang builder
This commit is contained in:
+77
-48
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user