diff --git a/toolkit/components/extensions/ExtensionContent.jsm b/toolkit/components/extensions/ExtensionContent.jsm index fd83aa29c61..b64b90d4a4c 100644 --- a/toolkit/components/extensions/ExtensionContent.jsm +++ b/toolkit/components/extensions/ExtensionContent.jsm @@ -46,6 +46,7 @@ var { Messenger, injectAPI, flushJarCache, + detectLanguage, } = ExtensionUtils; function isWhenBeforeOrSame(when1, when2) { @@ -122,6 +123,11 @@ var api = context => { getUILanguage: function() { return context.extension.localeData.uiLocale; }, + + detectLanguage: function(text, callback) { + let result = detectLanguage(text); + return context.wrapPromise(result, callback); + }, }, }; }; diff --git a/toolkit/components/extensions/ExtensionUtils.jsm b/toolkit/components/extensions/ExtensionUtils.jsm index 2a508fa3c24..eeaf0b5d0c6 100644 --- a/toolkit/components/extensions/ExtensionUtils.jsm +++ b/toolkit/components/extensions/ExtensionUtils.jsm @@ -16,7 +16,8 @@ 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"); @@ -433,7 +434,6 @@ LocaleData.prototype = { // locale code. return Locale.getLocale().replace(/-/g, "_"); }, - }; // This is a generic class for managing event listeners. Example usage: @@ -961,6 +961,18 @@ const PlatformInfo = Object.freeze({ })(), }); +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, @@ -980,4 +992,5 @@ this.ExtensionUtils = { extend, flushJarCache, instanceOf, + detectLanguage, }; diff --git a/toolkit/components/extensions/ext-i18n.js b/toolkit/components/extensions/ext-i18n.js index 61ce563acb2..6a502a89f37 100644 --- a/toolkit/components/extensions/ext-i18n.js +++ b/toolkit/components/extensions/ext-i18n.js @@ -1,5 +1,10 @@ "use strict"; +Cu.import("resource://gre/modules/ExtensionUtils.jsm"); +var { + detectLanguage, +} = ExtensionUtils; + extensions.registerSchemaAPI("i18n", null, (extension, context) => { return { i18n: { @@ -10,6 +15,10 @@ extensions.registerSchemaAPI("i18n", null, (extension, context) => { getUILanguage: function() { return extension.localeData.uiLocale; }, + + detectLanguage: function(text) { + return detectLanguage(text); + }, }, }; }); diff --git a/toolkit/components/extensions/schemas/i18n.json b/toolkit/components/extensions/schemas/i18n.json index d43d3cdbda8..fbfa5911bb2 100644 --- a/toolkit/components/extensions/schemas/i18n.json +++ b/toolkit/components/extensions/schemas/i18n.json @@ -78,7 +78,6 @@ }, { "name": "detectLanguage", - "unsupported": true, "type": "function", "description": "Detects the language of the provided text using CLD.", "async": "callback", diff --git a/toolkit/components/extensions/test/mochitest/test_ext_i18n.html b/toolkit/components/extensions/test/mochitest/test_ext_i18n.html index 844bdd0b361..50f325466c9 100644 --- a/toolkit/components/extensions/test/mochitest/test_ext_i18n.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_i18n.html @@ -242,6 +242,119 @@ add_task(function* test_get_ui_language() { 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(); +}); +