Also translate HTML elements

This commit is contained in:
Pierre Ossman 2016-11-14 22:02:12 +01:00
parent 3cdc603aa4
commit edffd9e2f8
7 changed files with 450 additions and 13 deletions

View File

@ -96,6 +96,9 @@ var UI;
UI.initSettings(); UI.initSettings();
// Translate the DOM
Util.Localisation.translateDOM();
// Adapt the interface for touch screen devices // Adapt the interface for touch screen devices
if (Util.isTouchDevice) { if (Util.isTouchDevice) {
document.documentElement.classList.add("noVNC_touch"); document.documentElement.classList.add("noVNC_touch");

View File

@ -440,7 +440,81 @@ Util.Localisation = {
} else { } else {
return id; return id;
} }
} },
// Traverses the DOM and translates relevant fields
// See https://html.spec.whatwg.org/multipage/dom.html#attr-translate
translateDOM: function () {
function process(elem, enabled) {
function isAnyOf(searchElement, items) {
return items.indexOf(searchElement) !== -1;
}
function translateAttribute(elem, attr) {
var str = elem.getAttribute(attr);
str = Util.Localisation.get(str);
elem.setAttribute(attr, str);
}
function translateTextNode(node) {
var str = node.data.trim();
str = Util.Localisation.get(str);
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 in ["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"])) {
translateAttribute(elem, "value");
}
}
for (var i = 0;i < elem.childNodes.length;i++) {
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);
},
}; };
/* [module] export default Util; */ /* [module] export default Util; */

View File

@ -37,6 +37,7 @@
"chai": "^3.5.0", "chai": "^3.5.0",
"commander": "^2.9.0", "commander": "^2.9.0",
"fs-extra": "^1.0.0", "fs-extra": "^1.0.0",
"jsdom": "*",
"karma": "^1.3.0", "karma": "^1.3.0",
"karma-chai": "^0.1.0", "karma-chai": "^0.1.0",
"karma-mocha": "^1.3.0", "karma-mocha": "^1.3.0",
@ -46,6 +47,7 @@
"karma-sinon": "^1.0.5", "karma-sinon": "^1.0.5",
"karma-sinon-chai-latest": "^0.1.0", "karma-sinon-chai-latest": "^0.1.0",
"mocha": "^3.1.2", "mocha": "^3.1.2",
"node-getopt": "*",
"open": "^0.0.5", "open": "^0.0.5",
"phantomjs-prebuilt": "^2.1.13", "phantomjs-prebuilt": "^2.1.13",
"po2json": "*", "po2json": "*",

View File

