diff --git a/devtools/client/shared/l10n.js b/devtools/client/shared/l10n.js new file mode 100644 index 000000000000..04bb006359ba --- /dev/null +++ b/devtools/client/shared/l10n.js @@ -0,0 +1,122 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { Ci } = require("chrome"); +const Services = require("Services"); + +/** + * Localization convenience methods. + * + * @param string stringBundleName + * The desired string bundle's name. + */ +function LocalizationHelper(stringBundleName) { + loader.lazyGetter(this, "stringBundle", () => + Services.strings.createBundle(stringBundleName)); + loader.lazyGetter(this, "ellipsis", () => + Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data); +} + +LocalizationHelper.prototype = { + /** + * L10N shortcut function. + * + * @param string name + * @return string + */ + getStr: function(name) { + return this.stringBundle.GetStringFromName(name); + }, + + /** + * L10N shortcut function. + * + * @param string name + * @param array args + * @return string + */ + getFormatStr: function(name, ...args) { + return this.stringBundle.formatStringFromName(name, args, args.length); + }, + + /** + * L10N shortcut function for numeric arguments that need to be formatted. + * All numeric arguments will be fixed to 2 decimals and given a localized + * decimal separator. Other arguments will be left alone. + * + * @param string name + * @param array args + * @return string + */ + getFormatStrWithNumbers: function(name, ...args) { + let newArgs = args.map(x => typeof x == "number" ? this.numberWithDecimals(x, 2) : x); + return this.stringBundle.formatStringFromName(name, newArgs, newArgs.length); + }, + + /** + * Converts a number to a locale-aware string format and keeps a certain + * number of decimals. + * + * @param number number + * The number to convert. + * @param number decimals [optional] + * Total decimals to keep. + * @return string + * The localized number as a string. + */ + numberWithDecimals: function(number, decimals = 0) { + // If this is an integer, don't do anything special. + if (number === (number|0)) { + return number; + } + // If this isn't a number (and yes, `isNaN(null)` is false), return zero. + if (isNaN(number) || number === null) { + return "0"; + } + + let localized = number.toLocaleString(); + + // If no grouping or decimal separators are available, bail out, because + // padding with zeros at the end of the string won't make sense anymore. + if (!localized.match(/[^\d]/)) { + return localized; + } + + return number.toLocaleString(undefined, { + maximumFractionDigits: decimals, + minimumFractionDigits: decimals + }); + } +}; + +/** + * A helper for having the same interface as LocalizationHelper, but for more than + * one file. Useful for abstracting l10n string locations. + */ +function MultiLocalizationHelper(...stringBundleNames) { + let instances = stringBundleNames.map(bundle => new LocalizationHelper(bundle)); + + // Get all function members of the LocalizationHelper class, making sure we're not + // executing any potential getters while doing so, and wrap all the + // methods we've found to work on all given string bundles. + Object.getOwnPropertyNames(LocalizationHelper.prototype) + .map(name => ({ + name: name, + descriptor: Object.getOwnPropertyDescriptor(LocalizationHelper.prototype, name) + })) + .filter(({ descriptor }) => descriptor.value instanceof Function) + .forEach(method => { + this[method.name] = (...args) => { + for (let l10n of instances) { + try { + return method.descriptor.value.apply(l10n, args); + } catch (e) {} + } + }; + }); +} + +exports.LocalizationHelper = LocalizationHelper; +exports.MultiLocalizationHelper = MultiLocalizationHelper; diff --git a/devtools/client/shared/moz.build b/devtools/client/shared/moz.build index c3ab5dae89d5..9d2d1132987e 100644 --- a/devtools/client/shared/moz.build +++ b/devtools/client/shared/moz.build @@ -32,6 +32,7 @@ DevToolsModules( 'getjson.js', 'inplace-editor.js', 'Jsbeautify.jsm', + 'l10n.js', 'node-attribute-parser.js', 'options-view.js', 'output-parser.js', diff --git a/devtools/client/shared/widgets/ViewHelpers.jsm b/devtools/client/shared/widgets/ViewHelpers.jsm index a0d8d601804b..62eecd4e52b1 100644 --- a/devtools/client/shared/widgets/ViewHelpers.jsm +++ b/devtools/client/shared/widgets/ViewHelpers.jsm @@ -306,114 +306,7 @@ this.ViewHelpers = { } }; -/** - * Localization convenience methods. - * - * @param string aStringBundleName - * The desired string bundle's name. - */ -ViewHelpers.L10N = function(aStringBundleName) { - XPCOMUtils.defineLazyGetter(this, "stringBundle", () => - Services.strings.createBundle(aStringBundleName)); - XPCOMUtils.defineLazyGetter(this, "ellipsis", () => - Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data); -}; - -ViewHelpers.L10N.prototype = { - stringBundle: null, - - /** - * L10N shortcut function. - * - * @param string aName - * @return string - */ - getStr: function(aName) { - return this.stringBundle.GetStringFromName(aName); - }, - - /** - * L10N shortcut function. - * - * @param string aName - * @param array aArgs - * @return string - */ - getFormatStr: function(aName, ...aArgs) { - return this.stringBundle.formatStringFromName(aName, aArgs, aArgs.length); - }, - - /** - * L10N shortcut function for numeric arguments that need to be formatted. - * All numeric arguments will be fixed to 2 decimals and given a localized - * decimal separator. Other arguments will be left alone. - * - * @param string aName - * @param array aArgs - * @return string - */ - getFormatStrWithNumbers: function(aName, ...aArgs) { - let newArgs = aArgs.map(x => typeof x == "number" ? this.numberWithDecimals(x, 2) : x); - return this.stringBundle.formatStringFromName(aName, newArgs, newArgs.length); - }, - - /** - * Converts a number to a locale-aware string format and keeps a certain - * number of decimals. - * - * @param number aNumber - * The number to convert. - * @param number aDecimals [optional] - * Total decimals to keep. - * @return string - * The localized number as a string. - */ - numberWithDecimals: function(aNumber, aDecimals = 0) { - // If this is an integer, don't do anything special. - if (aNumber == (aNumber | 0)) { - return aNumber; - } - if (isNaN(aNumber) || aNumber == null) { - return "0"; - } - let localized = aNumber.toLocaleString(); // localize - - // If no grouping or decimal separators are available, bail out, because - // padding with zeros at the end of the string won't make sense anymore. - if (!localized.match(/[^\d]/)) { - return localized; - } - - return aNumber.toLocaleString(undefined, { - maximumFractionDigits: aDecimals, - minimumFractionDigits: aDecimals - }); - } -}; - -/** - * A helper for having the same interface as ViewHelpers.L10N, but for - * more than one file. Useful for abstracting l10n string locations. - */ -ViewHelpers.MultiL10N = function(aStringBundleNames) { - let l10ns = aStringBundleNames.map(bundle => new ViewHelpers.L10N(bundle)); - let proto = ViewHelpers.L10N.prototype; - - Object.getOwnPropertyNames(proto) - .map(name => ({ - name: name, - desc: Object.getOwnPropertyDescriptor(proto, name) - })) - .filter(property => property.desc.value instanceof Function) - .forEach(method => { - this[method.name] = function(...args) { - for (let l10n of l10ns) { - try { return method.desc.value.apply(l10n, args) } catch (e) {} - } - }; - }); -}; /** * Shortcuts for lazily accessing and setting various preferences.