1
0
forked from me/IronOS

Dropping single line menu support

This commit is contained in:
Ben V. Brown
2020-09-06 16:44:19 +10:00
parent c7bde079cd
commit aa78ca2594
5 changed files with 660 additions and 717 deletions

View File

@@ -1,353 +1,329 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>TS100 Translation Editor</title> <title>TS100 Translation Editor</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="translations_commons.js"></script> <script src="translations_commons.js"></script>
<script src="translations_def.js"></script> <script src="translations_def.js"></script>
<script> <script>
var app; var app;
var defMap = {}; var defMap = {};
function save(){ function save(){
saveJSON(app.current, "translation_"+app.current.languageCode.toLowerCase()+".json"); saveJSON(app.current, "translation_"+app.current.languageCode.toLowerCase()+".json");
} }
function view(){ function view(){
showJSON(app.current, "translation_"+app.current.languageCode.toLowerCase()+".json"); showJSON(app.current, "translation_"+app.current.languageCode.toLowerCase()+".json");
} }
function fileChanged(e) { function fileChanged(e) {
var target = e; var target = e;
var id = target.id; var id = target.id;
var file = target.files[0]; var file = target.files[0];
if (!file) { if (!file) {
return; return;
} }
var fr = new FileReader(); var fr = new FileReader();
fr.onload = function(e) { fr.onload = function(e) {
try { try {
var json = JSON.parse(e.target.result); var json = JSON.parse(e.target.result);
} catch (ex) { } catch (ex) {
console.log(ex); console.log(ex);
alert("Invalid JSON file: " + file.name); alert("Invalid JSON file: " + file.name);
return; return;
} }
if (id == "referent-lang-file") { if (id == "referent-lang-file") {
if (checkTranslationFile(file.name)) { if (checkTranslationFile(file.name)) {
app.referent = json; app.referent = json;
app.meta.referentLoaded = true; app.meta.referentLoaded = true;
} }
} else if (id == "current-lang-file") { } else if (id == "current-lang-file") {
if (checkTranslationFile(file.name)) { if (checkTranslationFile(file.name)) {
app.current = json; app.current = json;
if (!app.current.cyrillicGlyphs){ if (!app.current.cyrillicGlyphs){
app.current.cyrillicGlyphs = false; app.current.cyrillicGlyphs = false;
} }
app.meta.currentLoaded = true; app.meta.currentLoaded = true;
} }
} }
synchronizeData(); synchronizeData();
} }
fr.readAsText(file); fr.readAsText(file);
} }
function synchronizeData() { function synchronizeData() {
app.obsolete = {}; app.obsolete = {};
copyMissing(app.def.messages, app.referent.messages, app.current.messages); copyMissing(app.def.messages, app.referent.messages, app.current.messages);
copyMissing(app.def.characters, app.referent.characters, app.current.characters); copyMissing(app.def.characters, app.referent.characters, app.current.characters);
copyMissing(app.def.menuGroups, app.referent.menuGroups, app.current.menuGroups); copyMissing(app.def.menuGroups, app.referent.menuGroups, app.current.menuGroups);
copyMissing(app.def.menuOptions, app.referent.menuOptions, app.current.menuOptions); copyMissing(app.def.menuOptions, app.referent.menuOptions, app.current.menuOptions);
} }
/** /**
* Copy all missing properties from referent to current * Copy all missing properties from referent to current
* for each entry in definition * for each entry in definition
*/ */
function copyMissing(defList, referentMap, currentMap) { function copyMissing(defList, referentMap, currentMap) {
if (!isDefined(defList) || !isDefined(referentMap) || !isDefined(currentMap)) { if (!isDefined(defList) || !isDefined(referentMap) || !isDefined(currentMap)) {
return; return;
} }
var len = defList.length; var len = defList.length;
for (var i = 0; i < len; i++) { for (var i = 0; i < len; i++) {
var id = defList[i].id; var id = defList[i].id;
if (!isDefined(referentMap[id])) { if (!isDefined(referentMap[id])) {
referentMap[id] = ''; referentMap[id] = '';
} }
if (!isDefined(currentMap[id])) { if (!isDefined(currentMap[id])) {
currentMap[id] = referentMap[id]; currentMap[id] = referentMap[id];
} }
} }
processObsolete(defList, currentMap); processObsolete(defList, currentMap);
} }
// Passes through all entries from the given map. // Passes through all entries from the given map.
// If a corresponding entry is not found in the defList, it is removed from the map, and added into the obsolete map. // If a corresponding entry is not found in the defList, it is removed from the map, and added into the obsolete map.
function processObsolete(defList, map) { function processObsolete(defList, map) {
// Index list to map for faster search // Index list to map for faster search
var defMap = copyArrayToMap(defList); var defMap = copyArrayToMap(defList);
Object.keys(map).forEach(function(key) { Object.keys(map).forEach(function(key) {
if (!isDefined(defMap[key])) { if (!isDefined(defMap[key])) {
app.obsolete[key] = { id : key, value : map[key]}; app.obsolete[key] = { id : key, value : map[key]};
delete map[key]; delete map[key];
} }
}); });
} }
function length(obj, mode) { function length(obj, mode) {
if (!isDefined(mode) || mode == 0) { if (!isDefined(mode) || mode == 0) {
// return direct length // return direct length
return obj.length; return obj.length;
} else if (mode == 1) { }
// return length of text property // return the longest length in text2 array
return obj.text.length; return Math.max(isDefinedNN(obj.text2[0]) ? obj.text2[0].length : 0, isDefinedNN(obj.text2[1]) ? obj.text2[1].length : 0);
} else if (mode == 2) {
// return the longest length in text2 array }
return Math.max(isDefinedNN(obj.text2[0]) ? obj.text2[0].length : 0, isDefinedNN(obj.text2[1]) ? obj.text2[1].length : 0);
} function getAttribute(obj, attribute) {
} var d = "2";
var v = obj[attribute+d];
function getAttribute(obj, attribute, isDouble) { if (isDefined(v))
var d = isDouble ? "2" : ""; return v;
var v = obj[attribute+d]; return obj[attribute];
if (isDefined(v)) }
return v;
return obj[attribute]; function loaded() {
} app = new Vue({
el : '#app',
function loaded() { data : {
app = new Vue({ meta : {
el : '#app', referentLoaded : false,
data : { currentLoaded : false,
meta : { },
referentLoaded : false, def : {
currentLoaded : false, },
}, referent : {
def : { messages : {}
}, },
referent : { current : {
messages : {} loaded: false,
}, },
current : { obsolete : {},
loaded: false, },
}, methods : {
obsolete : {}, validateInput: function(valMap, id, mode) {
menuDouble : false var d = defMap[id];
}, var vLen = 0;
methods : { if (!isDefined(mode))
validateInput: function(valMap, id, mode) { mode = 0;
var d = defMap[id];
var vLen = 0; try {
if (!isDefined(mode)) // Sum for complex length
mode = 0; for (var i = 0; i < d.lenSum.fields.length; i++) {
vLen += length(valMap[d.lenSum.fields[i]], mode);
try { }
// Sum for complex length d = d.lenSum;
for (var i = 0; i < d.lenSum.fields.length; i++) { } catch (e) {
vLen += length(valMap[d.lenSum.fields[i]], mode); // Single field length
} vLen = length(valMap[id], mode);
d = d.lenSum; }
} catch (e) { var maxLen = getAttribute(d, 'maxLen', mode == 2);
// Single field length var minLen = getAttribute(d, 'minLen', mode == 2);
vLen = length(valMap[id], mode); var len = getAttribute(d, 'len', mode == 2);
} if (isNumber(maxLen) && vLen > maxLen
var maxLen = getAttribute(d, 'maxLen', mode == 2); || isNumber(minLen) && vLen < minLen
var minLen = getAttribute(d, 'minLen', mode == 2); || isNumber(len) && vLen != len
var len = getAttribute(d, 'len', mode == 2); ) {
if (isNumber(maxLen) && vLen > maxLen return "invalid";
|| isNumber(minLen) && vLen < minLen }
|| isNumber(len) && vLen != len },
) {
return "invalid"; constraintString: function(e) {
} var str = "";
}, var delim = "";
var v;
constraintString: function(e, d) { d = "2";
var str = ""; if (isDefinedNN(e.lenSum)) {
var delim = ""; str = "len("+(e.lenSum.fields+"").replace(/,/g," + ")+") -> ";
var v; e = e.lenSum;
if (!isDefined(d) || d == false) { }
d = ""; v = getAttribute(e, 'len', d);
} else { if (isNumber(v)) {
d = "2"; str += delim + "len=" + v;
} delim = " and ";
}
if (isDefinedNN(e.lenSum)) { v = getAttribute(e, 'minLen', d);
str = "len("+(e.lenSum.fields+"").replace(/,/g," + ")+") -> "; if (isNumber(v)) {
e = e.lenSum; str += delim + "len>=" + v;
} delim = " and ";
v = getAttribute(e, 'len', d); }
if (isNumber(v)) { v = getAttribute(e, 'maxLen', d);
str += delim + "len=" + v; if (isNumber(v)) {
delim = " and "; str += delim + "len<=" + v;
} delim = " and ";
v = getAttribute(e, 'minLen', d); }
if (isNumber(v)) { return str;
str += delim + "len>=" + v; }
delim = " and "; }
} });
v = getAttribute(e, 'maxLen', d); app.def = def;
if (isNumber(v)) { copyArrayToMap(app.def.messages, defMap);
str += delim + "len<=" + v; copyArrayToMap(app.def.characters, defMap);
delim = " and "; copyArrayToMap(app.def.menuGroups, defMap);
} copyArrayToMap(app.def.menuOptions, defMap);
return str; }
}
} window.onload=loaded;
}); </script>
app.def = def; <link href="translations.css" rel="stylesheet" type="text/css">
copyArrayToMap(app.def.messages, defMap); </head>
copyArrayToMap(app.def.characters, defMap); <body>
copyArrayToMap(app.def.menuGroups, defMap);
copyArrayToMap(app.def.menuOptions, defMap); <div id="app">
} <h1>TS100 Translation Editor<span v-if="meta.currentLoaded"> - {{ current.languageLocalName }} [{{current.languageCode}}]</span></h1>
<table class="header data">
window.onload=loaded; <tr>
</script> <td class="label">Referent Language</td>
<link href="translations.css" rel="stylesheet" type="text/css"> <td class="value">
</head> <input type="file" id="referent-lang-file" onchange="fileChanged(this)" accept=".json">
<body> <span class="selected" v-if="meta.referentLoaded">{{ referent.languageLocalName }} [{{referent.languageCode}}]</span>
</td>
<div id="app"> </tr>
<h1>TS100 Translation Editor<span v-if="meta.currentLoaded"> - {{ current.languageLocalName }} [{{current.languageCode}}]</span></h1> <tr v-if="meta.referentLoaded">
<table class="header data"> <td class="label">Current Language</td>
<tr> <td class="value">
<td class="label">Referent Language</td> <input type="file" id="current-lang-file" onchange="fileChanged(this)" accept=".json">
<td class="value"> <span class="selected" v-if="meta.currentLoaded">{{ current.languageLocalName }} [{{current.languageCode}}]</span>
<input type="file" id="referent-lang-file" onchange="fileChanged(this)" accept=".json"> </td>
<span class="selected" v-if="meta.referentLoaded">{{ referent.languageLocalName }} [{{referent.languageCode}}]</span> </tr>
</td> <tr v-if="meta.currentLoaded">
</tr> <td class="label">Local Language Code</td>
<tr v-if="meta.referentLoaded"> <td class="value"><input type="text" v-model="current.languageCode" maxlength="8" v-on:change="current.languageCode=current.languageCode.toUpperCase()" class="short"></td>
<td class="label">Current Language</td> </tr>
<td class="value"> <tr v-if="meta.currentLoaded">
<input type="file" id="current-lang-file" onchange="fileChanged(this)" accept=".json"> <td class="label">Local Language Name</td>
<span class="selected" v-if="meta.currentLoaded">{{ current.languageLocalName }} [{{current.languageCode}}]</span> <td class="value"><input type="text" v-model="current.languageLocalName" class="short"></td>
</td> </tr>
</tr> <tr v-if="meta.currentLoaded">
<tr v-if="meta.currentLoaded"> <td class="label">Font table to use</td>
<td class="label">Local Language Code</td> <td class="value">
<td class="value"><input type="text" v-model="current.languageCode" maxlength="8" v-on:change="current.languageCode=current.languageCode.toUpperCase()" class="short"></td> <select v-model="current.cyrillicGlyphs" v-on:change="current.cyrillicGlyphs = current.cyrillicGlyphs=='true'">
</tr> <option value="false">Latin Extended</option>
<tr v-if="meta.currentLoaded"> <option value="true">Cyrillic Glyphs</option>
<td class="label">Local Language Name</td> </select>
<td class="value"><input type="text" v-model="current.languageLocalName" class="short"></td> </td>
</tr> </tr>
<tr v-if="meta.currentLoaded"> </table>
<td class="label">Font table to use</td>
<td class="value"> <div v-if="def.messages && referent.messages && current.messages">
<select v-model="current.cyrillicGlyphs" v-on:change="current.cyrillicGlyphs = current.cyrillicGlyphs=='true'">
<option value="false">Latin Extended</option> <div class="footer">
<option value="true">Cyrillic Glyphs</option> <input type="button" value="Save" onclick="save()">
</select> <input type="button" value="View" onclick="view()">
</td> </div>
</tr>
</table> <div v-if="Object.keys(obsolete).length > 0">
<h2>Obsolete</h2>
<div v-if="def.messages && referent.messages && current.messages"> <table class="data">
<tr v-for="entry in obsolete">
<div class="footer"> <td class="label"><div class="stringId">{{entry.id}}</div></td>
<input type="button" value="Save" onclick="save()"> <td class="value"><div class="ref">{{entry.value}}</div></td>
<input type="button" value="View" onclick="view()"> </tr>
</div> </table>
</div>
<div v-if="Object.keys(obsolete).length > 0">
<h2>Obsolete</h2> <h2>Messages and Strings</h2>
<table class="data"> <table class="data">
<tr v-for="entry in obsolete"> <tr v-for="message in def.messages" v-bind:class="validateInput(current.messages, message.id)">
<td class="label"><div class="stringId">{{entry.id}}</div></td> <td class="label"><div class="stringId">{{message.id}}</div></td>
<td class="value"><div class="ref">{{entry.value}}</div></td> <td class="value">
</tr> <div class="constraint">{{constraintString(message)}}</div>
</table> <div class="ref">{{referent.messages[message.id]}}</div>
</div> <div class="note" v-if="message.note">{{message.note}}</div>
<div class="tran"><input :id="'in_'+message.id" type="text" v-model="current.messages[message.id]" v-bind:class="{unchanged : current.messages[message.id] == referent.messages[message.id], empty : current.messages[message.id]==''}"></div>
<h2>Messages and Strings</h2> </td>
<table class="data"> </tr>
<tr v-for="message in def.messages" v-bind:class="validateInput(current.messages, message.id)"> </table>
<td class="label"><div class="stringId">{{message.id}}</div></td>
<td class="value"> <h2>Characters</h2>
<div class="constraint">{{constraintString(message)}}</div> <table class="data">
<div class="ref">{{referent.messages[message.id]}}</div> <tr v-for="char in def.characters" v-bind:class="validateInput(current.characters, char.id)">
<div class="note" v-if="message.note">{{message.note}}</div> <td class="label"><div class="stringId">{{char.id}}</div></td>
<div class="tran"><input :id="'in_'+message.id" type="text" v-model="current.messages[message.id]" v-bind:class="{unchanged : current.messages[message.id] == referent.messages[message.id], empty : current.messages[message.id]==''}"></div> <td class="value">
</td> <div class="constraint">{{constraintString(char)}}</div>
</tr> <div class="ref">{{referent.characters[char.id]}}</div>
</table> <div class="tran"><input type="text" v-model="current.characters[char.id]" v-bind:class="{unchanged : current.characters[char.id] == referent.characters[char.id], empty : current.characters[char.id].length != 1}"></div>
</td>
<h2>Characters</h2> </tr>
<table class="data"> </table>
<tr v-for="char in def.characters" v-bind:class="validateInput(current.characters, char.id)">
<td class="label"><div class="stringId">{{char.id}}</div></td> <h2>Menu Groups</h2>
<td class="value"> <table class="data">
<div class="constraint">{{constraintString(char)}}</div> <tr v-for="menu in def.menuGroups" v-bind:class="validateInput(current.menuGroups, menu.id, 2)">
<div class="ref">{{referent.characters[char.id]}}</div> <td class="label"><div class="stringId">{{menu.id}}</div></td>
<div class="tran"><input type="text" v-model="current.characters[char.id]" v-bind:class="{unchanged : current.characters[char.id] == referent.characters[char.id], empty : current.characters[char.id].length != 1}"></div> <td class="value">
</td> <div class="label">Menu Name</div>
</tr> <div class="constraint">{{constraintString(menu)}}</div>
</table> <div class="ref">{{referent.menuGroups[menu.id].text2}}</div>
<div class="tran" v-bind:class="{unchanged : current.menuGroups[menu.id].text2[0] == referent.menuGroups[menu.id].text2[0] && current.menuGroups[menu.id].text2[1] == referent.menuGroups[menu.id].text2[1], empty : current.menuGroups[menu.id].text2[0] == '' || current.menuGroups[menu.id].text2[1] == ''}"><input type="text" v-model="current.menuGroups[menu.id].text2[0]"><input type="text" v-model="current.menuGroups[menu.id].text2[1]"></div>
<h2>Menu Groups</h2> <div class="label">Description</div>
<table class="data"> <div class="ref">{{referent.menuGroups[menu.id].desc}}</div>
<tr v-for="menu in def.menuGroups" v-bind:class="validateInput(current.menuGroups, menu.id, 2)"> <div class="tran"><input type="text" v-model="current.menuGroups[menu.id].desc" v-bind:class="{unchanged : current.menuGroups[menu.id].desc == referent.menuGroups[menu.id].desc, empty : current.menuGroups[menu.id].desc == ''}"></div>
<td class="label"><div class="stringId">{{menu.id}}</div></td> </td>
<td class="value"> </tr>
<div class="label">Menu Name</div> </table>
<div class="constraint">{{constraintString(menu)}}</div>
<div class="ref">{{referent.menuGroups[menu.id].text2}}</div> <h2>Menu Options</h2>
<div class="tran" v-bind:class="{unchanged : current.menuGroups[menu.id].text2[0] == referent.menuGroups[menu.id].text2[0] && current.menuGroups[menu.id].text2[1] == referent.menuGroups[menu.id].text2[1], empty : current.menuGroups[menu.id].text2[0] == '' || current.menuGroups[menu.id].text2[1] == ''}"><input type="text" v-model="current.menuGroups[menu.id].text2[0]"><input type="text" v-model="current.menuGroups[menu.id].text2[1]"></div> <table class="data">
<div class="label">Description</div> <tr v-for="menu in def.menuOptions" v-bind:class="validateInput(current.menuOptions, menu.id, 2)">
<div class="ref">{{referent.menuGroups[menu.id].desc}}</div> <td class="label"><div class="stringId">{{menu.id}}</div></td>
<div class="tran"><input type="text" v-model="current.menuGroups[menu.id].desc" v-bind:class="{unchanged : current.menuGroups[menu.id].desc == referent.menuGroups[menu.id].desc, empty : current.menuGroups[menu.id].desc == ''}"></div> <td class="value">
</td> <div v-bind:class="{hidden : false}">
</tr> <div class="label">Menu Name (Double-Line)</div>
</table> <div class="constraint">{{constraintString(menu)}}</div>
<div class="ref">{{referent.menuOptions[menu.id].text2}}</div>
<h2>Menu Options</h2> <div class="tran" v-bind:class="{unchanged : current.menuOptions[menu.id].text2[0] == referent.menuOptions[menu.id].text2[0] && current.menuOptions[menu.id].text2[1] == referent.menuOptions[menu.id].text2[1], empty : current.menuOptions[menu.id].text2[0] == '' || current.menuOptions[menu.id].text2[1] == ''}"><input type="text" v-model="current.menuOptions[menu.id].text2[0]"><input type="text" v-model="current.menuOptions[menu.id].text2[1]"></div>
<table class="data"> </div>
<tr> <div class="label">Description</div>
<td class="label">Menu Type</td> <div class="ref">{{referent.menuOptions[menu.id].desc}}</div>
<td class="value"> <div class="tran"><input type="text" v-model="current.menuOptions[menu.id].desc" v-bind:class="{unchanged : current.menuOptions[menu.id].desc == referent.menuOptions[menu.id].desc, empty : current.menuOptions[menu.id].desc == ''}"></div>
<select v-model="current.menuDouble" v-on:change="current.menuDouble = current.menuDouble=='true'"> </td>
<option value="false">Single-Line</option> </tr>
<option value="true">Double-Line</option> </table>
</select>
</td> <div class="footer">
</tr> <input type="button" value="Save" onclick="save()">
<tr v-for="menu in def.menuOptions" v-bind:class="validateInput(current.menuOptions, menu.id, (current.menuDouble ? 2 : 1))"> <input type="button" value="View" onclick="view()">
<td class="label"><div class="stringId">{{menu.id}}</div></td> </div>
<td class="value"> </div>
<div v-bind:class="{hidden : current.menuDouble}"> </div>
<div class="label">Menu Name (Single-Line)</div> </body>
<div class="constraint">{{constraintString(menu, current.menuDouble)}}</div>
<div class="ref">{{referent.menuOptions[menu.id].text}}</div>
<div class="tran"><input type="text" v-model="current.menuOptions[menu.id].text" v-bind:class="{unchanged : current.menuOptions[menu.id].text == referent.menuOptions[menu.id].text, empty : current.menuOptions[menu.id].text == ''}"></div>
</div>
<div v-bind:class="{hidden : !current.menuDouble}">
<div class="label">Menu Name (Double-Line)</div>
<div class="constraint">{{constraintString(menu, current.menuDouble)}}</div>
<div class="ref">{{referent.menuOptions[menu.id].text2}}</div>
<div class="tran" v-bind:class="{unchanged : current.menuOptions[menu.id].text2[0] == referent.menuOptions[menu.id].text2[0] && current.menuOptions[menu.id].text2[1] == referent.menuOptions[menu.id].text2[1], empty : current.menuOptions[menu.id].text2[0] == '' || current.menuOptions[menu.id].text2[1] == ''}"><input type="text" v-model="current.menuOptions[menu.id].text2[0]"><input type="text" v-model="current.menuOptions[menu.id].text2[1]"></div>
</div>
<div class="label">Description</div>
<div class="ref">{{referent.menuOptions[menu.id].desc}}</div>
<div class="tran"><input type="text" v-model="current.menuOptions[menu.id].desc" v-bind:class="{unchanged : current.menuOptions[menu.id].desc == referent.menuOptions[menu.id].desc, empty : current.menuOptions[menu.id].desc == ''}"></div>
</td>
</tr>
</table>
<div class="footer">
<input type="button" value="Save" onclick="save()">
<input type="button" value="View" onclick="view()">
</div>
</div>
</div>
</body>
</html> </html>

View File

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

View File

@@ -167,11 +167,8 @@ def getLetterCounts(defs, lang):
obj = lang['menuOptions'] obj = lang['menuOptions']
for mod in defs['menuOptions']: for mod in defs['menuOptions']:
eid = mod['id'] eid = mod['id']
if lang['menuDouble']: textList.append(obj[eid]['text2'][0])
textList.append(obj[eid]['text2'][0]) textList.append(obj[eid]['text2'][1])
textList.append(obj[eid]['text2'][1])
else:
textList.append(obj[eid]['text'])
obj = lang['menuGroups'] obj = lang['menuGroups']
for mod in defs['menuGroups']: for mod in defs['menuGroups']:
@@ -369,14 +366,6 @@ def writeLanguage(languageCode, defs, f):
c) + "\"," + "//{} \n".format(c))) c) + "\"," + "//{} \n".format(c)))
f.write(to_unicode("};\n\n")) f.write(to_unicode("};\n\n"))
# ----- Menu Options
# Menu type
f.write(
to_unicode(
"const enum ShortNameType SettingsShortNameType = SHORT_NAME_" +
("DOUBLE" if lang['menuDouble'] else "SINGLE") + "_LINE;\n"))
# ----- Writing SettingsDescriptions # ----- Writing SettingsDescriptions
obj = lang['menuOptions'] obj = lang['menuOptions']
f.write(to_unicode("const char* SettingsShortNames[][2] = {\n")) f.write(to_unicode("const char* SettingsShortNames[][2] = {\n"))
@@ -388,19 +377,14 @@ def writeLanguage(languageCode, defs, f):
if 'feature' in mod: if 'feature' in mod:
f.write(to_unicode("#ifdef " + mod['feature'] + "\n")) f.write(to_unicode("#ifdef " + mod['feature'] + "\n"))
f.write(to_unicode(" /* ["+"{:02d}".format(index)+"] " + eid.ljust(maxLen)[:maxLen] + " */ ")) f.write(to_unicode(" /* ["+"{:02d}".format(index)+"] " + eid.ljust(maxLen)[:maxLen] + " */ "))
if lang['menuDouble']: f.write(
f.write( to_unicode(
to_unicode( "{ \"" +
"{ \"" + convStr(symbolConversionTable, (obj[eid]['text2'][0])) +
convStr(symbolConversionTable, (obj[eid]['text2'][0])) + "\", \"" +
"\", \"" + convStr(symbolConversionTable, (obj[eid]['text2'][1])) +
convStr(symbolConversionTable, (obj[eid]['text2'][1])) + "\" }," + "//{} \n".format(obj[eid]['text2'])))
"\" }," + "//{} \n".format(obj[eid]['text2'])))
else:
f.write(
to_unicode("{ \"" +
convStr(symbolConversionTable, (obj[eid]['text'])) +
"\" }," + "//{} \n".format(obj[eid]['text'])))
if 'feature' in mod: if 'feature' in mod:
f.write(to_unicode("#endif\n")) f.write(to_unicode("#endif\n"))
index = index + 1 index = index + 1

