зеркало из https://github.com/mozilla/gecko-dev.git
Bug 979424 - Implement structure and state switching for translation infobar, r=felipe.
This commit is contained in:
Родитель
9e742dea43
Коммит
8861e06a05
|
@ -793,6 +793,12 @@ panelview > .social-panel-frame {
|
|||
height: auto;
|
||||
}
|
||||
|
||||
/* Translation */
|
||||
notification[value="translation"] {
|
||||
-moz-binding: url("chrome://browser/content/translation-infobar.xml#translationbar");
|
||||
}
|
||||
|
||||
/* Social */
|
||||
/* Note the chatbox 'width' values are duplicated in socialchat.xml */
|
||||
chatbox {
|
||||
-moz-binding: url("chrome://browser/content/socialchat.xml#chatbox");
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
browser.jar:
|
||||
content/browser/translation-infobar.xml
|
|
@ -10,6 +10,12 @@ EXTRA_JS_MODULES = [
|
|||
'LanguageDetector.jsm'
|
||||
]
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
'test/browser.ini'
|
||||
]
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += [
|
||||
'test/xpcshell.ini'
|
||||
]
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[DEFAULT]
|
||||
|
||||
[browser_translation_infobar.js]
|
|
@ -0,0 +1,155 @@
|
|||
/* 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/. */
|
||||
|
||||
// tests the translation infobar, using a fake 'Translation' implementation.
|
||||
|
||||
var Translation = {
|
||||
supportedSourceLanguages: ["en", "zh", "ja", "es", "de", "fr", "ru", "ar", "ko", "pt"],
|
||||
supportedTargetLanguages: ["en", "pl", "tr", "vi"],
|
||||
defaultTargetLanguage: "en",
|
||||
|
||||
_translateFrom: "",
|
||||
_translateTo: "",
|
||||
_deferred: null,
|
||||
translate: function(aFrom, aTo) {
|
||||
this._translateFrom = aFrom;
|
||||
this._translateTo = aTo;
|
||||
this._deferred = Promise.defer();
|
||||
return this._deferred.promise;
|
||||
},
|
||||
|
||||
_reset: function() {
|
||||
this._translateFrom = "";
|
||||
this._translateTo = "";
|
||||
this._deferred = null;
|
||||
},
|
||||
|
||||
failTranslation: function() {
|
||||
this._deferred.reject();
|
||||
this._reset();
|
||||
},
|
||||
|
||||
finishTranslation: function() {
|
||||
this._deferred.resolve();
|
||||
this._reset();
|
||||
},
|
||||
|
||||
_showOriginalCalled: false,
|
||||
showOriginalContent: function() {
|
||||
this._showOriginalCalled = true;
|
||||
},
|
||||
|
||||
_showTranslationCalled: false,
|
||||
showTranslatedContent: function() {
|
||||
this._showTranslationCalled = true;
|
||||
},
|
||||
|
||||
showTranslationUI: function(aLanguage) {
|
||||
let notificationBox = gBrowser.getNotificationBox();
|
||||
let notif = notificationBox.appendNotification("", "translation", null,
|
||||
notificationBox.PRIORITY_INFO_HIGH);
|
||||
notif.init(this);
|
||||
notif.detectedLanguage = aLanguage;
|
||||
return notif;
|
||||
}
|
||||
};
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
// Show an info bar saying the current page is in French
|
||||
let notif = Translation.showTranslationUI("fr");
|
||||
is(notif.state, notif.STATE_OFFER, "the infobar is offering translation");
|
||||
is(notif._getAnonElt("detectedLanguage").value, "fr", "The detected language is displayed");
|
||||
|
||||
// Click the "Translate" button
|
||||
notif._getAnonElt("translate").click();
|
||||
is(notif.state, notif.STATE_TRANSLATING, "the infobar is in the translating state");
|
||||
ok(!!Translation._deferred, "Translation.translate has been called");
|
||||
is(Translation._translateFrom, "fr", "from language correct");
|
||||
is(Translation._translateTo, Translation.defaultTargetLanguage, "from language correct");
|
||||
|
||||
// Make the translation fail and check we are in the error state.
|
||||
Translation.failTranslation();
|
||||
is(notif.state, notif.STATE_ERROR, "infobar in the error state");
|
||||
|
||||
// Click the try again button
|
||||
notif._getAnonElt("tryAgain").click();
|
||||
is(notif.state, notif.STATE_TRANSLATING, "infobar in the translating state");
|
||||
ok(!!Translation._deferred, "Translation.translate has been called");
|
||||
is(Translation._translateFrom, "fr", "from language correct");
|
||||
is(Translation._translateTo, Translation.defaultTargetLanguage, "to language correct");
|
||||
|
||||
// Make the translation succeed and check we are in the 'translated' state.
|
||||
Translation.finishTranslation();
|
||||
is(notif.state, notif.STATE_TRANSLATED, "infobar in the translated state");
|
||||
|
||||
// Test 'Show Original' / 'Show Translation' buttons.
|
||||
// First check 'Show Original' is visible and 'Show Translation' is hidden.
|
||||
ok(!notif._getAnonElt("showOriginal").hidden, "'Show Original' button visible");
|
||||
ok(notif._getAnonElt("showTranslation").hidden, "'Show Translation' button hidden");
|
||||
// Click the button.
|
||||
notif._getAnonElt("showOriginal").click();
|
||||
// Check the correct function has been called.
|
||||
ok(Translation._showOriginalCalled, "'Translation.showOriginalContent' called")
|
||||
ok(!Translation._showTranslationCalled, "'Translation.showTranslatedContent' not called")
|
||||
Translation._showOriginalCalled = false;
|
||||
// And the 'Show Translation' button is now visible.
|
||||
ok(notif._getAnonElt("showOriginal").hidden, "'Show Original' button hidden");
|
||||
ok(!notif._getAnonElt("showTranslation").hidden, "'Show Translation' button visible");
|
||||
// Click the 'Show Translation' button
|
||||
notif._getAnonElt("showTranslation").click();
|
||||
// Check the correct function has been called.
|
||||
ok(!Translation._showOriginalCalled, "'Translation.showOriginalContent' not called")
|
||||
ok(Translation._showTranslationCalled, "'Translation.showTranslatedContent' called")
|
||||
Translation._showTranslationCalled = false;
|
||||
// Check that the 'Show Original' button is visible again.
|
||||
ok(!notif._getAnonElt("showOriginal").hidden, "'Show Original' button visible");
|
||||
ok(notif._getAnonElt("showTranslation").hidden, "'Show Translation' button hidden");
|
||||
|
||||
// Check that changing the source language causes a re-translation
|
||||
let from = notif._getAnonElt("fromLanguage");
|
||||
from.value = "es";
|
||||
from.doCommand();
|
||||
is(notif.state, notif.STATE_TRANSLATING, "infobar in the translating state");
|
||||
ok(!!Translation._deferred, "Translation.translate has been called");
|
||||
is(Translation._translateFrom, "es", "from language correct");
|
||||
is(Translation._translateTo, Translation.defaultTargetLanguage, "to language correct");
|
||||
Translation.finishTranslation();
|
||||
|
||||
// Check that changing the target language causes a re-translation
|
||||
let to = notif._getAnonElt("toLanguage");
|
||||
to.value = "pl";
|
||||
to.doCommand();
|
||||
is(notif.state, notif.STATE_TRANSLATING, "infobar in the translating state");
|
||||
ok(!!Translation._deferred, "Translation.translate has been called");
|
||||
is(Translation._translateFrom, "es", "from language correct");
|
||||
is(Translation._translateTo, "pl", "to language correct");
|
||||
Translation.finishTranslation();
|
||||
|
||||
// Cleanup.
|
||||
notif.close();
|
||||
|
||||
// Reopen the info bar to check that it's possible to override the detected language.
|
||||
notif = Translation.showTranslationUI("fr");
|
||||
is(notif.state, notif.STATE_OFFER, "the infobar is offering translation");
|
||||
is(notif._getAnonElt("detectedLanguage").value, "fr", "The detected language is displayed");
|
||||
// Change the language and click 'Translate'
|
||||
notif._getAnonElt("detectedLanguage").value = "ja";
|
||||
notif._getAnonElt("translate").click();
|
||||
is(notif.state, notif.STATE_TRANSLATING, "the infobar is in the translating state");
|
||||
ok(!!Translation._deferred, "Translation.translate has been called");
|
||||
is(Translation._translateFrom, "ja", "from language correct");
|
||||
notif.close();
|
||||
|
||||
// Reopen one last time to check the 'Not Now' button closes the notification.
|
||||
notif = Translation.showTranslationUI("fr");
|
||||
|
||||
let notificationBox = gBrowser.getNotificationBox();
|
||||
ok(!!notificationBox.getNotificationWithValue("translation"), "there's a 'translate' notification");
|
||||
notif._getAnonElt("notNow").click();
|
||||
ok(!notificationBox.getNotificationWithValue("translation"), "no 'translate' notification after clicking 'not now'");
|
||||
|
||||
finish();
|
||||
}
|
|
@ -0,0 +1,196 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE bindings [
|
||||
<!ENTITY % notificationDTD SYSTEM "chrome://global/locale/notification.dtd">
|
||||
%notificationDTD;
|
||||
<!ENTITY % translationDTD SYSTEM "chrome://browser/locale/translation.dtd" >
|
||||
%translationDTD;
|
||||
]>
|
||||
|
||||
<bindings id="translationBindings"
|
||||
xmlns="http://www.mozilla.org/xbl"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:xbl="http://www.mozilla.org/xbl">
|
||||
<binding id="translationbar" extends="chrome://global/content/bindings/notification.xml#notification" role="xul:alert">
|
||||
<resources>
|
||||
<stylesheet src="chrome://global/skin/notification.css"/>
|
||||
</resources>
|
||||
<content>
|
||||
<xul:hbox class="notification-inner outset" flex="1" xbl:inherits="type">
|
||||
<xul:hbox anonid="details" align="center" flex="1">
|
||||
<xul:image anonid="messageImage" class="messageImage" xbl:inherits="src=image,type,value"/>
|
||||
<xul:deck anonid="translationStates" selectedIndex="0">
|
||||
|
||||
<!-- offer to translate -->
|
||||
<xul:hbox class="translate-offer-box" align="baseline">
|
||||
<xul:label value="&translation.thisPageIsIn.label;"/>
|
||||
<xul:menulist anonid="detectedLanguage">
|
||||
<xul:menupopup/>
|
||||
</xul:menulist>
|
||||
<xul:label value="&translation.translateThisPage.label;"/>
|
||||
<xul:button label="&translation.translate.button;" anonid="translate"
|
||||
oncommand="document.getBindingParent(this).translate();"/>
|
||||
<xul:button label="&translation.notNow.button;" anonid="notNow"
|
||||
oncommand="document.getBindingParent(this).close();"/>
|
||||
</xul:hbox>
|
||||
|
||||
<!-- translating -->
|
||||
<xul:vbox class="translating-box" pack="center">
|
||||
<xul:label value="&translation.translatingContent.label;"/>
|
||||
</xul:vbox>
|
||||
|
||||
<!-- translated -->
|
||||
<xul:hbox class="translated-box" align="baseline">
|
||||
<xul:label value="&translation.translatedFrom.label;"/>
|
||||
<xul:menulist anonid="fromLanguage"
|
||||
oncommand="document.getBindingParent(this).translate()">
|
||||
<xul:menupopup/>
|
||||
</xul:menulist>
|
||||
<xul:label value="&translation.translatedTo.label;"/>
|
||||
<xul:menulist anonid="toLanguage"
|
||||
oncommand="document.getBindingParent(this).translate()">
|
||||
<xul:menupopup/>
|
||||
</xul:menulist>
|
||||
<xul:button anonid="showOriginal"
|
||||
label="&translation.showOriginal.button;"
|
||||
oncommand="document.getBindingParent(this).showOriginal();"/>
|
||||
<xul:button anonid="showTranslation"
|
||||
label="&translation.showTranslation.button;"
|
||||
oncommand="document.getBindingParent(this).showTranslation();"/>
|
||||
</xul:hbox>
|
||||
|
||||
<!-- error -->
|
||||
<xul:hbox class="translation-error" align="baseline">
|
||||
<xul:label value="&translation.errorTranslating.label;"/>
|
||||
<xul:button label="&translation.tryAgain.button;" anonid="tryAgain"
|
||||
oncommand="document.getBindingParent(this).translate();"/>
|
||||
</xul:hbox>
|
||||
|
||||
</xul:deck>
|
||||
<xul:spacer flex="1"/>
|
||||
|
||||
<xul:button type="menu" label="&translation.options.menu;">
|
||||
<xul:menupopup/>
|
||||
</xul:button>
|
||||
|
||||
</xul:hbox>
|
||||
<xul:toolbarbutton ondblclick="event.stopPropagation();"
|
||||
class="messageCloseButton close-icon tabbable"
|
||||
xbl:inherits="hidden=hideclose"
|
||||
tooltiptext="&closeNotification.tooltip;"
|
||||
oncommand="document.getBindingParent(this).close();"/>
|
||||
</xul:hbox>
|
||||
</content>
|
||||
<implementation>
|
||||
<field name="STATE_OFFER" readonly="true">0</field>
|
||||
<field name="STATE_TRANSLATING" readonly="true">1</field>
|
||||
<field name="STATE_TRANSLATED" readonly="true">2</field>
|
||||
<field name="STATE_ERROR" readonly="true">3</field>
|
||||
|
||||
<property name="state"
|
||||
onget="return this._getAnonElt('translationStates').selectedIndex;"
|
||||
onset="this._getAnonElt('translationStates').selectedIndex = val;"/>
|
||||
|
||||
<!-- Initialize the infobar with a translation object exposing these
|
||||
properties:
|
||||
- supportedSourceLanguages, array of supported source language codes
|
||||
- supportedTargetLanguages, array of supported target language codes
|
||||
- defaultTargetLanguage, code of the language to use by default for
|
||||
translation.
|
||||
- translate, method starting the translation of the current page.
|
||||
Returns a promise.
|
||||
- showOriginalContent, method showing the original page content.
|
||||
- showTranslatedContent, method showing the translation for an
|
||||
already translated page whose original content is shown.
|
||||
-->
|
||||
<method name="init">
|
||||
<parameter name="aTranslation"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
this.translation = aTranslation;
|
||||
let bundle = Cc["@mozilla.org/intl/stringbundle;1"]
|
||||
.getService(Ci.nsIStringBundleService)
|
||||
.createBundle("chrome://global/locale/languageNames.properties");
|
||||
|
||||
let detectedLanguage = this._getAnonElt("detectedLanguage");
|
||||
let fromLanguage = this._getAnonElt("fromLanguage");
|
||||
for (let code of this.translation.supportedSourceLanguages) {
|
||||
let name = bundle.GetStringFromName(code);
|
||||
detectedLanguage.appendItem(name, code);
|
||||
fromLanguage.appendItem(name, code);
|
||||
}
|
||||
|
||||
let toLanguage = this._getAnonElt("toLanguage");
|
||||
for (let code of this.translation.supportedTargetLanguages)
|
||||
toLanguage.appendItem(bundle.GetStringFromName(code), code);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="_getAnonElt">
|
||||
<parameter name="aAnonId"/>
|
||||
<body>
|
||||
return document.getAnonymousElementByAttribute(this, "anonid", aAnonId);
|
||||
</body>
|
||||
</method>
|
||||
|
||||
|
||||
<field name="_detectedLanguage">""</field>
|
||||
<property name="detectedLanguage" onget="return this._detectedLanguage;">
|
||||
<setter><![CDATA[
|
||||
this._getAnonElt("detectedLanguage").value = val;
|
||||
this._detectedLanguage = val;
|
||||
return val;
|
||||
]]></setter>
|
||||
</property>
|
||||
|
||||
<method name="translate">
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (this.state == this.STATE_OFFER) {
|
||||
this._getAnonElt("fromLanguage").value =
|
||||
this._getAnonElt("detectedLanguage").value;
|
||||
this._getAnonElt("toLanguage").value =
|
||||
this.translation.defaultTargetLanguage;
|
||||
}
|
||||
|
||||
this._getAnonElt("showOriginal").hidden = false;
|
||||
this._getAnonElt("showTranslation").hidden = true;
|
||||
|
||||
this.state = this.STATE_TRANSLATING;
|
||||
this.translation.translate(this._getAnonElt("fromLanguage").value,
|
||||
this._getAnonElt("toLanguage").value)
|
||||
.then(() => { this.state = this.STATE_TRANSLATED; },
|
||||
() => { this.state = this.STATE_ERROR; });
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="showOriginal">
|
||||
<body>
|
||||
<![CDATA[
|
||||
this._getAnonElt("showOriginal").hidden = true;
|
||||
this._getAnonElt("showTranslation").hidden = false;
|
||||
|
||||
this.translation.showOriginalContent();
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="showTranslation">
|
||||
<body>
|
||||
<![CDATA[
|
||||
this._getAnonElt("showOriginal").hidden = false;
|
||||
this._getAnonElt("showTranslation").hidden = true;
|
||||
|
||||
this.translation.showTranslatedContent();
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
</implementation>
|
||||
</binding>
|
||||
</bindings>
|
|
@ -0,0 +1,20 @@
|
|||
<!-- 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/. -->
|
||||
|
||||
<!ENTITY translation.thisPageIsIn.label "This page is in">
|
||||
<!ENTITY translation.translateThisPage.label "Translate this page?">
|
||||
<!ENTITY translation.translate.button "Translate">
|
||||
<!ENTITY translation.notNow.button "Not Now">
|
||||
|
||||
<!ENTITY translation.translatingContent.label "Translating page content…">
|
||||
|
||||
<!ENTITY translation.translatedFrom.label "This page has been translated from">
|
||||
<!ENTITY translation.translatedTo.label "to">
|
||||
<!ENTITY translation.showOriginal.button "Show Original">
|
||||
<!ENTITY translation.showTranslation.button "Show Translation">
|
||||
|
||||
<!ENTITY translation.errorTranslating.label "There has been an error translating this page.">
|
||||
<!ENTITY translation.tryAgain.button "Try Again">
|
||||
|
||||
<!ENTITY translation.options.menu "Options">
|
|
@ -76,6 +76,7 @@
|
|||
locale/browser/tabbrowser.properties (%chrome/browser/tabbrowser.properties)
|
||||
locale/browser/tabview.properties (%chrome/browser/tabview.properties)
|
||||
locale/browser/taskbar.properties (%chrome/browser/taskbar.properties)
|
||||
locale/browser/translation.dtd (%chrome/browser/translation.dtd)
|
||||
locale/browser/downloads/downloads.dtd (%chrome/browser/downloads/downloads.dtd)
|
||||
locale/browser/downloads/downloads.properties (%chrome/browser/downloads/downloads.properties)
|
||||
locale/browser/places/places.dtd (%chrome/browser/places/places.dtd)
|
||||
|
|
Загрузка…
Ссылка в новой задаче