Mozilla Fluent (.ftl) translator that runs in your browser
Drop a Mozilla Fluent .ftl file in below, pick a target language, download the translated file. The .ftl file stays on your device.
How it works
Drop a .ftl file on the page, click to choose one, or paste the contents straight in. The parser reads the file in your browser and tells you how many messages, attributes, and select-expression variants still need translation. Pick a target language and translation starts immediately. The translated file is reassembled from the same parsed structure so the original comments, identifiers, term definitions, and select-expression scaffolding all round-trip byte-for-byte.
Quality Warning
Please don’t rely on the quality of these translations to ship a finalized product. This is just a starting point. It’s machine translation and there are no guarantees about its quality.
What we do not do
The .ftl file itself never leaves your device. We do not see what you are translating, we do not store any of the strings, and there is nothing about your file in our logs. The privacy wedge in DropFormat is structural: the Fluent parser, the Google Translate caller, and the file writer all run inside your browser tab.
What does leave your device: the individual strings (each message body, each attribute value, each variant of a select expression) go to translate.googleapis.com the same way they would if you pasted them into translate.google.com yourself. We are not in the middle. If your file contains internal product strings you do not want a third party to see, do not translate it here; use an offline tool.
What it supports
Message values, message attributes (.label, .title, .aria-label, etc.), term definitions (-brand-name = DropFormat), term attributes, and select expressions for plural and gender all become rows in the review pane. Each variant of { $count -> [one] one *[other] N } is translated independently and re-assembled into the same select expression on download.
Term references like { -brand-name } and variable references like { $user } stay verbatim in the surrounding source text. The translation step preserves them as-is so your branded names and runtime placeholders are not localized away.
Multi-line patterns with the standard four-space indent are dedented before translation and re-indented on output, so a paragraph value stays a paragraph value in the translated file.
What it does not support yet
Wrapping text around a top-level select expression (for example, You have { $count -> ... } emails today) is not extracted in this version: the variants are translated, but the wrapping text stays in the source language. Re-shape the message so the select expression spans the whole pattern if you need every word translated.
Function calls inside placeables ({ NUMBER($price, currencyDisplay: "symbol") }) are preserved verbatim, but no localization of the function arguments themselves happens. Edit the rendered argument in the review pane if it needs translation.
What we tolerate, and what we reject
Some malformed files we repair on the way in. Others we refuse to load so we do not make things worse. This list is the parser’s actual behaviour, generated from the format module itself, not aspirational copy.
Repaired automatically:
- UTF-8 byte order mark (BOM) at the start of the file
- Windows (CRLF) or mixed line endings
- File truncated mid-message at the tail (we keep complete messages)
Refused with an explanation:
- Empty file. Add at least one message and re-upload.
- No translatable entries (comments only). Add at least one identifier = value message.
- Looks like a different translation format (PO, .strings, XLIFF). Use the /translate/ hub to pick the right translator.
Common questions
Why is the translation in some cases worse than the Google Translate website? The free endpoint we use is the same one the Google Translate website uses, but the API does not always know about the context the website’s interface provides. For names, branded terms, or jargon, expect to override in the review pane.
What if I am translating something Google’s endpoint will not accept (slang, profanity, code-mixed text)? The free endpoint occasionally returns the source unchanged for content it would rather not translate. Those entries show “kept original” in the review pane so you can hand-translate just those.
Does it cache anything? No. Each run starts fresh.
What about PO, XLIFF, ARB, .xcstrings, .properties, JSON, .strings? Each has its own translator at /translate/po/, /translate/xliff/, /translate/arb/, /translate/xcstrings/, /translate/properties/, /translate/json/, and /translate/strings/. Drop your file at the page that matches it.
Is there a paid tier? Not yet, and not for Fluent specifically. If a paid tier ships it is for larger batch counts and larger per-file sizes, not for “more formats.” Every format-pair stays free.
What is Fluent?
Fluent is the localization system Mozilla built to replace gettext, properties, and DTD-based formats across Firefox, Thunderbird, and a growing list of other projects. A .ftl file is plain UTF-8 text whose syntax is line-oriented and indent-significant. Each top-level entry is either a message (greeting = Hello) translators ship to the UI, or a term (-brand-name = DropFormat) the localizer can reference from inside messages so brand strings stay consistent.
The headline Fluent feature is asymmetric translation: a message in English can have a single value while its German counterpart can wrap a select expression around it for grammatical gender, and an Arabic counterpart can use six plural-form variants. Translators do not need to ask the developer to add new strings; they can grow the message as their language needs. This translator preserves those structural features on round-trip and lets you fill in each variant independently.
Fluent files are friendly to diff, code-review, and edit by hand, which makes them the kind of localization file most “cloud translation services” want you to upload. A browser-only translator makes sense for the wedge cases where uploading the file is what you specifically want to avoid.