Skip to content

Commit

Permalink
Make the app browser multilingual (#340)
Browse files Browse the repository at this point in the history
This allows the app browser messages to be almost fully translated. The localization uses the Fluent Project system built into Firefox.

Note: Due to some problems, the localization does not work yet and prevents standard Firefox messages from being translated as well.
  • Loading branch information
filips123 committed Jan 22, 2024
1 parent 7610e8b commit b9b09ac
Show file tree
Hide file tree
Showing 10 changed files with 361 additions and 95 deletions.
6 changes: 6 additions & 0 deletions docs/docs/user-guide/browser.md
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,12 @@ customize page, *uncheck the icon bar entry* in the menu and *check it again*.
* Preference name: `firefoxpwa.enableHidingIconBar`
* Default value: `false`

!!! note

When [the tabs mode is enabled](#show-browser-tabs-and-enable-using-multi-tabbed-web-apps),
the "icon bar" is renamed to the "tabs bar" in the customize page menu. The preference name
and other functionalities still remain the same.

<!-- Section: Keyboard Shortcuts -->

### Disabling keyboard shortcuts
Expand Down
9 changes: 9 additions & 0 deletions native/userchrome/profile/chrome/pwa/boot.jsm
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
AppConstants: 'resource://gre/modules/AppConstants.jsm',
BrowserWindowTracker: 'resource:///modules/BrowserWindowTracker.jsm',
NetUtil: 'resource://gre/modules/NetUtil.jsm',
LangPackMatcher: "resource://gre/modules/LangPackMatcher.jsm",
applySystemIntegration: 'resource://pwa/utils/systemIntegration.jsm',
});

Expand Down Expand Up @@ -192,5 +193,13 @@ if (AppConstants.platform === 'macosx') {
}
}

// Register the available localization sources
Services.obs.addObserver(async () => {
const l10nLocales = await LangPackMatcher.getAvailableLocales();
if (!l10nLocales.includes('en-US')) l10nLocales.push('en-US');
const l10nSource = new L10nFileSource('pwa', 'app', l10nLocales, 'resource://pwa/localization/{locale}/');
L10nRegistry.getInstance().registerSources([l10nSource]);
}, 'final-ui-startup');

