```{eval-rst} .. role:: bash(code) :language: bash ``` ```{eval-rst} .. role:: js(code) :language: javascript ``` ```{eval-rst} .. role:: python(code) :language: python ``` # Fluent to Fluent Migrations When migrating existing Fluent messages, it's possible to copy a source directly with {python}`COPY_PATTERN`, or to apply string replacements and other changes by extending the {python}`TransformPattern` visitor class. These transforms work with individual Fluent patterns, i.e. the body of a Fluent message or one of its attributes. ## Copying Fluent Patterns Consider for example a patch modifying an existing message to move the original value to a {js}`alt` attribute. Original message: ```fluent about-logins-icon = Warning icon .title = Breached website ``` New message: ```fluent about-logins-breach-icon = .alt = Warning icon .title = Breached website ``` This type of changes requires a new message identifier, which in turn causes existing translations to be lost. It’s possible to migrate the existing translated content with: ```python from fluent.migrate import COPY_PATTERN ctx.add_transforms( "browser/browser/aboutLogins.ftl", "browser/browser/aboutLogins.ftl", transforms_from( """ about-logins-breach-icon = .alt = {COPY_PATTERN(from_path, "about-logins-icon")} .title = {COPY_PATTERN(from_path, "about-logins-icon.title")} """,from_path="browser/browser/aboutLogins.ftl"), ) ``` In this specific case, the destination and source files are the same. The dot notation is used to access attributes: {js}`about-logins-icon.title` matches the {js}`title` attribute of the message with identifier {js}`about-logins-icon`, while {js}`about-logins-icon` alone matches the value of the message. :::{warning} The second argument of {python}`COPY_PATTERN` and {python}`TransformPattern` identifies a pattern, so using the message identifier will not migrate the message as a whole, with all its attributes, only its value. ::: ## Transforming Fluent Patterns To apply changes to Fluent messages, you may extend the {python}`TransformPattern` class to create your transformation. This is a powerful general-purpose tool, of which {python}`COPY_PATTERN` is the simplest extension that applies no transformation to the source. Consider for example a patch copying an existing message to strip out its HTML content to use as an ARIA value. Original message: ```fluent videocontrols-label = { $position } / { $duration } ``` New message: ```fluent videocontrols-scrubber = .aria-valuetext = { $position } / { $duration } ``` A migration may be applied to create this new message with: ```python from fluent.migrate.transforms import TransformPattern import fluent.syntax.ast as FTL class STRIP_SPAN(TransformPattern): def visit_TextElement(self, node): node.value = re.sub("]*>", "", node.value) return node def migrate(ctx): path = "toolkit/toolkit/global/videocontrols.ftl" ctx.add_transforms( path, path, [ FTL.Message( id=FTL.Identifier("videocontrols-scrubber"), attributes=[ FTL.Attribute( id=FTL.Identifier("aria-valuetext"), value=STRIP_SPAN(path, "videocontrols-label"), ), ], ), ], ) ``` Note that a custom extension such as {python}`STRIP_SPAN` is not supported by the {python}`transforms_from` utility, so the list of transforms needs to be defined explicitly. Internally, {python}`TransformPattern` extends the [fluent.syntax](https://projectfluent.org/python-fluent/fluent.syntax/stable/) {python}`Transformer`, which defines the {python}`FTL` AST used here. As a specific convenience, pattern element visitors such as {python}`visit_TextElement` are allowed to return a {python}`FTL.Pattern` to replace themselves with more than one node.