// // Jala Project [http://opensvn.csie.org/traccgi/jala] // // Copyright 2004 ORF Online und Teletext GmbH // // Licensed under the Apache License, Version 2.0 (the ``License''); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an ``AS IS'' BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // $Revision$ // $LastChangedBy$ // $LastChangedDate$ // $HeadURL$ // /** * @fileoverview * 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 * or call it directly using the JavaScript shell of Rhino: * java -cp rhino.jar org.mozilla.javascript.tools.shell.Main PoParser.js [namespace] * * The accepted arguments are: * */ /** * Constructs a new PoParser instance. * @class Instances of this class can generate JavaScript message files out * of GNU Gettext .po files for use with jala.I18n (and possibly other internationalization * environments too). * @param {String} handler An optional namespace where the parsed messages should be stored * @returns A newly created instance of PoParser * @constructor */ var PoParser = function(namespace) { /** * An array containing the parsed messages * @type Array */ this.messages = []; /** * The locale key string (eg. "de_AT") of the .po file * @type String */ this.localeKey = null; /** * The namespace (optional) where to store the generated messages * @type String */ this.namespace = namespace; return this; }; /** * A regular expression for splitting the contents of a .po file into * single lines * @type RegExp */ PoParser.REGEX_LINES = /\r\n|\r|\n|\u0085|\u2028|\u2029/; /** * A regular expression for parsing singular message keys * @type RegExp */ PoParser.REGEX_MSGID = /^\s*msgid(?!_plural)\s+\"(.*)\"\s*$/; /** * A regular expression for parsing plural message keys * @type RegExp */ PoParser.REGEX_MSGID_PLURAL = /^\s*msgid_plural\s+\"(.*)\"\s*$/; /** * A regular expression for parsing message key continuations * @type RegExp */ PoParser.REGEX_MSG_CONT = /^\s*\"(.*)\"\s*$/; /** * A regular expression for parsing a message translation * @type RegExp */ PoParser.REGEX_MSGSTR = /^\s*msgstr(?:\[(\d)\])?\s+\"(.*)\"\s*$/; /** * A regular expression used to detect lines other than whitespace * and comments * @type RegExp */ PoParser.REGEX_DATA = /^\s*msg/; PoParser.isData = function(str) { return PoParser.REGEX_DATA.test(str); }; /** * Reads the file passed as argument, assuming that it is UTF-8 encoded * @param {java.io.File} file The file to read * @returns The contents of the file * @type java.lang.String */ PoParser.readFile = function(file) { 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 read = 0; var r = 0; while ((r = inStream.read(buffer, read, buffer.length - read)) > -1) { read += r; if (read == buffer.length) { // grow input buffer 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); buffer = newBuffer; } } inStream.close(); return new java.lang.String(buffer, 0, read); } /** * Parses the PO file passed as argument into the messages array * of this PoParser instance. * @param {java.io.File} file The .po file to parse */ PoParser.prototype.parse = function(file) { // parse the locale key out of the file name var fileName = file.getName(); if (!(this.localeKey = fileName.substring(0, fileName.indexOf(".")))) { throw "Invalid PO file name: " + fileName; } // read the PO file content and parse it into messages var content = PoParser.readFile(file); var start = new Date(); var lines = content.split(PoParser.REGEX_LINES); var idx = -1; var line = null; var m, value, nr; var msg; var hasMoreLines = function() { return idx < lines.length - 1; }; var nextLine = function() { return (line = lines[idx += 1]) != null; }; var getContinuation = function(str) { var nLine; while ((nLine = lines[idx + 1]) != null) { if ((m = nLine.match(PoParser.REGEX_MSG_CONT)) != null) { str += m[1]; nextLine(); } else { break; } } return str; } while (nextLine()) { if ((m = line.match(PoParser.REGEX_MSGID)) != null) { value = getContinuation(m[1]); if (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; } } } return; }; /** * Converts the array containing the parsed messages into a message * catalog script file and saves it on disk. * @param {java.io.File} outputDir The directory where the message * file should be saved */ PoParser.prototype.writeToFile = function(output) { var buf = new java.lang.StringBuffer(); // write header buf.append('/**\n'); buf.append(' * Instantiate the messages namespace if it\'s not already existing\n'); buf.append(' */\n'); var objPath = ""; if (this.namespace) { objPath += this.namespace; buf.append('if (!global.' + objPath + ') {\n'); buf.append(' global.' + objPath + ' = {};\n'); buf.append('}\n'); objPath += "."; } objPath += "messages"; buf.append('if (!global.' + objPath + ') {\n'); buf.append(' global.' + objPath + ' = {};\n'); buf.append('}\n\n'); buf.append('/**\n'); buf.append(' * Messages for locale "' + this.localeKey + '"\n'); buf.append(' */\n'); objPath += "." + this.localeKey; buf.append('global.' + objPath + ' = {\n'); // write messages for (var i=0;i [namespace]"); print(": Either a single .po file or a directory containing .po files"); print(": The directory where the generated messages files should be stored"); print("[namespace]: An optional global namespace where the messages should be"); print(" stored (eg. a namespace like 'jala' will lead to messages"); print(" stored in global.jala.messages by their locale."); quit(); } var input = new java.io.File(arguments[0]); var output = new java.io.File(arguments[1]); var namespace = arguments[2]; // check if the output destination is a directory if (output.isFile()) { print("Invalid arguments: the output destination must be a directory."); quit(); } if (namespace && namespace.indexOf(".") != -1) { print("Invalid arguments: Please don't specify complex object paths, as this"); print("would corrupt the messages file."); quit(); } // parse the PO file(s) and create the message catalog files var parser; if (input.isDirectory()) { var files = input.listFiles(); var file; for (var i=0;i