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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
.. role:: bash(code)
:language: bash
.. role:: js(code)
:language: javascript
.. 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:
.. code-block:: fluent
about-logins-icon = Warning icon
.title = Breached website
New message:
.. code-block:: 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:
.. code-block:: 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:
.. code-block:: fluent
videocontrols-label =
{ $position }<span data-l10n-name="duration"> / { $duration }</span>
New message:
.. code-block:: fluent
videocontrols-scrubber =
.aria-valuetext = { $position } / { $duration }
A migration may be applied to create this new message with:
.. code-block:: 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("</?span[^>]*>", "", 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`__
: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.
__ https://projectfluent.org/python-fluent/fluent.syntax/stable/
|