FTL-Extract keeps the Fluent translation files of a Python project in sync with the
code that uses them. It finds every translation key in your .py files and writes the .ftl files for each locale:
new keys are added, keys the code no longer uses are commented out, and existing translations are left alone.
The ftl CLI is written in Rust and ships as a native binary inside the Python wheel.
📖 Documentation: https://ftl-extract.readthedocs.io/
uv add --dev FTL-Extract
# or
pip install FTL-Extract# app/handlers.py
async def start(i18n, name: str) -> str:
return i18n.get("welcome", name=name)
async def menu(i18n) -> str:
return i18n.menu.title()ftl extract app app/locales -l en -l uk# app/locales/en/_default.ftl
menu-title = menu-title
welcome = welcome{ $name }Translate the placeholders, then keep everything in sync:
ftl extract app app/locales -l en -l uk # update .ftl files after code changes
ftl stub app/locales/en app/stub.pyi # generate a .pyi stub for autocompletion
ftl check app/locales --code-path app # validate translations in CIThe options can live in pyproject.toml, so the commands need no arguments:
[tool.ftl-extract.extract]
code-path = "app"
locales-path = "app/locales"
languages = ["en", "uk"]
[tool.ftl-extract.stub]
locales-path = "app/locales/en"
stub-path = "app/stub.pyi"
[tool.ftl-extract.check]
locales-path = "app/locales"
code-path = "app"- Getting started
- Configuration reference
ftl extract,ftl stub,ftl check- CI integration
- FAQ
- Changelog
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. See the contributing guide.