chg: merged parser code modified in antville app back into hopkit

This commit is contained in:
Tobi Schäfer 2020-04-03 14:11:49 +02:00
parent 0bf39c8f4b
commit 657f66c0ce
2 changed files with 441 additions and 399 deletions

View file

@ -1,29 +1,21 @@
//
// Jala Project [http://opensvn.csie.org/traccgi/jala] // Jala Project [http://opensvn.csie.org/traccgi/jala]
// //
// Copyright 2004 ORF Online und Teletext GmbH // Copyright 2004 ORF Online und Teletext GmbH.
// //
// Licensed under the Apache License, Version 2.0 (the ``License''); // Licensed under the Apache License, Version 2.0 (the ``License'');
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an ``AS IS'' BASIS, // distributed under the License is distributed on an ``AS IS'' BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
//
// $Revision$
// $LastChangedBy$
// $LastChangedDate$
// $HeadURL$
//
/** /**
* @fileoverview * @fileoverview
* MessageParser script that extracts all gettext message macros * MessageParser script that extracts all gettext message macros
* out of skin files and all calls of gettext functions * out of skin files and all calls of gettext functions
* (that is "gettext", "ngettext" and "_") out of function * (that is "gettext", "ngettext" and "_") out of function
@ -39,10 +31,10 @@
* @constructor * @constructor
*/ */
var Message = function(id, pluralId) { var Message = function(id, pluralId) {
this.id = id && String(id); this.id = id && String(id);
this.pluralId = pluralId && String(pluralId); this.pluralId = pluralId && String(pluralId);
this.locations = []; this.locations = [];
return this; return this;
}; };
/** /**
@ -54,11 +46,11 @@ var Message = function(id, pluralId) {
* @type String * @type String
*/ */
Message.getKey = function(id, pluralId) { Message.getKey = function(id, pluralId) {
if (id && pluralId) { if (id && pluralId) {
return id + pluralId; return id + pluralId;
} else { } else {
return id; return id;
} }
}; };
/** /**
@ -71,28 +63,28 @@ Message.getKey = function(id, pluralId) {
* @type String * @type String
*/ */
Message.formatId = function(str, wrap) { Message.formatId = function(str, wrap) {
var escapeQuotes = function(s) { var escapeQuotes = function(s) {
return s.replace(/(^|[^\\])"/g, '$1\\"'); return s.replace(/(^|[^\\])"/g, '$1\\"');
}; };
var len = 80; var len = 80;
var buf = new java.lang.StringBuffer(); var buf = new java.lang.StringBuffer();
if (wrap == true && str.length > len) { if (wrap == true && str.length > len) {
buf.append('""\n'); buf.append('""\n');
var offset = 0; var offset = 0;
while (offset < str.length) { while (offset < str.length) {
buf.append('"');
buf.append(escapeQuotes(str.substring(offset, offset += len)));
buf.append('"');
buf.append("\n");
}
return buf.toString();
} else {
buf.append('"'); buf.append('"');
buf.append(escapeQuotes(str)); buf.append(escapeQuotes(str.substring(offset, offset += len)));
buf.append('"\n'); buf.append('"');
} buf.append("\n");
return buf.toString(); }
return buf.toString();
} else {
buf.append('"');
buf.append(escapeQuotes(str));
buf.append('"\n');
}
return buf.toString();
}; };
/** /**
@ -103,7 +95,10 @@ Message.formatId = function(str, wrap) {
* was found at * was found at
*/ */
Message.prototype.addLocation = function(filePath, lineNum) { Message.prototype.addLocation = function(filePath, lineNum) {
this.locations.push(filePath + ":" + lineNum); const basePath = java.nio.file.Paths.get(java.io.File(app.dir).getCanonicalPath());
const locationPath = java.nio.file.Paths.get(filePath);
const relativePath = basePath.relativize(locationPath);
this.locations.push(relativePath + ":" + lineNum);
}; };
/** /**
@ -113,34 +108,34 @@ Message.prototype.addLocation = function(filePath, lineNum) {
* to write into * to write into
*/ */
Message.prototype.write = function(buf) { Message.prototype.write = function(buf) {
for (var i=0;i<this.locations.length;i++) { for (var i=0;i<this.locations.length;i++) {
buf.append("#: "); buf.append("#: ");
buf.append(this.locations[i]); buf.append(this.locations[i]);
buf.append("\n"); buf.append("\n");
} }
if (this.id.indexOf("{") > -1 if (this.id.indexOf("{") > -1
|| (this.pluralId != null && this.pluralId.indexOf("{") > -1)) { || (this.pluralId != null && this.pluralId.indexOf("{") > -1)) {
buf.append("#, java-format\n"); buf.append("#, java-format\n");
} }
buf.append('msgid '); buf.append('msgid ');
buf.append(Message.formatId(this.id)); buf.append(Message.formatId(this.id));
if (this.pluralId != null) { if (this.pluralId != null) {
buf.append('msgid_plural '); buf.append('msgid_plural ');
buf.append(Message.formatId(this.pluralId)); buf.append(Message.formatId(this.pluralId));
buf.append('msgstr[0] ""\nmsgstr[1] ""\n') buf.append('msgstr[0] ""\nmsgstr[1] ""\n')
} else { } else {
buf.append('msgstr ""\n') buf.append('msgstr ""\n')
} }
buf.append("\n"); buf.append("\n");
return; return;
}; };
/** /**
* @constructor * @constructor
*/ */
var MessageParser = function() { var MessageParser = function() {
this.messages = {}; this.messages = {};
return this; return this;
}; };
/** /**
@ -150,10 +145,11 @@ var MessageParser = function() {
* @type Object * @type Object
*/ */
MessageParser.FUNCTION_NAMES = { MessageParser.FUNCTION_NAMES = {
"_": true, "_": true,
"gettext": true, "gettext": true,
"ngettext": true, "ngettext": true,
"markgettext": true "markgettext": true,
"cgettext": true
}; };
/** /**
@ -197,7 +193,7 @@ MessageParser.REGEX_PARAM = /([\w]*)\s*=\s*["'](.*?)["']\s*(?=\w+=|$)/gm;
* @type Number * @type Number
*/ */
MessageParser.getLineNum = function(str, idx) { MessageParser.getLineNum = function(str, idx) {
return str.substring(0, idx).split(/.*(?:\r\n|\n\r|\r|\n)/).length; return str.substring(0, idx).split(/.*(?:\r\n|\n\r|\r|\n)/).length;
}; };
/** /**
@ -208,36 +204,36 @@ MessageParser.getLineNum = function(str, idx) {
* @param {String} encoding The encoding to use * @param {String} encoding The encoding to use
*/ */
MessageParser.prototype.parse = function(file, encoding) { MessageParser.prototype.parse = function(file, encoding) {
if (file.isDirectory()) { if (file.isDirectory()) {
var list = file.list(); var list = file.list();
for (var i=0;i<list.length;i++) { for (var i=0;i<list.length;i++) {
this.parse(new java.io.File(file, list[i]), encoding); this.parse(new java.io.File(file, list[i]), encoding);
}
} else {
var fName, dotIdx;
fName = file.getName();
if ((dotIdx = fName.lastIndexOf(".")) > -1) {
switch (String(fName.substring(dotIdx+1))) {
case "skin":
print("Parsing skin file " + file.getCanonicalPath() + "...");
this.parseSkinFile(file, encoding);
break;
case "hac":
case "js":
print("Parsing function file " + file.getCanonicalPath() + "...");
this.parseFunctionFile(file, encoding);
break;
default:
break;
} }
} else { }
var fName, dotIdx; }
fName = file.getName(); return;
if ((dotIdx = fName.lastIndexOf(".")) > -1) {
switch (String(fName.substring(dotIdx+1))) {
case "skin":
print("Parsing skin file " + file.getAbsolutePath() + "...");
this.parseSkinFile(file, encoding);
break;
case "hac":
case "js":
print("Parsing function file " + file.getAbsolutePath() + "...");
this.parseFunctionFile(file, encoding);
break;
default:
break;
}
}
}
return;
}; };
/** @ignore */ /** @ignore */
MessageParser.prototype.toString = function() { MessageParser.prototype.toString = function() {
return "[Jala Message Parser]"; return "[Jala Message Parser]";
}; };
/** /**
@ -247,61 +243,65 @@ MessageParser.prototype.toString = function() {
* @param {String} encoding The encoding to use * @param {String} encoding The encoding to use
*/ */
MessageParser.prototype.parseFunctionFile = function(file, encoding) { MessageParser.prototype.parseFunctionFile = function(file, encoding) {
var fis = new java.io.FileInputStream(file); var fis = new java.io.FileInputStream(file);
var isr = new java.io.InputStreamReader(fis, encoding || "UTF-8"); var isr = new java.io.InputStreamReader(fis, encoding || "UTF-8");
var reader = new java.io.BufferedReader(isr); var reader = new java.io.BufferedReader(isr);
var tokenizer = new java.io.StreamTokenizer(reader); var tokenizer = new java.io.StreamTokenizer(reader);
var messages = [], stack = []; var messages = [], stack = [];
var c; var c;
while ((c = tokenizer.nextToken()) != java.io.StreamTokenizer.TT_EOF) { while ((c = tokenizer.nextToken()) != java.io.StreamTokenizer.TT_EOF) {
switch (c) { switch (c) {
case java.io.StreamTokenizer.TT_WORD: case java.io.StreamTokenizer.TT_WORD:
if (MessageParser.FUNCTION_NAMES[tokenizer.sval] == true) { if (MessageParser.FUNCTION_NAMES[tokenizer.sval] == true) {
stack.push({name: tokenizer.sval, lineNr: tokenizer.lineno()}); stack.push({name: tokenizer.sval, lineNr: tokenizer.lineno()});
} else if (stack.length > 0) { } else if (stack.length > 0) {
// it's something else than a string argument inside a gettext method call // it's something else than a string argument inside a gettext method call
// so finalize the argument parsing here as we aren't interested in that // so finalize the argument parsing here as we aren't interested in that
messages.push(stack.pop()); messages.push(stack.pop());
} }
break; break;
case java.io.StreamTokenizer.TT_NUMBER: case java.io.StreamTokenizer.TT_NUMBER:
break; break;
default: default:
if (stack.length > 0) { if (stack.length > 0) {
if ("\u0028".charCodeAt(0) == c) { if ("\u0028".charCodeAt(0) == c) {
// start of arguments (an opening bracket) // start of arguments (an opening bracket)
stack[stack.length-1].args = []; stack[stack.length-1].args = [];
} else if ("\u0029".charCodeAt(0) == c) { } else if ("\u0029".charCodeAt(0) == c) {
// end of arguments (a closing bracket) // end of arguments (a closing bracket)
messages.push(stack.pop()); messages.push(stack.pop());
} else if ("\u0022".charCodeAt(0) == c || "\u0027".charCodeAt(0) == c) { } else if ("\u0022".charCodeAt(0) == c || "\u0027".charCodeAt(0) == c) {
// a quoted string argument // a quoted string argument
stack[stack.length-1].args.push(tokenizer.sval); stack[stack.length-1].args.push(tokenizer.sval);
} }
} }
break; break;
}
}
if (messages.length > 0) {
var msgParam, key, msg;
for (var i=0;i<messages.length;i++) {
msgParam = messages[i];
if (msgParam.args && msgParam.args.length > 0) {
if (msgParam.name === "cgettext" || msgParam.name === "markgettext") {
msgParam.args[0] = cgettext.getKey(msgParam.args[0], msgParam.args[1]);
delete msgParam.args[1];
}
key = Message.getKey(msgParam.args[0]);
if (!(msg = this.messages[key])) {
this.messages[key] = msg = new Message(msgParam.args[0], msgParam.args[1]);
}
if (!msg.pluralId && msgParam.args.length > 1) {
msg.pluralId = msgParam.args[1];
}
msg.addLocation(file.getCanonicalPath(), msgParam.lineNr);
} }
} }
if (messages.length > 0) { }
var msgParam, key, msg; fis.close();
for (var i=0;i<messages.length;i++) { isr.close();
msgParam = messages[i]; reader.close();
if (msgParam.args && msgParam.args.length > 0) { return;
key = Message.getKey(msgParam.args[0]);
if (!(msg = this.messages[key])) {
this.messages[key] = msg = new Message(msgParam.args[0], msgParam.args[1]);
}
if (!msg.pluralId && msgParam.args.length > 1) {
msg.pluralId = msgParam.args[1];
}
msg.addLocation(file.getAbsolutePath(), msgParam.lineNr);
}
}
}
fis.close();
isr.close();
reader.close();
return;
}; };
/** /**
@ -313,78 +313,128 @@ MessageParser.prototype.parseFunctionFile = function(file, encoding) {
* @param {String} encoding The encoding to use * @param {String} encoding The encoding to use
*/ */
MessageParser.prototype.parseSkinFile = function(file, encoding) { MessageParser.prototype.parseSkinFile = function(file, encoding) {
var content = readFile(file.getAbsolutePath(), encoding || "UTF-8"); var self = this;
var macro, id, pluralId, params, key; var source = readFile(file.getAbsolutePath(), encoding || "UTF-8");
while (macro = MessageParser.REGEX_MACRO.exec(content)) {
while (params = MessageParser.REGEX_PARAM.exec(macro[3])) { var checkNestedMacros = function(iterator) {
if (macro[2] == MessageParser.MACRO_NAME) { var macros = [];
if (params[1] == "text") { while (iterator.hasNext()) {
id = params[2]; macro = iterator.next();
pluralId = null; if (macro && macro.constructor !== String) {
} else if (params[1] == "plural") { macros.push(macro);
pluralId = params[2]; }
} }
} else { processMacros(macros);
if (params[1] == MessageParser.ATTRIBUTE_NAME) { }
id = params[2];
pluralId = null; var processMacros = function(macros) {
} else if (params[1] == "plural") { var re = gettext_macro.REGEX;
pluralId = params[2]; var id, pluralId, name, args, param, key, msg;
} for each (var macro in macros) {
} id = pluralId = null;
name = macro.getName();
param = macro.getNamedParams();
if (param) {
checkNestedMacros(param.values().iterator());
if (name === MessageParser.MACRO_NAME) {
id = param.get("text");
pluralId = param.get("plural");
} else if (param.containsKey("message") === MessageParser.ATTRIBUTE_NAME) {
id = param.get("message");
pluralId = param.get("plural");
}
}
args = macro.getPositionalParams();
if (args) {
checkNestedMacros(args.iterator());
if (name === "gettext" || name === "markgettext") {
id = cgettext.getKey(args.get(0), param && param.get("context"));
} else if (name === "ngettext") {
id = args.get(0);
pluralId = args.get(1);
}
} }
if (id != null) { if (id != null) {
// create new Message instance or update the existing one if (id.constructor !== String) {
key = Message.getKey(id); continue;
if (!(msg = this.messages[key])) { }
this.messages[key] = msg = new Message(id, pluralId, file.getAbsolutePath()); // create new Message instance or update the existing one
} id = id.replace(re, String.SPACE);
msg.addLocation(file.getAbsolutePath(), pluralId && (pluralId = pluralId.replace(re, String.SPACE));
MessageParser.getLineNum(content, macro.index)); key = Message.getKey(id);
if (!(msg = self.messages[key])) {
self.messages[key] = msg = new Message(id, pluralId, file.getCanonicalPath());
}
msg.addLocation(file.getCanonicalPath(), MessageParser.getLineNum(source, macro.start));
} }
} }
return; }
};
var skin = createSkin(source);
if (skin.hasMainskin()) {
processMacros(skin.getMacros());
}
for each (var name in skin.getSubskinNames()) {
var subskin = skin.getSubskin(name);
processMacros(subskin.getMacros());
}
return;
}
/** /**
* Prints a standard Header of a .po file * Prints a standard Header of a .po file
* FIXME: Allow custom header (template?)
* FIXME: why the hell is Plural-Forms ignored in poEdit? * FIXME: why the hell is Plural-Forms ignored in poEdit?
* @see http://drupal.org/node/17564 * @see http://drupal.org/node/17564
*/ */
MessageParser.prototype.getPotString = function() { MessageParser.prototype.getPotString = function() {
var buf = new java.lang.StringBuffer(); var date = new Date;
buf.append('# SOME DESCRIPTIVE TITLE.\n'); var buf = new java.lang.StringBuffer();
buf.append('# Copyright (C) YEAR THE PACKAGE\'S COPYRIGHT HOLDER\n'); buf.append('#\n');
buf.append('# This file is distributed under the same license as the PACKAGE package.\n'); buf.append('# The Antville Project\n');
buf.append('# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n'); buf.append('# http://code.google.com/p/antville\n');
buf.append('#\n'); buf.append('#\n');
buf.append('#, fuzzy\n'); buf.append('# Copyright 2001-' + date.getFullYear() + ' by the Workers of Antville.\n');
buf.append('msgid ""\n'); buf.append('#\n');
buf.append('msgstr ""\n'); buf.append("# Licensed under the Apache License, Version 2.0 (the ``License''\n");
buf.append('"Project-Id-Version: PACKAGE VERSION\\n"\n'); buf.append('# you may not use this file except in compliance with the License.\n');
buf.append('"Report-Msgid-Bugs-To: \\n"\n'); buf.append('# You may obtain a copy of the License at\n');
var sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mmZ"); buf.append('#\n');
buf.append('"POT-Creation-Date: ' + sdf.format(new java.util.Date()) + '\\n"\n'); buf.append('# http://www.apache.org/licenses/LICENSE-2.0\n');
buf.append('"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n"\n'); buf.append('#\n');
buf.append('"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n"\n'); buf.append('# Unless required by applicable law or agreed to in writing, software\n');
buf.append('"Language-Team: LANGUAGE <LL@li.org>\\n"\n'); buf.append("# distributed under the License is distributed on an ``AS IS'' BASIS,\n");
buf.append('"MIME-Version: 1.0\\n"\n'); buf.append('# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n');
buf.append('"Content-Type: text/plain; charset=utf-8\\n"\n'); buf.append('# See the License for the specific language governing permissions and\n');
buf.append('"Content-Transfer-Encoding: 8bit\\n"\n'); buf.append('# limitations under the License.\n');
buf.append('"Plural-Forms: nplurals=2; plural=(n != 1);\\n"\n'); buf.append('#\n\n');
buf.append('\n'); buf.append('#, fuzzy\n');
buf.append('msgid ""\n');
buf.append('msgstr ""\n');
buf.append('"Project-Id-Version: Antville-' + Root.VERSION + '\\n"\n');
buf.append('"Report-Msgid-Bugs-To: mail@antville.org\\n"\n');
var sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mmZ");
buf.append('"POT-Creation-Date: ' + sdf.format(new java.util.Date()) + '\\n"\n');
buf.append('"PO-Revision-Date: ' + sdf.format(new java.util.Date()) + '\\n"\n');
//buf.append('"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n"\n');
buf.append('"Language-Team: The Antville People <mail@antville.org>\\n"\n');
buf.append('"MIME-Version: 1.0\\n"\n');
buf.append('"Content-Type: text/plain; charset=utf-8\\n"\n');
buf.append('"Content-Transfer-Encoding: 8bit\\n"\n');
buf.append('"Plural-Forms: nplurals=2; plural=(n != 1);\\n"\n');
buf.append('\n');
// sort all messages by their singular key // sort all messages by their singular key
var keys = []; var keys = [];
for (var i in this.messages) { for (var i in this.messages) {
keys[keys.length] = this.messages[i].id; keys[keys.length] = this.messages[i].id;
} }
keys.sort(); keys.sort();
// add all the messages // add all the messages
for (var i=0;i<keys.length;i++) { for (var i=0;i<keys.length;i++) {
this.messages[keys[i]].write(buf); this.messages[keys[i]].write(buf);
} }
return buf.toString(); return new java.lang.String(buf);
}; };
/** /**
@ -392,10 +442,10 @@ MessageParser.prototype.getPotString = function() {
* @param {java.io.File} file The file to write to * @param {java.io.File} file The file to write to
*/ */
MessageParser.prototype.writeToFile = function(file) { MessageParser.prototype.writeToFile = function(file) {
var writer = new java.io.FileWriter(file); var writer = new java.io.FileWriter(file);
writer.write(new java.lang.String(this.getPotString().getBytes("UTF-8"))); writer.write(new java.lang.String(this.getPotString().getBytes("UTF-8")));
writer.close(); writer.close();
return; return;
}; };
/** /**
@ -405,24 +455,24 @@ var toParse = [];
var arg, outFile, file, fileEncoding; var arg, outFile, file, fileEncoding;
for (var i=0;i<arguments.length;i++) { for (var i=0;i<arguments.length;i++) {
arg = arguments[i]; arg = arguments[i];
if (arg.indexOf("-o") === 0 && i < arguments.length -1) { if (arg.indexOf("-o") === 0 && i < arguments.length -1) {
outFile = new java.io.File(arguments[i += 1]); outFile = new java.io.File(arguments[i += 1]);
} else if (arg.indexOf("-e") === 0 && i < arguments.length -1) { } else if (arg.indexOf("-e") === 0 && i < arguments.length -1) {
fileEncoding = arguments[i += 1]; fileEncoding = arguments[i += 1];
} else { } else {
// add argument to list of files and directories to parse // add argument to list of files and directories to parse
toParse.push(new java.io.File(arg)); toParse.push(new java.io.File(arg));
} }
} }
// start parsing // start parsing
var parser = new MessageParser(); var parser = new MessageParser();
for (var i=0;i<toParse.length;i++) { for (var i=0;i<toParse.length;i++) {
parser.parse(toParse[i], fileEncoding); parser.parse(toParse[i], fileEncoding);
} }
if (outFile != null) { if (outFile != null) {
parser.writeToFile(outFile); parser.writeToFile(outFile);
} else { } else {
print(parser.getPotString()); print(parser.getPotString());
} }

View file

@ -1,34 +1,26 @@
//
// Jala Project [http://opensvn.csie.org/traccgi/jala] // Jala Project [http://opensvn.csie.org/traccgi/jala]
// //
// Copyright 2004 ORF Online und Teletext GmbH // Copyright 2004 ORF Online und Teletext GmbH.
// //
// Licensed under the Apache License, Version 2.0 (the ``License''); // Licensed under the Apache License, Version 2.0 (the ``License'');
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an ``AS IS'' BASIS, // distributed under the License is distributed on an ``AS IS'' BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
//
// $Revision$
// $LastChangedBy$
// $LastChangedDate$
// $HeadURL$
//
/** /**
* @fileoverview * @fileoverview
* A parser script that converts GNU Gettext .po files into plain JavaScript objects * A parser script that converts GNU Gettext .po files into plain JavaScript objects
* for use with jala.I18n. To run it either start the build script of HopKit * for use with jala.I18n. To run it either start the build script of HopKit
* or call it directly using the JavaScript shell of Rhino: * or call it directly using the JavaScript shell of Rhino:
* <code>java -cp rhino.jar org.mozilla.javascript.tools.shell.Main PoParser.js <input> <output> [namespace]</code> * <code>java -cp rhino.jar org.mozilla.javascript.tools.shell.Main PoParser.js <input> <output> [namespace]</code>
* *
* The accepted arguments are: * The accepted arguments are:
* <ul> * <ul>
* <li><code>input</code>: Either a single .po file or a directory containing multiple files</li> * <li><code>input</code>: Either a single .po file or a directory containing multiple files</li>
@ -49,24 +41,24 @@
* @constructor * @constructor
*/ */
var PoParser = function(namespace) { var PoParser = function(namespace) {
/** /**
* An array containing the parsed messages * An array containing the parsed messages
* @type Array * @type Array
*/ */
this.messages = []; this.messages = [];
/** /**
* The locale key string (eg. "de_AT") of the .po file * The locale key string (eg. "de_AT") of the .po file
* @type String * @type String
*/ */
this.localeKey = null; this.localeKey = null;
/** /**
* The namespace (optional) where to store the generated messages * The namespace (optional) where to store the generated messages
* @type String * @type String
*/ */
this.namespace = namespace; this.namespace = namespace;
return this; return this;
}; };
/** /**
@ -108,7 +100,7 @@ PoParser.REGEX_MSGSTR = /^\s*msgstr(?:\[(\d)\])?\s+\"(.*)\"\s*$/;
PoParser.REGEX_DATA = /^\s*msg/; PoParser.REGEX_DATA = /^\s*msg/;
PoParser.isData = function(str) { PoParser.isData = function(str) {
return PoParser.REGEX_DATA.test(str); return PoParser.REGEX_DATA.test(str);
}; };
/** /**
@ -118,21 +110,21 @@ PoParser.isData = function(str) {
* @type java.lang.String * @type java.lang.String
*/ */
PoParser.readFile = function(file) { PoParser.readFile = function(file) {
var inStream = new java.io.InputStreamReader(new java.io.FileInputStream(file), "UTF-8"); var inStream = new java.io.InputStreamReader(new java.io.FileInputStream(file), "UTF-8");
var buffer = new java.lang.reflect.Array.newInstance(java.lang.Character.TYPE, 2048); var buffer = new java.lang.reflect.Array.newInstance(java.lang.Character.TYPE, 2048);
var read = 0; var read = 0;
var r = 0; var r = 0;
while ((r = inStream.read(buffer, read, buffer.length - read)) > -1) { while ((r = inStream.read(buffer, read, buffer.length - read)) > -1) {
read += r; read += r;
if (read == buffer.length) { if (read == buffer.length) {
// grow input buffer // grow input buffer
var newBuffer = new java.lang.reflect.Array.newInstance(java.lang.Character.TYPE, buffer.length * 2); var newBuffer = new java.lang.reflect.Array.newInstance(java.lang.Character.TYPE, buffer.length * 2);
java.lang.System.arraycopy(buffer, 0, newBuffer, 0, buffer.length); java.lang.System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
buffer = newBuffer; buffer = newBuffer;
} }
} }
inStream.close(); inStream.close();
return new java.lang.String(buffer, 0, read); return new java.lang.String(buffer, 0, read);
} }
/** /**
@ -141,63 +133,63 @@ PoParser.readFile = function(file) {
* @param {java.io.File} file The .po file to parse * @param {java.io.File} file The .po file to parse
*/ */
PoParser.prototype.parse = function(file) { PoParser.prototype.parse = function(file) {
// parse the locale key out of the file name // parse the locale key out of the file name
var fileName = file.getName(); var fileName = file.getName();
if (!(this.localeKey = fileName.substring(0, fileName.indexOf(".")))) { if (!(this.localeKey = fileName.substring(0, fileName.indexOf(".")))) {
throw "Invalid PO file name: " + fileName; throw "Invalid PO file name: " + fileName;
} }
// read the PO file content and parse it into messages // read the PO file content and parse it into messages
var content = PoParser.readFile(file); var content = PoParser.readFile(file);
var start = new Date(); var start = new Date();
var lines = content.split(PoParser.REGEX_LINES); var lines = content.split(PoParser.REGEX_LINES);
var idx = -1; var idx = -1;
var line = null; var line = null;
var m, value, nr; var m, value, nr;
var msg; var msg;
var hasMoreLines = function() { var hasMoreLines = function() {
return idx < lines.length - 1; return idx < lines.length - 1;
}; };
var nextLine = function() { var nextLine = function() {
return (line = lines[idx += 1]) != null; return (line = lines[idx += 1]) != null;
}; };
var getContinuation = function(str) { var getContinuation = function(str) {
var nLine; var nLine;
while ((nLine = lines[idx + 1]) != null) { while ((nLine = lines[idx + 1]) != null) {
if ((m = nLine.match(PoParser.REGEX_MSG_CONT)) != null) { if ((m = nLine.match(PoParser.REGEX_MSG_CONT)) != null) {
str += m[1]; str += m[1];
nextLine(); nextLine();
} else { } else {
break; break;
}
} }
return str; }
} return str;
}
while (nextLine()) { while (nextLine()) {
if ((m = line.match(PoParser.REGEX_MSGID)) != null) { if ((m = line.match(PoParser.REGEX_MSGID)) != null) {
value = getContinuation(m[1]); value = getContinuation(m[1]);
if (value) { if (value) {
msg = this.messages[this.messages.length] = new Message(value); msg = this.messages[this.messages.length] = new Message(value);
}
} else if ((m = line.match(PoParser.REGEX_MSGID_PLURAL)) != null) {
value = getContinuation(m[1]);
if (value && msg != null) {
msg.pluralKey = value;
}
} else if ((m = line.match(PoParser.REGEX_MSGSTR)) != null) {
nr = m[1];
value = getContinuation(m[2]);
if (value && msg != null) {
nr = parseInt(nr, 10);
msg.translations[nr || 0] = value;
}
} }
} } else if ((m = line.match(PoParser.REGEX_MSGID_PLURAL)) != null) {
return; value = getContinuation(m[1]);
if (value && msg != null) {
msg.pluralKey = value;
}
} else if ((m = line.match(PoParser.REGEX_MSGSTR)) != null) {
nr = m[1];
value = getContinuation(m[2]);
if (value && msg != null) {
nr = parseInt(nr, 10);
msg.translations[nr || 0] = value;
}
}
}
return;
}; };
/** /**
@ -207,44 +199,44 @@ PoParser.prototype.parse = function(file) {
* file should be saved * file should be saved
*/ */
PoParser.prototype.writeToFile = function(output) { PoParser.prototype.writeToFile = function(output) {
var buf = new java.lang.StringBuffer(); var buf = new java.lang.StringBuffer();
// write header // write header
buf.append('/**\n'); buf.append('/**\n');
buf.append(' * Instantiate the messages namespace if it\'s not already existing\n'); buf.append(' * Instantiate the messages namespace if it\'s not already existing\n');
buf.append(' */\n'); buf.append(' */\n');
var objPath = ""; var objPath = "";
if (this.namespace) { if (this.namespace) {
objPath += this.namespace; objPath += this.namespace;
buf.append('if (!global.' + objPath + ') {\n'); buf.append('if (!global.' + objPath + ') {\n');
buf.append(' global.' + objPath + ' = {};\n'); buf.append(' global.' + objPath + ' = {};\n');
buf.append('}\n'); buf.append('}\n');
objPath += "."; objPath += ".";
} }
objPath += "messages"; objPath += "messages";
buf.append('if (!global.' + objPath + ') {\n'); buf.append('if (!global.' + objPath + ') {\n');
buf.append(' global.' + objPath + ' = {};\n'); buf.append(' global.' + objPath + ' = {};\n');
buf.append('}\n\n'); buf.append('}\n\n');
buf.append('/**\n'); buf.append('/**\n');
buf.append(' * Messages for locale "' + this.localeKey + '"\n'); buf.append(' * Messages for locale "' + this.localeKey + '"\n');
buf.append(' */\n'); buf.append(' */\n');
objPath += "." + this.localeKey; var fname = objPath + "." + this.localeKey + ".js";
buf.append('global.' + objPath + ' = {\n'); objPath += "['" + this.localeKey + "']";
// write messages buf.append('global.' + objPath + ' = {\n');
for (var i=0;i<this.messages.length; i++) { // write messages
this.messages[i].write(buf); for (var i=0;i<this.messages.length; i++) {
} this.messages[i].write(buf);
// write footer }
buf.append('};\n'); // write footer
buf.append('};\n');
// write the message catalog into the outFile // write the message catalog into the outFile
var file = new java.io.File(output, objPath + ".js"); var file = new java.io.File(output, fname);
var writer = new java.io.FileWriter(file); var writer = new java.io.FileWriter(file);
writer.write(buf.toString()); writer.write(new java.lang.String(buf.toString().getBytes("UTF-8")));
// writer.write(buf.toString().getBytes("UTF-8")); writer.close();
writer.close(); print("generated messages file " + file.getAbsolutePath());
print("generated messages file " + file.getAbsolutePath()); return;
return;
}; };
/** /**
@ -255,10 +247,10 @@ PoParser.prototype.writeToFile = function(output) {
* @constructor * @constructor
*/ */
var Message = function(singularKey) { var Message = function(singularKey) {
this.singularKey = singularKey; this.singularKey = singularKey;
this.pluralKey = null; this.pluralKey = null;
this.translations = []; this.translations = [];
return this; return this;
} }
/** /**
@ -267,37 +259,37 @@ var Message = function(singularKey) {
* string to * string to
*/ */
Message.prototype.write = function(buf) { Message.prototype.write = function(buf) {
var writeLine = function(key, value) { var writeLine = function(key, value) {
buf.append(' "'); buf.append(' "');
buf.append(key); buf.append(key);
buf.append('": "'); buf.append('": "');
if (value !== null && value !== undefined) { if (value !== null && value !== undefined) {
buf.append(value); buf.append(value);
} }
buf.append('",\n'); buf.append('",\n');
}; };
if (this.singularKey != null) { if (this.singularKey != null) {
writeLine(this.singularKey, this.translations[0]); writeLine(this.singularKey, this.translations[0]);
if (this.pluralKey != null) { if (this.pluralKey != null) {
writeLine(this.pluralKey, this.translations[1]); writeLine(this.pluralKey, this.translations[1]);
} }
} }
return; return;
} }
/** /**
* Main script body * Main script body
*/ */
if (arguments.length < 2) { if (arguments.length < 2) {
print("Usage:"); print("Usage:");
print("PoParser.js <input> <output> [namespace]"); print("PoParser.js <input> <output> [namespace]");
print("<input>: Either a single .po file or a directory containing .po files"); print("<input>: Either a single .po file or a directory containing .po files");
print("<output>: The directory where the generated messages files should be stored"); print("<output>: The directory where the generated messages files should be stored");
print("[namespace]: An optional global namespace where the messages should be"); print("[namespace]: An optional global namespace where the messages should be");
print(" stored (eg. a namespace like 'jala' will lead to messages"); print(" stored (eg. a namespace like 'jala' will lead to messages");
print(" stored in global.jala.messages by their locale."); print(" stored in global.jala.messages by their locale.");
quit(); quit();
} }
var input = new java.io.File(arguments[0]); var input = new java.io.File(arguments[0]);
@ -306,31 +298,31 @@ var namespace = arguments[2];
// check if the output destination is a directory // check if the output destination is a directory
if (output.isFile()) { if (output.isFile()) {
print("Invalid arguments: the output destination must be a directory."); print("Invalid arguments: the output destination must be a directory.");
quit(); quit();
} }
if (namespace && namespace.indexOf(".") != -1) { if (namespace && namespace.indexOf(".") != -1) {
print("Invalid arguments: Please don't specify complex object paths, as this"); print("Invalid arguments: Please don't specify complex object paths, as this");
print("would corrupt the messages file."); print("would corrupt the messages file.");
quit(); quit();
} }
// parse the PO file(s) and create the message catalog files // parse the PO file(s) and create the message catalog files
var parser; var parser;
if (input.isDirectory()) { if (input.isDirectory()) {
var files = input.listFiles(); var files = input.listFiles();
var file; var file;
for (var i=0;i<files.length;i++) { for (var i=0;i<files.length;i++) {
file = files[i]; file = files[i];
if (file.getName().endsWith(".po")) { if (file.getName().endsWith(".po")) {
parser = new PoParser(namespace); parser = new PoParser(namespace);
parser.parse(file); parser.parse(file);
parser.writeToFile(output); parser.writeToFile(output);
} }
} }
} else { } else {
parser = new PoParser(namespace); parser = new PoParser(namespace);
parser.parse(input); parser.parse(input);
parser.writeToFile(output); parser.writeToFile(output);
} }