Try to be more consistent in how we capitalize things. Both the "Title Case" and "Sentence case" styles are popular, so either would work. Google and Mozilla both prefer "Sentence case", so let's follow them.
120 lines
3.6 KiB
JavaScript
Executable File
120 lines
3.6 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
/*
|
|
* xgettext-html: HTML gettext parser
|
|
* Copyright (C) 2018 The noVNC authors
|
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
|
*/
|
|
|
|
const getopt = require('node-getopt');
|
|
const jsdom = require("jsdom");
|
|
const fs = require("fs");
|
|
|
|
const opt = getopt.create([
|
|
['o', 'output=FILE', 'write output to specified file'],
|
|
['h', 'help', 'display this help'],
|
|
]).bindHelp().parseSystem();
|
|
|
|
const strings = {};
|
|
|
|
function addString(str, location) {
|
|
// We assume surrounding whitespace, and whitespace around line
|
|
// breaks, is just for source formatting
|
|
str = str.split("\n").map(s => s.trim()).join(" ").trim();
|
|
|
|
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", "submit"])) {
|
|
addString(elem.getAttribute("value"), locator(elem));
|
|
}
|
|
}
|
|
|
|
for (let i = 0; i < elem.childNodes.length; i++) {
|
|
let 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, locator(node));
|
|
}
|
|
}
|
|
}
|
|
|
|
for (let i = 0; i < opt.argv.length; i++) {
|
|
const fn = opt.argv[i];
|
|
const file = fs.readFileSync(fn, "utf8");
|
|
const dom = new jsdom.JSDOM(file, { includeNodeLocations: true });
|
|
const body = dom.window.document.body;
|
|
|
|
let locator = (elem) => {
|
|
const offset = dom.nodeLocation(elem).startOffset;
|
|
const line = file.slice(0, offset).split("\n").length;
|
|
return fn + ":" + line;
|
|
};
|
|
|
|
process(body, locator, true);
|
|
}
|
|
|
|
let output = "";
|
|
|
|
for (let 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);
|