165 lines
4.6 KiB
JavaScript
165 lines
4.6 KiB
JavaScript
/*
|
|
* noVNC: HTML5 VNC client
|
|
* Copyright (C) 2012 Joel Martin
|
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
|
*
|
|
* See README.md for usage and integration instructions.
|
|
*/
|
|
|
|
/*
|
|
* Localization Utilities
|
|
*/
|
|
|
|
export class Localizer {
|
|
constructor() {
|
|
// Currently configured language
|
|
this.language = 'en';
|
|
|
|
// Current dictionary of translations
|
|
this.dictionary = undefined;
|
|
}
|
|
|
|
// Configure suitable language based on user preferences
|
|
setup(supportedLanguages) {
|
|
this.language = 'en'; // Default: US English
|
|
|
|
/*
|
|
* Navigator.languages only available in Chrome (32+) and FireFox (32+)
|
|
* Fall back to navigator.language for other browsers
|
|
*/
|
|
let userLanguages;
|
|
if (typeof window.navigator.languages == 'object') {
|
|
userLanguages = window.navigator.languages;
|
|
} else {
|
|
userLanguages = [navigator.language || navigator.userLanguage];
|
|
}
|
|
|
|
for (let i = 0; i < userLanguages.length; i++) {
|
|
const userLang = userLanguages[i]
|
|
.toLowerCase()
|
|
.replace('_', '-')
|
|
.split('-');
|
|
|
|
// Built-in default?
|
|
if ((userLang[0] === 'en')
|
|
&& ((userLang[1] === undefined) || (userLang[1] === 'us'))) {
|
|
return;
|
|
}
|
|
|
|
// First pass: perfect match
|
|
for (let j = 0; j < supportedLanguages.length; j++) {
|
|
const supLang = supportedLanguages[j]
|
|
.toLowerCase()
|
|
.replace('_', '-')
|
|
.split('-');
|
|
|
|
if (userLang[0] !== supLang[0]) continue;
|
|
if (userLang[1] !== supLang[1]) continue;
|
|
|
|
this.language = supportedLanguages[j];
|
|
return;
|
|
}
|
|
|
|
// Second pass: fallback
|
|
for (let j = 0; j < supportedLanguages.length; j++) {
|
|
const supLang = supportedLanguages[j]
|
|
.toLowerCase()
|
|
.replace('_', '-')
|
|
.split('-');
|
|
|
|
if (userLang[0] !== supLang[0]) continue;
|
|
if (supLang[1] !== undefined) continue;
|
|
|
|
this.language = supportedLanguages[j];
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Retrieve localised text
|
|
get(id) {
|
|
if (typeof this.dictionary !== 'undefined' && this.dictionary[id]) {
|
|
return this.dictionary[id];
|
|
} else {
|
|
return id;
|
|
}
|
|
}
|
|
|
|
// Traverses the DOM and translates relevant fields
|
|
// See https://html.spec.whatwg.org/multipage/dom.html#attr-translate
|
|
translateDOM() {
|
|
const self = this;
|
|
|
|
function process(elem, enabled) {
|
|
function isAnyOf(searchElement, items) {
|
|
return items.indexOf(searchElement) !== -1;
|
|
}
|
|
|
|
function translateAttribute(elem, attr) {
|
|
const str = self.get(elem.getAttribute(attr));
|
|
elem.setAttribute(attr, str);
|
|
}
|
|
|
|
function translateTextNode(node) {
|
|
const str = self.get(node.data.trim());
|
|
node.data = str;
|
|
}
|
|
|
|
if (elem.hasAttribute('translate')) {
|
|
if (isAnyOf(elem.getAttribute('translate'), ['', 'yes'])) {
|
|
enabled = true;
|
|
} else if (isAnyOf(elem.getAttribute('translate'), ['no'])) {
|
|
enabled = false;
|
|
}
|
|
}
|
|
|
|
if (enabled) {
|
|
if (elem.hasAttribute('abbr')
|
|
&& elem.tagName === 'TH') {
|
|
translateAttribute(elem, 'abbr');
|
|
}
|
|
if (elem.hasAttribute('alt')
|
|
&& isAnyOf(elem.tagName, ['AREA', 'IMG', 'INPUT'])) {
|
|
translateAttribute(elem, 'alt');
|
|
}
|
|
if (elem.hasAttribute('download')
|
|
&& isAnyOf(elem.tagName, ['A', 'AREA'])) {
|
|
translateAttribute(elem, 'download');
|
|
}
|
|
if (elem.hasAttribute('label')
|
|
&& isAnyOf(elem.tagName, ['MENUITEM', 'MENU', 'OPTGROUP',
|
|
'OPTION', 'TRACK'])) {
|
|
translateAttribute(elem, 'label');
|
|
}
|
|
// FIXME: Should update "lang"
|
|
if (elem.hasAttribute('placeholder')
|
|
&& isAnyOf(elem.tagName, ['INPUT', 'TEXTAREA'])) {
|
|
translateAttribute(elem, 'placeholder');
|
|
}
|
|
if (elem.hasAttribute('title')) {
|
|
translateAttribute(elem, 'title');
|
|
}
|
|
if (elem.hasAttribute('value')
|
|
&& elem.tagName === 'INPUT'
|
|
&& isAnyOf(elem.getAttribute('type'), ['reset', 'button', 'submit'])) {
|
|
translateAttribute(elem, 'value');
|
|
}
|
|
}
|
|
|
|
for (let i = 0; i < elem.childNodes.length; i++) {
|
|
const node = elem.childNodes[i];
|
|
if (node.nodeType === node.ELEMENT_NODE) {
|
|
process(node, enabled);
|
|
} else if (node.nodeType === node.TEXT_NODE && enabled) {
|
|
translateTextNode(node);
|
|
}
|
|
}
|
|
}
|
|
|
|
process(document.body, true);
|
|
}
|
|
}
|
|
|
|
export const l10n = new Localizer();
|
|
export default l10n.get.bind(l10n);
|