]> git.proxmox.com Git - mirror_novnc.git/blob - app/localization.js
Use ES6 classes
[mirror_novnc.git] / app / localization.js
1 /*
2 * noVNC: HTML5 VNC client
3 * Copyright (C) 2012 Joel Martin
4 * Licensed under MPL 2.0 (see LICENSE.txt)
5 *
6 * See README.md for usage and integration instructions.
7 */
8
9 /*
10 * Localization Utilities
11 */
12
13 export class Localizer {
14 constructor() {
15 // Currently configured language
16 this.language = 'en';
17
18 // Current dictionary of translations
19 this.dictionary = undefined;
20 }
21
22 // Configure suitable language based on user preferences
23 setup(supportedLanguages) {
24 this.language = 'en'; // Default: US English
25
26 /*
27 * Navigator.languages only available in Chrome (32+) and FireFox (32+)
28 * Fall back to navigator.language for other browsers
29 */
30 let userLanguages;
31 if (typeof window.navigator.languages == 'object') {
32 userLanguages = window.navigator.languages;
33 } else {
34 userLanguages = [navigator.language || navigator.userLanguage];
35 }
36
37 for (let i = 0;i < userLanguages.length;i++) {
38 const userLang = userLanguages[i]
39 .toLowerCase()
40 .replace("_", "-")
41 .split("-");
42
43 // Built-in default?
44 if ((userLang[0] === 'en') &&
45 ((userLang[1] === undefined) || (userLang[1] === 'us'))) {
46 return;
47 }
48
49 // First pass: perfect match
50 for (let j = 0; j < supportedLanguages.length; j++) {
51 const supLang = supportedLanguages[j]
52 .toLowerCase()
53 .replace("_", "-")
54 .split("-");
55
56 if (userLang[0] !== supLang[0])
57 continue;
58 if (userLang[1] !== supLang[1])
59 continue;
60
61 this.language = supportedLanguages[j];
62 return;
63 }
64
65 // Second pass: fallback
66 for (let j = 0;j < supportedLanguages.length;j++) {
67 const supLang = supportedLanguages[j]
68 .toLowerCase()
69 .replace("_", "-")
70 .split("-");
71
72 if (userLang[0] !== supLang[0])
73 continue;
74 if (supLang[1] !== undefined)
75 continue;
76
77 this.language = supportedLanguages[j];
78 return;
79 }
80 }
81 }
82
83 // Retrieve localised text
84 get(id) {
85 if (typeof this.dictionary !== 'undefined' && this.dictionary[id]) {
86 return this.dictionary[id];
87 } else {
88 return id;
89 }
90 }
91
92 // Traverses the DOM and translates relevant fields
93 // See https://html.spec.whatwg.org/multipage/dom.html#attr-translate
94 translateDOM() {
95 const self = this;
96
97 function process(elem, enabled) {
98 function isAnyOf(searchElement, items) {
99 return items.indexOf(searchElement) !== -1;
100 }
101
102 function translateAttribute(elem, attr) {
103 const str = self.get(elem.getAttribute(attr));
104 elem.setAttribute(attr, str);
105 }
106
107 function translateTextNode(node) {
108 const str = self.get(node.data.trim());
109 node.data = str;
110 }
111
112 if (elem.hasAttribute("translate")) {
113 if (isAnyOf(elem.getAttribute("translate"), ["", "yes"])) {
114 enabled = true;
115 } else if (isAnyOf(elem.getAttribute("translate"), ["no"])) {
116 enabled = false;
117 }
118 }
119
120 if (enabled) {
121 if (elem.hasAttribute("abbr") &&
122 elem.tagName === "TH") {
123 translateAttribute(elem, "abbr");
124 }
125 if (elem.hasAttribute("alt") &&
126 isAnyOf(elem.tagName, ["AREA", "IMG", "INPUT"])) {
127 translateAttribute(elem, "alt");
128 }
129 if (elem.hasAttribute("download") &&
130 isAnyOf(elem.tagName, ["A", "AREA"])) {
131 translateAttribute(elem, "download");
132 }
133 if (elem.hasAttribute("label") &&
134 isAnyOf(elem.tagName, ["MENUITEM", "MENU", "OPTGROUP",
135 "OPTION", "TRACK"])) {
136 translateAttribute(elem, "label");
137 }
138 // FIXME: Should update "lang"
139 if (elem.hasAttribute("placeholder") &&
140 isAnyOf(elem.tagName, ["INPUT", "TEXTAREA"])) {
141 translateAttribute(elem, "placeholder");
142 }
143 if (elem.hasAttribute("title")) {
144 translateAttribute(elem, "title");
145 }
146 if (elem.hasAttribute("value") &&
147 elem.tagName === "INPUT" &&
148 isAnyOf(elem.getAttribute("type"), ["reset", "button", "submit"])) {
149 translateAttribute(elem, "value");
150 }
151 }
152
153 for (let i = 0; i < elem.childNodes.length; i++) {
154 const node = elem.childNodes[i];
155 if (node.nodeType === node.ELEMENT_NODE) {
156 process(node, enabled);
157 } else if (node.nodeType === node.TEXT_NODE && enabled) {
158 translateTextNode(node);
159 }
160 }
161 }
162
163 process(document.body, true);
164 }
165 }
166
167 export const l10n = new Localizer();
168 export default l10n.get.bind(l10n);