summaryrefslogtreecommitdiffstats
path: root/addon-sdk/source/test/addons/l10n-properties
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /addon-sdk/source/test/addons/l10n-properties
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'addon-sdk/source/test/addons/l10n-properties')
-rw-r--r--addon-sdk/source/test/addons/l10n-properties/app-extension/application.ini11
-rw-r--r--addon-sdk/source/test/addons/l10n-properties/app-extension/bootstrap.js339
-rw-r--r--addon-sdk/source/test/addons/l10n-properties/app-extension/install.rdf33
-rw-r--r--addon-sdk/source/test/addons/l10n-properties/app-extension/locale/en-GB.properties28
-rw-r--r--addon-sdk/source/test/addons/l10n-properties/app-extension/locale/en-US.properties22
-rw-r--r--addon-sdk/source/test/addons/l10n-properties/app-extension/locale/eo.properties5
-rw-r--r--addon-sdk/source/test/addons/l10n-properties/app-extension/locale/fr-FR.properties14
-rw-r--r--addon-sdk/source/test/addons/l10n-properties/data/test-localization.html24
-rw-r--r--addon-sdk/source/test/addons/l10n-properties/main.js202
-rw-r--r--addon-sdk/source/test/addons/l10n-properties/package.json6
10 files changed, 684 insertions, 0 deletions
diff --git a/addon-sdk/source/test/addons/l10n-properties/app-extension/application.ini b/addon-sdk/source/test/addons/l10n-properties/app-extension/application.ini
new file mode 100644
index 000000000..6cec69a16
--- /dev/null
+++ b/addon-sdk/source/test/addons/l10n-properties/app-extension/application.ini
@@ -0,0 +1,11 @@
+[App]
+Vendor=Varma
+Name=Test App
+Version=1.0
+BuildID=20060101
+Copyright=Copyright (c) 2009 Atul Varma
+ID=xulapp@toolness.com
+
+[Gecko]
+MinVersion=1.9.2.0
+MaxVersion=2.0.*
diff --git a/addon-sdk/source/test/addons/l10n-properties/app-extension/bootstrap.js b/addon-sdk/source/test/addons/l10n-properties/app-extension/bootstrap.js
new file mode 100644
index 000000000..fbb9b5186
--- /dev/null
+++ b/addon-sdk/source/test/addons/l10n-properties/app-extension/bootstrap.js
@@ -0,0 +1,339 @@
+/* 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/. */
+
+// @see http://dxr.mozilla.org/mozilla-central/source/js/src/xpconnect/loader/mozJSComponentLoader.cpp
+
+'use strict';
+
+// IMPORTANT: Avoid adding any initialization tasks here, if you need to do
+// something before add-on is loaded consider addon/runner module instead!
+
+const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu,
+ results: Cr, manager: Cm } = Components;
+const ioService = Cc['@mozilla.org/network/io-service;1'].
+ getService(Ci.nsIIOService);
+const resourceHandler = ioService.getProtocolHandler('resource').
+ QueryInterface(Ci.nsIResProtocolHandler);
+const systemPrincipal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')();
+const scriptLoader = Cc['@mozilla.org/moz/jssubscript-loader;1'].
+ getService(Ci.mozIJSSubScriptLoader);
+const prefService = Cc['@mozilla.org/preferences-service;1'].
+ getService(Ci.nsIPrefService).
+ QueryInterface(Ci.nsIPrefBranch);
+const appInfo = Cc["@mozilla.org/xre/app-info;1"].
+ getService(Ci.nsIXULAppInfo);
+const vc = Cc["@mozilla.org/xpcom/version-comparator;1"].
+ getService(Ci.nsIVersionComparator);
+
+const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm");
+
+const REASON = [ 'unknown', 'startup', 'shutdown', 'enable', 'disable',
+ 'install', 'uninstall', 'upgrade', 'downgrade' ];
+
+const bind = Function.call.bind(Function.bind);
+
+var loader = null;
+var unload = null;
+var cuddlefishSandbox = null;
+var nukeTimer = null;
+
+// Utility function that synchronously reads local resource from the given
+// `uri` and returns content string.
+function readURI(uri) {
+ let channel = NetUtil.newChannel({
+ uri: NetUtil.newURI(uri, "UTF-8"),
+ loadUsingSystemPrincipal: true
+ });
+ let stream = channel.open2();
+
+ let cstream = Cc['@mozilla.org/intl/converter-input-stream;1'].
+ createInstance(Ci.nsIConverterInputStream);
+ cstream.init(stream, 'UTF-8', 0, 0);
+
+ let str = {};
+ let data = '';
+ let read = 0;
+ do {
+ read = cstream.readString(0xffffffff, str);
+ data += str.value;
+ } while (read != 0);
+
+ cstream.close();
+
+ return data;
+}
+
+// We don't do anything on install & uninstall yet, but in a future
+// we should allow add-ons to cleanup after uninstall.
+function install(data, reason) {}
+function uninstall(data, reason) {}
+
+function startup(data, reasonCode) {
+ try {
+ let reason = REASON[reasonCode];
+ // URI for the root of the XPI file.
+ // 'jar:' URI if the addon is packed, 'file:' URI otherwise.
+ // (Used by l10n module in order to fetch `locale` folder)
+ let rootURI = data.resourceURI.spec;
+
+ // TODO: Maybe we should perform read harness-options.json asynchronously,
+ // since we can't do anything until 'sessionstore-windows-restored' anyway.
+ let options = JSON.parse(readURI(rootURI + './harness-options.json'));
+
+ let id = options.jetpackID;
+ let name = options.name;
+
+ // Clean the metadata
+ options.metadata[name]['permissions'] = options.metadata[name]['permissions'] || {};
+
+ // freeze the permissionss
+ Object.freeze(options.metadata[name]['permissions']);
+ // freeze the metadata
+ Object.freeze(options.metadata[name]);
+
+ // Register a new resource 'domain' for this addon which is mapping to
+ // XPI's `resources` folder.
+ // Generate the domain name by using jetpack ID, which is the extension ID
+ // by stripping common characters that doesn't work as a domain name:
+ let uuidRe =
+ /^\{([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\}$/;
+
+ let domain = id.
+ toLowerCase().
+ replace(/@/g, '-at-').
+ replace(/\./g, '-dot-').
+ replace(uuidRe, '$1');
+
+ let prefixURI = 'resource://' + domain + '/';
+ let resourcesURI = ioService.newURI(rootURI + '/resources/', null, null);
+ resourceHandler.setSubstitution(domain, resourcesURI);
+
+ // Create path to URLs mapping supported by loader.
+ let paths = {
+ // Relative modules resolve to add-on package lib
+ './': prefixURI + name + '/lib/',
+ './tests/': prefixURI + name + '/tests/',
+ '': 'resource://gre/modules/commonjs/'
+ };
+
+ // Maps addon lib and tests ressource folders for each package
+ paths = Object.keys(options.metadata).reduce(function(result, name) {
+ result[name + '/'] = prefixURI + name + '/lib/'
+ result[name + '/tests/'] = prefixURI + name + '/tests/'
+ return result;
+ }, paths);
+
+ // We need to map tests folder when we run sdk tests whose package name
+ // is stripped
+ if (name == 'addon-sdk')
+ paths['tests/'] = prefixURI + name + '/tests/';
+
+ let useBundledSDK = options['force-use-bundled-sdk'];
+ if (!useBundledSDK) {
+ try {
+ useBundledSDK = prefService.getBoolPref("extensions.addon-sdk.useBundledSDK");
+ }
+ catch (e) {
+ // Pref doesn't exist, allow using Firefox shipped SDK
+ }
+ }
+
+ // Starting with Firefox 21.0a1, we start using modules shipped into firefox
+ // Still allow using modules from the xpi if the manifest tell us to do so.
+ // And only try to look for sdk modules in xpi if the xpi actually ship them
+ if (options['is-sdk-bundled'] &&
+ (vc.compare(appInfo.version, '21.0a1') < 0 || useBundledSDK)) {
+ // Maps sdk module folders to their resource folder
+ paths[''] = prefixURI + 'addon-sdk/lib/';
+ // test.js is usually found in root commonjs or SDK_ROOT/lib/ folder,
+ // so that it isn't shipped in the xpi. Keep a copy of it in sdk/ folder
+ // until we no longer support SDK modules in XPI:
+ paths['test'] = prefixURI + 'addon-sdk/lib/sdk/test.js';
+ }
+
+ // Retrieve list of module folder overloads based on preferences in order to
+ // eventually used a local modules instead of files shipped into Firefox.
+ let branch = prefService.getBranch('extensions.modules.' + id + '.path');
+ paths = branch.getChildList('', {}).reduce(function (result, name) {
+ // Allows overloading of any sub folder by replacing . by / in pref name
+ let path = name.substr(1).split('.').join('/');
+ // Only accept overloading folder by ensuring always ending with `/`
+ if (path) path += '/';
+ let fileURI = branch.getCharPref(name);
+
+ // On mobile, file URI has to end with a `/` otherwise, setSubstitution
+ // takes the parent folder instead.
+ if (fileURI[fileURI.length-1] !== '/')
+ fileURI += '/';
+
+ // Maps the given file:// URI to a resource:// in order to avoid various
+ // failure that happens with file:// URI and be close to production env
+ let resourcesURI = ioService.newURI(fileURI, null, null);
+ let resName = 'extensions.modules.' + domain + '.commonjs.path' + name;
+ resourceHandler.setSubstitution(resName, resourcesURI);
+
+ result[path] = 'resource://' + resName + '/';
+ return result;
+ }, paths);
+
+ // Make version 2 of the manifest
+ let manifest = options.manifest;
+
+ // Import `cuddlefish.js` module using a Sandbox and bootstrap loader.
+ let cuddlefishPath = 'loader/cuddlefish.js';
+ let cuddlefishURI = 'resource://gre/modules/commonjs/sdk/' + cuddlefishPath;
+ if (paths['sdk/']) { // sdk folder has been overloaded
+ // (from pref, or cuddlefish is still in the xpi)
+ cuddlefishURI = paths['sdk/'] + cuddlefishPath;
+ }
+ else if (paths['']) { // root modules folder has been overloaded
+ cuddlefishURI = paths[''] + 'sdk/' + cuddlefishPath;
+ }
+
+ cuddlefishSandbox = loadSandbox(cuddlefishURI);
+ let cuddlefish = cuddlefishSandbox.exports;
+
+ // Normalize `options.mainPath` so that it looks like one that will come
+ // in a new version of linker.
+ let main = options.mainPath;
+
+ unload = cuddlefish.unload;
+ loader = cuddlefish.Loader({
+ paths: paths,
+ // modules manifest.
+ manifest: manifest,
+
+ // Add-on ID used by different APIs as a unique identifier.
+ id: id,
+ // Add-on name.
+ name: name,
+ // Add-on version.
+ version: options.metadata[name].version,
+ // Add-on package descriptor.
+ metadata: options.metadata[name],
+ // Add-on load reason.
+ loadReason: reason,
+
+ prefixURI: prefixURI,
+ // Add-on URI.
+ rootURI: rootURI,
+ // options used by system module.
+ // File to write 'OK' or 'FAIL' (exit code emulation).
+ resultFile: options.resultFile,
+ // Arguments passed as --static-args
+ staticArgs: options.staticArgs,
+
+ // Arguments related to test runner.
+ modules: {
+ '@test/options': {
+ allTestModules: options.allTestModules,
+ iterations: options.iterations,
+ filter: options.filter,
+ profileMemory: options.profileMemory,
+ stopOnError: options.stopOnError,
+ verbose: options.verbose,
+ parseable: options.parseable,
+ checkMemory: options.check_memory,
+ }
+ }
+ });
+
+ let module = cuddlefish.Module('sdk/loader/cuddlefish', cuddlefishURI);
+ let require = cuddlefish.Require(loader, module);
+
+ require('sdk/addon/runner').startup(reason, {
+ loader: loader,
+ main: main,
+ prefsURI: rootURI + 'defaults/preferences/prefs.js'
+ });
+ } catch (error) {
+ dump('Bootstrap error: ' +
+ (error.message ? error.message : String(error)) + '\n' +
+ (error.stack || error.fileName + ': ' + error.lineNumber) + '\n');
+ throw error;
+ }
+};
+
+function loadSandbox(uri) {
+ let proto = {
+ sandboxPrototype: {
+ loadSandbox: loadSandbox,
+ ChromeWorker: ChromeWorker
+ }
+ };
+ let sandbox = Cu.Sandbox(systemPrincipal, proto);
+ // Create a fake commonjs environnement just to enable loading loader.js
+ // correctly
+ sandbox.exports = {};
+ sandbox.module = { uri: uri, exports: sandbox.exports };
+ sandbox.require = function (id) {
+ if (id !== "chrome")
+ throw new Error("Bootstrap sandbox `require` method isn't implemented.");
+
+ return Object.freeze({ Cc: Cc, Ci: Ci, Cu: Cu, Cr: Cr, Cm: Cm,
+ CC: bind(CC, Components), components: Components,
+ ChromeWorker: ChromeWorker });
+ };
+ scriptLoader.loadSubScript(uri, sandbox, 'UTF-8');
+ return sandbox;
+}
+
+function unloadSandbox(sandbox) {
+ if (Cu.getClassName(sandbox, true) == "Sandbox")
+ Cu.nukeSandbox(sandbox);
+}
+
+function setTimeout(callback, delay) {
+ let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ timer.initWithCallback({ notify: callback }, delay,
+ Ci.nsITimer.TYPE_ONE_SHOT);
+ return timer;
+}
+
+function shutdown(data, reasonCode) {
+ let reason = REASON[reasonCode];
+ if (loader) {
+ unload(loader, reason);
+ unload = null;
+
+ // Don't waste time cleaning up if the application is shutting down
+ if (reason != "shutdown") {
+ // Avoid leaking all modules when something goes wrong with one particular
+ // module. Do not clean it up immediatly in order to allow executing some
+ // actions on addon disabling.
+ // We need to keep a reference to the timer, otherwise it is collected
+ // and won't ever fire.
+ nukeTimer = setTimeout(nukeModules, 1000);
+ }
+ }
+};
+
+function nukeModules() {
+ nukeTimer = null;
+ // module objects store `exports` which comes from sandboxes
+ // We should avoid keeping link to these object to avoid leaking sandboxes
+ for (let key in loader.modules) {
+ delete loader.modules[key];
+ }
+ // Direct links to sandboxes should be removed too
+ for (let key in loader.sandboxes) {
+ let sandbox = loader.sandboxes[key];
+ delete loader.sandboxes[key];
+ // Bug 775067: From FF17 we can kill all CCW from a given sandbox
+ unloadSandbox(sandbox);
+ }
+ loader = null;
+
+ // both `toolkit/loader` and `system/xul-app` are loaded as JSM's via
+ // `cuddlefish.js`, and needs to be unloaded to avoid memory leaks, when
+ // the addon is unload.
+
+ unloadSandbox(cuddlefishSandbox.loaderSandbox);
+ unloadSandbox(cuddlefishSandbox.xulappSandbox);
+
+ // Bug 764840: We need to unload cuddlefish otherwise it will stay alive
+ // and keep a reference to this compartment.
+ unloadSandbox(cuddlefishSandbox);
+ cuddlefishSandbox = null;
+}
diff --git a/addon-sdk/source/test/addons/l10n-properties/app-extension/install.rdf b/addon-sdk/source/test/addons/l10n-properties/app-extension/install.rdf
new file mode 100644
index 000000000..8fc710557
--- /dev/null
+++ b/addon-sdk/source/test/addons/l10n-properties/app-extension/install.rdf
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<!-- 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/. -->
+
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+ <Description about="urn:mozilla:install-manifest">
+ <em:id>xulapp@toolness.com</em:id>
+ <em:version>1.0</em:version>
+ <em:type>2</em:type>
+ <em:bootstrap>true</em:bootstrap>
+ <em:unpack>false</em:unpack>
+
+ <!-- Firefox -->
+ <em:targetApplication>
+ <Description>
+ <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
+ <em:minVersion>21.0</em:minVersion>
+ <em:maxVersion>25.0a1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Test App</em:name>
+ <em:description>Harness for tests.</em:description>
+ <em:creator>Mozilla Corporation</em:creator>
+ <em:homepageURL></em:homepageURL>
+ <em:optionsType></em:optionsType>
+ <em:updateURL></em:updateURL>
+ </Description>
+</RDF>
diff --git a/addon-sdk/source/test/addons/l10n-properties/app-extension/locale/en-GB.properties b/addon-sdk/source/test/addons/l10n-properties/app-extension/locale/en-GB.properties
new file mode 100644
index 000000000..d14e6de0b
--- /dev/null
+++ b/addon-sdk/source/test/addons/l10n-properties/app-extension/locale/en-GB.properties
@@ -0,0 +1,28 @@
+# 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/.
+
+Translated= Yes
+
+text-content=no <b>HTML</b> injection
+
+downloadsCount=%d downloads
+downloadsCount[one]=one download
+
+pluralTest=fallback to other
+pluralTest[zero]=optional zero form
+
+explicitPlural[one]=one
+explicitPlural[other]=other
+
+# You can use unicode char escaping in order to inject space at the beginning/
+# end of your string. (Regular spaces are automatically ignore by .properties
+# file parser)
+unicodeEscape = \u0020\u0040\u0020
+# this string equals to " @ "
+
+# bug 1033309 plurals with multiple placeholders
+first_identifier[one]=first entry is %s and the second one is %s.
+first_identifier=the entries are %s and %s.
+second_identifier[other]=first entry is %s and the second one is %s.
+third_identifier=first entry is %s and the second one is %s.
diff --git a/addon-sdk/source/test/addons/l10n-properties/app-extension/locale/en-US.properties b/addon-sdk/source/test/addons/l10n-properties/app-extension/locale/en-US.properties
new file mode 100644
index 000000000..487fceb1d
--- /dev/null
+++ b/addon-sdk/source/test/addons/l10n-properties/app-extension/locale/en-US.properties
@@ -0,0 +1,22 @@
+# 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/.
+
+Translated= Yes
+
+text-content=no <b>HTML</b> injection
+
+downloadsCount=%d downloads
+downloadsCount[one]=one download
+
+pluralTest=fallback to other
+pluralTest[zero]=optional zero form
+
+explicitPlural[one]=one
+explicitPlural[other]=other
+
+# You can use unicode char escaping in order to inject space at the beginning/
+# end of your string. (Regular spaces are automatically ignore by .properties
+# file parser)
+unicodeEscape = \u0020\u0040\u0020
+# this string equals to " @ "
diff --git a/addon-sdk/source/test/addons/l10n-properties/app-extension/locale/eo.properties b/addon-sdk/source/test/addons/l10n-properties/app-extension/locale/eo.properties
new file mode 100644
index 000000000..a979fca1a
--- /dev/null
+++ b/addon-sdk/source/test/addons/l10n-properties/app-extension/locale/eo.properties
@@ -0,0 +1,5 @@
+# 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/.
+
+Translated= jes
diff --git a/addon-sdk/source/test/addons/l10n-properties/app-extension/locale/fr-FR.properties b/addon-sdk/source/test/addons/l10n-properties/app-extension/locale/fr-FR.properties
new file mode 100644
index 000000000..2c5ffbb17
--- /dev/null
+++ b/addon-sdk/source/test/addons/l10n-properties/app-extension/locale/fr-FR.properties
@@ -0,0 +1,14 @@
+# 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/.
+
+Translated= Oui
+
+placeholderString= Placeholder %s
+
+# Plural forms
+%d downloads=%d téléchargements
+%d downloads[one]=%d téléchargement
+
+downloadsCount=%d téléchargements
+downloadsCount[one]=%d téléchargement
diff --git a/addon-sdk/source/test/addons/l10n-properties/data/test-localization.html b/addon-sdk/source/test/addons/l10n-properties/data/test-localization.html
new file mode 100644
index 000000000..5428863ad
--- /dev/null
+++ b/addon-sdk/source/test/addons/l10n-properties/data/test-localization.html
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+
+<html>
+ <head>
+ <meta charset="UTF-8">
+ <title>HTML Localization</title>
+ </head>
+ <body>
+ <div data-l10n-id="Not translated">Kept as-is</div>
+ <ul data-l10n-id="Translated">
+ <li>Inner html content is replaced,</li>
+ <li data-l10n-id="text-content">
+ Elements with data-l10n-id attribute whose parent element is translated
+ will be replaced by the content of the translation.
+ </li>
+ </ul>
+ <div data-l10n-id="text-content">No</div>
+ <div data-l10n-id="Translated">
+ A data-l10n-id value can be used in multiple elements
+ </div>
+ </body>
+</html
diff --git a/addon-sdk/source/test/addons/l10n-properties/main.js b/addon-sdk/source/test/addons/l10n-properties/main.js
new file mode 100644
index 000000000..b2ca0b191
--- /dev/null
+++ b/addon-sdk/source/test/addons/l10n-properties/main.js
@@ -0,0 +1,202 @@
+/* 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 prefs = require("sdk/preferences/service");
+const { Loader } = require('sdk/test/loader');
+const { resolveURI } = require('toolkit/loader');
+const { rootURI } = require("@loader/options");
+const { usingJSON } = require('sdk/l10n/json/core');
+
+const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
+const PREF_SELECTED_LOCALE = "general.useragent.locale";
+
+function setLocale(locale) {
+ prefs.set(PREF_MATCH_OS_LOCALE, false);
+ prefs.set(PREF_SELECTED_LOCALE, locale);
+}
+
+function resetLocale() {
+ prefs.reset(PREF_MATCH_OS_LOCALE);
+ prefs.reset(PREF_SELECTED_LOCALE);
+}
+
+function definePseudo(loader, id, exports) {
+ let uri = resolveURI(id, loader.mapping);
+ loader.modules[uri] = { exports: exports };
+}
+
+function createTest(locale, testFunction) {
+ return function (assert, done) {
+ let loader = Loader(module);
+ // Change the locale before loading new l10n modules in order to load
+ // the right .json file
+ setLocale(locale);
+ // Initialize main l10n module in order to load new locale files
+ loader.require("sdk/l10n/loader").
+ load(rootURI).
+ then(function success(data) {
+ definePseudo(loader, '@l10n/data', data);
+ // Execute the given test function
+ try {
+ testFunction(assert, loader, function onDone() {
+ loader.unload();
+ resetLocale();
+ done();
+ });
+ }
+ catch(e) {
+ console.exception(e);
+ }
+ },
+ function failure(error) {
+ assert.fail("Unable to load locales: " + error);
+ });
+ };
+}
+
+exports.testExactMatching = createTest("fr-FR", function(assert, loader, done) {
+ let _ = loader.require("sdk/l10n").get;
+ assert.equal(_("Not translated"), "Not translated",
+ "Key not translated");
+ assert.equal(_("Translated"), "Oui",
+ "Simple key translated");
+
+ // Placeholders
+ assert.equal(_("placeholderString", "works"), "Placeholder works",
+ "Value with placeholder");
+ assert.equal(_("Placeholder %s", "works"), "Placeholder works",
+ "Key without value but with placeholder");
+ assert.equal(_("Placeholders %2s %1s %s.", "working", "are", "correctly"),
+ "Placeholders are working correctly.",
+ "Multiple placeholders");
+
+ // Plurals
+ assert.equal(_("downloadsCount", 0),
+ "0 téléchargement",
+ "PluralForm form 'one' for 0 in french");
+ assert.equal(_("downloadsCount", 1),
+ "1 téléchargement",
+ "PluralForm form 'one' for 1 in french");
+ assert.equal(_("downloadsCount", 2),
+ "2 téléchargements",
+ "PluralForm form 'other' for n > 1 in french");
+
+ done();
+});
+
+exports.testHtmlLocalization = createTest("en-GB", function(assert, loader, done) {
+ // Ensure initing html component that watch document creations
+ // Note that this module is automatically initialized in
+ // cuddlefish.js:Loader.main in regular addons. But it isn't for unit tests.
+ let loaderHtmlL10n = loader.require("sdk/l10n/html");
+ loaderHtmlL10n.enable();
+
+ let uri = require("sdk/self").data.url("test-localization.html");
+ let worker = loader.require("sdk/page-worker").Page({
+ contentURL: uri,
+ contentScript: "new " + function ContentScriptScope() {
+ let nodes = document.body.querySelectorAll("*[data-l10n-id]");
+ self.postMessage([nodes[0].innerHTML,
+ nodes[1].innerHTML,
+ nodes[2].innerHTML,
+ nodes[3].innerHTML]);
+ },
+ onMessage: function (data) {
+ assert.equal(
+ data[0],
+ "Kept as-is",
+ "Nodes with unknown id in .properties are kept 'as-is'"
+ );
+ assert.equal(data[1], "Yes", "HTML is translated");
+ assert.equal(
+ data[2],
+ "no &lt;b&gt;HTML&lt;/b&gt; injection",
+ "Content from .properties is text content; HTML can't be injected."
+ );
+ assert.equal(data[3], "Yes", "Multiple elements with same data-l10n-id are accepted.");
+
+ done();
+ }
+ });
+});
+
+exports.testEnUsLocaleName = createTest("en-GB", function(assert, loader, done) {
+ let _ = loader.require("sdk/l10n").get;
+
+ assert.equal(_("Not translated"), "Not translated",
+ "String w/o translation is kept as-is");
+ assert.equal(_("Translated"), "Yes",
+ "String with translation is correctly translated");
+
+ // Check Unicode char escaping sequences
+ assert.equal(_("unicodeEscape"), " @ ",
+ "Unicode escaped sequances are correctly converted");
+
+ // Check plural forms regular matching
+ assert.equal(_("downloadsCount", 0),
+ "0 downloads",
+ "PluralForm form 'other' for 0 in english");
+ assert.equal(_("downloadsCount", 1),
+ "one download",
+ "PluralForm form 'one' for 1 in english");
+ assert.equal(_("downloadsCount", 2),
+ "2 downloads",
+ "PluralForm form 'other' for n != 1 in english");
+
+ // Check optional plural forms
+ assert.equal(_("pluralTest", 0),
+ "optional zero form",
+ "PluralForm form 'zero' can be optionaly specified. (Isn't mandatory in english)");
+ assert.equal(_("pluralTest", 1),
+ "fallback to other",
+ "If the specific plural form is missing, we fallback to 'other'");
+
+ // Ensure that we can omit specifying the generic key without [other]
+ // key[one] = ...
+ // key[other] = ... # Instead of `key = ...`
+ assert.equal(_("explicitPlural", 1),
+ "one",
+ "PluralForm form can be omitting generic key [i.e. without ...[other] at end of key)");
+ assert.equal(_("explicitPlural", 10),
+ "other",
+ "PluralForm form can be omitting generic key [i.e. without ...[other] at end of key)");
+
+ assert.equal(_("first_identifier", "ONE", "TWO"), "the entries are ONE and TWO.", "first_identifier no count");
+ assert.equal(_("first_identifier", 0, "ONE", "TWO"), "the entries are ONE and TWO.", "first_identifier with count = 0");
+ assert.equal(_("first_identifier", 1, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "first_identifier with count = 1");
+ assert.equal(_("first_identifier", 2, "ONE", "TWO"), "the entries are ONE and TWO.", "first_identifier with count = 2");
+
+ assert.equal(_("second_identifier", "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "second_identifier with no count");
+ assert.equal(_("second_identifier", 0, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "second_identifier with count = 0");
+ assert.equal(_("second_identifier", 1, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "second_identifier with count = 1");
+ assert.equal(_("second_identifier", 2, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "second_identifier with count = 2");
+
+ assert.equal(_("third_identifier", "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "third_identifier with no count");
+ assert.equal(_("third_identifier", 0, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "third_identifier with count = 0");
+ assert.equal(_("third_identifier", 2, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "third_identifier with count = 2");
+
+ done();
+});
+
+exports.testUsingJSON = function(assert) {
+ assert.equal(usingJSON, false, 'not using json');
+}
+
+exports.testShortLocaleName = createTest("eo", function(assert, loader, done) {
+ let _ = loader.require("sdk/l10n").get;
+ assert.equal(_("Not translated"), "Not translated",
+ "String w/o translation is kept as-is");
+ assert.equal(_("Translated"), "jes",
+ "String with translation is correctly translated");
+
+ done();
+});
+
+
+// Before running tests, disable HTML service which is automatially enabled
+// in api-utils/addon/runner.js
+require('sdk/l10n/html').disable();
+
+require("sdk/test/runner").runTestsFromModule(module);
diff --git a/addon-sdk/source/test/addons/l10n-properties/package.json b/addon-sdk/source/test/addons/l10n-properties/package.json
new file mode 100644
index 000000000..1747298cb
--- /dev/null
+++ b/addon-sdk/source/test/addons/l10n-properties/package.json
@@ -0,0 +1,6 @@
+{
+ "id": "test-l10n@jetpack",
+ "title": "Test L10n",
+ "main": "./main.js",
+ "version": "0.0.1"
+}