diff --git a/docs/docs/user-guide/browser.md b/docs/docs/user-guide/browser.md
index 79b2404a..405fb6a7 100644
--- a/docs/docs/user-guide/browser.md
+++ b/docs/docs/user-guide/browser.md
@@ -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.
+
### Disabling keyboard shortcuts
diff --git a/native/userchrome/profile/chrome/pwa/boot.jsm b/native/userchrome/profile/chrome/pwa/boot.jsm
index 1dfce905..2912e9ae 100644
--- a/native/userchrome/profile/chrome/pwa/boot.jsm
+++ b/native/userchrome/profile/chrome/pwa/boot.jsm
@@ -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',
});
@@ -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');
diff --git a/native/userchrome/profile/chrome/pwa/content/browser.jsm b/native/userchrome/profile/chrome/pwa/content/browser.jsm
index 72d67a41..23036ae1 100644
--- a/native/userchrome/profile/chrome/pwa/content/browser.jsm
+++ b/native/userchrome/profile/chrome/pwa/content/browser.jsm
@@ -25,6 +25,7 @@ class PwaBrowser {
//////////////////////////////
prepareLayout () {
+ this.loadLocalizationSources();
this.supportSmallWindowSizes();
this.createInfoElements();
this.createAddressInput();
@@ -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';
@@ -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');
}
@@ -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, () => {
@@ -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));
@@ -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
@@ -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 () {
@@ -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 (_) {}
});
}
@@ -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 &&
@@ -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);
@@ -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 });
@@ -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;
@@ -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 });
@@ -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));
@@ -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;
@@ -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,
});
@@ -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;
@@ -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;
@@ -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) {
@@ -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;
@@ -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;
@@ -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,
@@ -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,
@@ -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);
@@ -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);
diff --git a/native/userchrome/profile/chrome/pwa/content/preferences.jsm b/native/userchrome/profile/chrome/pwa/content/preferences.jsm
index ecd7c1ec..f4a852f2 100644
--- a/native/userchrome/profile/chrome/pwa/content/preferences.jsm
+++ b/native/userchrome/profile/chrome/pwa/content/preferences.jsm
@@ -1,4 +1,5 @@
XPCOMUtils.defineLazyModuleGetters(this, {
+ ShortcutUtils: 'resource://gre/modules/ShortcutUtils.jsm',
hookFunction: 'resource://pwa/utils/hookFunction.jsm',
xPref: 'resource://pwa/utils/xPref.jsm',
});
@@ -7,7 +8,13 @@ class PwaPreferences {
preferenceElementsAdded = false
constructor () {
+ // Register preference data
this.addPreferenceData();
+
+ // Register preference localization
+ document.l10n.addResourceIds(['pwa/preferences.ftl']);
+
+ // Register preference elements
try { this.addPreferenceElements() } catch {}
hookFunction(gMainPane, 'init', null, () => { this.addPreferenceElements(); });
@@ -44,74 +51,74 @@ class PwaPreferences {
const firefoxpwaGroup = MozXULElement.parseXULToFragment(`
-
-
-
+
+
+
-
-
-
+
+
+
-
-
+
+
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
-
-
-
+
+
+
-
+
@@ -120,18 +127,34 @@ class PwaPreferences {
const shortcutsGroup = MozXULElement.parseXULToFragment(`
-
-
-
-
+
+
+
+
`).firstChild;
+ function setShortcutMessage (selector, messageId, shortcutId) {
+ const target = shortcutsGroup.querySelector(selector);
+ target.setAttribute('data-l10n-id', messageId);
+
+ const shortcutElement = window.browsingContext.topChromeWindow.document.getElementById(shortcutId);
+ if (!shortcutElement) return;
+
+ const shortcutText = ShortcutUtils.prettifyShortcut(shortcutElement);
+ target.setAttribute('data-l10n-args', JSON.stringify({ shortcut: shortcutText }));
+ }
+
+ setShortcutMessage('#shortcutsCloseTab', 'shortcuts-close-tab', 'key_close');
+ setShortcutMessage('#shortcutsCloseWindow', 'shortcuts-close-window', 'key_closeWindow');
+ setShortcutMessage('#shortcutsQuitApplication', 'shortcuts-quit-application', 'key_quitApplication');
+ setShortcutMessage('#shortcutsPrivateBrowsing', 'shortcuts-private-browsing', 'key_privatebrowsing');
+
const startupGroup = document.getElementById('startupGroup');
if (startupGroup.hidden) firefoxpwaGroup.hidden = true;
if (startupGroup.hidden) shortcutsGroup.hidden = true;
@@ -140,7 +163,7 @@ class PwaPreferences {
}
handleTabsModePreferenceSwitch (onLoad = false) {
- function setTabsSectionDisabled(disabled) {
+ function setTabsSectionDisabled (disabled) {
document.querySelectorAll('#mainPrefPane > groupbox:nth-child(11) > *').forEach(elem => elem.disabled = disabled)
document.querySelector('#launchTypeNewTab').disabled = disabled
}
diff --git a/native/userchrome/profile/chrome/pwa/localization/en-US/pwa/appmenu.ftl b/native/userchrome/profile/chrome/pwa/localization/en-US/pwa/appmenu.ftl
new file mode 100644
index 00000000..535b72d4
--- /dev/null
+++ b/native/userchrome/profile/chrome/pwa/localization/en-US/pwa/appmenu.ftl
@@ -0,0 +1,5 @@
+## App menu items should be translated in the same style and capitalization as existing Firefox messages in your language
+## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/appmenu.ftl/?search=appmenuitem-new
+
+app-menu-new-default-browser =
+ .label = New default browser
diff --git a/native/userchrome/profile/chrome/pwa/localization/en-US/pwa/browser.ftl b/native/userchrome/profile/chrome/pwa/localization/en-US/pwa/browser.ftl
new file mode 100644
index 00000000..54ba1a4c
--- /dev/null
+++ b/native/userchrome/profile/chrome/pwa/localization/en-US/pwa/browser.ftl
@@ -0,0 +1,18 @@
+## Toolbars should be translated in the same style and capitalization as existing Firefox messages in your language
+## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/toolbarContextMenu.ftl/?string=231726
+## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browser.ftl/?string=217992
+
+# The label for the tabs/icon bar, when the tabs mode is enabled
+toolbar-tabs-ffpwa =
+ .toolbarname = Tabs Bar
+ .aria-label = Tabs
+
+# The label for the tabs/icon bar, when the tabs mode is disabled
+toolbar-icon-ffpwa =
+ .toolbarname = Icon Bar
+ .aria-label = Icon
+
+## Popup inputs should be translated in imperative mood, without any end punctuation
+
+# The prompt for the address input popup
+popup-address-input = Enter site address
diff --git a/native/userchrome/profile/chrome/pwa/localization/en-US/pwa/contextmenu.ftl b/native/userchrome/profile/chrome/pwa/localization/en-US/pwa/contextmenu.ftl
new file mode 100644
index 00000000..a80b1fa4
--- /dev/null
+++ b/native/userchrome/profile/chrome/pwa/localization/en-US/pwa/contextmenu.ftl
@@ -0,0 +1,31 @@
+## Context menu items should be translated in the same style and capitalization as existing Firefox messages in your language
+## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browserContext.ftl/?search=view-new-tab
+## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browserContext.ftl/?search=open-link
+
+context-menu-image-view-current-tab =
+ .label = Open Image
+ .accesskey = { main-context-menu-image-view-new-tab.accesskey }
+
+context-menu-image-view-new-tab =
+ .label = { main-context-menu-image-view-new-tab.label }
+ .accesskey = { main-context-menu-image-view-new-tab.accesskey }
+
+context-menu-image-view-new-window =
+ .label = Open Image in New Window
+ .accesskey = { main-context-menu-image-view-new-tab.accesskey }
+
+context-menu-video-view-current-tab =
+ .label = Open Video
+ .accesskey = { main-context-menu-video-view-new-tab.accesskey }
+
+context-menu-video-view-new-tab =
+ .label = { main-context-menu-video-view-new-tab.label }
+ .accesskey = { main-context-menu-video-view-new-tab.accesskey }
+
+context-menu-video-view-new-window =
+ .label = Open Video in New Window
+ .accesskey = { main-context-menu-video-view-new-tab.accesskey }
+
+context-menu-open-link-default-browser =
+ .label = Open Link in Default Browser
+ .accesskey = D
diff --git a/native/userchrome/profile/chrome/pwa/localization/en-US/pwa/customizemode.ftl b/native/userchrome/profile/chrome/pwa/localization/en-US/pwa/customizemode.ftl
new file mode 100644
index 00000000..df732b82
--- /dev/null
+++ b/native/userchrome/profile/chrome/pwa/localization/en-US/pwa/customizemode.ftl
@@ -0,0 +1,5 @@
+## Autohide messages should be translated in the same style and capitalization as existing Firefox messages in your language
+## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/customizeMode.ftl/?search=autohide
+
+customize-mode-mute-button-autohide =
+ .label = Hide button when not playing
diff --git a/native/userchrome/profile/chrome/pwa/localization/en-US/pwa/preferences.ftl b/native/userchrome/profile/chrome/pwa/localization/en-US/pwa/preferences.ftl
new file mode 100644
index 00000000..3f12905a
--- /dev/null
+++ b/native/userchrome/profile/chrome/pwa/localization/en-US/pwa/preferences.ftl
@@ -0,0 +1,105 @@
+## Progressive Web Apps Group Details
+
+firefoxpwa-group-header = Progressive Web Apps
+firefoxpwa-group-note = You may need to restart the browser to apply these settings
+
+## Colors Preferences
+
+sites-set-theme-color =
+ .label = Allow web apps to override a theme (titlebar) color
+
+sites-set-background-color =
+ .label = Allow web apps to override a background (window) color
+
+dynamic-theme-color =
+ .label = Allow web apps to dynamically change a theme color
+
+## Titlebar Preferences
+
+dynamic-window-title =
+ .label = Change the window title based on the web app's title
+
+dynamic-window-icon =
+ .label = Change the window icon based on the web app's icon
+
+native-window-controls =
+ .label = Always use native window controls
+
+## User Experience Preferences
+
+open-out-of-scope-in-default-browser =
+ .label = Open out-of-scope URLs in a default browser (can break some web apps)
+
+enable-tabs-mode =
+ .label = Show browser tabs and enable using multi-tabbed web apps
+
+## Links Target Preference
+
+links-target-description = When opening a link that should normally open in a new window or tab
+
+links-target-choice-current-tab =
+ .label = Force links into the current tab
+
+links-target-choice-new-tab =
+ .label = Force links into a new tab
+
+links-target-choice-new-window =
+ .label = Force links into a new window
+
+links-target-choice-keep =
+ .label = Do not change link behaviour
+
+## Launch Type Preference
+
+launch-type-description = When launching a web app that is already opened
+
+launch-type-choice-new-window =
+ .label = Open web app in a new window
+
+launch-type-choice-new-tab =
+ .label = Open web app in a new tab
+
+launch-type-choice-replace =
+ .label = Replace the existing tab
+
+launch-type-choice-focus =
+ .label = Focus the existing window
+
+## Address Bar Preference
+
+display-address-bar-description = Display the address bar
+
+display-address-bar-choice-out-of-scope =
+ .label = When the URL is out-of-scope
+
+display-address-bar-choice-always =
+ .label = Always
+
+display-address-bar-choice-never =
+ .label = Never
+
+## Allowed Domains Preference
+
+allowed-domains-description = Domains always allowed to be opened in the app browser
+
+allowed-domains-input =
+ .placeholder = Enter a comma-separated list of domains...
+
+## Keyboard Shortcuts Group Details
+
+shortcuts-group-header = Keyboard Shortcuts
+shortcuts-group-note = You may need to restart the browser to apply these settings
+
+## Keyboard Shortcuts Preferences
+
+shortcuts-close-tab =
+ .label = Close tab ({ $shortcut })
+
+shortcuts-close-window =
+ .label = Close window ({ $shortcut })
+
+shortcuts-quit-application =
+ .label = Quit application ({ $shortcut })
+
+shortcuts-private-browsing =
+ .label = Private browsing ({ $shortcut })
diff --git a/native/userchrome/profile/chrome/pwa/localization/en-US/pwa/widgets.ftl b/native/userchrome/profile/chrome/pwa/localization/en-US/pwa/widgets.ftl
new file mode 100644
index 00000000..a7a34a41
--- /dev/null
+++ b/native/userchrome/profile/chrome/pwa/localization/en-US/pwa/widgets.ftl
@@ -0,0 +1,61 @@
+## Widget names should be translated in the same style and capitalization as existing Firefox messages in your language
+## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browser.ftl/?search=toolbar-button
+## Example: https://pontoon.mozilla.org/sl/firefox/browser/browser/browser.ftl/?search=navbar
+
+toolbar-button-mute =
+ .label = Toggle Sound
+ .tooltiptext = Toggle the page sound ({ $shortcut })
+
+toolbar-button-reader-view =
+ .label = Reader View
+ .tooltiptext = Toggle the reader view ({ $shortcut })
+
+toolbar-button-copy-link =
+ .label = Copy link
+ .tooltiptext = Copy a link to this page
+
+toolbar-button-share-link =
+ .label = Share link
+ .tooltiptext = Share a link to this page
+
+toolbar-button-send-to-device =
+ .label = Send to Device
+ .tooltiptext = Send this page to another device
+
+toolbar-button-open-in-browser =
+ .label = Open in Browser
+ .tooltiptext = Open this page in browser
+
+toolbar-button-tracking-protection =
+ .label = Tracking Protection
+ .tooltiptext = View information about tracking protection on this site
+
+toolbar-button-identity =
+ .label = Site Information
+ .tooltiptext = View information about this site
+
+toolbar-button-permissions =
+ .label = Site Permissions
+ .tooltiptext = View permissions granted to this site
+
+toolbar-button-notifications =
+ .label = Site Notifications
+ .tooltiptext = Popup notifications for this site
+
+toolbar-button-close =
+ .label = Close
+ .tooltiptext = Close the current page
+
+toolbar-button-home-ffpwa =
+ .label = { navbar-home.label }
+ .tooltiptext = App Start Page
+
+## Internal messages that should not be translated
+
+toolbar-button-back-ffpwa =
+ .label = { main-context-menu-back-2.aria-label }
+ .tooltiptext = { main-context-menu-back-2.tooltiptext }
+
+toolbar-button-forward-ffpwa =
+ .label = { main-context-menu-forward-2.aria-label }
+ .tooltiptext = { main-context-menu-forward-2.tooltiptext }