summaryrefslogtreecommitdiffstats
path: root/python/l10n
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /python/l10n
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'python/l10n')
-rw-r--r--python/l10n/fluent_migrations/__init__.py0
-rw-r--r--python/l10n/fluent_migrations/bug_1347955_aboutLogging.py35
-rw-r--r--python/l10n/fluent_migrations/bug_1552333_aboutCertError.py40
-rw-r--r--python/l10n/fluent_migrations/bug_1577257_profiler_title.py22
-rw-r--r--python/l10n/fluent_migrations/bug_1635548_browser_context.py82
-rw-r--r--python/l10n/fluent_migrations/bug_1760029_tabbrowser.py399
-rw-r--r--python/l10n/fluent_migrations/bug_1780585_pipFocusedstate.py54
-rw-r--r--python/l10n/fluent_migrations/bug_1786186_mobile_aboutConfig.py65
-rw-r--r--python/l10n/fluent_migrations/bug_1790387_featureCallout_a11y.py22
-rw-r--r--python/l10n/fluent_migrations/bug_1791178_downloadUI.py187
-rw-r--r--python/l10n/fluent_migrations/bug_1791178_downloadUtils.py172
-rw-r--r--python/l10n/fluent_migrations/bug_1791178_nsContextMenu_saveHelper.py39
-rw-r--r--python/l10n/fluent_migrations/bug_1793572_webrtc.py771
-rw-r--r--python/l10n/fluent_migrations/bug_1801035_places_window_no_size.py21
-rw-r--r--python/l10n/fluent_migrations/bug_1801915_confirmationHints.py29
-rw-r--r--python/l10n/fluent_migrations/bug_1802128_langpack_defines.py43
-rw-r--r--python/l10n/fluent_migrations/bug_1803010_downloads.py30
-rw-r--r--python/l10n/fluent_migrations/bug_1805319_webrtc_indicator_typo.py23
-rw-r--r--python/l10n/mozxchannel/__init__.py150
-rw-r--r--python/l10n/mozxchannel/projectconfig.py77
-rw-r--r--python/l10n/mozxchannel/source.py84
-rw-r--r--python/l10n/test_fluent_migrations/__init__.py0
-rw-r--r--python/l10n/test_fluent_migrations/fmt.py188
23 files changed, 2533 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_1347955_aboutLogging.py b/python/l10n/fluent_migrations/bug_1347955_aboutLogging.py
new file mode 100644
index 0000000000..a10aa1ae8d
--- /dev/null
+++ b/python/l10n/fluent_migrations/bug_1347955_aboutLogging.py
@@ -0,0 +1,35 @@
+# 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_PATTERN
+
+
+def migrate(ctx):
+ """Bug 1347955 - Move the logging sub-page of about:networking to about:logging, part {index}"""
+ ctx.add_transforms(
+ "toolkit/toolkit/about/aboutLogging.ftl",
+ "toolkit/toolkit/about/aboutLogging.ftl",
+ transforms_from(
+ """
+about-logging-current-log-file =
+ {COPY_PATTERN(from_path, "about-networking-current-log-file")}
+about-logging-current-log-modules =
+ {COPY_PATTERN(from_path, "about-networking-current-log-modules")}
+about-logging-set-log-file =
+ {COPY_PATTERN(from_path, "about-networking-set-log-file")}
+about-logging-set-log-modules =
+ {COPY_PATTERN(from_path, "about-networking-set-log-modules")}
+about-logging-start-logging =
+ {COPY_PATTERN(from_path, "about-networking-start-logging")}
+about-logging-stop-logging =
+ {COPY_PATTERN(from_path, "about-networking-stop-logging")}
+about-logging-log-tutorial =
+ {COPY_PATTERN(from_path, "about-networking-log-tutorial")}
+ """,
+ from_path="toolkit/toolkit/about/aboutNetworking.ftl",
+ ),
+ )
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_1577257_profiler_title.py b/python/l10n/fluent_migrations/bug_1577257_profiler_title.py
new file mode 100644
index 0000000000..4091e596da
--- /dev/null
+++ b/python/l10n/fluent_migrations/bug_1577257_profiler_title.py
@@ -0,0 +1,22 @@
+# 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_PATTERN
+
+
+def migrate(ctx):
+ """Bug 1577257 - Share logic behind panel headers across the UI, part {index}"""
+ ctx.add_transforms(
+ "browser/browser/appmenu.ftl",
+ "browser/browser/appmenu.ftl",
+ transforms_from(
+ """
+profiler-popup-header-text =
+ { COPY_PATTERN(from_path, "profiler-popup-title.value") }
+""",
+ from_path="browser/browser/appmenu.ftl",
+ ),
+ )
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_1760029_tabbrowser.py b/python/l10n/fluent_migrations/bug_1760029_tabbrowser.py
new file mode 100644
index 0000000000..9fe8cf3a7d
--- /dev/null
+++ b/python/l10n/fluent_migrations/bug_1760029_tabbrowser.py
@@ -0,0 +1,399 @@
+# 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, Transform
+
+
+def migrate(ctx):
+ """Bug 1760029 - Migrate tabbrowser to Fluent, part {index}."""
+
+ source = "browser/chrome/browser/tabbrowser.properties"
+ target = "browser/browser/tabbrowser.ftl"
+
+ browser_source = "browser/chrome/browser/browser.properties"
+ browser_target = "browser/browser/browser.ftl"
+
+ ctx.add_transforms(
+ target,
+ target,
+ [
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-empty-tab-title"),
+ value=COPY(source, "tabs.emptyTabTitle"),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-empty-private-tab-title"),
+ value=COPY(source, "tabs.emptyPrivateTabTitle2"),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-menuitem-close-tab"),
+ attributes=[
+ FTL.Attribute(
+ id=FTL.Identifier("label"), value=COPY(source, "tabs.closeTab")
+ )
+ ],
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-menuitem-close"),
+ attributes=[
+ FTL.Attribute(
+ id=FTL.Identifier("label"), value=COPY(source, "tabs.close")
+ )
+ ],
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-container-tab-title"),
+ value=REPLACE(
+ source,
+ "tabs.containers.tooltip",
+ {
+ "%1$S": VARIABLE_REFERENCE("title"),
+ "%2$S": VARIABLE_REFERENCE("containerName"),
+ },
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-tab-tooltip"),
+ attributes=[
+ FTL.Attribute(
+ id=FTL.Identifier("label"),
+ value=FTL.Pattern([FTL.Placeable(VARIABLE_REFERENCE("title"))]),
+ )
+ ],
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-close-tabs-tooltip"),
+ attributes=[
+ FTL.Attribute(
+ id=FTL.Identifier("label"),
+ value=PLURALS(
+ source,
+ "tabs.closeTabs.tooltip",
+ VARIABLE_REFERENCE("tabCount"),
+ foreach=lambda n: REPLACE_IN_TEXT(
+ n,
+ {"#1": VARIABLE_REFERENCE("tabCount")},
+ ),
+ ),
+ )
+ ],
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-mute-tab-audio-tooltip"),
+ attributes=[
+ FTL.Attribute(
+ id=FTL.Identifier("label"),
+ value=PLURALS(
+ source,
+ "tabs.muteAudio2.tooltip",
+ VARIABLE_REFERENCE("tabCount"),
+ foreach=lambda text: REPLACE_IN_TEXT(
+ text,
+ {
+ "#1": VARIABLE_REFERENCE("tabCount"),
+ "%S": VARIABLE_REFERENCE("shortcut"),
+ },
+ ),
+ ),
+ )
+ ],
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-unmute-tab-audio-tooltip"),
+ attributes=[
+ FTL.Attribute(
+ id=FTL.Identifier("label"),
+ value=PLURALS(
+ source,
+ "tabs.unmuteAudio2.tooltip",
+ VARIABLE_REFERENCE("tabCount"),
+ foreach=lambda text: REPLACE_IN_TEXT(
+ text,
+ {
+ "#1": VARIABLE_REFERENCE("tabCount"),
+ "%S": VARIABLE_REFERENCE("shortcut"),
+ },
+ ),
+ ),
+ )
+ ],
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-mute-tab-audio-background-tooltip"),
+ attributes=[
+ FTL.Attribute(
+ id=FTL.Identifier("label"),
+ value=PLURALS(
+ source,
+ "tabs.muteAudio2.background.tooltip",
+ VARIABLE_REFERENCE("tabCount"),
+ foreach=lambda n: REPLACE_IN_TEXT(
+ n,
+ {"#1": VARIABLE_REFERENCE("tabCount")},
+ ),
+ ),
+ )
+ ],
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-unmute-tab-audio-background-tooltip"),
+ attributes=[
+ FTL.Attribute(
+ id=FTL.Identifier("label"),
+ value=PLURALS(
+ source,
+ "tabs.unmuteAudio2.background.tooltip",
+ VARIABLE_REFERENCE("tabCount"),
+ foreach=lambda n: REPLACE_IN_TEXT(
+ n,
+ {"#1": VARIABLE_REFERENCE("tabCount")},
+ ),
+ ),
+ )
+ ],
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-unblock-tab-audio-tooltip"),
+ attributes=[
+ FTL.Attribute(
+ id=FTL.Identifier("label"),
+ value=PLURALS(
+ source,
+ "tabs.unblockAudio2.tooltip",
+ VARIABLE_REFERENCE("tabCount"),
+ foreach=lambda n: REPLACE_IN_TEXT(
+ n,
+ {"#1": VARIABLE_REFERENCE("tabCount")},
+ ),
+ ),
+ )
+ ],
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-confirm-close-tabs-title"),
+ value=PLURALS(
+ source,
+ "tabs.closeTabsTitle",
+ VARIABLE_REFERENCE("tabCount"),
+ foreach=lambda n: REPLACE_IN_TEXT(
+ n,
+ {"#1": VARIABLE_REFERENCE("tabCount")},
+ ),
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-confirm-close-tabs-button"),
+ value=COPY(source, "tabs.closeButtonMultiple"),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-confirm-close-tabs-checkbox"),
+ value=COPY(source, "tabs.closeTabsConfirmCheckbox"),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-confirm-close-windows-title"),
+ value=PLURALS(
+ source,
+ "tabs.closeWindowsTitle",
+ VARIABLE_REFERENCE("windowCount"),
+ foreach=lambda n: REPLACE_IN_TEXT(
+ n,
+ {"#1": VARIABLE_REFERENCE("windowCount")},
+ ),
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-confirm-close-windows-button"),
+ value=Transform.pattern_of(
+ FTL.SelectExpression(
+ selector=FTL.FunctionReference(
+ id=FTL.Identifier("PLATFORM"), arguments=FTL.CallArguments()
+ ),
+ variants=[
+ FTL.Variant(
+ key=FTL.Identifier("windows"),
+ value=COPY(source, "tabs.closeWindowsButtonWin"),
+ ),
+ FTL.Variant(
+ key=FTL.Identifier("other"),
+ value=COPY(source, "tabs.closeWindowsButton"),
+ default=True,
+ ),
+ ],
+ )
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-confirm-close-tabs-with-key-title"),
+ value=REPLACE(
+ source,
+ "tabs.closeTabsWithKeyTitle",
+ {"%1$S": TERM_REFERENCE("brand-short-name")},
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-confirm-close-tabs-with-key-button"),
+ value=REPLACE(
+ source,
+ "tabs.closeTabsWithKeyButton",
+ {"%1$S": TERM_REFERENCE("brand-short-name")},
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-confirm-close-tabs-with-key-checkbox"),
+ value=REPLACE(
+ source,
+ "tabs.closeTabsWithKeyConfirmCheckbox",
+ {"%1$S": VARIABLE_REFERENCE("quitKey")},
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-confirm-open-multiple-tabs-title"),
+ value=COPY(source, "tabs.openWarningTitle"),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-confirm-open-multiple-tabs-message"),
+ value=Transform.pattern_of(
+ FTL.SelectExpression(
+ selector=VARIABLE_REFERENCE("tabCount"),
+ variants=[
+ FTL.Variant(
+ key=FTL.Identifier("other"),
+ value=REPLACE(
+ source,
+ "tabs.openWarningMultipleBranded",
+ {
+ "%1$S": VARIABLE_REFERENCE("tabCount"),
+ "%2$S": TERM_REFERENCE("brand-short-name"),
+ ". ": FTL.TextElement(". "),
+ },
+ ),
+ default=True,
+ ),
+ ],
+ )
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-confirm-open-multiple-tabs-button"),
+ value=COPY(source, "tabs.openButtonMultiple"),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-confirm-open-multiple-tabs-checkbox"),
+ value=REPLACE(
+ source,
+ "tabs.openWarningPromptMeBranded",
+ {"%1$S": TERM_REFERENCE("brand-short-name")},
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-confirm-caretbrowsing-title"),
+ value=COPY(source, "browsewithcaret.checkWindowTitle"),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-confirm-caretbrowsing-message"),
+ value=COPY(source, "browsewithcaret.checkLabel"),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-confirm-caretbrowsing-checkbox"),
+ value=COPY(source, "browsewithcaret.checkMsg"),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-customizemode-tab-title"),
+ value=REPLACE(
+ browser_source,
+ "customizeMode.tabTitle",
+ {"%1$S": TERM_REFERENCE("brand-short-name")},
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-context-mute-tab"),
+ attributes=[
+ FTL.Attribute(
+ id=FTL.Identifier("label"),
+ value=COPY(browser_source, "muteTab.label"),
+ ),
+ FTL.Attribute(
+ id=FTL.Identifier("accesskey"),
+ value=COPY(browser_source, "muteTab.accesskey"),
+ ),
+ ],
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-context-unmute-tab"),
+ attributes=[
+ FTL.Attribute(
+ id=FTL.Identifier("label"),
+ value=COPY(browser_source, "unmuteTab.label"),
+ ),
+ FTL.Attribute(
+ id=FTL.Identifier("accesskey"),
+ value=COPY(browser_source, "unmuteTab.accesskey"),
+ ),
+ ],
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-context-mute-selected-tabs"),
+ attributes=[
+ FTL.Attribute(
+ id=FTL.Identifier("label"),
+ value=COPY(browser_source, "muteSelectedTabs2.label"),
+ ),
+ FTL.Attribute(
+ id=FTL.Identifier("accesskey"),
+ value=COPY(browser_source, "muteSelectedTabs2.accesskey"),
+ ),
+ ],
+ ),
+ FTL.Message(
+ id=FTL.Identifier("tabbrowser-context-unmute-selected-tabs"),
+ attributes=[
+ FTL.Attribute(
+ id=FTL.Identifier("label"),
+ value=COPY(browser_source, "unmuteSelectedTabs2.label"),
+ ),
+ FTL.Attribute(
+ id=FTL.Identifier("accesskey"),
+ value=COPY(browser_source, "unmuteSelectedTabs2.accesskey"),
+ ),
+ ],
+ ),
+ ],
+ )
+
+ ctx.add_transforms(
+ browser_target,
+ browser_target,
+ [
+ FTL.Message(
+ id=FTL.Identifier("refresh-blocked-refresh-label"),
+ value=REPLACE(
+ browser_source,
+ "refreshBlocked.refreshLabel",
+ {"%1$S": TERM_REFERENCE("brand-short-name")},
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("refresh-blocked-redirect-label"),
+ value=REPLACE(
+ browser_source,
+ "refreshBlocked.redirectLabel",
+ {"%1$S": TERM_REFERENCE("brand-short-name")},
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("refresh-blocked-allow"),
+ attributes=[
+ FTL.Attribute(
+ id=FTL.Identifier("label"),
+ value=COPY(browser_source, "refreshBlocked.goButton"),
+ ),
+ FTL.Attribute(
+ id=FTL.Identifier("accesskey"),
+ value=COPY(browser_source, "refreshBlocked.goButton.accesskey"),
+ ),
+ ],
+ ),
+ ],
+ )
diff --git a/python/l10n/fluent_migrations/bug_1780585_pipFocusedstate.py b/python/l10n/fluent_migrations/bug_1780585_pipFocusedstate.py
new file mode 100644
index 0000000000..6b1e706918
--- /dev/null
+++ b/python/l10n/fluent_migrations/bug_1780585_pipFocusedstate.py
@@ -0,0 +1,54 @@
+# coding=utf8
+
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+from fluent.migrate import COPY_PATTERN
+from fluent.migrate.helpers import transforms_from
+
+
+def migrate(ctx):
+ """Bug 1780585 - Enhanced focused states on PiP controls, part {index}."""
+
+ ctx.add_transforms(
+ "toolkit/toolkit/pictureinpicture/pictureinpicture.ftl",
+ "toolkit/toolkit/pictureinpicture/pictureinpicture.ftl",
+ transforms_from(
+ """
+pictureinpicture-pause-btn =
+ .aria-label = {COPY_PATTERN(from_path, "pictureinpicture-pause-cmd.aria-label")}
+ .tooltip = {COPY_PATTERN(from_path, "pictureinpicture-pause-cmd.title")}
+pictureinpicture-play-btn =
+ .aria-label = {COPY_PATTERN(from_path, "pictureinpicture-play-cmd.aria-label")}
+ .tooltip = {COPY_PATTERN(from_path, "pictureinpicture-play-cmd.title")}
+pictureinpicture-mute-btn =
+ .aria-label = {COPY_PATTERN(from_path, "pictureinpicture-mute-cmd.aria-label")}
+ .tooltip = {COPY_PATTERN(from_path, "pictureinpicture-mute-cmd.title")}
+pictureinpicture-unmute-btn =
+ .aria-label = {COPY_PATTERN(from_path, "pictureinpicture-unmute-cmd.aria-label")}
+ .tooltip = {COPY_PATTERN(from_path, "pictureinpicture-unmute-cmd.title")}
+pictureinpicture-unpip-btn =
+ .aria-label = {COPY_PATTERN(from_path, "pictureinpicture-unpip-cmd.aria-label")}
+ .tooltip = {COPY_PATTERN(from_path, "pictureinpicture-unpip-cmd.title")}
+pictureinpicture-close-btn =
+ .aria-label = {COPY_PATTERN(from_path, "pictureinpicture-close-cmd.aria-label")}
+ .tooltip = {COPY_PATTERN(from_path, "pictureinpicture-close-cmd.title")}
+pictureinpicture-subtitles-btn =
+ .aria-label = {COPY_PATTERN(from_path, "pictureinpicture-subtitles-cmd.aria-label")}
+ .tooltip = {COPY_PATTERN(from_path, "pictureinpicture-subtitles-cmd.title")}
+pictureinpicture-fullscreen-btn =
+ .aria-label = {COPY_PATTERN(from_path, "pictureinpicture-fullscreen-cmd.aria-label")}
+ .tooltip = {COPY_PATTERN(from_path, "pictureinpicture-fullscreen-cmd.title")}
+pictureinpicture-exit-fullscreen-btn =
+ .aria-label = {COPY_PATTERN(from_path, "pictureinpicture-exit-fullscreen-cmd.aria-label")}
+ .tooltip = {COPY_PATTERN(from_path, "pictureinpicture-exit-fullscreen-cmd.title")}
+pictureinpicture-seekbackward-btn =
+ .aria-label = {COPY_PATTERN(from_path, "pictureinpicture-seekbackward-cmd.aria-label")}
+ .tooltip = {COPY_PATTERN(from_path, "pictureinpicture-seekbackward-cmd.title")}
+pictureinpicture-seekforward-btn =
+ .aria-label = {COPY_PATTERN(from_path, "pictureinpicture-seekforward-cmd.aria-label")}
+ .tooltip = {COPY_PATTERN(from_path, "pictureinpicture-seekforward-cmd.title")}
+ """,
+ from_path="toolkit/toolkit/pictureinpicture/pictureinpicture.ftl",
+ ),
+ )
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_1790387_featureCallout_a11y.py b/python/l10n/fluent_migrations/bug_1790387_featureCallout_a11y.py
new file mode 100644
index 0000000000..0d6d839acd
--- /dev/null
+++ b/python/l10n/fluent_migrations/bug_1790387_featureCallout_a11y.py
@@ -0,0 +1,22 @@
+# coding=utf8
+
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+from fluent.migrate import COPY_PATTERN
+from fluent.migrate.helpers import transforms_from
+
+
+def migrate(ctx):
+ """Bug 1790387 - A11y improvements for the Feature Callout step indicator, part {index}"""
+ ctx.add_transforms(
+ "browser/browser/newtab/onboarding.ftl",
+ "browser/browser/newtab/onboarding.ftl",
+ transforms_from(
+ """
+onboarding-welcome-steps-indicator-label =
+ .aria-label = { COPY_PATTERN(from_path, "onboarding-welcome-steps-indicator2.aria-valuetext") }
+""",
+ from_path="browser/browser/newtab/onboarding.ftl",
+ ),
+ )
diff --git a/python/l10n/fluent_migrations/bug_1791178_downloadUI.py b/python/l10n/fluent_migrations/bug_1791178_downloadUI.py
new file mode 100644
index 0000000000..d6256889f2
--- /dev/null
+++ b/python/l10n/fluent_migrations/bug_1791178_downloadUI.py
@@ -0,0 +1,187 @@
+# 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, REPLACE, Transform
+
+
+def migrate(ctx):
+ """Bug 1791178 - Convert DownloadUIHelper.jsm to Fluent, part {index}."""
+
+ source = "toolkit/chrome/mozapps/downloads/downloads.properties"
+ target = "toolkit/toolkit/downloads/downloadUI.ftl"
+ ctx.add_transforms(
+ target,
+ target,
+ [
+ FTL.Message(
+ id=FTL.Identifier("download-ui-confirm-title"),
+ value=COPY(source, "quitCancelDownloadsAlertTitle"),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-ui-confirm-quit-cancel-downloads"),
+ value=Transform.pattern_of(
+ FTL.SelectExpression(
+ selector=VARIABLE_REFERENCE("downloadsCount"),
+ variants=[
+ FTL.Variant(
+ key=FTL.Identifier("1"),
+ value=COPY(source, "quitCancelDownloadsAlertMsg"),
+ ),
+ FTL.Variant(
+ key=FTL.Identifier("other"),
+ default=True,
+ value=REPLACE(
+ source,
+ "quitCancelDownloadsAlertMsgMultiple",
+ {"%1$S": VARIABLE_REFERENCE("downloadsCount")},
+ ),
+ ),
+ ],
+ )
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-ui-confirm-quit-cancel-downloads-mac"),
+ value=Transform.pattern_of(
+ FTL.SelectExpression(
+ selector=VARIABLE_REFERENCE("downloadsCount"),
+ variants=[
+ FTL.Variant(
+ key=FTL.Identifier("1"),
+ value=COPY(source, "quitCancelDownloadsAlertMsgMac"),
+ ),
+ FTL.Variant(
+ key=FTL.Identifier("other"),
+ default=True,
+ value=REPLACE(
+ source,
+ "quitCancelDownloadsAlertMsgMacMultiple",
+ {"%1$S": VARIABLE_REFERENCE("downloadsCount")},
+ ),
+ ),
+ ],
+ )
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-ui-confirm-offline-cancel-downloads"),
+ value=Transform.pattern_of(
+ FTL.SelectExpression(
+ selector=VARIABLE_REFERENCE("downloadsCount"),
+ variants=[
+ FTL.Variant(
+ key=FTL.Identifier("1"),
+ value=COPY(source, "offlineCancelDownloadsAlertMsg"),
+ ),
+ FTL.Variant(
+ key=FTL.Identifier("other"),
+ default=True,
+ value=REPLACE(
+ source,
+ "offlineCancelDownloadsAlertMsgMultiple",
+ {"%1$S": VARIABLE_REFERENCE("downloadsCount")},
+ ),
+ ),
+ ],
+ )
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier(
+ "download-ui-confirm-leave-private-browsing-windows-cancel-downloads"
+ ),
+ value=Transform.pattern_of(
+ FTL.SelectExpression(
+ selector=VARIABLE_REFERENCE("downloadsCount"),
+ variants=[
+ FTL.Variant(
+ key=FTL.Identifier("1"),
+ value=COPY(
+ source,
+ "leavePrivateBrowsingWindowsCancelDownloadsAlertMsg2",
+ ),
+ ),
+ FTL.Variant(
+ key=FTL.Identifier("other"),
+ default=True,
+ value=REPLACE(
+ source,
+ "leavePrivateBrowsingWindowsCancelDownloadsAlertMsgMultiple2",
+ {"%1$S": VARIABLE_REFERENCE("downloadsCount")},
+ ),
+ ),
+ ],
+ )
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-ui-cancel-downloads-ok"),
+ value=Transform.pattern_of(
+ FTL.SelectExpression(
+ selector=VARIABLE_REFERENCE("downloadsCount"),
+ variants=[
+ FTL.Variant(
+ key=FTL.Identifier("1"),
+ value=COPY(source, "cancelDownloadsOKText"),
+ ),
+ FTL.Variant(
+ key=FTL.Identifier("other"),
+ default=True,
+ value=REPLACE(
+ source,
+ "cancelDownloadsOKTextMultiple",
+ {"%1$S": VARIABLE_REFERENCE("downloadsCount")},
+ ),
+ ),
+ ],
+ )
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-ui-dont-quit-button"),
+ value=Transform.pattern_of(
+ FTL.SelectExpression(
+ selector=FTL.FunctionReference(
+ id=FTL.Identifier("PLATFORM"), arguments=FTL.CallArguments()
+ ),
+ variants=[
+ FTL.Variant(
+ key=FTL.Identifier("mac"),
+ value=COPY(source, "dontQuitButtonMac"),
+ ),
+ FTL.Variant(
+ key=FTL.Identifier("other"),
+ default=True,
+ value=COPY(source, "dontQuitButtonWin"),
+ ),
+ ],
+ )
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-ui-dont-go-offline-button"),
+ value=COPY(source, "dontGoOfflineButton"),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-ui-dont-leave-private-browsing-button"),
+ value=COPY(source, "dontLeavePrivateBrowsingButton2"),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-ui-file-executable-security-warning-title"),
+ value=COPY(source, "fileExecutableSecurityWarningTitle"),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-ui-file-executable-security-warning"),
+ value=REPLACE(
+ source,
+ "fileExecutableSecurityWarning",
+ {
+ "%1$S": VARIABLE_REFERENCE("executable"),
+ "%2$S": VARIABLE_REFERENCE("executable"),
+ },
+ ),
+ ),
+ ],
+ )
diff --git a/python/l10n/fluent_migrations/bug_1791178_downloadUtils.py b/python/l10n/fluent_migrations/bug_1791178_downloadUtils.py
new file mode 100644
index 0000000000..d6cb174a86
--- /dev/null
+++ b/python/l10n/fluent_migrations/bug_1791178_downloadUtils.py
@@ -0,0 +1,172 @@
+# 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
+
+
+def migrate(ctx):
+ """Bug 1791178 - Convert DownloadUtils.jsm to Fluent, part {index}."""
+
+ source = "toolkit/chrome/mozapps/downloads/downloads.properties"
+ target = "toolkit/toolkit/downloads/downloadUtils.ftl"
+ ctx.add_transforms(
+ target,
+ target,
+ [
+ FTL.Message(
+ id=FTL.Identifier("download-utils-short-seconds"),
+ value=PLURALS(source, "shortSeconds", VARIABLE_REFERENCE("timeValue")),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-utils-short-minutes"),
+ value=PLURALS(source, "shortMinutes", VARIABLE_REFERENCE("timeValue")),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-utils-short-hours"),
+ value=PLURALS(source, "shortHours", VARIABLE_REFERENCE("timeValue")),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-utils-short-days"),
+ value=PLURALS(source, "shortDays", VARIABLE_REFERENCE("timeValue")),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-utils-status"),
+ value=REPLACE(
+ source,
+ "statusFormat3",
+ {
+ "%4$S": VARIABLE_REFERENCE("timeLeft"),
+ "%1$S": VARIABLE_REFERENCE("transfer"),
+ "%2$S": VARIABLE_REFERENCE("rate"),
+ "%3$S": VARIABLE_REFERENCE("unit"),
+ },
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-utils-status-infinite-rate"),
+ value=REPLACE(
+ source,
+ "statusFormatInfiniteRate",
+ {
+ "%3$S": VARIABLE_REFERENCE("timeLeft"),
+ "%1$S": VARIABLE_REFERENCE("transfer"),
+ "%2$S": COPY(source, "infiniteRate"),
+ },
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-utils-status-no-rate"),
+ value=REPLACE(
+ source,
+ "statusFormatNoRate",
+ {
+ "%2$S": VARIABLE_REFERENCE("timeLeft"),
+ "%1$S": VARIABLE_REFERENCE("transfer"),
+ },
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-utils-bytes"), value=COPY(source, "bytes")
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-utils-kilobyte"),
+ value=COPY(source, "kilobyte"),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-utils-megabyte"),
+ value=COPY(source, "megabyte"),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-utils-gigabyte"),
+ value=COPY(source, "gigabyte"),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-utils-transfer-same-units"),
+ value=REPLACE(
+ source,
+ "transferSameUnits2",
+ {
+ "%1$S": VARIABLE_REFERENCE("progress"),
+ "%2$S": VARIABLE_REFERENCE("total"),
+ "%3$S": VARIABLE_REFERENCE("totalUnits"),
+ },
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-utils-transfer-diff-units"),
+ value=REPLACE(
+ source,
+ "transferDiffUnits2",
+ {
+ "%1$S": VARIABLE_REFERENCE("progress"),
+ "%2$S": VARIABLE_REFERENCE("progressUnits"),
+ "%3$S": VARIABLE_REFERENCE("total"),
+ "%4$S": VARIABLE_REFERENCE("totalUnits"),
+ },
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-utils-transfer-no-total"),
+ value=REPLACE(
+ source,
+ "transferNoTotal2",
+ {
+ "%1$S": VARIABLE_REFERENCE("progress"),
+ "%2$S": VARIABLE_REFERENCE("progressUnits"),
+ },
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-utils-time-pair"),
+ value=REPLACE(
+ source,
+ "timePair3",
+ {
+ "%1$S": VARIABLE_REFERENCE("time"),
+ "%2$S": VARIABLE_REFERENCE("unit"),
+ },
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-utils-time-left-single"),
+ value=REPLACE(
+ source, "timeLeftSingle3", {"%1$S": VARIABLE_REFERENCE("time")}
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-utils-time-left-double"),
+ value=REPLACE(
+ source,
+ "timeLeftDouble3",
+ {
+ "%1$S": VARIABLE_REFERENCE("time1"),
+ "%2$S": VARIABLE_REFERENCE("time2"),
+ },
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-utils-time-few-seconds"),
+ value=COPY(source, "timeFewSeconds2"),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-utils-time-unknown"),
+ value=COPY(source, "timeUnknown2"),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-utils-done-scheme"),
+ value=REPLACE(
+ source, "doneScheme2", {"%1$S": VARIABLE_REFERENCE("scheme")}
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-utils-done-file-scheme"),
+ value=COPY(source, "doneFileScheme"),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("download-utils-yesterday"),
+ value=COPY(source, "yesterday"),
+ ),
+ ],
+ )
diff --git a/python/l10n/fluent_migrations/bug_1791178_nsContextMenu_saveHelper.py b/python/l10n/fluent_migrations/bug_1791178_nsContextMenu_saveHelper.py
new file mode 100644
index 0000000000..d12533be9a
--- /dev/null
+++ b/python/l10n/fluent_migrations/bug_1791178_nsContextMenu_saveHelper.py
@@ -0,0 +1,39 @@
+# 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, REPLACE
+
+
+def migrate(ctx):
+ """Bug 1791178 - Convert nsContextMenu saveHelper() localization to Fluent, part {index}."""
+
+ source = "toolkit/chrome/mozapps/downloads/downloads.properties"
+ target = "browser/browser/downloads.ftl"
+ ctx.add_transforms(
+ target,
+ target,
+ [
+ FTL.Message(
+ id=FTL.Identifier("downloads-error-alert-title"),
+ value=COPY(source, "downloadErrorAlertTitle"),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("downloads-error-blocked-by"),
+ value=REPLACE(
+ source,
+ "downloadErrorBlockedBy",
+ {"%1$S": VARIABLE_REFERENCE("extension")},
+ ),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("downloads-error-extension"),
+ value=COPY(source, "downloadErrorExtension"),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("downloads-error-generic"),
+ value=COPY(source, "downloadErrorGeneric"),
+ ),
+ ],
+ )
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_1801035_places_window_no_size.py b/python/l10n/fluent_migrations/bug_1801035_places_window_no_size.py
new file mode 100644
index 0000000000..072d22cdd8
--- /dev/null
+++ b/python/l10n/fluent_migrations/bug_1801035_places_window_no_size.py
@@ -0,0 +1,21 @@
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+from fluent.migrate import COPY_PATTERN
+from fluent.migrate.helpers import transforms_from
+
+
+def migrate(ctx):
+ """Bug 1801035 - Stop localizing places window style, part {index}."""
+
+ ctx.add_transforms(
+ "browser/browser/places.ftl",
+ "browser/browser/places.ftl",
+ transforms_from(
+ """
+places-library3 =
+ .title = {{COPY_PATTERN(from_path, "places-library2.title")}}
+ """,
+ from_path="browser/browser/places.ftl",
+ ),
+ )
diff --git a/python/l10n/fluent_migrations/bug_1801915_confirmationHints.py b/python/l10n/fluent_migrations/bug_1801915_confirmationHints.py
new file mode 100644
index 0000000000..69b9cff67d
--- /dev/null
+++ b/python/l10n/fluent_migrations/bug_1801915_confirmationHints.py
@@ -0,0 +1,29 @@
+# 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 1801915 - Migrate confirmation Hints to Fluent"""
+ ctx.add_transforms(
+ "browser/browser/confirmationHints.ftl",
+ "browser/browser/confirmationHints.ftl",
+ transforms_from(
+ """
+confirmation-hint-breakage-report-sent = { COPY(from_path, "confirmationHint.breakageReport.label") }
+confirmation-hint-login-removed = { COPY(from_path, "confirmationHint.loginRemoved.label") }
+confirmation-hint-page-bookmarked = { COPY(from_path, "confirmationHint.pageBookmarked2.label") }
+confirmation-hint-password-saved = { COPY(from_path, "confirmationHint.passwordSaved.label") }
+confirmation-hint-pin-tab = { COPY(from_path, "confirmationHint.pinTab.label") }
+confirmation-hint-pin-tab-description = { COPY(from_path, "confirmationHint.pinTab.description") }
+confirmation-hint-send-to-device = { COPY(from_path, "confirmationHint.sendToDevice.label") }
+""",
+ from_path="browser/chrome/browser/browser.properties",
+ ),
+ )
diff --git a/python/l10n/fluent_migrations/bug_1802128_langpack_defines.py b/python/l10n/fluent_migrations/bug_1802128_langpack_defines.py
new file mode 100644
index 0000000000..d1755307bd
--- /dev/null
+++ b/python/l10n/fluent_migrations/bug_1802128_langpack_defines.py
@@ -0,0 +1,43 @@
+# 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 COPY, LegacySource, Transform
+
+
+class CONTRIB(LegacySource):
+ """Custom transform converting a list of contributors from the old RDF based
+ list of entries into a comma delimited list.
+
+ Based on code from python/mozbuild/mozbuild/action/langpack_manifest.py
+ """
+
+ def __call__(self, ctx):
+ element: FTL.TextElement = super(CONTRIB, self).__call__(ctx)
+ str: str = element.value.replace("<em:contributor>", "")
+ tokens = str.split("</em:contributor>")
+ tokens = map(lambda t: t.strip(), tokens)
+ tokens = filter(lambda t: t != "", tokens)
+ element.value = ", ".join(tokens)
+ return Transform.pattern_of(element)
+
+
+def migrate(ctx):
+ """Bug 1802128 - Convert langpack defines.inc to Fluent, part {index}."""
+
+ browser = "browser/defines.inc"
+ target = "browser/langpack-metadata.ftl"
+ ctx.add_transforms(
+ target,
+ target,
+ [
+ FTL.Message(
+ id=FTL.Identifier("langpack-creator"),
+ value=COPY(browser, "MOZ_LANGPACK_CREATOR"),
+ ),
+ FTL.Message(
+ id=FTL.Identifier("langpack-contributors"),
+ value=CONTRIB(browser, "MOZ_LANGPACK_CONTRIBUTORS"),
+ ),
+ ],
+ )
diff --git a/python/l10n/fluent_migrations/bug_1803010_downloads.py b/python/l10n/fluent_migrations/bug_1803010_downloads.py
new file mode 100644
index 0000000000..bfea61b564
--- /dev/null
+++ b/python/l10n/fluent_migrations/bug_1803010_downloads.py
@@ -0,0 +1,30 @@
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+from fluent.migrate import COPY_PATTERN
+from fluent.migrate.helpers import transforms_from
+
+
+def migrate(ctx):
+ """Bug 1803010 - Use .value consistently in downloads.ftl, part {index}."""
+
+ for string in [
+ "downloading-file-opens-in-hours-and-minutes",
+ "downloading-file-opens-in-minutes",
+ "downloading-file-opens-in-minutes-and-seconds",
+ "downloading-file-opens-in-seconds",
+ "downloading-file-opens-in-some-time",
+ ]:
+ ctx.add_transforms(
+ "browser/browser/downloads.ftl",
+ "browser/browser/downloads.ftl",
+ transforms_from(
+ """
+{0}-2 =
+ .value = {{COPY_PATTERN(from_path, "{0}")}}
+ """.format(
+ string
+ ),
+ from_path="browser/browser/downloads.ftl",
+ ),
+ )
diff --git a/python/l10n/fluent_migrations/bug_1805319_webrtc_indicator_typo.py b/python/l10n/fluent_migrations/bug_1805319_webrtc_indicator_typo.py
new file mode 100644
index 0000000000..e37ad5da68
--- /dev/null
+++ b/python/l10n/fluent_migrations/bug_1805319_webrtc_indicator_typo.py
@@ -0,0 +1,23 @@
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+from fluent.migrate import COPY_PATTERN
+import fluent.syntax.ast as FTL
+
+
+def migrate(ctx):
+ """Bug 1805319 - Fix typo in webrtc indicator message id, part {index}."""
+
+ path = "browser/browser/webrtcIndicator.ftl"
+ ctx.add_transforms(
+ path,
+ path,
+ [
+ FTL.Message(
+ id=FTL.Identifier("webrtc-allow-share-microphone-unsafe-delegation"),
+ value=COPY_PATTERN(
+ path, "webrtc-allow-share-microphone-unsafe-delegations"
+ ),
+ ),
+ ],
+ )
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..683a7b0640
--- /dev/null
+++ b/python/l10n/mozxchannel/source.py
@@ -0,0 +1,84 @@
+# 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 pytoml as 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)
+ parse_ctx.data = toml.loads(data, filename=parse_ctx.path)
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