-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.ts
118 lines (104 loc) · 3.4 KB
/
main.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// import packages
import { XMLParser } from "npm:fast-xml-parser";
import { decode } from "npm:html-entities";
import { parse } from "flags";
// get CLI args
const args = parse(Deno.args);
const exmlDir: string = args["input-path"] || "./EXML/"; // --input-path="path/to/file" default: "./EXML/"
const outputDir: string = args["output-path"] || "./output/"; // --output-path="path/to/file" default: "./output/"
const outputFileName: string = args.filename || "translation.txt"; // --filename=Lenni.txt default: "translation.txt"
const languageArgs = args["_"].map((language) =>
language.toString().toLowerCase()
); // english german -- this needs the .toString() method because TS would complain
const timer: boolean = args.timer;
// create directories if they don't exist yet
Deno.mkdirSync(exmlDir, { recursive: true });
Deno.mkdirSync(outputDir, { recursive: true });
// set up interfaces
interface Xml {
"@version": string;
"@encoding": string;
}
// newer MBINCompiler versions (> 4.70) do not have the additional "Property"
interface LocEntry {
"@name": string;
"@value": string;
Property?: {
"@name": string;
"@value": string;
};
}
interface TkLocalisationEntry {
Property: Array<LocEntry>;
"@value": string;
}
interface Data {
Property: {
Property: TkLocalisationEntry[];
"@name": string;
};
"@template": string;
}
interface RootObject {
"?xml": Xml;
Data: Data;
}
interface LangData {
[key: string]: {
[key: string]: string;
};
}
// initialise global variables
const files = Array.from(Deno.readDirSync(exmlDir)).filter(
(file) => file.isFile && file.name.toLowerCase().endsWith(".exml")
);
const langData: LangData = {};
// set up XML parser
const options = {
ignoreAttributes: false,
attributeNamePrefix: "@",
};
const parser = new XMLParser(options);
if (timer) console.time("Total time");
console.log("Starting");
// loop through EXML files
for (let i = 0; i < files.length; i++) {
const file = files[i];
const fullFileName = file.name;
const fileData = Deno.readTextFileSync(exmlDir + fullFileName);
const document: RootObject = parser.parse(fileData);
const langElements = document.Data.Property.Property;
// loop over TkLocalisationData sections
for (const locEntry of langElements) {
const locEntryData = locEntry.Property;
const langKey = locEntryData[0]["@value"];
langData[langKey] ??= {};
// loop over individual lang keys and their assigned values
// we're skipping index 0 since that's the langkey and already covered above
for (let j = 1; j < locEntryData.length; j++) {
const entry = locEntryData[j];
const language = entry["@name"];
if (languageArgs.length && !languageArgs.includes(language.toLowerCase()))
continue;
const langValue = entry.Property
? entry.Property["@value"]
: entry["@value"];
if (!langValue) continue;
langData[langKey][language] = decode(langValue, { level: "xml" });
}
}
console.log(
`${i + 1} / ${files.length} (${Math.round(
((i + 1) / files.length) * 100
)}%) - Processed ${fullFileName}`
);
}
const textContent = [];
for (const key in langData) {
textContent.push(key + "\n");
Object.values(langData[key]).forEach((item) => textContent.push(item + "\n"));
textContent.push("\n");
}
Deno.writeTextFileSync(outputDir + outputFileName, textContent.join("").trim());
console.log("done!");
if (timer) console.timeEnd("Total time");