language file merge/split tool

This commit is contained in:
2026-09-19 18:20:31 +08:00
parent e2d8658136
commit d7299daeaa
6 changed files with 165 additions and 0 deletions
Executable
+109
View File
@@ -0,0 +1,109 @@
#!/usr/bin/env ts-node
import fs from 'fs';
// a sample tool that merge (zh|en)(sfw|nsfw) ver together for edit faster
// for other language translator: if you need to use this script, just edit `targetLangFiles` and `workFile`.
const targetLangFiles: LanguageInstance[] = [
{suffix: "en_us_nsfw", file: "NSFW (Default Pack)/en_us.json", overrideSuffix: "en_us_sfw", ro: true},
{suffix: "en_us_sfw", file: "SFW (baked in NoN)/en_us.json", ro: true},
{suffix: "zh_cn_nsfw", file: "NSFW (Default Pack)/zh_cn.json", overrideSuffix: "zh_cn_sfw"},
{suffix: "zh_cn_sfw", file: "SFW (baked in NoN)/zh_cn.json"}
]
const workFile = "zh_cn.json"
type LanguageInstance = {
suffix: string,
file: string,
data?: Record<string, string>,
overrideSuffix?: string,
override?: LanguageInstance,
ro?: boolean // read only, don't edit.
}
const mainLangFile = "SFW (baked in NoN)/en_us.json";
const targetLangFilesBySuffix: Record<string, LanguageInstance> = {}
for (let l of targetLangFiles) {
if (!l.suffix.match(/^[a-z0-9-_]+$/i)) throw new Error(`bad Suffix: ${JSON.stringify(l.suffix)} that not match ^[a-z0-9-_]+$ (ignore case)`)
targetLangFilesBySuffix[l.suffix] = l
}
for (let l of targetLangFiles) {
if (l.overrideSuffix) l.override = targetLangFilesBySuffix[l.overrideSuffix]
}
type KVPair = [string, string];
function parseOrdered(src: string): KVPair[] {
const o: KVPair[] = [];
JSON.parse(src, (k, v) => {
if (k === '') return v
if (typeof k !== "string" || typeof v !== "string") throw new Error(`unexpected k/v:${JSON.stringify([k, v])}`)
o.push([k, v])
return v;
});
return o;
}
function stringify(data: KVPair[]): string {
const s = JSON.stringify
const body = data.map(([k, v]) => ` ${s(k)}: ${s(v)}`).join(",\n")
return `{\n${body}\n}\n`
}
const keyOrder = parseOrdered(fs.readFileSync(mainLangFile, "utf-8")).map(([k, v]) => k)
function genMergedLang(): void {
for (const l of targetLangFiles) {
l.data = JSON.parse(fs.readFileSync(l.file, "utf-8")) as Record<string, string>
}
const output: KVPair[] = []
for (let key of keyOrder) {
for (let l of targetLangFiles) {
let value = l.data?.[key];
if (l.override?.data?.[key] === value) value = undefined
if (value) output.push([`${key}.${l.suffix}`, value])
}
}
fs.writeFileSync(workFile, stringify(output))
}
function writeBackLang() {
let mixed = parseOrdered(fs.readFileSync(workFile, "utf-8"));
const buffer: Record<string, KVPair[]> = {}
for (const key of Object.keys(targetLangFilesBySuffix)) buffer[key] = []
for (const [k, v] of mixed) {
const [key, suffix] = k.split(/\.(?=[^.]*$)/);
if (!buffer[suffix]) throw new Error(`can't parse the suffix: ${JSON.stringify(suffix)} from key: ${JSON.stringify(k)}`)
buffer[suffix].push([key, v])
}
for (let suffix in buffer) {
if (targetLangFilesBySuffix[suffix].ro) continue
fs.writeFileSync(targetLangFilesBySuffix[suffix].file, stringify(buffer[suffix]))
}
}
;(() => {
let files = [targetLangFiles.map(l => l.file), workFile].flat()
let latestFile = files.reduce<{ mtime: Date, fileName: string } | null>(
(latest, file) => {
if (!fs.existsSync(file)) return latest
let stat = fs.statSync(file);
if (!latest) return {mtime: stat.mtime, fileName: file};
return stat.mtime > latest.mtime ? {mtime: stat.mtime, fileName: file} : latest
}, null);
if (!latestFile) return
if (latestFile.fileName === workFile)
writeBackLang()
else
genMergedLang()
})();