Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
238 lines (219 sloc)
10.1 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<meta charset="UTF-8"> | |
<link rel="stylesheet" href="../../css/spectre.min.css"> | |
</head> | |
<body> | |
<p>Please choose a language from the following list:</p> | |
<div class="form-group"> | |
<select id="languages" class="form-select"> | |
</select> | |
</div> | |
<div class="form-group"> | |
<input id="translations" type="checkbox" /> <label for="translations">Add common language translations like "Yes", "No", "On", "Off"<br/><i>(Not recommended. For translations use the option under <code>More...</code> in the app loader.</i></label> | |
</div> | |
<p>Then click <button id="upload" class="btn btn-primary">Upload</button></p> | |
<script src="../../core/lib/customize.js"></script> | |
<script src="../../core/js/utils.js"></script> | |
<script src="locales.js" charset="utf-8"></script> | |
<script> | |
/* | |
eg. the built-in en_GB is: | |
exports = { name : "en_GB", currencySym:"£", | |
translate : str=>str, // as-is | |
date : (d,short) => short?("0"+d.getDate()).substr(-2)+"/"+("0"+(d.getMonth()+1)).substr(-2)+"/"+d.getFullYear():d.toString().substr(4,11), // Date to "Feb 28 2020" or "28/02/2020"(short) | |
time : (d,short) => { // Date to "4:15.28 pm" or "15:42"(short) | |
if (short) | |
return d.toString().substr(16,5); | |
else { | |
var h = d.getHours(), m = d.getMinutes(), r = "am"; | |
if (h==0) { h=12; } | |
else if (h>=12) { | |
if (h>12) h-=12; | |
r = "pm"; | |
} | |
return (" "+h).substr(-2)+":"+("0"+m).substr(-2)+"."+("0"+d.getSeconds()).substr(-2)+" "+r; | |
} | |
}, | |
dow : (d,short) => short?d.toString().substr(0,3):"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday".split(",")[d.getDay()], // Date to "Monday" or "Mon"(short) | |
month : (d,short) => short?d.toString().substr(4,3):"January,February,March,April,May,June,July,August,September,October,November,December".split(",")[d.getMonth()], // Date to "February" or "Feb"(short) | |
number : n => n.toString(), // more fancy? | |
currency : n => "£"+n.toFixed(2), // number to "£1.00" | |
distance : m => (m<1000)?Math.round(m)+"m":Math.round(m/160.934)/10+"mi", // meters to "123m" or "1.2mi" depending on size | |
speed : s => Math.round(s*0.621371)+"mph",// kph to "123mph" | |
temp : t => Math.round(t)+"'C" // degrees C to degrees C | |
meridian: d => (d.getHours() <= 12) ? "am":"pm", | |
}; | |
*/ | |
function codePageLookup(lang, codePage, ch) { | |
var chCode = ch.charCodeAt(); | |
if (chCode>=32 && chCode<128) return ch; // ASCII - copy it | |
// not normal ASCII | |
var n; // default is a space | |
if (codePage.map.indexOf(ch)>=0) // look up in char map, escape that | |
n = 128+codePage.map.indexOf(ch); | |
/*else if (chCode<256) // it's non-ascii, but <256 - just escape it | |
n = chCode;*/ | |
else { | |
if (CODEPAGE_CONVERSIONS[ch]) return CODEPAGE_CONVERSIONS[ch]; | |
console.error(`Locale ${lang}: Character ${ch} (${chCode}) is not in Code Page ${codePage.name}`); | |
return undefined; | |
} | |
// escape the char | |
return '\\x'+(n+256).toString(16).slice(-2); | |
} | |
// do some sanity checks | |
Object.keys(locales).forEach(function(localeName) { | |
var locale = locales[localeName]; | |
if (locale.trans && !locale.trans.on) console.error(localeName+": If translations are provided, 'on' *must* be included"); | |
if (distanceUnits[locale.distance[0]]===undefined) console.error(localeName+": Unknown distance unit "+locale.distance[0]); | |
if (distanceUnits[locale.distance[1]]===undefined) console.error(localeName+": Unknown distance unit "+locale.distance[1]); | |
if (speedUnits[locale.speed]===undefined) console.error(localeName+": Unknown speed unit "+locale.speed); | |
if (locale.temperature!='°C' && locale.temperature!='°F') | |
console.error(localeName+": Unknown temperature unit "+locale.temperature); | |
// Now check that codepage is ok and all chars in translation are in that codepage | |
const codePageName = "ISO8859-1"; | |
if (locale.codePage) codePageName = locale.codePage; | |
const codePage = codePages[codePageName]; | |
if (codePage===undefined) console.error(localeName+": Unknown codePage "+codePageName); | |
function checkChars(v,path) { | |
if ("object"==typeof v) | |
Object.keys(v).forEach(k=>checkChars(v[k], path+"."+k)); | |
else if ("string"==typeof v) | |
for (var i=0;i<v.length;i++) | |
if (codePageLookup(localeName, codePage, v[i])===undefined) | |
console.error(` ... in ${path}[${i}]`); | |
} | |
checkChars(locale,localeName); | |
}); | |
var languageSelector = document.getElementById("languages"); | |
languageSelector.innerHTML = Object.keys(locales).map(l=>{ | |
var localeParts = l.split("_"); // en_GB -> ["en","GB"] | |
var icon = ""; | |
// If we have a 2 char ISO country code, use it to get the unicode flag | |
if (localeParts[1] && localeParts[1].length==2) | |
icon = localeParts[1].toUpperCase().replace(/./g, char => String.fromCodePoint(char.charCodeAt(0)+127397) )+" "; | |
if (localeParts[1]=="NAV") | |
icon = "⛵✈️ "; | |
return `<option value="${l}">${icon}${l}</option>` | |
}).join("\n"); | |
document.getElementById("upload").addEventListener("click", function() { | |
const lang = languageSelector.options[languageSelector.selectedIndex].value; | |
console.log(`Language ${lang}`); | |
const translations = document.getElementById('translations').checked; | |
console.log(`Translations: ${translations}`); | |
const locale = locales[lang]; | |
if (!locale) { | |
alert(`Language ${lang} not found!`); | |
return; | |
} | |
if (!translations) | |
locale.trans = null; | |
const codePageName = "ISO8859-1"; | |
if (locale.codePage) | |
codePageName = locale.codePage; | |
const codePage = codePages[codePageName]; | |
if (!codePage) { | |
alert(`Code Page ${codePageName} not found!`); | |
return; | |
} | |
// Convert object to JSON, with codepage | |
function js(x) { | |
// do nortmal conversion | |
x = JSON.stringify(x); | |
/* assume any out of bounds character is going to | |
be inside a quoted string */ | |
var s = ''; | |
for (var i=0;i<x.length;i++) { | |
var ch = codePageLookup(lang, codePage, x[i]); | |
s += (ch===undefined) ? "?" : ch; | |
} | |
return s; | |
} | |
function unitConv(x) { | |
return x === 1 ? 'n' : 'n/' + x | |
} | |
var replaceList = { | |
"%Y": "d.getFullYear()", | |
"%y": "(d.getFullYear().toString()).slice(-2)", | |
"%m": "('0'+(d.getMonth()+1).toString()).slice(-2)", | |
"%-m": "d.getMonth()+1", | |
"%d": "('0'+d.getDate()).slice(-2)", | |
"%-d": "d.getDate()", | |
"%HH": "('0'+getHours(d)).slice(-2)", | |
"%MM": "('0'+d.getMinutes()).slice(-2)", | |
"%SS": "('0'+d.getSeconds()).slice(-2)", | |
"%A": `${js(locale.day)}.split(',')[d.getDay()]`, | |
"%a": `${js(locale.abday)}.split(',')[d.getDay()]`, | |
"%B": `${js(locale.month)}.split(',')[d.getMonth()]`, | |
"%b": `${js(locale.abmonth)}.split(',')[d.getMonth()]`, | |
"%p": `d.getHours()<12?${js(locale.ampm[0].toUpperCase())}:${js(locale.ampm[1].toUpperCase())}`, | |
"%P": `d.getHours()<12?${js(locale.ampm[0].toLowerCase())}:${js(locale.ampm[1].toLowerCase())}` | |
}; | |
var timeN = locale.timePattern[0]; | |
var timeS = locale.timePattern[1]; | |
var dateN = locale.datePattern[0]; | |
var dateS = locale.datePattern[1]; | |
Object.keys(replaceList).forEach(e => { | |
timeN = timeN.replace(e,"${"+replaceList[e]+"}"); | |
timeS = timeS.replace(e,"${"+replaceList[e]+"}"); | |
dateN = dateN.replace(e,"${"+replaceList[e]+"}"); | |
dateS = dateS.replace(e,"${"+replaceList[e]+"}"); | |
}); | |
var currency = locale.currency_first ? | |
`${js(locale.currency_symbol)} + exports.number(n)`: | |
`exports.number(n) + ${js(locale.currency_symbol)}`; | |
var temperature = locale.temperature=='°F' ? '(t*9/5)+32' : 't'; | |
var localeModule = ` | |
function round(n, dp) { | |
if (dp===undefined) dp=0; | |
var p = Math.min(dp,dp - Math.floor(Math.log(n)/Math.log(10))); | |
return n.toFixed(p); | |
} | |
var is12; | |
function getHours(d) { | |
var h = d.getHours(); | |
if (is12===undefined) is12 = (require('Storage').readJSON('setting.json',1)||{})["12hour"]; | |
if (!is12) return h; | |
return (h%12==0) ? 12 : h%12; | |
} | |
exports = { | |
name: ${js(locale.lang)}, | |
currencySym: ${js(locale.currency_symbol)}, | |
dow: (d,short) => ${js(locale.day + ',' + locale.abday)}.split(',')[d.getDay() + (short ? 7 : 0)], | |
month: (d,short) => ${js(locale.month + ',' + locale.abmonth)}.split(',')[d.getMonth() + (short ? 12 : 0)], | |
number: (n, dec) => { | |
if (dec == null) dec = 2; | |
var w = n.toFixed(dec), | |
k = w|0, | |
b = n < 0 ? 1 : 0, | |
u = Math.abs(w-k), | |
d = (''+u.toFixed(dec)).substr(2, dec), | |
s = ''+k, | |
i = s.length, | |
r = ''; | |
while ((i-=3) > b) { | |
r = '${locale.thousands_sep}' + s.substr(i, 3) + r; | |
} | |
return s.substr(0, i + 3) + r + (d ? '${locale.decimal_point}' + d: ''); | |
}, | |
currency: n => ${currency}, | |
distance: (n,dp) => n < ${distanceUnits[locale.distance[1]]} ? round(${unitConv(distanceUnits[locale.distance[0]])},dp) + ${js(locale.distance[0])} : round(${unitConv(distanceUnits[locale.distance[1]])},dp) + ${js(locale.distance[1])}, | |
speed: (n,dp) => round(${unitConv(speedUnits[locale.speed])},dp) + ${js(locale.speed)}, | |
temp: (t,dp) => round(${temperature},dp) + ${js(locale.temperature)}, | |
translate: s => ${locale.trans?`{var t=${js(locale.trans)};s=''+s;return t[s]||t[s.toLowerCase()]||s;}`:`s`}, | |
date: (d,short) => short ? \`${dateS}\` : \`${dateN}\`, | |
time: (d,short) => short ? \`${timeS}\` : \`${timeN}\`, | |
meridian: d => d.getHours() < 12 ? ${js(locale.ampm[0])}:${js(locale.ampm[1])}, | |
}; | |
`.trim(); | |
console.log("Locale Module is:",localeModule); | |
sendCustomizedApp({ | |
storage:[ | |
{name:"locale", url:"locale.js", content:localeModule} | |
] | |
}); | |
}); | |
</script> | |
</body> | |
</html> |