@ -17,7 +17,7 @@ update-js: $(JSFILES)
./po2js $< $@ ./po2js $< $@
update-pot: update-pot:
xgettext --output=noVNC.pot \ xgettext --output=noVNC.js.pot \
--copyright-holder="Various Authors" \ --copyright-holder="Various Authors" \
--package-name="noVNC" \ --package-name="noVNC" \
--package-version="$(VERSION)" \ --package-version="$(VERSION)" \
@ -27,3 +27,8 @@ update-pot:
../app/*.js \ ../app/*.js \
../core/*.js \ ../core/*.js \
../core/input/*.js ../core/input/*.js
./xgettext-html --output=noVNC.html.pot \
../vnc.html
msgcat --output-file=noVNC.pot \
--sort-by-file noVNC.js.pot noVNC.html.pot
rm -f noVNC.js.pot noVNC.html.pot

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: noVNC 0.6.1\n" "Project-Id-Version: noVNC 0.6.1\n"
"Report-Msgid-Bugs-To: novnc@googlegroups.com\n" "Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
"POT-Creation-Date: 2016-11-15 08:11+0100\n" "POT-Creation-Date: 2016-11-15 19:32+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -17,35 +17,35 @@ msgstr ""
"Content-Type: text/plain; charset=CHARSET\n" "Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: ../app/ui.js:402 #: ../app/ui.js:405
msgid "Connecting..." msgid "Connecting..."
msgstr "" msgstr ""
#: ../app/ui.js:409 #: ../app/ui.js:412
msgid "Connected (encrypted) to " msgid "Connected (encrypted) to "
msgstr "" msgstr ""
#: ../app/ui.js:411 #: ../app/ui.js:414
msgid "Connected (unencrypted) to " msgid "Connected (unencrypted) to "
msgstr "" msgstr ""
#: ../app/ui.js:416 #: ../app/ui.js:419
msgid "Disconnecting..." msgid "Disconnecting..."
msgstr "" msgstr ""
#: ../app/ui.js:421 #: ../app/ui.js:424
msgid "Disconnected" msgid "Disconnected"
msgstr "" msgstr ""
#: ../app/ui.js:1006 ../core/rfb.js:278 #: ../app/ui.js:1009 ../core/rfb.js:278
msgid "Must set host and port" msgid "Must set host and port"
msgstr "" msgstr ""
#: ../app/ui.js:1059 #: ../app/ui.js:1062
msgid "Password is required" msgid "Password is required"
msgstr "" msgstr ""
#: ../app/ui.js:1272 #: ../app/ui.js:1275
msgid "" msgid ""
"Forcing clipping mode since scrollbars aren't supported by IE in fullscreen" "Forcing clipping mode since scrollbars aren't supported by IE in fullscreen"
msgstr "" msgstr ""
@ -53,3 +53,239 @@ msgstr ""
#: ../core/rfb.js:556 #: ../core/rfb.js:556
msgid "Disconnect timeout" msgid "Disconnect timeout"
msgstr "" msgstr ""
#: ../vnc.html:70
msgid "noVNC encountered an error:"
msgstr ""
#: ../vnc.html:78
msgid "Hide/Show the control bar"
msgstr ""
#: ../vnc.html:85
msgid "Move/Drag Viewport"
msgstr ""
#: ../vnc.html:85
msgid "viewport drag"
msgstr ""
#: ../vnc.html:91 ../vnc.html:94 ../vnc.html:97 ../vnc.html:100
msgid "Active Mouse Button"
msgstr ""
#: ../vnc.html:91
msgid "No mousebutton"
msgstr ""
#: ../vnc.html:94
msgid "Left mousebutton"
msgstr ""
#: ../vnc.html:97
msgid "Middle mousebutton"
msgstr ""
#: ../vnc.html:100
msgid "Right mousebutton"
msgstr ""
#: ../vnc.html:103
msgid "Keyboard"
msgstr ""
#: ../vnc.html:103
msgid "Show Keyboard"
msgstr ""
#: ../vnc.html:110
msgid "Extra keys"
msgstr ""
#: ../vnc.html:110
msgid "Show Extra Keys"
msgstr ""
#: ../vnc.html:115
msgid "Ctrl"
msgstr ""
#: ../vnc.html:115
msgid "Toggle Ctrl"
msgstr ""
#: ../vnc.html:118
msgid "Alt"
msgstr ""
#: ../vnc.html:118
msgid "Toggle Alt"
msgstr ""
#: ../vnc.html:121
msgid "Send Tab"
msgstr ""
#: ../vnc.html:121
msgid "Tab"
msgstr ""
#: ../vnc.html:124
msgid "Esc"
msgstr ""
#: ../vnc.html:124
msgid "Send Escape"
msgstr ""
#: ../vnc.html:127
msgid "Ctrl+Alt+Del"
msgstr ""
#: ../vnc.html:127
msgid "Send Ctrl-Alt-Del"
msgstr ""
#: ../vnc.html:135
msgid "Shutdown/Reboot"
msgstr ""
#: ../vnc.html:135
msgid "Shutdown/Reboot..."
msgstr ""
#: ../vnc.html:141
msgid "Power"
msgstr ""
#: ../vnc.html:143
msgid "Shutdown"
msgstr ""
#: ../vnc.html:144
msgid "Reboot"
msgstr ""
#: ../vnc.html:145
msgid "Reset"
msgstr ""
#: ../vnc.html:150 ../vnc.html:156
msgid "Clipboard"
msgstr ""
#: ../vnc.html:160
msgid "Clear"
msgstr ""
#: ../vnc.html:166
msgid "Fullscreen"
msgstr ""
#: ../vnc.html:171 ../vnc.html:178
msgid "Settings"
msgstr ""
#: ../vnc.html:181
msgid "Encrypt"
msgstr ""
#: ../vnc.html:184
msgid "True Color"
msgstr ""
#: ../vnc.html:187
msgid "Local Cursor"
msgstr ""
#: ../vnc.html:190
msgid "Clip to Window"
msgstr ""
#: ../vnc.html:193
msgid "Shared Mode"
msgstr ""
#: ../vnc.html:196
msgid "View Only"
msgstr ""
#: ../vnc.html:200
msgid "Path:"
msgstr ""
#: ../vnc.html:204
msgid "Scaling Mode:"
msgstr ""
#: ../vnc.html:206
msgid "None"
msgstr ""
#: ../vnc.html:207
msgid "Local Scaling"
msgstr ""
#: ../vnc.html:208
msgid "Local Downscaling"
msgstr ""
#: ../vnc.html:209
msgid "Remote Resizing"
msgstr ""
#: ../vnc.html:213
msgid "Repeater ID:"
msgstr ""
#: ../vnc.html:219
msgid "Style:"
msgstr ""
#: ../vnc.html:221
msgid "default"
msgstr ""
#: ../vnc.html:227
msgid "Logging:"
msgstr ""
#: ../vnc.html:234
msgid "Apply"
msgstr ""
#: ../vnc.html:241 ../vnc.html:271
msgid "Connect"
msgstr ""
#: ../vnc.html:244
msgid "Disconnect"
msgstr ""
#: ../vnc.html:251
msgid "Connection"
msgstr ""
#: ../vnc.html:254
msgid "Host:"
msgstr ""
#: ../vnc.html:258
msgid "Port:"
msgstr ""
#: ../vnc.html:262 ../vnc.html:290
msgid "Password:"
msgstr ""
#: ../vnc.html:266
msgid "Token:"
msgstr ""
#: ../vnc.html:294
msgid "Send Password"
msgstr ""
#: ../vnc.html:319
msgid "Canvas not supported."
msgstr ""

117
po/xgettext-html Executable file
View File

@ -0,0 +1,117 @@
#!/usr/bin/env node
/*
* xgettext-html: HTML gettext parser
* Copyright (C) 2016 Pierre Ossman
* Licensed under MPL 2.0 (see LICENSE.txt)
*/
var getopt = require('node-getopt');
var jsdom = require("jsdom");
var fs = require("fs");
opt = getopt.create([
['o' , 'output=FILE' , 'write output to specified file'],
['h' , 'help' , 'display this help'],
]).bindHelp().parseSystem();
var strings = {};
function addString(str, location) {
if (str.length == 0) {
return;
}
if (strings[str] === undefined) {
strings[str] = {}
}
strings[str][location] = null;
}
// See https://html.spec.whatwg.org/multipage/dom.html#attr-translate
function process(elem, locator, enabled) {
function isAnyOf(searchElement, items) {
return items.indexOf(searchElement) !== -1;
}
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") {
addString(elem.getAttribute("abbr"), locator(elem));
}
if (elem.hasAttribute("alt") &&
isAnyOf(elem.tagName, ["AREA", "IMG", "INPUT"])) {
addString(elem.getAttribute("alt"), locator(elem));
}
if (elem.hasAttribute("download") &&
isAnyOf(elem.tagName, ["A", "AREA"])) {
addString(elem.getAttribute("download"), locator(elem));
}
if (elem.hasAttribute("label") &&
isAnyOf(elem.tagName, ["MENUITEM", "MENU", "OPTGROUP",
"OPTION", "TRACK"])) {
addString(elem.getAttribute("label"), locator(elem));
}
if (elem.hasAttribute("placeholder") &&
isAnyOf(elem.tagName in ["INPUT", "TEXTAREA"])) {
addString(elem.getAttribute("placeholder"), locator(elem));
}
if (elem.hasAttribute("title")) {
addString(elem.getAttribute("title"), locator(elem));
}
if (elem.hasAttribute("value") &&
elem.tagName === "INPUT" &&
isAnyOf(elem.getAttribute("type"), ["reset", "button"])) {
addString(elem.getAttribute("value"), locator(elem));
}
}
for (var i = 0;i < elem.childNodes.length;i++) {
node = elem.childNodes[i];
if (node.nodeType === node.ELEMENT_NODE) {
process(node, locator, enabled);
} else if (node.nodeType === node.TEXT_NODE && enabled) {
addString(node.data.trim(), locator(node));
}
}
}
for (var i = 0;i < opt.argv.length;i++) {
var file;
fn = opt.argv[i];
file = fs.readFileSync(fn, "utf8");
doc = jsdom.jsdom(file);
locator = function (elem) {
offset = jsdom.nodeLocation(elem).start;
line = file.slice(0, offset).split("\n").length;
return fn + ":" + line;
};
process(doc.body, locator, true);
}
var output = "";
for (str in strings) {
output += "#:";
for (location in strings[str]) {
output += " " + location;
}
output += "\n";
output += "msgid " + JSON.stringify(str) + "\n";
output += "msgstr \"\"\n";
output += "\n";
}
fs.writeFileSync(opt.options.output, output);

View File

@ -79,7 +79,7 @@
<div class="noVNC_scroll"> <div class="noVNC_scroll">
<h1 class="noVNC_logo"><span>no</span><br />VNC</h1> <h1 class="noVNC_logo" translate="no"><span>no</span><br />VNC</h1>
<!-- Drag/Pan the viewport --> <!-- Drag/Pan the viewport -->
<input type="image" alt="viewport drag" src="app/images/drag.svg" <input type="image" alt="viewport drag" src="app/images/drag.svg"
@ -304,7 +304,7 @@
</div> </div>
<div id="noVNC_container"> <div id="noVNC_container">
<h1 id="noVNC_logo" class="noVNC_logo"><span>no</span><br />VNC</h1> <h1 id="noVNC_logo" class="noVNC_logo" translate="no"><span>no</span><br />VNC</h1>
<!-- HTML5 Canvas --> <!-- HTML5 Canvas -->
<div id="noVNC_screen"> <div id="noVNC_screen">