Bug 1509977 - Port bugs 1469696, 1505594, 1493711, 1505594, 1509080 and 1488442. r=jorgk
Bug 1469696: Support installing official language packs from AMO Bug 1505594: Removing a requested locale puts it ahead of its label in the available dropdown Bug 1493711: Pref off downloading langpacks outside of release Bug 1505594: Put removed browser locale in the right order Bug 1509080: Clear language change confirmation content on hide Bug 1488442: Support disabled language packs in multilingual UI
This commit is contained in:
Родитель
d077228c6b
Коммит
40325e2253
|
@ -804,3 +804,6 @@ pref("intl.regional_prefs.use_os_locales", true);
|
|||
|
||||
// Multi-lingual preferences
|
||||
pref("intl.multilingual.enabled", false);
|
||||
|
||||
// We don't support yet language pack download from ATN
|
||||
pref("intl.multilingual.downloadEnabled", false);
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
<link rel="localization" href="branding/brand.ftl"/>
|
||||
<link rel="localization" href="messenger/preferences/preferences.ftl"/>
|
||||
<link rel="localization" href="messenger/preferences/fonts.ftl"/>
|
||||
<link rel="localization" href="messenger/preferences/languages.ftl"/>
|
||||
</linkset>
|
||||
|
||||
<script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
|
||||
|
|
|
@ -194,7 +194,7 @@
|
|||
<button id="manageMessengerLanguagesButton"
|
||||
class="accessory-button"
|
||||
data-l10n-id="manage-messenger-languages-button"
|
||||
oncommand="gAdvancedPane.showMessengerLanguages()"/>
|
||||
oncommand="gAdvancedPane.showMessengerLanguages({search: false})"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
<hbox id="confirmMessengerLanguage"
|
||||
|
|
|
@ -548,9 +548,18 @@ var gAdvancedPane = {
|
|||
},
|
||||
|
||||
initMessengerLocale() {
|
||||
let localeCodes = Services.locale.availableLocales;
|
||||
let localeNames = Services.intl.getLocaleDisplayNames(undefined, localeCodes);
|
||||
let locales = localeCodes.map((code, i) => ({code, name: localeNames[i]}));
|
||||
gAdvancedPane.setMessengerLocales(Services.locale.requestedLocale);
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the available list of locales and select the locale that the user
|
||||
* is "selecting". This could be the currently requested locale or a locale
|
||||
* that the user would like to switch to after confirmation.
|
||||
*/
|
||||
async setMessengerLocales(selected) {
|
||||
let available = Services.locale.availableLocales;
|
||||
let localeNames = Services.intl.getLocaleDisplayNames(undefined, available);
|
||||
let locales = available.map((code, i) => ({code, name: localeNames[i]}));
|
||||
locales.sort((a, b) => a.name > b.name);
|
||||
|
||||
let fragment = document.createDocumentFragment();
|
||||
|
@ -560,31 +569,50 @@ var gAdvancedPane = {
|
|||
menuitem.setAttribute("label", name);
|
||||
fragment.appendChild(menuitem);
|
||||
}
|
||||
|
||||
// Add an option to search for more languages if downloading is supported.
|
||||
if (Services.prefs.getBoolPref("intl.multilingual.downloadEnabled")) {
|
||||
let menuitem = document.createXULElement("menuitem");
|
||||
menuitem.id = "defaultBrowserLanguageSearch";
|
||||
menuitem.setAttribute(
|
||||
"label", await document.l10n.formatValue("messenger-languages-search"));
|
||||
menuitem.setAttribute("value", "search");
|
||||
menuitem.addEventListener("command", () => {
|
||||
gMainPane.showBrowserLanguages({search: true});
|
||||
});
|
||||
fragment.appendChild(menuitem);
|
||||
}
|
||||
|
||||
let menulist = document.getElementById("defaultMessengerLanguage");
|
||||
let menupopup = menulist.querySelector("menupopup");
|
||||
menupopup.textContent = "";
|
||||
menupopup.appendChild(fragment);
|
||||
menulist.value = Services.locale.requestedLocale;
|
||||
menulist.value = selected;
|
||||
|
||||
document.getElementById("messengerLanguagesBox").hidden = false;
|
||||
},
|
||||
|
||||
showMessengerLanguages() {
|
||||
showMessengerLanguages({search}) {
|
||||
let opts = {selected: gAdvancedPane.selectedLocales, search};
|
||||
gSubDialog.open(
|
||||
"chrome://messenger/content/preferences/messengerLanguages.xul",
|
||||
null, this.requestingLocales, this.messengerLanguagesClosed);
|
||||
null, opts, this.messengerLanguagesClosed);
|
||||
},
|
||||
|
||||
/* Show or hide the confirm change message bar based on the updated ordering. */
|
||||
messengerLanguagesClosed() {
|
||||
let requesting = this.gMessengerLanguagesDialog.requestedLocales;
|
||||
let requested = Services.locale.requestedLocales;
|
||||
let defaultMessengerLanguage = document.getElementById("defaultMessengerLanguage");
|
||||
if (requesting && requesting.join(",") != requested.join(",")) {
|
||||
gAdvancedPane.showConfirmLanguageChangeMessageBar(requesting);
|
||||
defaultMessengerLanguage.value = requesting[0];
|
||||
let selected = this.gMessengerLanguagesDialog.selected;
|
||||
let active = Services.locale.appLocalesAsBCP47;
|
||||
|
||||
// Prepare for changing the locales if they are different than the current locales.
|
||||
if (selected && selected.join(",") != active.join(",")) {
|
||||
gAdvancedPane.showConfirmLanguageChangeMessageBar(selected);
|
||||
gAdvancedPane.setMessengerLocales(selected[0]);
|
||||
return;
|
||||
}
|
||||
defaultMessengerLanguage.value = Services.locale.requestedLocale;
|
||||
|
||||
// They matched, so we can reset the UI.
|
||||
gAdvancedPane.setMessengerLocales(Services.locale.appLocaleAsBCP47);
|
||||
gAdvancedPane.hideConfirmLanguageChangeMessageBar();
|
||||
},
|
||||
|
||||
|
@ -633,13 +661,14 @@ var gAdvancedPane = {
|
|||
}
|
||||
|
||||
messageBar.hidden = false;
|
||||
this.requestingLocales = locales;
|
||||
this.selectedLocales = locales;
|
||||
},
|
||||
|
||||
hideConfirmLanguageChangeMessageBar() {
|
||||
let messageBar = document.getElementById("confirmMessengerLanguage");
|
||||
messageBar.hidden = true;
|
||||
messageBar.querySelector(".message-bar-button").removeAttribute("locales");
|
||||
let contentContainer = messageBar.querySelector(".message-bar-content-container");
|
||||
contentContainer.textContent = "";
|
||||
this.requestingLocales = null;
|
||||
},
|
||||
|
||||
|
@ -663,10 +692,14 @@ var gAdvancedPane = {
|
|||
/* Show or hide the confirm change message bar based on the new locale. */
|
||||
onMessengerLanguageChange(event) {
|
||||
let locale = event.target.value;
|
||||
if (locale == Services.locale.requestedLocale) {
|
||||
|
||||
if (locale == "search") {
|
||||
return;
|
||||
} else if (locale == Services.locale.appLocaleAsBCP47) {
|
||||
this.hideConfirmLanguageChangeMessageBar();
|
||||
return;
|
||||
}
|
||||
|
||||
let locales = Array.from(new Set([
|
||||
locale,
|
||||
...Services.locale.requestedLocales,
|
||||
|
|
|
@ -4,6 +4,23 @@
|
|||
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "AddonManager",
|
||||
"resource://gre/modules/AddonManager.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "AddonRepository",
|
||||
"resource://gre/modules/addons/AddonRepository.jsm");
|
||||
|
||||
/* This dialog provides an interface for managing what language the browser is
|
||||
* displayed in.
|
||||
*
|
||||
* There is a list of "requested" locales and a list of "available" locales. The
|
||||
* requested locales must be installed and enabled. Available locales could be
|
||||
* installed and enabled, or fetched from the AMO language tools API.
|
||||
*
|
||||
* If a langpack is disabled, there is no way to determine what locale it is for and
|
||||
* it will only be listed as available if that locale is also available on AMO and
|
||||
* the user has opted to search for more languages.
|
||||
*/
|
||||
|
||||
class OrderedListBox {
|
||||
constructor({richlistbox, upButton, downButton, removeButton, onRemove}) {
|
||||
this.richlistbox = richlistbox;
|
||||
|
@ -128,13 +145,18 @@ class OrderedListBox {
|
|||
}
|
||||
|
||||
class SortedItemSelectList {
|
||||
constructor({menulist, button, onSelect}) {
|
||||
constructor({menulist, button, onSelect, onChange, compareFn}) {
|
||||
this.menulist = menulist;
|
||||
this.popup = menulist.firstElementChild;
|
||||
this.button = button;
|
||||
this.compareFn = compareFn;
|
||||
this.items = [];
|
||||
|
||||
menulist.addEventListener("command", () => {
|
||||
button.disabled = !menulist.selectedItem;
|
||||
if (menulist.selectedItem) {
|
||||
onChange(this.items[menulist.selectedIndex]);
|
||||
}
|
||||
});
|
||||
button.addEventListener("command", () => {
|
||||
if (!menulist.selectedItem) return;
|
||||
|
@ -144,18 +166,19 @@ class SortedItemSelectList {
|
|||
menulist.setAttribute("label", menulist.getAttribute("placeholder"));
|
||||
button.disabled = true;
|
||||
menulist.disabled = menulist.itemCount == 0;
|
||||
menulist.selectedIndex = -1;
|
||||
|
||||
onSelect(item);
|
||||
});
|
||||
}
|
||||
|
||||
setItems(items) {
|
||||
this.items = items.sort((a, b) => a.label > b.label);
|
||||
this.items = items.sort(this.compareFn);
|
||||
this.populate();
|
||||
}
|
||||
|
||||
populate() {
|
||||
let {items, menulist, popup} = this;
|
||||
let {button, items, menulist, popup} = this;
|
||||
popup.textContent = "";
|
||||
|
||||
let frag = document.createDocumentFragment();
|
||||
|
@ -166,6 +189,8 @@ class SortedItemSelectList {
|
|||
|
||||
menulist.setAttribute("label", menulist.getAttribute("placeholder"));
|
||||
menulist.disabled = menulist.itemCount == 0;
|
||||
menulist.selectedIndex = -1;
|
||||
button.disabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -174,27 +199,51 @@ class SortedItemSelectList {
|
|||
* @param {object} item The item to insert.
|
||||
*/
|
||||
addItem(item) {
|
||||
let {items, menulist, popup} = this;
|
||||
let i;
|
||||
let {compareFn, items, menulist, popup} = this;
|
||||
|
||||
// Find the index of the item to insert before.
|
||||
for (i = 0; i < items.length && items[i].label < item.label; i++)
|
||||
;
|
||||
|
||||
let i = items.findIndex(el => compareFn(el, item) >= 0);
|
||||
items.splice(i, 0, item);
|
||||
popup.insertBefore(this.createItem(item), menulist.getItemAtIndex(i));
|
||||
menulist.disabled = menulist.itemCount == 0;
|
||||
}
|
||||
|
||||
createItem({label, value}) {
|
||||
createItem({label, value, className, disabled}) {
|
||||
let item = document.createElement("menuitem");
|
||||
item.value = value;
|
||||
item.setAttribute("label", label);
|
||||
if (className)
|
||||
item.classList.add(className);
|
||||
if (disabled)
|
||||
item.setAttribute("disabled", "true");
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the inputs and set a data-l10n-id on the menulist. This can be
|
||||
* reverted with `enableWithMessageId()`.
|
||||
*/
|
||||
disableWithMessageId(messageId) {
|
||||
this.menulist.setAttribute("data-l10n-id", messageId);
|
||||
this.menulist.setAttribute("image", "chrome://global/skin/icons/loading.png");
|
||||
this.menulist.disabled = true;
|
||||
this.button.disabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the inputs and set a data-l10n-id on the menulist. This can be
|
||||
* reverted with `disableWithMessageId()`.
|
||||
*/
|
||||
enableWithMessageId(messageId) {
|
||||
this.menulist.setAttribute("data-l10n-id", messageId);
|
||||
this.menulist.removeAttribute("image");
|
||||
this.menulist.disabled = this.menulist.itemCount == 0;
|
||||
this.button.disabled = !this.menulist.selectedItem;
|
||||
}
|
||||
}
|
||||
|
||||
function getLocaleDisplayInfo(localeCodes) {
|
||||
let availableLocales = new Set(Services.locale.availableLocales);
|
||||
let packagedLocales = new Set(Services.locale.packagedLocales);
|
||||
let localeNames = Services.intl.getLocaleDisplayNames(undefined, localeCodes);
|
||||
return localeCodes.map((code, i) => {
|
||||
|
@ -203,51 +252,241 @@ function getLocaleDisplayInfo(localeCodes) {
|
|||
label: localeNames[i],
|
||||
value: code,
|
||||
canRemove: !packagedLocales.has(code),
|
||||
installed: availableLocales.has(code),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function compareItems(a, b) {
|
||||
// Sort by installed.
|
||||
if (a.installed != b.installed) {
|
||||
return a.installed ? -1 : 1;
|
||||
|
||||
// The search label is always last.
|
||||
} else if (a.value == "search") {
|
||||
return 1;
|
||||
} else if (b.value == "search") {
|
||||
return -1;
|
||||
|
||||
// If both items are locales, sort by label.
|
||||
} else if (a.value && b.value) {
|
||||
return a.label.localeCompare(b.label);
|
||||
|
||||
// One of them is a label, put it first.
|
||||
} else if (a.value) {
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
var gMessengerLanguagesDialog = {
|
||||
_availableLocales: null,
|
||||
_requestedLocales: null,
|
||||
requestedLocales: null,
|
||||
_selectedLocales: null,
|
||||
selectedLocales: null,
|
||||
|
||||
get downloadEnabled() {
|
||||
// Downloading langpacks isn't always supported, check the pref.
|
||||
return Services.prefs.getBoolPref("intl.multilingual.downloadEnabled");
|
||||
},
|
||||
|
||||
beforeAccept() {
|
||||
this.requestedLocales = this._requestedLocales.items.map(item => item.value);
|
||||
this.selected = this.getSelectedLocales();
|
||||
return true;
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
// Maintain the previously requested locales even if we cancel out.
|
||||
this.requestedLocales = window.arguments[0];
|
||||
async onLoad() {
|
||||
// Maintain the previously selected locales even if we cancel out.
|
||||
let {selected, search} = window.arguments[0] || {};
|
||||
this.selectedLocales = selected;
|
||||
|
||||
let requested = this.requestedLocales || Services.locale.requestedLocales;
|
||||
let requestedSet = new Set(requested);
|
||||
let available = Services.locale.availableLocales
|
||||
.filter(locale => !requestedSet.has(locale));
|
||||
// This is a list of available locales that the user selected. It's more
|
||||
// restricted than the Intl notion of `requested` as it only contains
|
||||
// locale codes for which we have matching locales available.
|
||||
// The first time this dialog is opened, populate with appLocalesAsBCP47.
|
||||
let selectedLocales = this.selectedLocales || Services.locale.appLocalesAsBCP47;
|
||||
let selectedLocaleSet = new Set(selectedLocales);
|
||||
let available = Services.locale.availableLocales;
|
||||
let availableSet = new Set(available);
|
||||
|
||||
// Filter selectedLocales since the user may select a locale when it is
|
||||
// available and then disable it.
|
||||
selectedLocales = selectedLocales.filter(locale => availableSet.has(locale));
|
||||
// Nothing in available should be in selectedSet.
|
||||
available = available.filter(locale => !selectedLocaleSet.has(locale));
|
||||
|
||||
this.initSelectedLocales(selectedLocales);
|
||||
await this.initAvailableLocales(available, search);
|
||||
|
||||
this.initRequestedLocales(requested);
|
||||
this.initAvailableLocales(available);
|
||||
this.initialized = true;
|
||||
},
|
||||
|
||||
initRequestedLocales(requested) {
|
||||
this._requestedLocales = new OrderedListBox({
|
||||
richlistbox: document.getElementById("requestedLocales"),
|
||||
initSelectedLocales(selectedLocales) {
|
||||
this._selectedLocales = new OrderedListBox({
|
||||
richlistbox: document.getElementById("selectedLocales"),
|
||||
upButton: document.getElementById("up"),
|
||||
downButton: document.getElementById("down"),
|
||||
removeButton: document.getElementById("remove"),
|
||||
onRemove: (item) => this._availableLocales.addItem(item),
|
||||
onRemove: (item) => this.selectedLocaleRemoved(item),
|
||||
});
|
||||
this._requestedLocales.setItems(getLocaleDisplayInfo(requested));
|
||||
this._selectedLocales.setItems(getLocaleDisplayInfo(selectedLocales));
|
||||
},
|
||||
|
||||
initAvailableLocales(available) {
|
||||
async initAvailableLocales(available, search) {
|
||||
this._availableLocales = new SortedItemSelectList({
|
||||
menulist: document.getElementById("availableLocales"),
|
||||
button: document.getElementById("add"),
|
||||
onSelect: (item) => this._requestedLocales.addItem(item),
|
||||
compareFn: compareItems,
|
||||
onSelect: (item) => this.availableLanguageSelected(item),
|
||||
onChange: (item) => {
|
||||
this.hideError();
|
||||
if (item.value == "search") {
|
||||
this.loadLocalesFromAMO();
|
||||
}
|
||||
},
|
||||
});
|
||||
this._availableLocales.setItems(getLocaleDisplayInfo(available));
|
||||
|
||||
// Populate the list with the installed locales even if the user is
|
||||
// searching in case the download fails.
|
||||
await this.loadLocalesFromInstalled(available);
|
||||
|
||||
// If the user opened this from the "Search for more languages" option,
|
||||
// search AMO for available locales.
|
||||
if (search) {
|
||||
return this.loadLocalesFromATN();
|
||||
}
|
||||
|
||||
return undefined;
|
||||
},
|
||||
|
||||
async loadLocalesFromATN() {
|
||||
if (!this.downloadEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable the dropdown while we hit the network.
|
||||
this._availableLocales.disableWithMessageId("messenger-languages-searching");
|
||||
|
||||
// Fetch the available langpacks from AMO.
|
||||
let availableLangpacks;
|
||||
try {
|
||||
availableLangpacks = await AddonRepository.getAvailableLangpacks();
|
||||
} catch (e) {
|
||||
this.showError();
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the available langpack info for later use.
|
||||
this.availableLangpacks = new Map();
|
||||
for (let {target_locale, url, hash} of availableLangpacks) {
|
||||
this.availableLangpacks.set(target_locale, {url, hash});
|
||||
}
|
||||
|
||||
// Remove the installed locales from the available ones.
|
||||
let installedLocales = new Set(Services.locale.availableLocales);
|
||||
let notInstalledLocales = availableLangpacks
|
||||
.filter(({target_locale}) => !installedLocales.has(target_locale))
|
||||
.map(lang => lang.target_locale);
|
||||
|
||||
// Create the rows for the remote locales.
|
||||
let availableItems = getLocaleDisplayInfo(notInstalledLocales);
|
||||
availableItems.push({
|
||||
label: await document.l10n.formatValue("messenger-languages-available-label"),
|
||||
className: "label-item",
|
||||
disabled: true,
|
||||
installed: false,
|
||||
});
|
||||
|
||||
// Remove the search option and add the remote locales.
|
||||
let items = this._availableLocales.items;
|
||||
items.pop();
|
||||
items = items.concat(availableItems);
|
||||
|
||||
// Update the dropdown and enable it again.
|
||||
this._availableLocales.setItems(items);
|
||||
this._availableLocales.enableWithMessageId("messenger-languages-select-language");
|
||||
},
|
||||
|
||||
async loadLocalesFromInstalled(available) {
|
||||
let items;
|
||||
if (available.length > 0) {
|
||||
items = getLocaleDisplayInfo(available);
|
||||
items.push(await this.createInstalledLabel());
|
||||
} else {
|
||||
items = [];
|
||||
}
|
||||
if (this.downloadEnabled) {
|
||||
items.push({
|
||||
label: await document.l10n.formatValue("messenger-languages-search"),
|
||||
value: "search",
|
||||
});
|
||||
}
|
||||
this._availableLocales.setItems(items);
|
||||
},
|
||||
|
||||
async availableLanguageSelected(item) {
|
||||
let available = new Set(Services.locale.availableLocales);
|
||||
|
||||
if (available.has(item.value)) {
|
||||
this._selectedLocales.addItem(item);
|
||||
if (available.size == this._selectedLocales.items.length) {
|
||||
// Remove the installed label, they're all installed.
|
||||
this._availableLocales.items.shift();
|
||||
this._availableLocales.setItems(this._availableLocales.items);
|
||||
}
|
||||
} else if (this.availableLangpacks.has(item.value)) {
|
||||
this._availableLocales.disableWithMessageId("messenger-languages-downloading");
|
||||
|
||||
let {url, hash} = this.availableLangpacks.get(item.value);
|
||||
let install = await AddonManager.getInstallForURL(
|
||||
url, "application/x-xpinstall", hash);
|
||||
|
||||
try {
|
||||
await install.install();
|
||||
} catch (e) {
|
||||
this.showError();
|
||||
return;
|
||||
}
|
||||
|
||||
item.installed = true;
|
||||
this._selectedLocales.addItem(item);
|
||||
this._availableLocales.enableWithMessageId("messenger-languages-select-language");
|
||||
} else {
|
||||
this.showError();
|
||||
}
|
||||
},
|
||||
|
||||
showError() {
|
||||
document.querySelectorAll(".warning-message-separator")
|
||||
.forEach(separator => separator.classList.add("thin"));
|
||||
document.getElementById("warning-message").hidden = false;
|
||||
this._availableLocales.enableWithMessageId("messenger-languages-select-language");
|
||||
},
|
||||
|
||||
hideError() {
|
||||
document.querySelectorAll(".warning-message-separator")
|
||||
.forEach(separator => separator.classList.remove("thin"));
|
||||
document.getElementById("warning-message").hidden = true;
|
||||
},
|
||||
|
||||
getSelectedLocales() {
|
||||
return this._selectedLocales.items.map(item => item.value);
|
||||
},
|
||||
|
||||
async selectedLocaleRemoved(item) {
|
||||
this._availableLocales.addItem(item);
|
||||
|
||||
// If the item we added is at the top of the list, it needs the label.
|
||||
if (this._availableLocales.items[0] == item) {
|
||||
this._availableLocales.addItem(await this.createInstalledLabel());
|
||||
}
|
||||
},
|
||||
|
||||
async createInstalledLabel() {
|
||||
return {
|
||||
label: await document.l10n.formatValue("messenger-languages-installed-label"),
|
||||
className: "label-item",
|
||||
disabled: true,
|
||||
installed: true,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
|
|
@ -35,27 +35,32 @@
|
|||
</columns>
|
||||
<rows>
|
||||
<row flex="1">
|
||||
<richlistbox id="requestedLocales" flex="1"/>
|
||||
<richlistbox id="selectedLocales" flex="1"/>
|
||||
<vbox>
|
||||
<button id="up" disabled="true" data-l10n-id="languages-customize-moveup"/>
|
||||
<button id="down" disabled="true" data-l10n-id="languages-customize-movedown"/>
|
||||
<button id="remove" disabled="true" data-l10n-id="languages-customize-remove"/>
|
||||
<button id="up" class="action-button" disabled="true" data-l10n-id="languages-customize-moveup"/>
|
||||
<button id="down" class="action-button" disabled="true" data-l10n-id="languages-customize-movedown"/>
|
||||
<button id="remove" class="action-button" disabled="true" data-l10n-id="languages-customize-remove"/>
|
||||
</vbox>
|
||||
</row>
|
||||
<row>
|
||||
<menulist id="availableLocales"
|
||||
class="available-locales-list"
|
||||
data-l10n-id="languages-customize-select-language"
|
||||
data-l10n-attrs="placeholder">
|
||||
data-l10n-id="messenger-languages-select-language"
|
||||
data-l10n-attrs="placeholder,label">
|
||||
<menupopup/>
|
||||
</menulist>
|
||||
<button id="add"
|
||||
class="add-browser-language"
|
||||
class="add-messenger-language action-button"
|
||||
data-l10n-id="languages-customize-add"
|
||||
disabled="true"/>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
<separator/>
|
||||
<separator class="warning-message-separator"/>
|
||||
<hbox id="warning-message" class="message-bar message-bar-warning" hidden="true">
|
||||
<image class="message-bar-icon"/>
|
||||
<description class="message-bar-description" data-l10n-id="messenger-languages-error"/>
|
||||
</hbox>
|
||||
<separator class="warning-message-separator"/>
|
||||
</vbox>
|
||||
</dialog>
|
||||
|
|
|
@ -26,3 +26,20 @@ messenger-languages-window =
|
|||
.style = width: 40em
|
||||
|
||||
messenger-languages-description = { -brand-short-name } will display the first language as your default and will display alternate languages if necessary in the order they appear.
|
||||
|
||||
messenger-languages-search = Search for more languages…
|
||||
|
||||
messenger-languages-searching =
|
||||
.label = Searching for languages…
|
||||
|
||||
messenger-languages-downloading =
|
||||
.label = Downloading…
|
||||
|
||||
messenger-languages-select-language =
|
||||
.label = Select a language to add…
|
||||
.placeholder = Select a language to add…
|
||||
|
||||
messenger-languages-installed-label = Installed languages
|
||||
messenger-languages-available-label = Available languages
|
||||
|
||||
messenger-languages-error = { -brand-short-name } can't update your languages right now. Check that you are connected to the internet or try again.
|
||||
|
|
|
@ -420,13 +420,31 @@ richlistbox:focus > richlistitem[selected="true"] {
|
|||
min-width: 20em;
|
||||
}
|
||||
|
||||
#MessengerLanguagesDialog > .dialog-button-box > .dialog-button[dlgtype="accept"] {
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
|
||||
#availableLocales {
|
||||
margin: 0;
|
||||
margin-inline-end: 4px;
|
||||
}
|
||||
|
||||
.add-browser-language {
|
||||
margin: 0 4px;
|
||||
#warning-message > .message-bar-description {
|
||||
width: 32em;
|
||||
}
|
||||
|
||||
.add-messenger-language {
|
||||
margin: 0;
|
||||
margin-inline-start: 4px;
|
||||
}
|
||||
|
||||
.action-button {
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
|
||||
/* Menulist styles */
|
||||
.label-item {
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Загрузка…
Ссылка в новой задаче