mirror of
https://git.proxmox.com/git/mirror_novnc
synced 2025-08-04 15:39:09 +00:00

* Change copyright header This updates the copyright header to say "The noVNC Authors". People who previously had copyright listings are now under the AUTHORS file.
173 lines
5.5 KiB
JavaScript
173 lines
5.5 KiB
JavaScript
/*
|
|
* noVNC: HTML5 VNC client
|
|
* Copyright (C) 2018 The noVNC Authors
|
|
* 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);
|