// // 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 Methods and macros for internationalization * of Helma applications. */ // Define the global namespace for Jala modules if (!global.jala) { global.jala = {}; } /** * Constructs a new instance of jala.I18n * @class This class provides various functions and macros for * internationalization of Helma applications. * @constructor * @type jala.I18n */ jala.I18n = function() { /** * The default object containing the messages. * @ignore */ var messages = global.messages; /** * The default method for retrieving the locale. * @ignore */ var localeGetter = function() { return java.util.Locale.getDefault(); }; /** * Overwrite the default object containing * the messages (ie. a vanilla EcmaScript object). * @param {Object} msgObject The object containing the messages */ this.setMessages = function(msgObject) { messages = msgObject; }; /** * Get the message object. * @returns The object containing the messages * @type Object */ this.getMessages = function() { return messages; }; /** * Set the method for retrieving the locale. * @param {Function} func The getter method */ this.setLocaleGetter = function(func) { if (func && func.constructor == Function) { localeGetter = func; } else { throw Error("Getter method to retrieve locale must be a function"); } return; }; /** * Get the method for retrieving the locale. * @returns The getter method * @type Function */ this.getLocaleGetter = function() { return localeGetter; }; return this; }; /** * The default handler containing the messages. * @ignore */ jala.I18n.HANDLER = global; /** @ignore */ jala.I18n.prototype.toString = function() { return "[Jala i18n]"; }; /** * Set (overwrite) the default handler containing * the messages (ie. a vanilla EcmaScript object). * @param {Object} handler The handler containing the message object * @deprecated Use {@link #setMessages} instead */ jala.I18n.prototype.setHandler = function(handler) { this.setMessages(handler.messages); return; }; /** * Returns the locale for the given id, which is expected to follow * the form language[_COUNTRY][_variant], where language * is a valid ISO Language Code (eg. "de"), COUNTRY a valid ISO * Country Code (eg. "AT"), and variant an identifier for the variant to use. * @returns The locale for the given id * @type java.util.Locale */ jala.I18n.prototype.getLocale = function(localeId) { if (localeId) { if (localeId.indexOf("_") > -1) { var arr = localeId.split("_"); if (arr.length == 3) { return new java.util.Locale(arr[0], arr[1], arr[2]); } else { return new java.util.Locale(arr[0], arr[1]); } } else { return new java.util.Locale(localeId); } } return java.util.Locale.getDefault(); } /** * Tries to "translate" the given message key into a localized * message. * @param {String} key The message to translate (required) * @param {String} plural The plural form of the message to translate * @param {Number} amount A number to determine whether to use the * singular or plural form of the message * @returns The localized message or the appropriate key if no * localized message was found * @type String */ jala.I18n.prototype.translate = function(singularKey, pluralKey, amount) { var translation = null; if (singularKey) { // use the getter method for retrieving the locale var locale = this.getLocaleGetter()(); var catalog, key; if ((catalog = jala.i18n.getCatalog(locale))) { if (arguments.length == 3 && amount != 1) { // is plural key = pluralKey; } else { key = singularKey; } if (!(translation = catalog[key])) { translation = key; app.logger.debug("jala.i18n.translate(): Can't find message '" + key + "' for locale '" + locale + "'"); } } else { app.logger.debug("jala.i18n.translate(): Can't find message catalog for locale '" + locale + "'"); if (!pluralKey || amount == 1) { translation = singularKey; } else { translation = pluralKey; } } } return translation; }; /** * Helper method to get the message catalog * corresponding to the actual locale. * @params {java.util.Locale} locale * @returns The message catalog. */ jala.I18n.prototype.getCatalog = function(locale) { if (!jala.I18n.catalogs) { jala.I18n.catalogs = {}; } var catalog = jala.I18n.catalogs[locale]; if (catalog) return catalog; var messages = this.getMessages(); if (locale && messages) { catalog = messages[locale.toLanguageTag()]; jala.I18n.catalogs[locale] = catalog; } return catalog; }; /** * Converts the message passed as argument into an instance * of java.text.MessageFormat, and formats it using the * replacement values passed. * @param {String} message The message to format * @param {Array} values An optional array containing replacement values * @returns The formatted message or, if the formatting fails, the * message passed as argument. * @type String * @see http://java.sun.com/j2se/1.5.0/docs/api/java/text/MessageFormat.html */ jala.I18n.prototype.formatMessage = function(message, values) { if (message) { var args = null; if (values != null && values.length > 0) { args = java.lang.reflect.Array.newInstance(java.lang.Object, values.length); var arg; for (var i=0;inumber must * match the number of the additional argument (starting with zero). * @param {String} key The message to localize * @returns The translated message * @type String * @see #translate * @see #formatMessage */ jala.I18n.prototype.gettext = function(key /** [value 0][, value 1][, ...] */) { return this.formatMessage(this.translate(key), Array.prototype.splice.call(arguments, 1)); }; /** * Returns a localized message for the message key passed as * argument. In contrast to gettext() this method * can handle plural forms based on the amount passed as argument. * If no localization is found, the appropriate message key is * returned. Any additional arguments passed to this function * will be used as replacement values during message rendering. * To reference these values the message can contain placeholders * following "{number}" notation, where number must * match the number of the additional argument (starting with zero). * @param {String} singularKey The singular message to localize * @param {String} pluralKey The plural form of the message to localize * @param {Number} amount The amount which is used to determine * whether the singular or plural form of the message should be returned. * @returns The translated message * @type String * @see #translate * @see #formatMessage */ jala.I18n.prototype.ngettext = function(singularKey, pluralKey, amount /** [value 0][, value 1][, ...] */) { return this.formatMessage(this.translate(singularKey, pluralKey, amount || 0), Array.prototype.splice.call(arguments, 2)); }; /** * A simple proxy method which is used to mark a message string * for the i18n parser as to be translated. * @param {String} key The message that should be seen by the * i18n parser as to be translated. * @returns The message in unmodified form * @type String */ jala.I18n.prototype.markgettext = function(key) { return key; }; /** * Returns a translated message. The following macro attributes * are accepted: * * @returns The translated message * @type String * @see #gettext * @see #ngettext */ jala.I18n.prototype.message_macro = function(param) { if (param.text) { var args = [param.text]; if (param.plural) { args[args.length] = param.plural; } if (param.values != null) { var arr = param.values.split(/\s*,\s*/g); // convert replacement values: if the value name doesn't contain // a dot, look for a global property with that name, otherwise // for a property of the specified macro handler object. var propName, dotIdx, handlerName, handler; for (var i=0;i 0) { var handlerName = propName.substring(0, dotIdx); if (handlerName == "request") { handler = req.data; } else if (handlerName == "response") { handler = res.data; } else if (!(handler = res.handlers[handlerName])) { continue; } propName = propName.substring(dotIdx + 1); // primitive security: don't allow access to internal properties // and a property named "password" if (propName.charAt(0) != "_" && propName.toLowerCase() != "password") { value = handler[propName]; } } else { value = global[propName]; } if (value != null) { // if its a HopObject collection or Array, use its size/length // as value if (value instanceof HopObject) { value = value.size(); } else if (value instanceof Array) { value = value.length; } } args[args.length] = value; } } } if (param.plural) { return this.ngettext.apply(this, args); } else { return this.gettext.apply(this, args); } } return; }; /** * Default i18n class instance. * @type jala.I18n * @final */ jala.i18n = new jala.I18n(); /** * For convenience reasons the public methods and macros are * put into global scope too */ var gettext = function() { return jala.i18n.gettext.apply(jala.i18n, arguments); }; var ngettext = function() { return jala.i18n.ngettext.apply(jala.i18n, arguments); }; var markgettext = function() { return jala.i18n.markgettext.apply(jala.i18n, arguments); }; var message_macro = function() { return jala.i18n.message_macro.apply(jala.i18n, arguments); };