Project Fluent: Localization and Pluralization in Firefox
This article explores how Mozilla’s Project Fluent revolutionizes software localization in the Firefox Web Browser. It examines the limitations of traditional translation systems and details how Fluent utilizes asymmetric localization, selectors, and isolated grammar logic to handle complex natural language rules, including intricate pluralization and gender agreement, across dozens of global languages.
Asymmetric Localization
Traditional localization systems rely on a one-to-one mapping between the source language (usually English) and the target language. This forces developers to hardcode English grammar rules into the software, breaking translations for languages with more complex structures.
Project Fluent solves this in Firefox through "asymmetric localization." The complexity of one language does not restrict or complicate another. A developer can write a simple string in English, and a localizer can expand that single string into multiple complex variants in their native language without needing the developer to change the underlying Firefox source code.
Solving Complex Pluralization with Selectors
Pluralization is one of the hardest challenges in localization. While English only has two plural categories (singular and plural), other languages have many more. For example, Polish and Russian have four categories, and Arabic has six.
Fluent handles this natively by integrating Unicode CLDR (Common Locale Data Repository) plural rules and utilizing a syntax feature called "selectors." Selectors work like switch statements in programming.
In Firefox, an English .ftl (Fluent Translation List)
file might handle closing tabs like this:
tabs-close-warning = { $tabCount ->
[one] You are about to close { $tabCount } tab.
*[other] You are about to close { $tabCount } tabs.
}
For the exact same interface element, the Polish localizer can add the necessary grammatical categories without altering Firefox’s code:
tabs-close-warning = { $tabCount ->
[one] Zamkniesz { $tabCount } kartę.
[few] Zamkniesz { $tabCount } karty.
[many] Zamkniesz { $tabCount } kart.
*[other] Zamkniesz { $tabCount } karty.
}
The asterisk (*) denotes the default fallback value if
no other conditions are met. You can even match exact numbers (like
[0] or [1]) to create more natural-sounding
phrases, such as "You are about to close no tabs" instead of "0
tabs."
Terms, Attributes, and Grammatical Cases
Natural language complexity extends beyond plurals to include grammatical gender, animacy, and case declensions. In many languages, verbs and adjectives must change depending on the gender of the noun they reference, or a brand name must change its ending based on its role in a sentence.
Fluent addresses this using "Terms" (denoted with a hyphen, like
-brand-name) and custom attributes. Localizers can attach
metadata to a term and use that metadata in selectors.
-brand-name = Firefox
.gender = masculine
update-successful = { -brand-name.gender ->
[masculine] { -brand-name } a été mis à jour.
[feminine] { -brand-name } a été mise à jour.
*[other] { -brand-name } a été mis(e) à jour.
}
If Mozilla ever rebrands a product or uses a different term (like
"Firefox Nightly" vs. "Aurora"), the localizer only needs to change the
.gender attribute in one place. Every sentence referencing
that term in the browser will automatically adjust its grammar to
match.
Decoupling Code from Grammar
In the Firefox codebase, Project Fluent creates a strict separation of concerns between engineers and translators.
To display a localized message, a Firefox engineer only needs to
reference the message ID (e.g., tabs-close-warning) and
pass the raw data variables (e.g., { tabCount: 5 }). The
Fluent engine inside Firefox evaluates the variables, applies the target
language's specific logic from the .ftl file, and outputs
the correct string to the browser's user interface. This ensures that
Firefox reads naturally in every supported language while keeping the
browser's source code clean and maintainable.