]>
Commit | Line | Data |
---|---|---|
edffd9e2 PO |
1 | #!/usr/bin/env node |
2 | /* | |
3 | * xgettext-html: HTML gettext parser | |
4 | * Copyright (C) 2016 Pierre Ossman | |
5 | * Licensed under MPL 2.0 (see LICENSE.txt) | |
6 | */ | |
7 | ||
8 | var getopt = require('node-getopt'); | |
9 | ||
10 | var jsdom = require("jsdom"); | |
11 | var fs = require("fs"); | |
12 | ||
13 | opt = getopt.create([ | |
14 | ['o' , 'output=FILE' , 'write output to specified file'], | |
15 | ['h' , 'help' , 'display this help'], | |
16 | ]).bindHelp().parseSystem(); | |
17 | ||
18 | var strings = {}; | |
19 | ||
20 | function addString(str, location) { | |
21 | if (str.length == 0) { | |
22 | return; | |
23 | } | |
24 | ||
25 | if (strings[str] === undefined) { | |
26 | strings[str] = {} | |
27 | } | |
28 | strings[str][location] = null; | |
29 | } | |
30 | ||
31 | // See https://html.spec.whatwg.org/multipage/dom.html#attr-translate | |
32 | function process(elem, locator, enabled) { | |
33 | function isAnyOf(searchElement, items) { | |
34 | return items.indexOf(searchElement) !== -1; | |
35 | } | |
36 | ||
37 | if (elem.hasAttribute("translate")) { | |
38 | if (isAnyOf(elem.getAttribute("translate"), ["", "yes"])) { | |
39 | enabled = true; | |
40 | } else if (isAnyOf(elem.getAttribute("translate"), ["no"])) { | |
41 | enabled = false; | |
42 | } | |
43 | } | |
44 | ||
45 | if (enabled) { | |
46 | if (elem.hasAttribute("abbr") && | |
47 | elem.tagName === "TH") { | |
48 | addString(elem.getAttribute("abbr"), locator(elem)); | |
49 | } | |
50 | if (elem.hasAttribute("alt") && | |
51 | isAnyOf(elem.tagName, ["AREA", "IMG", "INPUT"])) { | |
52 | addString(elem.getAttribute("alt"), locator(elem)); | |
53 | } | |
54 | if (elem.hasAttribute("download") && | |
55 | isAnyOf(elem.tagName, ["A", "AREA"])) { | |
56 | addString(elem.getAttribute("download"), locator(elem)); | |
57 | } | |
58 | if (elem.hasAttribute("label") && | |
59 | isAnyOf(elem.tagName, ["MENUITEM", "MENU", "OPTGROUP", | |
60 | "OPTION", "TRACK"])) { | |
61 | addString(elem.getAttribute("label"), locator(elem)); | |
62 | } | |
63 | if (elem.hasAttribute("placeholder") && | |
64 | isAnyOf(elem.tagName in ["INPUT", "TEXTAREA"])) { | |
65 | addString(elem.getAttribute("placeholder"), locator(elem)); | |
66 | } | |
67 | if (elem.hasAttribute("title")) { | |
68 | addString(elem.getAttribute("title"), locator(elem)); | |
69 | } | |
70 | if (elem.hasAttribute("value") && | |
71 | elem.tagName === "INPUT" && | |
72 | isAnyOf(elem.getAttribute("type"), ["reset", "button"])) { | |
73 | addString(elem.getAttribute("value"), locator(elem)); | |
74 | } | |
75 | } | |
76 | ||
77 | for (var i = 0;i < elem.childNodes.length;i++) { | |
78 | node = elem.childNodes[i]; | |
79 | if (node.nodeType === node.ELEMENT_NODE) { | |
80 | process(node, locator, enabled); | |
81 | } else if (node.nodeType === node.TEXT_NODE && enabled) { | |
82 | addString(node.data.trim(), locator(node)); | |
83 | } | |
84 | } | |
85 | } | |
86 | ||
87 | for (var i = 0;i < opt.argv.length;i++) { | |
88 | var file; | |
89 | ||
90 | fn = opt.argv[i]; | |
91 | file = fs.readFileSync(fn, "utf8"); | |
0f897520 PO |
92 | dom = new jsdom.JSDOM(file, { includeNodeLocations: true }); |
93 | body = dom.window.document.body; | |
edffd9e2 PO |
94 | |
95 | locator = function (elem) { | |
0f897520 | 96 | offset = dom.nodeLocation(elem).startOffset; |
edffd9e2 PO |
97 | line = file.slice(0, offset).split("\n").length; |
98 | return fn + ":" + line; | |
99 | }; | |
100 | ||
0f897520 | 101 | process(body, locator, true); |
edffd9e2 PO |
102 | } |
103 | ||
104 | var output = ""; | |
105 | ||
106 | for (str in strings) { | |
107 | output += "#:"; | |
108 | for (location in strings[str]) { | |
109 | output += " " + location; | |
110 | } | |
111 | output += "\n"; | |
112 | ||
113 | output += "msgid " + JSON.stringify(str) + "\n"; | |
114 | output += "msgstr \"\"\n"; | |
115 | output += "\n"; | |
116 | } | |
117 | ||
118 | fs.writeFileSync(opt.options.output, output); |