#!/usr/bin/env node
/*
* Extracts translatable strings from manifest.json files.
*
*/
function fatal(message, code) {
console.log((filename || "manifest2po") + ": " + message);
process.exit(code || 1);
}
function usage() {
console.log("usage: manifest2po [-o output] input...");
process.exit(2);
}
var fs, path, stdio;
try {
fs = require('fs');
path = require('path');
stdio = require('stdio');
} catch (ex) {
fatal(ex.message, 127); /* missing looks for this */
}
var opts = stdio.getopt({
directory: { key: "d", args: 1, description: "Base directory for input files" },
output: { key: "o", args: 1, description: "Output file" },
from: { key: "f", args: 1, description: "File containing list of input files" },
});
if (!opts.from && opts.args.length < 1) {
usage();
}
var input = opts.args;
var entries = { };
/* Filename being parsed */
var filename = null;
prepare();
/* Decide what input files to process */
function prepare() {
if (opts.from) {
fs.readFile(opts.from, { encoding: "utf-8"}, function(err, data) {
if (err)
fatal(err.message);
input = data.split("\n").filter(function(value) {
return !!value;
}).concat(input);
step();
});
} else {
step();
}
}
/* Now process each file in turn */
function step() {
filename = input.shift();
if (filename === undefined) {
finish();
return;
}
if (path.basename(filename) != "manifest.json")
return step();
fs.readFile(filename, { encoding: "utf-8"}, function(err, data) {
if (err)
fatal(err.message);
process_manifest(JSON.parse(data));
return step();
});
}
function process_manifest(manifest) {
if (manifest.menu)
process_menu(manifest.menu);
if (manifest.tools)
process_menu(manifest.tools);
}
function process_keywords(keywords) {
keywords.forEach(v => {
v.matches.forEach(keyword =>
push({
msgid: keyword,
locations: [ filename ]
})
);
});
}
function process_docs(docs) {
docs.forEach(doc => {
push({
msgid: doc.label,
locations: [ filename ]
})
});
}
function process_menu(menu) {
for (var m in menu) {
if (menu[m].label) {
push({
msgid: menu[m].label,
locations: [ filename ]
});
}
if (menu[m].keywords)
process_keywords(menu[m].keywords);
if (menu[m].docs)
process_docs(menu[m].docs);
}
}
/* Push an entry onto the list */
function push(entry) {
var key = entry.msgid + "\0" + entry.msgid_plural + "\0" + entry.msgctxt;
var prev = entries[key];
if (prev) {
prev.locations = prev.locations.concat(entry.locations);
} else {
entries[key] = entry;
}
}
/* Escape a string for inclusion in po file */
function escape(string) {
var bs = string.split('\\').join('\\\\').split('"').join('\\"');
return bs.split("\n").map(function(line) {
return '"' + line + '"';
}).join("\n");
}
/* Finish by writing out the strings */
function finish() {
var result = [
'msgid ""',
'msgstr ""',
'"Project-Id-Version: PACKAGE_VERSION\\n"',
'"MIME-Version: 1.0\\n"',
'"Content-Type: text/plain; charset=UTF-8\\n"',
'"Content-Transfer-Encoding: 8bit\\n"',
'"X-Generator: Cockpit manifest2po\\n"',
'',
];
var msgid, entry;
for (msgid in entries) {
entry = entries[msgid];
result.push('#: ' + entry.locations.join(" "));
if (entry.msgctxt)
result.push('msgctxt ' + escape(entry.msgctxt));
result.push('msgid ' + escape(entry.msgid));
if (entry.msgid_plural) {
result.push('msgid_plural ' + escape(entry.msgid_plural));
result.push('msgstr[0] ""');
result.push('msgstr[1] ""');
} else {
result.push('msgstr ""');
}
result.push('');
}
var data = result.join('\n');
if (!opts.output) {
process.stdout.write(data);
process.exit(0);
} else {
fs.writeFile(opts.output, data, function(err) {
if (err)
fatal(err.message);
process.exit(0);
});
}
}