Skip to main content

FTL-Extract

Description

FTL-Extract is a Python package that extracts Fluent keys from .py files and generates .ftl file with extracted keys.

The ftl CLI is implemented in Rust and ships as a native binary inside the Python wheel.


Installation

Use the package manager pip to install FTL-Extract.

$ pip install FTL-Extract

Or use modern tool like UV to install FTL-Extract.

$ uv add --dev FTL-Extract

Usage

First of all, you should create locales directory in your project.

$ mkdir project_path/locales

Then, you can use the following command to extract keys from your code.

$ ftl extract project_path/code_path project_path/locales

By default, FTL-Extract will create a directory named en and put all keys into _default.ftl file.

You can also keep command defaults in pyproject.toml:

[tool.ftl-extract.extract]
code-path = "project_path/code_path"
locales-path = "project_path/locales"
languages = ["en", "uk"]
i18n-keys-append = ["LF", "LazyProxy"]
ignore-attributes-append = ["core"]
exclude-dirs-append = ["./tests/*"]
ignore-kwargs = ["when"]
comment-junks = true
comment-keys-mode = "comment"
line-endings = "lf"
cache = true

[tool.ftl-extract.stub]
locales-path = "project_path/locales/en"
stub-path = "project_path/code_path/stub.pyi"
export-tree = false

[tool.ftl-extract.check]
locales-path = "project_path/locales"
code-path = "project_path/code_path"
languages = ["uk"]
checks = ["all"]
suggest-from = ["en"]
fail-on = ["error"]
report-path = "reports/ftl-check"
report-format = "json"

Then run commands without repeating the configured paths:

$ ftl extract
$ ftl stub
$ ftl check

By default, ftl searches for pyproject.toml from the current directory upward. Use --config to select a specific file:

$ ftl --config ./pyproject.toml extract

CLI arguments override values from pyproject.toml; built-in defaults are used when neither is provided.

To print a ready-to-edit configuration sample, use:

$ ftl config sample
$ ftl config sample --command extract

In some cases, you may want to extract keys to specific .ftl files. So, there is new keyword argument _path in i18n.get and i18n.<key>.

# Before
i18n.get("key-1", arg1="value1", arg2="value2")

# After
i18n.get("key-1", arg1="value1", arg2="value2", _path="dir/ftl_file.ftl")

# Also
i18n.key_1(arg1="value1", arg2="value2", _path="dir/ftl_file.ftl")

# Or
i18n.some.key_1(arg1="value1", arg2="value2", _path="dir/ftl_file.ftl")

💁‍♂️ Explanation of the ftl extract command

$ ftl extract project_path/code_path project_path/locales
  • project_path/code_path - path to the project directory where the code is located.
  • project_path/locales - path to the project directory where the .ftl files will be located.

📚 Additional arguments

  • -l or --language - add a new language to the project.
  • -k or --i18n-keys - add additional i18n keys to the extractor.
  • -K or --i18n-keys-append - add additional i18n keys to the extractor and append them to the default list.
  • -p or --i18n-keys-prefix - add a prefix to the i18n keys. For example, self.i18n.<key>().
  • -e or --exclude-dirs - exclude specific directories from the extraction process.
  • -E or --exclude-dirs-append - add more directories to exclude from the extraction process.
  • -i or --ignore-attributes - ignore specific attributes of the i18n.* like i18n.set_locale.
  • -I or --append-ignore-attributes - add more attributes to ignore to the default list.
  • --ignore-kwargs - ignore specific kwargs of the i18n_keys like when=... in aiogram_dialog.I18nFormat(..., when=...).
  • --comment-junks - comments errored translations in the .ftl file.
  • --default-ftl-file - specify the default .ftl file name.
  • --comment-keys-mode - specify the comment keys mode. It will comment keys that are not used in the code or print warnings about them. Available modes: comment, warn.
  • -v or --verbose - print additional information about the process.
  • --dry-run - run the command without making any changes to the files.
  • --cache - cache extracted Python keys between runs and reuse them when source file metadata and extractor options are unchanged. By default, the cache is stored in .ftl-extract-cache/extract-<package-version>-v<schema-version>.bin.
  • --cache-path - custom cache directory or file path. Directory paths store the cache as extract-<package-version>-v<schema-version>.bin. Passing this option enables the cache.
  • --clear-cache - delete the existing extraction cache before running.
  • --allow-parse-errors - continue when a Python file cannot be read or parsed. By default, ftl extract refuses to write any .ftl file when such a file is found, because keys used in that file would otherwise look unused and be commented out. With this flag, the affected files are reported as warnings and skipped. Conflicting key usage (the same key with different _path= values or different kwargs) always aborts the run. Config key: allow-parse-errors = true.

