diff options
Diffstat (limited to 'python/l10n')
33 files changed, 3563 insertions, 0 deletions
diff --git a/python/l10n/fluent_migrations/__init__.py b/python/l10n/fluent_migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/python/l10n/fluent_migrations/__init__.py diff --git a/python/l10n/fluent_migrations/bug_1552333_aboutCertError.py b/python/l10n/fluent_migrations/bug_1552333_aboutCertError.py new file mode 100644 index 0000000000..5c8300e01f --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1552333_aboutCertError.py @@ -0,0 +1,40 @@ +# coding=utf8 + +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +import fluent.syntax.ast as FTL +from fluent.migrate.helpers import transforms_from +from fluent.migrate.helpers import VARIABLE_REFERENCE +from fluent.migrate import COPY, REPLACE + + +def migrate(ctx): + """Bug 1552333 - Migrate strings from pipnss.properties to aboutCertError.ftl""" + ctx.add_transforms( + "browser/browser/aboutCertError.ftl", + "browser/browser/aboutCertError.ftl", + transforms_from( + """ +cert-error-symantec-distrust-admin = { COPY(from_path, "certErrorSymantecDistrustAdministrator") } +""", + from_path="security/manager/chrome/pipnss/pipnss.properties", + ), + ) + ctx.add_transforms( + "browser/browser/aboutCertError.ftl", + "browser/browser/aboutCertError.ftl", + [ + FTL.Message( + id=FTL.Identifier("cert-error-symantec-distrust-description"), + value=REPLACE( + "security/manager/chrome/pipnss/pipnss.properties", + "certErrorSymantecDistrustDescription1", + { + "%1$S": VARIABLE_REFERENCE("hostname"), + }, + normalize_printf=True, + ), + ), + ], + ) diff --git a/python/l10n/fluent_migrations/bug_1635548_browser_context.py b/python/l10n/fluent_migrations/bug_1635548_browser_context.py new file mode 100644 index 0000000000..33bd0efc95 --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1635548_browser_context.py @@ -0,0 +1,82 @@ +# coding=utf8 + +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +import fluent.syntax.ast as FTL +from fluent.migrate.helpers import transforms_from, VARIABLE_REFERENCE +from fluent.migrate import REPLACE, COPY + + +def migrate(ctx): + """Bug 1635548 - Migrate browser-context.inc to Fluent, part {index}""" + target = "toolkit/toolkit/global/textActions.ftl" + reference = "toolkit/toolkit/global/textActions.ftl" + ctx.add_transforms( + target, + reference, + transforms_from( + """ +text-action-spell-add-to-dictionary = + .label = { COPY(from_path, "spellAddToDictionary.label") } + .accesskey = { COPY(from_path, "spellAddToDictionary.accesskey") } + +text-action-spell-undo-add-to-dictionary = + .label = { COPY(from_path, "spellUndoAddToDictionary.label") } + .accesskey = { COPY(from_path, "spellUndoAddToDictionary.accesskey") } + +text-action-spell-check-toggle = + .label = { COPY(from_path, "spellCheckToggle.label") } + .accesskey = { COPY(from_path, "spellCheckToggle.accesskey") } + +text-action-spell-dictionaries = + .label = { COPY(from_path, "spellDictionaries.label") } + .accesskey = { COPY(from_path, "spellDictionaries.accesskey") } +""", + from_path="toolkit/chrome/global/textcontext.dtd", + ), + ) + + target = "toolkit/toolkit/global/textActions.ftl" + reference = "toolkit/toolkit/global/textActions.ftl" + ctx.add_transforms( + target, + reference, + transforms_from( + """ +text-action-spell-add-dictionaries = + .label = { COPY(from_path, "spellAddDictionaries.label") } + .accesskey = { COPY(from_path, "spellAddDictionaries.accesskey") } +""", + from_path="browser/chrome/browser/browser.dtd", + ), + ) + + target = "browser/browser/browserContext.ftl" + reference = "browser/browser/browserContext.ftl" + ctx.add_transforms( + target, + reference, + [ + FTL.Message( + id=FTL.Identifier("main-context-menu-open-link-in-container-tab"), + attributes=[ + FTL.Attribute( + FTL.Identifier("label"), + REPLACE( + "browser/chrome/browser/browser.properties", + "userContextOpenLink.label", + {"%1$S": VARIABLE_REFERENCE("containerName")}, + ), + ), + FTL.Attribute( + FTL.Identifier("accesskey"), + COPY( + "browser/chrome/browser/browser.dtd", + "openLinkCmdInTab.accesskey", + ), + ), + ], + ) + ], + ) diff --git a/python/l10n/fluent_migrations/bug_1738056_aboutDialog_channel.py b/python/l10n/fluent_migrations/bug_1738056_aboutDialog_channel.py new file mode 100644 index 0000000000..b867155ebf --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1738056_aboutDialog_channel.py @@ -0,0 +1,33 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +import fluent.syntax.ast as FTL +import re + +from fluent.migrate.transforms import TransformPattern + + +class INSERT_VARIABLE(TransformPattern): + def visit_TextElement(self, node): + node.value = re.sub( + 'current-channel"></label', + 'current-channel">{ $channel }</label', + node.value, + ) + return node + + +def migrate(ctx): + """Bug 1738056 - Convert about dialog channel listing to fluent, part {index}.""" + + about_dialog_ftl = "browser/browser/aboutDialog.ftl" + ctx.add_transforms( + about_dialog_ftl, + about_dialog_ftl, + [ + FTL.Message( + id=FTL.Identifier("aboutdialog-channel-description"), + value=INSERT_VARIABLE(about_dialog_ftl, "channel-description"), + ), + ], + ) diff --git a/python/l10n/fluent_migrations/bug_1786186_mobile_aboutConfig.py b/python/l10n/fluent_migrations/bug_1786186_mobile_aboutConfig.py new file mode 100644 index 0000000000..99c6673f92 --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1786186_mobile_aboutConfig.py @@ -0,0 +1,65 @@ +# coding=utf8 + +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +import fluent.syntax.ast as FTL +from fluent.migrate.helpers import transforms_from +from fluent.migrate.transforms import COPY + + +def migrate(ctx): + """Bug 1786186 - Migrate mobile about:config to Fluent, part {index}""" + + target = "mobile/android/mobile/android/aboutConfig.ftl" + + ctx.add_transforms( + target, + target, + transforms_from( + """ + +config-toolbar-search = + .placeholder = { COPY(path1, "toolbar.searchPlaceholder") } +config-new-pref-name = + .placeholder = { COPY(path1, "newPref.namePlaceholder") } +config-new-pref-value-boolean = { COPY(path1, "newPref.valueBoolean") } +config-new-pref-value-string = { COPY(path1, "newPref.valueString") } +config-new-pref-value-integer = { COPY(path1, "newPref.valueInteger") } +config-new-pref-string = + .placeholder = { COPY(path1, "newPref.stringPlaceholder") } +config-new-pref-number = + .placeholder = { COPY(path1, "newPref.numberPlaceholder") } +config-new-pref-cancel-button = { COPY(path1, "newPref.cancelButton") } +config-context-menu-copy-pref-name = + .label = { COPY(path1, "contextMenu.copyPrefName") } +config-context-menu-copy-pref-value = + .label = { COPY(path1, "contextMenu.copyPrefValue") } +""", + path1="mobile/android/chrome/config.dtd", + ), + ) + + source = "mobile/android/chrome/config.properties" + ctx.add_transforms( + target, + target, + [ + FTL.Message( + id=FTL.Identifier("config-new-pref-create-button"), + value=COPY(source, "newPref.createButton"), + ), + FTL.Message( + id=FTL.Identifier("config-new-pref-change-button"), + value=COPY(source, "newPref.changeButton"), + ), + FTL.Message( + id=FTL.Identifier("config-pref-toggle-button"), + value=COPY(source, "pref.toggleButton"), + ), + FTL.Message( + id=FTL.Identifier("config-pref-reset-button"), + value=COPY(source, "pref.resetButton"), + ), + ], + ) diff --git a/python/l10n/fluent_migrations/bug_1793557_extensions.py b/python/l10n/fluent_migrations/bug_1793557_extensions.py new file mode 100644 index 0000000000..0c04a87509 --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1793557_extensions.py @@ -0,0 +1,912 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +import fluent.syntax.ast as FTL +from fluent.migrate.helpers import TERM_REFERENCE, VARIABLE_REFERENCE +from fluent.migrate.transforms import ( + COPY, + COPY_PATTERN, + PLURALS, + REPLACE, + REPLACE_IN_TEXT, +) + + +def migrate(ctx): + """Bug 1793557 - Convert extension strings to Fluent, part {index}.""" + + browser_properties = "browser/chrome/browser/browser.properties" + browser_ftl = "browser/browser/browser.ftl" + notifications = "browser/browser/addonNotifications.ftl" + extensions_ui = "browser/browser/extensionsUI.ftl" + extensions = "toolkit/toolkit/global/extensions.ftl" + permissions = "toolkit/toolkit/global/extensionPermissions.ftl" + + ctx.add_transforms( + browser_ftl, + browser_ftl, + [ + FTL.Message( + id=FTL.Identifier("popup-notification-addon-install-unsigned"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("value"), + value=COPY(browser_properties, "addonInstall.unsigned"), + ) + ], + ), + ], + ) + + ctx.add_transforms( + notifications, + notifications, + [ + FTL.Message( + id=FTL.Identifier("xpinstall-prompt"), + value=REPLACE( + browser_properties, + "xpinstallPromptMessage", + {"%1$S": TERM_REFERENCE("brand-short-name")}, + ), + ), + FTL.Message( + id=FTL.Identifier("xpinstall-prompt-header"), + value=REPLACE( + browser_properties, + "xpinstallPromptMessage.header", + {"%1$S": VARIABLE_REFERENCE("host")}, + ), + ), + FTL.Message( + id=FTL.Identifier("xpinstall-prompt-message"), + value=REPLACE( + browser_properties, + "xpinstallPromptMessage.message", + {"%1$S": VARIABLE_REFERENCE("host")}, + ), + ), + FTL.Message( + id=FTL.Identifier("xpinstall-prompt-header-unknown"), + value=COPY(browser_properties, "xpinstallPromptMessage.header.unknown"), + ), + FTL.Message( + id=FTL.Identifier("xpinstall-prompt-message-unknown"), + value=COPY( + browser_properties, "xpinstallPromptMessage.message.unknown" + ), + ), + FTL.Message( + id=FTL.Identifier("xpinstall-prompt-dont-allow"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY( + browser_properties, "xpinstallPromptMessage.dontAllow" + ), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY( + browser_properties, + "xpinstallPromptMessage.dontAllow.accesskey", + ), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("xpinstall-prompt-never-allow"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY( + browser_properties, "xpinstallPromptMessage.neverAllow" + ), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY( + browser_properties, + "xpinstallPromptMessage.neverAllow.accesskey", + ), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("xpinstall-prompt-never-allow-and-report"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY( + browser_properties, + "xpinstallPromptMessage.neverAllowAndReport", + ), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY( + browser_properties, + "xpinstallPromptMessage.neverAllowAndReport.accesskey", + ), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("site-permission-install-first-prompt-midi-header"), + value=COPY( + browser_properties, "sitePermissionInstallFirstPrompt.midi.header" + ), + ), + FTL.Message( + id=FTL.Identifier("site-permission-install-first-prompt-midi-message"), + value=COPY( + browser_properties, "sitePermissionInstallFirstPrompt.midi.message" + ), + ), + FTL.Message( + id=FTL.Identifier("xpinstall-prompt-install"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY( + browser_properties, "xpinstallPromptMessage.install" + ), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY( + browser_properties, + "xpinstallPromptMessage.install.accesskey", + ), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("xpinstall-disabled-locked"), + value=COPY(browser_properties, "xpinstallDisabledMessageLocked"), + ), + FTL.Message( + id=FTL.Identifier("xpinstall-disabled"), + value=COPY(browser_properties, "xpinstallDisabledMessage"), + ), + FTL.Message( + id=FTL.Identifier("xpinstall-disabled-button"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY(browser_properties, "xpinstallDisabledButton"), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY( + browser_properties, "xpinstallDisabledButton.accesskey" + ), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("addon-install-blocked-by-policy"), + value=REPLACE( + browser_properties, + "addonInstallBlockedByPolicy", + { + "%1$S": VARIABLE_REFERENCE("addonName"), + "%2$S": VARIABLE_REFERENCE("addonId"), + "%3$S": FTL.TextElement(""), + }, + ), + ), + FTL.Message( + id=FTL.Identifier("addon-domain-blocked-by-policy"), + value=COPY(browser_properties, "addonDomainBlockedByPolicy"), + ), + FTL.Message( + id=FTL.Identifier("addon-install-full-screen-blocked"), + value=COPY(browser_properties, "addonInstallFullScreenBlocked"), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-sideload-menu-item"), + value=REPLACE( + browser_properties, + "webextPerms.sideloadMenuItem", + { + "%1$S": VARIABLE_REFERENCE("addonName"), + "%2$S": TERM_REFERENCE("brand-short-name"), + }, + ), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-update-menu-item"), + value=REPLACE( + browser_properties, + "webextPerms.updateMenuItem", + {"%1$S": VARIABLE_REFERENCE("addonName")}, + ), + ), + FTL.Message( + id=FTL.Identifier("addon-removal-title"), + value=COPY_PATTERN(browser_ftl, "addon-removal-title"), + ), + FTL.Message( + id=FTL.Identifier("addon-removal-message"), + value=REPLACE( + browser_properties, + "webext.remove.confirmation.message", + { + "%1$S": VARIABLE_REFERENCE("name"), + "%2$S": TERM_REFERENCE("brand-shorter-name"), + }, + ), + ), + FTL.Message( + id=FTL.Identifier("addon-removal-button"), + value=COPY(browser_properties, "webext.remove.confirmation.button"), + ), + FTL.Message( + id=FTL.Identifier("addon-removal-abuse-report-checkbox"), + value=COPY_PATTERN(browser_ftl, "addon-removal-abuse-report-checkbox"), + ), + FTL.Message( + id=FTL.Identifier("addon-downloading-and-verifying"), + value=PLURALS( + browser_properties, + "addonDownloadingAndVerifying", + VARIABLE_REFERENCE("addonCount"), + foreach=lambda n: REPLACE_IN_TEXT( + n, + {"#1": VARIABLE_REFERENCE("addonCount")}, + ), + ), + ), + FTL.Message( + id=FTL.Identifier("addon-download-verifying"), + value=COPY(browser_properties, "addonDownloadVerifying"), + ), + FTL.Message( + id=FTL.Identifier("addon-install-cancel-button"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY( + browser_properties, "addonInstall.cancelButton.label" + ), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY( + browser_properties, "addonInstall.cancelButton.accesskey" + ), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("addon-install-accept-button"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY( + browser_properties, "addonInstall.acceptButton2.label" + ), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY( + browser_properties, "addonInstall.acceptButton2.accesskey" + ), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("addon-confirm-install-message"), + value=PLURALS( + browser_properties, + "addonConfirmInstall.message", + VARIABLE_REFERENCE("addonCount"), + foreach=lambda n: REPLACE_IN_TEXT( + n, + { + "#1": TERM_REFERENCE("brand-short-name"), + "#2": VARIABLE_REFERENCE("addonCount"), + }, + ), + ), + ), + FTL.Message( + id=FTL.Identifier("addon-confirm-install-unsigned-message"), + value=PLURALS( + browser_properties, + "addonConfirmInstallUnsigned.message", + VARIABLE_REFERENCE("addonCount"), + foreach=lambda n: REPLACE_IN_TEXT( + n, + { + "#1": TERM_REFERENCE("brand-short-name"), + "#2": VARIABLE_REFERENCE("addonCount"), + }, + ), + ), + ), + FTL.Message( + id=FTL.Identifier("addon-confirm-install-some-unsigned-message"), + value=PLURALS( + browser_properties, + "addonConfirmInstallSomeUnsigned.message", + VARIABLE_REFERENCE("addonCount"), + foreach=lambda n: REPLACE_IN_TEXT( + n, + { + "#1": TERM_REFERENCE("brand-short-name"), + "#2": VARIABLE_REFERENCE("addonCount"), + }, + ), + ), + ), + FTL.Message( + id=FTL.Identifier("addon-install-error-network-failure"), + value=COPY(browser_properties, "addonInstallError-1"), + ), + FTL.Message( + id=FTL.Identifier("addon-install-error-incorrect-hash"), + value=REPLACE( + browser_properties, + "addonInstallError-2", + {"%1$S": TERM_REFERENCE("brand-short-name")}, + ), + ), + FTL.Message( + id=FTL.Identifier("addon-install-error-corrupt-file"), + value=COPY(browser_properties, "addonInstallError-3"), + ), + FTL.Message( + id=FTL.Identifier("addon-install-error-file-access"), + value=REPLACE( + browser_properties, + "addonInstallError-4", + { + "%2$S": VARIABLE_REFERENCE("addonName"), + "%1$S": TERM_REFERENCE("brand-short-name"), + }, + ), + ), + FTL.Message( + id=FTL.Identifier("addon-install-error-not-signed"), + value=REPLACE( + browser_properties, + "addonInstallError-5", + {"%1$S": TERM_REFERENCE("brand-short-name")}, + ), + ), + FTL.Message( + id=FTL.Identifier("addon-install-error-invalid-domain"), + value=REPLACE( + browser_properties, + "addonInstallError-8", + {"%2$S": VARIABLE_REFERENCE("addonName")}, + ), + ), + FTL.Message( + id=FTL.Identifier("addon-local-install-error-network-failure"), + value=COPY(browser_properties, "addonLocalInstallError-1"), + ), + FTL.Message( + id=FTL.Identifier("addon-local-install-error-incorrect-hash"), + value=REPLACE( + browser_properties, + "addonLocalInstallError-2", + {"%1$S": TERM_REFERENCE("brand-short-name")}, + ), + ), + FTL.Message( + id=FTL.Identifier("addon-local-install-error-corrupt-file"), + value=COPY(browser_properties, "addonLocalInstallError-3"), + ), + FTL.Message( + id=FTL.Identifier("addon-local-install-error-file-access"), + value=REPLACE( + browser_properties, + "addonLocalInstallError-4", + { + "%2$S": VARIABLE_REFERENCE("addonName"), + "%1$S": TERM_REFERENCE("brand-short-name"), + }, + ), + ), + FTL.Message( + id=FTL.Identifier("addon-local-install-error-not-signed"), + value=COPY(browser_properties, "addonLocalInstallError-5"), + ), + FTL.Message( + id=FTL.Identifier("addon-install-error-incompatible"), + value=REPLACE( + browser_properties, + "addonInstallErrorIncompatible", + { + "%3$S": VARIABLE_REFERENCE("addonName"), + "%1$S": TERM_REFERENCE("brand-short-name"), + "%2$S": VARIABLE_REFERENCE("appVersion"), + }, + ), + ), + FTL.Message( + id=FTL.Identifier("addon-install-error-blocklisted"), + value=REPLACE( + browser_properties, + "addonInstallErrorBlocklisted", + {"%1$S": VARIABLE_REFERENCE("addonName")}, + ), + ), + ], + ) + + ctx.add_transforms( + extensions_ui, + extensions_ui, + [ + FTL.Message( + id=FTL.Identifier("webext-perms-learn-more"), + value=COPY(browser_properties, "webextPerms.learnMore2"), + ), + FTL.Message( + id=FTL.Identifier("webext-default-search-description"), + value=REPLACE( + browser_properties, + "webext.defaultSearch.description", + { + "%1$S": VARIABLE_REFERENCE("addonName"), + "%2$S": VARIABLE_REFERENCE("currentEngine"), + "%3$S": VARIABLE_REFERENCE("newEngine"), + }, + ), + ), + FTL.Message( + id=FTL.Identifier("webext-default-search-yes"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY(browser_properties, "webext.defaultSearchYes.label"), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY( + browser_properties, "webext.defaultSearchYes.accessKey" + ), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("webext-default-search-no"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY(browser_properties, "webext.defaultSearchNo.label"), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY( + browser_properties, "webext.defaultSearchNo.accessKey" + ), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("addon-post-install-message"), + value=REPLACE( + browser_properties, + "addonPostInstall.message3", + {"%1$S": VARIABLE_REFERENCE("addonName")}, + ), + ), + ], + ) + + ctx.add_transforms( + extensions, + extensions, + [ + FTL.Message( + id=FTL.Identifier("webext-perms-header"), + value=REPLACE( + browser_properties, + "webextPerms.header", + {"%1$S": VARIABLE_REFERENCE("extension")}, + ), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-header-with-perms"), + value=REPLACE( + browser_properties, + "webextPerms.headerWithPerms", + {"%1$S": VARIABLE_REFERENCE("extension")}, + ), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-header-unsigned"), + value=REPLACE( + browser_properties, + "webextPerms.headerUnsigned", + {"%1$S": VARIABLE_REFERENCE("extension")}, + ), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-header-unsigned-with-perms"), + value=REPLACE( + browser_properties, + "webextPerms.headerUnsignedWithPerms", + {"%1$S": VARIABLE_REFERENCE("extension")}, + ), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-add"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY(browser_properties, "webextPerms.add.label"), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY(browser_properties, "webextPerms.add.accessKey"), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("webext-perms-cancel"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY(browser_properties, "webextPerms.cancel.label"), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY(browser_properties, "webextPerms.cancel.accessKey"), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("webext-perms-sideload-header"), + value=REPLACE( + browser_properties, + "webextPerms.sideloadHeader", + {"%1$S": VARIABLE_REFERENCE("extension")}, + ), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-sideload-text"), + value=COPY(browser_properties, "webextPerms.sideloadText2"), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-sideload-text-no-perms"), + value=COPY(browser_properties, "webextPerms.sideloadTextNoPerms"), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-sideload-enable"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY( + browser_properties, "webextPerms.sideloadEnable.label" + ), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY( + browser_properties, "webextPerms.sideloadEnable.accessKey" + ), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("webext-perms-sideload-cancel"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY( + browser_properties, "webextPerms.sideloadCancel.label" + ), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY( + browser_properties, "webextPerms.sideloadCancel.accessKey" + ), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("webext-perms-update-text"), + value=REPLACE( + browser_properties, + "webextPerms.updateText2", + {"%1$S": VARIABLE_REFERENCE("extension")}, + ), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-update-accept"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY( + browser_properties, "webextPerms.updateAccept.label" + ), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY( + browser_properties, "webextPerms.updateAccept.accessKey" + ), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("webext-perms-optional-perms-header"), + value=REPLACE( + browser_properties, + "webextPerms.optionalPermsHeader", + {"%1$S": VARIABLE_REFERENCE("extension")}, + ), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-optional-perms-list-intro"), + value=COPY(browser_properties, "webextPerms.optionalPermsListIntro"), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-optional-perms-allow"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY( + browser_properties, "webextPerms.optionalPermsAllow.label" + ), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY( + browser_properties, + "webextPerms.optionalPermsAllow.accessKey", + ), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("webext-perms-optional-perms-deny"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY( + browser_properties, "webextPerms.optionalPermsDeny.label" + ), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY( + browser_properties, + "webextPerms.optionalPermsDeny.accessKey", + ), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("webext-perms-host-description-all-urls"), + value=COPY(browser_properties, "webextPerms.hostDescription.allUrls"), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-host-description-wildcard"), + value=REPLACE( + browser_properties, + "webextPerms.hostDescription.wildcard", + {"%1$S": VARIABLE_REFERENCE("domain")}, + ), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-host-description-too-many-wildcards"), + value=PLURALS( + browser_properties, + "webextPerms.hostDescription.tooManyWildcards", + VARIABLE_REFERENCE("domainCount"), + foreach=lambda n: REPLACE_IN_TEXT( + n, + {"#1": VARIABLE_REFERENCE("domainCount")}, + ), + ), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-host-description-one-site"), + value=REPLACE( + browser_properties, + "webextPerms.hostDescription.oneSite", + {"%1$S": VARIABLE_REFERENCE("domain")}, + ), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-host-description-too-many-sites"), + value=PLURALS( + browser_properties, + "webextPerms.hostDescription.tooManySites", + VARIABLE_REFERENCE("domainCount"), + foreach=lambda n: REPLACE_IN_TEXT( + n, + {"#1": VARIABLE_REFERENCE("domainCount")}, + ), + ), + ), + FTL.Message( + id=FTL.Identifier("webext-site-perms-header-with-gated-perms-midi"), + value=REPLACE( + browser_properties, + "webextSitePerms.headerWithGatedPerms.midi", + { + "%1$S": VARIABLE_REFERENCE("hostname"), + }, + ), + ), + FTL.Message( + id=FTL.Identifier( + "webext-site-perms-header-with-gated-perms-midi-sysex" + ), + value=REPLACE( + browser_properties, + "webextSitePerms.headerWithGatedPerms.midi-sysex", + { + "%1$S": VARIABLE_REFERENCE("hostname"), + }, + ), + ), + FTL.Message( + id=FTL.Identifier("webext-site-perms-description-gated-perms-midi"), + value=COPY( + browser_properties, "webextSitePerms.descriptionGatedPerms.midi" + ), + ), + FTL.Message( + id=FTL.Identifier("webext-site-perms-header-with-perms"), + value=REPLACE( + browser_properties, + "webextSitePerms.headerWithPerms", + { + "%1$S": VARIABLE_REFERENCE("extension"), + "%2$S": VARIABLE_REFERENCE("hostname"), + }, + ), + ), + FTL.Message( + id=FTL.Identifier("webext-site-perms-header-unsigned-with-perms"), + value=REPLACE( + browser_properties, + "webextSitePerms.headerUnsignedWithPerms", + { + "%1$S": VARIABLE_REFERENCE("extension"), + "%2$S": VARIABLE_REFERENCE("hostname"), + }, + ), + ), + FTL.Message( + id=FTL.Identifier("webext-site-perms-midi"), + value=COPY(browser_properties, "webextSitePerms.description.midi"), + ), + FTL.Message( + id=FTL.Identifier("webext-site-perms-midi-sysex"), + value=COPY( + browser_properties, "webextSitePerms.description.midi-sysex" + ), + ), + ], + ) + + ctx.add_transforms( + permissions, + permissions, + [ + FTL.Message( + id=FTL.Identifier("webext-perms-description-bookmarks"), + value=COPY(browser_properties, "webextPerms.description.bookmarks"), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-description-browserSettings"), + value=COPY( + browser_properties, "webextPerms.description.browserSettings" + ), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-description-browsingData"), + value=COPY(browser_properties, "webextPerms.description.browsingData"), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-description-clipboardRead"), + value=COPY(browser_properties, "webextPerms.description.clipboardRead"), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-description-clipboardWrite"), + value=COPY( + browser_properties, "webextPerms.description.clipboardWrite" + ), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-description-declarativeNetRequest"), + value=COPY( + browser_properties, "webextPerms.description.declarativeNetRequest" + ), + ), + FTL.Message( + id=FTL.Identifier( + "webext-perms-description-declarativeNetRequestFeedback" + ), + value=COPY( + browser_properties, + "webextPerms.description.declarativeNetRequestFeedback", + ), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-description-devtools"), + value=COPY(browser_properties, "webextPerms.description.devtools"), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-description-downloads"), + value=COPY(browser_properties, "webextPerms.description.downloads"), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-description-downloads-open"), + value=COPY( + browser_properties, "webextPerms.description.downloads.open" + ), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-description-find"), + value=COPY(browser_properties, "webextPerms.description.find"), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-description-geolocation"), + value=COPY(browser_properties, "webextPerms.description.geolocation"), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-description-history"), + value=COPY(browser_properties, "webextPerms.description.history"), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-description-management"), + value=COPY(browser_properties, "webextPerms.description.management"), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-description-nativeMessaging"), + value=REPLACE( + browser_properties, + "webextPerms.description.nativeMessaging", + {"%1$S": TERM_REFERENCE("brand-short-name")}, + ), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-description-notifications"), + value=COPY(browser_properties, "webextPerms.description.notifications"), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-description-pkcs11"), + value=COPY(browser_properties, "webextPerms.description.pkcs11"), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-description-privacy"), + value=COPY(browser_properties, "webextPerms.description.privacy"), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-description-proxy"), + value=COPY(browser_properties, "webextPerms.description.proxy"), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-description-sessions"), + value=COPY(browser_properties, "webextPerms.description.sessions"), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-description-tabs"), + value=COPY(browser_properties, "webextPerms.description.tabs"), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-description-tabHide"), + value=COPY(browser_properties, "webextPerms.description.tabHide"), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-description-topSites"), + value=COPY(browser_properties, "webextPerms.description.topSites"), + ), + FTL.Message( + id=FTL.Identifier("webext-perms-description-webNavigation"), + value=COPY(browser_properties, "webextPerms.description.webNavigation"), + ), + ], + ) diff --git a/python/l10n/fluent_migrations/bug_1793572_webrtc.py b/python/l10n/fluent_migrations/bug_1793572_webrtc.py new file mode 100644 index 0000000000..eb07f939a8 --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1793572_webrtc.py @@ -0,0 +1,771 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +import fluent.syntax.ast as FTL +from fluent.migrate.helpers import TERM_REFERENCE, VARIABLE_REFERENCE +from fluent.migrate.transforms import ( + COPY, + COPY_PATTERN, + PLURALS, + REPLACE, + REPLACE_IN_TEXT, +) + + +def migrate(ctx): + """Bug 1793572 - Convert WebRTC strings to Fluent, part {index}.""" + + source = "browser/chrome/browser/webrtcIndicator.properties" + browser = "browser/chrome/browser/browser.properties" + browser_ftl = "browser/browser/browser.ftl" + target = "browser/browser/webrtcIndicator.ftl" + + ctx.add_transforms( + target, + target, + [ + FTL.Message( + id=FTL.Identifier("webrtc-indicator-window"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("title"), + value=COPY_PATTERN(target, "webrtc-indicator-title"), + ) + ], + ), + FTL.Message( + id=FTL.Identifier("webrtc-indicator-sharing-camera-and-microphone"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("tooltiptext"), + value=COPY( + source, "webrtcIndicator.sharingCameraAndMicrophone.tooltip" + ), + ) + ], + ), + FTL.Message( + id=FTL.Identifier("webrtc-indicator-sharing-camera"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("tooltiptext"), + value=COPY(source, "webrtcIndicator.sharingCamera.tooltip"), + ) + ], + ), + FTL.Message( + id=FTL.Identifier("webrtc-indicator-sharing-microphone"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("tooltiptext"), + value=COPY(source, "webrtcIndicator.sharingMicrophone.tooltip"), + ) + ], + ), + FTL.Message( + id=FTL.Identifier("webrtc-indicator-sharing-application"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("tooltiptext"), + value=COPY( + source, "webrtcIndicator.sharingApplication.tooltip" + ), + ) + ], + ), + FTL.Message( + id=FTL.Identifier("webrtc-indicator-sharing-screen"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("tooltiptext"), + value=COPY(source, "webrtcIndicator.sharingScreen.tooltip"), + ) + ], + ), + FTL.Message( + id=FTL.Identifier("webrtc-indicator-sharing-window"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("tooltiptext"), + value=COPY(source, "webrtcIndicator.sharingWindow.tooltip"), + ) + ], + ), + FTL.Message( + id=FTL.Identifier("webrtc-indicator-sharing-browser"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("tooltiptext"), + value=COPY(source, "webrtcIndicator.sharingBrowser.tooltip"), + ) + ], + ), + FTL.Message( + id=FTL.Identifier("webrtc-indicator-menuitem-control-sharing"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY(source, "webrtcIndicator.controlSharing.menuitem"), + ) + ], + ), + FTL.Message( + id=FTL.Identifier("webrtc-indicator-menuitem-control-sharing-on"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=REPLACE( + source, + "webrtcIndicator.controlSharingOn.menuitem", + {"%1$S": VARIABLE_REFERENCE("streamTitle")}, + ), + ) + ], + ), + FTL.Message( + id=FTL.Identifier("webrtc-indicator-menuitem-sharing-camera-with"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=REPLACE( + source, + "webrtcIndicator.sharingCameraWith.menuitem", + {"%1$S": VARIABLE_REFERENCE("streamTitle")}, + ), + ) + ], + ), + FTL.Message( + id=FTL.Identifier("webrtc-indicator-menuitem-sharing-microphone-with"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=REPLACE( + source, + "webrtcIndicator.sharingMicrophoneWith.menuitem", + {"%1$S": VARIABLE_REFERENCE("streamTitle")}, + ), + ) + ], + ), + FTL.Message( + id=FTL.Identifier("webrtc-indicator-menuitem-sharing-application-with"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=REPLACE( + source, + "webrtcIndicator.sharingApplicationWith.menuitem", + {"%1$S": VARIABLE_REFERENCE("streamTitle")}, + ), + ) + ], + ), + FTL.Message( + id=FTL.Identifier("webrtc-indicator-menuitem-sharing-screen-with"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=REPLACE( + source, + "webrtcIndicator.sharingScreenWith.menuitem", + {"%1$S": VARIABLE_REFERENCE("streamTitle")}, + ), + ) + ], + ), + FTL.Message( + id=FTL.Identifier("webrtc-indicator-menuitem-sharing-window-with"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=REPLACE( + source, + "webrtcIndicator.sharingWindowWith.menuitem", + {"%1$S": VARIABLE_REFERENCE("streamTitle")}, + ), + ) + ], + ), + FTL.Message( + id=FTL.Identifier("webrtc-indicator-menuitem-sharing-browser-with"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=REPLACE( + source, + "webrtcIndicator.sharingBrowserWith.menuitem", + {"%1$S": VARIABLE_REFERENCE("streamTitle")}, + ), + ) + ], + ), + FTL.Message( + id=FTL.Identifier( + "webrtc-indicator-menuitem-sharing-camera-with-n-tabs" + ), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=PLURALS( + source, + "webrtcIndicator.sharingCameraWithNTabs.menuitem", + VARIABLE_REFERENCE("tabCount"), + foreach=lambda n: REPLACE_IN_TEXT( + n, + {"#1": VARIABLE_REFERENCE("tabCount")}, + ), + ), + ) + ], + ), + FTL.Message( + id=FTL.Identifier( + "webrtc-indicator-menuitem-sharing-microphone-with-n-tabs" + ), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=PLURALS( + source, + "webrtcIndicator.sharingMicrophoneWithNTabs.menuitem", + VARIABLE_REFERENCE("tabCount"), + foreach=lambda n: REPLACE_IN_TEXT( + n, + {"#1": VARIABLE_REFERENCE("tabCount")}, + ), + ), + ) + ], + ), + FTL.Message( + id=FTL.Identifier( + "webrtc-indicator-menuitem-sharing-application-with-n-tabs" + ), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=PLURALS( + source, + "webrtcIndicator.sharingApplicationWithNTabs.menuitem", + VARIABLE_REFERENCE("tabCount"), + foreach=lambda n: REPLACE_IN_TEXT( + n, + {"#1": VARIABLE_REFERENCE("tabCount")}, + ), + ), + ) + ], + ), + FTL.Message( + id=FTL.Identifier( + "webrtc-indicator-menuitem-sharing-screen-with-n-tabs" + ), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=PLURALS( + source, + "webrtcIndicator.sharingScreenWithNTabs.menuitem", + VARIABLE_REFERENCE("tabCount"), + foreach=lambda n: REPLACE_IN_TEXT( + n, + {"#1": VARIABLE_REFERENCE("tabCount")}, + ), + ), + ) + ], + ), + FTL.Message( + id=FTL.Identifier( + "webrtc-indicator-menuitem-sharing-window-with-n-tabs" + ), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=PLURALS( + source, + "webrtcIndicator.sharingWindowWithNTabs.menuitem", + VARIABLE_REFERENCE("tabCount"), + foreach=lambda n: REPLACE_IN_TEXT( + n, + {"#1": VARIABLE_REFERENCE("tabCount")}, + ), + ), + ) + ], + ), + FTL.Message( + id=FTL.Identifier( + "webrtc-indicator-menuitem-sharing-browser-with-n-tabs" + ), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=PLURALS( + source, + "webrtcIndicator.sharingBrowserWithNTabs.menuitem", + VARIABLE_REFERENCE("tabCount"), + foreach=lambda n: REPLACE_IN_TEXT( + n, + {"#1": VARIABLE_REFERENCE("tabCount")}, + ), + ), + ) + ], + ), + FTL.Message( + id=FTL.Identifier("webrtc-item-camera"), + value=REPLACE( + browser, + "getUserMedia.sharingMenuCamera", + { + "%1$S (": FTL.TextElement(""), + "%1$S(": FTL.TextElement(""), + ")": FTL.TextElement(""), + ")": FTL.TextElement(""), + }, + ), + ), + FTL.Message( + id=FTL.Identifier("webrtc-item-microphone"), + value=REPLACE( + browser, + "getUserMedia.sharingMenuMicrophone", + { + "%1$S (": FTL.TextElement(""), + "%1$S(": FTL.TextElement(""), + ")": FTL.TextElement(""), + ")": FTL.TextElement(""), + }, + ), + ), + FTL.Message( + id=FTL.Identifier("webrtc-item-audio-capture"), + value=REPLACE( + browser, + "getUserMedia.sharingMenuAudioCapture", + { + "%1$S (": FTL.TextElement(""), + "%1$S(": FTL.TextElement(""), + ")": FTL.TextElement(""), + ")": FTL.TextElement(""), + }, + ), + ), + FTL.Message( + id=FTL.Identifier("webrtc-item-application"), + value=REPLACE( + browser, + "getUserMedia.sharingMenuApplication", + { + "%1$S (": FTL.TextElement(""), + "%1$S(": FTL.TextElement(""), + ")": FTL.TextElement(""), + ")": FTL.TextElement(""), + }, + ), + ), + FTL.Message( + id=FTL.Identifier("webrtc-item-screen"), + value=REPLACE( + browser, + "getUserMedia.sharingMenuScreen", + { + "%1$S (": FTL.TextElement(""), + "%1$S(": FTL.TextElement(""), + ")": FTL.TextElement(""), + ")": FTL.TextElement(""), + }, + ), + ), + FTL.Message( + id=FTL.Identifier("webrtc-item-window"), + value=REPLACE( + browser, + "getUserMedia.sharingMenuWindow", + { + "%1$S (": FTL.TextElement(""), + "%1$S(": FTL.TextElement(""), + ")": FTL.TextElement(""), + ")": FTL.TextElement(""), + }, + ), + ), + FTL.Message( + id=FTL.Identifier("webrtc-item-browser"), + value=REPLACE( + browser, + "getUserMedia.sharingMenuBrowser", + { + "%1$S (": FTL.TextElement(""), + "%1$S(": FTL.TextElement(""), + ")": FTL.TextElement(""), + ")": FTL.TextElement(""), + }, + ), + ), + FTL.Message( + id=FTL.Identifier("webrtc-sharing-menuitem-unknown-host"), + value=COPY(browser, "getUserMedia.sharingMenuUnknownHost"), + ), + FTL.Message( + id=FTL.Identifier("webrtc-sharing-menuitem"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=FTL.Pattern( + [FTL.TextElement("{ $origin } ({ $itemList })")] + ), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("webrtc-sharing-menu"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY(browser, "getUserMedia.sharingMenu.label"), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY(browser, "getUserMedia.sharingMenu.accesskey"), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("webrtc-allow-share-camera"), + value=REPLACE( + browser, + "getUserMedia.shareCamera3.message", + {"%1$S": VARIABLE_REFERENCE("origin")}, + ), + ), + FTL.Message( + id=FTL.Identifier("webrtc-allow-share-microphone"), + value=REPLACE( + browser, + "getUserMedia.shareMicrophone3.message", + {"%1$S": VARIABLE_REFERENCE("origin")}, + ), + ), + FTL.Message( + id=FTL.Identifier("webrtc-allow-share-screen"), + value=REPLACE( + browser, + "getUserMedia.shareScreen4.message", + {"%1$S": VARIABLE_REFERENCE("origin")}, + ), + ), + FTL.Message( + id=FTL.Identifier("webrtc-allow-share-camera-and-microphone"), + value=REPLACE( + browser, + "getUserMedia.shareCameraAndMicrophone3.message", + {"%1$S": VARIABLE_REFERENCE("origin")}, + ), + ), + FTL.Message( + id=FTL.Identifier("webrtc-allow-share-camera-and-audio-capture"), + value=REPLACE( + browser, + "getUserMedia.shareCameraAndAudioCapture3.message", + {"%1$S": VARIABLE_REFERENCE("origin")}, + ), + ), + FTL.Message( + id=FTL.Identifier("webrtc-allow-share-screen-and-microphone"), + value=REPLACE( + browser, + "getUserMedia.shareScreenAndMicrophone4.message", + {"%1$S": VARIABLE_REFERENCE("origin")}, + ), + ), + FTL.Message( + id=FTL.Identifier("webrtc-allow-share-screen-and-audio-capture"), + value=REPLACE( + browser, + "getUserMedia.shareScreenAndAudioCapture4.message", + {"%1$S": VARIABLE_REFERENCE("origin")}, + ), + ), + FTL.Message( + id=FTL.Identifier("webrtc-allow-share-audio-capture"), + value=REPLACE( + browser, + "getUserMedia.shareAudioCapture3.message", + {"%1$S": VARIABLE_REFERENCE("origin")}, + ), + ), + FTL.Message( + id=FTL.Identifier("webrtc-allow-share-speaker"), + value=REPLACE( + browser, + "selectAudioOutput.shareSpeaker.message", + {"%1$S": VARIABLE_REFERENCE("origin")}, + ), + ), + FTL.Message( + id=FTL.Identifier("webrtc-allow-share-camera-unsafe-delegation"), + value=REPLACE( + browser, + "getUserMedia.shareCameraUnsafeDelegation2.message", + { + "%1$S": VARIABLE_REFERENCE("origin"), + "%2$S": VARIABLE_REFERENCE("thirdParty"), + }, + ), + ), + FTL.Message( + id=FTL.Identifier("webrtc-allow-share-microphone-unsafe-delegations"), + value=REPLACE( + browser, + "getUserMedia.shareMicrophoneUnsafeDelegations2.message", + { + "%1$S": VARIABLE_REFERENCE("origin"), + "%2$S": VARIABLE_REFERENCE("thirdParty"), + }, + ), + ), + FTL.Message( + id=FTL.Identifier("webrtc-allow-share-screen-unsafe-delegation"), + value=REPLACE( + browser, + "getUserMedia.shareScreenUnsafeDelegation2.message", + { + "%1$S": VARIABLE_REFERENCE("origin"), + "%2$S": VARIABLE_REFERENCE("thirdParty"), + }, + ), + ), + FTL.Message( + id=FTL.Identifier( + "webrtc-allow-share-camera-and-microphone-unsafe-delegation" + ), + value=REPLACE( + browser, + "getUserMedia.shareCameraAndMicrophoneUnsafeDelegation2.message", + { + "%1$S": VARIABLE_REFERENCE("origin"), + "%2$S": VARIABLE_REFERENCE("thirdParty"), + }, + ), + ), + FTL.Message( + id=FTL.Identifier( + "webrtc-allow-share-camera-and-audio-capture-unsafe-delegation" + ), + value=REPLACE( + browser, + "getUserMedia.shareCameraAndAudioCaptureUnsafeDelegation2.message", + { + "%1$S": VARIABLE_REFERENCE("origin"), + "%2$S": VARIABLE_REFERENCE("thirdParty"), + }, + ), + ), + FTL.Message( + id=FTL.Identifier( + "webrtc-allow-share-screen-and-microphone-unsafe-delegation" + ), + value=REPLACE( + browser, + "getUserMedia.shareScreenAndMicrophoneUnsafeDelegation2.message", + { + "%1$S": VARIABLE_REFERENCE("origin"), + "%2$S": VARIABLE_REFERENCE("thirdParty"), + }, + ), + ), + FTL.Message( + id=FTL.Identifier( + "webrtc-allow-share-screen-and-audio-capture-unsafe-delegation" + ), + value=REPLACE( + browser, + "getUserMedia.shareScreenAndAudioCaptureUnsafeDelegation2.message", + { + "%1$S": VARIABLE_REFERENCE("origin"), + "%2$S": VARIABLE_REFERENCE("thirdParty"), + }, + ), + ), + FTL.Message( + id=FTL.Identifier("webrtc-allow-share-speaker-unsafe-delegation"), + value=REPLACE( + browser, + "selectAudioOutput.shareSpeakerUnsafeDelegation.message", + { + "%1$S": VARIABLE_REFERENCE("origin"), + "%2$S": VARIABLE_REFERENCE("thirdParty"), + }, + ), + ), + FTL.Message( + id=FTL.Identifier("webrtc-share-screen-warning"), + value=COPY(browser, "getUserMedia.shareScreenWarning2.message"), + ), + FTL.Message( + id=FTL.Identifier("webrtc-share-browser-warning"), + value=REPLACE( + browser, + "getUserMedia.shareFirefoxWarning2.message", + {"%1$S": TERM_REFERENCE("brand-short-name")}, + ), + ), + FTL.Message( + id=FTL.Identifier("webrtc-share-screen-learn-more"), + value=COPY(browser, "getUserMedia.shareScreen.learnMoreLabel"), + ), + FTL.Message( + id=FTL.Identifier("webrtc-pick-window-or-screen"), + value=COPY(browser, "getUserMedia.pickWindowOrScreen.label"), + ), + FTL.Message( + id=FTL.Identifier("webrtc-share-entire-screen"), + value=COPY(browser, "getUserMedia.shareEntireScreen.label"), + ), + FTL.Message( + id=FTL.Identifier("webrtc-share-pipe-wire-portal"), + value=COPY(browser, "getUserMedia.sharePipeWirePortal.label"), + ), + FTL.Message( + id=FTL.Identifier("webrtc-share-monitor"), + value=REPLACE( + browser, + "getUserMedia.shareMonitor.label", + {"%1$S": VARIABLE_REFERENCE("monitorIndex")}, + ), + ), + FTL.Message( + id=FTL.Identifier("webrtc-share-application"), + value=PLURALS( + browser, + "getUserMedia.shareApplicationWindowCount.label", + VARIABLE_REFERENCE("windowCount"), + foreach=lambda n: REPLACE_IN_TEXT( + n, + { + "#1": VARIABLE_REFERENCE("appName"), + "#2": VARIABLE_REFERENCE("windowCount"), + }, + ), + ), + ), + FTL.Message( + id=FTL.Identifier("webrtc-action-allow"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY(browser, "getUserMedia.allow.label"), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY(browser, "getUserMedia.allow.accesskey"), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("webrtc-action-block"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY_PATTERN( + browser_ftl, "popup-screen-sharing-block.label" + ), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY_PATTERN( + browser_ftl, "popup-screen-sharing-block.accesskey" + ), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("webrtc-action-always-block"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY_PATTERN( + browser_ftl, "popup-screen-sharing-always-block.label" + ), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY_PATTERN( + browser_ftl, + "popup-screen-sharing-always-block.accesskey", + ), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("webrtc-action-not-now"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY(browser, "getUserMedia.notNow.label"), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY(browser, "getUserMedia.notNow.accesskey"), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("webrtc-remember-allow-checkbox"), + value=COPY(browser, "getUserMedia.remember"), + ), + FTL.Message( + id=FTL.Identifier("webrtc-mute-notifications-checkbox"), + value=COPY_PATTERN(browser_ftl, "popup-mute-notifications-checkbox"), + ), + FTL.Message( + id=FTL.Identifier("webrtc-reason-for-no-permanent-allow-screen"), + value=REPLACE( + browser, + "getUserMedia.reasonForNoPermanentAllow.screen3", + {"%1$S": TERM_REFERENCE("brand-short-name")}, + ), + ), + FTL.Message( + id=FTL.Identifier("webrtc-reason-for-no-permanent-allow-audio"), + value=REPLACE( + browser, + "getUserMedia.reasonForNoPermanentAllow.audio", + {"%1$S": TERM_REFERENCE("brand-short-name")}, + ), + ), + FTL.Message( + id=FTL.Identifier("webrtc-reason-for-no-permanent-allow-insecure"), + value=REPLACE( + browser, + "getUserMedia.reasonForNoPermanentAllow.insecure", + {"%1$S": TERM_REFERENCE("brand-short-name")}, + ), + ), + ], + ) + + ctx.add_transforms( + browser_ftl, + browser_ftl, + [ + FTL.Message( + id=FTL.Identifier("popup-select-window-or-screen"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=COPY(browser, "getUserMedia.selectWindowOrScreen2.label"), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=COPY( + browser, "getUserMedia.selectWindowOrScreen2.accesskey" + ), + ), + ], + ), + ], + ) diff --git a/python/l10n/fluent_migrations/bug_1813077_popup_notification_learn_more.py b/python/l10n/fluent_migrations/bug_1813077_popup_notification_learn_more.py new file mode 100644 index 0000000000..bba68d163e --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1813077_popup_notification_learn_more.py @@ -0,0 +1,22 @@ +# coding=utf8 + +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +from __future__ import absolute_import +import fluent.syntax.ast as FTL +from fluent.migrate.helpers import transforms_from + + +def migrate(ctx): + """Bug 1813077 - Migrate xpinstallPromptMessage.learnMore to Fluent , part {index}.""" + + ctx.add_transforms( + "browser/browser/browser.ftl", + "browser/browser/browser.ftl", + transforms_from( + """ +popup-notification-xpinstall-prompt-learn-more = { COPY("browser/chrome/browser/browser.properties", "xpinstallPromptMessage.learnMore") } +""" + ), + ) diff --git a/python/l10n/fluent_migrations/bug_1814261_mixed_content_identity_panel.py b/python/l10n/fluent_migrations/bug_1814261_mixed_content_identity_panel.py new file mode 100644 index 0000000000..a135870df8 --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1814261_mixed_content_identity_panel.py @@ -0,0 +1,49 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +import fluent.syntax.ast as FTL + +from fluent.migrate.transforms import TransformPattern + + +class STRIP_LEARNMORE(TransformPattern): + # Used to remove `<a data-l10n-name="link">SOME TEXT</a>` from a string + def visit_TextElement(self, node): + link_start = node.value.find('<label data-l10n-name="link">') + if link_start != -1: + # Replace string up to the link, remove remaining spaces afterwards. + # Removing an extra character directly is not safe, as it could be + # punctuation. + node.value = node.value[:link_start].rstrip() + + return node + + +def migrate(ctx): + """Bug 1814261 - Use moz-support-link in the mixed-content section of the identity panel, part {index}.""" + + browser_ftl = "browser/browser/browser.ftl" + ctx.add_transforms( + browser_ftl, + browser_ftl, + [ + FTL.Message( + id=FTL.Identifier("identity-description-active-blocked2"), + value=STRIP_LEARNMORE( + browser_ftl, "identity-description-active-blocked" + ), + ), + FTL.Message( + id=FTL.Identifier("identity-description-passive-loaded-insecure2"), + value=STRIP_LEARNMORE( + browser_ftl, "identity-description-passive-loaded-insecure" + ), + ), + FTL.Message( + id=FTL.Identifier("identity-description-passive-loaded-mixed2"), + value=STRIP_LEARNMORE( + browser_ftl, "identity-description-passive-loaded-mixed" + ), + ), + ], + ) diff --git a/python/l10n/fluent_migrations/bug_1814266_identity_custom_root.py b/python/l10n/fluent_migrations/bug_1814266_identity_custom_root.py new file mode 100644 index 0000000000..7eb144230d --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1814266_identity_custom_root.py @@ -0,0 +1,34 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +import fluent.syntax.ast as FTL + +from fluent.migrate.transforms import TransformPattern + + +class STRIP_LEARNMORE(TransformPattern): + # Used to remove `<a data-l10n-name="link">SOME TEXT</a>` from a string + def visit_TextElement(self, node): + link_start = node.value.find('<label data-l10n-name="link">') + # Replace string up to the link, remove remaining spaces afterwards. + # Removing an extra character directly is not safe, as it could be + # punctuation. + node.value = node.value[:link_start].rstrip() + + return node + + +def migrate(ctx): + """Bug 1814266 - Use moz-support-link in identity panel, part {index}.""" + + browser_ftl = "browser/browser/browser.ftl" + ctx.add_transforms( + browser_ftl, + browser_ftl, + [ + FTL.Message( + id=FTL.Identifier("identity-description-custom-root2"), + value=STRIP_LEARNMORE(browser_ftl, "identity-description-custom-root"), + ), + ], + ) diff --git a/python/l10n/fluent_migrations/bug_1818322_mozTabList.py b/python/l10n/fluent_migrations/bug_1818322_mozTabList.py new file mode 100644 index 0000000000..9be3037550 --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1818322_mozTabList.py @@ -0,0 +1,26 @@ +# coding=utf8 + +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + + +from fluent.migrate.helpers import transforms_from +from fluent.migrate import COPY + + +def migrate(ctx): + """Bug 1818322 - Create MozTabList and MozTabRow reusable components, part {index}.""" + ctx.add_transforms( + "toolkit/toolkit/global/mozTabList.ftl", + "toolkit/toolkit/global/mozTabList.ftl", + transforms_from( + """ +mztabrow-tabs-list-tab = + .title = {COPY_PATTERN(from_path, "firefoxview-tabs-list-tab-button.title")} +mztabrow-dismiss-tab-button = + .title = {COPY_PATTERN(from_path, "firefoxview-closed-tabs-dismiss-tab.title")} +mztabrow-just-now-timestamp = {COPY_PATTERN(from_path, "firefoxview-just-now-timestamp")} + """, + from_path="browser/browser/firefoxView.ftl", + ), + ) diff --git a/python/l10n/fluent_migrations/bug_1820654_update_manual.py b/python/l10n/fluent_migrations/bug_1820654_update_manual.py new file mode 100644 index 0000000000..fe3a18e38b --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1820654_update_manual.py @@ -0,0 +1,30 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +import fluent.syntax.ast as FTL + +from fluent.migrate.transforms import TransformPattern + + +class REPLACE_LABEL(TransformPattern): + # Used to replace `<label data-l10n-name="manual-link"/>` + def visit_TextElement(self, node): + node.value = node.value.replace("<label", "<a") + + return node + + +def migrate(ctx): + """Bug 1820654 - Use html:a in manualUpdate box, part {index}.""" + + aboutDialog_ftl = "browser/browser/aboutDialog.ftl" + ctx.add_transforms( + aboutDialog_ftl, + aboutDialog_ftl, + [ + FTL.Message( + id=FTL.Identifier("aboutdialog-update-manual"), + value=REPLACE_LABEL(aboutDialog_ftl, "update-manual"), + ), + ], + ) diff --git a/python/l10n/fluent_migrations/bug_1821187_migrationWizard_password_file_import_strings.py b/python/l10n/fluent_migrations/bug_1821187_migrationWizard_password_file_import_strings.py new file mode 100644 index 0000000000..9f74ff9837 --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1821187_migrationWizard_password_file_import_strings.py @@ -0,0 +1,27 @@ +# coding=utf8 + +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +from __future__ import absolute_import +import fluent.syntax.ast as FTL +from fluent.migrate import COPY_PATTERN +from fluent.migrate.helpers import transforms_from + + +def migrate(ctx): + """Bug 1821187 - Copy password file import strings to migrationWizard.ftl, part {index}.""" + + ctx.add_transforms( + "browser/browser/migrationWizard.ftl", + "browser/browser/migrationWizard.ftl", + transforms_from( + """ +migration-passwords-from-file-csv-filter-title = + {COPY_PATTERN(from_path, "about-logins-import-file-picker-csv-filter-title")} +migration-passwords-from-file-tsv-filter-title = + {COPY_PATTERN(from_path, "about-logins-import-file-picker-tsv-filter-title")} + """, + from_path="browser/browser/aboutLogins.ftl", + ), + ) diff --git a/python/l10n/fluent_migrations/bug_1821779_migrationWizard_browser_names.py b/python/l10n/fluent_migrations/bug_1821779_migrationWizard_browser_names.py new file mode 100644 index 0000000000..6bcaa35da8 --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1821779_migrationWizard_browser_names.py @@ -0,0 +1,38 @@ +# coding=utf8 + +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +from __future__ import absolute_import +import fluent.syntax.ast as FTL +from fluent.migrate.helpers import transforms_from + + +def migrate(ctx): + """Bug 1821779 - Move existing browser names to migrationWizard.ftl, part {index}.""" + + ctx.add_transforms( + "browser/browser/migrationWizard.ftl", + "browser/browser/migrationWizard.ftl", + transforms_from( + """ +migration-wizard-migrator-display-name-brave = {COPY_PATTERN(from_path, "import-from-brave.label")} +migration-wizard-migrator-display-name-canary = {COPY_PATTERN(from_path, "import-from-canary.label")} +migration-wizard-migrator-display-name-chrome = {COPY_PATTERN(from_path, "import-from-chrome.label")} +migration-wizard-migrator-display-name-chrome-beta = {COPY_PATTERN(from_path, "import-from-chrome-beta.label")} +migration-wizard-migrator-display-name-chrome-dev = {COPY_PATTERN(from_path, "import-from-chrome-dev.label")} +migration-wizard-migrator-display-name-chromium = {COPY_PATTERN(from_path, "import-from-chromium.label")} +migration-wizard-migrator-display-name-chromium-360se = {COPY_PATTERN(from_path, "import-from-360se.label")} +migration-wizard-migrator-display-name-chromium-edge = {COPY_PATTERN(from_path, "import-from-edge.label")} +migration-wizard-migrator-display-name-chromium-edge-beta = {COPY_PATTERN(from_path, "import-from-edge-beta.label")} +migration-wizard-migrator-display-name-edge-legacy = {COPY_PATTERN(from_path, "import-from-edge-legacy.label")} +migration-wizard-migrator-display-name-firefox = {COPY_PATTERN(from_path, "import-from-firefox.label")} +migration-wizard-migrator-display-name-ie = {COPY_PATTERN(from_path, "import-from-ie.label")} +migration-wizard-migrator-display-name-opera = {COPY_PATTERN(from_path, "import-from-opera.label")} +migration-wizard-migrator-display-name-opera-gx = {COPY_PATTERN(from_path, "import-from-opera-gx.label")} +migration-wizard-migrator-display-name-safari = {COPY_PATTERN(from_path, "import-from-safari.label")} +migration-wizard-migrator-display-name-vivaldi = {COPY_PATTERN(from_path, "import-from-vivaldi.label")} +""", + from_path="browser/browser/migration.ftl", + ), + ) diff --git a/python/l10n/fluent_migrations/bug_1828443_pocket_policy.py b/python/l10n/fluent_migrations/bug_1828443_pocket_policy.py new file mode 100644 index 0000000000..6f9fbe5d7b --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1828443_pocket_policy.py @@ -0,0 +1,30 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +import fluent.syntax.ast as FTL + +from fluent.migrate.transforms import TransformPattern + + +class REPLACE_POCKET(TransformPattern): + # Replace Pocket with term `{ -pocket-brand-name }` + def visit_TextElement(self, node): + node.value = node.value.replace("Pocket", "{ -pocket-brand-name }") + + return node + + +def migrate(ctx): + """Bug 1828443 - Use Fluent term for Pocket in policy string, part {index}.""" + + ftl_file = "browser/browser/policies/policies-descriptions.ftl" + ctx.add_transforms( + ftl_file, + ftl_file, + [ + FTL.Message( + id=FTL.Identifier("policy-DisablePocket2"), + value=REPLACE_POCKET(ftl_file, "policy-DisablePocket"), + ), + ], + ) diff --git a/python/l10n/fluent_migrations/bug_1828767_sanitize_dialog_native_size.py b/python/l10n/fluent_migrations/bug_1828767_sanitize_dialog_native_size.py new file mode 100644 index 0000000000..9d311c3fae --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1828767_sanitize_dialog_native_size.py @@ -0,0 +1,77 @@ +from fluent.migrate import COPY_PATTERN +from fluent.migrate.transforms import TransformPattern, REPLACE_IN_TEXT +from fluent.migrate.helpers import MESSAGE_REFERENCE +import fluent.syntax.ast as FTL + +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + + +class REPLACE_PATTERN(TransformPattern): + """Hacky custom transform that works.""" + + def __init__(self, ctx, path, key, replacements, **kwargs): + super(REPLACE_PATTERN, self).__init__(path, key, **kwargs) + self.ctx = ctx + self.replacements = replacements + + def visit_Pattern(self, source): + source = self.generic_visit(source) + target = FTL.Pattern([]) + for element in source.elements: + if isinstance(element, FTL.TextElement): + pattern = REPLACE_IN_TEXT(element, self.replacements)(self.ctx) + target.elements += pattern.elements + else: + target.elements += [element] + return target + + +def replace_with_min_size_transform(ctx, file, identifier, to_identifier=None): + if to_identifier is None: + to_identifier = identifier + "2" + attributes = [ + FTL.Attribute( + id=FTL.Identifier("title"), + value=COPY_PATTERN(file, "{}.title".format(identifier)), + ), + FTL.Attribute( + id=FTL.Identifier("style"), + value=REPLACE_PATTERN( + ctx, + file, + "{}.style".format(identifier), + { + "width:": FTL.TextElement("min-width:"), + "height:": FTL.TextElement("min-height:"), + }, + ), + ), + ] + + return FTL.Message( + id=FTL.Identifier(to_identifier), + attributes=attributes, + ) + + +def migrate(ctx): + """Bug 1828767 - Migrate sanitize dialog to use min-width, part {index}.""" + ctx.add_transforms( + "browser/browser/sanitize.ftl", + "browser/browser/sanitize.ftl", + [ + replace_with_min_size_transform( + ctx, + "browser/browser/sanitize.ftl", + "dialog-title", + "sanitize-dialog-title", + ), + replace_with_min_size_transform( + ctx, + "browser/browser/sanitize.ftl", + "dialog-title-everything", + "sanitize-dialog-title-everything", + ), + ], + ) diff --git a/python/l10n/fluent_migrations/bug_1830042_places.py b/python/l10n/fluent_migrations/bug_1830042_places.py new file mode 100644 index 0000000000..0109d3952f --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1830042_places.py @@ -0,0 +1,127 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +import fluent.syntax.ast as FTL +from fluent.migrate.helpers import TERM_REFERENCE, transforms_from, VARIABLE_REFERENCE +from fluent.migrate.transforms import COPY, PLURALS, REPLACE, REPLACE_IN_TEXT + + +def migrate(ctx): + """Bug 1830042 - Convert places.properties to Fluent, part {index}.""" + + source = "browser/chrome/browser/places/places.properties" + target = "browser/browser/places.ftl" + ctx.add_transforms( + target, + target, + [ + FTL.Message( + id=FTL.Identifier("places-details-pane-no-items"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("value"), + value=COPY(source, "detailsPane.noItems"), + ) + ], + ), + FTL.Message( + id=FTL.Identifier("places-details-pane-items-count"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("value"), + value=PLURALS( + source, + "detailsPane.itemsCountLabel", + VARIABLE_REFERENCE("count"), + foreach=lambda n: REPLACE_IN_TEXT( + n, + {"#1": VARIABLE_REFERENCE("count")}, + ), + ), + ) + ], + ), + FTL.Message( + id=FTL.Identifier("places-locked-prompt"), + value=REPLACE( + source, + "lockPrompt.text", + {"%1$S": TERM_REFERENCE("brand-short-name")}, + ), + ), + ] + + transforms_from( + """ +places-empty-bookmarks-folder = + .label = { COPY(source, "bookmarksMenuEmptyFolder") } +places-delete-page = + .label = + { $count -> + [1] { COPY(source, "cmd.deleteSinglePage.label") } + *[other] { COPY(source, "cmd.deleteMultiplePages.label") } + } + .accesskey = { COPY(source, "cmd.deleteSinglePage.accesskey") } +places-create-bookmark = + .label = + { $count -> + [1] { COPY(source, "cmd.bookmarkSinglePage2.label") } + *[other] { COPY(source, "cmd.bookmarkMultiplePages2.label") } + } + .accesskey = { COPY(source, "cmd.bookmarkSinglePage2.accesskey") } +places-search-bookmarks = + .placeholder = { COPY(source, "searchBookmarks") } +places-search-history = + .placeholder = { COPY(source, "searchHistory") } +places-search-downloads = + .placeholder = { COPY(source, "searchDownloads") } +places-view-sortby-name = + .label = { COPY(source, "view.sortBy.1.name.label") } + .accesskey = { COPY(source, "view.sortBy.1.name.accesskey") } +places-view-sortby-url = + .label = { COPY(source, "view.sortBy.1.url.label") } + .accesskey = { COPY(source, "view.sortBy.1.url.accesskey") } +places-view-sortby-date = + .label = { COPY(source, "view.sortBy.1.date.label") } + .accesskey = { COPY(source, "view.sortBy.1.date.accesskey") } +places-view-sortby-visit-count = + .label = { COPY(source, "view.sortBy.1.visitCount.label") } + .accesskey = { COPY(source, "view.sortBy.1.visitCount.accesskey") } +places-view-sortby-date-added = + .label = { COPY(source, "view.sortBy.1.dateAdded.label") } + .accesskey = { COPY(source, "view.sortBy.1.dateAdded.accesskey") } +places-view-sortby-last-modified = + .label = { COPY(source, "view.sortBy.1.lastModified.label") } + .accesskey = { COPY(source, "view.sortBy.1.lastModified.accesskey") } +places-view-sortby-tags = + .label = { COPY(source, "view.sortBy.1.tags.label") } + .accesskey = { COPY(source, "view.sortBy.1.tags.accesskey") } +""", + source=source, + ), + ) + + ctx.add_transforms( + "browser/browser/placesPrompts.ftl", + "browser/browser/placesPrompts.ftl", + [ + FTL.Message( + id=FTL.Identifier("places-error-title"), + value=FTL.Pattern([FTL.Placeable(TERM_REFERENCE("brand-short-name"))]), + ), + ] + + transforms_from( + """ +places-no-title = { COPY(source, "noTitle") } +places-bookmarks-backup-title = { COPY(source, "bookmarksBackupTitle") } +places-bookmarks-restore-alert-title = { COPY(source, "bookmarksRestoreAlertTitle") } +places-bookmarks-restore-alert = { COPY(source, "bookmarksRestoreAlert") } +places-bookmarks-restore-title = { COPY(source, "bookmarksRestoreTitle") } +places-bookmarks-restore-filter-name = { COPY(source, "bookmarksRestoreFilterName") } +places-bookmarks-restore-format-error = { COPY(source, "bookmarksRestoreFormatError") } +places-bookmarks-restore-parse-error = { COPY(source, "bookmarksRestoreParseError") } +places-bookmarks-import = { COPY(source, "SelectImport") } +places-bookmarks-export = { COPY(source, "EnterExport") } +""", + source=source, + ), + ) diff --git a/python/l10n/fluent_migrations/bug_1831851_accounts.py b/python/l10n/fluent_migrations/bug_1831851_accounts.py new file mode 100644 index 0000000000..d50a0192c8 --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1831851_accounts.py @@ -0,0 +1,195 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +import fluent.syntax.ast as FTL +from fluent.migrate.helpers import VARIABLE_REFERENCE +from fluent.migrate.transforms import COPY, PLURALS, REPLACE, REPLACE_IN_TEXT + + +def migrate(ctx): + """Bug 1831851 - Migrate accounts.properties to Fluent, part {index}.""" + + accounts = "browser/chrome/browser/accounts.properties" + accounts_ftl = "browser/browser/accounts.ftl" + preferences_ftl = "browser/browser/preferences/preferences.ftl" + + ctx.add_transforms( + accounts_ftl, + accounts_ftl, + [ + FTL.Message( + id=FTL.Identifier("account-reconnect"), + value=REPLACE( + accounts, + "reconnectDescription", + {"%1$S": VARIABLE_REFERENCE("email")}, + ), + ), + FTL.Message( + id=FTL.Identifier("account-verify"), + value=REPLACE( + accounts, "verifyDescription", {"%1$S": VARIABLE_REFERENCE("email")} + ), + ), + FTL.Message( + id=FTL.Identifier("account-send-to-all-devices-titlecase"), + value=COPY(accounts, "sendToAllDevices.menuitem"), + ), + FTL.Message( + id=FTL.Identifier("account-manage-devices-titlecase"), + value=COPY(accounts, "manageDevices.menuitem"), + ), + FTL.Message( + id=FTL.Identifier("account-send-tab-to-device-singledevice-status"), + value=COPY(accounts, "sendTabToDevice.singledevice.status"), + ), + FTL.Message( + id=FTL.Identifier("account-send-tab-to-device-singledevice-learnmore"), + value=COPY(accounts, "sendTabToDevice.singledevice"), + ), + FTL.Message( + id=FTL.Identifier("account-send-tab-to-device-connectdevice"), + value=COPY(accounts, "sendTabToDevice.connectdevice"), + ), + FTL.Message( + id=FTL.Identifier("account-send-tab-to-device-verify-status"), + value=COPY(accounts, "sendTabToDevice.verify.status"), + ), + FTL.Message( + id=FTL.Identifier("account-send-tab-to-device-verify"), + value=COPY(accounts, "sendTabToDevice.verify"), + ), + FTL.Message( + id=FTL.Identifier("account-connection-title"), + value=FTL.Pattern( + [ + FTL.Placeable( + FTL.TermReference( + id=FTL.Identifier("fxaccount-brand-name"), + arguments=FTL.CallArguments( + named=[ + FTL.NamedArgument( + FTL.Identifier("capitalization"), + FTL.StringLiteral("title"), + ) + ] + ), + ) + ) + ] + ), + ), + FTL.Message( + id=FTL.Identifier("account-connection-connected-with"), + value=REPLACE( + accounts, + "otherDeviceConnectedBody", + {"%1$S": VARIABLE_REFERENCE("deviceName")}, + ), + ), + FTL.Message( + id=FTL.Identifier("account-connection-connected-with-noname"), + value=COPY(accounts, "otherDeviceConnectedBody.noDeviceName"), + ), + FTL.Message( + id=FTL.Identifier("account-connection-connected"), + value=COPY(accounts, "thisDeviceConnectedBody"), + ), + FTL.Message( + id=FTL.Identifier("account-connection-disconnected"), + value=COPY(accounts, "thisDeviceDisconnectedBody"), + ), + FTL.Message( + id=FTL.Identifier("account-single-tab-arriving-title"), + value=COPY(accounts, "tabArrivingNotification.title"), + ), + FTL.Message( + id=FTL.Identifier("account-single-tab-arriving-from-device-title"), + value=REPLACE( + accounts, + "tabArrivingNotificationWithDevice.title", + {"%1$S": VARIABLE_REFERENCE("deviceName")}, + ), + ), + FTL.Message( + id=FTL.Identifier("account-single-tab-arriving-truncated-url"), + value=REPLACE( + accounts, + "singleTabArrivingWithTruncatedURL.body", + {"%1$S": VARIABLE_REFERENCE("url")}, + ), + ), + FTL.Message( + id=FTL.Identifier("account-multiple-tabs-arriving-title"), + value=COPY(accounts, "multipleTabsArrivingNotification.title"), + ), + FTL.Message( + id=FTL.Identifier("account-multiple-tabs-arriving-from-single-device"), + value=PLURALS( + accounts, + "unnamedTabsArrivingNotification2.body", + VARIABLE_REFERENCE("tabCount"), + foreach=lambda n: REPLACE_IN_TEXT( + n, + { + "#1": VARIABLE_REFERENCE("tabCount"), + "#2": VARIABLE_REFERENCE("deviceName"), + }, + ), + ), + ), + FTL.Message( + id=FTL.Identifier( + "account-multiple-tabs-arriving-from-multiple-devices" + ), + value=PLURALS( + accounts, + "unnamedTabsArrivingNotificationMultiple2.body", + VARIABLE_REFERENCE("tabCount"), + foreach=lambda n: REPLACE_IN_TEXT( + n, + {"#1": VARIABLE_REFERENCE("tabCount")}, + ), + ), + ), + FTL.Message( + id=FTL.Identifier("account-multiple-tabs-arriving-from-unknown-device"), + value=PLURALS( + accounts, + "unnamedTabsArrivingNotificationNoDevice.body", + VARIABLE_REFERENCE("tabCount"), + foreach=lambda n: REPLACE_IN_TEXT( + n, + {"#1": VARIABLE_REFERENCE("tabCount")}, + ), + ), + ), + ], + ) + + ctx.add_transforms( + preferences_ftl, + preferences_ftl, + [ + FTL.Message( + id=FTL.Identifier("sync-verification-sent-title"), + value=COPY(accounts, "verificationSentTitle"), + ), + FTL.Message( + id=FTL.Identifier("sync-verification-sent-body"), + value=REPLACE( + accounts, + "verificationSentBody", + {"%1$S": VARIABLE_REFERENCE("email")}, + ), + ), + FTL.Message( + id=FTL.Identifier("sync-verification-not-sent-title"), + value=COPY(accounts, "verificationNotSentTitle"), + ), + FTL.Message( + id=FTL.Identifier("sync-verification-not-sent-body"), + value=COPY(accounts, "verificationNotSentBody"), + ), + ], + ) diff --git a/python/l10n/fluent_migrations/bug_1831872_sync.py b/python/l10n/fluent_migrations/bug_1831872_sync.py new file mode 100644 index 0000000000..f66c27bf5a --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1831872_sync.py @@ -0,0 +1,31 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +import fluent.syntax.ast as FTL +from fluent.migrate.helpers import TERM_REFERENCE, VARIABLE_REFERENCE +from fluent.migrate.transforms import REPLACE + + +def migrate(ctx): + """Bug 1831872 - Migrate sync.properties to Fluent, part {index}.""" + + source = "services/sync/sync.properties" + target = "toolkit/services/accounts.ftl" + ctx.add_transforms( + target, + target, + [ + FTL.Message( + id=FTL.Identifier("account-client-name"), + value=REPLACE( + source, + "client.name2", + { + "%1$S": VARIABLE_REFERENCE("user"), + "%2$S": TERM_REFERENCE("brand-short-name"), + "%3$S": VARIABLE_REFERENCE("system"), + }, + ), + ), + ], + ) diff --git a/python/l10n/fluent_migrations/bug_1832138_ctrlTab.py b/python/l10n/fluent_migrations/bug_1832138_ctrlTab.py new file mode 100644 index 0000000000..195e3cee49 --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1832138_ctrlTab.py @@ -0,0 +1,37 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +import fluent.syntax.ast as FTL +from fluent.migrate.helpers import VARIABLE_REFERENCE +from fluent.migrate.transforms import PLURALS, REPLACE_IN_TEXT + + +def migrate(ctx): + """Bug 1832138 - Convert browser-ctrlTab.js to Fluent, part {index}.""" + + browser = "browser/chrome/browser/browser.properties" + target = "browser/browser/tabbrowser.ftl" + + ctx.add_transforms( + target, + target, + [ + FTL.Message( + id=FTL.Identifier("tabbrowser-ctrl-tab-list-all-tabs"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=PLURALS( + browser, + "ctrlTab.listAllTabs.label", + VARIABLE_REFERENCE("tabCount"), + foreach=lambda n: REPLACE_IN_TEXT( + n, + {"#1": VARIABLE_REFERENCE("tabCount")}, + ), + ), + ) + ], + ), + ], + ) diff --git a/python/l10n/fluent_migrations/bug_1832141_recently_closed.py b/python/l10n/fluent_migrations/bug_1832141_recently_closed.py new file mode 100644 index 0000000000..530f6f05dd --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1832141_recently_closed.py @@ -0,0 +1,76 @@ +import fluent.syntax.ast as FTL +from fluent.migrate import COPY_PATTERN, PLURALS, REPLACE_IN_TEXT +from fluent.migrate.helpers import VARIABLE_REFERENCE + +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + + +class CUSTOM_PLURALS(PLURALS): + def __call__(self, ctx): + pattern = super().__call__(ctx) + el = pattern.elements[0] + if isinstance(el, FTL.Placeable) and isinstance( + el.expression, FTL.SelectExpression + ): + selexp = el.expression + else: + selexp = FTL.SelectExpression( + VARIABLE_REFERENCE("tabCount"), + [FTL.Variant(FTL.Identifier("other"), pattern, default=True)], + ) + pattern = FTL.Pattern([FTL.Placeable(selexp)]) + selexp.variants[0:0] = [ + FTL.Variant( + FTL.NumberLiteral("0"), + FTL.Pattern([FTL.Placeable(VARIABLE_REFERENCE("winTitle"))]), + ) + ] + return pattern + + +def migrate(ctx): + """Bug 1832141 - Migrate strings to recentlyClosed.ftl, part {index}.""" + + appmenu = "browser/browser/appmenu.ftl" + browser = "browser/chrome/browser/browser.properties" + menubar = "browser/browser/menubar.ftl" + target = "browser/browser/recentlyClosed.ftl" + + ctx.add_transforms( + target, + target, + [ + FTL.Message( + id=FTL.Identifier("recently-closed-menu-reopen-all-tabs"), + value=COPY_PATTERN(menubar, "menu-history-reopen-all-tabs"), + ), + FTL.Message( + id=FTL.Identifier("recently-closed-menu-reopen-all-windows"), + value=COPY_PATTERN(menubar, "menu-history-reopen-all-windows"), + ), + FTL.Message( + id=FTL.Identifier("recently-closed-panel-reopen-all-tabs"), + value=COPY_PATTERN(appmenu, "appmenu-reopen-all-tabs"), + ), + FTL.Message( + id=FTL.Identifier("recently-closed-panel-reopen-all-windows"), + value=COPY_PATTERN(appmenu, "appmenu-reopen-all-windows"), + ), + FTL.Message( + id=FTL.Identifier("recently-closed-undo-close-window-label"), + value=CUSTOM_PLURALS( + browser, + "menuUndoCloseWindowLabel", + VARIABLE_REFERENCE("tabCount"), + foreach=lambda n: REPLACE_IN_TEXT( + n, + { + "#1": VARIABLE_REFERENCE("winTitle"), + "#2": VARIABLE_REFERENCE("tabCount"), + }, + ), + ), + ), + ], + ) diff --git a/python/l10n/fluent_migrations/bug_1832179_sendTabToDevice.py b/python/l10n/fluent_migrations/bug_1832179_sendTabToDevice.py new file mode 100644 index 0000000000..526b8a90e0 --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1832179_sendTabToDevice.py @@ -0,0 +1,37 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +import fluent.syntax.ast as FTL +from fluent.migrate.helpers import VARIABLE_REFERENCE +from fluent.migrate.transforms import PLURALS, REPLACE_IN_TEXT + + +def migrate(ctx): + """Bug 1832179 - Convert sendTabsToDevice.label string to Fluent, part {index}.""" + + browser = "browser/chrome/browser/browser.properties" + target = "browser/browser/sync.ftl" + + ctx.add_transforms( + target, + target, + [ + FTL.Message( + id=FTL.Identifier("fxa-menu-send-tab-to-device"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=PLURALS( + browser, + "sendTabsToDevice.label", + VARIABLE_REFERENCE("tabCount"), + foreach=lambda n: REPLACE_IN_TEXT( + n, + {"#1": VARIABLE_REFERENCE("tabCount")}, + ), + ), + ) + ], + ), + ], + ) diff --git a/python/l10n/fluent_migrations/bug_1832186_popupwarning.py b/python/l10n/fluent_migrations/bug_1832186_popupwarning.py new file mode 100644 index 0000000000..4239899725 --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1832186_popupwarning.py @@ -0,0 +1,139 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +import fluent.syntax.ast as FTL +from fluent.migrate.helpers import TERM_REFERENCE, VARIABLE_REFERENCE +from fluent.migrate.transforms import ( + COPY, + PLURALS, + REPLACE, + REPLACE_IN_TEXT, +) + + +def migrate(ctx): + """Bug 1832186 - Migrate popup warning strings to Fluent, part {index}.""" + + source = "browser/chrome/browser/browser.properties" + target = "browser/browser/browser.ftl" + + ctx.add_transforms( + target, + target, + [ + FTL.Message( + id=FTL.Identifier("popup-warning-message"), + value=PLURALS( + source, + "popupWarning.message", + VARIABLE_REFERENCE("popupCount"), + foreach=lambda n: REPLACE_IN_TEXT( + n, + { + "#1": TERM_REFERENCE("brand-short-name"), + "#2": VARIABLE_REFERENCE("popupCount"), + }, + ), + ), + ), + FTL.Message( + id=FTL.Identifier("popup-warning-exceeded-message"), + value=PLURALS( + source, + "popupWarning.exceeded.message", + VARIABLE_REFERENCE("popupCount"), + foreach=lambda n: REPLACE_IN_TEXT( + n, + { + "#1": TERM_REFERENCE("brand-short-name"), + "#2": VARIABLE_REFERENCE("popupCount"), + }, + ), + ), + ), + FTL.Message( + id=FTL.Identifier("popup-warning-button"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=FTL.Pattern( + [ + FTL.Placeable( + FTL.SelectExpression( + selector=FTL.FunctionReference( + FTL.Identifier("PLATFORM"), + FTL.CallArguments(), + ), + variants=[ + FTL.Variant( + key=FTL.Identifier("windows"), + value=COPY( + source, "popupWarningButton" + ), + ), + FTL.Variant( + key=FTL.Identifier("other"), + value=COPY( + source, "popupWarningButtonUnix" + ), + default=True, + ), + ], + ) + ) + ] + ), + ), + FTL.Attribute( + id=FTL.Identifier("accesskey"), + value=FTL.Pattern( + [ + FTL.Placeable( + FTL.SelectExpression( + selector=FTL.FunctionReference( + FTL.Identifier("PLATFORM"), + FTL.CallArguments(), + ), + variants=[ + FTL.Variant( + key=FTL.Identifier("windows"), + value=COPY( + source, + "popupWarningButton.accesskey", + ), + ), + FTL.Variant( + key=FTL.Identifier("other"), + value=COPY( + source, + "popupWarningButtonUnix.accesskey", + ), + default=True, + ), + ], + ) + ) + ] + ), + ), + ], + ), + FTL.Message( + id=FTL.Identifier("popup-show-popup-menuitem"), + attributes=[ + FTL.Attribute( + id=FTL.Identifier("label"), + value=REPLACE( + source, + "popupShowPopupPrefix", + { + "%1$S": VARIABLE_REFERENCE("popupURI"), + "‘": FTL.TextElement("“"), + "’": FTL.TextElement("”"), + }, + ), + ) + ], + ), + ], + ) diff --git a/python/l10n/fluent_migrations/bug_1832668_firefoxView_navigation.py b/python/l10n/fluent_migrations/bug_1832668_firefoxView_navigation.py new file mode 100644 index 0000000000..c742f64b36 --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1832668_firefoxView_navigation.py @@ -0,0 +1,27 @@ +# coding=utf8 + +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + + +from fluent.migrate.helpers import transforms_from +from fluent.migrate import COPY + + +def migrate(ctx): + """Bug 1832668 - Add new side navigation component to Firefox View Next page, part {index}.""" + ctx.add_transforms( + "browser/browser/firefoxView.ftl", + "browser/browser/firefoxView.ftl", + transforms_from( + """ +firefoxview-overview-nav = {COPY_PATTERN(from_path, "firefoxview-overview-navigation")} + .title = {COPY_PATTERN(from_path, "firefoxview-overview-navigation")} +firefoxview-history-nav = {COPY_PATTERN(from_path, "firefoxview-history-navigation")} + .title = {COPY_PATTERN(from_path, "firefoxview-history-navigation")} +firefoxview-opentabs-nav = {COPY_PATTERN(from_path, "firefoxview-opentabs-navigation")} + .title = {COPY_PATTERN(from_path, "firefoxview-opentabs-navigation")} + """, + from_path="browser/browser/firefoxView.ftl", + ), + ) diff --git a/python/l10n/fluent_migrations/bug_1833228_fxviewTabList.py b/python/l10n/fluent_migrations/bug_1833228_fxviewTabList.py new file mode 100644 index 0000000000..43d0d8834c --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1833228_fxviewTabList.py @@ -0,0 +1,44 @@ +# coding=utf8 + +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + + +from fluent.migrate.helpers import transforms_from +from fluent.migrate import COPY + + +def migrate(ctx): + """Bug 1833228 - Remove "moz-" from moz-tab-list component and associated .ftl, .css files, part {index}.""" + ctx.add_transforms( + "browser/browser/fxviewTabList.ftl", + "browser/browser/fxviewTabList.ftl", + transforms_from( + """ +fxviewtabrow-open-menu-button = + .title = {COPY_PATTERN(from_path, "mztabrow-open-menu-button.title")} +fxviewtabrow-date = {COPY_PATTERN(from_path, "mztabrow-date")} +fxviewtabrow-time = {COPY_PATTERN(from_path, "mztabrow-time")} +fxviewtabrow-tabs-list-tab = + .title = {COPY_PATTERN(from_path, "mztabrow-tabs-list-tab.title")} +fxviewtabrow-dismiss-tab-button = + .title = {COPY_PATTERN(from_path, "mztabrow-dismiss-tab-button.title")} +fxviewtabrow-just-now-timestamp = {COPY_PATTERN(from_path, "mztabrow-just-now-timestamp")} +fxviewtabrow-delete = {COPY_PATTERN(from_path, "mztabrow-delete")} + .accesskey = {COPY_PATTERN(from_path, "mztabrow-delete.accesskey")} +fxviewtabrow-forget-about-this-site = {COPY_PATTERN(from_path, "mztabrow-forget-about-this-site")} + .accesskey = {COPY_PATTERN(from_path, "mztabrow-forget-about-this-site.accesskey")} +fxviewtabrow-open-in-window = {COPY_PATTERN(from_path, "mztabrow-open-in-window")} + .accesskey = {COPY_PATTERN(from_path, "mztabrow-open-in-window.accesskey")} +fxviewtabrow-open-in-private-window = {COPY_PATTERN(from_path, "mztabrow-open-in-private-window")} + .accesskey = {COPY_PATTERN(from_path, "mztabrow-open-in-private-window.accesskey")} +fxviewtabrow-add-bookmark = {COPY_PATTERN(from_path, "mztabrow-add-bookmark")} + .accesskey = {COPY_PATTERN(from_path, "mztabrow-add-bookmark.accesskey")} +fxviewtabrow-save-to-pocket = {COPY_PATTERN(from_path, "mztabrow-save-to-pocket")} + .accesskey = {COPY_PATTERN(from_path, "mztabrow-save-to-pocket.accesskey")} +fxviewtabrow-copy-link = {COPY_PATTERN(from_path, "mztabrow-copy-link")} + .accesskey = {COPY_PATTERN(from_path, "mztabrow-copy-link.accesskey")} + """, + from_path="browser/browser/mozTabList.ftl", + ), + ) diff --git a/python/l10n/fluent_migrations/bug_1835559_aboutDialog_explicit_textContent.py b/python/l10n/fluent_migrations/bug_1835559_aboutDialog_explicit_textContent.py new file mode 100644 index 0000000000..1d73d62692 --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1835559_aboutDialog_explicit_textContent.py @@ -0,0 +1,67 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +import fluent.syntax.ast as FTL + +from fluent.migrate.transforms import TransformPattern + + +class INSERT_VAR_IN_TAG(TransformPattern): + def visit_TextElement(self, node): + old_str = node.value + new_str = old_str + # update-downloading / update-downloading-message + new_str = new_str.replace( + '<label data-l10n-name="download-status"/>', + '<label data-l10n-name="download-status">{ $transfer }</label>', + ) + # update-manual / update-internal-error + new_str = new_str.replace( + '<label data-l10n-name="manual-link"/>', + '<label data-l10n-name="manual-link">{ $displayUrl }</label>', + ) + # aboutdialog-update-manual + new_str = new_str.replace( + '<a data-l10n-name="manual-link"/>', + '<a data-l10n-name="manual-link">{ $displayUrl }</a>', + ) + if old_str == new_str and "$" not in old_str: + print("Warning: Failed to insert var in link in {}".format(old_str)) + else: + node.value = new_str + return node + + +def migrate(ctx): + """Bug 1835559, insert textContent var in a/label elements, part {index}.""" + + aboutDialog_ftl = "browser/browser/aboutDialog.ftl" + ctx.add_transforms( + aboutDialog_ftl, + aboutDialog_ftl, + [ + FTL.Message( + id=FTL.Identifier("settings-update-downloading"), + value=INSERT_VAR_IN_TAG(aboutDialog_ftl, "update-downloading"), + ), + FTL.Message( + id=FTL.Identifier("aboutdialog-update-downloading"), + value=INSERT_VAR_IN_TAG(aboutDialog_ftl, "update-downloading-message"), + ), + # Note: while we're renaming anyway: strip "aboutdialog-" prefix + # because it is used in main.inc.xhtml, not aboutDialog.xhtml. + FTL.Message( + id=FTL.Identifier("settings-update-manual-with-link"), + value=INSERT_VAR_IN_TAG(aboutDialog_ftl, "aboutdialog-update-manual"), + ), + # This is the actual update-manual message in aboutDialog.xhtml. + FTL.Message( + id=FTL.Identifier("aboutdialog-update-manual-with-link"), + value=INSERT_VAR_IN_TAG(aboutDialog_ftl, "update-manual"), + ), + FTL.Message( + id=FTL.Identifier("update-internal-error2"), + value=INSERT_VAR_IN_TAG(aboutDialog_ftl, "update-internal-error"), + ), + ], + ) diff --git a/python/l10n/fluent_migrations/bug_1866295_new_device_migration_strings.py b/python/l10n/fluent_migrations/bug_1866295_new_device_migration_strings.py new file mode 100644 index 0000000000..a8ccb8c145 --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1866295_new_device_migration_strings.py @@ -0,0 +1,22 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +from fluent.migrate.helpers import transforms_from + + +def migrate(ctx): + """Bug 1866295 - Land new strings for device migration ASRouter messages, part {index}.""" + + source = "browser/browser/newtab/asrouter.ftl" + target = source + + ctx.add_transforms( + target, + target, + transforms_from( + """ +device-migration-fxa-spotlight-getting-new-device-primary-button = {COPY_PATTERN(from_path, "device-migration-fxa-spotlight-primary-button")} +""", + from_path=source, + ), + ) diff --git a/python/l10n/fluent_migrations/bug_1867346_new_device_migration_string_replacement.py b/python/l10n/fluent_migrations/bug_1867346_new_device_migration_string_replacement.py new file mode 100644 index 0000000000..a926bff41a --- /dev/null +++ b/python/l10n/fluent_migrations/bug_1867346_new_device_migration_string_replacement.py @@ -0,0 +1,22 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +from fluent.migrate.helpers import transforms_from + + +def migrate(ctx): + """Bug 1867346 - Replace a string for device migration ASRouter messages, part {index}.""" + + source = "browser/browser/newtab/asrouter.ftl" + target = source + + ctx.add_transforms( + target, + target, + transforms_from( + """ +device-migration-fxa-spotlight-getting-new-device-header-2 = {COPY_PATTERN(from_path, "fxa-sync-cfr-header")} +""", + from_path=source, + ), + ) diff --git a/python/l10n/mozxchannel/__init__.py b/python/l10n/mozxchannel/__init__.py new file mode 100644 index 0000000000..66ee3966ca --- /dev/null +++ b/python/l10n/mozxchannel/__init__.py @@ -0,0 +1,150 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this, +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import os +import shutil +from collections import defaultdict +from dataclasses import dataclass, field +from datetime import datetime +from pathlib import Path + +import hglib +from compare_locales import merge +from mozpack import path as mozpath + +from . import projectconfig, source + + +def get_default_config(topsrcdir, strings_path): + assert isinstance(topsrcdir, Path) + assert isinstance(strings_path, Path) + return { + "strings": { + "path": strings_path, + "url": "https://hg.mozilla.org/l10n/gecko-strings-quarantine/", + "heads": {"default": "default"}, + "update_on_pull": True, + "push_url": "ssh://hg.mozilla.org/l10n/gecko-strings-quarantine/", + }, + "source": { + "mozilla-unified": { + "path": topsrcdir, + "url": "https://hg.mozilla.org/mozilla-unified/", + "heads": { + # This list of repositories is ordered, starting with the + # one with the most recent content (central) to the oldest + # (ESR). In case two ESR versions are supported, the oldest + # ESR goes last (e.g. esr78 goes after esr91). + "central": "mozilla-central", + "beta": "releases/mozilla-beta", + "release": "releases/mozilla-release", + "esr102": "releases/mozilla-esr102", + }, + "config_files": [ + "browser/locales/l10n.toml", + "mobile/android/locales/l10n.toml", + ], + }, + }, + } + + +@dataclass +class TargetRevs: + target: bytes = None + revs: list = field(default_factory=list) + + +@dataclass +class CommitRev: + repo: str + rev: bytes + + @property + def message(self): + return ( + f"X-Channel-Repo: {self.repo}\n" + f'X-Channel-Revision: {self.rev.decode("ascii")}' + ) + + +class CrossChannelCreator: + def __init__(self, config): + self.config = config + self.strings_path = config["strings"]["path"] + self.message = ( + f"cross-channel content for {datetime.utcnow().strftime('%Y-%m-%d %H:%M')}" + ) + + def create_content(self): + self.prune_target() + revs = [] + for repo_name, repo_config in self.config["source"].items(): + with hglib.open(repo_config["path"]) as repo: + revs.extend(self.create_for_repo(repo, repo_name, repo_config)) + self.commit(revs) + return 0 + + def prune_target(self): + for leaf in self.config["strings"]["path"].iterdir(): + if leaf.name == ".hg": + continue + shutil.rmtree(leaf) + + def create_for_repo(self, repo, repo_name, repo_config): + print(f"Processing {repo_name} in {repo_config['path']}") + source_target_revs = defaultdict(TargetRevs) + revs_for_commit = [] + parse_kwargs = { + "env": {"l10n_base": str(self.strings_path.parent)}, + "ignore_missing_includes": True, + } + for head, head_name in repo_config["heads"].items(): + print(f"Gathering files for {head}") + rev = repo.log(revrange=head)[0].node + revs_for_commit.append(CommitRev(head_name, rev)) + p = source.HgTOMLParser(repo, rev) + project_configs = [] + for config_file in repo_config["config_files"]: + project_configs.append(p.parse(config_file, **parse_kwargs)) + project_configs[-1].set_locales(["en-US"], deep=True) + hgfiles = source.HGFiles(repo, rev, project_configs) + for targetpath, refpath, _, _ in hgfiles: + source_target_revs[refpath].revs.append(rev) + source_target_revs[refpath].target = targetpath + root = repo.root() + print(f"Writing {repo_name} content to target") + for refpath, targetrevs in source_target_revs.items(): + local_ref = mozpath.relpath(refpath, root) + content = self.get_content(local_ref, repo, targetrevs.revs) + target_dir = mozpath.dirname(targetrevs.target) + if not os.path.isdir(target_dir): + os.makedirs(target_dir) + with open(targetrevs.target, "wb") as fh: + fh.write(content) + return revs_for_commit + + def commit(self, revs): + message = self.message + "\n\n" + if "TASK_ID" in os.environ: + message += f"X-Task-ID: {os.environ['TASK_ID']}\n\n" + message += "\n".join(rev.message for rev in revs) + with hglib.open(self.strings_path) as repo: + repo.commit(message=message, addremove=True) + + def get_content(self, local_ref, repo, revs): + if local_ref.endswith(b".toml"): + return self.get_config_content(local_ref, repo, revs) + if len(revs) < 2: + return repo.cat([b"path:" + local_ref], rev=revs[0]) + contents = [repo.cat([b"path:" + local_ref], rev=rev) for rev in revs] + try: + return merge.merge_channels(local_ref.decode("utf-8"), contents) + except merge.MergeNotSupportedError: + return contents[0] + + def get_config_content(self, local_ref, repo, revs): + # We don't support merging toml files + content = repo.cat([b"path:" + local_ref], rev=revs[0]) + return projectconfig.process_config(content) diff --git a/python/l10n/mozxchannel/projectconfig.py b/python/l10n/mozxchannel/projectconfig.py new file mode 100644 index 0000000000..23d8120a3c --- /dev/null +++ b/python/l10n/mozxchannel/projectconfig.py @@ -0,0 +1,77 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this, +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import re + +from compare_locales import mozpath + +# The local path where we write the config files to +TARGET_PATH = b"_configs" + + +def process_config(toml_content): + """Process TOML configuration content to match the l10n setup for + the reference localization, return target_path and content. + + The code adjusts basepath, [[paths]], and [[includes]] + """ + # adjust basepath in content. '.' works in practice, also in theory? + new_base = mozpath.relpath(b".", TARGET_PATH) + if not new_base: + new_base = b"." # relpath to '.' is '', sadly + base_line = b'\nbasepath = "%s"' % new_base + content1 = re.sub(br"^\s*basepath\s*=\s*.+", base_line, toml_content, flags=re.M) + + # process [[paths]] + start = 0 + content2 = b"" + for m in re.finditer( + br"\[\[\s*paths\s*\]\].+?(?=\[|\Z)", content1, re.M | re.DOTALL + ): + content2 += content1[start : m.start()] + path_content = m.group() + l10n_line = re.search(br"^\s*l10n\s*=.*$", path_content, flags=re.M).group() + # remove variable expansions + new_reference = re.sub(br"{\s*\S+\s*}", b"", l10n_line) + # make the l10n a reference line + new_reference = re.sub(br"^(\s*)l10n(\s*=)", br"\1reference\2", new_reference) + content2 += re.sub( + br"^\s*reference\s*=.*$", new_reference, path_content, flags=re.M + ) + start = m.end() + content2 += content1[start:] + + start = 0 + content3 = b"" + for m in re.finditer( + br"\[\[\s*includes\s*\]\].+?(?=\[|\Z)", content2, re.M | re.DOTALL + ): + content3 += content2[start : m.start()] + include_content = m.group() + m_ = re.search(br'^\s*path = "(.+?)"', include_content, flags=re.M) + content3 += ( + include_content[: m_.start(1)] + + generate_filename(m_.group(1)) + + include_content[m_.end(1) :] + ) + start = m.end() + content3 += content2[start:] + + return content3 + + +def generate_filename(path): + segs = path.split(b"/") + # strip /locales/ from filename + segs = [seg for seg in segs if seg != b"locales"] + # strip variables from filename + segs = [seg for seg in segs if not seg.startswith(b"{") and not seg.endswith(b"}")] + if segs[-1] == b"l10n.toml": + segs.pop() + segs[-1] += b".toml" + outpath = b"-".join(segs) + if TARGET_PATH != b".": + # prepend the target path, if it's not '.' + outpath = mozpath.join(TARGET_PATH, outpath) + return outpath diff --git a/python/l10n/mozxchannel/source.py b/python/l10n/mozxchannel/source.py new file mode 100644 index 0000000000..b9d2067980 --- /dev/null +++ b/python/l10n/mozxchannel/source.py @@ -0,0 +1,88 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this, +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import toml +from compare_locales import mozpath, paths +from compare_locales.paths.matcher import expand + +from .projectconfig import generate_filename + + +class HGFiles(paths.ProjectFiles): + def __init__(self, repo, rev, projects): + self.repo = repo + self.ctx = repo[rev] + self.root = repo.root() + self.manifest = None + self.configs_map = {} + # get paths for our TOML files + for p in projects: + all_configpaths = { + mozpath.abspath(c.path).encode("utf-8") for c in p.configs + } + for refpath in all_configpaths: + local_path = mozpath.relpath(refpath, self.root) + if local_path not in self.ctx: + print("ignoring", refpath) + continue + targetpath = b"/".join( + ( + expand(None, "{l10n_base}", p.environ).encode("utf-8"), + b"en-US", + generate_filename(local_path), + ) + ) + self.configs_map[refpath] = targetpath + super(HGFiles, self).__init__("en-US", projects) + for m in self.matchers: + m["l10n"].encoding = "utf-8" + if "reference" in m: + m["reference"].encoding = "utf-8" + if self.exclude: + for m in self.exclude.matchers: + m["l10n"].encoding = "utf-8" + if "reference" in m: + m["reference"].encoding = "utf-8" + + def _files(self, matcher): + for f in self.ctx.manifest(): + f = mozpath.join(self.root, f) + if matcher.match(f): + yield f + + def __iter__(self): + for t in super(HGFiles, self).__iter__(): + yield t + for refpath, targetpath in self.configs_map.items(): + yield targetpath, refpath, None, set() + + def match(self, path): + m = super(HGFiles, self).match(path) + if m: + return m + for refpath, targetpath in self.configs_map.items(): + if path in [refpath, targetpath]: + return targetpath, refpath, None, set() + + +class HgTOMLParser(paths.TOMLParser): + "subclass to load from our hg context" + + def __init__(self, repo, rev): + self.repo = repo + self.rev = rev + self.root = repo.root().decode("utf-8") + + def load(self, parse_ctx): + try: + path = parse_ctx.path + local_path = "path:" + mozpath.relpath(path, self.root) + data = self.repo.cat(files=[local_path.encode("utf-8")], rev=self.rev) + except Exception: + raise paths.ConfigNotFound(parse_ctx.path) + + try: + parse_ctx.data = toml.loads(data.decode()) + except toml.TomlDecodeError as e: + raise RuntimeError(f"In file '{parse_ctx.path}':\n {e!s}") from e diff --git a/python/l10n/test_fluent_migrations/__init__.py b/python/l10n/test_fluent_migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/python/l10n/test_fluent_migrations/__init__.py diff --git a/python/l10n/test_fluent_migrations/fmt.py b/python/l10n/test_fluent_migrations/fmt.py new file mode 100644 index 0000000000..150a942e78 --- /dev/null +++ b/python/l10n/test_fluent_migrations/fmt.py @@ -0,0 +1,188 @@ +import codecs +import logging +import os +import re +import shutil +import sys +from difflib import unified_diff + +import hglib +import mozpack.path as mozpath +from compare_locales.merge import merge_channels +from compare_locales.paths.configparser import TOMLParser +from compare_locales.paths.files import ProjectFiles +from fluent.migrate import validator +from fluent.syntax import FluentParser, FluentSerializer +from mach.util import get_state_dir + + +def inspect_migration(path): + """Validate recipe and extract some metadata.""" + return validator.Validator.validate(path) + + +def prepare_object_dir(cmd): + """Prepare object dir to have an up-to-date clone of gecko-strings. + + We run this once per mach invocation, for all tested migrations. + """ + obj_dir = mozpath.join(cmd.topobjdir, "python", "l10n") + if not os.path.exists(obj_dir): + os.makedirs(obj_dir) + state_dir = get_state_dir() + if os.path.exists(mozpath.join(state_dir, "gecko-strings")): + cmd.run_process( + ["hg", "pull", "-u"], cwd=mozpath.join(state_dir, "gecko-strings") + ) + else: + cmd.run_process( + ["hg", "clone", "https://hg.mozilla.org/l10n/gecko-strings"], + cwd=state_dir, + ) + return obj_dir + + +def diff_resources(left_path, right_path): + parser = FluentParser(with_spans=False) + serializer = FluentSerializer(with_junk=True) + lines = [] + for p in (left_path, right_path): + with codecs.open(p, encoding="utf-8") as fh: + res = parser.parse(fh.read()) + lines.append(serializer.serialize(res).splitlines(True)) + sys.stdout.writelines( + chunk for chunk in unified_diff(lines[0], lines[1], left_path, right_path) + ) + + +def test_migration(cmd, obj_dir, to_test, references): + """Test the given recipe. + + This creates a workdir by l10n-merging gecko-strings and the m-c source, + to mimmic gecko-strings after the patch to test landed. + It then runs the recipe with a gecko-strings clone as localization, both + dry and wet. + It inspects the generated commits, and shows a diff between the merged + reference and the generated content. + The diff is intended to be visually inspected. Some changes might be + expected, in particular when formatting of the en-US strings is different. + """ + rv = 0 + migration_name = os.path.splitext(os.path.split(to_test)[1])[0] + work_dir = mozpath.join(obj_dir, migration_name) + + paths = os.path.normpath(to_test).split(os.sep) + # Migration modules should be in a sub-folder of l10n. + migration_module = ( + ".".join(paths[paths.index("l10n") + 1 : -1]) + "." + migration_name + ) + + if os.path.exists(work_dir): + shutil.rmtree(work_dir) + os.makedirs(mozpath.join(work_dir, "reference")) + l10n_toml = mozpath.join( + cmd.topsrcdir, cmd.substs["MOZ_BUILD_APP"], "locales", "l10n.toml" + ) + pc = TOMLParser().parse(l10n_toml, env={"l10n_base": work_dir}) + pc.set_locales(["reference"]) + files = ProjectFiles("reference", [pc]) + for ref in references: + if ref != mozpath.normpath(ref): + cmd.log( + logging.ERROR, + "fluent-migration-test", + { + "file": to_test, + "ref": ref, + }, + 'Reference path "{ref}" needs to be normalized for {file}', + ) + rv = 1 + continue + full_ref = mozpath.join(work_dir, "reference", ref) + m = files.match(full_ref) + if m is None: + raise ValueError("Bad reference path: " + ref) + m_c_path = m[1] + g_s_path = mozpath.join(work_dir, "gecko-strings", ref) + resources = [ + b"" if not os.path.exists(f) else open(f, "rb").read() + for f in (g_s_path, m_c_path) + ] + ref_dir = os.path.dirname(full_ref) + if not os.path.exists(ref_dir): + os.makedirs(ref_dir) + open(full_ref, "wb").write(merge_channels(ref, resources)) + client = hglib.clone( + source=mozpath.join(get_state_dir(), "gecko-strings"), + dest=mozpath.join(work_dir, "en-US"), + ) + client.open() + old_tip = client.tip().node + run_migration = [ + cmd._virtualenv_manager.python_path, + "-m", + "fluent.migrate.tool", + "--lang", + "en-US", + "--reference-dir", + mozpath.join(work_dir, "reference"), + "--localization-dir", + mozpath.join(work_dir, "en-US"), + "--dry-run", + migration_module, + ] + cmd.run_process( + run_migration, + cwd=work_dir, + line_handler=print, + ) + # drop --dry-run + run_migration.pop(-2) + cmd.run_process( + run_migration, + cwd=work_dir, + line_handler=print, + ) + tip = client.tip().node + if old_tip == tip: + cmd.log( + logging.WARN, + "fluent-migration-test", + { + "file": to_test, + }, + "No migration applied for {file}", + ) + return rv + for ref in references: + diff_resources( + mozpath.join(work_dir, "reference", ref), + mozpath.join(work_dir, "en-US", ref), + ) + messages = [ + l.desc.decode("utf-8") for l in client.log(b"::%s - ::%s" % (tip, old_tip)) + ] + bug = re.search("[0-9]{5,}", migration_name) + # Just check first message for bug number, they're all following the same pattern + if bug is None or bug.group() not in messages[0]: + rv = 1 + cmd.log( + logging.ERROR, + "fluent-migration-test", + { + "file": to_test, + }, + "Missing or wrong bug number for {file}", + ) + if any("part {}".format(n + 1) not in msg for n, msg in enumerate(messages)): + rv = 1 + cmd.log( + logging.ERROR, + "fluent-migration-test", + { + "file": to_test, + }, + 'Commit messages should have "part {{index}}" for {file}', + ) + return rv |