Bug 1246754 - Complete the implementation of chrome.i18n.detectLanguage, r?kmag
MozReview-Commit-ID: 7cvJj0QP5XO
--- a/toolkit/components/extensions/ExtensionContent.jsm
+++ b/toolkit/components/extensions/ExtensionContent.jsm
@@ -41,16 +41,17 @@ Cu.import("resource://gre/modules/Extens
var {
runSafeSyncWithoutClone,
BaseContext,
LocaleData,
MessageBroker,
Messenger,
injectAPI,
flushJarCache,
+ detectLanguage,
} = ExtensionUtils;
function isWhenBeforeOrSame(when1, when2) {
let table = {"document_start": 0,
"document_end": 1,
"document_idle": 2};
return table[when1] <= table[when2];
}
@@ -117,16 +118,21 @@ var api = context => {
i18n: {
getMessage: function(messageName, substitutions) {
return context.extension.localizeMessage(messageName, substitutions);
},
getUILanguage: function() {
return context.extension.localeData.uiLocale;
},
+
+ detectLanguage: function(text, callback) {
+ let result = detectLanguage(text);
+ return context.wrapPromise(result, callback);
+ },
},
};
};
// Represents a content script.
function Script(options, deferred = PromiseUtils.defer()) {
this.options = options;
this.run_at = this.options.run_at;
--- a/toolkit/components/extensions/ExtensionUtils.jsm
+++ b/toolkit/components/extensions/ExtensionUtils.jsm
@@ -11,17 +11,18 @@ const Cc = Components.classes;
const Cu = Components.utils;
const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
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");
function filterStack(error) {
return String(error.stack).replace(/(^.*(Task\.jsm|Promise-backend\.js).*\n)+/gm, "<Promise Chain>\n");
}
// Run a function and report exceptions.
@@ -428,17 +429,16 @@ LocaleData.prototype = {
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:
//
// new EventManager(context, "api.subAPI", fire => {
// let listener = (...) => {
// // Fire any listeners registered with addListener.
// fire(arg1, arg2);
@@ -956,16 +956,28 @@ const PlatformInfo = Object.freeze({
arch = "x86-32";
} else if (arch == "x86_64") {
arch = "x86-64";
}
return arch;
})(),
});
+function detectLanguage(text) {
+ return LanguageDetector.detectLanguage(text).then(result => ({
+ isReliable: result.confident,
+ languages: result.languages.map(lang => {
+ return {
+ language: lang.languageCode,
+ percentage: lang.percent,
+ };
+ }),
+ }));
+}
+
this.ExtensionUtils = {
runSafeWithoutClone,
runSafeSyncWithoutClone,
runSafe,
runSafeSync,
BaseContext,
DefaultWeakMap,
EventManager,
@@ -975,9 +987,10 @@ this.ExtensionUtils = {
injectAPI,
MessageBroker,
Messenger,
PlatformInfo,
SpreadArgs,
extend,
flushJarCache,
instanceOf,
+ detectLanguage,
};
--- a/toolkit/components/extensions/ext-i18n.js
+++ b/toolkit/components/extensions/ext-i18n.js
@@ -1,15 +1,24 @@
"use strict";
+Cu.import("resource://gre/modules/ExtensionUtils.jsm");
+var {
+ detectLanguage,
+} = ExtensionUtils;
+
extensions.registerSchemaAPI("i18n", null, (extension, context) => {
return {
i18n: {
getMessage: function(messageName, substitutions) {
return extension.localizeMessage(messageName, substitutions);
},
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
@@ -73,17 +73,16 @@
"parameters": [],
"returns": {
"type": "string",
"description": "The browser UI language code such as en-US or fr-FR."
}
},
{
"name": "detectLanguage",
- "unsupported": true,
"type": "function",
"description": "Detects the language of the provided text using CLD.",
"async": "callback",
"parameters": [
{
"type": "string",
"name": "text",
"description": "User input string to be translated."
--- a/toolkit/components/extensions/test/mochitest/test_ext_i18n.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_i18n.html
@@ -237,12 +237,125 @@ add_task(function* test_get_ui_language(
extension.sendMessage(["expect-results", "he"]);
yield extension.awaitMessage("done");
win.close();
yield extension.unload();
});
+
+add_task(function* test_detect_language() {
+ const af_string = " aam skukuza die naam beteken hy wat skoonvee of hy wat alles onderstebo keer wysig " +
+ "bosveldkampe boskampe is kleiner afgeleë ruskampe wat oor min fasiliteite beskik daar is geen restaurante " +
+ "of winkels nie en slegs oornagbesoekers word toegelaat bateleur";
+ // String with intermixed French/English text
+ const fr_en_string = "France is the largest country in Western Europe and the third-largest in Europe as a whole. " +
+ "A accès aux chiens et aux frontaux qui lui ont été il peut consulter et modifier ses collections et exporter " +
+ "Cet article concerne le pays européen aujourd’hui appelé République française. Pour d’autres usages du nom France, " +
+ "Pour une aide rapide et effective, veuiller trouver votre aide dans le menu ci-dessus." +
+ "Motoring events began soon after the construction of the first successful gasoline-fueled automobiles. The quick brown fox jumped over the lazy dog";
+
+ function backgroundScript() {
+ function checkResult(source, result, expected) {
+ browser.test.assertEq(expected.isReliable, result.isReliable, "result.confident is true");
+ browser.test.assertEq(
+ expected.languages.length,
+ result.languages.length,
+ `result.languages contains the expected number of languages in ${source}`);
+ expected.languages.forEach((lang, index) => {
+ browser.test.assertEq(
+ lang.percentage,
+ result.languages[index].percentage,
+ `element ${index} of result.languages array has the expected percentage in ${source}`);
+ browser.test.assertEq(
+ lang.language,
+ result.languages[index].language,
+ `element ${index} of result.languages array has the 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([
+ browser.i18n.detectLanguage(msg),
+ new Promise(
+ resolve => browser.tabs.sendMessage(tabId, msg, resolve)),
+ ]).then(([backgroundResults, contentResults]) => {
+ checkResult("background", backgroundResults, expected);
+ checkResult("contentScript", contentResults, expected);
+
+ browser.test.sendMessage("done");
+ });
+ });
+ }
+
+ function content() {
+ browser.runtime.onMessage.addListener((msg, sender, respond) => {
+ browser.i18n.detectLanguage(msg, 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: `(${backgroundScript})()`,
+
+ files: {
+ "content_script.js": `(${content})()`,
+ },
+ });
+
+ let win = window.open("file_sample.html");
+
+ yield extension.startup();
+ yield extension.awaitMessage("ready");
+
+ let expected = {
+ isReliable: true,
+ languages: [
+ {
+ language: "fr",
+ percentage: 67,
+ },
+ {
+ language: "en",
+ percentage: 32,
+ },
+ ],
+ };
+ extension.sendMessage([fr_en_string, expected]);
+ yield extension.awaitMessage("done");
+
+ expected = {
+ isReliable: true,
+ languages: [
+ {
+ language: "af",
+ percentage: 99,
+ },
+ ],
+ };
+ extension.sendMessage([af_string, expected]);
+ yield extension.awaitMessage("done");
+
+ win.close();
+
+ yield extension.unload();
+});
+
</script>
</body>
</html>