```{eval-rst} .. role:: bash(code) :language: bash ``` ```{eval-rst} .. role:: js(code) :language: javascript ``` ```{eval-rst} .. role:: python(code) :language: python ``` # Migration Recipes and Their Lifecycle The actual migrations are performed running Python modules called **migration recipes**, which contain directives on how to migrate strings, which files are involved, transformations to apply, etc. These recipes are stored in [mozilla-central](https://hg.mozilla.org/mozilla-central/file/default/python/l10n/fluent_migrations). When part of Firefox’s UI is migrated to Fluent, a migration recipe should be attached to the same patch that adds new strings to `.ftl` files. Migration recipes can quickly become obsolete, because the referenced strings and files are removed from repositories as part of ongoing development. For these reasons, l10n-drivers periodically clean up the `fluent_migrations` folder in mozilla-central, keeping only recipes for 2 shipping versions (Nightly and Beta). :::{hint} As a developer you don’t need to bother about updating migration recipes already in `mozilla-central`: if a new patch removes a string or file that is used in a migration recipe, simply ignore it, since the entire recipe will be removed within a couple of cycles. ::: ## How to Write Migration Recipes The migration recipe’s filename should start with a reference to the associated bug number, and include a brief description of the bug, e.g. {bash}`bug_1451992_preferences_applicationManager.py` is the migration recipe used to migrate the Application Manager window in preferences. It’s also possible to look at existing recipes in [mozilla-central](https://hg.mozilla.org/mozilla-central/file/default/python/l10n/fluent_migrations) for inspiration. ## General Recipe Structure A migration recipe is a Python module, implementing the {py:func}`migrate` function, which takes a {py:class}`MigrationContext` as input. The API provided by the context is ```python class MigrationContext: def add_transforms(self, target, reference, transforms): """Define transforms for target using reference as template. `target` is a path of the destination FTL file relative to the localization directory. `reference` is a path to the template FTL file relative to the reference directory. Each transform is an extended FTL node with `Transform` nodes as some values. For transforms that merely copy legacy messages or Fluent patterns, using `fluent.migrate.helpers.transforms_from` is recommended. """ ``` The skeleton of a migration recipe just implements the {py:func}`migrate` function calling into {py:func}`ctx.add_transforms`, and looks like ```python # coding=utf8 # Any copyright is dedicated to the Public Domain. # http://creativecommons.org/publicdomain/zero/1.0/ from __future__ import absolute_import def migrate(ctx): """Bug 1552333 - Migrate feature to Fluent, part {index}""" target = 'browser/browser/feature.ftl' reference = 'browser/browser/feature.ftl' ctx.add_transforms( target, reference, [], # Actual transforms go here. ) ``` One can call into {py:func}`ctx.add_transforms` multiple times. In particular, one can create migrated content in multiple files as part of a single migration recipe by calling {py:func}`ctx.add_transforms` with different target-reference pairs. The *docstring* for this function will be used as a commit message in VCS, that’s why it’s important to make sure the bug reference is correct, and to keep the `part {index}` section: multiple strings could have multiple authors, and would be migrated in distinct commits (part 1, part 2, etc.). ## Transforms The work of the migrations is done by the transforms that are passed as last argument to {py:func}`ctx.add_transforms`. They're instances of either Fluent {py:class}`fluent.syntax.ast.Message` or {py:class}`Term`, and their content can depend on existing translation sources. The skeleton of a Message looks like ```python FTL.Message( id=FTL.Identifier( name="msg", ), value=FTL.Pattern( elements=[ FTL.TextElement( value="A string", ), ], ), ) ``` When migrating existing legacy translations, you'll replace an `FTL.TextElement` with a `COPY(legacy_path, "old_id")`, or one of its variations we detail {doc}`next `. When migrating existing Fluent translations, an `FTL.Pattern` is replaced with a `COPY_PATTERN(old_path, "old-id")`.