Bug 1213450 - Complete the implementation of chrome.i18n - getAcceptLanguages, r?kmag
Implement browser.i18n.getAcceptLanguages() including tests
Rebase against fx-team, resolving some conflicts
Fix eslint errors/warnings
MozReview-Commit-ID: 52sZWsIHbl4
--- a/toolkit/components/extensions/ExtensionContent.jsm
+++ b/toolkit/components/extensions/ExtensionContent.jsm
@@ -115,16 +115,21 @@ var api = context => {
inIncognitoContext: PrivateBrowsingUtils.isContentWindowPrivate(context.contentWindow),
},
i18n: {
getMessage: function(messageName, substitutions) {
return context.extension.localizeMessage(messageName, substitutions);
},
+ getAcceptLanguages: function(callback) {
+ let result = context.extension.localeData.acceptLanguages;
+ return context.wrapPromise(Promise.resolve(result), callback);
+ },
+
getUILanguage: function() {
return context.extension.localeData.uiLocale;
},
detectLanguage: function(text, callback) {
let result = detectLanguage(text);
return context.wrapPromise(result, callback);
},
--- a/toolkit/components/extensions/ExtensionUtils.jsm
+++ b/toolkit/components/extensions/ExtensionUtils.jsm
@@ -15,16 +15,18 @@ Cu.import("resource://gre/modules/XPCOMU
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "LanguageDetector",
"resource:///modules/translation/LanguageDetector.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Locale",
"resource://gre/modules/Locale.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
+ "resource://gre/modules/Preferences.jsm");
function filterStack(error) {
return String(error.stack).replace(/(^.*(Task\.jsm|Promise-backend\.js).*\n)+/gm, "<Promise Chain>\n");
}
// Run a function and report exceptions.
function runSafeSyncWithoutClone(f, ...args) {
try {
@@ -424,16 +426,26 @@ LocaleData.prototype = {
// Message names are also case-insensitive, so normalize them to lower-case.
result.set(key.toLowerCase(), value);
}
this.messages.set(locale, result);
return result;
},
+ get acceptLanguages() {
+ let result = Preferences.get("intl.accept_languages", "", Ci.nsIPrefLocalizedString);
+ result = result.split(",");
+ result = result.map(lang => {
+ return lang.replace(/-/g, "_").trim();
+ });
+ return result;
+ },
+
+
get uiLocale() {
// Return the browser locale, but convert it to a Chrome-style
// locale code.
return Locale.getLocale().replace(/-/g, "_");
},
};
// This is a generic class for managing event listeners. Example usage:
--- a/toolkit/components/extensions/ext-i18n.js
+++ b/toolkit/components/extensions/ext-i18n.js
@@ -7,16 +7,21 @@ var {
extensions.registerSchemaAPI("i18n", null, (extension, context) => {
return {
i18n: {
getMessage: function(messageName, substitutions) {
return extension.localizeMessage(messageName, substitutions);
},
+ getAcceptLanguages: function() {
+ let result = extension.localeData.acceptLanguages;
+ return Promise.resolve(result);
+ },
+
getUILanguage: function() {
return extension.localeData.uiLocale;
},
detectLanguage: function(text) {
return detectLanguage(text);
},
},
--- a/toolkit/components/extensions/schemas/i18n.json
+++ b/toolkit/components/extensions/schemas/i18n.json
@@ -25,17 +25,16 @@
"id": "LanguageCode",
"type": "string",
"description": "An ISO language code such as <code>en</code> or <code>fr</code>. For a complete list of languages supported by this method, see <a href='http://src.chromium.org/viewvc/chrome/trunk/src/third_party/cld/languages/internal/languages.cc'>kLanguageInfoTable</a>. For an unknown language, <code>und</code> will be returned, which means that [percentage] of the text is unknown to CLD"
}
],
"functions": [
{
"name": "getAcceptLanguages",
- "unsupported": true,
"type": "function",
"description": "Gets the accept-languages of the browser. This is different from the locale used by the browser; to get the locale, use $(ref:i18n.getUILanguage).",
"async": "callback",
"parameters": [
{
"type": "function",
"name": "callback",
"parameters": [
--- a/toolkit/components/extensions/test/mochitest/test_ext_i18n.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_i18n.html
@@ -9,16 +9,17 @@
<script type="text/javascript" src="head.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="text/javascript">
"use strict";
+SimpleTest.registerCleanupFunction(() => { SpecialPowers.clearUserPref("intl.accept_languages"); });
SimpleTest.registerCleanupFunction(() => { SpecialPowers.clearUserPref("general.useragent.locale"); });
add_task(function* test_i18n() {
function runTests(assertEq) {
let _ = browser.i18n.getMessage.bind(browser.i18n);
let url = browser.runtime.getURL("/");
assertEq(url, `moz-extension://${_("@@extension_id")}/`, "@@extension_id builtin message");
@@ -158,16 +159,95 @@ add_task(function* test_i18n() {
let win = window.open("file_sample.html");
yield extension.awaitMessage("content-script-finished");
win.close();
yield extension.awaitFinish("l10n");
yield extension.unload();
});
+add_task(function* test_get_accept_languages() {
+ function background() {
+ function checkResults(source, results, expected) {
+ browser.test.assertEq(
+ expected.length,
+ results.length,
+ `got expected number of languages in ${source}`);
+ results.forEach((lang, index) => {
+ browser.test.assertEq(
+ expected[index],
+ lang,
+ `got expected language in ${source}`);
+ });
+ }
+
+ let tabId;
+
+ browser.tabs.query({currentWindow: true, active: true}, tabs => {
+ tabId = tabs[0].id;
+ browser.test.sendMessage("ready");
+ });
+
+ browser.test.onMessage.addListener(([msg, expected]) => {
+ Promise.all([
+ new Promise(
+ resolve => browser.tabs.sendMessage(tabId, "get-results", resolve)),
+ browser.i18n.getAcceptLanguages(),
+ ]).then(([contentResults, backgroundResults]) => {
+ checkResults("contentScript", contentResults, expected);
+ checkResults("background", backgroundResults, expected);
+
+ browser.test.sendMessage("done");
+ });
+ });
+ }
+
+ function content() {
+ browser.runtime.onMessage.addListener((msg, sender, respond) => {
+ browser.i18n.getAcceptLanguages(respond);
+ return true;
+ });
+ }
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ "content_scripts": [{
+ "matches": ["http://mochi.test/*/file_sample.html"],
+ "run_at": "document_start",
+ "js": ["content_script.js"],
+ }],
+ },
+
+ background: `(${background})()`,
+
+ files: {
+ "content_script.js": `(${content})()`,
+ },
+ });
+
+ let win = window.open("file_sample.html");
+
+ yield extension.startup();
+ yield extension.awaitMessage("ready");
+
+ let expectedLangs = ["en_US", "en"];
+ extension.sendMessage(["expect-results", expectedLangs]);
+ yield extension.awaitMessage("done");
+
+ expectedLangs = ["en_US", "en", "fr_CA", "fr"];
+ SpecialPowers.setCharPref("intl.accept_languages", expectedLangs.toString());
+ extension.sendMessage(["expect-results", expectedLangs]);
+ yield extension.awaitMessage("done");
+ SpecialPowers.clearUserPref("intl.accept_languages");
+
+ win.close();
+
+ yield extension.unload();
+});
+
add_task(function* test_get_ui_language() {
function getResults() {
return {
getUILanguage: browser.i18n.getUILanguage(),
getMessage: browser.i18n.getMessage("@@ui_locale"),
};
}