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