| #!/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) { |
| 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++) { |
| 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 (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; |
| |
| function 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 (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); |