1 /* See license.txt for terms of usage */ 2 3 // ********************************************************************************************* // 4 // Constants 5 6 const Cc = Components.classes; 7 const Ci = Components.interfaces; 8 const Cu = Components.utils; 9 10 const DEFAULT_LOCALE = "en-US"; 11 12 var EXPORTED_SYMBOLS = []; 13 14 // ********************************************************************************************* // 15 // Services 16 17 Cu.import("resource://firebug/fbtrace.js"); 18 Cu.import("resource://gre/modules/Services.jsm"); 19 Cu.import("resource://firebug/prefLoader.js"); 20 Cu.import("resource://gre/modules/PluralForm.jsm"); 21 22 // ********************************************************************************************* // 23 // Firebug UI Localization 24 25 var stringBundleService = Services.strings; 26 var categoryManager = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager); 27 28 // This module 29 var Locale = {}; 30 31 /* 32 * $STR - intended for localization of a static string. 33 * $STRF - intended for localization of a string with dynamically inserted values. 34 * $STRP - intended for localization of a string with dynamically plural forms. 35 * 36 * Notes: 37 * 1) Name with _ in place of spaces is the key in the firebug.properties file. 38 * 2) If the specified key isn't localized for particular language, both methods use 39 * the part after the last dot (in the specified name) as the return value. 40 * 41 * Examples: 42 * $STR("Label"); - search for key "Label" within the firebug.properties file 43 * and returns its value. If the key doesn't exist returns "Label". 44 * 45 * $STR("Button Label"); - search for key "Button_Label" withing the firebug.properties 46 * file. If the key doesn't exist returns "Button Label". 47 * 48 * $STR("net.Response Header"); - search for key "net.Response_Header". If the key doesn't 49 * exist returns "Response Header". 50 * 51 * firebug.properties: 52 * net.timing.Request_Time=Request Time: %S [%S] 53 * 54 * var param1 = 10; 55 * var param2 = "ms"; 56 * $STRF("net.timing.Request Time", param1, param2); -> "Request Time: 10 [ms]" 57 * 58 * - search for key "net.timing.Request_Time" within the firebug.properties file. Parameters 59 * are inserted at specified places (%S) in the same order as they are passed. If the 60 * key doesn't exist the method returns "Request Time". 61 */ 62 Locale.$STR = function(name, bundle) 63 { 64 var strKey = name.replace(" ", "_", "g"); 65 66 if (!PrefLoader.getPref("useDefaultLocale")) 67 { 68 try 69 { 70 if (bundle) 71 return bundle.getString(strKey); 72 else 73 return Locale.getStringBundle().GetStringFromName(strKey); 74 } 75 catch (err) 76 { 77 if (FBTrace.DBG_LOCALE) 78 FBTrace.sysout("lib.getString FAILS '" + name + "'", err); 79 } 80 } 81 82 try 83 { 84 // The en-US string should be always available. 85 var defaultBundle = Locale.getDefaultStringBundle(); 86 if (defaultBundle) 87 return defaultBundle.GetStringFromName(strKey); 88 } 89 catch (err) 90 { 91 if (FBTrace.DBG_LOCALE) 92 FBTrace.sysout("lib.getString (default) FAILS '" + name + "'", err); 93 } 94 95 // Don't panic now and use only the label after last dot. 96 var index = name.lastIndexOf("."); 97 if (index > 0 && name.charAt(index-1) != "\\") 98 name = name.substr(index + 1); 99 name = name.replace("_", " ", "g"); 100 return name; 101 } 102 103 Locale.$STRF = function(name, args, bundle) 104 { 105 var strKey = name.replace(" ", "_", "g"); 106 107 if (!PrefLoader.getPref("useDefaultLocale")) 108 { 109 try 110 { 111 if (bundle) 112 return bundle.getFormattedString(strKey, args); 113 else 114 return Locale.getStringBundle().formatStringFromName(strKey, args, args.length); 115 } 116 catch (err) 117 { 118 if (FBTrace.DBG_LOCALE) 119 FBTrace.sysout("lib.getString FAILS '" + name + "'", err); 120 } 121 } 122 123 try 124 { 125 // The en-US string should be always available. 126 var defaultBundle = Locale.getDefaultStringBundle(); 127 if (defaultBundle) 128 return defaultBundle.formatStringFromName(strKey, args, args.length); 129 } 130 catch (err) 131 { 132 if (FBTrace.DBG_LOCALE) 133 FBTrace.sysout("lib.getString (default) FAILS '" + name + "'", err); 134 } 135 136 // Don't panic now and use only the label after last dot. 137 var index = name.lastIndexOf("."); 138 if (index > 0) 139 name = name.substr(index + 1); 140 141 return name; 142 } 143 144 Locale.$STRP = function(name, args, index, bundle) 145 { 146 // xxxHonza: 147 // pluralRule from chrome://global/locale/intl.properties for Chinese is 1, 148 // which is wrong, it should be 0. 149 150 var getPluralForm = PluralForm.get; 151 var getNumForms = PluralForm.numForms; 152 153 // Get custom plural rule; otherwise the rule from chrome://global/locale/intl.properties 154 // (depends on the current locale) is used. 155 var pluralRule = Locale.getPluralRule(); 156 if (!isNaN(parseInt(pluralRule, 10))) 157 [getPluralForm, getNumForms] = PluralForm.makeGetter(pluralRule); 158 159 // Index of the argument with plural form (there must be only one arg that needs plural form). 160 if (!index) 161 index = 0; 162 163 // Get proper plural form from the string (depends on the current Firefox locale). 164 var translatedString = Locale.$STRF(name, args, bundle); 165 if (translatedString.search(";") > 0) 166 return getPluralForm(args[index], translatedString); 167 168 // translatedString contains no ";", either rule 0 or getString fails 169 return translatedString; 170 } 171 172 /* 173 * Use the current value of the attribute as a key to look up the localized value. 174 */ 175 Locale.internationalize = function(element, attr, args) 176 { 177 if (element) 178 { 179 var xulString = element.getAttribute(attr); 180 if (xulString) 181 { 182 var localized = args ? Locale.$STRF(xulString, args) : Locale.$STR(xulString); 183 // Set localized value of the attribute only if it exists. 184 if (localized) 185 element.setAttribute(attr, localized); 186 } 187 } 188 else 189 { 190 if (FBTrace.DBG_LOCALE) 191 FBTrace.sysout("Failed to internationalize element with attr "+attr+" args:"+args); 192 } 193 } 194 195 Locale.internationalizeElements = function(doc, elements, attributes) 196 { 197 for (var i=0; i<elements.length; i++) 198 { 199 var element = elements[i]; 200 201 if (typeof(elements) == "string") 202 element = doc.getElementById(elements[i]); 203 204 if (!element) 205 continue; 206 207 // Remove fbInternational class, so that the label is not translated again later. 208 element.classList.remove("fbInternational"); 209 210 for (var j=0; j<attributes.length; j++) 211 { 212 if (element.hasAttribute(attributes[j])) 213 Locale.internationalize(element, attributes[j]); 214 } 215 } 216 } 217 218 Locale.registerStringBundle = function(bundleURI) 219 { 220 // Notice that this category entry must not be persistent in Fx 4.0 221 categoryManager.addCategoryEntry("strings_firebug", bundleURI, "", false, true); 222 this.stringBundle = null; 223 224 bundleURI = getDefaultStringBundleURI(bundleURI); 225 categoryManager.addCategoryEntry("default_strings_firebug", bundleURI, "", false, true); 226 this.defaultStringBundle = null; 227 } 228 229 Locale.getStringBundle = function() 230 { 231 if (!this.stringBundle) 232 this.stringBundle = stringBundleService.createExtensibleBundle("strings_firebug"); 233 return this.stringBundle; 234 } 235 236 Locale.getDefaultStringBundle = function() 237 { 238 if (!this.defaultStringBundle) 239 this.defaultStringBundle = stringBundleService.createExtensibleBundle("default_strings_firebug"); 240 return this.defaultStringBundle; 241 } 242 243 Locale.getPluralRule = function() 244 { 245 try 246 { 247 return this.getStringBundle().GetStringFromName("pluralRule"); 248 } 249 catch (err) 250 { 251 } 252 } 253 254 // ********************************************************************************************* // 255 // Helpers 256 257 function getDefaultStringBundleURI(bundleURI) 258 { 259 var chromeRegistry = Cc["@mozilla.org/chrome/chrome-registry;1"]. 260 getService(Ci.nsIChromeRegistry); 261 262 var uri = Services.io.newURI(bundleURI, "UTF-8", null); 263 var fileURI = chromeRegistry.convertChromeURL(uri).spec; 264 var parts = fileURI.split("/"); 265 parts[parts.length - 2] = DEFAULT_LOCALE; 266 267 return parts.join("/"); 268 } 269 270 // ********************************************************************************************* // 271