diff --git a/apps/locale/ChangeLog b/apps/locale/ChangeLog index 982103cd16..0800573214 100644 --- a/apps/locale/ChangeLog +++ b/apps/locale/ChangeLog @@ -21,3 +21,4 @@ 0.17: Fix regression where long month names were 'undefined' (fix #1641) 0.18: Fix lint warnings, change anv->janv for fr_BE and fr_CH 0.19: Deprecate currency information +0.20: Use locale data from CLDR diff --git a/apps/locale/locales-meta.js b/apps/locale/locales-meta.js new file mode 100644 index 0000000000..6c98ace9f6 --- /dev/null +++ b/apps/locale/locales-meta.js @@ -0,0 +1,725 @@ +/* jshint esversion: 6 */ + +/** + * How many meters per X? + */ +exports.distanceUnits = { + m: 1, + ft: 0.3048, + yd: 0.9144, + mi: 1609.34, + km: 1000, + kmi: 1000, + nm: 1852, +}; + +/** + * How many kph per X? + */ +exports.speedUnits = { + kmh: 1, + kph: 1, + "km/h": 1, + kmt: 1, + "km/t": 1, + mph: 1.60934, + kts: 1.852, +}; + +/** + * For a codepage, 'map' is a map of char codes 128 and above. + * Where there is no character, just use '.' + */ +exports.codePages = { + "ISO8859-1": { + name: "ISO8859-1", + map: ` + €.‚ƒ„…†‡ˆ‰Š‹Œ.Ž. + .‘’“”•–—˜™š›œ.žŸ + .¡¢£¤¥¦§¨©ª«¬.®¯ + °±²³´µ¶·¸¹º»¼½¾¿ + ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ + ÐÑÒÓÔÕÖרÙÚÛÜÝÞß + àáâãäåæçèéêëìíîï + ðñòóôõö÷øùúûüýþÿ + `.replace(/[ \n]/g, ""), + }, +}; + +// charFallbacks is now in core/js/utils.js as CODEPAGE_CONVERSIONS + +/** + * timePattern / datePattern: + * + * %Y year, all digits (2004, 2020, 32100) + * %y year, last two digits (04, 20, 00) + * %m month, two digits (01, 12) + * %-m month, one or two digits (1, 12) + * %d day of month, two digits (01, 26) + * %-d day of month, one or two digits (1, 26) + * + * %A locale's weekday name, full (e.g., Sunday) + * %a locale's weekday name, abbreviated (e.g., Sun) + * %B locale's month name, full (e.g., January) + * %b locale's month name, abbreviated (e.g., Jan) + * + * %HH hours, two digits (00..23) + * %MM minutes, two digits (00..59) + * %SS seconds, two digits (00..60) + * %P locale's equivalent of either am or pm, lowercase + * %p locale's equivalent of either AM or PM, uppercase + */ + +/** + * A list of all supported locales. + * + * To add a new locale, you must manually define: + * - `calendar` (try 'gregory') + * - `numberingSystem` (try 'latn') + * All other values are autogenerated but you can override them here if you disagree with the autogenerated result. + * If your locale is unsupported, you can define a `fallbackLang` that will be used for default values. + * + * If your language contains fancy unicode characters, they are probably not supported by Espruino. + * In that case, you can define a fallback character in `character_fallback_map`. + * + * To see the final output of the autogenerator, run: + * node bin/create-locales-js.mjs LOCALECODE + * + * LOCALECODE is the locale that you want to see the output for, e.g. "en_US". + * + * Limits: + * decimal_point: must be 1 char + * thousands_sep: must be 1 char + * speed: must be <5 chars + * distance: must be <4 chars, ideally 2 + * temperature: must be <4 chars, ideally 2 + * am/pm: must be <4 chars, ideally 2 + * long time: must be <9 chars + * short time: must be <6 char + * long date: must be <15 chars, ideally <13 + * short date: must be <12 chars, ideally <10 + * abmonth: short months (must be <5 chars, ideally 3) + * month: normal month names (current longest is 11 chars) + * abday: short days (must be <5 chars, ideally 3) + * day: normal day names (current longest is 13 chars) + */ +exports.locales = { + en_GB: { + // this is default + calendar: "gregory", + numberingSystem: "latn", + speed: "mph", + distance: { 0: "yd", 1: "mi" }, + datePattern: { 0: "%b %d %Y", 1: "%d/%m/%Y" }, + abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", + }, + en_US: { + notes: "USA with MM/DD/YY date", + calendar: "gregory", + numberingSystem: "latn", + speed: "mph", + distance: { 0: "ft", 1: "mi" }, + temperature: "°F", + }, + "en_US 2": { + notes: "USA with YYYY-MM-DD date", + calendar: "gregory", + numberingSystem: "latn", + speed: "mph", + distance: { 0: "ft", 1: "mi" }, + temperature: "°F", + datePattern: { 0: "%b %d, %Y", 1: "%Y-%m-%d" }, + }, + en_IN: { + calendar: "gregory", + numberingSystem: "latn", + speed: "kmh", + abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", + }, + en_IE: { + calendar: "gregory", + numberingSystem: "latn", + speed: "kmh", + abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", + }, + en_NAV: { + // navigation units nautical miles and knots + fallbackLang: "en_GB", // the locale generator doesn't know what NAV is, so give it a different lang to take values from + icon: "⛵✈️", + calendar: "gregory", + numberingSystem: "latn", + speed: "kts", + distance: { 0: "m", 1: "nm" }, + }, + de_DE: { + calendar: "gregory", + numberingSystem: "latn", + speed: "kmh", + datePattern: { 0: "%d. %b %Y", 1: "%d.%m.%Y" }, // 1. März 2020 // 01.03.20 + trans: { + yes: "ja", + Yes: "Ja", + no: "nein", + No: "Nein", + ok: "ok", + on: "an", + off: "aus", + "< Back": "< Zurück", + Delete: "Löschen", + "Mark Unread": "Als ungelesen markieren", + }, + }, + en_JP: { + // we do not have the font, so it is not ja_JP + fallbackLang: "en_GB", // en_JP is not supported by the generator, so we give it somewhere else to grab the english names from + calendar: "gregory", + numberingSystem: "latn", + speed: "kmh", + distance: { 0: "m", 1: "km" }, + datePattern: { 0: "%Y/%m/%d", 1: "%Y/%m/%d" }, + }, + nl_NL: { + calendar: "gregory", + numberingSystem: "latn", + speed: "kmh", + ampm: { 0: "AM", 1: "PM" }, + trans: { + yes: "ja", + Yes: "Ja", + no: "nee", + No: "Nee", + ok: "ok", + on: "aan", + off: "uit", + "< Back": "< Terug", + Delete: "Verwijderen", + "Mark Unread": "Markeer als ongelezen", + }, + }, + en_NL: { + // English date units with Dutch number and navigation units. + calendar: "gregory", + numberingSystem: "latn", + datePattern: { 0: "%b %d %Y", 1: "%d/%m/%Y" }, // Feb 28 2020" // "01/03/2020"(short) + abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", + }, + en_CA: { + calendar: "gregory", + numberingSystem: "latn", + ampm: { 0: "am", 1: "pm" }, + }, + fr_FR: { + calendar: "gregory", + numberingSystem: "latn", + abmonth: "janv,févr,mars,avr,mai,juin,juil,août,sept,oct,nov,déc", + abday: "dim,lun,mar,mer,jeu,ven,sam", + trans: { + yes: "oui", + Yes: "Oui", + no: "non", + No: "Non", + ok: "ok", + on: "on", + off: "off", + }, + }, + fr_MC: { + calendar: "gregory", + numberingSystem: "latn", + abmonth: "janv,févr,mars,avr,mai,juin,juil,août,sept,oct,nov,déc", + abday: "dim,lun,mar,mer,jeu,ven,sam", + }, + sv_SE: { + calendar: "gregory", + numberingSystem: "latn", + speed: "km/t", + datePattern: { 0: "%b %d %Y", 1: "%Y-%m-%d" }, // feb 1 2020 // 2020-03-01 + abmonth: "jan,feb,mars,apr,maj,juni,juli,aug,sep,okt,nov,dec", + trans: { + yes: "ja", + Yes: "Ja", + no: "nej", + No: "Nej", + ok: "ok", + on: "on", + off: "off", + }, + }, + en_SE: { + // Swedish localisation with English text + calendar: "gregory", + numberingSystem: "latn", + abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", + }, + da_DK: { + calendar: "gregory", + numberingSystem: "latn", + speed: "km/t", + abmonth: "jan,feb,mar,apr,maj,jun,jul,aug,sep,okt,nov,dec", + abday: "søn,man,tir,ons,tor,fre,lør", + trans: { yes: "ja", Yes: "Ja", no: "nej", No: "Nej", ok: "ok" }, + }, + en_DK: { + // Danish units with english language + calendar: "gregory", + numberingSystem: "latn", + abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", + }, + en_NZ: { + calendar: "gregory", + numberingSystem: "latn", + speed: "kph", + abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", + }, + en_AU: { + calendar: "gregory", + numberingSystem: "latn", + speed: "kmh", + abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", + }, + de_AT: { + calendar: "gregory", + numberingSystem: "latn", + speed: "kmh", + trans: { + yes: "ja", + Yes: "Ja", + no: "nein", + No: "Nein", + ok: "ok", + on: "an", + off: "aus", + "< Back": "< Zurück", + Delete: "Löschen", + "Mark Unread": "Als ungelesen markieren", + }, + }, + en_IL: { + calendar: "gregory", + numberingSystem: "latn", + speed: "kmh", + abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", + }, + es_ES: { + calendar: "gregory", + numberingSystem: "latn", + speed: "kmh", + ampm: { 0: "AM", 1: "PM" }, + trans: { + yes: "sí", + Yes: "Sí", + no: "no", + No: "No", + ok: "ok", + on: "on", + off: "off", + "< Back": "< Atrás", + Delete: "Borrar ", + "Mark Unread": "Marcar como no leído", + }, + }, + fr_BE: { + calendar: "gregory", + numberingSystem: "latn", + speed: "kmh", + abmonth: "janv,févr,mars,avr,mai,juin,juil,août,sept,oct,nov,déc", + abday: "dim,lun,mar,mer,jeu,ven,sam", + trans: { + yes: "oui", + Yes: "Oui", + no: "non", + No: "Non", + ok: "ok", + on: "on", + off: "off", + }, + }, + nl_BE: { + calendar: "gregory", + numberingSystem: "latn", + ampm: { 0: "AM", 1: "PM" }, + }, + de_BE: { + calendar: "gregory", + numberingSystem: "latn", + }, + fi_FI: { + calendar: "gregory", + numberingSystem: "latn", + speed: "kmh", + ampm: { 0: "ap", 1: "ip" }, + abmonth: "tamm,helm,maal,huht,touk,kesä,hein,elo,syys,loka,marr,joul", + trans: { + yes: "oui", + Yes: "Oui", + no: "no", + No: "No", + ok: "ok", + on: "on", + off: "off", + }, + }, + de_CH: { + calendar: "gregory", + numberingSystem: "latn", + speed: "kmh", + ampm: { 0: "VM", 1: "NM" }, + trans: { + yes: "ja", + Yes: "Ja", + no: "nein", + No: "Nein", + ok: "ok", + on: "an", + off: "aus", + }, + }, + fr_CH: { + calendar: "gregory", + numberingSystem: "latn", + speed: "kmh", + abmonth: "janv,févr,mars,avr,mai,juin,juil,août,sept,oct,nov,déc", + abday: "dim,lun,mar,mer,jeu,ven,sam", + trans: { + yes: "oui", + Yes: "Oui", + no: "non", + No: "Non", + ok: "ok", + on: "on", + off: "off", + }, + }, + it_CH: { + calendar: "gregory", + numberingSystem: "latn", + speed: "kmh", + trans: { + yes: "sì", + Yes: "Sì", + no: "no", + No: "No", + ok: "ok", + on: "on", + off: "off", + }, + }, + it_IT: { + calendar: "gregory", + numberingSystem: "latn", + speed: "kmh", + trans: { + yes: "sì", + Yes: "Sì", + no: "no", + No: "No", + ok: "ok", + on: "on", + off: "off", + }, + }, + wae_CH: { + calendar: "gregory", + numberingSystem: "latn", + speed: "kmh", + trans: { + yes: "sì", + Yes: "Sì", + no: "no", + No: "No", + ok: "ok", + on: "on", + off: "off", + }, + }, + tr_TR: { + calendar: "gregory", + numberingSystem: "latn", + speed: "kmh", + trans: { + yes: "evet", + Yes: "Evet", + no: "hayir", + No: "Hayir", + ok: "tamam", + on: "acik", + off: "kapali", + }, + }, + hu_HU: { + calendar: "gregory", + numberingSystem: "latn", + speed: "kph", + ampm: { 0: "de", 1: "du" }, + datePattern: { + 0: "%Y. %b %-d.", + 1: "%Y.%m.%d.", + }, + abmonth: "jan,feb,már,ápr,máj,jún,júl,aug,szep,okt,nov,dec", + trans: { + yes: "igen", + Yes: "Igen", + no: "nem", + No: "Nem", + ok: "ok", + on: "be", + off: "ki", + }, + }, + oc_FR: { + calendar: "gregory", + numberingSystem: "latn", + abmonth: "gen,feb,març,abr,mai,junh,jul,ago,set,oct,nov,dec", + abday: "dg,dl,dm,dc,dj,dv,ds", + trans: { + yes: "òc", + Yes: "Òc", + no: "non", + No: "Non", + ok: "ok", + on: "on", + off: "off", + }, + }, + pt_BR: { + calendar: "gregory", + numberingSystem: "latn", + speed: "kmh", + datePattern: { + 0: "%-d. %b %Y", + 1: "%d/%m/%Y", + }, + abmonth: "jan,fev,mar,abr,mai,jun,jul,ago,set,out,nov,dez", + abday: "dom,seg,ter,qua,qui,sex,sáb", + trans: { + yes: "sim", + Yes: "Sim", + no: "não", + No: "Não", + ok: "confirmar", + on: "ativado", + off: "desativado", + }, + }, + cs_CZ: { + calendar: "gregory", + numberingSystem: "latn", + speed: "kmh", + ampm: { 0: "dop", 1: "odp" }, + trans: { + yes: "ano", + Yes: "Ano", + no: "ne", + No: "Ne", + ok: "ok", + on: "zap", + off: "vyp", + }, + }, + hr_HR: { + calendar: "gregory", + numberingSystem: "latn", + ampm: { 0: "dop", 1: "pop" }, + datePattern: { + 0: "%-d. %b %Y.", + 1: "%d.%m.%Y.", + }, + trans: { + yes: "da", + Yes: "Da", + no: "ne", + No: "Ne", + ok: "ok", + on: "Uklj.", + off: "Isklj.", + "< Back": "< Natrag", + }, + }, + sl_SI: { + calendar: "gregory", + numberingSystem: "latn", + ampm: { 0: "dop", 1: "pop" }, + abmonth: "jan,feb,mar,apr,maj,jun,jul,avg,sep,okt,nov,dec", + abday: "ned,pon,tor,sre,čet,pet,sob", + trans: { + yes: "da", + Yes: "Da", + no: "ne", + No: "Ne", + ok: "ok", + on: "Vklj.", + off: "Izklj.", + "< Back": "< Nazaj", + }, + }, + pt_PT: { + calendar: "gregory", + numberingSystem: "latn", + speed: "kmh", + ampm: { 0: "am", 1: "pm" }, + abmonth: "jan,fev,mar,abr,mai,jun,jul,ago,set,out,nov,dez", + abday: "dom,seg,ter,qua,qui,sex,sáb", + trans: { + yes: "sim", + Yes: "Sim", + no: "não", + No: "Não", + ok: "ok", + on: "on", + off: "off", + }, + }, + pl_PL: { + calendar: "gregory", + numberingSystem: "latn", + speed: "kmh", + abday: "ndz,pon,wt,śr,czw,pt,sob", + trans: { + yes: "tak", + Yes: "Tak", + no: "nie", + No: "Nie", + ok: "ok", + on: "on", + off: "off", + "< Back": "< Wstecz", + }, + }, + lv_LV: { + calendar: "gregory", + numberingSystem: "latn", + speed: "kmh", + ampm: { 0: "pri", 1: "pēc" }, + datePattern: { + 0: "%Y. %-d. %b", + 1: "%d.%m.%y", + }, + abmonth: "jan,feb,mar,apr,mai,jūn,jūl,aug,sep,okt,nov,dec", + abday: "sv,pr,ot,tr,ce,pk,se", + trans: { + yes: "jā", + Yes: "Jā", + no: "nē", + No: "Nē", + ok: "labi", + on: "Ieslēgt", + off: "Izslēgt", + "< Back": "< Atpakaļ", + }, + }, + nn_NO: { + calendar: "gregory", + numberingSystem: "latn", + speed: "kmt", + ampm: { 0: "FM", 1: "EM" }, + trans: { + yes: "ja", + Yes: "Ja", + no: "nei", + No: "Nei", + ok: "ok", + on: "på", + off: "av", + "< Back": "< Tilbake", + Delete: "Slett", + "Mark Unread": "Merk som ulesen", + }, + }, + nb_NO: { + calendar: "gregory", + numberingSystem: "latn", + speed: "kmh", + ampm: { 0: "AM", 1: "PM" }, + abday: "søn,man,tir,ons,tor,fre,lør", + trans: { + yes: "ja", + Yes: "Ja", + no: "nei", + No: "Nei", + ok: "ok", + on: "på", + off: "av", + "< Back": "< Tilbake", + Delete: "Slett", + "Mark Unread": "Merk som ulest", + }, + }, + ca_ES: { + icon: "🇪🇺", + calendar: "gregory", + numberingSystem: "latn", + speed: "kmh", + ampm: { 0: "AM", 1: "PM" }, + abmonth: "gen,febr,març,abr,maig,juny,jul,ag,set,oct,nov,des", + trans: { + yes: "sí", + Yes: "Sí", + no: "no", + No: "No", + ok: "d'acord", + on: "on", + off: "off", + "< Back": "< Enrere", + Delete: "Esborra", + "Mark Unread": "Marca com a no llegit", + }, + }, + sq_AL: { + calendar: "gregory", + numberingSystem: "latn", + ampm: { 0: "AM", 1: "PM" }, + }, + /* + "he_IL": { // This won't work until we get a font - see https://github.com/espruino/BangleApps/issues/399 + codePage : "ISO8859-8", + lang: "he_IL", + calendar: "gregory", + numberingSystem: "latn", + speed: "קמ״ש", + distance: { 0: "מ׳", 1: "ק״מ" }, + timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, + datePattern: { 0: "%A, %B %d, %Y", "1": "%d/%m/%Y" }, // Sunday, 1 March 2020 // 01/03/2020 + abmonth: "ינו,פבר,מרץ,אפר,מאי,יונ,יול,אוג,ספט,אוק,נוב,דצמ", + month: "ינואר,פברואר,מרץ,אפריל,מאי,יוני,יולי,אוגוסט,ספטמבר,אוקטובר,נובמבר,דצמבר", + abday: "א׳,ב׳,ג׳,ד׳,ה,ו׳,ש׳", + day: "ראשון,שני,שלישי,רביעי,חמישי,שישי,שבת", + trans: { yes: "כן", Yes: "כן", no: "לא", No: "לא", ok: "אישור", on: "פעיל", off: "כבוי" } + } +*/ + /** + * These test strings are designed to be as wide and tall as real locale strings can be. + * All apps should be able to display them properly, to ensure that they work with all locales. + * To make the strings as long as possible, wide characters like "w" and "m" is used, + * and to make them taller, "k" and "g" are used together. + */ + "ts_TS test": { + icon: "🐛", + notes: "Produces the longest possible output. Useful for testing.", + calendar: "gregory", + numberingSystem: "latn", + decimal_point: ",", + thousands_sep: ",", + speed: "km/h", + distance: { + 0: "kmi", + 1: "kmi", + }, + temperature: "°C", + ampm: { + 0: "dop", + 1: "odp", + }, + timePattern: { + 0: "%HHh%MM:%SS", + 1: "%HHh%MM", + }, + datePattern: { + 0: "%b, %d, %Y", + 1: "%d. %m %Y", + }, + abmonth: Array(12).fill("mgmk").join(","), + month: Array(12).fill("megmmaskuum").join(","), + abday: Array(7).fill("mgmk").join(","), + day: Array(7).fill("megmavammkkom").join(","), + }, +}; diff --git a/apps/locale/locales.js b/apps/locale/locales.js index 11af6c1d81..13f76015c2 100644 --- a/apps/locale/locales.js +++ b/apps/locale/locales.js @@ -2,8 +2,16 @@ /* exported distanceUnits */ /* exported speedUnits */ /* exported codePages */ +/* exported datePatterns */ +/* exported timePatterns */ +/* exported meridians */ /* exported locales */ -const distanceUnits = { // how many meters per X? +/** + * THIS FILE IS AUTOGENERATED. + * If you need to edit it, please do so in 'apps/locale/locales-meta.js' instead. + */ + +const distanceUnits = { "m": 1, "ft": 0.3048, "yd": 0.9144, @@ -12,747 +20,1752 @@ const distanceUnits = { // how many meters per X? "kmi": 1000, "nm": 1852 }; -const speedUnits = { // how many kph per X? +const speedUnits = { "kmh": 1, "kph": 1, "km/h": 1, "kmt": 1, "km/t": 1, - "km/tim": 1, "mph": 1.60934, "kts": 1.852 }; -/* -For a codepage, 'map' is a map of char codes 128 and above. -Where there is no character, just use '.' -*/ const codePages = { - "ISO8859-1" : { - name : "ISO8859-1", - map : ` -€.‚ƒ„…†‡ˆ‰Š‹Œ.Ž. -.‘’“”•–—˜™š›œ.žŸ -.¡¢£¤¥¦§¨©ª«¬.®¯ -°±²³´µ¶·¸¹º»¼½¾¿ -ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ -ÐÑÒÓÔÕÖרÙÚÛÜÝÞß -àáâãäåæçèéêëìíîï -ðñòóôõö÷øùúûüýþÿ -`.replace(/[ \n]/g,"") + "ISO8859-1": { + "name": "ISO8859-1", + "map": "€.‚ƒ„…†‡ˆ‰Š‹Œ.Ž..‘’“”•–—˜™š›œ.žŸ.¡¢£¤¥¦§¨©ª«¬.®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ" } }; -// charFallbacks is now in core/js/utils.js as CODEPAGE_CONVERSIONS - -/* -timePattern / datePattern: - - %Y year four digits - %y last two digits of year (00..99) - %m month (01..12) - %d day of month (e.g, 01) - - %a locale's abbreviated weekday name (e.g., Sun) - %A locale's full weekday name (e.g., Sunday) - %b locale's abbreviated month name (e.g., Jan) - %B locale's full month name (e.g., January) +const datePatterns = { + "0": [ + "%-d %b %Y", + "%-d. %-m. %Y", + "%-d. %b %Y", + "%-d. %b %Y.", + "%-d.%-m.%Y", + "%Y. %-d. %b", + "%Y. %b %-d.", + "%Y/%m/%d", + "%b %-d, %Y", + "%b %d %Y", + "%b %d, %Y", + "%b, %d, %Y", + "%d. %b %Y", + "%d.%m.%Y", + "%d/%m/%Y" + ], + "1": [ + "%-d. %-m. %y", + "%-d.%-m.%Y", + "%-d.%-m.%y", + "%-d.%m.%Y", + "%-d/%-m/%y", + "%-d/%m/%Y", + "%-d/%m/%y", + "%-m/%-d/%y", + "%Y-%m-%d", + "%Y.%m.%d.", + "%Y/%m/%d", + "%d-%m-%Y", + "%d. %m %Y", + "%d.%m.%Y", + "%d.%m.%Y.", + "%d.%m.%y", + "%d/%m/%Y", + "%d/%m/%y" + ] +}; - %H hour (00..23) - %M minute (00..59) - %S second (00..60) - %p locale's equivalent of either AM or PM; blank if not known - %P like %p, but lower case - - -in locales: +const timePatterns = { + "0": [ + "%HH.%MM.%SS", + "%HH:%MM:%SS", + "%HHh%MM:%SS" + ], + "1": [ + "%HH.%MM", + "%HH:%MM", + "%HHh%MM" + ] +}; - abmonth: short months (must be <5 chars, ideally 3) - month: normal month names - abday: short days (must be <5 chars, ideally 3) - day: normal day names -*/ +const meridians = { + "0": [ + "AM", + "FM", + "VM", + "am", + "ap", + "de", + "dop", + "fm", + "pri", + "ÖÖ" + ], + "1": [ + "EM", + "NM", + "PM", + "du", + "em", + "ip", + "odp", + "pec", + "pm", + "pop", + "ÖS" + ] +}; var locales = { - "en_GB": { // this is default - lang: "en_GB", - decimal_point: ".", - thousands_sep: ",", - speed: 'mph', - distance: { "0": "yd", "1": "mi" }, - temperature: '°C', - ampm: { 0: "am", 1: "pm" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%b %d %Y", 1: "%d/%m/%Y" }, // Feb 28 2020" // "01/03/2020"(short) - abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", - month: "January,February,March,April,May,June,July,August,September,October,November,December", - abday: "Sun,Mon,Tue,Wed,Thu,Fri,Sat", - day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday", - // No translation for english... + "en_GB": { + "lang": "en_GB", + "icon": "🇬🇧", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ".", + "thousands_sep": ",", + "speed": "mph", + "distance": { + "0": "yd", + "1": "mi" + }, + "temperature": "°C", + "ampm": { + "0": "am", + "1": "pm" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%b %d %Y", + "1": "%d/%m/%Y" + }, + "abmonth": "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", + "month": "January,February,March,April,May,June,July,August,September,October,November,December", + "abday": "Sun,Mon,Tue,Wed,Thu,Fri,Sat", + "day": "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday" }, "en_US": { - lang: "en_US", - notes: "USA with MM/DD/YY date", - decimal_point: ".", - thousands_sep: ",", - speed: "mph", - distance: { 0: "ft", 1: "mi" }, - temperature: "°F", - ampm: { 0: "am", 1: "pm" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%b %d, %Y", 1: "%m/%d/%y" }, - abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", - month: "January,February,March,April,May,June,July,August,September,October,November,December", - abday: "Sun,Mon,Tue,Wed,Thu,Fri,Sat", - day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday", - // No translation for english... + "lang": "en_US", + "icon": "🇺🇸", + "notes": "USA with MM/DD/YY date", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ".", + "thousands_sep": ",", + "speed": "mph", + "distance": { + "0": "ft", + "1": "mi" + }, + "temperature": "°F", + "ampm": { + "0": "AM", + "1": "PM" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%b %-d, %Y", + "1": "%-m/%-d/%y" + }, + "abmonth": "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", + "month": "January,February,March,April,May,June,July,August,September,October,November,December", + "abday": "Sun,Mon,Tue,Wed,Thu,Fri,Sat", + "day": "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday" }, "en_US 2": { - lang: "en_US 2", icon:"🇺🇸", - notes: "USA with YYYY-MM-DD date", - decimal_point: ".", - thousands_sep: ",", - speed: "mph", - distance: { 0: "ft", 1: "mi" }, - temperature: "°F", - ampm: { 0: "am", 1: "pm" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%b %d, %Y", 1: "%Y-%m-%d" }, - abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", - month: "January,February,March,April,May,June,July,August,September,October,November,December", - abday: "Sun,Mon,Tue,Wed,Thu,Fri,Sat", - day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday", - // No translation for english... + "lang": "en_US 2", + "icon": "🇺🇸", + "notes": "USA with YYYY-MM-DD date", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ".", + "thousands_sep": ",", + "speed": "mph", + "distance": { + "0": "ft", + "1": "mi" + }, + "temperature": "°F", + "ampm": { + "0": "AM", + "1": "PM" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%b %d, %Y", + "1": "%Y-%m-%d" + }, + "abmonth": "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", + "month": "January,February,March,April,May,June,July,August,September,October,November,December", + "abday": "Sun,Mon,Tue,Wed,Thu,Fri,Sat", + "day": "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday" }, "en_IN": { - lang: "en_IN", - decimal_point: ".", - thousands_sep: ",", - speed: 'kmh', - distance: { "0": "m", "1": "km" }, - temperature: '°C', - ampm: { 0: "am", 1: "pm" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%d %b %Y", 1: "%d/%m/%Y" }, // 28 Feb 2020" // "28/03/2020"(short) - abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", - month: "January,February,March,April,May,June,July,August,September,October,November,December", - abday: "Sun,Mon,Tue,Wed,Thu,Fri,Sat", - day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday", - // No translation for english... + "lang": "en_IN", + "icon": "🇮🇳", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ".", + "thousands_sep": ",", + "speed": "kmh", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "am", + "1": "pm" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%-d %b %Y", + "1": "%d/%m/%y" + }, + "abmonth": "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", + "month": "January,February,March,April,May,June,July,August,September,October,November,December", + "abday": "Sun,Mon,Tue,Wed,Thu,Fri,Sat", + "day": "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday" }, "en_IE": { - lang: "en_IE", - decimal_point: ".", - thousands_sep: ",", - speed: 'kmh', - distance: { "0": "m", "1": "km" }, - temperature: '°C', - ampm: { 0: "am", 1: "pm" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%d %b %Y", 1: "%d/%m/%Y" }, // 28 Feb 2020" // "28/03/2020"(short) - abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", - month: "January,February,March,April,May,June,July,August,September,October,November,December", - abday: "Sun,Mon,Tue,Wed,Thu,Fri,Sat", - day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday", - // No translation for english... - }, - "en_NAV": { // navigation units nautical miles and knots - lang: "en_NAV", icon: "⛵✈️", - decimal_point: ".", - thousands_sep: ",", - speed: 'kts', - distance: { "0": "m", "1": "nm" }, - temperature: '°C', - ampm: { 0: "am", 1: "pm" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%b %d %Y", 1: "%d/%m/%Y" }, // Feb 28 2020" // "01/03/2020"(short) - abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", - month: "January,February,March,April,May,June,July,August,September,October,November,December", - abday: "Sun,Mon,Tue,Wed,Thu,Fri,Sat", - day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday", - // No translation for english... + "lang": "en_IE", + "icon": "🇮🇪", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ".", + "thousands_sep": ",", + "speed": "kmh", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "am", + "1": "pm" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%-d %b %Y", + "1": "%d/%m/%Y" + }, + "abmonth": "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", + "month": "January,February,March,April,May,June,July,August,September,October,November,December", + "abday": "Sun,Mon,Tue,Wed,Thu,Fri,Sat", + "day": "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday" + }, + "en_NAV": { + "lang": "en_NAV", + "icon": "⛵✈️", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ".", + "thousands_sep": ",", + "speed": "kts", + "distance": { + "0": "m", + "1": "nm" + }, + "temperature": "°C", + "ampm": { + "0": "am", + "1": "pm" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%b %d %Y", + "1": "%d/%m/%Y" + }, + "abmonth": "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", + "month": "January,February,March,April,May,June,July,August,September,October,November,December", + "abday": "Sun,Mon,Tue,Wed,Thu,Fri,Sat", + "day": "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday" }, "de_DE": { - lang: "de_DE", - decimal_point: ",", - thousands_sep: ".", - speed: "kmh", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "", 1: "" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%d. %b %Y", "1": "%d.%m.%Y" }, // 1. Mär 2020 // 01.03.20 - abmonth: "Jan,Feb,Mär,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Dez", - month: "Januar,Februar,März,April,Mai,Juni,Juli,August,September,Oktober,November,Dezember", - abday: "So,Mo,Di,Mi,Do,Fr,Sa", - day: "Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag", - trans: { yes: "ja", Yes: "Ja", no: "nein", No: "Nein", ok: "ok", on: "an", off: "aus", - "< Back": "< Zurück", "Delete": "Löschen", "Mark Unread": "Als ungelesen markieren" } - }, - "en_JP": { // we do not have the font, so it is not ja_JP - lang: "en_JP", - decimal_point: ".", - thousands_sep: ",", - speed: "kmh", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "", 1: "" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%Y/%m/%d", 1: "%y/%m/%d" }, - abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", - month: "January,February,March,April,May,June,July,August,September,October,November,December", - abday: "Sun,Mon,Tue,Wed,Thu,Fri,Sat", - day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday", - // No translation for english... + "lang": "de_DE", + "icon": "🇩🇪", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": ".", + "speed": "kmh", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "AM", + "1": "PM" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%d. %b %Y", + "1": "%d.%m.%Y" + }, + "abmonth": "Jan,Feb,Mär,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Dez", + "month": "Januar,Februar,März,April,Mai,Juni,Juli,August,September,Oktober,November,Dezember", + "abday": "So,Mo,Di,Mi,Do,Fr,Sa", + "day": "Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag", + "trans": { + "yes": "ja", + "Yes": "Ja", + "no": "nein", + "No": "Nein", + "ok": "ok", + "on": "an", + "off": "aus", + "< Back": "< Zurück", + "Delete": "Löschen", + "Mark Unread": "Als ungelesen markieren" + } + }, + "en_JP": { + "lang": "en_JP", + "icon": "🇯🇵", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ".", + "thousands_sep": ",", + "speed": "kmh", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "am", + "1": "pm" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%Y/%m/%d", + "1": "%Y/%m/%d" + }, + "abmonth": "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", + "month": "January,February,March,April,May,June,July,August,September,October,November,December", + "abday": "Sun,Mon,Tue,Wed,Thu,Fri,Sat", + "day": "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday" }, "nl_NL": { - lang: "nl_NL", - decimal_point: ",", - thousands_sep: ".", - speed: "kmh", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "", 1: "" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%d %b %Y", 1: "%d-%m-%Y" }, // 28 feb 2020 // 28-02-2020 - abday: "zo,ma,di,wo,do,vr,za", - day: "zondag,maandag,dinsdag,woensdag,donderdag,vrijdag,zaterdag", - abmonth: "jan,feb,mrt,apr,mei,jun,jul,aug,sep,okt,nov,dec", - month: "januari,februari,maart,april,mei,juni,juli,augustus,september,oktober,november,december", - trans: { yes: "ja", Yes: "Ja", no: "nee", No: "Nee", ok: "ok", on: "aan", off: "uit", - "< Back": "< Terug", "Delete": "Verwijderen", "Mark Unread": "Markeer als ongelezen" } - }, - "en_NL": { // English date units with Dutch number and navigation units. - lang: "en_NL", - decimal_point: ",", - thousands_sep: ".", - speed: "km/h", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "am", 1: "pm" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%b %d %Y", 1: "%d/%m/%Y" }, // Feb 28 2020" // "01/03/2020"(short) - abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", - month: "January,February,March,April,May,June,July,August,September,October,November,December", - abday: "Sun,Mon,Tue,Wed,Thu,Fri,Sat", - day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday", + "lang": "nl_NL", + "icon": "🇳🇱", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": ".", + "speed": "kmh", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "AM", + "1": "PM" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%-d %b %Y", + "1": "%d-%m-%Y" + }, + "abmonth": "jan,feb,mrt,apr,mei,jun,jul,aug,sep,okt,nov,dec", + "month": "januari,februari,maart,april,mei,juni,juli,augustus,september,oktober,november,december", + "abday": "zo,ma,di,wo,do,vr,za", + "day": "zondag,maandag,dinsdag,woensdag,donderdag,vrijdag,zaterdag", + "trans": { + "yes": "ja", + "Yes": "Ja", + "no": "nee", + "No": "Nee", + "ok": "ok", + "on": "aan", + "off": "uit", + "< Back": "< Terug", + "Delete": "Verwijderen", + "Mark Unread": "Markeer als ongelezen" + } + }, + "en_NL": { + "lang": "en_NL", + "icon": "🇳🇱", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": ".", + "speed": "km/h", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "am", + "1": "pm" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%b %d %Y", + "1": "%d/%m/%Y" + }, + "abmonth": "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", + "month": "January,February,March,April,May,June,July,August,September,October,November,December", + "abday": "Sun,Mon,Tue,Wed,Thu,Fri,Sat", + "day": "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday" }, "en_CA": { - lang: "en_CA", - decimal_point: ".", - thousands_sep: ",", - speed: "km/h", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "am", 1: "pm" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%A, %B %d, %Y", "1": "%Y-%m-%d" }, // Sunday, March 1, 2020 // 2012-12-20 - abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", - month: "January,February,March,April,May,June,July,August,September,October,November,December", - abday: "Sun,Mon,Tue,Wed,Thu,Fri,Sat", - day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday", - // No translation for english... + "lang": "en_CA", + "icon": "🇨🇦", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ".", + "thousands_sep": ",", + "speed": "km/h", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "am", + "1": "pm" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%b %-d, %Y", + "1": "%Y-%m-%d" + }, + "abmonth": "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", + "month": "January,February,March,April,May,June,July,August,September,October,November,December", + "abday": "Sun,Mon,Tue,Wed,Thu,Fri,Sat", + "day": "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday" }, "fr_FR": { - lang: "fr_FR", - decimal_point: ",", - thousands_sep: " ", - speed: "km/h", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "", 1: "" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%d %B %Y", "1": "%d/%m/%Y" }, // 1 mars 2020 // 01/03/2020 - abmonth: "janv,févr,mars,avril,mai,juin,juil,août,sept,oct,nov,déc", - month: "janvier,février,mars,avril,mai,juin,juillet,août,septembre,octobre,novembre,décembre", - abday: "dim,lun,mar,mer,jeu,ven,sam", - day: "dimanche,lundi,mardi,mercredi,jeudi,vendredi,samedi", - trans: { yes: "oui", Yes: "Oui", no: "non", No: "Non", ok: "ok", on: "on", off: "off" } + "lang": "fr_FR", + "icon": "🇫🇷", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": " ", + "speed": "km/h", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "AM", + "1": "PM" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%-d %b %Y", + "1": "%d/%m/%Y" + }, + "abmonth": "janv,févr,mars,avr,mai,juin,juil,août,sept,oct,nov,déc", + "month": "janvier,février,mars,avril,mai,juin,juillet,août,septembre,octobre,novembre,décembre", + "abday": "dim,lun,mar,mer,jeu,ven,sam", + "day": "dimanche,lundi,mardi,mercredi,jeudi,vendredi,samedi", + "trans": { + "yes": "oui", + "Yes": "Oui", + "no": "non", + "No": "Non", + "ok": "ok", + "on": "on", + "off": "off" + } + }, + "fr_MC": { + "lang": "fr_MC", + "icon": "🇲🇨", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": " ", + "speed": "km/h", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "AM", + "1": "PM" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%-d %b %Y", + "1": "%d/%m/%Y" + }, + "abmonth": "janv,févr,mars,avr,mai,juin,juil,août,sept,oct,nov,déc", + "month": "janvier,février,mars,avril,mai,juin,juillet,août,septembre,octobre,novembre,décembre", + "abday": "dim,lun,mar,mer,jeu,ven,sam", + "day": "dimanche,lundi,mardi,mercredi,jeudi,vendredi,samedi" }, "sv_SE": { - lang: "sv_SE", - decimal_point: ",", - thousands_sep: ".", - speed: "km/tim", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "fm", 1: "em" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%b %d %Y", "1": "%Y-%m-%d" }, // feb 1 2020 // 2020-03-01 - abmonth: "jan,feb,mars,apr,maj,juni,juli,aug,sep,okt,nov,dec", - month: "januari,februari,mars,april,maj,juni,juli,augusti,september,oktober,november,december", - abday: "sön,mån,tis,ons,tors,fre,lör", - day: "söndag,måndag,tisdag,onsdag,torsdag,fredag,lördag", - trans: { yes: "ja", Yes: "Ja", no: "nej", No: "Nej", ok: "ok", on: "on", off: "off" } - }, - "en_SE": { // Swedish localisation with English text - lang: "en_SE", - decimal_point: ",", - thousands_sep: ".", - speed: 'km/h', - distance: { "0": "m", "1": "km" }, - temperature: '°C', - ampm: { 0: "", 1: "" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%B %d %Y", "1": "%Y-%m-%d" }, // March 1 2020 // 2020-03-01 - abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", - month: "January,February,March,April,May,June,July,August,September,October,November,December", - abday: "Sun,Mon,Tue,Wed,Thu,Fri,Sat", - day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday", - // No translation for english... + "lang": "sv_SE", + "icon": "🇸🇪", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": " ", + "speed": "km/t", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "fm", + "1": "em" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%b %d %Y", + "1": "%Y-%m-%d" + }, + "abmonth": "jan,feb,mars,apr,maj,juni,juli,aug,sep,okt,nov,dec", + "month": "januari,februari,mars,april,maj,juni,juli,augusti,september,oktober,november,december", + "abday": "sön,mån,tis,ons,tors,fre,lör", + "day": "söndag,måndag,tisdag,onsdag,torsdag,fredag,lördag", + "trans": { + "yes": "ja", + "Yes": "Ja", + "no": "nej", + "No": "Nej", + "ok": "ok", + "on": "on", + "off": "off" + } + }, + "en_SE": { + "lang": "en_SE", + "icon": "🇸🇪", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": " ", + "speed": "km/h", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "am", + "1": "pm" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%-d %b %Y", + "1": "%Y-%m-%d" + }, + "abmonth": "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", + "month": "January,February,March,April,May,June,July,August,September,October,November,December", + "abday": "Sun,Mon,Tue,Wed,Thu,Fri,Sat", + "day": "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday" }, "da_DK": { - lang: "da_DK", - decimal_point: ",", - thousands_sep: ".", - speed: "km/t", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "", 1: "" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%d. %b. %Y", 1: "%d/%m %Y" }, // 1. feb. 2020 // 01/02 2020 // a better short ver. is 1/2 2020 but its not supported - abmonth: "jan,feb,mar,apr,maj,jun,jul,aug,sep,okt,nov,dec", - month: "januar,februar,marts,april,maj,juni,juli,august,september,oktober,november,december", - abday: "søn,man,tir,ons,tor,fre,lør", - day: "søndag,mandag,tirsdag,onsdag,torsdag,fredag,lørdag", - trans: { yes: "ja", Yes: "Ja", no: "nej", No: "Nej", ok: "ok", on: "tændt", off: "slukket" } // no single danish translation for "on"/"off", should not be used - }, - "en_DK": { // Danish units with english language - lang: "en_DK", - decimal_point: ",", - thousands_sep: ".", - speed: "km/h", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "", 1: "" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%d. %b. %Y", 1: "%d/%m %Y" }, // 1. feb. 2020 // 01/02 2020 // a better short ver. is 1/2 2020 but its not supported - abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", - month: "January,February,March,April,May,June,July,August,September,October,November,December", - abday: "Sun,Mon,Tue,Wed,Thu,Fri,Sat", - day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday", - // No translation for english... + "lang": "da_DK", + "icon": "🇩🇰", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": ".", + "speed": "km/t", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "AM", + "1": "PM" + }, + "timePattern": { + "0": "%HH.%MM.%SS", + "1": "%HH.%MM" + }, + "datePattern": { + "0": "%-d. %b %Y", + "1": "%d.%m.%Y" + }, + "abmonth": "jan,feb,mar,apr,maj,jun,jul,aug,sep,okt,nov,dec", + "month": "januar,februar,marts,april,maj,juni,juli,august,september,oktober,november,december", + "abday": "søn,man,tir,ons,tor,fre,lør", + "day": "søndag,mandag,tirsdag,onsdag,torsdag,fredag,lørdag", + "trans": { + "yes": "ja", + "Yes": "Ja", + "no": "nej", + "No": "Nej", + "ok": "ok" + } + }, + "en_DK": { + "lang": "en_DK", + "icon": "🇩🇰", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": ".", + "speed": "km/h", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "am", + "1": "pm" + }, + "timePattern": { + "0": "%HH.%MM.%SS", + "1": "%HH.%MM" + }, + "datePattern": { + "0": "%-d %b %Y", + "1": "%d/%m/%Y" + }, + "abmonth": "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", + "month": "January,February,March,April,May,June,July,August,September,October,November,December", + "abday": "Sun,Mon,Tue,Wed,Thu,Fri,Sat", + "day": "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday" }, "en_NZ": { - lang: "en_NZ", - decimal_point: ".", - thousands_sep: ",", - speed: "kph", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "am", 1: "pm" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%A, %B %d, %Y", "1": "%d/%m/%y" }, // Sunday, 1 March 2020 // 1/3/20 - abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", - month: "January,February,March,April,May,June,July,August,September,October,November,December", - abday: "Sun,Mon,Tue,Wed,Thu,Fri,Sat", - day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday", - // No translation for english... + "lang": "en_NZ", + "icon": "🇳🇿", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ".", + "thousands_sep": ",", + "speed": "kph", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "am", + "1": "pm" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%-d %b %Y", + "1": "%d/%m/%Y" + }, + "abmonth": "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", + "month": "January,February,March,April,May,June,July,August,September,October,November,December", + "abday": "Sun,Mon,Tue,Wed,Thu,Fri,Sat", + "day": "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday" }, "en_AU": { - lang: "en_AU", - decimal_point: ".", - thousands_sep: ",", - speed: "kmh", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "am", 1: "pm" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%A, %B %d, %Y", "1": "%d/%m/%y" }, // Sunday, 1 March 2020 // 1/3/20 - abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", - month: "January,February,March,April,May,June,July,August,September,October,November,December", - abday: "Sun,Mon,Tue,Wed,Thu,Fri,Sat", - day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday", - // No translation for english... + "lang": "en_AU", + "icon": "🇦🇺", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ".", + "thousands_sep": ",", + "speed": "kmh", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "am", + "1": "pm" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%-d %b %Y", + "1": "%-d/%-m/%y" + }, + "abmonth": "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", + "month": "January,February,March,April,May,June,July,August,September,October,November,December", + "abday": "Sun,Mon,Tue,Wed,Thu,Fri,Sat", + "day": "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday" }, "de_AT": { - lang: "de_AT", - decimal_point: ",", - thousands_sep: ".", - speed: "kmh", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "", 1: "" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%A, %d. %B %Y", "1": "%d.%m.%y" }, // Sonntag, 1. März 2020 // 01.03.20 - abmonth: "Jän,Feb,März,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Dez", - month: "Jänner,Februar,März,April,Mai,Juni,Juli,August,September,Oktober,November,Dezember", - abday: "So,Mo,Di,Mi,Do,Fr,Sa", - day: "Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag", - trans: { yes: "ja", Yes: "Ja", no: "nein", No: "Nein", ok: "ok", on: "an", off: "aus", - "< Back": "< Zurück", "Delete": "Löschen", "Mark Unread": "Als ungelesen markieren" } + "lang": "de_AT", + "icon": "🇦🇹", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": " ", + "speed": "kmh", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "AM", + "1": "PM" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%d.%m.%Y", + "1": "%d.%m.%y" + }, + "abmonth": "Jän,Feb,Mär,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Dez", + "month": "Jänner,Februar,März,April,Mai,Juni,Juli,August,September,Oktober,November,Dezember", + "abday": "So,Mo,Di,Mi,Do,Fr,Sa", + "day": "Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag", + "trans": { + "yes": "ja", + "Yes": "Ja", + "no": "nein", + "No": "Nein", + "ok": "ok", + "on": "an", + "off": "aus", + "< Back": "< Zurück", + "Delete": "Löschen", + "Mark Unread": "Als ungelesen markieren" + } }, "en_IL": { - lang: "en_IL", - decimal_point: ",", - thousands_sep: ".", - speed: "kmh", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "am", 1: "pm" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%A, %B %d, %Y", "1": "%d/%m/%Y" }, // Sunday, 1 March 2020 // 01/03/2020 - abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", - month: "January,February,March,April,May,June,July,August,September,October,November,December", - abday: "Sun,Mon,Tue,Wed,Thu,Fri,Sat", - day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday", - // No translation for english... + "lang": "en_IL", + "icon": "🇮🇱", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ".", + "thousands_sep": ",", + "speed": "kmh", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "am", + "1": "pm" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%-d %b %Y", + "1": "%d/%m/%Y" + }, + "abmonth": "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", + "month": "January,February,March,April,May,June,July,August,September,October,November,December", + "abday": "Sun,Mon,Tue,Wed,Thu,Fri,Sat", + "day": "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday" }, "es_ES": { - lang: "es_ES", - decimal_point: ",", - thousands_sep: ".", - speed: "kmh", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "", 1: "" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%A, %d de %B de %Y", "1": "%d/%m/%y" }, // domingo, 1 de marzo de 2020 // 01/03/20 - abmonth: "ene,feb,mar,abr,may,jun,jul,ago,sept,oct,nov,dic", - month: "enero,febrero,marzo,abril,mayo,junio,julio,agosto,septiembre,octubre,noviembre,diciembre", - abday: "dom,lun,mar,mié,jue,vie,sáb", - day: "domingo,lunes,martes,miércoles,jueves,viernes,sábado", - trans: { yes: "sí", Yes: "Sí", no: "no", No: "No", ok: "ok", on: "on", off: "off", - "< Back": "< Atrás", "Delete": "Borrar ", "Mark Unread": "Marcar como no leído" } + "lang": "es_ES", + "icon": "🇪🇸", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": ".", + "speed": "kmh", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "AM", + "1": "PM" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%-d %b %Y", + "1": "%-d/%-m/%y" + }, + "abmonth": "ene,feb,mar,abr,may,jun,jul,ago,sept,oct,nov,dic", + "month": "enero,febrero,marzo,abril,mayo,junio,julio,agosto,septiembre,octubre,noviembre,diciembre", + "abday": "dom,lun,mar,mié,jue,vie,sáb", + "day": "domingo,lunes,martes,miércoles,jueves,viernes,sábado", + "trans": { + "yes": "sí", + "Yes": "Sí", + "no": "no", + "No": "No", + "ok": "ok", + "on": "on", + "off": "off", + "< Back": "< Atrás", + "Delete": "Borrar ", + "Mark Unread": "Marcar como no leído" + } }, "fr_BE": { - lang: "fr_BE", - decimal_point: ",", - thousands_sep: ".", - speed: "kmh", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "", 1: "" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%A %B %d %Y", "1": "%d/%m/%y" }, // dimanche 1 mars 2020 // 01/03/20 - abmonth: "janv.,févr.,mars,avril,mai,juin,juil.,août,sept.,oct.,nov.,déc.", - month: "janvier,février,mars,avril,mai,juin,juillet,août,septembre,octobre,novembre,décembre", - abday: "dim,lun,mar,mer,jeu,ven,sam", - day: "dimanche,lundi,mardi,mercredi,jeudi,vendredi,samedi", - trans: { yes: "oui", Yes: "Oui", no: "non", No: "Non", ok: "ok", on: "on", off: "off" } + "lang": "fr_BE", + "icon": "🇧🇪", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": " ", + "speed": "kmh", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "AM", + "1": "PM" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%-d %b %Y", + "1": "%-d/%m/%y" + }, + "abmonth": "janv,févr,mars,avr,mai,juin,juil,août,sept,oct,nov,déc", + "month": "janvier,février,mars,avril,mai,juin,juillet,août,septembre,octobre,novembre,décembre", + "abday": "dim,lun,mar,mer,jeu,ven,sam", + "day": "dimanche,lundi,mardi,mercredi,jeudi,vendredi,samedi", + "trans": { + "yes": "oui", + "Yes": "Oui", + "no": "non", + "No": "Non", + "ok": "ok", + "on": "on", + "off": "off" + } + }, + "nl_BE": { + "lang": "nl_BE", + "icon": "🇧🇪", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": ".", + "speed": "km/h", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "AM", + "1": "PM" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%-d %b %Y", + "1": "%-d/%m/%Y" + }, + "abmonth": "jan,feb,mrt,apr,mei,jun,jul,aug,sep,okt,nov,dec", + "month": "januari,februari,maart,april,mei,juni,juli,augustus,september,oktober,november,december", + "abday": "zo,ma,di,wo,do,vr,za", + "day": "zondag,maandag,dinsdag,woensdag,donderdag,vrijdag,zaterdag" + }, + "de_BE": { + "lang": "de_BE", + "icon": "🇧🇪", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": ".", + "speed": "km/h", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "AM", + "1": "PM" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%d.%m.%Y", + "1": "%d.%m.%y" + }, + "abmonth": "Jan,Feb,Mär,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Dez", + "month": "Januar,Februar,März,April,Mai,Juni,Juli,August,September,Oktober,November,Dezember", + "abday": "So,Mo,Di,Mi,Do,Fr,Sa", + "day": "Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag" }, "fi_FI": { - lang: "fi_FI", - decimal_point: ",", - thousands_sep: ".", - speed: "kmh", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "ap", 1: "ip" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, // 17.00.00 // 17.00 - datePattern: { 0: "%A %d. %B %Y", "1": "%-d/%-m/%Y" }, // sunnuntai 1. maaliskuuta 2020 // 1.3.2020 - abmonth: "tammik,helmik,maalisk,huhtik,toukok,kesäk,heinäk,elok,syysk,lokak,marrask,jouluk", - month: "tammikuuta,helmikuuta,maaliskuuta,huhtikuuta,toukokuuta,kesäkuuta,heinäkuuta,elokuuta,syyskuuta,lokakuuta,marraskuuta,joulukuuta", - abday: "su,ma,ti,ke,to,pe,la", - day: "sunnuntaina,maanantaina,tiistaina,keskiviikkona,torstaina,perjantaina,lauantaina", - trans: { yes: "oui", Yes: "Oui", no: "no", No: "No", ok: "ok", on: "on", off: "off" } + "lang": "fi_FI", + "icon": "🇫🇮", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": " ", + "speed": "kmh", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "ap", + "1": "ip" + }, + "timePattern": { + "0": "%HH.%MM.%SS", + "1": "%HH.%MM" + }, + "datePattern": { + "0": "%-d.%-m.%Y", + "1": "%-d.%-m.%Y" + }, + "abmonth": "tamm,helm,maal,huht,touk,kesä,hein,elo,syys,loka,marr,joul", + "month": "tammikuu,helmikuu,maaliskuu,huhtikuu,toukokuu,kesäkuu,heinäkuu,elokuu,syyskuu,lokakuu,marraskuu,joulukuu", + "abday": "su,ma,ti,ke,to,pe,la", + "day": "sunnuntai,maanantai,tiistai,keskiviikko,torstai,perjantai,lauantai", + "trans": { + "yes": "oui", + "Yes": "Oui", + "no": "no", + "No": "No", + "ok": "ok", + "on": "on", + "off": "off" + } }, "de_CH": { - lang: "de_CH", - decimal_point: ",", - thousands_sep: ".", - speed: "kmh", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "vorm", 1: " nachm" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%A, %d. %B %Y", "1": "%d.%m.%Y" }, // Sonntag, 1. März 2020 // 1.3.2020 - abmonth: "Jan,Feb,März,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Dez", - month: "Januar,Februar,März,April,Mai,Juni,Juli,August,September,Oktober,November,Dezember", - abday: "So,Mo,Di,Mi,Do,Fr,Sa", - day: "Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag", - trans: { yes: "ja", Yes: "Ja", no: "nein", No: "Nein", ok: "ok", on: "an", off: "aus" } + "lang": "de_CH", + "icon": "🇨🇭", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ".", + "thousands_sep": "’", + "speed": "kmh", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "VM", + "1": "NM" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%d.%m.%Y", + "1": "%d.%m.%y" + }, + "abmonth": "Jan,Feb,Mär,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Dez", + "month": "Januar,Februar,März,April,Mai,Juni,Juli,August,September,Oktober,November,Dezember", + "abday": "So,Mo,Di,Mi,Do,Fr,Sa", + "day": "Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag", + "trans": { + "yes": "ja", + "Yes": "Ja", + "no": "nein", + "No": "Nein", + "ok": "ok", + "on": "an", + "off": "aus" + } }, "fr_CH": { - lang: "fr_CH", - decimal_point: ",", - thousands_sep: ".", - speed: "kmh", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "AM", 1: "PM" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%A %d %B %Y", "1": "%d/%m/%y" }, // dimanche 1 mars 2020 // 01/03/20 - abmonth: "janv.,févr.,mars,avril,mai,juin,juil.,août,sept.,oct.,nov.,déc.", - month: "janvier,février,mars,avril,mai,juin,juillet,août,septembre,octobre,novembre,décembre", - abday: "dim,lun,mar,mer,jeu,ven,sam", - day: "dimanche,lundi,mardi,mercredi,jeudi,vendredi,samedi", - trans: { yes: "oui", Yes: "Oui", no: "non", No: "Non", ok: "ok", on: "on", off: "off" } + "lang": "fr_CH", + "icon": "🇨🇭", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": " ", + "speed": "kmh", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "AM", + "1": "PM" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%-d %b %Y", + "1": "%d.%m.%y" + }, + "abmonth": "janv,févr,mars,avr,mai,juin,juil,août,sept,oct,nov,déc", + "month": "janvier,février,mars,avril,mai,juin,juillet,août,septembre,octobre,novembre,décembre", + "abday": "dim,lun,mar,mer,jeu,ven,sam", + "day": "dimanche,lundi,mardi,mercredi,jeudi,vendredi,samedi", + "trans": { + "yes": "oui", + "Yes": "Oui", + "no": "non", + "No": "Non", + "ok": "ok", + "on": "on", + "off": "off" + } }, "it_CH": { - lang: "it_CH", - decimal_point: ",", - thousands_sep: ".", - speed: 'kmh', - distance: { "0": "m", "1": "km" }, - temperature: '°C', - ampm: { 0: "", 1: "" }, - timePattern: { 0: "%HH:%MM.%SS", 1: "%HH:%MM" }, // 17:00.00 // 17:00 - datePattern: { 0: "%d %b %Y", "1": "%d/%m/%Y" }, // 1 marzo 2020 // 01/03/2020 - abmonth: "gen,feb,mar,apr,mag,giu,lug,ago,set,ott,nov,dic", - month: "gennaio,febbraio,marzo,aprile,maggio,giugno,luglio,agosto,settembre,ottobre,novembre,dicembre", - abday: "dom,lun,mar,mer,gio,ven,sab", - day: "domenica,lunedì,martedì,mercoledì,giovedì,venerdì,sabato", - trans: { yes: "sì", Yes: "Sì", no: "no", No: "No", ok: "ok", on: "on", off: "off" } + "lang": "it_CH", + "icon": "🇨🇭", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ".", + "thousands_sep": "’", + "speed": "kmh", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "AM", + "1": "PM" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%-d %b %Y", + "1": "%d.%m.%y" + }, + "abmonth": "gen,feb,mar,apr,mag,giu,lug,ago,set,ott,nov,dic", + "month": "gennaio,febbraio,marzo,aprile,maggio,giugno,luglio,agosto,settembre,ottobre,novembre,dicembre", + "abday": "dom,lun,mar,mer,gio,ven,sab", + "day": "domenica,lunedì,martedì,mercoledì,giovedì,venerdì,sabato", + "trans": { + "yes": "sì", + "Yes": "Sì", + "no": "no", + "No": "No", + "ok": "ok", + "on": "on", + "off": "off" + } }, "it_IT": { - lang: "it_IT", - decimal_point: ",", - thousands_sep: ".", - speed: 'kmh', - distance: { "0": "m", "1": "km" }, - temperature: '°C', - ampm: { 0: "", 1: "" }, - timePattern: { 0: "%HH:%MM.%SS", 1: "%HH:%MM" }, // 17:00.00 // 17:00 - datePattern: { 0: "%d %b %Y", "1": "%d/%m/%Y" }, // 1 marzo 2020 // 01/03/2020 - abmonth: "gen,feb,mar,apr,mag,giu,lug,ago,set,ott,nov,dic", - month: "gennaio,febbraio,marzo,aprile,maggio,giugno,luglio,agosto,settembre,ottobre,novembre,dicembre", - abday: "dom,lun,mar,mer,gio,ven,sab", - day: "domenica,lunedì,martedì,mercoledì,giovedì,venerdì,sabato", - trans: { yes: "sì", Yes: "Sì", no: "no", No: "No", ok: "ok", on: "on", off: "off" } + "lang": "it_IT", + "icon": "🇮🇹", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": ".", + "speed": "kmh", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "AM", + "1": "PM" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%-d %b %Y", + "1": "%d/%m/%y" + }, + "abmonth": "gen,feb,mar,apr,mag,giu,lug,ago,set,ott,nov,dic", + "month": "gennaio,febbraio,marzo,aprile,maggio,giugno,luglio,agosto,settembre,ottobre,novembre,dicembre", + "abday": "dom,lun,mar,mer,gio,ven,sab", + "day": "domenica,lunedì,martedì,mercoledì,giovedì,venerdì,sabato", + "trans": { + "yes": "sì", + "Yes": "Sì", + "no": "no", + "No": "No", + "ok": "ok", + "on": "on", + "off": "off" + } }, "wae_CH": { - lang: "wae_CH", - decimal_point: ",", - thousands_sep: ".", - speed: 'kmh', - distance: { "0": "m", "1": "km" }, - temperature: '°C', - ampm: { 0: "", 1: "" }, - timePattern: { 0: "%HH.%MM.%SS", 1: "%HH.%MM" }, // 17.00.00 // 17.00 - datePattern: { 0: "%A, %d. %B %Y", "1": "%Y-%m-%d" }, // Sunntag, 1. Märze 2020 // 2020-03-01 - abmonth: "Jen,Hor,Mär,Abr,Mei,Brá,Hei,Öig,Her,Wím,Win,Chr", - month: "Jenner,Hornig,Märze,Abrille,Meije,Bráčet,Heiwet,Öigšte,Herbštmánet,Wímánet,Wintermánet,Chrištmánet", - abday: "Sun,Män,Ziš,Mit,Fró,Fri,Sam", - day: "Sunntag,Mäntag,Zištag,Mittwuč,Fróntag,Fritag,Samštag", - trans: { yes: "sì", Yes: "Sì", no: "no", No: "No", ok: "ok", on: "on", off: "off" } - }, - "tr_TR": { // this is default - lang: "tr_TR", - decimal_point: ",", - thousands_sep: ".", - speed: 'kmh', - distance: { "0": "m", "1": "km" }, - temperature: '°C', - ampm: { 0: "öö", 1: "ös" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%d %w %Y %A", 1: "%d/%m/%Y" }, // 1 Mart 2020 Pazar // "01/03/2020" - abmonth: "Oca,Sub,Mar,Nis,May,Haz,Tem,Agu,Eyl,Eki,Kas,Ara", - month: "Ocak,Subat,Mart,Nisan,Mayis,Haziran,Temmuz,Agustos,Eylul,Ekim,Kasim,Aralik", - abday: "Paz,Pzt,Sal,Car,Per,Cum,Cmt", - day: "Pazar,Pazartesi,Sali,Carsamba,Persembe,Cuma,Cumartesi", - trans: { yes: "evet", Yes: "Evet", no: "hayir", No: "Hayir", ok: "tamam", on: "acik", off: "kapali" } + "lang": "wae_CH", + "icon": "🇨🇭", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": "’", + "speed": "kmh", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "AM", + "1": "PM" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%-d. %b %Y", + "1": "%Y-%m-%d" + }, + "abmonth": "Jen,Hor,Mär,Abr,Mei,Brá,Hei,Öig,Her,Wím,Win,Chr", + "month": "Jenner,Hornig,Märze,Abrille,Meije,Brácet,Heiwet,Öigšte,Herbštmánet,Wímánet,Wintermánet,Chrištmánet", + "abday": "Sun,Män,Ziš,Mit,Fró,Fri,Sam", + "day": "Sunntag,Mäntag,Zištag,Mittwuc,Fróntag,Fritag,Samštag", + "trans": { + "yes": "sì", + "Yes": "Sì", + "no": "no", + "No": "No", + "ok": "ok", + "on": "on", + "off": "off" + } + }, + "tr_TR": { + "lang": "tr_TR", + "icon": "🇹🇷", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": ".", + "speed": "kmh", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "ÖÖ", + "1": "ÖS" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%-d %b %Y", + "1": "%-d.%m.%Y" + }, + "abmonth": "Oca,Sub,Mar,Nis,May,Haz,Tem,Agu,Eyl,Eki,Kas,Ara", + "month": "Ocak,Subat,Mart,Nisan,Mayis,Haziran,Temmuz,Agustos,Eylül,Ekim,Kasim,Aralik", + "abday": "Paz,Pzt,Sal,Çar,Per,Cum,Cmt", + "day": "Pazar,Pazartesi,Sali,Çarsamba,Persembe,Cuma,Cumartesi", + "trans": { + "yes": "evet", + "Yes": "Evet", + "no": "hayir", + "No": "Hayir", + "ok": "tamam", + "on": "acik", + "off": "kapali" + } }, "hu_HU": { - lang: "hu_HU", - decimal_point: ",", - thousands_sep: " ", - speed: 'kph', - distance: { "0": "m", "1": "km" }, - temperature: '°C', - ampm: { 0: "de", 1: "du" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%Y %b %d, %A", 1: "%Y.%m.%d" }, // 2020 Feb 28, Péntek" // "2020.03.01."(short) - abmonth: "Jan,Feb,Már,Ápr,Máj,Jún,Júl,Aug,Szep,Okt,Nov,Dec", - month: "Január,Február,Március,Április,Május,Június,Július,Augusztus,Szeptember,Október,November,December", - abday: "Vas,Hét,Ke,Szer,Csüt,Pén,Szom", - day: "Vasárnap,Hétfő,Kedd,Szerda,Csütörtök,Péntek,Szombat", - trans: { yes: "igen", Yes: "Igen", no: "nem", No: "Nem", ok: "ok", on: "be", off: "ki" } + "lang": "hu_HU", + "icon": "🇭🇺", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": " ", + "speed": "kph", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "de", + "1": "du" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%Y. %b %-d.", + "1": "%Y.%m.%d." + }, + "abmonth": "jan,feb,már,ápr,máj,jún,júl,aug,szep,okt,nov,dec", + "month": "január,február,március,április,május,június,július,augusztus,szeptember,október,november,december", + "abday": "V,H,K,Sze,Cs,P,Szo", + "day": "vasárnap,hétfo,kedd,szerda,csütörtök,péntek,szombat", + "trans": { + "yes": "igen", + "Yes": "Igen", + "no": "nem", + "No": "Nem", + "ok": "ok", + "on": "be", + "off": "ki" + } }, "oc_FR": { - lang: "oc_FR", - decimal_point: ",", - thousands_sep: " ", - speed: "km/h", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "", 1: "" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%A %d %B de %Y", "1": "%d/%m/%Y" }, // dimenge 1 de març de 2020 // 01/03/2020 - abmonth: "gen.,febr.,març,abril,mai,junh,julh,ago.,set.,oct.,nov.,dec.", - month: "genièr,febrièr,març,abril,mai,junh,julhet,agost,setembre,octòbre,novembre,decembre", - abday: "dg,dl,dm,dc,dj,dv,ds", - day: "dimenge,diluns,dimars,dimècres,dijòus,divendres,dissabte", - trans: { yes: "òc", Yes: "Òc", no: "non", No: "Non", ok: "ok", on: "on", off: "off" } + "lang": "oc_FR", + "icon": "🇫🇷", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": " ", + "speed": "km/h", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "AM", + "1": "PM" + }, + "timePattern": { + "0": "%HHh%MM:%SS", + "1": "%HHh%MM" + }, + "datePattern": { + "0": "%-d %b %Y", + "1": "%-d/%m/%y" + }, + "abmonth": "gen,feb,març,abr,mai,junh,jul,ago,set,oct,nov,dec", + "month": "genièr,febrièr,març,abril,mai,junh,julhet,agost,setembre,octòbre,novembre,decembre", + "abday": "dg,dl,dm,dc,dj,dv,ds", + "day": "dimenge,diluns,dimars,dimècres,dijòus,divendres,dissabte", + "trans": { + "yes": "òc", + "Yes": "Òc", + "no": "non", + "No": "Non", + "ok": "ok", + "on": "on", + "off": "off" + } }, "pt_BR": { - lang: "pt_BR", - decimal_point: ",", - thousands_sep: ".", - speed: "kmh", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "am", 1: "pm" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%d %b %Y", 1: "%d/%m/%y" }, - abmonth: "Jan,Fev,Mar,Abr,Mai,Jun,Jul,Ago,Set,Out,Nov,Dez", - month: "Janeiro,Fevereiro,Março,Abril,Maio,Junho,Julho,Agosto,Setembro,Outubro,Novembro,Dezembro", - abday: "Dom,Seg,Ter,Qua,Qui,Sex,Sab", - day: "Domingo,Segunda-feira,Terça-feira,Quarta-feira,Quinta-feira,Sexta-feira,Sábado", - trans: { yes: "sim", Yes: "Sim", no: "não", No: "Não", ok: "confirmar", on: "ativado", off: "desativado" } - }, - "cs_CZ": { // THIS NEVER WORKED PROPERLY - many chars are not in the ISO8859-1 codepage and we use CODEPAGE_CONVERSIONS - lang: "cs_CZ", - decimal_point: ",", - thousands_sep: " ", - speed: 'kmh', - distance: { "0": "m", "1": "km" }, - temperature: '°C', - ampm: { 0: "dop", 1: "odp" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%d. %b %Y", 1: "%d.%m.%Y" }, // "30. led 2020" // "30.01.2020"(short) - abmonth: "led,úno,bře,dub,kvě,čvn,čvc,srp,zář,říj,lis,pro", - month: "leden,únor,březen,duben,květen,červen,červenec,srpen,září,říjen,listopad,prosinec", - abday: "ne,po,út,st,čt,pá,so", - day: "neděle,pondělí,úterý,středa,čtvrtek,pátek,sobota", - trans: { yes: "ano", Yes: "Ano", no: "ne", No: "Ne", ok: "ok", on: "zap", off: "vyp" } - }, - "hr_HR": { - lang: "hr_HR", - decimal_point: ",", - thousands_sep: ".", - speed: "km/h", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "dop.", 1: "pop." }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%-d. %b %Y", 1: "%-d.%-m.%Y" }, // "3. jan. 2020" // "3.1.2020"(short) - abmonth: "sij.,velj.,ožu.,tra.,svi,lip.,srp.,kol.,ruj.,lis.,stu.,pro.", - month: "siječanj,veljača,ožujak,travanj,svibanj,lipanj,srpanj,kolovoz,rujan,listopad,studeni,prosinac", - abday: "ned.,pon.,uto.,sri.,čet.,pet.,sub.", - day: "nedjelja,ponedjeljak,utorak,srijeda,četvrtak,petak,subota", - trans: { yes: "da", Yes: "Da", no: "ne", No: "Ne", ok: "ok", on: "Uklj.", off: "Isklj.", "< Back": "< Natrag" } + "lang": "pt_BR", + "icon": "🇧🇷", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": ".", + "speed": "kmh", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "AM", + "1": "PM" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%-d. %b %Y", + "1": "%d/%m/%Y" + }, + "abmonth": "jan,fev,mar,abr,mai,jun,jul,ago,set,out,nov,dez", + "month": "janeiro,fevereiro,março,abril,maio,junho,julho,agosto,setembro,outubro,novembro,dezembro", + "abday": "dom,seg,ter,qua,qui,sex,sáb", + "day": "domingo,segunda-feira,terça-feira,quarta-feira,quinta-feira,sexta-feira,sábado", + "trans": { + "yes": "sim", + "Yes": "Sim", + "no": "não", + "No": "Não", + "ok": "confirmar", + "on": "ativado", + "off": "desativado" + } + }, + "cs_CZ": { + "lang": "cs_CZ", + "icon": "🇨🇿", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": " ", + "speed": "kmh", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "dop", + "1": "odp" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%-d. %-m. %Y", + "1": "%d.%m.%y" + }, + "abmonth": "led,úno,bre,dub,kve,cvn,cvc,srp,zár,ríj,lis,pro", + "month": "leden,únor,brezen,duben,kveten,cerven,cervenec,srpen,zárí,ríjen,listopad,prosinec", + "abday": "ne,po,út,st,ct,pá,so", + "day": "nedele,pondelí,úterý,streda,ctvrtek,pátek,sobota", + "trans": { + "yes": "ano", + "Yes": "Ano", + "no": "ne", + "No": "Ne", + "ok": "ok", + "on": "zap", + "off": "vyp" + } + }, + "hr_HR": { + "lang": "hr_HR", + "icon": "🇭🇷", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": ".", + "speed": "km/h", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "dop", + "1": "pop" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%-d. %b %Y.", + "1": "%d.%m.%Y." + }, + "abmonth": "sij,velj,ožu,tra,svi,lip,srp,kol,ruj,lis,stu,pro", + "month": "sijecanj,veljaca,ožujak,travanj,svibanj,lipanj,srpanj,kolovoz,rujan,listopad,studeni,prosinac", + "abday": "ned,pon,uto,sri,cet,pet,sub", + "day": "nedjelja,ponedjeljak,utorak,srijeda,cetvrtak,petak,subota", + "trans": { + "yes": "da", + "Yes": "Da", + "no": "ne", + "No": "Ne", + "ok": "ok", + "on": "Uklj.", + "off": "Isklj.", + "< Back": "< Natrag" + } }, "sl_SI": { - lang: "sl_SI", - decimal_point: ",", - thousands_sep: ".", - speed: "km/h", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "dop.", 1: "pop." }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%-d. %b %Y", 1: "%-d.%-m.%Y" }, // "3. jan. 2020" // "3.1.2020"(short) - abmonth: "jan.,feb.,mar.,apr.,maj,jun.,jul.,avg.,sep.,okt.,nov.,dec.", - month: "januar,februar,marec,april,maj,junij,julij,avgust,september,oktober,november,december", - abday: "ned.,pon.,tor.,sre.,čet.,pet.,sob.", - day: "nedelja,ponedeljek,torek,sreda,četrtek,petek,sobota", - trans: { yes: "da", Yes: "Da", no: "ne", No: "Ne", ok: "ok", on: "Vklj.", off: "Izklj.", "< Back": "< Nazaj" } + "lang": "sl_SI", + "icon": "🇸🇮", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": ".", + "speed": "km/h", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "dop", + "1": "pop" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%-d. %b %Y", + "1": "%-d. %-m. %y" + }, + "abmonth": "jan,feb,mar,apr,maj,jun,jul,avg,sep,okt,nov,dec", + "month": "januar,februar,marec,april,maj,junij,julij,avgust,september,oktober,november,december", + "abday": "ned,pon,tor,sre,cet,pet,sob", + "day": "nedelja,ponedeljek,torek,sreda,cetrtek,petek,sobota", + "trans": { + "yes": "da", + "Yes": "Da", + "no": "ne", + "No": "Ne", + "ok": "ok", + "on": "Vklj.", + "off": "Izklj.", + "< Back": "< Nazaj" + } }, "pt_PT": { - lang: "pt_PT", - decimal_point: ",", - thousands_sep: " ", - speed: "kmh", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "am", 1: "pm" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%d %b %Y", 1: "%d/%m/%y" }, - abmonth: "Jan,Fev,Mar,Abr,Mai,Jun,Jul,Ago,Set,Out,Nov,Dez", - month: "Janeiro,Fevereiro,Março,Abril,Maio,Junho,Julho,Agosto,Setembro,Outubro,Novembro,Dezembro", - abday: "Dom,Seg,Ter,Qua,Qui,Sex,Sab", - day: "Domingo,Segunda-feira,Terça-feira,Quarta-feira,Quinta-feira,Sexta-feira,Sábado", - trans: { yes: "sim", Yes: "Sim", no: "não", No: "Não", ok: "ok", on: "on", off: "off" } + "lang": "pt_PT", + "icon": "🇵🇹", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": " ", + "speed": "kmh", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "am", + "1": "pm" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%d/%m/%Y", + "1": "%d/%m/%y" + }, + "abmonth": "jan,fev,mar,abr,mai,jun,jul,ago,set,out,nov,dez", + "month": "janeiro,fevereiro,março,abril,maio,junho,julho,agosto,setembro,outubro,novembro,dezembro", + "abday": "dom,seg,ter,qua,qui,sex,sáb", + "day": "domingo,segunda-feira,terça-feira,quarta-feira,quinta-feira,sexta-feira,sábado", + "trans": { + "yes": "sim", + "Yes": "Sim", + "no": "não", + "No": "Não", + "ok": "ok", + "on": "on", + "off": "off" + } }, "pl_PL": { - lang: "pl_PL", - decimal_point: ",", - thousands_sep: " ", - speed: "kmh", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "", 1: "" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%d. %b %Y", "1": "%d.%m.%Y" }, // 1. Mar 2021 // 01.03.2021 - abmonth: "Sty,Lut,Mar,Kwi,Maj,Cze,Lip,Sie,Wrz,Paź,Lis,Gru", - month: "Styczeń,Luty,Marzec,Kwiecień,Maj,Czerwiec,Lipiec,Sierpień,Wrzesień,Październik,Listopad,Grudzień", - abday: "Ndz,Pon,Wt,Śr,Czw,Pt,Sob", - day: "Niedziela,Poniedziałek,Wtorek,Środa,Czwartek,Piątek,Sobota", - trans: { yes: "tak", Yes: "Tak", no: "nie", No: "Nie", ok: "ok", on: "on", off: "off", "< Back": "< Wstecz" } - }, - "lv_LV": { // Using charfallbacks - lang: "lv_LV", - decimal_point: ",", - thousands_sep: " ", - speed: "kmh", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "", 1: "" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%d. %b %Y", "1": "%d.%m.%Y" }, // 1. Mar 2020 // 01.03.20 - abmonth: "Jan,Feb,Mar,Apr,Mai,Jūn,Jūl,Aug,Sep,Okt,Nov,Dec", - month: "Janvāris,Februāris,Marts,Aprīlis,Maijs,Jūnijs,Jūlijs,Augusts,Septemberis,Oktobris,Novembris,Decembris", - abday: "Pr,Ot,Tr,Ce,Pk,Se,Sv", - day: "Pirmdiena,Otrdiena,Trešdiena,Ceturtdiena,Piektdiena,Sestdiena,Svētdiena", - trans: { yes: "jā", Yes: "Jā", no: "nē", No: "Nē", ok: "labi", on: "Ieslēgt", off: "Izslēgt", "< Back": "< Atpakaļ" } - }, - "nn_NO": { // Using charfallbacks - lang: "nn_NO", - decimal_point: ",", - thousands_sep: " ", - speed: "kmt", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "", 1: "" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%d. %b %Y", "1": "%d.%m.%Y" }, // 1. Mar 2020 // 01.03.20 - abmonth: "Jan,Feb,Mar,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Des", - month: "Januar,Februar,Mars,April,Mai,Juni,Juli,August,September,Oktober,November,Desember", - abday: "Su,Må,Ty,On,To,Fr,La", - day: "Sundag,Måndag,Tysdag,Onsdag,Torsdag,Fredag,Laurdag", - trans: { yes: "ja", Yes: "Ja", no: "nei", No: "Nei", ok: "ok", on: "på", off: "av", "< Back": "< Tilbake", "Delete": "Slett", "Mark Unread": "Merk som ulesen" } - }, - "nb_NO": { // Using charfallbacks - lang: "nb_NO", - decimal_point: ",", - thousands_sep: " ", - speed: "kmh", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "", 1: "" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%d. %b %Y", "1": "%d.%m.%Y" }, // 1. Mar 2020 // 01.03.20 - abmonth: "Jan,Feb,Mar,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Des", - month: "Januar,Februar,Mars,April,Mai,Juni,Juli,August,September,Oktober,November,Desember", - abday: "Sø,Ma,Ti,On,To,Fr,Lø", - day: "Søndag,Mandag,Tirsdag,Onsdag,Torsdag,Fredag,Lørdag", - trans: { yes: "ja", Yes: "Ja", no: "nei", No: "Nei", ok: "ok", on: "på", off: "av", "< Back": "< Tilbake", "Delete": "Slett", "Mark Unread": "Merk som ulest" } + "lang": "pl_PL", + "icon": "🇵🇱", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": " ", + "speed": "kmh", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "AM", + "1": "PM" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%-d %b %Y", + "1": "%-d.%m.%Y" + }, + "abmonth": "sty,lut,mar,kwi,maj,cze,lip,sie,wrz,paz,lis,gru", + "month": "styczen,luty,marzec,kwiecien,maj,czerwiec,lipiec,sierpien,wrzesien,pazdziernik,listopad,grudzien", + "abday": "ndz,pon,wt,sr,czw,pt,sob", + "day": "niedziela,poniedzialek,wtorek,sroda,czwartek,piatek,sobota", + "trans": { + "yes": "tak", + "Yes": "Tak", + "no": "nie", + "No": "Nie", + "ok": "ok", + "on": "on", + "off": "off", + "< Back": "< Wstecz" + } + }, + "lv_LV": { + "lang": "lv_LV", + "icon": "🇱🇻", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": " ", + "speed": "kmh", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "pri", + "1": "pec" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%Y. %-d. %b", + "1": "%d.%m.%y" + }, + "abmonth": "jan,feb,mar,apr,mai,jun,jul,aug,sep,okt,nov,dec", + "month": "janvaris,februaris,marts,aprilis,maijs,junijs,julijs,augusts,septembris,oktobris,novembris,decembris", + "abday": "sv,pr,ot,tr,ce,pk,se", + "day": "Svetdiena,Pirmdiena,Otrdiena,Trešdiena,Ceturtdiena,Piektdiena,Sestdiena", + "trans": { + "yes": "ja", + "Yes": "Ja", + "no": "ne", + "No": "Ne", + "ok": "labi", + "on": "Ieslegt", + "off": "Izslegt", + "< Back": "< Atpakal" + } + }, + "nn_NO": { + "lang": "nn_NO", + "icon": "🇳🇴", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": " ", + "speed": "kmt", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "FM", + "1": "EM" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%-d. %b %Y", + "1": "%d.%m.%Y" + }, + "abmonth": "jan,feb,mar,apr,mai,jun,jul,aug,sep,okt,nov,des", + "month": "januar,februar,mars,april,mai,juni,juli,august,september,oktober,november,desember", + "abday": "søn,mån,tys,ons,tor,fre,lau", + "day": "søndag,måndag,tysdag,onsdag,torsdag,fredag,laurdag", + "trans": { + "yes": "ja", + "Yes": "Ja", + "no": "nei", + "No": "Nei", + "ok": "ok", + "on": "på", + "off": "av", + "< Back": "< Tilbake", + "Delete": "Slett", + "Mark Unread": "Merk som ulesen" + } + }, + "nb_NO": { + "lang": "nb_NO", + "icon": "🇳🇴", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": " ", + "speed": "kmh", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "AM", + "1": "PM" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%-d. %b %Y", + "1": "%d.%m.%Y" + }, + "abmonth": "jan,feb,mar,apr,mai,jun,jul,aug,sep,okt,nov,des", + "month": "januar,februar,mars,april,mai,juni,juli,august,september,oktober,november,desember", + "abday": "søn,man,tir,ons,tor,fre,lør", + "day": "søndag,mandag,tirsdag,onsdag,torsdag,fredag,lørdag", + "trans": { + "yes": "ja", + "Yes": "Ja", + "no": "nei", + "No": "Nei", + "ok": "ok", + "on": "på", + "off": "av", + "< Back": "< Tilbake", + "Delete": "Slett", + "Mark Unread": "Merk som ulest" + } }, "ca_ES": { - lang: "ca_ES", - icon: "🇪🇺", - decimal_point: ",", - thousands_sep: ".", - speed: "kmh", - distance: { 0: "m", 1: "km" }, - temperature: "°C", - ampm: { 0: "", 1: "" }, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%d %B %Y", "1": "%d/%m/%y" }, - abmonth: "gen.,febr.,març,abr.,maig,juny,jul.,ag.,set.,oct.,nov.,des.", - month: "gener,febrer,març,abril,maig,juny,juliol,agost,setembre,octubre,novembre,desembre", - abday: "dg.,dl.,dt.,dc.,dj.,dv.,ds.", - day: "diumenge,dilluns,dimarts,dimecres,dijous,divendres,dissabte", - trans: { yes: "sí", Yes: "Sí", no: "no", No: "No", ok: "d'acord", on: "on", off: "off", - "< Back": "< Enrere", "Delete": "Esborra", "Mark Unread": "Marca com a no llegit" } - }, -/*, - "he_IL": { // This won't work until we get a font - see https://github.com/espruino/BangleApps/issues/399 - codePage : "ISO8859-8", - lang: "he_IL", - decimal_point: ",", - thousands_sep: ".", - speed: "קמ״ש", - distance: { 0: "מ׳", 1: "ק״מ" }, - temperature: "°C", - ampm: {0:"am",1:"pm"}, - timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, - datePattern: { 0: "%A, %B %d, %Y", "1": "%d/%m/%Y" }, // Sunday, 1 March 2020 // 01/03/2020 - abmonth: "ינו,פבר,מרץ,אפר,מאי,יונ,יול,אוג,ספט,אוק,נוב,דצמ", - month: "ינואר,פברואר,מרץ,אפריל,מאי,יוני,יולי,אוגוסט,ספטמבר,אוקטובר,נובמבר,דצמבר", - abday: "א׳,ב׳,ג׳,ד׳,ה,ו׳,ש׳", - day: "ראשון,שני,שלישי,רביעי,חמישי,שישי,שבת", - trans: { yes: "כן", Yes: "כן", no: "לא", No: "לא", ok: "אישור", on: "פעיל", off: "כבוי" } - }*/ + "lang": "ca_ES", + "icon": "🇪🇺", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": ".", + "speed": "kmh", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "AM", + "1": "PM" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%-d %b %Y", + "1": "%-d/%-m/%y" + }, + "abmonth": "gen,febr,març,abr,maig,juny,jul,ag,set,oct,nov,des", + "month": "gener,febrer,març,abril,maig,juny,juliol,agost,setembre,octubre,novembre,desembre", + "abday": "dg.,dl.,dt.,dc.,dj.,dv.,ds.", + "day": "diumenge,dilluns,dimarts,dimecres,dijous,divendres,dissabte", + "trans": { + "yes": "sí", + "Yes": "Sí", + "no": "no", + "No": "No", + "ok": "d'acord", + "on": "on", + "off": "off", + "< Back": "< Enrere", + "Delete": "Esborra", + "Mark Unread": "Marca com a no llegit" + } + }, + "sq_AL": { + "lang": "sq_AL", + "icon": "🇦🇱", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": " ", + "speed": "km/h", + "distance": { + "0": "m", + "1": "km" + }, + "temperature": "°C", + "ampm": { + "0": "AM", + "1": "PM" + }, + "timePattern": { + "0": "%HH:%MM:%SS", + "1": "%HH:%MM" + }, + "datePattern": { + "0": "%-d %b %Y", + "1": "%-d.%-m.%y" + }, + "abmonth": "jan,shk,mar,pri,maj,qer,korr,gush,sht,tet,nën,dhj", + "month": "janar,shkurt,mars,prill,maj,qershor,korrik,gusht,shtator,tetor,nëntor,dhjetor", + "abday": "die,hën,mar,mër,enj,pre,sht", + "day": "e diel,e hënë,e martë,e mërkurë,e enjte,e premte,e shtunë" + }, + "ts_TS test": { + "lang": "ts_TS test", + "icon": "🐛", + "notes": "Produces the longest possible output. Useful for testing.", + "calendar": "gregory", + "numberingSystem": "latn", + "decimal_point": ",", + "thousands_sep": ",", + "speed": "km/h", + "distance": { + "0": "kmi", + "1": "kmi" + }, + "temperature": "°C", + "ampm": { + "0": "dop", + "1": "odp" + }, + "timePattern": { + "0": "%HHh%MM:%SS", + "1": "%HHh%MM" + }, + "datePattern": { + "0": "%b, %d, %Y", + "1": "%d. %m %Y" + }, + "abmonth": "mgmk,mgmk,mgmk,mgmk,mgmk,mgmk,mgmk,mgmk,mgmk,mgmk,mgmk,mgmk", + "month": "megmmaskuum,megmmaskuum,megmmaskuum,megmmaskuum,megmmaskuum,megmmaskuum,megmmaskuum,megmmaskuum,megmmaskuum,megmmaskuum,megmmaskuum,megmmaskuum", + "abday": "mgmk,mgmk,mgmk,mgmk,mgmk,mgmk,mgmk", + "day": "megmavammkkom,megmavammkkom,megmavammkkom,megmavammkkom,megmavammkkom,megmavammkkom,megmavammkkom" + } }; diff --git a/apps/locale/metadata.json b/apps/locale/metadata.json index 36cb2eae72..c2f76ca8a4 100644 --- a/apps/locale/metadata.json +++ b/apps/locale/metadata.json @@ -1,7 +1,7 @@ { "id": "locale", "name": "Languages", - "version": "0.19", + "version": "0.20", "description": "Translations for different countries", "icon": "locale.png", "type": "locale", diff --git a/bin/create-locales-js.mjs b/bin/create-locales-js.mjs new file mode 100755 index 0000000000..67c1c8a7cb --- /dev/null +++ b/bin/create-locales-js.mjs @@ -0,0 +1,677 @@ +#!/usr/bin/env node + +/** + * @file + * Will generate the `apps/locale/locales.js` file that contains locale-specific information for each supported language. + * Most of the information is gathered automatically from sources like CLDR, but some of it is manually set in `apps/locale/locales-meta.js`. + * + * Remember to run `npm install`, otherwise this script won't have access to the necessary libraries. + * + * To generate all locales, run: + * node bin/create-locales-js.mjs + * + * To generate all locales and be notified of minor issues, run: + * node bin/create-locales-js.mjs --ideal + * + * To see the output for a single locale, run: + * node bin/create-locales-js.mjs LOCALECODE + * + * LOCALECODE is the locale that you want to see the output for, e.g. "en_US". The locale must be defined in `apps/locale/locales-meta.js`. + * When outputting a single locale, the `--ideal` flag is automatically set. + */ + +import process from "node:process"; +import fs from "node:fs"; +import path from "node:path"; +import cldr from "cldr"; +import Utils from "../core/js/utils.js"; +import meta from "../apps/locale/locales-meta.js"; + +/** + * The name of the file that we import the metadata and seed locales from. + * This is only used for human-readable prompts. + */ +const inputPathFriendlyName = "apps/locale/locales-meta.js"; + +/** + * The file that the full locales will be written to. + */ +const outputPath = "apps/locale/locales.js"; + +/** + * An overview of all keys in a locale object. + * This is useful for multiple things: + * - ensure there are no unsupported keys in the seed data (add the key here and in the generator code if you need to support a new key) + * - ensure that keys in the output locales are always printed in this order. That helps a lot when looking at diffs of what changed + */ +const localeKeys = [ + "lang", + "fallbackLang", + "icon", + "notes", + "calendar", + "numberingSystem", + "decimal_point", + "thousands_sep", + "speed", + "distance", + "temperature", + "ampm", + "timePattern", + "datePattern", + "abmonth", + "month", + "abday", + "day", + "trans", +]; + +/** + * Maps from the Unicode standard datetime format to the custom datetime format used by Espruino. + * The Unicode standard is a superset of Espruinos, so some mappings are only approximations and other mappings are not supported at all. + * + * + * The unicode datetime format is documented here: + * https://www.unicode.org/reports/tr35/tr35-29.html#Date_Format_Patterns + * + * + * The Espruino datetime format is as follows: + * + * %Y year, all digits (2004, 2020, 32100) + * %y year, last two digits (04, 20, 00) + * %m month, two digits (01, 12) + * %-m month, one or two digits (1, 12) + * %d day of month, two digits (01, 26) + * %-d day of month, one or two digits (1, 26) + * + * %A locale's weekday name, full (e.g., Sunday) + * %a locale's weekday name, abbreviated (e.g., Sun) + * %B locale's month name, full (e.g., January) + * %b locale's month name, abbreviated (e.g., Jan) + * + * %HH hours, two digits (00..23) + * %MM minutes, two digits (00..59) + * %SS seconds, two digits (00..60) + * %P locale's equivalent of either am or pm, lowercase + * %p locale's equivalent of either AM or PM, uppercase + */ +const datetime_format_map = { + y: "%Y", + yy: "%y", + yyy: "%Y", + yyyy: "%Y", + Y: "%Y", + YY: "%y", + YYY: "%Y", + YYYY: "%Y", + M: "%-m", + MM: "%m", + MMM: "%b", + MMMM: "%B", + MMMMM: "%b", + L: "%-m", + LL: "%m", + LLL: "%b", + LLLL: "%B", + LLLLL: "%b", + d: "%-d", + dd: "%d", + E: "%a", + EE: "%a", + EEE: "%a", + EEEE: "%A", + EEEEE: "%a", + EEEEEE: "%a", + eee: "%a", + eeee: "%A", + eeeee: "%a", + eeeeee: "%a", + ccc: "%a", + cccc: "%A", + ccccc: "%a", + cccccc: "%a", + a: "", // should be %p but espruino time pattern should not include am/pm + h: "%HH", + hh: "%HH", + H: "%HH", + HH: "%HH", + m: "%MM", + mm: "%MM", + s: "%SS", + ss: "%SS", +}; +/** + * Maps the Espruino datetime format to max character lengths. + * Used when determining if a format is too long. + */ +const datetime_length_map = { + "%Y": 4, + "%y": 2, + "%m": 2, + "%-m": 2, + "%d": 2, + "%-d": 2, + "%A": 13, + "%a": 4, + "%B": 11, + "%b": 4, + "%HH": 2, + "%MM": 2, + "%SS": 2, + "%P": 0, + "%p": 0, +}; + +/** + * Takes a Unicode datetime format string and returns a datetime format string that Espruino can parse. + * The Unicode standard is a superset of Espruinos, so some mappings are only approximations and other mappings are not supported at all. + * + * @param {string} datetimeUnicode - The datetime Unicode format + * @returns {string} - The datetime Espruino format + */ +function transformDatetimeFormat(datetimeUnicode) { + let datetimeEspruino = ""; + let formatBuffer = ""; + function commitBuffer(i) { + if (formatBuffer[0] === "'") { + if (formatBuffer.length > 1 && formatBuffer.at(-1) === "'") { + datetimeEspruino += formatBuffer.slice(1, -1); + formatBuffer = ""; + } + } else if ( + formatBuffer.length > 0 && + formatBuffer[0] !== datetimeUnicode[i] + ) { + datetimeEspruino += datetime_format_map[formatBuffer] ?? formatBuffer; + formatBuffer = ""; + } + } + for (let i = 0; i < datetimeUnicode.length; i++) { + commitBuffer(i); + formatBuffer += datetimeUnicode[i]; + } + commitBuffer(datetimeUnicode.length); + // If we remove am/pm we sometimes need to remove some whitespace as well + datetimeEspruino = datetimeEspruino.trim(); + return datetimeEspruino; +} + +/** + * Takes an Espruino datetime format string and returns the maximum possible length of characters that the format could use. + * + * @param {string} datetimeEspruino - The datetime Espruino format + * @returns {number} - The maximum possible length of the format + */ +function getLengthOfDatetimeFormat(datetimeEspruino) { + let formatLength = 0; + let formatBuffer = ""; + function commitBuffer(i) { + if (formatBuffer.length === 0) { + if (datetimeEspruino[i] === "%") { + formatBuffer = "%"; + } else { + formatLength++; + } + } else { + formatBuffer += datetimeEspruino[i]; + const length = datetime_length_map[formatBuffer]; + if (length !== undefined) { + formatLength += length; + formatBuffer = ""; + } + } + } + for (let i = 0; i < datetimeEspruino.length; i++) { + commitBuffer(i); + } + return formatLength; +} + +/** + * Most country codes have a corresponding emoji flag, this function will return that emoji. + * WARNING: There is no check to see if the emoji is supported by various ecosystems. + * + * @param {string} countryCode - The two-letter country code + * @returns {string} - The emoji for that country + */ +function getFlagEmoji(countryCode) { + const codePoints = countryCode + .toUpperCase() + .split("") + .map((char) => 127397 + char.charCodeAt()); + return String.fromCodePoint(...codePoints); +} + +/** + * We use the Ecmascript standard to define which calendar to use, but the CLDR package sometimes uses different names. + * This maps from Ecmascript calendar ids to CLDR calendar ids. + */ +const calendar_id_map = { + gregory: "gregorian", + "islamic-civil": "islamicc", +}; + +/** + * Checks if the CLDR database contains data on the specified locale. + * The check is memoized so it is fine to call this many times. + * + * @param {string} localeId - The locale id to check. + * @returns {boolean} - True if the data exists, false if not. + */ +function checkCLDR(localeId) { + if (idInCLDR.has(localeId)) return true; + if (cldr.localeIds.includes(localeId.toLowerCase())) { + idInCLDR.add(localeId); + return true; + } else { + return false; + } +} +const idInCLDR = new Set(); + +/** + * The main function that takes a minimal seed locale and generates all other locale values based on the seed. + * Refer to the documentation at the top of this file to learn more. + * + * @param {string} localeName - The user-facing locale name. Locale names must start with a valid locale id such as "en_US". + * @param {object} locales - The manually defined locale data. + * @returns {boolean} - The full locale information. + * @throws if it is unable to determine what a value should be, the error message should explain what value needs to be set manually. + */ +function generateLocale(localeName, locales) { + let locale = locales[localeName]; + + for (const key of Object.keys(locale)) { + if (!localeKeys.includes(key)) { + throw new TypeError( + `The locale ${localeName} contains a key '${key}' that is not supported. Maybe you misspelled it?`, + ); + } + } + + if (!locale.calendar) { + throw new TypeError( + `The locale ${localeName} must define a valid calendar`, + ); + } + const calendar = calendar_id_map[locale.calendar] ?? locale.calendar; + + if (!locale.numberingSystem) { + throw new TypeError( + `The locale ${localeName} must define a valid numbering system`, + ); + } + const numberingSystem = locale.numberingSystem; + + const localeMatch = localeName.match( + /^(?[a-zA-Z_]*_(?:(?[A-Z]{2})|(?[A-Z]{3})))(?:$|[^a-zA-Z_])/u, + ); + const localeId = localeMatch?.groups?.localeId; + const countryIdA2 = localeMatch?.groups?.countryIdA2; + + // If ever needed, you can convert A3 locale id's to A2 here. + const countryIdA3 = localeMatch?.groups?.countryIdA3; + + if ((!localeId || !countryIdA2) && !locale.fallbackLang) { + throw new TypeError( + `The locale ${localeName} must start with a valid locale id, such as "en_US" or "da_DK"`, + ); + } + const countryId = countryIdA2; + + if (locale.lang && locale.lang !== localeName) { + throw new TypeError( + `locale.lang is not identical to the locale key for locale ${localeName}, please make it`, + ); + } + locale.lang ||= localeName; + + let fallbackLocale; + if (locale.fallbackLang) { + fallbackLocale = generateLocale(locale.fallbackLang, locales); + } + + if (!locale.icon) { + locale.icon ||= countryId + ? getFlagEmoji(countryId) + : undefined || fallbackLocale?.icon; + if (!locale.icon) { + throw new TypeError( + `Unable to determine a suitable icon for locale ${localeName}, please provide it manually`, + ); + } + } + if (!locale.decimal_point || !locale.thousands_sep) { + if (checkCLDR(localeId)) { + const numberSymbols = cldr.extractNumberSymbols( + localeId, + numberingSystem, + ); + locale.decimal_point ||= numberSymbols.decimal; + locale.thousands_sep ||= numberSymbols.group; + } + locale.decimal_point ||= fallbackLocale?.decimal_point; + locale.thousands_sep ||= fallbackLocale?.thousands_sep; + if (!locale.decimal_point || !locale.thousands_sep) { + throw new TypeError( + `Could not determine the decimal and thousands separators for locale ${localeName}, please provide them manually`, + ); + } + } + locale.speed ||= fallbackLocale?.speed || "km/h"; // true 99% of the time, some locales prefer a translated name but often still accept the english version + locale.distance ||= {}; + locale.distance["0"] ||= fallbackLocale?.distance["0"] || "m"; // true 99% of the time + locale.distance["1"] ||= fallbackLocale?.distance["1"] || "km"; // true 99% of the time + locale.temperature ||= fallbackLocale?.temperature || "°C"; // true 99% of the time + if (!locale.ampm?.["0"] || !locale.ampm?.["1"]) { + locale.ampm ||= {}; + if (checkCLDR(localeId)) { + const dayPeriods = cldr.extractDayPeriods(localeId, calendar)?.standAlone + ?.abbreviated; + locale.ampm["0"] ||= dayPeriods.am; + locale.ampm["1"] ||= dayPeriods.pm; + } + locale.ampm["0"] ||= fallbackLocale?.ampm?.["0"]; + locale.ampm["1"] ||= fallbackLocale?.ampm?.["1"]; + if (!locale.ampm?.["0"] || !locale.ampm?.["1"]) { + throw new TypeError( + `Could not determine AM/PM format for locale ${localeName}, please provide it manually`, + ); + } + } + if (!locale.timePattern?.["0"] || !locale.timePattern?.["1"]) { + locale.timePattern ||= {}; + if (checkCLDR(localeId)) { + const timeFormats = cldr.extractTimeFormats(localeId, calendar); + locale.timePattern["0"] ||= transformDatetimeFormat(timeFormats.medium); + locale.timePattern["1"] ||= transformDatetimeFormat(timeFormats.short); + } + locale.timePattern["0"] ||= fallbackLocale?.timePattern["0"]; + locale.timePattern["1"] ||= fallbackLocale?.timePattern["1"]; + if (!locale.timePattern?.["0"] || !locale.timePattern?.["1"]) { + throw new TypeError( + `Could not determine time format for locale ${localeName}, please provide it manually`, + ); + } + } + if (!locale.datePattern?.["0"] || !locale.datePattern?.["1"]) { + locale.datePattern ||= {}; + if (checkCLDR(localeId)) { + const dateFormats = cldr.extractDateFormats(localeId, calendar); + locale.datePattern["0"] ||= transformDatetimeFormat(dateFormats.medium); + locale.datePattern["1"] ||= transformDatetimeFormat(dateFormats.short); + } + locale.datePattern["0"] ||= fallbackLocale?.datePattern["0"]; + locale.datePattern["1"] ||= fallbackLocale?.datePattern["1"]; + if (!locale.datePattern?.["0"] || !locale.datePattern?.["1"]) { + throw new TypeError( + `Could not determine date format for locale ${localeName}, please provide it manually`, + ); + } + } + if (!locale.abmonth || !locale.month) { + if (checkCLDR(localeId)) { + const months = cldr.extractMonthNames(localeId, calendar); + locale.abmonth ||= months?.standAlone?.abbreviated?.join(","); + locale.month ||= months?.standAlone?.wide?.join(","); + } + locale.abmonth ||= fallbackLocale?.abmonth; + locale.month ||= fallbackLocale?.month; + if (!locale.abmonth || !locale.month) { + throw new TypeError( + `Could not determine month names for locale ${localeName}, please provide them manually`, + ); + } + } + if (!locale.abday || !locale.day) { + if (checkCLDR(localeId)) { + const days = cldr.extractDayNames(localeId, calendar); + locale.abday ||= days?.standAlone?.abbreviated?.join(","); + locale.day ||= days?.standAlone?.wide?.join(","); + } + locale.abday ||= fallbackLocale?.abday; + locale.day ||= fallbackLocale?.day; + if (!locale.abday || !locale.day) { + throw new TypeError( + `Could not determine day names for locale ${localeName}, please provide them manually`, + ); + } + } + + // Save space by deleting fields that will evaluate to false + for (const key of Object.keys(locale)) { + if (locale[key] === false) { + delete locale[key]; + } + } + + // There is no need to include information about how we internally generate the language + delete locale.fallbackLang; + + locale = approximateChars(locale); + + // Order all keys in the locale to ensure consistent output + const orderedLocale = {}; + for (const key of localeKeys) { + if (key in locale) orderedLocale[key] = locale[key]; + } + + return orderedLocale; +} + +/** + * Checks whether a generated locale conforms to some basic standards. + * It is possible that the CLDR data does not conform to a standard, in which case manual data must be entered. + * + * @param {object} locale - The locale to test. + * @param {boolean} checkIdeal - If true, logs comments about aspects that could be more ideal. + * @returns {boolean} - True if the locale is conformant, false if something needs to be changed. + */ +function checkLocale(locale, checkIdeal = false) { + let localePasses = true; + function checkLength(name, string, max, ideal) { + if (string.length > max) { + console.error( + `${name} "${string}" in locale ${locale.lang} must not be longer than ${max} characters`, + ); + localePasses = false; + } + if (checkIdeal && ideal && string.length > ideal) { + console.warn( + `${name} "${string}" in locale ${locale.lang} should ideally not be longer than ${ideal} characters`, + ); + } + } + function checkFormatLength(name, format, max, ideal) { + const length = getLengthOfDatetimeFormat(format); + if (length > max) { + console.error( + `${name} "${format}" in locale ${locale.lang} must not be longer than ${max} characters`, + ); + localePasses = false; + } + if (checkIdeal && ideal && length > ideal) { + console.warn( + `${name} "${format}" in locale ${locale.lang} should ideally not be longer than ${ideal} characters`, + ); + } + } + function checkIsIn(name, value, listName, list) { + if (!list.includes(value)) { + console.error( + `${name} "${value}" must be included in the ${listName} map`, + ); + localePasses = false; + } + } + checkLength("decimal point", locale.decimal_point, 1); + checkLength("thousands separator", locale.decimal_point, 1); + checkLength("speed", locale.speed, 4); + const speedUnits = Object.keys(meta.speedUnits); + checkIsIn("speed", locale.speed, "speedUnits", speedUnits); + checkLength("distance", locale.distance["0"], 3, 2); + checkLength("distance", locale.distance["1"], 3, 2); + const distanceUnits = Object.keys(meta.distanceUnits); + checkIsIn("distance", locale.distance["0"], "distanceUnits", distanceUnits); + checkIsIn("distance", locale.distance["1"], "distanceUnits", distanceUnits); + checkLength("temperature", locale.temperature, 2); + checkLength("AM", locale.ampm["0"], 3, 2); + checkLength("PM", locale.ampm["1"], 3, 2); + checkFormatLength("long time", locale.timePattern["0"], 8); + checkFormatLength("short time", locale.timePattern["1"], 5); + checkFormatLength("long date", locale.datePattern["0"], 14, 12); + checkFormatLength("short date", locale.datePattern["1"], 11, 9); + for (const abmonth of locale.abmonth.split(",")) { + checkLength("abmonth", abmonth, 4, 3); + } + for (const month of locale.month.split(",")) { + checkLength("month", month, 11); + } + for (const abday of locale.abday.split(",")) { + checkLength("abday", abday, 4, 3); + } + for (const day of locale.day.split(",")) { + checkLength("day", day, 13); + } + return localePasses; +} + +/** + * Replaces unsupported string characters with similar characters that are supported. + * + * @param {object} object - The object where all strings should be checked for unsupported characters. + * @returns {object} - The same object, strings are now using supported characters. + */ +function approximateChars(object) { + for (let [key, value] of Object.entries(object)) { + // emoji icons are not used on the watch, so they are free to use fancy chars + if (key === "icon") continue; + if (typeof value === "string") { + const stringChars = value.split(""); + let newString = ""; + for (const char of stringChars) { + newString += codePageLookup(char); + } + object[key] = newString; + } else if ( + typeof value === "object" && + value !== null && + !Array.isArray(value) + ) { + approximateChars(value); + } + } + return object; + function codePageLookup(char) { + const charCode = char.charCodeAt(); + if (charCode >= 32 && charCode < 128) { + // ASCII - fully supported + return char; + } else if (meta.codePages["ISO8859-1"].map.indexOf(char) >= 0) { + // At upload time, the char can be converted to a custom codepage + return char; + } else if (Utils.CODEPAGE_CONVERSIONS[char]) { + // There is a custom fallback char for this, return it + return Utils.CODEPAGE_CONVERSIONS[char]; + } + throw new Error( + `Character ${char} (${charCode}) is not supported by Banglejs. Please add it to CODEPAGE_CONVERSIONS.`, + ); + } +} + +/** + * Outputs the metadata and locales as a JS file. + * + * @param {object} meta - The metadata + * @param {object} locales - The generated locales + */ +function outputLocales(meta, locales) { + let content = `/* jshint esversion: 6 */ +/* exported distanceUnits */ +/* exported speedUnits */ +/* exported codePages */ +/* exported datePatterns */ +/* exported timePatterns */ +/* exported meridians */ +/* exported locales */ +/** + * THIS FILE IS AUTOGENERATED. + * If you need to edit it, please do so in '${inputPathFriendlyName}' instead. + */ +`; + + const localeValues = Object.values(locales); + const datePatterns = { + 0: [ + ...new Set(localeValues.map((locale) => locale.datePattern["0"])), + ].sort(), + 1: [ + ...new Set(localeValues.map((locale) => locale.datePattern["1"])), + ].sort(), + }; + const timePatterns = { + 0: [ + ...new Set(localeValues.map((locale) => locale.timePattern["0"])), + ].sort(), + 1: [ + ...new Set(localeValues.map((locale) => locale.timePattern["1"])), + ].sort(), + }; + const meridians = { + 0: [ + ...new Set(localeValues.map((locale) => locale.ampm["0"])), + ].sort(), + 1: [ + ...new Set(localeValues.map((locale) => locale.ampm["1"])), + ].sort(), + } + + const indent = 2; + content += `\nconst distanceUnits = ${JSON.stringify(meta.distanceUnits, undefined, indent)};\n`; + content += `const speedUnits = ${JSON.stringify(meta.speedUnits, undefined, indent)};\n`; + content += `\nconst codePages = ${JSON.stringify(meta.codePages, undefined, indent)};\n`; + content += `\nconst datePatterns = ${JSON.stringify(datePatterns, undefined, indent)};\n`; + content += `\nconst timePatterns = ${JSON.stringify(timePatterns, undefined, indent)};\n`; + content += `\nconst meridians = ${JSON.stringify(meridians, undefined, indent)};\n`; + content += `\nvar locales = ${JSON.stringify(locales, undefined, indent)};\n`; + + fs.writeFileSync(path.resolve(outputPath), content); +} + +const idealFlag = "--ideal"; +const arg = process.argv[2]; +if (arg && arg !== idealFlag) { + console.log("Dev mode, creating", arg); + const devLocale = meta.locales[arg]; + if (!devLocale) { + throw new TypeError( + `You need to define the locale in '${inputPathFriendlyName}' before this program can work. The key must be '${arg}'.`, + ); + } + const generatedLocale = generateLocale(arg, meta.locales); + console.log(generatedLocale); + checkLocale(generatedLocale, true); +} else { + const localeNames = Object.keys(meta.locales); + const createdLocales = {}; + console.log( + `Creating ${localeNames.length} locales, this might take a minute...`, + ); + let localesPassed = true; + for (const localeName of localeNames) { + const generatedLocale = generateLocale(localeName, meta.locales); + createdLocales[localeName] = generatedLocale; + const localePassed = checkLocale(generatedLocale, arg === idealFlag); + if (!localePassed) localesPassed = false; + } + console.log(`Writing locales to '${outputPath}'`); + outputLocales(meta, createdLocales); + if (localesPassed) { + console.log(`Succesfully created all locales!`); + } else { + throw new Error( + "Some locales do not conform to standards, please fix this.", + ); + } +} diff --git a/core b/core index dc682af217..7e9095073f 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit dc682af2179512525474c43ed616c801a9570ab8 +Subproject commit 7e9095073f2121afad4bf1d9514d3d049f7be680 diff --git a/package-lock.json b/package-lock.json index 71f5cec909..f186da7747 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,8 @@ "acorn": "^7.2.0" }, "devDependencies": { + "@types/cldr": "^7.1.4", + "cldr": "^7.5.0", "eslint": "^8.57.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-plugin-import": "^2.29.1", @@ -152,6 +154,12 @@ "node": ">= 8" } }, + "node_modules/@types/cldr": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/@types/cldr/-/cldr-7.1.4.tgz", + "integrity": "sha512-p4J5+33G/iniIYGkt8ry/rCoBtJWSC2xFpW9LCygtkBNznKb870KFT/LCEU2Jd9h/6g1PteglodpWBjWdCKlpA==", + "dev": true + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -164,6 +172,15 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -487,6 +504,18 @@ "node": ">=6" } }, + "node_modules/chainsaw": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.0.9.tgz", + "integrity": "sha512-nG8PYH+/4xB+8zkV4G844EtfvZ5tTiLFoX3dZ4nhF4t3OCKIb9UvaFyNmeZO2zOSmRWzBoTD+napN6hiL+EgcA==", + "dev": true, + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -542,6 +571,23 @@ "node": ">= 6" } }, + "node_modules/cldr": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/cldr/-/cldr-7.5.0.tgz", + "integrity": "sha512-2qy3ASYFbNToTujNnk5Y8ak++B4TH/G+S8AEOrN1xUFZhxhmqWDPUGnOFGyId61vD2Trf+yE65wVzIcdE/bpPg==", + "dev": true, + "dependencies": { + "@xmldom/xmldom": "^0.8.0", + "escodegen": "^2.0.0", + "esprima": "^4.0.1", + "memoizeasync": "^1.1.0", + "passerror": "^1.1.1", + "pegjs": "^0.10.0", + "seq": "^0.3.5", + "unicoderegexp": "^0.4.1", + "xpath": "^0.0.33" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -798,6 +844,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, "node_modules/eslint": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", @@ -1027,6 +1094,19 @@ "node": ">=0.4.0" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", @@ -1431,6 +1511,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hashish": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/hashish/-/hashish-0.0.4.tgz", + "integrity": "sha512-xyD4XgslstNAs72ENaoFvgMwtv8xhiDtC2AtzCG+8yF7W/Knxxm9BX+e2s25mm+HxMKh0rBmXVOEGF3zNImXvA==", + "dev": true, + "dependencies": { + "traverse": ">=0.2.4" + }, + "engines": { + "node": "*" + } + }, "node_modules/hasown": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", @@ -1924,6 +2016,22 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lru-cache": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz", + "integrity": "sha512-dVmQmXPBlTgFw77hm60ud//l2bCuDKkqC2on1EBoM7s9Urm9IQDrnujwZ93NFnAq0dVZ0HBXTS7PwEG+YE7+EQ==", + "dev": true + }, + "node_modules/memoizeasync": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/memoizeasync/-/memoizeasync-1.1.0.tgz", + "integrity": "sha512-HMfzdLqClZo8HMyuM9B6TqnXCNhw82iVWRLqd2cAdXi063v2iJB4mQfWFeKVByN8VUwhmDZ8NMhryBwKrPRf8Q==", + "dev": true, + "dependencies": { + "lru-cache": "2.5.0", + "passerror": "1.1.1" + } + }, "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -2256,6 +2364,15 @@ "node": ">=6" } }, + "node_modules/passerror": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/passerror/-/passerror-1.1.1.tgz", + "integrity": "sha512-PwrEQJBkJMxnxG+tdraz95vTstYnCRqiURNbGtg/vZHLgcAODc9hbiD5ZumGUoh3bpw0F0qKLje7Vd2Fd5Lx3g==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -2289,6 +2406,18 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/pegjs": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/pegjs/-/pegjs-0.10.0.tgz", + "integrity": "sha512-qI5+oFNEGi3L5HAxDwN2LA4Gg7irF70Zs25edhjld9QemOgp0CbvMtbFcMvFtEo1OityPrcCzkQFB8JP/hxgow==", + "dev": true, + "bin": { + "pegjs": "bin/pegjs" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -2592,6 +2721,19 @@ "semver": "bin/semver.js" } }, + "node_modules/seq": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/seq/-/seq-0.3.5.tgz", + "integrity": "sha512-sisY2Ln1fj43KBkRtXkesnRHYNdswIkIibvNe/0UKm2GZxjMbqmccpiatoKr/k2qX5VKiLU8xm+tz/74LAho4g==", + "dev": true, + "dependencies": { + "chainsaw": ">=0.0.7 <0.1", + "hashish": ">=0.0.2 <0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/set-function-length": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", @@ -2680,6 +2822,16 @@ "semver": "bin/semver.js" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -2830,6 +2982,15 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -2960,6 +3121,12 @@ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", "dev": true }, + "node_modules/unicoderegexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/unicoderegexp/-/unicoderegexp-0.4.1.tgz", + "integrity": "sha512-ydh8D5mdd2ldTS25GtZJEgLciuF0Qf2n3rwPhonELk3HioX201ClYGvZMc1bCmx6nblZiADQwbMWekeIqs51qw==", + "dev": true + }, "node_modules/union": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", @@ -3061,6 +3228,15 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/xpath": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.33.tgz", + "integrity": "sha512-NNXnzrkDrAzalLhIUc01jO2mOzXGXh1JwPgkihcLLzw98c0WgYDmmjSh1Kl3wzaxSVWMuA+fe0WTWOBDWCBmNA==", + "dev": true, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -3168,6 +3344,12 @@ "fastq": "^1.6.0" } }, + "@types/cldr": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/@types/cldr/-/cldr-7.1.4.tgz", + "integrity": "sha512-p4J5+33G/iniIYGkt8ry/rCoBtJWSC2xFpW9LCygtkBNznKb870KFT/LCEU2Jd9h/6g1PteglodpWBjWdCKlpA==", + "dev": true + }, "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -3180,6 +3362,12 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true }, + "@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "dev": true + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -3415,6 +3603,15 @@ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, + "chainsaw": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.0.9.tgz", + "integrity": "sha512-nG8PYH+/4xB+8zkV4G844EtfvZ5tTiLFoX3dZ4nhF4t3OCKIb9UvaFyNmeZO2zOSmRWzBoTD+napN6hiL+EgcA==", + "dev": true, + "requires": { + "traverse": ">=0.3.0 <0.4" + } + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -3452,6 +3649,23 @@ } } }, + "cldr": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/cldr/-/cldr-7.5.0.tgz", + "integrity": "sha512-2qy3ASYFbNToTujNnk5Y8ak++B4TH/G+S8AEOrN1xUFZhxhmqWDPUGnOFGyId61vD2Trf+yE65wVzIcdE/bpPg==", + "dev": true, + "requires": { + "@xmldom/xmldom": "^0.8.0", + "escodegen": "^2.0.0", + "esprima": "^4.0.1", + "memoizeasync": "^1.1.0", + "passerror": "^1.1.1", + "pegjs": "^0.10.0", + "seq": "^0.3.5", + "unicoderegexp": "^0.4.1", + "xpath": "^0.0.33" + } + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -3649,6 +3863,18 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, + "escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "source-map": "~0.6.1" + } + }, "eslint": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", @@ -3829,6 +4055,12 @@ } } }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, "esquery": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", @@ -4110,6 +4342,15 @@ "has-symbols": "^1.0.3" } }, + "hashish": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/hashish/-/hashish-0.0.4.tgz", + "integrity": "sha512-xyD4XgslstNAs72ENaoFvgMwtv8xhiDtC2AtzCG+8yF7W/Knxxm9BX+e2s25mm+HxMKh0rBmXVOEGF3zNImXvA==", + "dev": true, + "requires": { + "traverse": ">=0.2.4" + } + }, "hasown": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", @@ -4462,6 +4703,22 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "lru-cache": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz", + "integrity": "sha512-dVmQmXPBlTgFw77hm60ud//l2bCuDKkqC2on1EBoM7s9Urm9IQDrnujwZ93NFnAq0dVZ0HBXTS7PwEG+YE7+EQ==", + "dev": true + }, + "memoizeasync": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/memoizeasync/-/memoizeasync-1.1.0.tgz", + "integrity": "sha512-HMfzdLqClZo8HMyuM9B6TqnXCNhw82iVWRLqd2cAdXi063v2iJB4mQfWFeKVByN8VUwhmDZ8NMhryBwKrPRf8Q==", + "dev": true, + "requires": { + "lru-cache": "2.5.0", + "passerror": "1.1.1" + } + }, "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -4705,6 +4962,12 @@ "callsites": "^3.0.0" } }, + "passerror": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/passerror/-/passerror-1.1.1.tgz", + "integrity": "sha512-PwrEQJBkJMxnxG+tdraz95vTstYnCRqiURNbGtg/vZHLgcAODc9hbiD5ZumGUoh3bpw0F0qKLje7Vd2Fd5Lx3g==", + "dev": true + }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -4729,6 +4992,12 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "pegjs": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/pegjs/-/pegjs-0.10.0.tgz", + "integrity": "sha512-qI5+oFNEGi3L5HAxDwN2LA4Gg7irF70Zs25edhjld9QemOgp0CbvMtbFcMvFtEo1OityPrcCzkQFB8JP/hxgow==", + "dev": true + }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -4922,6 +5191,16 @@ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true }, + "seq": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/seq/-/seq-0.3.5.tgz", + "integrity": "sha512-sisY2Ln1fj43KBkRtXkesnRHYNdswIkIibvNe/0UKm2GZxjMbqmccpiatoKr/k2qX5VKiLU8xm+tz/74LAho4g==", + "dev": true, + "requires": { + "chainsaw": ">=0.0.7 <0.1", + "hashish": ">=0.0.2 <0.1" + } + }, "set-function-length": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", @@ -4991,6 +5270,13 @@ } } }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -5102,6 +5388,12 @@ "nopt": "~1.0.10" } }, + "traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "dev": true + }, "tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -5199,6 +5491,12 @@ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", "dev": true }, + "unicoderegexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/unicoderegexp/-/unicoderegexp-0.4.1.tgz", + "integrity": "sha512-ydh8D5mdd2ldTS25GtZJEgLciuF0Qf2n3rwPhonELk3HioX201ClYGvZMc1bCmx6nblZiADQwbMWekeIqs51qw==", + "dev": true + }, "union": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", @@ -5279,6 +5577,12 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "xpath": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.33.tgz", + "integrity": "sha512-NNXnzrkDrAzalLhIUc01jO2mOzXGXh1JwPgkihcLLzw98c0WgYDmmjSh1Kl3wzaxSVWMuA+fe0WTWOBDWCBmNA==", + "dev": true + }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 10d0c67681..318ad604db 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,8 @@ "license": "MIT", "repository": "https://github.com/espruino/BangleApps", "devDependencies": { + "@types/cldr": "^7.1.4", + "cldr": "^7.5.0", "eslint": "^8.57.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-plugin-import": "^2.29.1", @@ -17,11 +19,13 @@ "lint-modules": "eslint ./modules --ext .js", "test": "node bin/sanitycheck.js && npm run lint-apps && npm run lint-modules", "update-local-apps": "./bin/create_apps_json.sh apps.local.json", + "update-locales": "./bin/create-locales-js.mjs", "local": "npm-watch & npx http-server -a localhost -c-1", "start": "npx http-server -c-1" }, "watch": { - "update-local-apps": "apps/*/metadata.json" + "update-local-apps": "apps/*/metadata.json", + "update-locales": "apps/locale/locales-meta.js" }, "dependencies": { "acorn": "^7.2.0"