Add menu option for language translations (fix #136)

pull/1315/head
Gordon Williams 2022-01-18 16:21:30 +00:00
parent 5a4f15435f
commit 85be6af02e
4 changed files with 103 additions and 14 deletions

View File

@ -2,15 +2,20 @@
/* Scans for strings that may be in English in each app, and /* Scans for strings that may be in English in each app, and
outputs a list of strings that have been found. outputs a list of strings that have been found.
Early work towards internationalisation. See https://github.com/espruino/BangleApps/issues/1311
See https://github.com/espruino/BangleApps/issues/136
*/ */
var IGNORE_STRINGS = [
"5x5",
"5x9Numeric7Seg",
"Vector"
];
var BASEDIR = __dirname+"/../"; var BASEDIR = __dirname+"/../";
Espruino = require(BASEDIR+"core/lib/espruinotools.js"); Espruino = require(BASEDIR+"core/lib/espruinotools.js");
var fs = require("fs"); var fs = require("fs");
var APPSDIR = BASEDIR+"apps/"; var APPSDIR = BASEDIR+"apps/";
function ERROR(s) { function ERROR(s) {
console.error("ERROR: "+s); console.error("ERROR: "+s);
process.exit(1); process.exit(1);
@ -18,6 +23,9 @@ function ERROR(s) {
function WARN(s) { function WARN(s) {
console.log("Warning: "+s); console.log("Warning: "+s);
} }
function log(s) {
console.log(s);
}
var appsFile, apps; var appsFile, apps;
try { try {
@ -39,31 +47,65 @@ function isNotString(s) {
if (s.endsWith(".json") || s.endsWith(".img")) return true; // a filename if (s.endsWith(".json") || s.endsWith(".img")) return true; // a filename
if (s.endsWith("=")) return true; // probably base64 if (s.endsWith("=")) return true; // probably base64
if (s.startsWith("BTN")) return true; // button name if (s.startsWith("BTN")) return true; // button name
if (IGNORE_STRINGS.includes(s)) return true; // one we know to ignore
return false; return false;
} }
var textStrings = []; // A string that *could* be translated?
var untranslatedStrings = [];
// Strings that are marked with 'LANG'
var translatedStrings = [];
console.log("Scanning..."); console.log("Scanning apps...");
apps.forEach((app,appIdx) => { apps.forEach((app,appIdx) => {
var appDir = APPSDIR+app.id+"/"; var appDir = APPSDIR+app.id+"/";
app.storage.forEach((file) => { app.storage.forEach((file) => {
if (!file.url || !file.name.endsWith(".js")) return; if (!file.url || !file.name.endsWith(".js")) return;
var fileContents = fs.readFileSync(appDir+file.url).toString(); var fileContents = fs.readFileSync(appDir+file.url).toString();
var lex = Espruino.Core.Utils.getLexer(fileContents); var lex = Espruino.Core.Utils.getLexer(fileContents);
var lastIdx = 0;
var tok = lex.next(); var tok = lex.next();
while (tok!==undefined) { while (tok!==undefined) {
var previousString = fileContents.substring(lastIdx, tok.startIdx);
if (tok.type=="STRING") { if (tok.type=="STRING") {
if (!isNotString(tok.value)) { if (previousString.includes("/*LANG*/")) { // translated!
//console.log(tok.str); if (!translatedStrings.includes(tok.value))
if (!textStrings.includes(tok.value)) translatedStrings.push(tok.value);
textStrings.push(tok.value); } else { // untranslated - potential to translate?
if (!isNotString(tok.value)) {
if (!untranslatedStrings.includes(tok.value))
untranslatedStrings.push(tok.value);
}
} }
} }
lastIdx = tok.endIdx;
tok = lex.next(); tok = lex.next();
} }
}); });
}); });
console.log("Done"); untranslatedStrings.sort();
textStrings.sort(); translatedStrings.sort();
console.log(textStrings.join("\n"));
var report = "";
/* // too many! don't output these
log("Possible English Strings that could be translated");
log("=================================================================");
log("");
log("Add these to IGNORE_STRINGS if the don't make sense...");
log("");
log(untranslatedStrings.map(s=>JSON.stringify(s)).join(",\n"));*/
log("");
var languages = JSON.parse(fs.readFileSync(BASEDIR+"/lang/index.json").toString());
languages.forEach(language => {
console.log("Scanning "+language.code);
log(language.code);
log("==========");
var translations = JSON.parse(fs.readFileSync(BASEDIR+"/lang/"+language.url).toString());
translatedStrings.forEach(str => {
if (!translations.GLOBAL[str])
console.log(`Missing translation for ${JSON.stringify(str)}`);
});
log("");
});
console.log("Done.");

View File

@ -141,6 +141,11 @@
<input type="checkbox" id="settings-settime"> <input type="checkbox" id="settings-settime">
<i class="form-icon"></i> Always update time when we connect <i class="form-icon"></i> Always update time when we connect
</label> </label>
<div class="form-group">
<select class="form-select form-inline" id="settings-lang" style="width: 10em">
<option value="">None (English)</option>
</select>&nbsp;&nbsp;<span>Translations (<a href="https://github.com/espruino/BangleApps/issues/1311" target="_blank">BETA - more info</a>)</span>
</div>
<button class="btn" id="defaultsettings">Default settings</button> <button class="btn" id="defaultsettings">Default settings</button>
</div> </div>
</div> </div>

View File

@ -1,9 +1,9 @@
{ {
"//":"British English language translations - the default strings in apps are all english anyway, so no need to have translations for most things", "//":"British English language translations - the default strings in apps are all english anyway, so no need to have translations for most things",
"GLOBAL": { "GLOBAL": {
"//":"Translations that apply for all apps", "//":"Translations that apply for all apps"
}, },
"alarm": { "alarm": {
"//":"App-specific overrides", "//":"App-specific overrides"
} }
} }

View File

@ -164,6 +164,48 @@ window.addEventListener('load', (event) => {
showToast("App Install failed, "+err,"error"); showToast("App Install failed, "+err,"error");
}); });
}); });
// Load language list
httpGet("lang/index.json").then(languagesJSON=>{
var languages;
try {
languages = JSON.parse(languagesJSON);
} catch(e) {
console.error("lang/index.json Corrupted", e);
}
function reloadLanguage() {
LANGUAGE = undefined;
if (SETTINGS.language) {
var language = languages.find(l=>l.code==SETTINGS.language);
if (language) {
var langURL = "lang/"+language.url;
httpGet(langURL).then(languageJSON=>{
try {
LANGUAGE = JSON.parse(languageJSON);
console.log(`${langURL} loaded successfully`);
} catch(e) {
console.error(`${langURL} Corrupted`, e);
}
});
} else {
console.error(`Language code ${JSON.stringify(SETTINGS.language)} not found in lang/index.json`);
}
}
}
var selectLang = document.getElementById("settings-lang");
console.log(languages);
languages.forEach(lang => {
selectLang.innerHTML += `<option value="${lang.code}" ${SETTINGS.language==lang.code?"selected":""}>${lang.name} (${lang.code})</option>`;
});
selectLang.addEventListener("change",event=>{
SETTINGS.language = event.target.value;
reloadLanguage();
saveSettings();
});
reloadLanguage();
});
}); });
function onAppJSONLoaded() { function onAppJSONLoaded() {