michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: "use strict"; michael@0: michael@0: module.metadata = { michael@0: "stability": "stable" michael@0: }; michael@0: michael@0: const json = require("./l10n/json/core"); michael@0: const { get: getKey } = require("./l10n/core"); michael@0: const properties = require("./l10n/properties/core"); michael@0: const { getRulesForLocale } = require("./l10n/plural-rules"); michael@0: michael@0: // Retrieve the plural mapping function michael@0: let pluralMappingFunction = getRulesForLocale(json.language()) || michael@0: getRulesForLocale("en"); michael@0: michael@0: exports.get = function get(k) { michael@0: // For now, we only accept a "string" as first argument michael@0: // TODO: handle plural forms in gettext pattern michael@0: if (typeof k !== "string") michael@0: throw new Error("First argument of localization method should be a string"); michael@0: let n = arguments[1]; michael@0: michael@0: // Get translation from big hashmap or default to hard coded string: michael@0: let localized = getKey(k, n) || k; michael@0: michael@0: // # Simplest usecase: michael@0: // // String hard coded in source code: michael@0: // _("Hello world") michael@0: // // Identifier of a key stored in properties file michael@0: // _("helloString") michael@0: if (arguments.length <= 1) michael@0: return localized; michael@0: michael@0: let args = arguments; michael@0: michael@0: if (typeof localized == "object" && "other" in localized) { michael@0: // # Plural form: michael@0: // // Strings hard coded in source code: michael@0: // _(["One download", "%d downloads"], 10); michael@0: // // Identifier of a key stored in properties file michael@0: // _("downloadNumber", 0); michael@0: let n = arguments[1]; michael@0: michael@0: // First handle simple universal forms that may not be mandatory michael@0: // for each language, (i.e. not different than 'other' form, michael@0: // but still usefull for better phrasing) michael@0: // For example 0 in english is the same form than 'other' michael@0: // but we accept 'zero' form if specified in localization file michael@0: if (n === 0 && "zero" in localized) michael@0: localized = localized["zero"]; michael@0: else if (n === 1 && "one" in localized) michael@0: localized = localized["one"]; michael@0: else if (n === 2 && "two" in localized) michael@0: localized = localized["two"]; michael@0: else { michael@0: let pluralForm = pluralMappingFunction(n); michael@0: if (pluralForm in localized) michael@0: localized = localized[pluralForm]; michael@0: else // Fallback in case of error: missing plural form michael@0: localized = localized["other"]; michael@0: } michael@0: michael@0: // Simulate a string with one placeholder: michael@0: args = [null, n]; michael@0: } michael@0: michael@0: // # String with placeholders: michael@0: // // Strings hard coded in source code: michael@0: // _("Hello %s", username) michael@0: // // Identifier of a key stored in properties file michael@0: // _("helloString", username) michael@0: // * We supports `%1s`, `%2s`, ... pattern in order to change arguments order michael@0: // in translation. michael@0: // * In case of plural form, we has `%d` instead of `%s`. michael@0: let offset = 1; michael@0: localized = localized.replace(/%(\d*)(s|d)/g, function (v, n) { michael@0: let rv = args[n != "" ? n : offset]; michael@0: offset++; michael@0: return rv; michael@0: }); michael@0: michael@0: return localized; michael@0: }