Bug 1829687 - Apply UX feedback to consolidate translation panel views; r=nordzilla,fluent-reviewers,flod

UX changed their mind on how the default translation panel should work.
I originally pushed back against getting this done for MVP, but the
logic is much easier when the translation panel can be manually invoked
by the user.

Differential Revision: https://phabricator.services.mozilla.com/D179087
This commit is contained in:
Greg Tatum 2023-05-27 16:24:20 +00:00
parent 7ce36dd8a6
commit d729409e1b
4 changed files with 329 additions and 298 deletions

View File

@ -10,18 +10,16 @@
noautofocus="true"
aria-labelledby="translations-panel-main-header-label"
orient="vertical">
<panelmultiview id="translations-panel-multiview">
<!-- Default page -->
<panelmultiview id="translations-panel-multiview"
mainViewId="translations-panel-view-default">
<panelview id="translations-panel-view-default"
class="PanelUI-subView translations-panel-view"
role="document"
showheader="true">
<hbox class="panel-header translations-panel-header">
<html:h1>
<html:span data-l10n-id="translations-panel-default-header"></html:span>
<html:span id="translations-panel-header"></html:span>
</html:h1>
<!-- The "toolbarbutton#translations-panel-settings" will get cloned into other
.panel-header elements. -->
<toolbarbutton id="translations-panel-settings" class="panel-info-button"
data-l10n-id="translations-panel-settings-button"
closemenu="none"
@ -47,9 +45,6 @@
autocheck="false"
oncommand="TranslationsPanel.onNeverTranslateSite()"/>
<menuseparator/>
<menuitem data-l10n-id="translations-panel-settings-change-source-language"
class="translations-panel-change-source"
oncommand="TranslationsPanel.showDualView()"/>
<menuitem data-l10n-id="translations-panel-settings-manage-languages"
oncommand="TranslationsPanel.openManageLanguages()"/>
</menupopup>
@ -57,129 +52,94 @@
</hbox>
<vbox class="translations-panel-content">
<description id="translations-panel-default-description"></description>
<label data-l10n-id="translations-panel-default-translate-to-label"></label>
<menulist id="translations-panel-default-to"
flex="1"
value="detect"
size="large">
<menupopup id="translations-panel-default-to-menupopup"
class="translations-panel-language-menupopup-to">
<!-- The list of <menuitem> will be dynamically inserted. -->
</menupopup>
</menulist>
<vbox id="translations-panel-lang-selection">
<label data-l10n-id="translations-panel-from-label"></label>
<menulist id="translations-panel-from"
flex="1"
value="detect"
size="large"
oncommand="TranslationsPanel.onChangeLanguages(event)">
<menupopup id="translations-panel-from-menupopup"
class="translations-panel-language-menupopup-from">
<menuitem data-l10n-id="translations-panel-choose-language" value=""></menuitem>
<!-- The list of <menuitem> will be dynamically inserted. -->
</menupopup>
</menulist>
<label data-l10n-id="translations-panel-to-label"></label>
<menulist id="translations-panel-to"
flex="1"
value="detect"
size="large"
oncommand="TranslationsPanel.onChangeLanguages(event)">
<menupopup id="translations-panel-to-menupopup"
class="translations-panel-language-menupopup-to">
<menuitem data-l10n-id="translations-panel-choose-language" value=""></menuitem>
<!-- The list of <menuitem> will be dynamically inserted. -->
</menupopup>
</menulist>
</vbox>
<vbox id="translations-panel-error">
<hbox class="translations-panel-error-header">
<image class="translations-panel-error-icon translations-panel-error-header-icon" />
<description id="translations-panel-error-message"></description>
</hbox>
<hbox id="translations-panel-error-message-hint"></hbox>
<hbox pack="end">
<button id="translations-panel-translate-hint-action" />
</hbox>
</vbox>
</vbox>
<hbox class="panel-footer translations-panel-footer">
<hbox id="translations-panel-error">
<image class="translations-panel-error-icon" />
<description id="translations-panel-error-message"></description>
</hbox>
<button class="subviewbutton panel-subview-footer-button"
id="translations-panel-not-now"
oncommand="TranslationsPanel.onCancel(event);"
data-l10n-id="translations-panel-default-translate-cancel"
tabindex="0">
</button>
<button default="true"
<button id="translations-panel-restore-button"
class="subviewbutton panel-subview-footer-button"
oncommand="TranslationsPanel.onDefaultTranslate(event);"
data-l10n-id="translations-panel-default-translate-button"
oncommand="TranslationsPanel.onRestore(event);"
data-l10n-id="translations-panel-restore-button"
tabindex="0">
</button>
</hbox>
</panelview>
<!-- Dual language selection view -->
<panelview id="translations-panel-view-dual"
class="PanelUI-subView translations-panel-view"
role="document"
data-l10n-id="translations-panel-dual-header">
<vbox class="translations-panel-content">
<label data-l10n-id="translations-panel-dual-from-label"></label>
<menulist id="translations-panel-dual-from"
flex="1"
value="detect"
size="large"
oncommand="TranslationsPanel.onChangeDualLanguages(event)">
<menupopup id="translations-panel-dual-from-menupopup"
class="translations-panel-language-menupopup-from">
<menuitem data-l10n-id="translations-panel-choose-language" value=""></menuitem>
<!-- The list of <menuitem> will be dynamically inserted. -->
</menupopup>
</menulist>
<label data-l10n-id="translations-panel-dual-to-label"></label>
<menulist id="translations-panel-dual-to"
flex="1"
value="detect"
size="large"
oncommand="TranslationsPanel.onChangeDualLanguages(event)">
<menupopup id="translations-panel-dual-to-menupopup"
class="translations-panel-language-menupopup-to">
<!-- The list of <menuitem> will be dynamically inserted. -->
</menupopup>
</menulist>
</vbox>
<hbox class="panel-footer translations-panel-footer">
<button class="subviewbutton panel-subview-footer-button"
id="translations-panel-not-now"
<button id="translations-panel-not-now"
class="subviewbutton panel-subview-footer-button"
oncommand="TranslationsPanel.onCancel(event);"
data-l10n-id="translations-panel-dual-cancel-button"
data-l10n-id="translations-panel-translate-cancel"
tabindex="0">
</button>
<button id="translations-panel-dual-translate"
<button id="translations-panel-translate"
class="subviewbutton panel-subview-footer-button"
oncommand="TranslationsPanel.onTranslate(event);"
data-l10n-id="translations-panel-translate-button"
default="true"
class="subviewbutton panel-subview-footer-button"
oncommand="TranslationsPanel.onDualTranslate(event);"
data-l10n-id="translations-panel-default-translate-button"
tabindex="0">
</button>
</hbox>
</panelview>
<!-- Revisit the translations panel -->
<panelview id="translations-panel-view-revisit"
<panelview id="translations-panel-view-unsupported-language"
class="PanelUI-subView translations-panel-view"
role="document"
showheader="true">
<hbox class="panel-header translations-panel-header">
<image class="translations-panel-error-icon" />
<html:h1>
<html:span id="translations-panel-revisit-header">
<!-- The "to" and "from" languages will be set here -->
</html:span>
<html:span data-l10n-id="translations-panel-error-unsupported"></html:span>
</html:h1>
<!-- The "toolbarbutton#translations-panel-settings" gets cloned here. -->
</hbox>
<vbox class="translations-panel-content">
<label id="translations-panel-revisit-label"
data-l10n-id="translations-panel-revisit-label">
</label>
<menulist id="translations-panel-revisit-to"
flex="1"
value="detect"
size="large"
oncommand="TranslationsPanel.onChangeRevisitTo(event)">
<menupopup class="translations-panel-language-menupopup-to">
<menuitem data-l10n-id="translations-panel-choose-language" value=""></menuitem>
<!-- The list of <menuitem> will be dynamically inserted. -->
</menupopup>
</menulist>
<description id="translations-panel-error-unsupported-hint"></description>
</vbox>
<hbox class="panel-footer translations-panel-footer">
<button class="subviewbutton panel-subview-footer-button"
oncommand="TranslationsPanel.onRestore(event);"
data-l10n-id="translations-panel-revisit-restore-button"
oncommand="TranslationsPanel.onChangeSourceLanguage(event);"
data-l10n-id="translations-panel-error-change-button"
tabindex="0">
</button>
<button id="translations-panel-revisit-translate"
<button class="subviewbutton panel-subview-footer-button"
oncommand="TranslationsPanel.onCancel(event);"
data-l10n-id="translations-panel-error-dismiss-button"
default="true"
class="subviewbutton panel-subview-footer-button"
oncommand="TranslationsPanel.onRevisitTranslate(event);"
data-l10n-id="translations-panel-revisit-translate-button"
tabindex="0">
</button>
</hbox>