💁‍♂️ Explanation of the ftl stub command

$ ftl stub 'project_path/locales/<locale>' 'project_path/code_path'
  • project_path/locales/<locale> - path to the locales directory where the <locale> directory (e.g. en) contains .ftl files located.
  • project_path/code_path - path to the directory where the stub.pyi will be located.

💁‍♂️ Explanation of the ftl check command

$ ftl check project_path/locales --code-path project_path/code_path -l uk --suggest-from en
  • project_path/locales - path to the locales root directory that contains locale folders like en, uk, etc.

📚 Additional arguments

  • --check - validation to run. Supported checks: all, untranslated, syntax, references, missing, stale, kwargs. If omitted, all checks run.
  • --code-path - path to Python code. Required for --check missing, --check stale, and --check kwargs.
  • -l or --language - check only selected locales. Can be passed multiple times.
  • --suggest-from - locale(s) used to suggest non-placeholder translations for missing items. Can be passed multiple times.
  • --fail-on - minimum diagnostic severity that should return exit code 1. --fail-on error (the default) fails only on errors; --fail-on warn fails on warnings and errors. Pass --fail-on with no value in pyproject.toml (fail-on = []) to always exit 0.
  • --severity - override the severity of a check, for example --severity stale=error. Can be passed multiple times. See Severities for the defaults.
  • --report-path - optional report file path for batch processing reports. If no extension is provided, .txt or .json is appended automatically based on --report-format.
  • --report-format - report file format: terminal or json (default: json).

In default/all mode, ftl check runs syntax validation first. If syntax errors are found, the remaining checks are skipped until the Fluent files are fixed, and the command still returns a normal check report. The process exit code is controlled by fail-on.

The stale check treats a message referenced by another .ftl message as used, even when Python code does not call it directly.

Python files that cannot be read or parsed are reported as extraction errors by the missing, stale, and kwargs checks, since their results cannot be trusted while such files exist. The report names each file with the line and column of the syntax error.

Breaking change: ftl untranslated has been removed. Use ftl check --check untranslated instead.

📁 Keys are matched per file, not just by name

The missing, stale, and kwargs checks compare a key together with the .ftl file it belongs to, relative to the locale directory. A key that exists in the locale but lives in a different file than the code declares is reported twice: as missing in the file the code expects, and as stale in the file where it actually is.

# Code says the key lives in pages/main.ftl
i18n.get("page-title", _path="pages/main.ftl")
# locales/en/_default.ftl  ->  missing in pages/main.ftl, stale in _default.ftl
page-title = Page title

This is intentional: ftl extract owns the file layout and always writes a key to the file named by _path= (or to the default file when _path= is absent). It surprises people who organised their locale files by hand. To fix it, either add the matching _path= argument in code, or run ftl extract, which moves the key to the expected file and comments out the old copy.

⚖️ Severities

Every diagnostic is either an error or a warn. Errors mean the application is broken or will break at runtime; warnings mean the translation catalogue is untidy. The defaults are:

Check Default severity Why
syntax error The .ftl file cannot be loaded at all.
references error A message references a message or term that does not exist.
missing error Code uses a key that the locale does not provide.
kwargs error Code passes different variables than the message expects.
extraction error A Python file could not be analysed, so the other results are
incomplete.
stale warn The locale contains a key that code no longer uses.
untranslated warn A message is still equal to its key.

With the default --fail-on error, a project that only has stale or untranslated keys exits 0 and reports FTL check passed with warnings. Use --fail-on warn to fail on warnings too, or change the severity of individual checks, on the command line:

$ ftl check project_path/locales --code-path project_path/code_path --severity stale=error --severity untranslated=warn

or in pyproject.toml:

[tool.ftl-extract.check]
severity = { stale = "error", untranslated = "warn" }

Command-line overrides take precedence over pyproject.toml. Syntax errors always stop the remaining checks, even when their severity is set to warn, because the broken files cannot be analysed.

Config examples for each check

Run every available check. This is also the default when checks is omitted:

[tool.ftl-extract.check]
locales-path = "app/bot/locales"
code-path = "app/bot"
languages = ["uk", "pl"]
checks = ["all"]
suggest-from = ["en"]
fail-on = ["error"]
report-path = "reports/ftl-check"
report-format = "json"

Check only untranslated placeholders. This check does not need code-path:

[tool.ftl-extract.check]
locales-path = "app/bot/locales"
languages = ["uk", "pl"]
checks = ["untranslated"]
suggest-from = ["en"]
fail-on = ["error"]
report-format = "terminal"

Check only Fluent syntax errors. This check does not need code-path:

