summaryrefslogtreecommitdiffstats
path: root/comm/docs/l10n/fluent_migrations.md
blob: 13639ca187778a81b5441be5345ffcc2597acafe (plain)
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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# Migrating Strings to Fluent Files

Like [Firefox](https://firefox-source-docs.mozilla.org/l10n/migrations/index.html),
Thunderbird developers are working on migrating strings from legacy formats to
Fluent. The process is very similar to how migrations are done for Firefox. The
differences are detailed below.

## Migration Recipes

When part of Thunderbird’s UI is migrated to Fluent, a migration recipe should
be included in the same patch that adds new strings to .ftl files. Recipies are
stored in [comm-central](https://hg.mozilla.org/comm-central/file/tip/python/l10n/tb_fluent_migrations).
After a patch with migrations landed, it will be run for all locales as part of
the [Thunderbird Cross-Channel string quarantining process](cross_channel.md).

Be sure to read [Migrating Legacy Formats](https://firefox-source-docs.mozilla.org/l10n/migrations/legacy.html)
along with the below example.

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. `bug_1805746_calendar_view.py`
for the below example.

### Example: Migrate Multiple DTD strings to Fluent

Often, strings are migrated as part of ongoing UI work to convert XUL code
to HTML. Multiple DTD strings may convert to a single Fluent string with
attributes. It really depends on the document structure and what the UI changes
are doing.

**Legacy strings are in `comm/calendar/locales/en-US/chrome/calendar/calendar.dtd`**

```dtd
<!ENTITY calendar.day.button.tooltip            "Switch to day view" >
<!ENTITY calendar.day.button.label              "Day" >
```

**The (simplified) XUL code**

```xml
<radio id="calendar-day-view-button"
       label="&calendar.day.button.label;"
       tooltiptext="&calendar.day.button.tooltip;" />
```

**The new HTML**

```html
<button id="calTabDay" data-l10n-id="calendar-view-toggle-day"></button>
```

**The new FTL string**

```fluent
calendar-view-toggle-day = Day
  .title = Switch to day view
```

**Renders as:**

```html
<button id="calTabDay" title="Switch to day view">Day</button>
```

This case migrates two DTD strings, `calendar.day.button.label` and `calendar.day.button.tooltip`
to create a single FTL string, `calendar-view-toggle-day`, with one (`title`)
attribute.

**The migration recipe:**

```python
# Any copyright is dedicated to the Public Domain.
# http://creativecommons.org/publicdomain/zero/1.0/

from fluent.migratetb.helpers import transforms_from
from fluent.migratetb import COPY

def migrate(ctx):
    """Bug 1805746 - Update Calendar View selection part {index}."""
    target = reference = "calendar/calendar/calendar-widgets.ftl"
    source = "calendar/chrome/calendar/calendar.dtd"

    ctx.add_transforms(
        target,
        reference,
        transforms_from(
            """
calendar-view-toggle-day = { COPY(from_path, "calendar.day.button.label") }
    .title = { COPY(from_path, "calendar.day.button.tooltip") }
""",
            from_path=source,
        ),
    )
```

Migrations are Python modules, and implement
a single `migrate(MigrationContext)` function. The `migrate()` function makes
calls into `MigrationContext.add_transforms()`.

The `add_transforms()` function takes three arguments:
- `target_path`: Path to the target l10n file
- `reference_path`: Path to the reference (en-US) file
- A list of Transforms, the `source_path` (legacy translated strings file) is
  set here

```{note}
For Thunderbird migrations, the target and reference path are the same.
```

Transforms are rather dense AST nodes. See
[Transforms](https://firefox-source-docs.mozilla.org/l10n/migrations/overview.html#transforms)
for the exact details.

There are some helper functions that simplify creating the ASTs. The above example uses the
`transforms_from()` helper function. It is equivalent to:

```python
target = reference = "calendar/calendar/calendar-widgets.ftl"
source = "calendar/chrome/calendar/calendar.dtd"

ctx.add_transforms(
    target,
    reference,
    [
        FTL.Message(
            id=FTL.Identifier("calendar-view-toggle-day"),
            value=COPY(source, "calendar.day.button.label"),
            attributes=[
                FTL.Attribute(
                    id=FTL.Identifier("title"),
                    value=COPY(
                        source,
                        "calendar.day.button.tooltip"
                    )
                )
            ]
        )
    ]
)
```

`transforms_from()` allows copying reference FTL strings, and replacing the value
of each message with a `COPY` Transform that copies values from the DTD file at
`from_path`.

There are other Transforms like `COPY`. See
[Migrating Legacy Formats](https://firefox-source-docs.mozilla.org/l10n/migrations/legacy.html)
for usage information.

### Thunderbird migration helpers

The `REPLACE` works with `transforms_from` if provided the extra context that
it needs.

Starting with `aboutDialog.dtd` containing:

```xml
<!ENTITY update.updateButton.label3               "Restart to update &brandShorterName;">
```

```python
from fluent.migratetb.helpers import TERM_REFERENCE, transforms_from

# This can't just be a straight up literal dict (eg: {"a":"b"}) because the
# validator fails... so make it a function call that returns a dict.. it works
about_replacements = dict({
    "&brandShorterName;": TERM_REFERENCE("brand-shorter-name"),
})


def migrate(ctx):
    """Bug 1816532 - Migrate aboutDialog.dtd strings to Fluent, part {index}"""
    target = reference = "mail/messenger/aboutDialog.ftl"
    source = "mail/chrome/messenger/aboutDialog.dtd"

    ctx.add_transforms(
        target,
        reference,
        transforms_from(
          """
update-update-button = { REPLACE(source, "update.updateButton.label3", about_replacements) }
    .accesskey = { COPY(source, "update.updateButton.accesskey") }
""", source=source, about_replacements=about_replacements))
```

The resulting `aboutDialog.ftl` will get:

```ftl
update-update-button = Restart to update { -brand-shorter-name }
    .accesskey = R
```