View File

@ -100,26 +100,69 @@ var TranslationsPanel = new (class {
});
};
getter("appMenuButton", "PanelUI-menu-button");
getter("button", "translations-button");
getter("buttonLocale", "translations-button-locale");
getter("buttonCircleArrows", "translations-button-circle-arrows");
getter("defaultDescription", "translations-panel-default-description");
getter("defaultToMenuList", "translations-panel-default-to");
getter("dualFromMenuList", "translations-panel-dual-from");
getter("dualToMenuList", "translations-panel-dual-to");
getter("dualTranslate", "translations-panel-dual-translate");
getter("defaultTranslate", "translations-panel-translate");
getter("error", "translations-panel-error");
getter("errorMessage", "translations-panel-error-message");
getter("errorMessageHint", "translations-panel-error-message-hint");
getter("errorHintAction", "translations-panel-translate-hint-action");
getter("fromMenuList", "translations-panel-from");
getter("header", "translations-panel-header");
getter("langSelection", "translations-panel-lang-selection");
getter("multiview", "translations-panel-multiview");
getter("notNow", "translations-panel-not-now");
getter("revisitHeader", "translations-panel-revisit-header");
getter("revisitMenuList", "translations-panel-revisit-to");
getter("revisitTranslate", "translations-panel-revisit-translate");
getter("notNowButton", "translations-panel-not-now");
getter("restoreButton", "translations-panel-restore-button");
getter("toMenuList", "translations-panel-to");
getter("unsupportedHint", "translations-panel-error-unsupported-hint");
}
return this.#lazyElements;
}
/**
* Cache the last command used for error hints so that it can be later removed.
*/
#lastHintCommand = null;
/**
* @param {object} options
* @param {string} options.message - l10n id
* @param {string} options.hint - l10n id
* @param {string} options.actionText - l10n id
* @param {Function} options.actionCommand - The action to perform.
*/
#showError({
message,
hint,
actionText: hintCommandText,
actionCommand: hintCommand,
}) {
const { error, errorMessage, errorMessageHint, errorHintAction } =
this.elements;
error.hidden = false;
document.l10n.setAttributes(errorMessage, message);
if (hint) {
errorMessageHint.hidden = false;
document.l10n.setAttributes(errorMessageHint, hint);
} else {
errorMessageHint.hidden = true;
}
if (hintCommand && hintCommandText) {
errorHintAction.removeEventListener("command", this.#lastHintCommand);
this.#lastHintCommand = hintCommand;
errorHintAction.addEventListener("command", hintCommand);
errorHintAction.hidden = false;
document.l10n.setAttributes(errorHintAction, hintCommandText);
} else {
errorHintAction.hidden = true;
}
}
/**
* @returns {TranslationsParent}
*/
@ -250,101 +293,111 @@ var TranslationsPanel = new (class {
}
/**
* Switch to the dual language view of choosing a source and target language.
* Show the default view of choosing a source and target language.
*
* @param {boolean} force - Force the page to show translation options.
*/
showDualView() {
async #showDefaultView(force = false) {
const {
dualTranslate,
dualFromMenuList,
dualToMenuList,
fromMenuList,
multiview,
defaultToMenuList,
panel,
error,
toMenuList,
defaultTranslate,
langSelection,
} = this.elements;
// Remove any old selected values synchronously before asking for new ones.
dualFromMenuList.value = "";
dualToMenuList.value = defaultToMenuList.value;
// Disable this button since the user must choose a new "from" language.
dualTranslate.disabled = true;
if (this.#langListsPhase === "error") {
// There was an error, display it in the view rather than the language
// dropdowns.
const { restoreButton, notNowButton, header, errorHintAction } =
this.elements;
multiview.showSubView("translations-panel-view-dual");
this.#showError({
message: "translations-panel-error-load-languages",
hint: "translations-panel-error-load-languages-hint",
actionText: "translations-panel-error-load-languages-hint-button",
actionCommand: () => this.#reloadLangList(),
});
document.l10n.setAttributes(header, "translations-panel-header");
defaultTranslate.disabled = true;
restoreButton.hidden = true;
notNowButton.hidden = false;
langSelection.hidden = true;
errorHintAction.disabled = false;
return;
}
// Remove any old selected values synchronously before asking for new ones.
fromMenuList.value = "";
error.hidden = true;
langSelection.hidden = false;
/** @type {null | LangTags} */
const langTags = await this.#fetchDetectedLanguages();
if (langTags?.isDocLangTagSupported || force) {
// Show the default view with the language selection
const { header, restoreButton, notNowButton } = this.elements;
document.l10n.setAttributes(header, "translations-panel-header");
if (langTags?.isDocLangTagSupported) {
fromMenuList.value = langTags?.docLangTag ?? "";
} else {
fromMenuList.value = "";
}
toMenuList.value = langTags?.userLangTag ?? "";
this.onChangeLanguages();
restoreButton.hidden = true;
notNowButton.hidden = false;
multiview.setAttribute("mainViewId", "translations-panel-view-default");
} else {
// Show the "unsupported language" view.
const { unsupportedHint } = this.elements;
multiview.setAttribute(
"mainViewId",
"translations-panel-view-unsupported-language"
);
let language;
if (langTags?.docLangTag) {
const displayNames = new Intl.DisplayNames(undefined, {
type: "language",
fallback: "none",
});
language = displayNames.of(langTags.docLangTag);
}
if (language) {
document.l10n.setAttributes(
unsupportedHint,
"translations-panel-error-unsupported-hint-known",
{ language }
);
} else {
document.l10n.setAttributes(
unsupportedHint,
"translations-panel-error-unsupported-hint-unknown"
);
}
}
// Focus the "from" language, as it is the only field not set.
panel.addEventListener(
"ViewShown",
() => {
dualFromMenuList.focus();
if (!fromMenuList.value) {
fromMenuList.focus();
}
if (!toMenuList.value) {
toMenuList.focus();
}
},
{ once: true }
);
}
/**
* Builds the <menulist> of languages for both the "from" and "to". This can be
* called every time the popup is shown, as it will retry when there is an error
* (such as a network error) or be a noop if it's already initialized.
*/
async #showDefaultView() {
await this.#ensureLangListsBuilt();
const actor = this.#getTranslationsActor();
const { defaultToMenuList, defaultDescription, multiview } = this.elements;
multiview.setAttribute("mainViewId", "translations-panel-view-default");
// Remove any old selected values synchronously before asking for new ones.
defaultToMenuList.value = "";
// TODO(Bug 1825801) - There is a race condition, we may download the languages, and
// later trigger the subview to be shown after opening the popup again. We need to
// properly handle this.
// TODO(Bug 1825801) - This could potentially be a bad pause, as we aren't showing
// the panel until the language list is ready. It's probably fine for a prototype,
// but should be handled for the MVP. We might want design direction here, as we need
// a subview for when the language list is still being retrieved.
/** @type {null | LangTags} */
const langTags = await actor.getLangTagsForTranslation();
if (langTags) {
const { docLangTag, appLangTag } = langTags;
defaultToMenuList.value = appLangTag;
this.#docLangTag = docLangTag;
} else {
// TODO(Bug 1829687): Handle the case when we don't have the document langauge tag
// which can only be triggered when the panel is shown manually. Currently
// this will never be shown.
this.#docLangTag = "en";
this.console.error("No language tags for translation were found.");
}
// Show the default view.
const displayNames = new Services.intl.DisplayNames(undefined, {
type: "language",
});
document.l10n.setAttributes(
defaultDescription,
"translations-panel-default-description",
{
pageLanguage: displayNames.of(this.#docLangTag),
}
);
if (!this.#wasPanelShown) {
// Note if a profile has used translations before, we may want to include additional
// messaging for first time users.
this.#wasPanelShown = true;
Services.prefs.setBoolPref("browser.translations.panel.wasShown", true);
}
for (const menuitem of defaultToMenuList.querySelectorAll("menuitem")) {
// It is not valid to translate into the original doc language.
menuitem.disabled = menuitem.value === this.#docLangTag;
}
}
/**
* Updates the checked states of the settings menu checkboxes that
* pertain to languages.
@ -482,31 +535,24 @@ var TranslationsPanel = new (class {
* @param {TranslationPair} translationPair
*/
async #showRevisitView({ fromLanguage, toLanguage }) {
const { multiview, revisitHeader, revisitMenuList, revisitTranslate } =
const { header, fromMenuList, toMenuList, restoreButton, notNowButton } =
this.elements;
await this.#ensureLangListsBuilt();
fromMenuList.value = fromLanguage;
toMenuList.value = toLanguage;
this.onChangeLanguages();
revisitMenuList.value = "";
revisitTranslate.disabled = true;
multiview.setAttribute("mainViewId", "translations-panel-view-revisit");
restoreButton.hidden = false;
notNowButton.hidden = true;
const displayNames = new Services.intl.DisplayNames(undefined, {
type: "language",
});
for (const menuitem of revisitMenuList.querySelectorAll("menuitem")) {
menuitem.disabled = menuitem.value === toLanguage;
}
document.l10n.setAttributes(
revisitHeader,
"translations-panel-revisit-header",
{
fromLanguage: displayNames.of(fromLanguage),
toLanguage: displayNames.of(toLanguage),
}
);
document.l10n.setAttributes(header, "translations-panel-revisit-header", {
fromLanguage: displayNames.of(fromLanguage),
toLanguage: displayNames.of(toLanguage),
});
}
/**
@ -522,13 +568,43 @@ var TranslationsPanel = new (class {
* When changing the "dual" view's language, handle cases where the translate button
* should be disabled.
*/
onChangeDualLanguages() {
const { dualTranslate, dualToMenuList, dualFromMenuList } = this.elements;
dualTranslate.disabled =
onChangeLanguages() {
const { defaultTranslate, toMenuList, fromMenuList } = this.elements;
defaultTranslate.disabled =
// The translation languages are the same, don't allow this translation.
dualToMenuList.value === dualFromMenuList.value ||
toMenuList.value === fromMenuList.value ||
// No "to" language was provided.
!toMenuList.value ||
// No "from" language was provided.
!dualFromMenuList.value;
!fromMenuList.value;
}
/**
* When a language is not supported and the menu is manually invoked, an error message
* is shown. This method switches the panel back to the language selection view.
* Note that this bypasses the showSubView method since the main view doesn't support
* a subview.
*/
async onChangeSourceLanguage(event) {
const { panel } = this.elements;
panel.addEventListener("popuphidden", async () => {}, { once: true });
PanelMultiView.hidePopup(panel);
await this.#showDefaultView(true /* force this view to be shown */);
PanelMultiView.openPopup(panel, this.elements.appMenuButton, {
position: "bottomright topright",
triggeringEvent: event,
}).catch(error => this.console.error(error));
}
async #reloadLangList() {
try {
await this.#ensureLangListsBuilt();
await this.#showDefaultView();
} catch (error) {
this.elements.errorHintAction.disabled = false;
}
}
/**
@ -539,6 +615,8 @@ var TranslationsPanel = new (class {
async open(event) {
const { panel, button } = this.elements;
await this.#ensureLangListsBuilt();
const { requestedTranslationPair } =
this.#getTranslationsActor().languageState;
@ -553,7 +631,12 @@ var TranslationsPanel = new (class {
}
this.#populateSettingsMenuItems();
PanelMultiView.openPopup(panel, button, {
const targetButton = button.contains(event.target)
? button
: this.elements.appMenuButton;
PanelMultiView.openPopup(panel, targetButton, {
position: "bottomright topright",
triggerEvent: event,
}).catch(error => this.console.error(error));
@ -581,42 +664,19 @@ var TranslationsPanel = new (class {
return requestedTranslationPair !== null;
}
/**
* Handle the translation button being clicked on the default view.
*/
async onDefaultTranslate() {
PanelMultiView.hidePopup(this.elements.panel);
const actor = this.#getTranslationsActor();
const docLangTag = await this.#getDocLangTag();
actor.translate(docLangTag, this.elements.defaultToMenuList.value);
}
/**
* Handle the translation button being clicked when there are two language options.
*/
async onDualTranslate() {
async onTranslate() {
PanelMultiView.hidePopup(this.elements.panel);
const actor = this.#getTranslationsActor();
actor.translate(
this.elements.dualFromMenuList.value,
this.elements.dualToMenuList.value
this.elements.fromMenuList.value,
this.elements.toMenuList.value
);
}
/**
* Handle the translation button being clicked when the page has already been
* translated.
*/
async onRevisitTranslate() {
PanelMultiView.hidePopup(this.elements.panel);
const actor = this.#getTranslationsActor();
const docLangTag = await this.#getDocLangTag();
actor.translate(docLangTag, this.elements.revisitMenuList.value);
}
onCancel() {
PanelMultiView.hidePopup(this.elements.panel);
}
@ -656,7 +716,7 @@ var TranslationsPanel = new (class {
this.#updateSettingsMenuLanguageCheckboxStates();
if (toggledOn && !translationsActive) {
await this.onDefaultTranslate();
await this.onTranslate();
} else if (!toggledOn && translationsActive) {
await this.onRestore();
}
@ -796,18 +856,18 @@ var TranslationsPanel = new (class {
switch (error) {
case null:
this.elements.error.hidden = true;
this.elements.notNow.hidden = false;
break;
case "engine-load-failure":
this.elements.error.hidden = false;
this.elements.notNow.hidden = true;
document.l10n.setAttributes(
this.elements.errorMessage,
"translations-panel-error-translating"
);
this.#showError({
message: "translations-panel-error-translating",
});
const targetButton = button.hidden
? this.elements.appMenuButton
: button;
// Re-open the menu on an error.
PanelMultiView.openPopup(panel, button, {
PanelMultiView.openPopup(panel, targetButton, {
position: "bottomright topright",
}).catch(panelError => this.console.error(panelError));

View File

@ -19,34 +19,9 @@ translations-panel-displayname-beta =
translations-panel-settings-manage-languages =
.label = Manage languages
translations-panel-settings-change-source-language =
.label = Change source language
# TODO(Bug 1831341): We still need the link for this menu item.
translations-panel-settings-about = About translations in { -brand-shorter-name }
## The translation panel appears from the url bar, and this view is the default
## translation view.
translations-panel-default-header = Translate this page?
# If your language requires declining the language name, a possible solution
# is to adapt the structure of the phrase, or use a support noun, e.g.
# `Looks like this page is in another language ({ $pageLanguage }). Want to translate it?`
# Variables:
# $pageLanguage (string) - The localized display name of the page's language
translations-panel-default-description = Looks like this page is in { $pageLanguage }. Want to translate it?
# This label is followed, on a new line, by a dropdown list of language names.
# If this structure is problematic for your locale, an alternative way is to
# translate this as `Target language:`
translations-panel-default-translate-to-label = Translate to
translations-panel-default-translate-button = Translate
translations-panel-default-translate-cancel = Not now
translations-panel-error-translating = There was a problem translating. Please try again.
translations-panel-error-load-languages = Couldnt load languages
translations-panel-error-load-languages-hint = Check your internet connection and try again.
# Text displayed for the option to always translate a given language
# Variables:
# $language (string) - The localized display name of the detected language
@ -67,19 +42,36 @@ translations-panel-settings-never-translate-unknown-language =
translations-panel-settings-never-translate-site =
.label = Never translate this site
## The translation panel appears from the url bar, and this view is the "dual" translate
## view that lets you choose a source language and target language for translation
## The translation panel appears from the url bar, and this view is the default
## translation view.
translations-panel-dual-header =
.title = Translate this page?
translations-panel-dual-cancel-button = Cancel
translations-panel-header = Translate this page?
translations-panel-translate-button = Translate
translations-panel-translate-cancel = Cancel
translations-panel-error-translating = There was a problem translating. Please try again.
translations-panel-error-load-languages = Couldnt load languages
translations-panel-error-load-languages-hint = Check your internet connection and try again.
translations-panel-error-load-languages-hint-button = Try again
translations-panel-error-unsupported = Translation isnt available for this page
translations-panel-error-dismiss-button = Got it
translations-panel-error-change-button = Change source language
# If your language requires declining the language name, a possible solution
# is to adapt the structure of the phrase, or use a support noun, e.g.
# `Sorry, we don't support the language yet: { $language }
#
# Variables:
# $language (string) - The language of the document.
translations-panel-error-unsupported-hint-known = Sorry, we dont support { $language } yet.
translations-panel-error-unsupported-hint-unknown = Sorry, we dont support this language yet.
## Each label is followed, on a new line, by a dropdown list of language names.
## If this structure is problematic for your locale, an alternative way is to
## translate them as `Source language:` and `Target language:`
translations-panel-dual-from-label = Translate from
translations-panel-dual-to-label = Translate to
translations-panel-from-label = Translate from
translations-panel-to-label = Translate to
## The translation panel appears from the url bar, and this view is the "restore" view
## that lets a user restore a page to the original language, or translate into another
@ -92,12 +84,10 @@ translations-panel-dual-to-label = Translate to
# Variables:
# $fromLanguage (string) - The original language of the document.
# $toLanguage (string) - The target language of the translation.
translations-panel-revisit-header = The page is translated from { $fromLanguage } to { $toLanguage }
translations-panel-revisit-label = Want to try another language?
translations-panel-revisit-header = This page is translated from { $fromLanguage } to { $toLanguage }
translations-panel-choose-language =
.label = Choose a language
translations-panel-revisit-restore-button = Show original
translations-panel-revisit-translate-button = Translate
translations-panel-restore-button = Show original
## Firefox Translations language management in about:preferences.

View File

@ -23,16 +23,15 @@ image.translations-panel-gear-icon {
}
.translations-panel-content {
display: flex;
flex-direction: column;
padding: var(--arrowpanel-padding);
gap: var(--arrowpanel-padding);
}
.translations-panel-content > label:first-child {
#translations-panel-lang-selection > label:first-child {
margin-block-start: 0;
}
.translations-panel-content > label {
#translations-panel-lang-selection > label {
margin-block: var(--arrowpanel-padding) 6px;
}
@ -43,18 +42,35 @@ image.translations-panel-gear-icon {
background-color: var(--button-primary-bgcolor);
}
#translations-panel-error {
color: #e22850;
/* Align the error icon to the start. */
align-items: start;
#translations-panel-translate-hint-action {
appearance: none;
background-color: var(--button-bgcolor);
border-radius: 4px;
color: var(--button-color);
padding: 8px 16px;
font-size: 0.9em;
}
@media (-moz-platform: windows) {
#translations-panel-error {
/* On windows the default button in the panel-footer is flipped with the order
property. In order to keep the error on the flex-start, it also needs a -1 order. */
order: -1;
}
#translations-panel-translate-hint-action:hover {
background-color: var(--button-hover-bgcolor);
}
#translations-panel-translate-hint-action:hover:active {
background-color: var(--button-active-bgcolor);
}
#translations-panel-translate-hint-action:focus-visible {
outline: var(--focus-outline);
outline-offset: var(--focus-outline-offset);
}
#translations-panel-error-message-hint {
margin-inline-start: 21px;
margin-block: 8px;
}
#translations-panel-error-message {
font-weight: 600;
}
.translations-panel-error-icon {
@ -62,10 +78,15 @@ image.translations-panel-gear-icon {
fill: currentColor;
list-style-image: url(chrome://global/skin/icons/error.svg);
margin-inline-end: 5px;
width: 16px;
}
@media (prefers-color-scheme: dark) {
#translations-panel-error {
color: #ff9aa2;
}
.translations-panel-error-header {
align-items: start;
}
#translations-panel-error {
border: 1px solid currentColor;
border-radius: 4px;
padding: 12px;
}