/* * 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);