diff options
Diffstat (limited to 'tools/addon-sdk-1.12/lib/sdk/l10n')
-rw-r--r-- | tools/addon-sdk-1.12/lib/sdk/l10n/core.js | 35 | ||||
-rw-r--r-- | tools/addon-sdk-1.12/lib/sdk/l10n/html.js | 93 | ||||
-rw-r--r-- | tools/addon-sdk-1.12/lib/sdk/l10n/loader.js | 71 | ||||
-rw-r--r-- | tools/addon-sdk-1.12/lib/sdk/l10n/locale.js | 126 | ||||
-rw-r--r-- | tools/addon-sdk-1.12/lib/sdk/l10n/plural-rules.js | 403 | ||||
-rw-r--r-- | tools/addon-sdk-1.12/lib/sdk/l10n/prefs.js | 44 |
6 files changed, 772 insertions, 0 deletions
diff --git a/tools/addon-sdk-1.12/lib/sdk/l10n/core.js b/tools/addon-sdk-1.12/lib/sdk/l10n/core.js new file mode 100644 index 0000000..50472e9 --- /dev/null +++ b/tools/addon-sdk-1.12/lib/sdk/l10n/core.js @@ -0,0 +1,35 @@ +/* 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/. */ + +// Following pseudo module is set by `api-utils/addon/runner` and its load +// method needs to be called before loading `core` module. But it may have +// failed, so that this pseudo won't be available + +module.metadata = { + "stability": "unstable" +}; + + +let hash = {}, bestMatchingLocale = null; +try { + let data = require("@l10n/data"); + hash = data.hash; + bestMatchingLocale = data.bestMatchingLocale; +} +catch(e) {} + +// Returns the translation for a given key, if available. +exports.get = function get(k) { + return k in hash ? hash[k] : null; +} + +// Returns the full length locale code: ja-JP-mac, en-US or fr +exports.locale = function locale() { + return bestMatchingLocale; +} +// Returns the short locale code: ja, en, fr +exports.language = function language() { + return bestMatchingLocale ? bestMatchingLocale.split("-")[0].toLowerCase() + : null; +} diff --git a/tools/addon-sdk-1.12/lib/sdk/l10n/html.js b/tools/addon-sdk-1.12/lib/sdk/l10n/html.js new file mode 100644 index 0000000..1a36415 --- /dev/null +++ b/tools/addon-sdk-1.12/lib/sdk/l10n/html.js @@ -0,0 +1,93 @@ +/* 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/. */ + +module.metadata = { + "stability": "unstable" +}; + +const { Ci } = require("chrome"); +const events = require("../system/events"); +const core = require("./core"); + +const assetsURI = require('../self').data.url(); + +// Taken from Gaia: +// https://github.com/andreasgal/gaia/blob/04fde2640a7f40314643016a5a6c98bf3755f5fd/webapi.js#L1470 +function translateElement(element) { + element = element || document; + + // check all translatable children (= w/ a `data-l10n-id' attribute) + var children = element.querySelectorAll('*[data-l10n-id]'); + var elementCount = children.length; + for (var i = 0; i < elementCount; i++) { + var child = children[i]; + + // translate the child + var key = child.dataset.l10nId; + var data = core.get(key); + if (data) + child.textContent = data; + } +} +exports.translateElement = translateElement; + +function onDocumentReady2Translate(event) { + let document = event.target; + document.removeEventListener("DOMContentLoaded", onDocumentReady2Translate, + false); + + translateElement(document); + + // Finally display document when we finished replacing all text content + document.documentElement.style.visibility = "visible"; +} + +function onContentWindow(event) { + let document = event.subject; + + // Accept only HTML documents + if (!(document instanceof Ci.nsIDOMHTMLDocument)) + return; + + // Bug 769483: data:URI documents instanciated with nsIDOMParser + // have a null `location` attribute at this time + if (!document.location) + return; + + // Accept only document from this addon + if (document.location.href.indexOf(assetsURI) !== 0) + return; + + // First hide content of the document in order to have content blinking + // between untranslated and translated states + // TODO: use result of bug 737003 discussion in order to avoid any conflict + // with document CSS + document.documentElement.style.visibility = "hidden"; + + // Wait for DOM tree to be built before applying localization + document.addEventListener("DOMContentLoaded", onDocumentReady2Translate, + false); +} + +// Listen to creation of content documents in order to translate them as soon +// as possible in their loading process +const ON_CONTENT = "document-element-inserted"; +let enabled = false; +function enable() { + if (!enabled) { + events.on(ON_CONTENT, onContentWindow); + enabled = true; + } +} +exports.enable = enable; + +function disable() { + if (enabled) { + events.off(ON_CONTENT, onContentWindow); + enabled = false; + } +} +exports.disable = disable; + +require("api-utils/unload").when(disable); diff --git a/tools/addon-sdk-1.12/lib/sdk/l10n/loader.js b/tools/addon-sdk-1.12/lib/sdk/l10n/loader.js new file mode 100644 index 0000000..9af6c5c --- /dev/null +++ b/tools/addon-sdk-1.12/lib/sdk/l10n/loader.js @@ -0,0 +1,71 @@ +/* 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"; + +module.metadata = { + "stability": "unstable" +}; + +const { Cc, Ci } = require("chrome"); +const { getPreferedLocales, findClosestLocale } = require("./locale"); +const { readURI } = require("../net/url"); +const { resolve } = require("../core/promise"); + +function parseJsonURI(uri) { + return readURI(uri). + then(JSON.parse). + then(null, function (error) { + throw Error("Failed to parse locale file:\n" + uri + "\n" + error); + }); +} + +// Returns the array stored in `locales.json` manifest that list available +// locales files +function getAvailableLocales(rootURI) { + let uri = rootURI + "locales.json"; + return parseJsonURI(uri).then(function (manifest) { + return "locales" in manifest && + Array.isArray(manifest.locales) ? + manifest.locales : []; + }); +} + +// Returns URI of the best locales file to use from the XPI +function getBestLocale(rootURI) { + // Read localization manifest file that contains list of available languages + return getAvailableLocales(rootURI).then(function (availableLocales) { + // Retrieve list of prefered locales to use + let preferedLocales = getPreferedLocales(); + + // Compute the most preferable locale to use by using these two lists + return findClosestLocale(availableLocales, preferedLocales); + }); +} + +/** + * Read localization files and returns a promise of data to put in `@l10n/data` + * pseudo module, in order to allow l10n/core to fetch it. + */ +exports.load = function load(rootURI) { + // First, search for a locale file: + return getBestLocale(rootURI).then(function (bestMatchingLocale) { + // It may be null if the addon doesn't have any locale file + if (!bestMatchingLocale) + return resolve(null); + + let localeURI = rootURI + "locale/" + bestMatchingLocale + ".json"; + + // Locale files only contains one big JSON object that is used as + // an hashtable of: "key to translate" => "translated key" + // TODO: We are likely to change this in order to be able to overload + // a specific key translation. For a specific package, module or line? + return parseJsonURI(localeURI).then(function (json) { + return { + hash: json, + bestMatchingLocale: bestMatchingLocale + }; + }); + }); +} diff --git a/tools/addon-sdk-1.12/lib/sdk/l10n/locale.js b/tools/addon-sdk-1.12/lib/sdk/l10n/locale.js new file mode 100644 index 0000000..0e59a64 --- /dev/null +++ b/tools/addon-sdk-1.12/lib/sdk/l10n/locale.js @@ -0,0 +1,126 @@ +/* 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"; + +module.metadata = { + "stability": "unstable" +}; + +const prefs = require("../preferences/service"); +const { Cu, Cc, Ci } = require("chrome"); +const { Services } = Cu.import("resource://gre/modules/Services.jsm"); + + +/** + * Gets the currently selected locale for display. + * Gets all usable locale that we can use sorted by priority of relevance + * @return Array of locales, begins with highest priority + */ +const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS"; +const PREF_SELECTED_LOCALE = "general.useragent.locale"; +const PREF_ACCEPT_LANGUAGES = "intl.accept_languages"; +exports.getPreferedLocales = function getPreferedLocales() { + let locales = []; + + function addLocale(locale) { + locale = locale.toLowerCase(); + if (locales.indexOf(locale) === -1) + locales.push(locale); + } + + // Most important locale is OS one. But we use it, only if + // "intl.locale.matchOS" pref is set to `true`. + // Currently only used for multi-locales mobile builds. + // http://mxr.mozilla.org/mozilla-central/source/mobile/android/installer/Makefile.in#46 + if (prefs.get(PREF_MATCH_OS_LOCALE, false)) { + let localeService = Cc["@mozilla.org/intl/nslocaleservice;1"]. + getService(Ci.nsILocaleService); + let osLocale = localeService.getLocaleComponentForUserAgent(); + addLocale(osLocale); + } + + // In some cases, mainly on Fennec and on Linux version, + // `general.useragent.locale` is a special 'localized' value, like: + // "chrome://global/locale/intl.properties" + let browserUiLocale = prefs.getLocalized(PREF_SELECTED_LOCALE, "") || + prefs.get(PREF_SELECTED_LOCALE, ""); + if (browserUiLocale) + addLocale(browserUiLocale); + + + // Third priority is the list of locales used for web content + let contentLocales = prefs.get(PREF_ACCEPT_LANGUAGES, ""); + if (contentLocales) { + // This list is a string of locales seperated by commas. + // There is spaces after commas, so strip each item + for each(let locale in contentLocales.split(",")) + addLocale(locale.replace(/(^\s+)|(\s+$)/g, "")); + } + + // Finally, we ensure that en-US is the final fallback if it wasn't added + addLocale("en-US"); + + return locales; +} + +/** + * Selects the closest matching locale from a list of locales. + * + * @param aLocales + * An array of available locales + * @param aMatchLocales + * An array of prefered locales, ordered by priority. Most wanted first. + * Locales have to be in lowercase. + * If null, uses getPreferedLocales() results + * @return the best match for the currently selected locale + * + * Stolen from http://mxr.mozilla.org/mozilla-central/source/toolkit/mozapps/extensions/XPIProvider.jsm + */ +exports.findClosestLocale = function findClosestLocale(aLocales, aMatchLocales) { + + aMatchLocales = aMatchLocales || exports.getPreferedLocales(); + + // Holds the best matching localized resource + let bestmatch = null; + // The number of locale parts it matched with + let bestmatchcount = 0; + // The number of locale parts in the match + let bestpartcount = 0; + + for each (let locale in aMatchLocales) { + let lparts = locale.split("-"); + for each (let localized in aLocales) { + let found = localized.toLowerCase(); + // Exact match is returned immediately + if (locale == found) + return localized; + + let fparts = found.split("-"); + /* If we have found a possible match and this one isn't any longer + then we dont need to check further. */ + if (bestmatch && fparts.length < bestmatchcount) + continue; + + // Count the number of parts that match + let maxmatchcount = Math.min(fparts.length, lparts.length); + let matchcount = 0; + while (matchcount < maxmatchcount && + fparts[matchcount] == lparts[matchcount]) + matchcount++; + + /* If we matched more than the last best match or matched the same and + this locale is less specific than the last best match. */ + if (matchcount > bestmatchcount || + (matchcount == bestmatchcount && fparts.length < bestpartcount)) { + bestmatch = localized; + bestmatchcount = matchcount; + bestpartcount = fparts.length; + } + } + // If we found a valid match for this locale return it + if (bestmatch) + return bestmatch; + } + return null; +} diff --git a/tools/addon-sdk-1.12/lib/sdk/l10n/plural-rules.js b/tools/addon-sdk-1.12/lib/sdk/l10n/plural-rules.js new file mode 100644 index 0000000..22e5b8b --- /dev/null +++ b/tools/addon-sdk-1.12/lib/sdk/l10n/plural-rules.js @@ -0,0 +1,403 @@ +/* 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/. */ + +// This file is automatically generated with /python-lib/plural-rules-generator.py +// Fetching data from: http://unicode.org/repos/cldr/trunk/common/supplemental/plurals.xml + +// Mapping of short locale name == to == > rule index in following list + +module.metadata = { + "stability": "unstable" +}; + +const LOCALES_TO_RULES = { + "af": 3, + "ak": 4, + "am": 4, + "ar": 1, + "asa": 3, + "az": 0, + "be": 11, + "bem": 3, + "bez": 3, + "bg": 3, + "bh": 4, + "bm": 0, + "bn": 3, + "bo": 0, + "br": 20, + "brx": 3, + "bs": 11, + "ca": 3, + "cgg": 3, + "chr": 3, + "cs": 12, + "cy": 17, + "da": 3, + "de": 3, + "dv": 3, + "dz": 0, + "ee": 3, + "el": 3, + "en": 3, + "eo": 3, + "es": 3, + "et": 3, + "eu": 3, + "fa": 0, + "ff": 5, + "fi": 3, + "fil": 4, + "fo": 3, + "fr": 5, + "fur": 3, + "fy": 3, + "ga": 8, + "gd": 24, + "gl": 3, + "gsw": 3, + "gu": 3, + "guw": 4, + "gv": 23, + "ha": 3, + "haw": 3, + "he": 2, + "hi": 4, + "hr": 11, + "hu": 0, + "id": 0, + "ig": 0, + "ii": 0, + "is": 3, + "it": 3, + "iu": 7, + "ja": 0, + "jmc": 3, + "jv": 0, + "ka": 0, + "kab": 5, + "kaj": 3, + "kcg": 3, + "kde": 0, + "kea": 0, + "kk": 3, + "kl": 3, + "km": 0, + "kn": 0, + "ko": 0, + "ksb": 3, + "ksh": 21, + "ku": 3, + "kw": 7, + "lag": 18, + "lb": 3, + "lg": 3, + "ln": 4, + "lo": 0, + "lt": 10, + "lv": 6, + "mas": 3, + "mg": 4, + "mk": 16, + "ml": 3, + "mn": 3, + "mo": 9, + "mr": 3, + "ms": 0, + "mt": 15, + "my": 0, + "nah": 3, + "naq": 7, + "nb": 3, + "nd": 3, + "ne": 3, + "nl": 3, + "nn": 3, + "no": 3, + "nr": 3, + "nso": 4, + "ny": 3, + "nyn": 3, + "om": 3, + "or": 3, + "pa": 3, + "pap": 3, + "pl": 13, + "ps": 3, + "pt": 3, + "rm": 3, + "ro": 9, + "rof": 3, + "ru": 11, + "rwk": 3, + "sah": 0, + "saq": 3, + "se": 7, + "seh": 3, + "ses": 0, + "sg": 0, + "sh": 11, + "shi": 19, + "sk": 12, + "sl": 14, + "sma": 7, + "smi": 7, + "smj": 7, + "smn": 7, + "sms": 7, + "sn": 3, + "so": 3, + "sq": 3, + "sr": 11, + "ss": 3, + "ssy": 3, + "st": 3, + "sv": 3, + "sw": 3, + "syr": 3, + "ta": 3, + "te": 3, + "teo": 3, + "th": 0, + "ti": 4, + "tig": 3, + "tk": 3, + "tl": 4, + "tn": 3, + "to": 0, + "tr": 0, + "ts": 3, + "tzm": 22, + "uk": 11, + "ur": 3, + "ve": 3, + "vi": 0, + "vun": 3, + "wa": 4, + "wae": 3, + "wo": 0, + "xh": 3, + "xog": 3, + "yo": 0, + "zh": 0, + "zu": 3 +}; + +// Utility functions for plural rules methods +function isIn(n, list) list.indexOf(n) !== -1; +function isBetween(n, start, end) start <= n && n <= end; + +// List of all plural rules methods, that maps an integer to the plural form name to use +const RULES = { + "0": function (n) { + + return "other" + }, + "1": function (n) { + if ((isBetween((n % 100), 3, 10))) + return "few"; + if (n == 0) + return "zero"; + if ((isBetween((n % 100), 11, 99))) + return "many"; + if (n == 2) + return "two"; + if (n == 1) + return "one"; + return "other" + }, + "2": function (n) { + if (n != 0 && (n % 10) == 0) + return "many"; + if (n == 2) + return "two"; + if (n == 1) + return "one"; + return "other" + }, + "3": function (n) { + if (n == 1) + return "one"; + return "other" + }, + "4": function (n) { + if ((isBetween(n, 0, 1))) + return "one"; + return "other" + }, + "5": function (n) { + if ((isBetween(n, 0, 2)) && n != 2) + return "one"; + return "other" + }, + "6": function (n) { + if (n == 0) + return "zero"; + if ((n % 10) == 1 && (n % 100) != 11) + return "one"; + return "other" + }, + "7": function (n) { + if (n == 2) + return "two"; + if (n == 1) + return "one"; + return "other" + }, + "8": function (n) { + if ((isBetween(n, 3, 6))) + return "few"; + if ((isBetween(n, 7, 10))) + return "many"; + if (n == 2) + return "two"; + if (n == 1) + return "one"; + return "other" + }, + "9": function (n) { + if (n == 0 || n != 1 && (isBetween((n % 100), 1, 19))) + return "few"; + if (n == 1) + return "one"; + return "other" + }, + "10": function (n) { + if ((isBetween((n % 10), 2, 9)) && !(isBetween((n % 100), 11, 19))) + return "few"; + if ((n % 10) == 1 && !(isBetween((n % 100), 11, 19))) + return "one"; + return "other" + }, + "11": function (n) { + if ((isBetween((n % 10), 2, 4)) && !(isBetween((n % 100), 12, 14))) + return "few"; + if ((n % 10) == 0 || (isBetween((n % 10), 5, 9)) || (isBetween((n % 100), 11, 14))) + return "many"; + if ((n % 10) == 1 && (n % 100) != 11) + return "one"; + return "other" + }, + "12": function (n) { + if ((isBetween(n, 2, 4))) + return "few"; + if (n == 1) + return "one"; + return "other" + }, + "13": function (n) { + if ((isBetween((n % 10), 2, 4)) && !(isBetween((n % 100), 12, 14))) + return "few"; + if (n != 1 && (isBetween((n % 10), 0, 1)) || (isBetween((n % 10), 5, 9)) || (isBetween((n % 100), 12, 14))) + return "many"; + if (n == 1) + return "one"; + return "other" + }, + "14": function (n) { + if ((isBetween((n % 100), 3, 4))) + return "few"; + if ((n % 100) == 2) + return "two"; + if ((n % 100) == 1) + return "one"; + return "other" + }, + "15": function (n) { + if (n == 0 || (isBetween((n % 100), 2, 10))) + return "few"; + if ((isBetween((n % 100), 11, 19))) + return "many"; + if (n == 1) + return "one"; + return "other" + }, + "16": function (n) { + if ((n % 10) == 1 && n != 11) + return "one"; + return "other" + }, + "17": function (n) { + if (n == 3) + return "few"; + if (n == 0) + return "zero"; + if (n == 6) + return "many"; + if (n == 2) + return "two"; + if (n == 1) + return "one"; + return "other" + }, + "18": function (n) { + if (n == 0) + return "zero"; + if ((isBetween(n, 0, 2)) && n != 0 && n != 2) + return "one"; + return "other" + }, + "19": function (n) { + if ((isBetween(n, 2, 10))) + return "few"; + if ((isBetween(n, 0, 1))) + return "one"; + return "other" + }, + "20": function (n) { + if ((isBetween((n % 10), 3, 4) || ((n % 10) == 9)) && !(isBetween((n % 100), 10, 19) || isBetween((n % 100), 70, 79) || isBetween((n % 100), 90, 99))) + return "few"; + if ((n % 1000000) == 0 && n != 0) + return "many"; + if ((n % 10) == 2 && !isIn((n % 100), [12, 72, 92])) + return "two"; + if ((n % 10) == 1 && !isIn((n % 100), [11, 71, 91])) + return "one"; + return "other" + }, + "21": function (n) { + if (n == 0) + return "zero"; + if (n == 1) + return "one"; + return "other" + }, + "22": function (n) { + if ((isBetween(n, 0, 1)) || (isBetween(n, 11, 99))) + return "one"; + return "other" + }, + "23": function (n) { + if ((isBetween((n % 10), 1, 2)) || (n % 20) == 0) + return "one"; + return "other" + }, + "24": function (n) { + if ((isBetween(n, 3, 10) || isBetween(n, 13, 19))) + return "few"; + if (isIn(n, [2, 12])) + return "two"; + if (isIn(n, [1, 11])) + return "one"; + return "other" + }, +}; + +/** + * Return a function that gives the plural form name for a given integer + * for the specified `locale` + * let fun = getRulesForLocale('en'); + * fun(1) -> 'one' + * fun(0) -> 'other' + * fun(1000) -> 'other' + */ +exports.getRulesForLocale = function getRulesForLocale(locale) { + let index = LOCALES_TO_RULES[locale]; + if (!(index in RULES)) { + console.warn('Plural form unknown for locale "' + locale + '"'); + return function () { return "other"; }; + } + return RULES[index]; +} + diff --git a/tools/addon-sdk-1.12/lib/sdk/l10n/prefs.js b/tools/addon-sdk-1.12/lib/sdk/l10n/prefs.js new file mode 100644 index 0000000..016077c --- /dev/null +++ b/tools/addon-sdk-1.12/lib/sdk/l10n/prefs.js @@ -0,0 +1,44 @@ +/* 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 observers = require("../deprecated/observer-service"); +const core = require("./core"); +const { id: jetpackId} = require('../self'); + +const OPTIONS_DISPLAYED = "addon-options-displayed"; + +function onOptionsDisplayed(document, addonId) { + if (addonId !== jetpackId) + return; + let query = 'setting[data-jetpack-id="' + jetpackId + '"][pref-name], ' + + 'button[data-jetpack-id="' + jetpackId + '"][pref-name]'; + let nodes = document.querySelectorAll(query); + for (let node of nodes) { + let name = node.getAttribute("pref-name"); + if (node.tagName == "setting") { + let desc = core.get(name + "_description"); + if (desc) + node.setAttribute("desc", desc); + let title = core.get(name + "_title"); + if (title) + node.setAttribute("title", title); + + for (let item of node.querySelectorAll("menuitem, radio")) { + let key = name + "_options." + item.getAttribute("label"); + let label = core.get(key); + if (label) + item.setAttribute("label", label); + } + } + else if (node.tagName == "button") { + let label = core.get(name + "_label"); + if (label) + node.setAttribute("label", label); + } + } +} + +observers.add(OPTIONS_DISPLAYED, onOptionsDisplayed); |