cmlenz@369: /** cmlenz@369: * Babel JavaScript Support cmlenz@369: * cmlenz@369: * Copyright (C) 2008 Edgewall Software cmlenz@369: * All rights reserved. cmlenz@369: * cmlenz@369: * This software is licensed as described in the file COPYING, which cmlenz@369: * you should have received as part of this distribution. The terms cmlenz@369: * are also available at http://babel.edgewall.org/wiki/License. cmlenz@369: * cmlenz@369: * This software consists of voluntary contributions made by many cmlenz@369: * individuals. For the exact contribution history, see the revision cmlenz@369: * history and logs, available at http://babel.edgewall.org/log/. cmlenz@369: */ cmlenz@369: cmlenz@369: /** cmlenz@369: * A simple module that provides a gettext like translation interface. cmlenz@369: * The catalog passed to load() must be a object conforming to this cmlenz@369: * interface:: cmlenz@369: * cmlenz@369: * { cmlenz@369: * messages: an object of {msgid: translations} items where cmlenz@369: * translations is an array of messages or a single cmlenz@369: * string if the message is not pluralizable. cmlenz@369: * plural_expr: the plural expression for the language. cmlenz@369: * locale: the identifier for this locale. cmlenz@369: * domain: the name of the domain. cmlenz@369: * } cmlenz@369: * cmlenz@369: * Missing elements in the object are ignored. cmlenz@369: * cmlenz@369: * Typical usage:: cmlenz@369: * cmlenz@369: * var translations = babel.Translations.load(...).install(); cmlenz@369: */ cmlenz@369: var babel = new function() { cmlenz@369: cmlenz@369: var defaultPluralExpr = function(n) { return n == 1 ? 0 : 1; }; cmlenz@369: var formatRegex = /%?%(?:\(([^\)]+)\))?([disr])/g; cmlenz@369: cmlenz@369: /** cmlenz@369: * A translations object implementing the gettext interface cmlenz@369: */ cmlenz@369: var Translations = this.Translations = function(locale, domain) { cmlenz@369: this.messages = {}; cmlenz@369: this.locale = locale || 'unknown'; cmlenz@369: this.domain = domain || 'messages'; cmlenz@369: this.pluralexpr = defaultPluralExpr; cmlenz@369: }; cmlenz@369: cmlenz@369: /** cmlenz@369: * Create a new translations object from the catalog and return it. cmlenz@369: * See the babel-module comment for more details. cmlenz@369: */ cmlenz@369: Translations.load = function(catalog) { cmlenz@369: var rv = new Translations(); cmlenz@369: rv.load(catalog); cmlenz@369: return rv; cmlenz@369: }; cmlenz@369: cmlenz@369: Translations.prototype = { cmlenz@369: /** cmlenz@369: * translate a single string. cmlenz@369: */ cmlenz@369: gettext: function(string) { cmlenz@369: var translated = this.messages[string]; cmlenz@369: if (typeof translated == 'undefined') cmlenz@369: return string; cmlenz@369: return (typeof translated == 'string') ? translated : translated[0]; cmlenz@369: }, cmlenz@369: cmlenz@369: /** cmlenz@369: * translate a pluralizable string cmlenz@369: */ cmlenz@369: ngettext: function(singular, plural, n) { cmlenz@369: var translated = this.messages[singular]; cmlenz@369: if (typeof translated == 'undefined') cmlenz@369: return (n == 1) ? singular : plural; cmlenz@369: return translated[this.pluralexpr(n)]; cmlenz@369: }, cmlenz@369: cmlenz@369: /** cmlenz@369: * Install this translation document wide. After this call, there are cmlenz@369: * three new methods on the window object: _, gettext and ngettext cmlenz@369: */ cmlenz@369: install: function() { cmlenz@369: var self = this; cmlenz@369: window._ = window.gettext = function(string) { cmlenz@369: return self.gettext(string); cmlenz@369: }; cmlenz@369: window.ngettext = function(singular, plural, n) { cmlenz@369: return self.ngettext(singular, plural, n); cmlenz@369: }; cmlenz@369: return this; cmlenz@369: }, cmlenz@369: cmlenz@369: /** cmlenz@369: * Works like Translations.load but updates the instance rather cmlenz@369: * then creating a new one. cmlenz@369: */ cmlenz@369: load: function(catalog) { cmlenz@369: if (catalog.messages) cmlenz@369: this.update(catalog.messages) cmlenz@369: if (catalog.plural_expr) cmlenz@369: this.setPluralExpr(catalog.plural_expr); cmlenz@369: if (catalog.locale) cmlenz@369: this.locale = catalog.locale; cmlenz@369: if (catalog.domain) cmlenz@369: this.domain = catalog.domain; cmlenz@369: return this; cmlenz@369: }, cmlenz@369: cmlenz@369: /** cmlenz@369: * Updates the translations with the object of messages. cmlenz@369: */ cmlenz@369: update: function(mapping) { cmlenz@369: for (var key in mapping) cmlenz@369: if (mapping.hasOwnProperty(key)) cmlenz@369: this.messages[key] = mapping[key]; cmlenz@369: return this; cmlenz@369: }, cmlenz@369: cmlenz@369: /** cmlenz@369: * Sets the plural expression cmlenz@369: */ cmlenz@369: setPluralExpr: function(expr) { cmlenz@369: this.pluralexpr = new Function('n', 'return +(' + expr + ')'); cmlenz@369: return this; cmlenz@369: } cmlenz@369: }; cmlenz@369: cmlenz@369: /** cmlenz@369: * A python inspired string formatting function. Supports named and cmlenz@369: * positional placeholders and "s", "d" and "i" as type characters cmlenz@369: * without any formatting specifications. cmlenz@369: * cmlenz@369: * Examples:: cmlenz@369: * cmlenz@369: * babel.format(_('Hello %s'), name) cmlenz@369: * babel.format(_('Progress: %(percent)s%%'), {percent: 100}) cmlenz@369: */ cmlenz@369: this.format = function() { cmlenz@369: var arg, string = arguments[0], idx = 0; cmlenz@369: if (arguments.length == 1) cmlenz@369: return string; cmlenz@369: else if (arguments.length == 2 && typeof arguments[1] == 'object') cmlenz@369: arg = arguments[1]; cmlenz@369: else { cmlenz@369: arg = []; cmlenz@369: for (var i = 1, n = arguments.length; i != n; ++i) cmlenz@369: arg[i - 1] = arguments[i]; cmlenz@369: } cmlenz@369: return string.replace(formatRegex, function(all, name, type) { cmlenz@369: if (all[0] == all[1]) return all.substring(1); cmlenz@369: var value = arg[name || idx++]; cmlenz@369: return (type == 'i' || type == 'd') ? +value : value; cmlenz@369: }); cmlenz@369: } cmlenz@369: cmlenz@369: };