зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1759782 - Remove Firefox Translations addon from Nightly r=mixedpuppy
Differential Revision: https://phabricator.services.mozilla.com/D141820
This commit is contained in:
Родитель
9ddc42292b
Коммит
b9b8c7c39b
|
@ -2023,45 +2023,6 @@ BrowserGlue.prototype = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// Set up a listener to enable/disable the translation extension
|
|
||||||
// based on its preference.
|
|
||||||
_monitorTranslationsPref() {
|
|
||||||
const PREF = "extensions.translations.disabled";
|
|
||||||
const ID = "firefox-translations@mozilla.org";
|
|
||||||
const oldID = "firefox-infobar-ui-bergamot-browser-extension@browser.mt";
|
|
||||||
|
|
||||||
// First, try to uninstall the old extension, if exists.
|
|
||||||
(async () => {
|
|
||||||
let addon = await AddonManager.getAddonByID(oldID);
|
|
||||||
if (addon) {
|
|
||||||
addon.uninstall().catch(Cu.reportError);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
const _checkTranslationsPref = async () => {
|
|
||||||
let addon = await AddonManager.getAddonByID(ID);
|
|
||||||
let disabled = Services.prefs.getBoolPref(PREF, false);
|
|
||||||
if (!addon && disabled) {
|
|
||||||
// not installed, bail out early.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!disabled) {
|
|
||||||
// first time install of addon and install on firefox update
|
|
||||||
addon =
|
|
||||||
(await AddonManager.maybeInstallBuiltinAddon(
|
|
||||||
ID,
|
|
||||||
"0.4.3",
|
|
||||||
"resource://builtin-addons/translations/"
|
|
||||||
)) || addon;
|
|
||||||
await addon.enable();
|
|
||||||
} else if (addon) {
|
|
||||||
await addon.disable();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Services.prefs.addObserver(PREF, _checkTranslationsPref);
|
|
||||||
_checkTranslationsPref();
|
|
||||||
},
|
|
||||||
|
|
||||||
async _setupSearchDetection() {
|
async _setupSearchDetection() {
|
||||||
// There is no pref for this add-on because it shouldn't be disabled.
|
// There is no pref for this add-on because it shouldn't be disabled.
|
||||||
const ID = "addons-search-detection@mozilla.com";
|
const ID = "addons-search-detection@mozilla.com";
|
||||||
|
@ -2332,9 +2293,6 @@ BrowserGlue.prototype = {
|
||||||
this._monitorIonStudies();
|
this._monitorIonStudies();
|
||||||
this._setupSearchDetection();
|
this._setupSearchDetection();
|
||||||
|
|
||||||
if (AppConstants.NIGHTLY_BUILD) {
|
|
||||||
this._monitorTranslationsPref();
|
|
||||||
}
|
|
||||||
this._monitorGPCPref();
|
this._monitorGPCPref();
|
||||||
this._monitorPrivacySegmentationPref();
|
this._monitorPrivacySegmentationPref();
|
||||||
},
|
},
|
||||||
|
|
|
@ -13,8 +13,3 @@ DIRS += [
|
||||||
"pictureinpicture",
|
"pictureinpicture",
|
||||||
"search-detection",
|
"search-detection",
|
||||||
]
|
]
|
||||||
|
|
||||||
if CONFIG["NIGHTLY_BUILD"]:
|
|
||||||
DIRS += [
|
|
||||||
"translations",
|
|
||||||
]
|
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
This folder contains the source files for Firefox Translations, which utilizes
|
|
||||||
the proceedings from Project Bergamot [1].
|
|
||||||
|
|
||||||
The feature is developed as a webextension [2] which utilizes a neural machine
|
|
||||||
translation decoder [3] ported to WebAssembly in order to process the
|
|
||||||
in page translation. The translation models [4] are downloaded on-demand by the
|
|
||||||
extension, so there's no need to bundle them too.
|
|
||||||
|
|
||||||
The folder `extension_src` contains the entire code of the extension's xpi and
|
|
||||||
the wasm module (which lies inside the folder wasm), and is automatically
|
|
||||||
generated by the `import_xpi.py` script, which is responsibile for cloning
|
|
||||||
the extension repo [2], build it, and generate the `jar.mn` package containing
|
|
||||||
all the pertinent files necessary for running it.
|
|
||||||
|
|
||||||
For any questions, reach out to anatal@mozilla.com.
|
|
||||||
|
|
||||||
[1] https://browser.mt/
|
|
||||||
[2] https://github.com/mozilla-extensions/bergamot-browser-extension
|
|
||||||
[3] https://github.com/mozilla/bergamot-translator/
|
|
||||||
[4] https://github.com/mozilla-applied-ml/bergamot-models
|
|
|
@ -1,50 +0,0 @@
|
||||||
{
|
|
||||||
"extensionName": {
|
|
||||||
"message": "Firefox Translations",
|
|
||||||
"description": "Name of the extension"
|
|
||||||
},
|
|
||||||
"extensionDescription": {
|
|
||||||
"message": "Neuronový strojový překlad v prohlížeči",
|
|
||||||
"description": "Description of the extension."
|
|
||||||
},
|
|
||||||
"currentlyDownloadingTranslationEngine": {
|
|
||||||
"message": "Stahování překladače",
|
|
||||||
"description": "Informs the user that the translations feature is currently downloading the translation engine."
|
|
||||||
},
|
|
||||||
"currentlyDownloadingLanguageModel": {
|
|
||||||
"message": "Stahování jazykového modelu",
|
|
||||||
"description": "Informs the user that the translations feature is currently downloading the language model files."
|
|
||||||
},
|
|
||||||
"detailedDownloadProgress": {
|
|
||||||
"message": "Staženo $PERCENTAGE$ % z $MB$ MB",
|
|
||||||
"description": "Informs the user how the language model file downloading is progressing.",
|
|
||||||
"placeholders": {
|
|
||||||
"percentage": {
|
|
||||||
"content": "$1",
|
|
||||||
"example": "50"
|
|
||||||
},
|
|
||||||
"mb": {
|
|
||||||
"content": "$2",
|
|
||||||
"example": "22,2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"currentlyLoadingLanguageModel": {
|
|
||||||
"message": "Načítání jazykového modelu",
|
|
||||||
"description": "Informs the user that the translations feature is currently loading the language model files into memory."
|
|
||||||
},
|
|
||||||
"loadedLanguageModel": {
|
|
||||||
"message": "Jazykový model načten",
|
|
||||||
"description": "Informs the user that the translations feature has loaded the language model files into memory."
|
|
||||||
},
|
|
||||||
"partsLeftToTranslate": {
|
|
||||||
"message": "Zbývající části k přeložení: $NUM$",
|
|
||||||
"description": "Informs the user that the translations feature has $NUM$ parts left to translate.",
|
|
||||||
"placeholders": {
|
|
||||||
"num": {
|
|
||||||
"content": "$1",
|
|
||||||
"example": "3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
{
|
|
||||||
"extensionName": {
|
|
||||||
"message": "Firefox Translations",
|
|
||||||
"description": "Name of the extension"
|
|
||||||
},
|
|
||||||
"extensionDescription": {
|
|
||||||
"message": "Neurale Maschine Übersetzung für den Browser.",
|
|
||||||
"description": "Description of the extension."
|
|
||||||
},
|
|
||||||
"currentlyDownloadingTranslationEngine": {
|
|
||||||
"message": "Derzeit Download Übersetzungs-Engine",
|
|
||||||
"description": "Informs the user that the translations feature is currently downloading the translation engine."
|
|
||||||
},
|
|
||||||
"currentlyDownloadingLanguageModel": {
|
|
||||||
"message": "Derzeit das Sprachmodell herunterladen",
|
|
||||||
"description": "Informs the user that the translations feature is currently downloading the language model files."
|
|
||||||
},
|
|
||||||
"detailedDownloadProgress": {
|
|
||||||
"message": "$PERCENTAGE$% von $MB$ mb heruntergeladen",
|
|
||||||
"description": "Informs the user how the language model file downloading is progressing.",
|
|
||||||
"placeholders": {
|
|
||||||
"percentage": {
|
|
||||||
"content": "$1",
|
|
||||||
"example": "50"
|
|
||||||
},
|
|
||||||
"mb": {
|
|
||||||
"content": "$2",
|
|
||||||
"example": "22.2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"currentlyLoadingLanguageModel": {
|
|
||||||
"message": "Derzeit ladende Sprachmodell",
|
|
||||||
"description": "Informs the user that the translations feature is currently loading the language model files into memory."
|
|
||||||
},
|
|
||||||
"loadedLanguageModel": {
|
|
||||||
"message": "Sprachmodell geladen",
|
|
||||||
"description": "Informs the user that the translations feature has loaded the language model files into memory."
|
|
||||||
},
|
|
||||||
"partsLeftToTranslate": {
|
|
||||||
"message": "Noch zu übersetzende Teile: $NUM$",
|
|
||||||
"description": "Informs the user that the translations feature has $NUM$ parts left to translate.",
|
|
||||||
"placeholders": {
|
|
||||||
"num": {
|
|
||||||
"content": "$1",
|
|
||||||
"example": "3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
{
|
|
||||||
"extensionName": {
|
|
||||||
"message": "Firefox Translations",
|
|
||||||
"description": "Name of the extension"
|
|
||||||
},
|
|
||||||
"extensionDescription": {
|
|
||||||
"message": "Neural Machine Translation for the browser.",
|
|
||||||
"description": "Description of the extension."
|
|
||||||
},
|
|
||||||
"currentlyDownloadingTranslationEngine": {
|
|
||||||
"message": "Currently downloading translation engine",
|
|
||||||
"description": "Informs the user that the translations feature is currently downloading the translation engine."
|
|
||||||
},
|
|
||||||
"currentlyDownloadingLanguageModel": {
|
|
||||||
"message": "Currently downloading language model",
|
|
||||||
"description": "Informs the user that the translations feature is currently downloading the language model files."
|
|
||||||
},
|
|
||||||
"detailedDownloadProgress": {
|
|
||||||
"message": "$PERCENTAGE$% out of $MB$ mb downloaded",
|
|
||||||
"description": "Informs the user how the language model file downloading is progressing.",
|
|
||||||
"placeholders": {
|
|
||||||
"percentage": {
|
|
||||||
"content": "$1",
|
|
||||||
"example": "50"
|
|
||||||
},
|
|
||||||
"mb": {
|
|
||||||
"content": "$2",
|
|
||||||
"example": "22.2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"currentlyLoadingLanguageModel": {
|
|
||||||
"message": "Currently loading language model",
|
|
||||||
"description": "Informs the user that the translations feature is currently loading the language model files into memory."
|
|
||||||
},
|
|
||||||
"loadedLanguageModel": {
|
|
||||||
"message": "Language model loaded",
|
|
||||||
"description": "Informs the user that the translations feature has loaded the language model files into memory."
|
|
||||||
},
|
|
||||||
"partsLeftToTranslate": {
|
|
||||||
"message": "Parts left to translate: $NUM$",
|
|
||||||
"description": "Informs the user that the translations feature has $NUM$ parts left to translate.",
|
|
||||||
"placeholders": {
|
|
||||||
"num": {
|
|
||||||
"content": "$1",
|
|
||||||
"example": "3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
{
|
|
||||||
"extensionName": {
|
|
||||||
"message": "Firefox Translations",
|
|
||||||
"description": "Name of the extension"
|
|
||||||
},
|
|
||||||
"extensionDescription": {
|
|
||||||
"message": "Traducción de la Máquina Neural para el navegador.",
|
|
||||||
"description": "Description of the extension."
|
|
||||||
},
|
|
||||||
"currentlyDownloadingTranslationEngine": {
|
|
||||||
"message": "Motor de traducción de descarga",
|
|
||||||
"description": "Informs the user that the translations feature is currently downloading the translation engine."
|
|
||||||
},
|
|
||||||
"currentlyDownloadingLanguageModel": {
|
|
||||||
"message": "Actualmente descargando el modelo de idioma",
|
|
||||||
"description": "Informs the user that the translations feature is currently downloading the language model files."
|
|
||||||
},
|
|
||||||
"detailedDownloadProgress": {
|
|
||||||
"message": "$PERCENTAGE$% de $MB$ mb descargado",
|
|
||||||
"description": "Informs the user how the language model file downloading is progressing.",
|
|
||||||
"placeholders": {
|
|
||||||
"percentage": {
|
|
||||||
"content": "$1",
|
|
||||||
"example": "50"
|
|
||||||
},
|
|
||||||
"mb": {
|
|
||||||
"content": "$2",
|
|
||||||
"example": "22.2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"currentlyLoadingLanguageModel": {
|
|
||||||
"message": "Actualmente cargar el modelo de idioma",
|
|
||||||
"description": "Informs the user that the translations feature is currently loading the language model files into memory."
|
|
||||||
},
|
|
||||||
"loadedLanguageModel": {
|
|
||||||
"message": "Modelo de lenguaje cargado",
|
|
||||||
"description": "Informs the user that the translations feature has loaded the language model files into memory."
|
|
||||||
},
|
|
||||||
"partsLeftToTranslate": {
|
|
||||||
"message": "Partes que quedan para traducir: $NUM$",
|
|
||||||
"description": "Informs the user that the translations feature has $NUM$ parts left to translate.",
|
|
||||||
"placeholders": {
|
|
||||||
"num": {
|
|
||||||
"content": "$1",
|
|
||||||
"example": "3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
{
|
|
||||||
"extensionName": {
|
|
||||||
"message": "Firefox Translations",
|
|
||||||
"description": "Name of the extension"
|
|
||||||
},
|
|
||||||
"extensionDescription": {
|
|
||||||
"message": "Neural Machine Translation brauseri jaoks.",
|
|
||||||
"description": "Description of the extension."
|
|
||||||
},
|
|
||||||
"currentlyDownloadingTranslationEngine": {
|
|
||||||
"message": "Praegu laaditakse tõlkemootor",
|
|
||||||
"description": "Informs the user that the translations feature is currently downloading the translation engine."
|
|
||||||
},
|
|
||||||
"currentlyDownloadingLanguageModel": {
|
|
||||||
"message": "Praegu allalaadimine keelemudel",
|
|
||||||
"description": "Informs the user that the translations feature is currently downloading the language model files."
|
|
||||||
},
|
|
||||||
"detailedDownloadProgress": {
|
|
||||||
"message": "$PERCENTAGE$% $MB$ mb allalaaditud",
|
|
||||||
"description": "Informs the user how the language model file downloading is progressing.",
|
|
||||||
"placeholders": {
|
|
||||||
"percentage": {
|
|
||||||
"content": "$1",
|
|
||||||
"example": "50"
|
|
||||||
},
|
|
||||||
"mb": {
|
|
||||||
"content": "$2",
|
|
||||||
"example": "22.2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"currentlyLoadingLanguageModel": {
|
|
||||||
"message": "Praegu laadiv keelemudel",
|
|
||||||
"description": "Informs the user that the translations feature is currently loading the language model files into memory."
|
|
||||||
},
|
|
||||||
"loadedLanguageModel": {
|
|
||||||
"message": "Keelemudel koormatud",
|
|
||||||
"description": "Informs the user that the translations feature has loaded the language model files into memory."
|
|
||||||
},
|
|
||||||
"partsLeftToTranslate": {
|
|
||||||
"message": "Osad jätta tõlkida: $NUM$",
|
|
||||||
"description": "Informs the user that the translations feature has $NUM$ parts left to translate.",
|
|
||||||
"placeholders": {
|
|
||||||
"num": {
|
|
||||||
"content": "$1",
|
|
||||||
"example": "3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
{
|
|
||||||
"extensionName": {
|
|
||||||
"message": "Firefox Translations",
|
|
||||||
"description": "Name of the extension"
|
|
||||||
},
|
|
||||||
"extensionDescription": {
|
|
||||||
"message": "Нейронный машинный перевод для браузера.",
|
|
||||||
"description": "Description of the extension."
|
|
||||||
},
|
|
||||||
"currentlyDownloadingTranslationEngine": {
|
|
||||||
"message": "Скачивание движка перевода",
|
|
||||||
"description": "Informs the user that the translations feature is currently downloading the translation engine."
|
|
||||||
},
|
|
||||||
"currentlyDownloadingLanguageModel": {
|
|
||||||
"message": "Скачивание языковой модели",
|
|
||||||
"description": "Informs the user that the translations feature is currently downloading the language model files."
|
|
||||||
},
|
|
||||||
"detailedDownloadProgress": {
|
|
||||||
"message": "$PERCENTAGE$% из $MB$ МБ загружено",
|
|
||||||
"description": "Informs the user how the language model file downloading is progressing.",
|
|
||||||
"placeholders": {
|
|
||||||
"percentage": {
|
|
||||||
"content": "$1",
|
|
||||||
"example": "50"
|
|
||||||
},
|
|
||||||
"mb": {
|
|
||||||
"content": "$2",
|
|
||||||
"example": "22.2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"currentlyLoadingLanguageModel": {
|
|
||||||
"message": "Загрузка языковой модели",
|
|
||||||
"description": "Informs the user that the translations feature is currently loading the language model files into memory."
|
|
||||||
},
|
|
||||||
"loadedLanguageModel": {
|
|
||||||
"message": "Языковая модель загружена",
|
|
||||||
"description": "Informs the user that the translations feature has loaded the language model files into memory."
|
|
||||||
},
|
|
||||||
"partsLeftToTranslate": {
|
|
||||||
"message": "Осталось частей для перевода: $NUM$",
|
|
||||||
"description": "Informs the user that the translations feature has $NUM$ parts left to translate.",
|
|
||||||
"placeholders": {
|
|
||||||
"num": {
|
|
||||||
"content": "$1",
|
|
||||||
"example": "3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,44 +0,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/. */
|
|
||||||
|
|
||||||
/* global ExtensionAPI, ExtensionCommon, Services */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
this.extensionPreferences = class extends ExtensionAPI {
|
|
||||||
getAPI() {
|
|
||||||
const { Preferences } = ChromeUtils.import(
|
|
||||||
"resource://gre/modules/Preferences.jsm",
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
const { ExtensionUtils } = ChromeUtils.import(
|
|
||||||
"resource://gre/modules/ExtensionUtils.jsm",
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
const { ExtensionError } = ExtensionUtils;
|
|
||||||
const telemetryInactivityThresholdInSecondsOverridePrefName = `extensions.translations.telemetryInactivityThresholdInSecondsOverride`;
|
|
||||||
return {
|
|
||||||
experiments: {
|
|
||||||
extensionPreferences: {
|
|
||||||
async getTelemetryInactivityThresholdInSecondsOverridePref() {
|
|
||||||
try {
|
|
||||||
const value = Preferences.get(
|
|
||||||
telemetryInactivityThresholdInSecondsOverridePrefName,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
if (!value) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return parseFloat(value);
|
|
||||||
} catch (error) {
|
|
||||||
// Surface otherwise silent or obscurely reported errors
|
|
||||||
console.error(error.message, error.stack);
|
|
||||||
throw new ExtensionError(error.message);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,15 +0,0 @@
|
||||||
[
|
|
||||||
{
|
|
||||||
"namespace": "experiments.extensionPreferences",
|
|
||||||
"description": "Enables read-only access to specific Extensions-related about:config preferences",
|
|
||||||
"functions": [
|
|
||||||
{
|
|
||||||
"name": "getTelemetryInactivityThresholdInSecondsOverridePref",
|
|
||||||
"type": "function",
|
|
||||||
"description": "Get the `extensions.translations.telemetryInactivityThresholdInSecondsOverride` preference's value",
|
|
||||||
"parameters": [],
|
|
||||||
"async": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
|
@ -1,40 +0,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/. */
|
|
||||||
|
|
||||||
/* global ExtensionAPI */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
this.languageDetector = class extends ExtensionAPI {
|
|
||||||
getAPI() {
|
|
||||||
const { LanguageDetector } = ChromeUtils.import(
|
|
||||||
"resource:///modules/translation/LanguageDetector.jsm",
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
|
|
||||||
const { ExtensionUtils } = ChromeUtils.import(
|
|
||||||
"resource://gre/modules/ExtensionUtils.jsm",
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
const { ExtensionError } = ExtensionUtils;
|
|
||||||
|
|
||||||
return {
|
|
||||||
experiments: {
|
|
||||||
languageDetector: {
|
|
||||||
/* Detect language */
|
|
||||||
detectLanguage: async function detectLanguage(str) {
|
|
||||||
try {
|
|
||||||
// console.log("Called detectLanguage(str)", str);
|
|
||||||
return LanguageDetector.detectLanguage(str);
|
|
||||||
} catch (error) {
|
|
||||||
// Surface otherwise silent or obscurely reported errors
|
|
||||||
console.error(error.message, error.stack);
|
|
||||||
throw new ExtensionError(error.message);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,20 +0,0 @@
|
||||||
[
|
|
||||||
{
|
|
||||||
"namespace": "experiments.languageDetector",
|
|
||||||
"description": "Provides access to the language detector that resides in tree",
|
|
||||||
"functions": [
|
|
||||||
{
|
|
||||||
"name": "detectLanguage",
|
|
||||||
"type": "function",
|
|
||||||
"async": true,
|
|
||||||
"description": "Detect language",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "params",
|
|
||||||
"type": "any"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
|
@ -1,58 +0,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/. */
|
|
||||||
|
|
||||||
/* global ExtensionAPI, ExtensionCommon, Services */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
this.telemetryEnvironment = class extends ExtensionAPI {
|
|
||||||
getAPI(context) {
|
|
||||||
const { TelemetryController } = ChromeUtils.import(
|
|
||||||
"resource://gre/modules/TelemetryController.jsm",
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
const { TelemetryEnvironment } = ChromeUtils.import(
|
|
||||||
"resource://gre/modules/TelemetryEnvironment.jsm",
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* These attributes are already sent as part of the telemetry ping envelope
|
|
||||||
* @returns {{}}
|
|
||||||
*/
|
|
||||||
const collectTelemetryEnvironmentBasedAttributes = () => {
|
|
||||||
const environment = TelemetryEnvironment.currentEnvironment;
|
|
||||||
// console.debug("TelemetryEnvironment.currentEnvironment", environment);
|
|
||||||
|
|
||||||
return {
|
|
||||||
systemMemoryMb: environment.system.memoryMB,
|
|
||||||
systemCpuCount: environment.system.cpu.count,
|
|
||||||
systemCpuCores: environment.system.cpu.cores,
|
|
||||||
systemCpuVendor: environment.system.cpu.vendor,
|
|
||||||
systemCpuFamily: environment.system.cpu.family,
|
|
||||||
systemCpuModel: environment.system.cpu.model,
|
|
||||||
systemCpuStepping: environment.system.cpu.stepping,
|
|
||||||
systemCpuL2cacheKB: environment.system.cpu.l2cacheKB,
|
|
||||||
systemCpuL3cacheKB: environment.system.cpu.l3cacheKB,
|
|
||||||
systemCpuSpeedMhz: environment.system.cpu.speedMHz,
|
|
||||||
systemCpuExtensions: environment.system.cpu.extensions,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
experiments: {
|
|
||||||
telemetryEnvironment: {
|
|
||||||
async getTranslationRelevantFxTelemetryMetrics() {
|
|
||||||
await TelemetryController.promiseInitialized();
|
|
||||||
const telemetryEnvironmentBasedAttributes = collectTelemetryEnvironmentBasedAttributes();
|
|
||||||
// console.debug("telemetryEnvironmentBasedAttributes", telemetryEnvironmentBasedAttributes);
|
|
||||||
return {
|
|
||||||
...telemetryEnvironmentBasedAttributes,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,15 +0,0 @@
|
||||||
[
|
|
||||||
{
|
|
||||||
"namespace": "experiments.telemetryEnvironment",
|
|
||||||
"description": "Enables read-only access to the translation-relevant info from the current telemetry environment",
|
|
||||||
"functions": [
|
|
||||||
{
|
|
||||||
"name": "getTranslationRelevantFxTelemetryMetrics",
|
|
||||||
"type": "function",
|
|
||||||
"description": "Get the translation-relevant info from the current telemetry environment",
|
|
||||||
"parameters": [],
|
|
||||||
"async": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
|
@ -1,57 +0,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/. */
|
|
||||||
|
|
||||||
/* global ExtensionAPI, ExtensionCommon, Services */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
this.telemetryPreferences = class extends ExtensionAPI {
|
|
||||||
getAPI(context) {
|
|
||||||
const EventManager = ExtensionCommon.EventManager;
|
|
||||||
const uploadEnabledPrefName = `datareporting.healthreport.uploadEnabled`;
|
|
||||||
const cachedClientIDPrefName = `toolkit.telemetry.cachedClientID`;
|
|
||||||
|
|
||||||
return {
|
|
||||||
experiments: {
|
|
||||||
telemetryPreferences: {
|
|
||||||
onUploadEnabledPrefChange: new EventManager({
|
|
||||||
context,
|
|
||||||
name: "telemetryPreferences.onUploadEnabledPrefChange",
|
|
||||||
register: fire => {
|
|
||||||
const callback = () => {
|
|
||||||
fire.async().catch(() => {}); // ignore Message Manager disconnects
|
|
||||||
};
|
|
||||||
Services.prefs.addObserver(uploadEnabledPrefName, callback);
|
|
||||||
return () => {
|
|
||||||
Services.prefs.removeObserver(uploadEnabledPrefName, callback);
|
|
||||||
};
|
|
||||||
},
|
|
||||||
}).api(),
|
|
||||||
async getUploadEnabledPref() {
|
|
||||||
return Services.prefs.getBoolPref(uploadEnabledPrefName, undefined);
|
|
||||||
},
|
|
||||||
onCachedClientIDPrefChange: new EventManager({
|
|
||||||
context,
|
|
||||||
name: "telemetryPreferences.onCachedClientIDPrefChange",
|
|
||||||
register: fire => {
|
|
||||||
const callback = () => {
|
|
||||||
fire.async().catch(() => {}); // ignore Message Manager disconnects
|
|
||||||
};
|
|
||||||
Services.prefs.addObserver(cachedClientIDPrefName, callback);
|
|
||||||
return () => {
|
|
||||||
Services.prefs.removeObserver(cachedClientIDPrefName, callback);
|
|
||||||
};
|
|
||||||
},
|
|
||||||
}).api(),
|
|
||||||
async getCachedClientIDPref() {
|
|
||||||
return Services.prefs.getStringPref(
|
|
||||||
cachedClientIDPrefName,
|
|
||||||
undefined,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,34 +0,0 @@
|
||||||
[
|
|
||||||
{
|
|
||||||
"namespace": "experiments.telemetryPreferences",
|
|
||||||
"description": "Enables read-only access to specific Telemetry-related about:config preferences",
|
|
||||||
"events": [
|
|
||||||
{
|
|
||||||
"name": "onUploadEnabledPrefChange",
|
|
||||||
"type": "function",
|
|
||||||
"parameters": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "onCachedClientIDPrefChange",
|
|
||||||
"type": "function",
|
|
||||||
"parameters": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"functions": [
|
|
||||||
{
|
|
||||||
"name": "getUploadEnabledPref",
|
|
||||||
"type": "function",
|
|
||||||
"description": "Get the uploadEnabled preference's value",
|
|
||||||
"parameters": [],
|
|
||||||
"async": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "getCachedClientIDPref",
|
|
||||||
"type": "function",
|
|
||||||
"description": "Get the cachedClientID preference's value",
|
|
||||||
"parameters": [],
|
|
||||||
"async": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
|
@ -1,320 +0,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/. */
|
|
||||||
|
|
||||||
/* global TranslationBrowserChromeUiNotificationManager */
|
|
||||||
/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "(TranslationBrowserChromeUi)" }]*/
|
|
||||||
|
|
||||||
const { setTimeout, clearTimeout } = ChromeUtils.import(
|
|
||||||
"resource://gre/modules/Timer.jsm",
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
|
|
||||||
const TranslationInfoBarStates = {
|
|
||||||
STATE_OFFER: 0,
|
|
||||||
STATE_TRANSLATING: 1,
|
|
||||||
STATE_TRANSLATED: 2,
|
|
||||||
STATE_ERROR: 3,
|
|
||||||
STATE_UNAVAILABLE: 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
class TranslationBrowserChromeUi {
|
|
||||||
constructor(Services, browser, context, apiEventEmitter, tabId) {
|
|
||||||
this.Services = Services;
|
|
||||||
this.uiState = null;
|
|
||||||
this.browser = browser;
|
|
||||||
this.context = context;
|
|
||||||
this.translationInfoBarShown = false;
|
|
||||||
this.shouldShowTranslationProgressTimer = undefined;
|
|
||||||
this.importTranslationNotification();
|
|
||||||
|
|
||||||
// The manager instance is injected into the translation notification bar and handles events from therein
|
|
||||||
this.translationBrowserChromeUiNotificationManager = new TranslationBrowserChromeUiNotificationManager(
|
|
||||||
browser,
|
|
||||||
apiEventEmitter,
|
|
||||||
tabId,
|
|
||||||
TranslationInfoBarStates,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get notificationBox() {
|
|
||||||
return this.browser.ownerGlobal.gBrowser.getNotificationBox(this.browser);
|
|
||||||
}
|
|
||||||
|
|
||||||
importTranslationNotification() {
|
|
||||||
const chromeWin = this.browser.ownerGlobal;
|
|
||||||
|
|
||||||
// As a workaround to be able to load updates for the translation notification on extension reload
|
|
||||||
// we use the current unix timestamp as part of the element id.
|
|
||||||
// TODO: Restrict use of Date.now() as cachebuster to development mode only
|
|
||||||
chromeWin.now = Date.now();
|
|
||||||
|
|
||||||
try {
|
|
||||||
chromeWin.customElements.setElementCreationCallback(
|
|
||||||
`translation-notification-${chromeWin.now}`,
|
|
||||||
() => {
|
|
||||||
this.Services.scriptloader.loadSubScript(
|
|
||||||
this.context.extension.getURL(
|
|
||||||
"experiment-apis/translateUi/content/translation-notification.js",
|
|
||||||
) +
|
|
||||||
"?cachebuster=" +
|
|
||||||
chromeWin.now,
|
|
||||||
chromeWin,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
console.log(
|
|
||||||
"Error occurred when attempting to load the translation notification script, but we continue nevertheless",
|
|
||||||
e,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onUiStateUpdate(uiState) {
|
|
||||||
// Set all values before showing a new translation infobar.
|
|
||||||
this.translationBrowserChromeUiNotificationManager.uiState = uiState;
|
|
||||||
this.setInfobarState(uiState.infobarState);
|
|
||||||
this.updateTranslationProgress(uiState);
|
|
||||||
const showInfoBar = this.shouldShowInfoBar(this.browser.contentPrincipal);
|
|
||||||
if (showInfoBar) {
|
|
||||||
this.showTranslationInfoBarIfNotAlreadyShown();
|
|
||||||
} else {
|
|
||||||
this.hideTranslationInfoBarIfShown();
|
|
||||||
}
|
|
||||||
// Always show the url bar icon
|
|
||||||
this.showURLBarIcon(showInfoBar);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Syncs infobarState with the inner infobar state variable of the infobar
|
|
||||||
* @param val
|
|
||||||
*/
|
|
||||||
setInfobarState(val) {
|
|
||||||
const notif = this.notificationBox.getNotificationWithValue("translation");
|
|
||||||
if (notif) {
|
|
||||||
notif.state = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Informs the infobar element of the current translation progress
|
|
||||||
*/
|
|
||||||
updateTranslationProgress(uiState) {
|
|
||||||
// Don't bother updating translation progress if not currently translating
|
|
||||||
if (
|
|
||||||
this.translationBrowserChromeUiNotificationManager.uiState
|
|
||||||
.infobarState !== TranslationInfoBarStates.STATE_TRANSLATING
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const notif = this.notificationBox.getNotificationWithValue("translation");
|
|
||||||
if (notif) {
|
|
||||||
const {
|
|
||||||
modelDownloading,
|
|
||||||
translationDurationMs,
|
|
||||||
localizedTranslationProgressText,
|
|
||||||
} = uiState;
|
|
||||||
|
|
||||||
// Always cancel ongoing timers so that we start from a clean state
|
|
||||||
if (this.shouldShowTranslationProgressTimer) {
|
|
||||||
clearTimeout(this.shouldShowTranslationProgressTimer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only show progress if translation has been going on for at least 3 seconds
|
|
||||||
// or we are currently downloading a model
|
|
||||||
let shouldShowTranslationProgress;
|
|
||||||
const thresholdMsAfterWhichToShouldTranslationProgress = 3000;
|
|
||||||
if (
|
|
||||||
translationDurationMs >=
|
|
||||||
thresholdMsAfterWhichToShouldTranslationProgress ||
|
|
||||||
modelDownloading
|
|
||||||
) {
|
|
||||||
shouldShowTranslationProgress = true;
|
|
||||||
} else {
|
|
||||||
// Use a timer to show the translation progress after the threshold
|
|
||||||
this.shouldShowTranslationProgressTimer = setTimeout(() => {
|
|
||||||
notif.updateTranslationProgress(
|
|
||||||
true,
|
|
||||||
localizedTranslationProgressText,
|
|
||||||
);
|
|
||||||
clearTimeout(this.shouldShowTranslationProgressTimer);
|
|
||||||
}, thresholdMsAfterWhichToShouldTranslationProgress - translationDurationMs);
|
|
||||||
// Don't show until then
|
|
||||||
shouldShowTranslationProgress = false;
|
|
||||||
}
|
|
||||||
notif.updateTranslationProgress(
|
|
||||||
shouldShowTranslationProgress,
|
|
||||||
localizedTranslationProgressText,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldShowInfoBar(principal) {
|
|
||||||
if (
|
|
||||||
![
|
|
||||||
TranslationInfoBarStates.STATE_OFFER,
|
|
||||||
TranslationInfoBarStates.STATE_TRANSLATING,
|
|
||||||
TranslationInfoBarStates.STATE_TRANSLATED,
|
|
||||||
TranslationInfoBarStates.STATE_ERROR,
|
|
||||||
].includes(
|
|
||||||
this.translationBrowserChromeUiNotificationManager.uiState.infobarState,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't show the infobar if we have no language detection results yet
|
|
||||||
if (
|
|
||||||
!this.translationBrowserChromeUiNotificationManager.uiState
|
|
||||||
.detectedLanguageResults
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't show the infobar if we couldn't confidently detect the language
|
|
||||||
if (
|
|
||||||
!this.translationBrowserChromeUiNotificationManager.uiState
|
|
||||||
.detectedLanguageResults.confident
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we should never show the infobar for this language.
|
|
||||||
const neverForLangs = this.Services.prefs.getCharPref(
|
|
||||||
"browser.translation.neverForLanguages",
|
|
||||||
);
|
|
||||||
if (
|
|
||||||
neverForLangs
|
|
||||||
.split(",")
|
|
||||||
.includes(
|
|
||||||
this.translationBrowserChromeUiNotificationManager.uiState
|
|
||||||
.detectedLanguageResults.language,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
// TranslationTelemetry.recordAutoRejectedTranslationOffer();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// or if we should never show the infobar for this domain.
|
|
||||||
const perms = this.Services.perms;
|
|
||||||
if (
|
|
||||||
perms.testExactPermissionFromPrincipal(principal, "translate") ===
|
|
||||||
perms.DENY_ACTION
|
|
||||||
) {
|
|
||||||
// TranslationTelemetry.recordAutoRejectedTranslationOffer();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
hideURLBarIcon() {
|
|
||||||
const chromeWin = this.browser.ownerGlobal;
|
|
||||||
const PopupNotifications = chromeWin.PopupNotifications;
|
|
||||||
const removeId = this.translationBrowserChromeUiNotificationManager.uiState
|
|
||||||
.originalShown
|
|
||||||
? "translated"
|
|
||||||
: "translate";
|
|
||||||
const notification = PopupNotifications.getNotification(
|
|
||||||
removeId,
|
|
||||||
this.browser,
|
|
||||||
);
|
|
||||||
if (notification) {
|
|
||||||
PopupNotifications.remove(notification);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
showURLBarIcon(showInfoBar) {
|
|
||||||
const chromeWin = this.browser.ownerGlobal;
|
|
||||||
const PopupNotifications = chromeWin.PopupNotifications;
|
|
||||||
const inactive =
|
|
||||||
!showInfoBar ||
|
|
||||||
this.translationBrowserChromeUiNotificationManager.uiState.originalShown;
|
|
||||||
|
|
||||||
// Remove existing url bar icon
|
|
||||||
["translated", "translate"].forEach(id => {
|
|
||||||
const notification = PopupNotifications.getNotification(id, this.browser);
|
|
||||||
if (notification) {
|
|
||||||
PopupNotifications.remove(notification);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const onClickCallback = (topic /* , aNewBrowser */) => {
|
|
||||||
if (topic === "swapping") {
|
|
||||||
const infoBarVisible = this.notificationBox.getNotificationWithValue(
|
|
||||||
"translation",
|
|
||||||
);
|
|
||||||
if (infoBarVisible) {
|
|
||||||
this.showTranslationInfoBar();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (topic !== "showing") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const translationNotification = this.notificationBox.getNotificationWithValue(
|
|
||||||
"translation",
|
|
||||||
);
|
|
||||||
if (translationNotification) {
|
|
||||||
translationNotification.close();
|
|
||||||
} else {
|
|
||||||
this.showTranslationInfoBar();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const addId = inactive ? "translate" : "translated";
|
|
||||||
|
|
||||||
PopupNotifications.show(
|
|
||||||
this.browser,
|
|
||||||
addId,
|
|
||||||
null,
|
|
||||||
addId + "-notification-icon",
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
{ dismissed: true, eventCallback: onClickCallback },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
showTranslationInfoBarIfNotAlreadyShown() {
|
|
||||||
const translationNotification = this.notificationBox.getNotificationWithValue(
|
|
||||||
"translation",
|
|
||||||
);
|
|
||||||
if (!translationNotification && !this.translationInfoBarShown) {
|
|
||||||
this.showTranslationInfoBar();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hideTranslationInfoBarIfShown() {
|
|
||||||
const translationNotification = this.notificationBox.getNotificationWithValue(
|
|
||||||
"translation",
|
|
||||||
);
|
|
||||||
if (translationNotification) {
|
|
||||||
translationNotification.close();
|
|
||||||
}
|
|
||||||
this.translationInfoBarShown = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
showTranslationInfoBar() {
|
|
||||||
console.debug("showTranslationInfoBar");
|
|
||||||
this.translationInfoBarShown = true;
|
|
||||||
const notificationBox = this.notificationBox;
|
|
||||||
const chromeWin = this.browser.ownerGlobal;
|
|
||||||
const notif = notificationBox.appendNotification(
|
|
||||||
"translation",
|
|
||||||
{
|
|
||||||
label: "",
|
|
||||||
priority: notificationBox.PRIORITY_INFO_HIGH,
|
|
||||||
is: `translation-notification-${chromeWin.now}`,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
notif.init(this.translationBrowserChromeUiNotificationManager);
|
|
||||||
this.translationBrowserChromeUiNotificationManager.infobarDisplayed(
|
|
||||||
notif._getSourceLang(),
|
|
||||||
notif._getTargetLang(),
|
|
||||||
);
|
|
||||||
return notif;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,80 +0,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/. */
|
|
||||||
|
|
||||||
/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "(TranslationBrowserChromeUiNotificationManager)" }]*/
|
|
||||||
|
|
||||||
class TranslationBrowserChromeUiNotificationManager {
|
|
||||||
constructor(browser, apiEventEmitter, tabId, TranslationInfoBarStates) {
|
|
||||||
this.uiState = null;
|
|
||||||
this.TranslationInfoBarStates = TranslationInfoBarStates;
|
|
||||||
this.apiEventEmitter = apiEventEmitter;
|
|
||||||
this.tabId = tabId;
|
|
||||||
this.browser = browser;
|
|
||||||
}
|
|
||||||
|
|
||||||
infobarDisplayed(from, to) {
|
|
||||||
console.log("infobarDisplayed", { from, to });
|
|
||||||
this.apiEventEmitter.emit("onInfoBarDisplayed", this.tabId, from, to);
|
|
||||||
}
|
|
||||||
|
|
||||||
toLanguageChanged(from, newTo) {
|
|
||||||
console.log("toLanguageChanged", { from, newTo });
|
|
||||||
this.apiEventEmitter.emit("onSelectTranslateTo", this.tabId, from, newTo);
|
|
||||||
}
|
|
||||||
|
|
||||||
fromLanguageChanged(newFrom, to) {
|
|
||||||
console.log("fromLanguageChanged", { newFrom, to });
|
|
||||||
this.apiEventEmitter.emit("onSelectTranslateFrom", this.tabId, newFrom, to);
|
|
||||||
}
|
|
||||||
|
|
||||||
infobarClosed(from, to) {
|
|
||||||
console.log("infobarClosed", { from, to });
|
|
||||||
this.apiEventEmitter.emit("onInfoBarClosed", this.tabId, from, to);
|
|
||||||
}
|
|
||||||
|
|
||||||
neverForLanguage(from, to) {
|
|
||||||
console.log("neverForLanguage", { from, to });
|
|
||||||
this.apiEventEmitter.emit(
|
|
||||||
"onNeverTranslateSelectedLanguage",
|
|
||||||
this.tabId,
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
neverForSite(from, to) {
|
|
||||||
console.log("neverForSite", { from, to });
|
|
||||||
this.apiEventEmitter.emit("onNeverTranslateThisSite", this.tabId, from, to);
|
|
||||||
}
|
|
||||||
|
|
||||||
showOriginalContent(from, to) {
|
|
||||||
console.log("showOriginalContent", { from, to });
|
|
||||||
this.apiEventEmitter.emit(
|
|
||||||
"onShowOriginalButtonPressed",
|
|
||||||
this.tabId,
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
showTranslatedContent(from, to) {
|
|
||||||
console.log("showTranslatedContent", { from, to });
|
|
||||||
this.apiEventEmitter.emit(
|
|
||||||
"onShowTranslatedButtonPressed",
|
|
||||||
this.tabId,
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
translate(from, to) {
|
|
||||||
console.log("translate", { from, to });
|
|
||||||
this.apiEventEmitter.emit("onTranslateButtonPressed", this.tabId, from, to);
|
|
||||||
}
|
|
||||||
|
|
||||||
notNow(from, to) {
|
|
||||||
console.log("notNow", { from, to });
|
|
||||||
this.apiEventEmitter.emit("onNotNowButtonPressed", this.tabId, from, to);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,177 +0,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/. */
|
|
||||||
|
|
||||||
/* global ExtensionAPI */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
this.translateUi = class extends ExtensionAPI {
|
|
||||||
getAPI(context) {
|
|
||||||
const { Services } = ChromeUtils.import(
|
|
||||||
"resource://gre/modules/Services.jsm",
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
|
|
||||||
const now = Date.now();
|
|
||||||
|
|
||||||
/* global TranslationBrowserChromeUiManager */
|
|
||||||
Services.scriptloader.loadSubScript(
|
|
||||||
context.extension.getURL(
|
|
||||||
"experiment-apis/translateUi/TranslationBrowserChromeUiManager.js",
|
|
||||||
) +
|
|
||||||
"?cachebuster=" +
|
|
||||||
now,
|
|
||||||
);
|
|
||||||
/* global TranslationBrowserChromeUi */
|
|
||||||
Services.scriptloader.loadSubScript(
|
|
||||||
context.extension.getURL(
|
|
||||||
"experiment-apis/translateUi/TranslationBrowserChromeUi.js",
|
|
||||||
) +
|
|
||||||
"?cachebuster=" +
|
|
||||||
now,
|
|
||||||
);
|
|
||||||
|
|
||||||
const { ExtensionCommon } = ChromeUtils.import(
|
|
||||||
"resource://gre/modules/ExtensionCommon.jsm",
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
|
|
||||||
const { EventManager, EventEmitter } = ExtensionCommon;
|
|
||||||
|
|
||||||
const apiEventEmitter = new EventEmitter();
|
|
||||||
|
|
||||||
const { ExtensionUtils } = ChromeUtils.import(
|
|
||||||
"resource://gre/modules/ExtensionUtils.jsm",
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
const { ExtensionError } = ExtensionUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Boilerplate-reducing factory method translating between
|
|
||||||
* apiEventEmitter.emit("translateUi.onFoo", ...args)
|
|
||||||
* and the actual web extension event being emitted
|
|
||||||
*
|
|
||||||
* @param {string} eventRef the event reference, eg "onFoo"
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
const eventManagerFactory = eventRef => {
|
|
||||||
const eventId = `translateUi.${eventRef}`;
|
|
||||||
return new EventManager({
|
|
||||||
context,
|
|
||||||
name: eventId,
|
|
||||||
register: fire => {
|
|
||||||
const listener = (event, ...args) => fire.async(...args);
|
|
||||||
apiEventEmitter.on(eventRef, listener);
|
|
||||||
return () => {
|
|
||||||
apiEventEmitter.off(eventRef, listener);
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const { tabManager } = context.extension;
|
|
||||||
const translationBrowserChromeUiInstancesByTabId = new Map();
|
|
||||||
const getTranslationBrowserChromeUiInstanceByTabId = tabId => {
|
|
||||||
if (translationBrowserChromeUiInstancesByTabId.has(tabId)) {
|
|
||||||
return translationBrowserChromeUiInstancesByTabId.get(tabId);
|
|
||||||
}
|
|
||||||
const tab = tabManager.get(tabId);
|
|
||||||
const { browser } = tab;
|
|
||||||
const translationBrowserChromeUi = new TranslationBrowserChromeUi(
|
|
||||||
Services,
|
|
||||||
browser,
|
|
||||||
context,
|
|
||||||
apiEventEmitter,
|
|
||||||
tabId,
|
|
||||||
);
|
|
||||||
translationBrowserChromeUiInstancesByTabId.set(
|
|
||||||
tabId,
|
|
||||||
translationBrowserChromeUi,
|
|
||||||
);
|
|
||||||
return translationBrowserChromeUi;
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
experiments: {
|
|
||||||
translateUi: {
|
|
||||||
/* Start reacting to translation state updates */
|
|
||||||
start: async function start() {
|
|
||||||
try {
|
|
||||||
console.log("Called start()");
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
"Inactivating legacy built-in translation feature (by setting browser.translation.ui.show and browser.translation.detectLanguage to false)",
|
|
||||||
);
|
|
||||||
Services.prefs.setBoolPref(`browser.translation.ui.show`, false);
|
|
||||||
Services.prefs.setBoolPref(
|
|
||||||
`browser.translation.detectLanguage`,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
} catch (error) {
|
|
||||||
// Surface otherwise silent or obscurely reported errors
|
|
||||||
console.error(error.message, error.stack);
|
|
||||||
throw new ExtensionError(error.message);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/* Set current ui state */
|
|
||||||
setUiState: async function setUiState(tabId, uiState) {
|
|
||||||
try {
|
|
||||||
// console.log("Called setUiState(tabId, uiState)", {tabId,uiState});
|
|
||||||
const translationBrowserChromeUi = getTranslationBrowserChromeUiInstanceByTabId(
|
|
||||||
tabId,
|
|
||||||
);
|
|
||||||
translationBrowserChromeUi.onUiStateUpdate(uiState);
|
|
||||||
return undefined;
|
|
||||||
} catch (error) {
|
|
||||||
// Surface otherwise silent or obscurely reported errors
|
|
||||||
console.error(error.message, error.stack);
|
|
||||||
throw new ExtensionError(error.message);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/* Stop reacting to translation state updates */
|
|
||||||
stop: async function stop() {
|
|
||||||
try {
|
|
||||||
console.log("Called stop()");
|
|
||||||
return undefined;
|
|
||||||
} catch (error) {
|
|
||||||
// Surface otherwise silent or obscurely reported errors
|
|
||||||
console.error(error.message, error.stack);
|
|
||||||
throw new ExtensionError(error.message);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/* Event boilerplate with listeners that forwards all but the first argument to the web extension event */
|
|
||||||
onInfoBarDisplayed: eventManagerFactory("onInfoBarDisplayed").api(),
|
|
||||||
onSelectTranslateTo: eventManagerFactory("onSelectTranslateTo").api(),
|
|
||||||
onSelectTranslateFrom: eventManagerFactory(
|
|
||||||
"onSelectTranslateFrom",
|
|
||||||
).api(),
|
|
||||||
onInfoBarClosed: eventManagerFactory("onInfoBarClosed").api(),
|
|
||||||
onNeverTranslateSelectedLanguage: eventManagerFactory(
|
|
||||||
"onNeverTranslateSelectedLanguage",
|
|
||||||
).api(),
|
|
||||||
onNeverTranslateThisSite: eventManagerFactory(
|
|
||||||
"onNeverTranslateThisSite",
|
|
||||||
).api(),
|
|
||||||
onShowOriginalButtonPressed: eventManagerFactory(
|
|
||||||
"onShowOriginalButtonPressed",
|
|
||||||
).api(),
|
|
||||||
onShowTranslatedButtonPressed: eventManagerFactory(
|
|
||||||
"onShowTranslatedButtonPressed",
|
|
||||||
).api(),
|
|
||||||
onTranslateButtonPressed: eventManagerFactory(
|
|
||||||
"onTranslateButtonPressed",
|
|
||||||
).api(),
|
|
||||||
onNotNowButtonPressed: eventManagerFactory(
|
|
||||||
"onNotNowButtonPressed",
|
|
||||||
).api(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,443 +0,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/. */
|
|
||||||
|
|
||||||
/* global MozElements */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
window.MozTranslationNotification = class extends MozElements.Notification {
|
|
||||||
static get markup() {
|
|
||||||
return `
|
|
||||||
<hbox anonid="details" align="center" flex="1">
|
|
||||||
<image class="messageImage"/>
|
|
||||||
<panel anonid="welcomePanel" class="translation-welcome-panel" type="arrow" align="start">
|
|
||||||
<image class="translation-welcome-logo"/>
|
|
||||||
<vbox flex="1" class="translation-welcome-content">
|
|
||||||
<description class="translation-welcome-headline" anonid="welcomeHeadline"/>
|
|
||||||
<description class="translation-welcome-body" anonid="welcomeBody"/>
|
|
||||||
<hbox align="center">
|
|
||||||
<label anonid="learnMore" class="plain" onclick="openTrustedLinkIn('https://support.mozilla.org/kb/automatic-translation', 'tab'); this.parentNode.parentNode.parentNode.hidePopup();" is="text-link"/>
|
|
||||||
<spacer flex="1"/>
|
|
||||||
<button anonid="thanksButton" onclick="this.parentNode.parentNode.parentNode.hidePopup();"/>
|
|
||||||
</hbox>
|
|
||||||
</vbox>
|
|
||||||
</panel>
|
|
||||||
<deck anonid="translationStates" selectedIndex="0">
|
|
||||||
<hbox class="translate-offer-box" align="center">
|
|
||||||
<label value="&translation.thisPageIsIn.label;"/>
|
|
||||||
<menulist class="notification-button" anonid="detectedLanguage">
|
|
||||||
<menupopup/>
|
|
||||||
</menulist>
|
|
||||||
<label value="&translation.translateThisPage.label;"/>
|
|
||||||
<button class="notification-button primary" label="&translation.translate.button;" anonid="translate" oncommand="this.closest('notification').translate();"/>
|
|
||||||
<button class="notification-button" label="&translation.notNow.button;" anonid="notNow" oncommand="this.closest('notification').notNow();"/>
|
|
||||||
</hbox>
|
|
||||||
<vbox class="translating-box" pack="center">
|
|
||||||
<hbox><label value="&translation.translatingContent.label;"/><label anonid="progress-label" value=""/></hbox>
|
|
||||||
</vbox>
|
|
||||||
<hbox class="translated-box" align="center">
|
|
||||||
<label value="&translation.translatedFrom.label;" style="margin-inline-end: 4px;"/>
|
|
||||||
<label anonid="fromLanguage" style="margin-inline:0;font-weight: bold;"/>
|
|
||||||
<label value="&translation.translatedTo.label;" style="margin-inline:3px;"/>
|
|
||||||
<label anonid="toLanguage" style="margin-inline:0;font-weight: bold;"/>
|
|
||||||
<label value="&translation.translatedToSuffix.label;"/>
|
|
||||||
<button anonid="showOriginal" class="notification-button" label="&translation.showOriginal.button;" oncommand="this.closest('notification').showOriginal();"/>
|
|
||||||
<button anonid="showTranslation" class="notification-button" label="&translation.showTranslation.button;" oncommand="this.closest('notification').showTranslation();"/>
|
|
||||||
</hbox>
|
|
||||||
<hbox class="translation-error" align="center">
|
|
||||||
<label value="&translation.errorTranslating.label;"/>
|
|
||||||
<button class="notification-button" label="&translation.tryAgain.button;" anonid="tryAgain" oncommand="this.closest('notification').translate();"/>
|
|
||||||
</hbox>
|
|
||||||
<vbox class="translation-unavailable" pack="center">
|
|
||||||
<label value="&translation.serviceUnavailable.label;"/>
|
|
||||||
</vbox>
|
|
||||||
</deck>
|
|
||||||
<spacer flex="1"/>
|
|
||||||
<button type="menu" class="notification-button" anonid="options" label="&translation.options.menu;">
|
|
||||||
<menupopup class="translation-menupopup" onpopupshowing="this.closest('notification').optionsShowing();">
|
|
||||||
<menuitem anonid="neverForLanguage" oncommand="this.closest('notification').neverForLanguage();"/>
|
|
||||||
<menuitem anonid="neverForSite" oncommand="this.closest('notification').neverForSite();" label="&translation.options.neverForSite.label;" accesskey="&translation.options.neverForSite.accesskey;"/>
|
|
||||||
<menuseparator/>
|
|
||||||
<menuitem oncommand="openPreferences('paneGeneral');" label="&translation.options.preferences.label;" accesskey="&translation.options.preferences.accesskey;"/>
|
|
||||||
</menupopup>
|
|
||||||
</button>
|
|
||||||
</hbox>
|
|
||||||
<toolbarbutton anonid="closeButton" ondblclick="event.stopPropagation();"
|
|
||||||
class="messageCloseButton close-icon tabbable"
|
|
||||||
tooltiptext="&closeNotification.tooltip;"
|
|
||||||
oncommand="this.parentNode.closeCommand();"/>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get entities() {
|
|
||||||
return [
|
|
||||||
"chrome://global/locale/notification.dtd",
|
|
||||||
"chrome://browser/locale/translation.dtd",
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
|
||||||
this.appendChild(this.constructor.fragment);
|
|
||||||
|
|
||||||
for (const [propertyName, selector] of [
|
|
||||||
["details", "[anonid=details]"],
|
|
||||||
["messageImage", ".messageImage"],
|
|
||||||
["spacer", "[anonid=spacer]"],
|
|
||||||
]) {
|
|
||||||
this[propertyName] = this.querySelector(selector);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateTranslationProgress(
|
|
||||||
shouldShowTranslationProgress,
|
|
||||||
localizedTranslationProgressText,
|
|
||||||
) {
|
|
||||||
const progressLabelValue = shouldShowTranslationProgress
|
|
||||||
? localizedTranslationProgressText
|
|
||||||
: "";
|
|
||||||
this._getAnonElt("progress-label").setAttribute(
|
|
||||||
"value",
|
|
||||||
progressLabelValue,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
set state(val) {
|
|
||||||
const deck = this._getAnonElt("translationStates");
|
|
||||||
|
|
||||||
const activeElt = document.activeElement;
|
|
||||||
if (activeElt && deck.contains(activeElt)) {
|
|
||||||
activeElt.blur();
|
|
||||||
}
|
|
||||||
|
|
||||||
let stateName;
|
|
||||||
for (const name of ["OFFER", "TRANSLATING", "TRANSLATED", "ERROR"]) {
|
|
||||||
if (Translation["STATE_" + name] === val) {
|
|
||||||
stateName = name.toLowerCase();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.setAttribute("state", stateName);
|
|
||||||
|
|
||||||
if (val === this.translation.TranslationInfoBarStates.STATE_TRANSLATED) {
|
|
||||||
this._handleButtonHiding();
|
|
||||||
}
|
|
||||||
|
|
||||||
deck.selectedIndex = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
get state() {
|
|
||||||
return this._getAnonElt("translationStates").selectedIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
init(translationBrowserChromeUiNotificationManager) {
|
|
||||||
this.translation = translationBrowserChromeUiNotificationManager;
|
|
||||||
|
|
||||||
this.localizedLanguagesByCode = {};
|
|
||||||
|
|
||||||
const sortByLocalizedName = function(list) {
|
|
||||||
const names = Services.intl.getLanguageDisplayNames(undefined, list);
|
|
||||||
return list
|
|
||||||
.map((code, i) => [code, names[i]])
|
|
||||||
.sort((a, b) => a[1].localeCompare(b[1]));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Fill the lists of supported source languages.
|
|
||||||
const detectedLanguage = this._getAnonElt("detectedLanguage");
|
|
||||||
const sourceLanguages = sortByLocalizedName(
|
|
||||||
this.translation.uiState.supportedSourceLanguages,
|
|
||||||
);
|
|
||||||
for (const [code, name] of sourceLanguages) {
|
|
||||||
detectedLanguage.appendItem(name, code);
|
|
||||||
this.localizedLanguagesByCode[code] = name;
|
|
||||||
}
|
|
||||||
detectedLanguage.value = this.translation.uiState.detectedLanguageResults.language;
|
|
||||||
|
|
||||||
// translatedFrom is only set if we have already translated this page.
|
|
||||||
const fromLanguage = this._getAnonElt("fromLanguage");
|
|
||||||
if (translationBrowserChromeUiNotificationManager.uiState.translatedFrom) {
|
|
||||||
for (const [code, name] of sourceLanguages) {
|
|
||||||
detectedLanguage.appendItem(name, code);
|
|
||||||
}
|
|
||||||
fromLanguage.setAttribute(
|
|
||||||
"value",
|
|
||||||
this.localizedLanguagesByCode[
|
|
||||||
translationBrowserChromeUiNotificationManager.uiState.translatedFrom
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill the list of supported target languages.
|
|
||||||
const toLanguage = this._getAnonElt("toLanguage");
|
|
||||||
const targetLanguages = sortByLocalizedName(
|
|
||||||
this.translation.uiState.supportedTargetLanguages,
|
|
||||||
);
|
|
||||||
for (const [code, name] of targetLanguages) {
|
|
||||||
this.localizedLanguagesByCode[code] = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (translationBrowserChromeUiNotificationManager.uiState.translatedTo) {
|
|
||||||
toLanguage.setAttribute(
|
|
||||||
"value",
|
|
||||||
this.localizedLanguagesByCode[
|
|
||||||
translationBrowserChromeUiNotificationManager.uiState.translatedTo
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (translationBrowserChromeUiNotificationManager.uiState.infobarState) {
|
|
||||||
this.state =
|
|
||||||
translationBrowserChromeUiNotificationManager.uiState.infobarState;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
// The welcome popup/notification is currently disabled
|
|
||||||
const kWelcomePref = "browser.translation.ui.welcomeMessageShown";
|
|
||||||
if (
|
|
||||||
Services.prefs.prefHasUserValue(kWelcomePref) ||
|
|
||||||
this.translation.browser !== gBrowser.selectedBrowser
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.addEventListener(
|
|
||||||
"transitionend",
|
|
||||||
function() {
|
|
||||||
// These strings are hardcoded because they need to reach beta
|
|
||||||
// without riding the trains.
|
|
||||||
const localizedStrings = {
|
|
||||||
en: [
|
|
||||||
"Hey look! It's something new!",
|
|
||||||
"Now the Web is even more accessible with our new in-page translation feature. Click the translate button to try it!",
|
|
||||||
"Learn more.",
|
|
||||||
"Thanks",
|
|
||||||
],
|
|
||||||
"es-AR": [
|
|
||||||
"\xA1Mir\xE1! \xA1Hay algo nuevo!",
|
|
||||||
"Ahora la web es a\xFAn m\xE1s accesible con nuestra nueva funcionalidad de traducci\xF3n integrada. \xA1Hac\xE9 clic en el bot\xF3n traducir para probarla!",
|
|
||||||
"Conoc\xE9 m\xE1s.",
|
|
||||||
"Gracias",
|
|
||||||
],
|
|
||||||
"es-ES": [
|
|
||||||
"\xA1Mira! \xA1Hay algo nuevo!",
|
|
||||||
"Con la nueva funcionalidad de traducci\xF3n integrada, ahora la Web es a\xFAn m\xE1s accesible. \xA1Pulsa el bot\xF3n Traducir y pru\xE9bala!",
|
|
||||||
"M\xE1s informaci\xF3n.",
|
|
||||||
"Gracias",
|
|
||||||
],
|
|
||||||
pl: [
|
|
||||||
"Sp\xF3jrz tutaj! To co\u015B nowego!",
|
|
||||||
"Sie\u0107 sta\u0142a si\u0119 w\u0142a\u015Bnie jeszcze bardziej dost\u0119pna dzi\u0119ki opcji bezpo\u015Bredniego t\u0142umaczenia stron. Kliknij przycisk t\u0142umaczenia, aby spr\xF3bowa\u0107!",
|
|
||||||
"Dowiedz si\u0119 wi\u0119cej",
|
|
||||||
"Dzi\u0119kuj\u0119",
|
|
||||||
],
|
|
||||||
tr: [
|
|
||||||
"Bak\u0131n, burada yeni bir \u015Fey var!",
|
|
||||||
"Yeni sayfa i\xE7i \xE7eviri \xF6zelli\u011Fimiz sayesinde Web art\u0131k \xE7ok daha anla\u015F\u0131l\u0131r olacak. Denemek i\xE7in \xC7evir d\xFC\u011Fmesine t\u0131klay\u0131n!",
|
|
||||||
"Daha fazla bilgi al\u0131n.",
|
|
||||||
"Te\u015Fekk\xFCrler",
|
|
||||||
],
|
|
||||||
vi: [
|
|
||||||
"Nh\xECn n\xE0y! \u0110\u1ED3 m\u1EDBi!",
|
|
||||||
"Gi\u1EDD \u0111\xE2y ch\xFAng ta c\xF3 th\u1EC3 ti\u1EBFp c\u1EADn web d\u1EC5 d\xE0ng h\u01A1n n\u1EEFa v\u1EDBi t\xEDnh n\u0103ng d\u1ECBch ngay trong trang. Hay nh\u1EA5n n\xFAt d\u1ECBch \u0111\u1EC3 th\u1EED!",
|
|
||||||
"T\xECm hi\u1EC3u th\xEAm.",
|
|
||||||
"C\u1EA3m \u01A1n",
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
let locale = Services.locale.appLocaleAsBCP47;
|
|
||||||
if (!(locale in localizedStrings)) {
|
|
||||||
locale = "en";
|
|
||||||
}
|
|
||||||
const strings = localizedStrings[locale];
|
|
||||||
|
|
||||||
this._getAnonElt("welcomeHeadline").setAttribute("value", strings[0]);
|
|
||||||
this._getAnonElt("welcomeBody").textContent = strings[1];
|
|
||||||
this._getAnonElt("learnMore").setAttribute("value", strings[2]);
|
|
||||||
this._getAnonElt("thanksButton").setAttribute("label", strings[3]);
|
|
||||||
|
|
||||||
// TODO: Figure out why this shows a strangely rendered popup at the corner of the window instead next to the URL bar
|
|
||||||
const panel = this._getAnonElt("welcomePanel");
|
|
||||||
panel.openPopup(
|
|
||||||
this._getAnonElt("messageImage"),
|
|
||||||
"bottomcenter topleft",
|
|
||||||
);
|
|
||||||
|
|
||||||
Services.prefs.setBoolPref(kWelcomePref, true);
|
|
||||||
},
|
|
||||||
{ once: true },
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
_getAnonElt(anonId) {
|
|
||||||
return this.querySelector("[anonid=" + anonId + "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
fromLanguageChanged() {
|
|
||||||
this.translation.fromLanguageChanged(
|
|
||||||
this._getSourceLang(),
|
|
||||||
this._getTargetLang(),
|
|
||||||
);
|
|
||||||
this.translate();
|
|
||||||
}
|
|
||||||
|
|
||||||
toLanguageChanged() {
|
|
||||||
this.translation.toLanguageChanged(
|
|
||||||
this._getSourceLang(),
|
|
||||||
this._getTargetLang(),
|
|
||||||
);
|
|
||||||
this.translate();
|
|
||||||
}
|
|
||||||
|
|
||||||
translate() {
|
|
||||||
const from = this._getSourceLang();
|
|
||||||
const to = this._getTargetLang();
|
|
||||||
|
|
||||||
// Initiate translation
|
|
||||||
this.translation.translate(from, to);
|
|
||||||
|
|
||||||
// Store the values used in the translation in the from and to inputs
|
|
||||||
if (
|
|
||||||
this.translation.uiState.infobarState ===
|
|
||||||
this.translation.TranslationInfoBarStates.STATE_OFFER
|
|
||||||
) {
|
|
||||||
this._getAnonElt("fromLanguage").setAttribute(
|
|
||||||
"value",
|
|
||||||
this.localizedLanguagesByCode[from],
|
|
||||||
);
|
|
||||||
this._getAnonElt("toLanguage").setAttribute(
|
|
||||||
"value",
|
|
||||||
this.localizedLanguagesByCode[to],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* To be called when the infobar should be closed per user's wish (e.g.
|
|
||||||
* by clicking the notification's close button, the not now button or choosing never to translate)
|
|
||||||
*/
|
|
||||||
closeCommand() {
|
|
||||||
const from = this._getSourceLang();
|
|
||||||
const to = this._getTargetLang();
|
|
||||||
this.close();
|
|
||||||
this.translation.infobarClosed(from, to);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* To be called when the infobar should be closed per user's wish
|
|
||||||
* by clicking the Not now button
|
|
||||||
*/
|
|
||||||
notNow() {
|
|
||||||
this.translation.notNow(this._getSourceLang(), this._getTargetLang());
|
|
||||||
this.closeCommand();
|
|
||||||
}
|
|
||||||
|
|
||||||
_handleButtonHiding() {
|
|
||||||
const originalShown = this.translation.uiState.originalShown;
|
|
||||||
this._getAnonElt("showOriginal").hidden = originalShown;
|
|
||||||
this._getAnonElt("showTranslation").hidden = !originalShown;
|
|
||||||
}
|
|
||||||
|
|
||||||
showOriginal() {
|
|
||||||
this.translation.showOriginalContent(
|
|
||||||
this._getSourceLang(),
|
|
||||||
this._getTargetLang(),
|
|
||||||
);
|
|
||||||
this._handleButtonHiding();
|
|
||||||
}
|
|
||||||
|
|
||||||
showTranslation() {
|
|
||||||
this.translation.showTranslatedContent(
|
|
||||||
this._getSourceLang(),
|
|
||||||
this._getTargetLang(),
|
|
||||||
);
|
|
||||||
this._handleButtonHiding();
|
|
||||||
}
|
|
||||||
|
|
||||||
_getSourceLang() {
|
|
||||||
const lang = this._getAnonElt("detectedLanguage").value;
|
|
||||||
if (!lang) {
|
|
||||||
throw new Error("Source language is not defined");
|
|
||||||
}
|
|
||||||
return lang;
|
|
||||||
}
|
|
||||||
|
|
||||||
_getTargetLang() {
|
|
||||||
return this.translation.uiState.defaultTargetLanguage;
|
|
||||||
}
|
|
||||||
|
|
||||||
optionsShowing() {
|
|
||||||
const lang = this._getSourceLang();
|
|
||||||
|
|
||||||
// Get the source language name.
|
|
||||||
const langName = Services.intl.getLanguageDisplayNames(undefined, [
|
|
||||||
lang,
|
|
||||||
])[0];
|
|
||||||
|
|
||||||
// Set the label and accesskey on the menuitem.
|
|
||||||
const bundle = Services.strings.createBundle(
|
|
||||||
"chrome://browser/locale/translation.properties",
|
|
||||||
);
|
|
||||||
let item = this._getAnonElt("neverForLanguage");
|
|
||||||
const kStrId = "translation.options.neverForLanguage";
|
|
||||||
item.setAttribute(
|
|
||||||
"label",
|
|
||||||
bundle.formatStringFromName(kStrId + ".label", [langName]),
|
|
||||||
);
|
|
||||||
item.setAttribute(
|
|
||||||
"accesskey",
|
|
||||||
bundle.GetStringFromName(kStrId + ".accesskey"),
|
|
||||||
);
|
|
||||||
|
|
||||||
// We may need to disable the menuitems if they have already been used.
|
|
||||||
// Check if translation is already disabled for this language:
|
|
||||||
const neverForLangs = Services.prefs.getCharPref(
|
|
||||||
"browser.translation.neverForLanguages",
|
|
||||||
);
|
|
||||||
item.disabled = neverForLangs.split(",").includes(lang);
|
|
||||||
|
|
||||||
// Check if translation is disabled for the domain:
|
|
||||||
const principal = this.translation.browser.contentPrincipal;
|
|
||||||
const perms = Services.perms;
|
|
||||||
item = this._getAnonElt("neverForSite");
|
|
||||||
item.disabled =
|
|
||||||
perms.testExactPermissionFromPrincipal(principal, "translate") ===
|
|
||||||
perms.DENY_ACTION;
|
|
||||||
}
|
|
||||||
|
|
||||||
neverForLanguage() {
|
|
||||||
const kPrefName = "browser.translation.neverForLanguages";
|
|
||||||
const sourceLang = this._getSourceLang();
|
|
||||||
|
|
||||||
let val = Services.prefs.getCharPref(kPrefName);
|
|
||||||
if (val) {
|
|
||||||
val += ",";
|
|
||||||
}
|
|
||||||
val += sourceLang;
|
|
||||||
|
|
||||||
Services.prefs.setCharPref(kPrefName, val);
|
|
||||||
|
|
||||||
this.translation.neverForLanguage(
|
|
||||||
this._getSourceLang(),
|
|
||||||
this._getTargetLang(),
|
|
||||||
);
|
|
||||||
this.closeCommand();
|
|
||||||
}
|
|
||||||
|
|
||||||
neverForSite() {
|
|
||||||
const principal = this.translation.browser.contentPrincipal;
|
|
||||||
const perms = Services.perms;
|
|
||||||
perms.addFromPrincipal(principal, "translate", perms.DENY_ACTION);
|
|
||||||
|
|
||||||
this.translation.neverForSite(this._getSourceLang(), this._getTargetLang());
|
|
||||||
this.closeCommand();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
customElements.define(
|
|
||||||
`translation-notification-${window.now}`,
|
|
||||||
window.MozTranslationNotification,
|
|
||||||
{
|
|
||||||
extends: "notification",
|
|
||||||
},
|
|
||||||
);
|
|
|
@ -1,100 +0,0 @@
|
||||||
[
|
|
||||||
{
|
|
||||||
"namespace": "experiments.translateUi",
|
|
||||||
"description": "Provides browser chrome UI that reacts to translation states and fires events on user interaction",
|
|
||||||
"functions": [
|
|
||||||
{
|
|
||||||
"name": "start",
|
|
||||||
"type": "function",
|
|
||||||
"async": true,
|
|
||||||
"description": "Start reacting to translation state updates",
|
|
||||||
"parameters": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "setUiState",
|
|
||||||
"type": "function",
|
|
||||||
"async": true,
|
|
||||||
"description": "Set current ui state",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "tabId",
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "uiState",
|
|
||||||
"type": "any"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "stop",
|
|
||||||
"type": "function",
|
|
||||||
"async": true,
|
|
||||||
"description": "Stop reacting to translation state updates",
|
|
||||||
"parameters": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"events": [
|
|
||||||
{
|
|
||||||
"name": "onInfoBarDisplayed",
|
|
||||||
"type": "function",
|
|
||||||
"description": "Foo",
|
|
||||||
"parameters": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "onSelectTranslateTo",
|
|
||||||
"type": "function",
|
|
||||||
"description": "Foo",
|
|
||||||
"parameters": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "onSelectTranslateFrom",
|
|
||||||
"type": "function",
|
|
||||||
"description": "Foo",
|
|
||||||
"parameters": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "onInfoBarClosed",
|
|
||||||
"type": "function",
|
|
||||||
"description": "Foo",
|
|
||||||
"parameters": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "onNeverTranslateSelectedLanguage",
|
|
||||||
"type": "function",
|
|
||||||
"description": "Foo",
|
|
||||||
"parameters": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "onNeverTranslateThisSite",
|
|
||||||
"type": "function",
|
|
||||||
"description": "Foo",
|
|
||||||
"parameters": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "onShowOriginalButtonPressed",
|
|
||||||
"type": "function",
|
|
||||||
"description": "Foo",
|
|
||||||
"parameters": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "onShowTranslatedButtonPressed",
|
|
||||||
"type": "function",
|
|
||||||
"description": "Foo",
|
|
||||||
"parameters": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "onTranslateButtonPressed",
|
|
||||||
"type": "function",
|
|
||||||
"description": "Foo",
|
|
||||||
"parameters": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "onNotNowButtonPressed",
|
|
||||||
"type": "function",
|
|
||||||
"description": "Foo",
|
|
||||||
"parameters": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 750 B |
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 1.7 KiB |
|
@ -1,136 +0,0 @@
|
||||||
{
|
|
||||||
"manifest_version": 2,
|
|
||||||
"name": "Firefox Translations",
|
|
||||||
"description": "__MSG_extensionDescription__",
|
|
||||||
"version": "0.4.3",
|
|
||||||
"incognito": "spanning",
|
|
||||||
"default_locale": "en_US",
|
|
||||||
"background": {
|
|
||||||
"scripts": [
|
|
||||||
"commons.js",
|
|
||||||
"background.js"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"content_scripts": [
|
|
||||||
{
|
|
||||||
"js": [
|
|
||||||
"commons.js"
|
|
||||||
],
|
|
||||||
"matches": [
|
|
||||||
"<all_urls>"
|
|
||||||
],
|
|
||||||
"all_frames": true,
|
|
||||||
"run_at": "document_idle",
|
|
||||||
"match_about_blank": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"js": [
|
|
||||||
"dom-translation-content-script.js"
|
|
||||||
],
|
|
||||||
"matches": [
|
|
||||||
"<all_urls>"
|
|
||||||
],
|
|
||||||
"all_frames": true,
|
|
||||||
"run_at": "document_idle",
|
|
||||||
"match_about_blank": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"permissions": [
|
|
||||||
"<all_urls>",
|
|
||||||
"storage"
|
|
||||||
],
|
|
||||||
"web_accessible_resources": [
|
|
||||||
"commons.js.map",
|
|
||||||
"background.js.map",
|
|
||||||
"dom-translation-content-script.js.map",
|
|
||||||
"translation-worker.js.map"
|
|
||||||
],
|
|
||||||
"icons": {
|
|
||||||
"16": "icons/translation.16x16.png",
|
|
||||||
"32": "icons/translation.32x32.png"
|
|
||||||
},
|
|
||||||
"hidden": false,
|
|
||||||
"experiment_apis": {
|
|
||||||
"extensionPreferences": {
|
|
||||||
"schema": "./experiment-apis/extensionPreferences/schema.json",
|
|
||||||
"parent": {
|
|
||||||
"scopes": [
|
|
||||||
"addon_parent"
|
|
||||||
],
|
|
||||||
"script": "./experiment-apis/extensionPreferences/api.js",
|
|
||||||
"paths": [
|
|
||||||
[
|
|
||||||
"experiments",
|
|
||||||
"extensionPreferences"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"languageDetector": {
|
|
||||||
"schema": "./experiment-apis/languageDetector/schema.json",
|
|
||||||
"parent": {
|
|
||||||
"scopes": [
|
|
||||||
"addon_parent"
|
|
||||||
],
|
|
||||||
"script": "./experiment-apis/languageDetector/api.js",
|
|
||||||
"paths": [
|
|
||||||
[
|
|
||||||
"experiments",
|
|
||||||
"languageDetector"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"telemetryEnvironment": {
|
|
||||||
"schema": "./experiment-apis/telemetryEnvironment/schema.json",
|
|
||||||
"parent": {
|
|
||||||
"scopes": [
|
|
||||||
"addon_parent"
|
|
||||||
],
|
|
||||||
"script": "./experiment-apis/telemetryEnvironment/api.js",
|
|
||||||
"paths": [
|
|
||||||
[
|
|
||||||
"experiments",
|
|
||||||
"telemetryEnvironment"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"telemetryPreferences": {
|
|
||||||
"schema": "./experiment-apis/telemetryPreferences/schema.json",
|
|
||||||
"parent": {
|
|
||||||
"scopes": [
|
|
||||||
"addon_parent"
|
|
||||||
],
|
|
||||||
"script": "./experiment-apis/telemetryPreferences/api.js",
|
|
||||||
"paths": [
|
|
||||||
[
|
|
||||||
"experiments",
|
|
||||||
"telemetryPreferences"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"translateUi": {
|
|
||||||
"schema": "./experiment-apis/translateUi/schema.json",
|
|
||||||
"parent": {
|
|
||||||
"scopes": [
|
|
||||||
"addon_parent"
|
|
||||||
],
|
|
||||||
"script": "./experiment-apis/translateUi/api.js",
|
|
||||||
"paths": [
|
|
||||||
[
|
|
||||||
"experiments",
|
|
||||||
"translateUi"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"applications": {
|
|
||||||
"gecko": {
|
|
||||||
"id": "firefox-translations@mozilla.org",
|
|
||||||
"strict_min_version": "90.0a1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,127 +0,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/.
|
|
||||||
|
|
||||||
# script to pull and import Firefox Translations's extension source code
|
|
||||||
|
|
||||||
import os.path
|
|
||||||
from zipfile import ZipFile
|
|
||||||
import subprocess
|
|
||||||
import shutil
|
|
||||||
import sys
|
|
||||||
|
|
||||||
# the version number is the only part of this script that needs to be updated
|
|
||||||
# in order to update the in-tree version with the latest xpi version
|
|
||||||
extension_version = input(
|
|
||||||
"Type the extension's version (tag) on Github you want to import: "
|
|
||||||
)
|
|
||||||
print("Importing version:", extension_version)
|
|
||||||
if not extension_version:
|
|
||||||
sys.exit("Value can't be empty.")
|
|
||||||
|
|
||||||
extension_folder = "firefox-translations-src"
|
|
||||||
|
|
||||||
if not os.path.exists("import_xpi.py"):
|
|
||||||
sys.exit("This script is intended to be executed from its local folder")
|
|
||||||
|
|
||||||
have_xpi = "N"
|
|
||||||
local_xpi_file = (
|
|
||||||
"firefox-translations-src/dist/production/firefox/"
|
|
||||||
"firefox-infobar-ui/firefox-translations-" + extension_version + ".xpi"
|
|
||||||
)
|
|
||||||
if os.path.isfile(local_xpi_file):
|
|
||||||
have_xpi = input(
|
|
||||||
"Extension xpi exists. Press Y to use it or any other key to rebuild it."
|
|
||||||
)
|
|
||||||
|
|
||||||
if have_xpi.lower() != "y":
|
|
||||||
# deleting old files if any
|
|
||||||
shutil.rmtree(extension_folder, ignore_errors=True)
|
|
||||||
# cloning the extension
|
|
||||||
subprocess.call(
|
|
||||||
(
|
|
||||||
"git clone -b v" + extension_version + " "
|
|
||||||
"https://github.com/mozilla-extensions/firefox-translations/ "
|
|
||||||
+ extension_folder
|
|
||||||
+ " "
|
|
||||||
).split()
|
|
||||||
)
|
|
||||||
# setting up the repo
|
|
||||||
subprocess.call("yarn install".split(), cwd=extension_folder)
|
|
||||||
# pulling bergamot-translator submodule, the repo containing the port of the
|
|
||||||
# neural machine translation engine to wasm
|
|
||||||
subprocess.call(
|
|
||||||
"git submodule update --init --recursive".split(),
|
|
||||||
cwd=extension_folder,
|
|
||||||
)
|
|
||||||
# build the wasm nmt module
|
|
||||||
subprocess.call(
|
|
||||||
"./bergamot-translator/build-wasm.sh".split(),
|
|
||||||
cwd=extension_folder,
|
|
||||||
)
|
|
||||||
# import the generated wasm module to the extension
|
|
||||||
subprocess.call(
|
|
||||||
"./import-bergamot-translator.sh ./bergamot-translator/build-wasm/".split(),
|
|
||||||
cwd=extension_folder,
|
|
||||||
)
|
|
||||||
# build the final xpi
|
|
||||||
env = {
|
|
||||||
**os.environ,
|
|
||||||
"MC": str(1),
|
|
||||||
}
|
|
||||||
subprocess.call(
|
|
||||||
"yarn build:firefox-infobar-ui".split(), cwd=extension_folder, env=env
|
|
||||||
)
|
|
||||||
|
|
||||||
shutil.rmtree("extension", ignore_errors=True)
|
|
||||||
os.mkdir("extension")
|
|
||||||
file_exceptions = [
|
|
||||||
"META-INF",
|
|
||||||
".md",
|
|
||||||
"BRANCH",
|
|
||||||
"COMMITHASH",
|
|
||||||
"LASTCOMMITDATETIME",
|
|
||||||
"VERSION",
|
|
||||||
".map",
|
|
||||||
".yaml",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def isValidFile(filename):
|
|
||||||
for exception in file_exceptions:
|
|
||||||
if exception in filename:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
file_set = set()
|
|
||||||
# read xpi files
|
|
||||||
with ZipFile(local_xpi_file, "r") as zip:
|
|
||||||
namelist = zip.namelist()
|
|
||||||
cleared_namelist = []
|
|
||||||
for filename in namelist:
|
|
||||||
if isValidFile(filename):
|
|
||||||
full_file_path = zip.extract(filename, "extension")
|
|
||||||
if filename.endswith(".js"):
|
|
||||||
filename = "browser/extensions/translations/{}".format(full_file_path)
|
|
||||||
subprocess.call(
|
|
||||||
str(
|
|
||||||
"./mach lint --linter license {} --fix".format(filename)
|
|
||||||
).split(),
|
|
||||||
cwd="../../../",
|
|
||||||
)
|
|
||||||
|
|
||||||
# patching BrowserGlue.jsm
|
|
||||||
with open("../../components/BrowserGlue.jsm") as fp:
|
|
||||||
count = 0
|
|
||||||
Lines = fp.readlines()
|
|
||||||
for line in Lines:
|
|
||||||
if "resource://builtin-addons/translations/" in line:
|
|
||||||
Lines[count - 1] = ' "{}",\n'.format(extension_version)
|
|
||||||
with open("../../components/BrowserGlue.jsm", "w") as outfile:
|
|
||||||
outfile.write("".join(Lines))
|
|
||||||
break
|
|
||||||
count += 1
|
|
||||||
|
|
||||||
|
|
||||||
print("Import finalized successfully")
|
|
|
@ -1,8 +0,0 @@
|
||||||
##### This file was automatically generated by the import_xpi.py script ####
|
|
||||||
# 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/.
|
|
||||||
|
|
||||||
browser.jar:
|
|
||||||
% resource builtin-addons %builtin-addons/ contentaccessible=yes
|
|
||||||
builtin-addons/translations/ (extension/**)
|
|
|
@ -1,4 +0,0 @@
|
||||||
with Files("**"):
|
|
||||||
BUG_COMPONENT = ("Firefox", "Translations")
|
|
||||||
|
|
||||||
JAR_MANIFESTS += ["jar.mn"]
|
|
Загрузка…
Ссылка в новой задаче