[tool.ftl-extract.check]
locales-path = "app/bot/locales"
languages = ["uk", "pl"]
checks = ["syntax"]
fail-on = ["error"]
report-format = "terminal"

Check only missing message and term references inside .ftl files. This check does not need code-path:

[tool.ftl-extract.check]
locales-path = "app/bot/locales"
languages = ["uk", "pl"]
checks = ["references"]
fail-on = ["error"]
report-format = "terminal"

Check keys used in Python but missing from locale files. This check requires code-path:

[tool.ftl-extract.check]
locales-path = "app/bot/locales"
code-path = "app/bot"
languages = ["uk", "pl"]
checks = ["missing"]
suggest-from = ["en"]
fail-on = ["error"]
report-format = "terminal"

Check stale .ftl messages that are not used by Python. This check requires code-path:

[tool.ftl-extract.check]
locales-path = "app/bot/locales"
code-path = "app/bot"
languages = ["uk", "pl"]
checks = ["stale"]
fail-on = ["error"]
report-format = "terminal"

Check Python keyword arguments against Fluent variables. This check requires code-path:

[tool.ftl-extract.check]
locales-path = "app/bot/locales"
code-path = "app/bot"
languages = ["uk", "pl"]
checks = ["kwargs"]
fail-on = ["error"]
report-format = "terminal"

Run a custom subset:

[tool.ftl-extract.check]
locales-path = "app/bot/locales"
code-path = "app/bot"
languages = ["uk", "pl"]
checks = ["syntax", "references", "missing", "kwargs"]
suggest-from = ["en"]
fail-on = ["error"]
report-path = "reports/ftl-check"
report-format = "json"

🙈 Ignore markers

Some keys are intentional exceptions: brand or domain terms that must stay equal to their key, or keys that are built dynamically in Python (f-strings, variables, getattr), which the extractor cannot see and would otherwise report as stale forever. Add a comment marker above such a message to opt it out of specific checks:

# ftl-extract: ignore untranslated
balance = balance

# ftl-extract: ignore stale
dynamic-key = Built from an f-string in Python

# ftl-extract: ignore all
brand = brand

The marker is # ftl-extract: ignore followed by the checks to skip: stale, untranslated, several names separated by commas or spaces, or all. A bare # ftl-extract: ignore means all. A message ignored for stale also keeps the messages and terms it references alive, exactly as if Python code used it.

The older spelling # ftl-extract: ignore-untranslated (and a bare # ignore line) still works as an alias of # ftl-extract: ignore untranslated.

FAQ

❓ - How to add more languages to the project ?

# Here we add 3 languages: English, Ukrainian and Polish
$ ftl extract project_path/code_path project_path/locales -l en -l uk -l pl

❓ - How to detect another i18n keys like LazyProxy or L ?

# Here we extract ftl keys from i18n-keys like `LF`, `LazyProxy` and `L`
$ ftl extract project_path/code_path project_path/locales -K LF -K LazyProxy -K L

How I use FTL-Extract in most of my projects

$ ftl extract \
  'app/bot' \
  'app/bot/locales' \
  -l 'en' \
  -l 'uk' \
  -K 'LF' \
  -I 'core' \
  -E './tests/*' \
  --ignore-kwargs 'when' \
  --comment-junks \
  --comment-keys-mode 'comment' \
  --cache \
  --verbose

Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

Please make sure to update tests as appropriate.

Metadata

Release files for FTL-Extract 0.12.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for FTL-Extract 0.12.0
File Size Uploaded
ftl_extract-0.12.0.tar.gz 96.6 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for FTL-Extract 0.12.0
File
ftl_extract-0.12.0-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
ftl_extract-0.12.0-py3-none-musllinux_1_2_x86_64.whl Python 3 none Linux musl 1.2+ x86-64 Details
ftl_extract-0.12.0-py3-none-musllinux_1_2_aarch64.whl Python 3 none Linux musl 1.2+ ARM64 Details
ftl_extract-0.12.0-py3-none-manylinux_2_28_x86_64.whl Python 3 none Linux glibc 2.28+ x86-64 Details
ftl_extract-0.12.0-py3-none-manylinux_2_28_aarch64.whl Python 3 none Linux glibc 2.28+ ARM64 Details
ftl_extract-0.12.0-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details
ftl_extract-0.12.0-py3-none-macosx_10_15_x86_64.whl Python 3 none macOS 10.15+ x86-64 Details

Total release size: 15.6 MB

Release files / ftl_extract-0.12.0.tar.gz

