#!/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);