# 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 ``` **The (simplified) XUL code** ```xml ``` **The new HTML** ```html ``` **The new FTL string** ```fluent calendar-view-toggle-day = Day .title = Switch to day view ``` **Renders as:** ```html ``` 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 ``` ```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 ```