1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
.. role:: bash(code)
:language: bash
.. role:: js(code)
:language: javascript
.. 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`__ for inspiration.
__ https://hg.mozilla.org/mozilla-central/file/default/python/l10n/fluent_migrations
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
.. code-block:: 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
.. code-block:: 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
.. code-block:: 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 <legacy>`. When migrating existing Fluent
translations, an ``FTL.Pattern`` is replaced with a
``COPY_PATTERN(old_path, "old-id")``.
|