Bug 1469684 - Allow switching between installed locales r?jaws
MozReview-Commit-ID: 1MkkZUfpJ8O
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1733,8 +1733,11 @@ pref("app.normandy.shieldLearnMoreUrl",
pref("app.shield.optoutstudies.enabled", true);
#else
pref("app.shield.optoutstudies.enabled", false);
#endif
// Savant Shield study preferences
pref("shield.savant.enabled", false);
pref("shield.savant.loglevel", "warn");
+
+// Multi-lingual preferences
+pref("intl.multilingual.enabled", false);
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -9,16 +9,18 @@
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/Downloads.jsm");
ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
ChromeUtils.import("resource:///modules/ShellService.jsm");
ChromeUtils.import("resource:///modules/TransientPrefs.jsm");
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/DownloadUtils.jsm");
+ChromeUtils.import("resource://gre/modules/L10nRegistry.jsm");
+ChromeUtils.import("resource://gre/modules/Localization.jsm");
ChromeUtils.defineModuleGetter(this, "CloudStorage",
"resource://gre/modules/CloudStorage.jsm");
XPCOMUtils.defineLazyServiceGetters(this, {
gCategoryManager: ["@mozilla.org/categorymanager;1", "nsICategoryManager"],
gHandlerService: ["@mozilla.org/uriloader/handler-service;1", "nsIHandlerService"],
gMIMEService: ["@mozilla.org/mime;1", "nsIMIMEService"],
gWebContentContentConverterService: ["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1", "nsIWebContentConverterService"],
@@ -239,16 +241,27 @@ if (AppConstants.MOZ_UPDATER) {
]);
}
}
// A promise that resolves when the list of application handlers is loaded.
// We store this in a global so tests can await it.
var promiseLoadHandlersList;
+// Load the preferences string bundle for a given locale.
+function getBundleForLocale(locale) {
+ function generateContexts(resourceIds) {
+ return L10nRegistry.generateContexts([locale], resourceIds);
+ }
+ return new Localization([
+ "browser/preferences/preferences.ftl",
+ "branding/brand.ftl",
+ ], generateContexts);
+}
+
var gNodeToObjectMap = new WeakMap();
var gMainPane = {
// The set of types the app knows how to handle. A hash of HandlerInfoWrapper
// objects, indexed by type.
_handledTypes: {},
// The list of types we can show, sorted by the sort column/direction.
@@ -344,16 +357,20 @@ var gMainPane = {
let connectionSettingsLink = document.getElementById("connectionSettingsLearnMore");
let connectionSettingsUrl = Services.urlFormatter.formatURLPref("app.support.baseURL") +
"prefs-connection-settings";
connectionSettingsLink.setAttribute("href", connectionSettingsUrl);
this.updateProxySettingsUI();
initializeProxyUI(gMainPane);
+ if (Services.prefs.getBoolPref("intl.multilingual.enabled")) {
+ gMainPane.initBrowserLocale();
+ }
+
if (AppConstants.platform == "win") {
// Functionality for "Show tabs in taskbar" on Windows 7 and up.
try {
let ver = parseFloat(Services.sysinfo.getProperty("version"));
let showTabsInTaskbar = document.getElementById("showTabsInTaskbar");
showTabsInTaskbar.hidden = ver < 6.1;
} catch (ex) { }
}
@@ -754,16 +771,70 @@ var gMainPane = {
checkbox.removeAttribute("disabled");
newValue = startupPref.value === this.STARTUP_PREF_RESTORE_SESSION;
}
if (checkbox.checked !== newValue) {
checkbox.checked = newValue;
}
},
+ initBrowserLocale() {
+ let localeCodes = Services.locale.getAvailableLocales();
+ let localeNames = Services.intl.getLocaleDisplayNames(undefined, localeCodes);
+ let locales = localeCodes.map((code, i) => ({code, name: localeNames[i]}));
+ locales.sort((a, b) => a.name > b.name);
+
+ let fragment = document.createDocumentFragment();
+ for (let {code, name} of locales) {
+ let menuitem = document.createElement("menuitem");
+ menuitem.setAttribute("value", code);
+ menuitem.setAttribute("label", name);
+ fragment.appendChild(menuitem);
+ }
+ let menulist = document.getElementById("defaultBrowserLanguage");
+ let menupopup = menulist.querySelector("menupopup");
+ menupopup.appendChild(fragment);
+ menulist.value = Services.locale.getRequestedLocale();
+
+ document.getElementById("browserLanguagesBox").hidden = false;
+ },
+
+ /* Show the confirmation message bar to allow a restart into the new language. */
+ async onBrowserLanguageChange(event) {
+ let locale = event.target.value;
+ let messageBar = document.getElementById("confirmBrowserLanguage");
+ if (locale == Services.locale.getRequestedLocale()) {
+ messageBar.hidden = true;
+ return;
+ }
+ // Set the text in the message bar for the new locale.
+ let newBundle = getBundleForLocale(locale);
+ let description = messageBar.querySelector("description");
+ description.textContent = await newBundle.formatValue(
+ "confirm-browser-language-change-description");
+ let button = messageBar.querySelector("button");
+ button.setAttribute(
+ "label", await newBundle.formatValue(
+ "confirm-browser-language-change-button"));
+ messageBar.hidden = false;
+ },
+
+ /* Confirm the locale change and restart the browser in the new locale. */
+ confirmBrowserLanguageChange() {
+ let locale = document.getElementById("defaultBrowserLanguage").value;
+ Services.locale.setRequestedLocales([locale]);
+
+ // Restart with the new locale.
+ let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
+ Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
+ if (!cancelQuit.data) {
+ Services.startup.quit(Services.startup.eAttemptQuit | Services.startup.eRestart);
+ }
+ },
+
onBrowserRestoreSessionChange(event) {
const value = event.target.checked;
const startupPref = Preferences.get("browser.startup.page");
let newValue;
if (value) {
// We need to restore the blank homepage setting in our other pref
if (startupPref.value === this.STARTUP_PREF_BLANK) {
--- a/browser/components/preferences/in-content/main.xul
+++ b/browser/components/preferences/in-content/main.xul
@@ -277,16 +277,30 @@
</hbox>
</vbox>
</groupbox>
<!-- Languages -->
<groupbox id="languagesGroup" data-category="paneGeneral" hidden="true">
<caption><label data-l10n-id="language-header"/></caption>
+ <vbox id="browserLanguagesBox" align="start" hidden="true">
+ <description flex="1" controls="chooseBrowserLanguage" data-l10n-id="choose-browser-language-description"/>
+ <menulist id="defaultBrowserLanguage" class="accessory-button" oncommand="gMainPane.onBrowserLanguageChange(event)" flex="1">
+ <menupopup/>
+ </menulist>
+ </vbox>
+ <hbox id="confirmBrowserLanguage" class="message-bar" align="center" hidden="true">
+ <image class="message-bar-icon"/>
+ <hbox class="message-bar-content" align="center" flex="1">
+ <description flex="1"/>
+ <button class="message-bar-button" oncommand="gMainPane.confirmBrowserLanguageChange()"/>
+ </hbox>
+ </hbox>
+
<hbox id="languagesBox" align="center">
<description flex="1" control="chooseLanguage" data-l10n-id="choose-language-description"/>
<!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
<hbox>
<button id="chooseLanguage"
class="accessory-button"
data-l10n-id="choose-button"
search-l10n-ids="
--- a/browser/locales/en-US/browser/preferences/preferences.ftl
+++ b/browser/locales/en-US/browser/preferences/preferences.ftl
@@ -246,16 +246,20 @@ colors-settings =
language-header = Language
choose-language-description = Choose your preferred language for displaying pages
choose-button =
.label = Choose…
.accesskey = o
+choose-browser-language-description = Choose the languages used to display menus, messages, and notifications from { -brand-short-name }.
+confirm-browser-language-change-description = Restart { -brand-short-name } to apply these changes
+confirm-browser-language-change-button = Apply and Restart
+
translate-web-pages =
.label = Translate web content
.accesskey = T
# The <img> element is replaced by the logo of the provider
# used to provide machine translations for web pages.
translate-attribution = Translations by <img data-l10n-name="logo"/>
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -898,8 +898,12 @@ menulist[indicator=true] > menupopup men
visibility: hidden;
}
/* Proxy port input */
.proxy-port-input {
width: calc(5ch + 22px); /* 5 chars + 11px padding on both sides */
}
+
+#defaultBrowserLanguage {
+ min-width: 20em;
+}
--- a/toolkit/themes/shared/in-content/common.inc.css
+++ b/toolkit/themes/shared/in-content/common.inc.css
@@ -36,16 +36,21 @@
--in-content-link-color-active: #003eaa;
--in-content-link-color-visited: #0a8dff;
--in-content-primary-button-background: #0a84ff;
--in-content-primary-button-background-hover: #0060df;
--in-content-primary-button-background-active: #003EAA;
--in-content-table-background: #ebebeb;
--in-content-table-border-dark-color: #d1d1d1;
--in-content-table-header-background: #0a84ff;
+ --grey-20: #ededf0;
+ --grey-90: #0c0c0d;
+ --grey-90-a10: rgba(12, 12, 13, 0.1);
+ --grey-90-a20: rgba(12, 12, 13, 0.2);
+ --grey-90-a30: rgba(12, 12, 13, 0.3);
}
html|html,
xul|page,
xul|window {
font: message-box;
-moz-appearance: none;
background-color: var(--in-content-page-background);
@@ -840,8 +845,46 @@ xul|treechildren::-moz-tree-cell-text,
xul|treechildren::-moz-tree-image {
color: var(--in-content-text-color);
}
xul|treechildren::-moz-tree-cell-text(selected),
xul|treechildren::-moz-tree-image(selected) {
color: var(--in-content-selected-text);
}
+
+/* Message bars */
+.message-bar {
+ background-color: var(--grey-20);
+ border-radius: 4px;
+ color: var(--grey-90);
+ min-height: 32px;
+ padding: 0 4px;
+}
+
+/* The message-bar-button styles have extra specificity to override
+ * the defaults for buttons. */
+.message-bar > hbox > .message-bar-button {
+ background-color: var(--grey-90-a10);
+ border: none;
+ border-radius: 2px;
+ height: 24px;
+ margin-inline-start: 8px;
+ padding: 0 8px;
+}
+
+.message-bar > hbox > .message-bar-button:hover {
+ background-color: var(--grey-90-a20);
+}
+
+.message-bar > hbox > .message-bar-button:active {
+ background-color: var(--grey-90-a30);
+}
+
+.message-bar-icon {
+ list-style-image: url("chrome://browser/skin/identity-icon.svg");
+ width: 24px;
+ height: 24px;
+ padding: 4px;
+ margin-inline-end: 4px;
+ -moz-context-properties: fill;
+ fill: currentColor;
+}