VulpesOS/gaia/shared/js/language_list.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

175 lines
4.9 KiB
JavaScript
Executable file

'use strict';
/**
* Helper object to find all supported languages;
*
* (Needs l10n.js or l20n.js, and the settings permission)
*/
(function(exports) {
var LOCALES_FILE = '/shared/resources/languages.json';
function readFile(file, callback) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function loadFile() {
if (xhr.readyState === 4) {
if (xhr.status === 0 || xhr.status === 200) {
resolve(xhr.response);
} else {
reject(xhr.statusText);
}
}
};
xhr.open('GET', file, true); // async
xhr.responseType = 'json';
xhr.send();
});
}
function onError(name) {
console.error('Error checking setting ' + name);
}
function readSetting(name, callback) {
var settings = window.navigator.mozSettings;
if (!settings || !settings.createLock) {
return callback(null);
}
return settings.createLock().get(name).then(function(res) {
return res[name];
}, onError.bind(null, name));
}
exports.LanguageList = {
_languages: null,
// for stubbing in tests
_readFile: readFile,
_readSetting: readSetting,
_extendPseudo: function(languages, currentLang, pseudoEnabled) {
// 1. remove buildtime pseudolocales is pseudo is disabled
if (!pseudoEnabled) {
for (var code in languages) {
var isCurrent = (code === currentLang);
if (code.indexOf('-x-ps') > -1 && !isCurrent) {
delete languages[code];
}
}
}
if (!document.l10n) {
return Promise.resolve(languages);
}
// 2. add the remaining runtime pseudolocales if pseudo enabled
var runtimePseudoCodes = Object.keys(document.l10n.pseudo).filter(
code => !(code in languages) && (code === currentLang || pseudoEnabled));
function obj(arr1, arr2) {
const zipped = [];
arr1.forEach(x => {
arr2.forEach(y => {
zipped.push([x, y]);
});
});
return zipped.reduce(
(obj, [x, y]) => Object.assign(obj, { [x]: y }), {});
/* jshint ignore: end */
}
return Promise.all(
runtimePseudoCodes.map(
code => document.l10n.pseudo[code].getName())).then(
names => Object.assign(languages, obj(runtimePseudoCodes, names)));
},
_extendAdditional: function(languages, ver, additional) {
/* jshint boss:true */
for (var lang in additional) {
for (var i = 0, locale; locale = additional[lang][i]; i++) {
if (locale.target === ver) {
languages[lang] = locale.name;
break;
}
}
}
},
_removeWithNoSpeech: function(languages, srEnabled) {
if (srEnabled) {
var speechLangs = new Set(
window.speechSynthesis.getVoices().map(v => v.lang.split('-')[0]));
for (var langName in languages) {
if (!speechLangs.has(langName.split('-')[0])) {
delete languages[langName];
}
}
}
},
_copyObj: function(obj) {
var copy = Object.create(null);
for (var prop in obj) {
copy[prop] = obj[prop];
}
return copy;
},
_parseVersion: function(ver) {
return ver.split('.').slice(0, 2).join('.');
},
_build: function() {
return Promise.all([
this._languages || (this._languages = this._readFile(LOCALES_FILE)),
this._readSetting('language.current'),
this._readSetting('devtools.pseudolocalization.enabled')
]).then(([langsFromFile, current, pseudoEnabled]) => {
var langs = this._copyObj(langsFromFile);
return Promise.all([
this._extendPseudo(langs, current, pseudoEnabled),
this._readSetting('langpack.channel'),
current,
this._readSetting('accessibility.screenreader'),
navigator.mozApps.getAdditionalLanguages()
]);
}).then(([langs, ver, current, srEnabled, addl]) => {
this._extendAdditional(langs, this._parseVersion(ver), addl);
this._removeWithNoSpeech(langs, srEnabled);
return [langs, current];
});
},
get: function(callback) {
if (!callback) {
return;
}
this._build().then(Function.prototype.apply.bind(callback, null));
},
wrapBidi: function(langCode, langName) {
// Right-to-Left (RTL) languages:
// (http://www.w3.org/International/questions/qa-scripts)
// Arabic, Hebrew, Farsi, Pashto, Bidi English (pseudo), Urdu
var rtlList = ['ar', 'he', 'fa', 'ps', 'ar-x-psbidi', 'ur'];
// Use script direction control-characters to wrap the text labels
// since markup (i.e. <bdo>) does not work inside <option> tags
// http://www.w3.org/International/tutorials/bidi-xhtml/#nomarkup
var lEmbedBegin =
(rtlList.indexOf(langCode) >= 0) ? '\u202b' : '\u202a';
var lEmbedEnd = '\u202c';
// The control-characters enforce the language-specific script
// direction to correctly display the text label (Bug #851457)
return lEmbedBegin + langName + lEmbedEnd;
}
};
}(window));