Download URL ftl_extract-0.12.0.tar.gz
Size 96.6 kB
Tags Source
SHA-256 checksum
How to use checksums
7b5e079557092a188f4dce184515bce470e7207b3e02734b99c18392e865b7d9
BLAKE2b-256 checksum
How to use checksums
b72e4fd766a88fd789519b3b6a9d097c24b6867fd50e9ae2f73e1d7eeaa6b75a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.

Transparency log

Release files / ftl_extract-0.12.0-py3-none-win_amd64.whl

Download URL ftl_extract-0.12.0-py3-none-win_amd64.whl
Size 2.4 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
5a2dabf5ca63b6c98b8219866fc9ddc2ecfc6eb9efc0167724b13f613f64243b
BLAKE2b-256 checksum
How to use checksums
316420ec7e25579023ed3b96bcb218504549b33aac3c00774f8c2296e59a7496
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.

Transparency log

Release files / ftl_extract-0.12.0-py3-none-musllinux_1_2_x86_64.whl

Download URL ftl_extract-0.12.0-py3-none-musllinux_1_2_x86_64.whl
Size 2.3 MB
Tags Linux musl 1.2+ x86-64 Python 3
SHA-256 checksum
How to use checksums
eaeb7d7528b93cce3acec50a1cdb61771409a7769051bd862c335706734e8267
BLAKE2b-256 checksum
How to use checksums
49cfa9b556dc979c490b8711d3dd04ec2fd6bb5840aa46674ec248e8855012e3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.

Transparency log

Release files / ftl_extract-0.12.0-py3-none-musllinux_1_2_aarch64.whl

Download URL ftl_extract-0.12.0-py3-none-musllinux_1_2_aarch64.whl
Size 2.2 MB
Tags Linux musl 1.2+ ARM64 Python 3
SHA-256 checksum
How to use checksums
2083ff578c9fb40716fb722a4a003c3813d1c6f039e5e6819d11570999225aab
BLAKE2b-256 checksum
How to use checksums
d01bfba7e47958846bd22ca29bcecbad650ce62ec8faf249f9b6caa50c9f8d2e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.

Transparency log

Release files / ftl_extract-0.12.0-py3-none-manylinux_2_28_x86_64.whl

Download URL ftl_extract-0.12.0-py3-none-manylinux_2_28_x86_64.whl
Size 2.3 MB
Tags Linux glibc 2.28+ x86-64 Python 3
SHA-256 checksum
How to use checksums
c3411cff4a9d3f118f96f928dc35b590645ecfb47ddcb35fe40da6539c46647e
BLAKE2b-256 checksum
How to use checksums
1e4849f7416d877065cb651707e73c9993db2fd17d20fed649dbd55e68b08458
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.

Transparency log

Release files / ftl_extract-0.12.0-py3-none-manylinux_2_28_aarch64.whl

Download URL ftl_extract-0.12.0-py3-none-manylinux_2_28_aarch64.whl
Size 2.1 MB
Tags Linux glibc 2.28+ ARM64 Python 3
SHA-256 checksum
How to use checksums
232511a2eafbbbae49d49289ab7734028c20063ca72b38757ca93771b59679a4
BLAKE2b-256 checksum
How to use checksums
54d9ed4e29e89d5c63ee0dfafb375b35ea7b1bc04f06b761b4d2550d2066e38a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.

Transparency log

Release files / ftl_extract-0.12.0-py3-none-macosx_11_0_arm64.whl

Download URL ftl_extract-0.12.0-py3-none-macosx_11_0_arm64.whl
Size 2.1 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
52f47c9b30a9a43538b28a8d85a95da54d9299eac0a74790c5cdd655969a25c9
BLAKE2b-256 checksum
How to use checksums
bf62f1920d8cf8013faca1216b4fe9b15d6163620a6c78b425a4f76019250927
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.

Transparency log

Release files / ftl_extract-0.12.0-py3-none-macosx_10_15_x86_64.whl

Download URL ftl_extract-0.12.0-py3-none-macosx_10_15_x86_64.whl
Size 2.2 MB
Tags Python 3 macOS 10.15+ x86-64
SHA-256 checksum
How to use checksums
01fc07ee0823fc0978fc14dee330ce54e654a8fe65a67db4b147f18062d8b65b
BLAKE2b-256 checksum
How to use checksums
8d7130aa37390b338c5956801903aad1aa2eb7515e4d5e3c85ed08038986f4cd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.

Transparency log

Release history Release notifications | RSS feed

0.12.1

8 release files

This release

0.12.0 This release

8 release files

0.11.0

7 release files

0.9.0

7 release files

0.8.0

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.6

2 release files

0.4.5

2 release files

0.4.4

2 release files

0.4.3

2 release files

0.4.2

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page