1
0
forked from me/IronOS
Files
IronOS/Translations/TranslationsParser.html
Thomas Weißschuh 884a830d62 TS100: move translation generation to Makefile
Translation.cpp is now automatically regenerated when necessary.
This frees the developer from having to remember to execute build.sh
after the translations have changed.

Translation.cpp has been moved from Core/Src/ to the new Core/Gen/ as
otherwise it would end up twice in SOURCE, once through the source
discovery and once through the explicit entry.
2021-01-16 12:25:03 +01:00

322 lines
7.9 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>TS100 Translation Parser</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="translations_commons.js"></script>
<script src="translations_def.js"></script>
<script>
var app;
var defMap = {};
var langMap = {};
var lang;
var defMsgMap;
var defCharMap;
var defGrpMap;
var defOptMap;
function save(langCode){
saveJSON(langMap[langCode], "translation_"+langCode.toLowerCase()+".json");
}
function view(langCode){
showJSON(langMap[langCode], "translation_"+langCode.toLowerCase()+".json");
}
function translationFileSelected(e) {
var target = e;
var id = target.id;
var file = target.files[0];
if (!file) {
return;
}
var fr = new FileReader();
fr.onload = function(e) {
parseTranslationFile(file.name, e.target.result);
}
fr.readAsText(file);
}
function parseTranslationFile(name, src) {
// remove multiline comments
src = src.replace(/\/\*[\s\S.]*?\*\//mg, "");
// remove single-line comments
src = src.replace(/\/\/.*/mg, "");
// remove empty lines
src = src.replace(/^\s*\n/gm, "");
var langCode = "";
var srcLines = src.split("\n");
var reMessage = /const\s+char\s*\*\s+([\w\d]+)\s*=\s*"(.*)"/;
var reSettingsDescStart = /const\s+char\s*\*\s+SettingsDescriptions\[/;
var reSettingsNamesStart = /const\s+char\s*\*\s+SettingsShortNames\[/;
var reSettingsMenuDescStart = /const\s+char\s*\*\s+SettingsMenuEntriesDescriptions\[/;
var reChar = /const\s+char\s+([\w\d]+)\s*=\s*'(\w)'/;
var reMenuMode = /SettingsShortNameType\s*=\s*SHORT_NAME_(\w+)_LINE/;
var reMenuStart = /\s*const\s+char\s*\*\s+SettingsMenuEntries\[/;
// var reString = /^\s*"(.*)"/;
var reString = /"(.*)"/;
var reSingleLine = /{\s*"(.*)"\s*}/;
var reDoubleLine = /{\s*"(.*)"\s*,\s*"(.*)"\s*}/;
var mode = '';
var entryIndex = 0;
for (var li = 0; li < srcLines.length; li++) {
// trim lines
line = srcLines[li] = srcLines[li].trim();
// if entering a new lang block
if (startsWith(line, "#ifdef LANG_")) {
mode = 'new-language';
langCode = line.substring(12);
lang = langMap[langCode];
// use existing or instantiate new
if (!isDefined(lang)) {
lang = {
languageCode: langCode,
cyrillicGlyphs: false,
messages: {},
characters: {},
menuGroups: {},
menuOptions: {}
};
langMap[langCode] = lang;
app.languages[app.languages.length] = langCode;
}
entryIndex = 0;
continue;
}
// Use Cyrillic glyphs
if (startsWith(line, "#define CYRILLIC_GLYPHS")) {
lang.cyrillicGlyphs = true;
entryIndex = 0;
continue;
}
// Menu type
reMenuMode.lastIndex = 0;
match = reMenuMode.exec(line);
if (match) {
entryIndex = 0;
continue;
}
// Messages
reMessage.lastIndex = 0;
match = reMessage.exec(line);
if (match) {
lang.messages[match[1]] = xunescape(match[2]);
entryIndex = 0;
continue;
}
// Chars descriptions
reChar.lastIndex = 0;
match = reChar.exec(line);
if (match) {
// found description block start
mode = 'char';
lang.characters[match[1]] = xunescape(match[2]);
entryIndex = 0;
continue;
}
// Settings descriptions
reSettingsDescStart.lastIndex = 0;
match = reSettingsDescStart.exec(line);
if (match) {
// found description block start
mode = 'settingsDesc';
entryIndex = 0;
continue;
}
reSettingsNamesStart.lastIndex = 0;
match = reSettingsNamesStart.exec(line);
if (match) {
// found description block start
mode = 'settingsNames';
entryIndex = 0;
continue;
}
reMenuStart.lastIndex = 0;
match = reMenuStart.exec(line);
if (match) {
// found description block start
mode = 'menu';
entryIndex = 0;
continue;
}
reSettingsMenuDescStart.lastIndex = 0;
match = reSettingsMenuDescStart.exec(line);
if (match) {
// found description block start
mode = 'menuDesc';
entryIndex = 0;
continue;
}
if (mode == 'menu') {
// processing menu group names
reString.lastIndex = 0;
match = reString.exec(line);
if (match) {
// found description string
var entry = getMenuGroup(entryIndex);
var m = match[1].split("\\n");
entry.text2[0] = xunescape(m[0]);
entry.text2[1] = xunescape(m[1]);
entryIndex++;
}
} else if (mode == 'menuDesc') {
// processing menu group descriptions
reString.lastIndex = 0;
match = reString.exec(line);
if (match) {
// found description string
var entry = getMenuGroup(entryIndex);
entry.desc = xunescape(match[1]);
entryIndex++;
}
} else if (mode == 'settingsDesc') {
// processing option descriptions
reString.lastIndex = 0;
match = reString.exec(line);
if (match) {
// found description string
var entry = getMenuOption(entryIndex);
entry.desc = xunescape(match[1]);
entryIndex++;
}
} else if (mode == 'settingsNames') {
reDoubleLine.lastIndex = 0;
match = reDoubleLine.exec(line);
if (match) {
var entry = getMenuOption(entryIndex);
entry.text2[0] = xunescape(match[1]);
entry.text2[1] = xunescape(match[2]);
entryIndex++;
} else {
reSingleLine.lastIndex = 0;
match = reSingleLine.exec(line);
if (match) {
var entry = getMenuOption(entryIndex);
entry.text = xunescape(match[1]);
entryIndex++;
}
}
}
}
app.done = 1;
}
function getMenuOption(entryIndex) {
var optionDef = def.menuOptions[entryIndex];
if (!isDefined(optionDef)) {
var s = "Could not find menu option with index "+entryIndex;
alert(s);
throw s;
}
var id = optionDef.id;
var entry = lang.menuOptions[id];
if (!isDefined(entry)) {
entry =
{
"text2": ["", ""],
"desc": ""
}
lang.menuOptions[id] = entry;
}
return entry;
}
function getMenuGroup(entryIndex) {
var optionDef = def.menuGroups[entryIndex];
if (!isDefined(optionDef)) {
var s = "Could not find menu group with index "+entryIndex;
alert(s);
throw s;
}
var id = optionDef.id;
var entry = lang.menuGroups[id];
if (!isDefined(entry)) {
entry =
{
"text2": ["", ""],
"desc": ""
}
lang.menuGroups[id] = entry;
}
return entry;
}
function markSaved(lang) {
document.getElementById("row_"+lang).classList.add("saved");
}
function loaded() {
app = new Vue({
el : '#app',
data : {
languages: [],
done : false,
def : {
}
},
methods : {
vSave : function(lang) {
save(lang);
markSaved(lang);
},
vView : function(lang) {
view(lang);
markSaved(lang);
}
}
});
app.def = def;
defMsgMap = copyArrayToMap(app.def.messages);
defCharMap = copyArrayToMap(app.def.characters);
defGrpMap = copyArrayToMap(app.def.menuGroups);
defOptMap = copyArrayToMap(app.def.menuOptions);
}
window.onload=loaded;
</script>
<link href="translations.css" rel="stylesheet" type="text/css">
</head>
<body>
<div id="app">
<h1>TS100 Translation Parser</h1>
<table class="header data">
<tr>
<td class="label">Translation.cpp</td>
<td class="value">
<input type="file" id="translation-cpp-file" onchange="translationFileSelected(this)" accept=".cpp">
</td>
</tr>
</table>
<div class="data" v-if="done">
<div class="value" v-for="lang in languages" :id="'row_'+lang">
<input type="button" :value="'Save '+lang" v-on:click="vSave(lang)">
<input type="button" :value="'View '+lang" v-on:click="vView(lang)">
</div>
</div>
</div>
</body>
</html>