// Import browser chrome modifications
ChromeUtils.import('resource://pwa/chrome.jsm');
127 changes: 65 additions & 62 deletions native/userchrome/profile/chrome/pwa/content/browser.jsm
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class PwaBrowser {
//////////////////////////////

prepareLayout () {
this.loadLocalizationSources();
this.supportSmallWindowSizes();
this.createInfoElements();
this.createAddressInput();
Expand All @@ -49,6 +50,11 @@ class PwaBrowser {
this.renameHomepageWidget();
}

loadLocalizationSources () {
const resourceIds = ['pwa/appmenu.ftl', 'pwa/contextmenu.ftl', 'pwa/browser.ftl', 'pwa/widgets.ftl', 'pwa/customizemode.ftl'];
document.l10n.addResourceIds(resourceIds);
}

supportSmallWindowSizes () {
document.getElementsByClassName('toolbar-items')[0].style.overflow = 'hidden';

Expand Down Expand Up @@ -143,8 +149,8 @@ class PwaBrowser {

createAddressInput () {
// Create a custom URL input method via shortcut
function addressInputHandle () {
const url = prompt('Enter site address');
async function addressInputHandle () {
const url = prompt(await document.l10n.formatValue('popup-address-input'));
if (url) window.openTrustedLinkIn(url, 'current');
}

Expand Down Expand Up @@ -175,7 +181,7 @@ class PwaBrowser {
document.getElementById('context-openlink').accessKey = 'N';

// Create context menu item that opens link in a default browser
const menuItem = this.createElement(document, 'menuitem', { id: 'contextmenu-openlinkdefault', label: 'Open Link in Default Browser', accesskey: 'D', oncommand: 'gContextMenu.openLinkInDefaultBrowser()' });
const menuItem = this.createElement(document, 'menuitem', { id: 'contextmenu-openlinkdefault', 'data-l10n-id': 'context-menu-open-link-default-browser', oncommand: 'gContextMenu.openLinkInDefaultBrowser()' });
document.getElementById('context-sep-open').before(menuItem)

hookFunction(window, 'openContextMenu', null, () => {
Expand Down Expand Up @@ -211,7 +217,7 @@ class PwaBrowser {
const menuItem = this.createElement(document, 'toolbarbutton', {
class: 'subviewbutton',
shortcut: 'Ctrl+Shift+N',
label: 'New default browser'
'data-l10n-id': 'app-menu-new-default-browser'
});

menuItem.onclick = () => MailIntegration._launchExternalUrl(makeURI(startURL));
Expand Down Expand Up @@ -583,10 +589,15 @@ class PwaBrowser {
// So it is disabled by default and can be enabled using about:config preference
if (!xPref.get(ChromeLoader.PREF_ENABLE_HIDING_ICON_BAR)) return;

// Setting the toolbar name will automatically add it to toolbars menu in customize page
const titleBar = document.getElementById('titlebar');
const iconBar = document.getElementById('TabsToolbar');
iconBar.setAttribute('toolbarname', 'Icon Bar');

// Needed so Fluent allows translating the toolbarname attribute
iconBar.setAttribute('data-l10n-attrs', 'toolbarname');

// Setting the toolbar name will automatically add it to toolbars menu in customize page
if (xPref.get(ChromeLoader.PREF_ENABLE_TABS_MODE)) document.l10n.setAttributes(iconBar, 'toolbar-tabs-ffpwa');
else document.l10n.setAttributes(iconBar, 'toolbar-icon-ffpwa');

// Hide tabs/icon bar on launch if it should be hidden by default
// Also prevent un-collapsing of tabs/icon bar by some Firefox function
Expand Down Expand Up @@ -834,16 +845,20 @@ class PwaBrowser {
}

renameOpenImageAction () {
// Rename open/view image context menu action based on links target preference
// Rename open/view image/video context menu action based on links target preference
// Links target overwrites need to be disabled when tab mode is enabled
const userPreference = xPref.get(ChromeLoader.PREF_LINKS_TARGET);
if (!userPreference || xPref.get(ChromeLoader.PREF_ENABLE_TABS_MODE)) return;

let actionLabel;
if (userPreference === 1) actionLabel = 'Open Image';
else if (userPreference === 2) actionLabel = 'Open Image in New Window';
const viewImage = document.getElementById('context-viewimage');
if (userPreference === 1) document.l10n.setAttributes(viewImage, 'context-menu-image-view-current-tab');
else if (userPreference === 3) document.l10n.setAttributes(viewImage, 'context-menu-image-view-new-tab');
else if (userPreference === 2) document.l10n.setAttributes(viewImage, 'context-menu-image-view-new-window');

document.getElementById('context-viewimage').setAttribute('label', actionLabel);
const viewVideo = document.getElementById('context-viewvideo');
if (userPreference === 1) document.l10n.setAttributes(viewVideo, 'context-menu-video-view-current-tab');
else if (userPreference === 3) document.l10n.setAttributes(viewVideo, 'context-menu-video-view-new-tab');
else if (userPreference === 2) document.l10n.setAttributes(viewVideo, 'context-menu-video-view-new-window');
}

disableNewTabShortcuts () {
Expand All @@ -857,7 +872,7 @@ class PwaBrowser {
renameHomepageWidget () {
hookFunction(window, 'onload', null, () => {
try {
this.modifyWidget('home-button', { tooltiptext: 'App Start Page' });
document.l10n.setAttributes(document.getElementById('home-button'), 'toolbar-button-home-ffpwa');
} catch (_) {}
});
}
Expand Down Expand Up @@ -889,11 +904,10 @@ class PwaBrowser {
// Create reader view widget
CustomizableUI.createWidget({
id: 'reader-view-button',
l10nId: 'toolbar-button-reader-view',
shortcutId: 'key_toggleReaderMode',
type: 'button',

label: 'Reader View',
tooltiptext: 'Toggle a reader view',

onCreated (node) {
if (
CustomizableUI.getAreaType(this.currentArea) !== CustomizableUI.TYPE_MENU_PANEL &&
Expand Down Expand Up @@ -961,11 +975,9 @@ class PwaBrowser {
// Create copy link widget
CustomizableUI.createWidget({
id: 'copy-link-button',
l10nId: 'toolbar-button-copy-link',
type: 'button',

label: 'Copy Link',
tooltiptext: 'Copy a link to this page',

onCommand (event) {
const currentUrl = gURLBar.makeURIReadable(event.target.ownerGlobal.gBrowser.selectedBrowser.currentURI).displaySpec;
const clipboardHandler = Cc['@mozilla.org/widget/clipboardhelper;1'].getService(Ci.nsIClipboardHelper);
Expand All @@ -980,11 +992,9 @@ class PwaBrowser {
CustomizableUI.createWidget({
id: 'share-link-button',
viewId: 'share-link-view',
l10nId: 'toolbar-button-share-link',
type: 'view',

label: 'Share Link',
tooltiptext: 'Share a link to this page',

onBeforeCreated: (document) => {
const viewCache = document.getElementById('appMenu-viewCache');
const shareLinkView = this.createElement(document, 'panelview', { id: 'share-link-view', flex: 1 });
Expand Down Expand Up @@ -1053,11 +1063,9 @@ class PwaBrowser {
if (AppConstants.isPlatformAndVersionAtLeast('win', '6.4')) {
CustomizableUI.createWidget({
id: 'share-link-button',
l10nId: 'toolbar-button-share-link',
type: 'button',

label: 'Share Link',
tooltiptext: 'Share a link to this page',

onCommand (event) {
const browser = event.target.ownerGlobal.gBrowser.selectedBrowser;
const currentUrl = gURLBar.makeURIReadable(browser.currentURI).displaySpec;
Expand All @@ -1075,11 +1083,9 @@ class PwaBrowser {
CustomizableUI.createWidget({
id: 'send-to-device-button',
viewId: 'send-to-device-view',
l10nId: 'toolbar-button-send-to-device',
type: 'view',

label: 'Send to Device',
tooltiptext: 'Send this page to another device',

onBeforeCreated: (document) => {
const viewCache = document.getElementById('appMenu-viewCache');
const shareLinkView = this.createElement(document, 'panelview', { id: 'send-to-device-view', flex: 1 });
Expand Down Expand Up @@ -1123,11 +1129,9 @@ class PwaBrowser {
// Create open in browser widget
CustomizableUI.createWidget({
id: 'open-in-browser-button',
l10nId: 'toolbar-button-open-in-browser',
type: 'button',

label: 'Open in Browser',
tooltiptext: 'Open this page in browser',

onCommand (event) {
// "Abusing" mail integration to open current URL in external browser
MailIntegration._launchExternalUrl(makeURI(gURLBar.makeURIReadable(event.target.ownerGlobal.gBrowser.selectedBrowser.currentURI).displaySpec));
Expand All @@ -1139,11 +1143,10 @@ class PwaBrowser {
// Create mute page widget
CustomizableUI.createWidget({
id: 'mute-button',
l10nId: 'toolbar-button-mute',
shortcutId: 'key_toggleMute',
type: 'button',

label: 'Toogle Sound',
tooltiptext: 'Toggle page sound',

onCreated (node) {
const document = node.ownerDocument;
const window = document.defaultView;
Expand Down Expand Up @@ -1183,7 +1186,7 @@ class PwaBrowser {

const muteAutohideCheckbox = window.gFFPWABrowser.createElement(document, 'checkbox', {
id: 'mute-button-autohide-checkbox',
label: 'Hide button when not playing',
'data-l10n-id': 'customize-mode-mute-button-autohide',
checked: true,
});

Expand Down Expand Up @@ -1296,9 +1299,6 @@ class PwaBrowser {
id: 'unified-extensions-button',
l10nId: 'unified-extensions-button',

label: 'Extensions',
tooltiptext: 'Access installed browser extensions',

onCreated (node) {
const document = node.ownerDocument;
const window = document.defaultView;
Expand Down Expand Up @@ -1341,11 +1341,9 @@ class PwaBrowser {
// Create tracking protection widget
CustomizableUI.createWidget({
id: 'tracking-protection-button',
l10nId: 'toolbar-button-tracking-protection',
type: 'button',

label: 'Tracking Protection',
tooltiptext: 'View information about tracking protection on this site',

onCreated (node) {
const document = node.ownerDocument;
const window = document.defaultView;
Expand Down Expand Up @@ -1379,25 +1377,40 @@ class PwaBrowser {

// Update widget icon and tooltip when needed
hookFunction(window.gProtectionsHandler, 'showDisabledTooltipForTPIcon', null, async function () {
node.setAttribute('tooltiptext', (await document.l10n.formatValue('tracking-protection-icon-disabled')).replace(/\.$/, ''));
node.setAttribute('label', (await document.l10n.formatMessages(['toolbar-button-tracking-protection']))?.[0]?.attributes?.find(attr => attr.name === 'label')?.value);
const message = (await document.l10n.formatValue('tracking-protection-icon-disabled'))?.replace(/\.$/, '');
node.setAttribute('aria-label', message);
node.setAttribute('tooltiptext', message);
});

hookFunction(window.gProtectionsHandler, 'showActiveTooltipForTPIcon', null, async function () {
node.setAttribute('tooltiptext', (await document.l10n.formatValue('tracking-protection-icon-active')).replace(/\.$/, ''));
console.log('aaa')
node.setAttribute('label', (await document.l10n.formatMessages(['toolbar-button-tracking-protection']))?.[0]?.attributes?.find(attr => attr.name === 'label')?.value);
const message = (await document.l10n.formatValue('tracking-protection-icon-active'))?.replace(/\.$/, '');
node.setAttribute('aria-label', message);
node.setAttribute('tooltiptext', message);
});

hookFunction(window.gProtectionsHandler, 'showNoTrackerTooltipForTPIcon', null, async function () {
node.setAttribute('tooltiptext', (await document.l10n.formatValue('tracking-protection-icon-no-trackers-detected')).replace(/\.$/, ''));
node.setAttribute('label', (await document.l10n.formatMessages(['toolbar-button-tracking-protection']))?.[0]?.attributes?.find(attr => attr.name === 'label')?.value);
const message = (await document.l10n.formatValue('tracking-protection-icon-no-trackers-detected'))?.replace(/\.$/, '');
node.setAttribute('aria-label', message);
node.setAttribute('tooltiptext', message);
});

// Force show widget on customize mode page and reset its state
hookFunction(window.gCustomizeMode, 'enter', null, () => {
window.gProtectionsHandler._trackingProtectionIconContainer.hidden = false;

document.l10n.setAttributes(node, 'toolbar-button-tracking-protection');
node.getElementsByClassName('toolbarbutton-icon')[0].removeAttribute('hasException');
node.getElementsByClassName('toolbarbutton-icon')[0].removeAttribute('active');
});

// Localize widget attributes when exiting customize mode
hookFunction(window.gCustomizeMode, 'exit', null, () => {
document.l10n.setAttributes(node, 'toolbar-button-tracking-protection');
});

// Force show widget if not in toolbar
hookFunction(window.gProtectionsHandler, 'onLocationChange', null, () => {
if (node.trackingProtectionAreaType === CustomizableUI.TYPE_TOOLBAR) {
Expand Down Expand Up @@ -1436,11 +1449,9 @@ class PwaBrowser {
// Create identity information widget
CustomizableUI.createWidget({
id: 'identity-button',
l10nId: 'toolbar-button-identity',
type: 'button',

label: 'Site Information',
tooltiptext: 'View information about this site',

onCreated (node) {
const document = node.ownerDocument;
const window = document.defaultView;
Expand Down Expand Up @@ -1502,11 +1513,9 @@ class PwaBrowser {
// Create permissions widget
CustomizableUI.createWidget({
id: 'permissions-button',
l10nId: 'toolbar-button-permissions',
type: 'button',

label: 'Site Permissions',
tooltiptext: 'View permissions granted to this site',

onCreated: (node) => {
const document = node.ownerDocument;
const window = document.defaultView;
Expand Down Expand Up @@ -1595,11 +1604,9 @@ class PwaBrowser {
// Create notifications widget
CustomizableUI.createWidget({
id: 'notifications-button',
l10nId: 'toolbar-button-notifications',
type: 'button',

label: 'Site Notifications',
tooltiptext: 'Popup notifications for this site',

removable: false,
overflows: false,
defaultArea: CustomizableUI.AREA_TABSTRIP,
Expand Down Expand Up @@ -1692,11 +1699,9 @@ class PwaBrowser {
// Create close page widget
CustomizableUI.createWidget({
id: 'close-page-button',
l10nId: 'toolbar-button-close',
type: 'button',

label: 'Close',
tooltiptext: 'Close the current page',

removable: false,
overflows: false,
defaultArea: CustomizableUI.AREA_NAVBAR,
Expand Down Expand Up @@ -1730,11 +1735,10 @@ class PwaBrowser {

CustomizableUI.createWidget({
id: 'back-button-ffpwa',
l10nId: 'toolbar-button-back-ffpwa',
shortcutId: 'goBackKb',
type: 'button',

label: 'Back',
tooltiptext: 'Go back one page',

onCommand (event) {
const window = event.target.ownerGlobal;
window.BrowserBack(event);
Expand All @@ -1743,11 +1747,10 @@ class PwaBrowser {

CustomizableUI.createWidget({
id: 'forward-button-ffpwa',
l10nId: 'toolbar-button-forward-ffpwa',
shortcutId: 'goForwardKb',
type: 'button',

label: 'Forward',
tooltiptext: 'Go back one page',

onCommand (event) {
const window = event.target.ownerGlobal;
window.BrowserForward(event);
Expand Down
Loading

0 comments on commit b9b09ac

Please sign in to comment.