1/** 2 * Babel JavaScript Support 3 * 4 * Copyright (C) 2008 Edgewall Software 5 * All rights reserved. 6 * 7 * This software is licensed as described in the file COPYING, which 8 * you should have received as part of this distribution. The terms 9 * are also available at http://babel.edgewall.org/wiki/License. 10 * 11 * This software consists of voluntary contributions made by many 12 * individuals. For the exact contribution history, see the revision 13 * history and logs, available at http://babel.edgewall.org/log/. 14 */ 15 16/** 17 * A simple module that provides a gettext like translation interface. 18 * The catalog passed to load() must be a object conforming to this 19 * interface:: 20 * 21 * { 22 * messages: an object of {msgid: translations} items where 23 * translations is an array of messages or a single 24 * string if the message is not pluralizable. 25 * plural_expr: the plural expression for the language. 26 * locale: the identifier for this locale. 27 * domain: the name of the domain. 28 * } 29 * 30 * Missing elements in the object are ignored. 31 * 32 * Typical usage:: 33 * 34 * var translations = babel.Translations.load(...).install(); 35 */ 36var babel = new function() { 37 38 var defaultPluralExpr = function(n) { return n == 1 ? 0 : 1; }; 39 var formatRegex = /%(?:(?:\(([^\)]+)\))?([disr])|%)/g; 40 var translations = {}; 41 var merged; 42 43 /** 44 * A translations object implementing the gettext interface 45 */ 46 var Translations = this.Translations = function(locale, domain) { 47 this.messages = {}; 48 this.locale = locale || 'unknown'; 49 this.domain = domain || 'messages'; 50 this.pluralexpr = defaultPluralExpr; 51 }; 52 53 /** 54 * Create a new translations object from the catalog and return it. 55 * See the babel-module comment for more details. 56 */ 57 Translations.load = function(catalog) { 58 var rv = new Translations(); 59 rv.load(catalog); 60 translations[rv.domain] = rv; 61 merged.load(catalog); 62 return rv; 63 }; 64 65 /** 66 * Get a Translations instance from the loaded translations. If the 67 * specified domain doesn't exist, returns a dummy Translations 68 * instance. 69 */ 70 Translations.get = function(domain) { 71 return translations[domain] || (new Translations({domain: domain})); 72 }; 73 74 Translations.prototype = { 75 /** 76 * translate a single string 77 * 78 * If extra parameters are given, use them to fill the format 79 * specified by the string. 80 */ 81 gettext: function(string) { 82 var translated = this.messages[string]; 83 if (typeof translated == 'undefined') 84 translated = string; 85 else if (typeof translated != 'string') 86 translated = translated[0]; 87 if (arguments.length > 1) { 88 arguments[0] = translated; 89 return babel.format.apply(this, arguments); 90 } 91 return translated; 92 }, 93 94 /** 95 * translate a pluralizable string 96 * 97 * If extra parameters are given, use them to fill the format 98 * specified by the string. 99 */ 100 ngettext: function(singular, plural, n) { 101 var translated = this.messages[singular]; 102 if (typeof translated == 'undefined') 103 translated = (n == 1) ? singular : plural; 104 else 105 translated = translated[this.pluralexpr(n)]; 106 if (arguments.length > 3) { 107 var format_args = Array.prototype.slice.call(arguments, 3); 108 format_args.unshift(translated); 109 return babel.format.apply(this, format_args) 110 } 111 return translated; 112 }, 113 114 /** 115 * Install this translation document wide. After this call, there are 116 * three new methods on the window object: _, gettext and ngettext 117 */ 118 install: function() { 119 window._ = window.gettext = function() { 120 return merged.gettext.apply(merged, arguments); 121 }; 122 window.ngettext = function(singular, plural, n) { 123 return merged.ngettext.apply(merged, arguments); 124 }; 125 return this; 126 }, 127 128 /** 129 * Works like Translations.load but updates the instance rather 130 * then creating a new one. 131 */ 132 load: function(catalog) { 133 if (catalog.messages) 134 this.update(catalog.messages) 135 if (catalog.plural_expr) 136 this.setPluralExpr(catalog.plural_expr); 137 if (catalog.locale) 138 this.locale = catalog.locale; 139 if (catalog.domain) 140 this.domain = catalog.domain; 141 return this; 142 }, 143 144 /** 145 * Updates the translations with the object of messages. 146 */ 147 update: function(mapping) { 148 for (var key in mapping) 149 if (mapping.hasOwnProperty(key)) 150 this.messages[key] = mapping[key]; 151 return this; 152 }, 153 154 /** 155 * Sets the plural expression 156 */ 157 setPluralExpr: function(expr) { 158 this.pluralexpr = new Function('n', 'return +(' + expr + ')'); 159 return this; 160 } 161 }; 162 163 merged = new Translations({}); 164 165 /**
166 * Translate a single string in the specified domain. 167 * 168 * If extra parameters are given, use them to fill the format 169 * specified by the string. 170 */ 171 window.dgettext = this.dgettext = function(domain, string) { 172 var rv = translations[domain]; 173 var args = Array.prototype.slice.call(arguments, 1); 174 if (typeof rv != 'undefined') 175 return rv.gettext.apply(rv, args); 176 if (arguments.length > 1) 177 return babel.format.apply(this, args); 178 return string; 179 }; 180 181 /** 182 * Translate a pluralizable string in the specified domain. 183 * 184 * If extra parameters are given, use them to fill the format 185 * specified by the string. 186 */ 187 window.dngettext = this.dngettext = function(domain, singular, plural, n) { 188 var rv = translations[domain]; 189 if (typeof rv != 'undefined') { 190 var args = Array.prototype.slice.call(arguments, 1); 191 return rv.ngettext.apply(rv, args); 192 } 193 if (arguments.length > 4) { 194 var args = Array.prototype.slice.call(arguments, 4); 195 args.unshift(singular); 196 return babel.format.apply(this, format_args) 197 } 198 return (n == 1) ? singular : plural; 199 }; 200 201 /** 202 * A python inspired string formatting function. Supports named and 203 * positional placeholders and "s", "d" and "i" as type characters 204 * without any formatting specifications. 205 * 206 * Examples:: 207 * 208 * babel.format(_('Hello %s'), name) 209 * babel.format(_('Progress: %(percent)s%%'), {percent: 100}) 210 */ 211 this.format = function() { 212 var arg, string = arguments[0], idx = 0; 213 if (arguments.length == 1) 214 return string; 215 else if (arguments.length == 2 && typeof arguments[1] == 'object') 216 arg = arguments[1]; 217 else { 218 arg = []; 219 for (var i = 1, n = arguments.length; i != n; ++i) 220 arg[i - 1] = arguments[i]; 221 } 222 return string.replace(formatRegex, function(all, name, type) { 223 if (all == '%%') return '%'; 224 var value = arg[name || idx++]; 225 return (type == 'i' || type == 'd') ? +value : value; 226 }); 227 } 228 229};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.