View File

@@ -9,16 +9,12 @@
#define TRANSLATION_H_ #define TRANSLATION_H_
#include "unit.h" #include "unit.h"
#include "stdint.h" #include "stdint.h"
enum ShortNameType {
SHORT_NAME_SINGLE_LINE = 1, SHORT_NAME_DOUBLE_LINE = 2,
};
extern const uint8_t USER_FONT_12[]; extern const uint8_t USER_FONT_12[];
extern const uint8_t USER_FONT_6x8[]; extern const uint8_t USER_FONT_6x8[];
/* /*
* When SettingsShortNameType is SHORT_NAME_SINGLE_LINE * When SettingsShortNameType is SHORT_NAME_SINGLE_LINE
* use SettingsShortNames as SettingsShortNames[16][1].. second column undefined * use SettingsShortNames as SettingsShortNames[16][1].. second column undefined
*/ */
extern const enum ShortNameType SettingsShortNameType;
extern const char *SettingsShortNames[28][2]; extern const char *SettingsShortNames[28][2];
extern const char *SettingsDescriptions[28]; extern const char *SettingsDescriptions[28];
extern const char *SettingsMenuEntries[4]; extern const char *SettingsMenuEntries[4];

View File

@@ -230,12 +230,6 @@ const menuitem advancedMenu[] = {
{ NULL, NULL, NULL } // end of menu marker. DO NOT REMOVE { NULL, NULL, NULL } // end of menu marker. DO NOT REMOVE
}; };
static void printShortDescriptionSingleLine(uint32_t shortDescIndex) {
OLED::setFont(0);
OLED::setCharCursor(0, 0);
OLED::print(SettingsShortNames[shortDescIndex][0]);
}
static void printShortDescriptionDoubleLine(uint32_t shortDescIndex) { static void printShortDescriptionDoubleLine(uint32_t shortDescIndex) {
OLED::setFont(1); OLED::setFont(1);
OLED::setCharCursor(0, 0); OLED::setCharCursor(0, 0);
@@ -254,11 +248,7 @@ static void printShortDescriptionDoubleLine(uint32_t shortDescIndex) {
static void printShortDescription(uint32_t shortDescIndex, static void printShortDescription(uint32_t shortDescIndex,
uint16_t cursorCharPosition) { uint16_t cursorCharPosition) {
// print short description (default single line, explicit double line) // print short description (default single line, explicit double line)
if (SettingsShortNameType == SHORT_NAME_DOUBLE_LINE) { printShortDescriptionDoubleLine(shortDescIndex);
printShortDescriptionDoubleLine(shortDescIndex);
} else {
printShortDescriptionSingleLine(shortDescIndex);
}
// prepare cursor for value // prepare cursor for value
OLED::setFont(0); OLED::setFont(0);