Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

CLI Tooling

The es-fluent-cli is the command-line companion for es-fluent. It builds a temporary runner over your workspace library crates, reads the derive inventory emitted by localizable types (see Deriving Messages), and manages the corresponding FTL translation files for you.

Installation

cargo install es-fluent-cli --locked

Configuration

The CLI reads your i18n.toml (see Getting Started) to locate FTL assets. Make sure it exists in your crate root:

# Default fallback language (required)
fallback_language = "en"

# Path to FTL assets relative to the config file (required)
assets_dir = "assets/locales"

# Features to enable if the crate's es-fluent derives are gated behind a feature (optional)
fluent_feature = ["my-feature"]

# Optional allowlist of namespace values for FTL file splitting
namespaces = ["ui", "errors", "messages"]

Common Workspace Options

Every command accepts --path <PATH>/-p <PATH> to choose a crate or workspace root instead of the current directory, and --package <NAME>/-P <NAME> to process one package from a workspace.

Commands

Init

For a new crate, scaffold the standard files first:

cargo es-fluent init

This creates i18n.toml, assets/locales/en/, src/i18n.rs, and a pub mod i18n; declaration in src/lib.rs. Use --manager dioxus or --manager bevy to scaffold those manager imports instead of the embedded manager. Use --build-rs when the crate uses manager macros and should rebuild when locale files are added, removed, or renamed. For Dioxus manifests, --dioxus-runtime client, --dioxus-runtime ssr, or --dioxus-runtime client,ssr selects which manager features are added by --update-cargo-toml; omitting it enables both. When --build-rs and --update-cargo-toml are used together, init also adds es-fluent-build under [build-dependencies].

init creates a library target because CLI inventory collection reads library targets. Put derived message types in src/lib.rs or another library crate; binary-only derived types in src/main.rs are not discovered by generate.

Useful options:

  • --fallback-language <LANG>: choose the fallback locale directory and config value.
  • --locales <LANG>: create additional locale directories, repeatable or comma-separated.
  • --assets-dir <PATH>: choose the locale asset directory relative to the crate root.
  • --namespaces <NAME>: write a namespace allowlist into i18n.toml, repeatable or comma-separated.
  • --dioxus-runtime <client|ssr>: choose Dioxus manager features, repeatable or comma-separated.
  • --update-cargo-toml: add the matching es-fluent, manager, and unic-langid dependencies.
  • --dry-run: preview files and manifest updates without writing them.
  • --force: overwrite generated files that already exist.

Before writing anything, init checks generated-file conflicts, directory targets, and Cargo.toml parseability when manifest updates are requested.

Generate

When you add new #[derive(EsFluent)], #[derive(EsFluentVariants)], or #[derive(EsFluentLabel)] types to your code, run:

cargo es-fluent generate

This will:

  1. Collect derive inventory registrations from workspace library targets.
  2. Update assets_dir/en/{your_crate}.ftl (and assets_dir/en/{your_crate}/{namespace}.ftl for namespaced types).
    • New items: Added as new messages.
    • Changed items: Variables updated (e.g. if you added a field).
    • Existing translations: Preserved untouched.

Use --mode conservative to merge generated keys while preserving manual-only entries and existing translations. This is the default. Use --mode aggressive when you want generated files rebuilt from the current Rust inventory.

Use --dry-run to preview changes without writing them. Use --force-run to bypass the staleness cache and run the generated runner through Cargo.

Literal string namespaces are checked as safe relative namespace paths at compile time. If you configure namespaces = [...] in i18n.toml, string-based namespaces are validated against the allowlist by both the compiler and the CLI during generate and watch.

Watch

Same as generate, but runs in watch mode — updating FTL files as you type:

cargo es-fluent watch

watch accepts the same --mode conservative|aggressive option as generate.

Check

Verify that all your translations are valid and no keys are missing:

cargo es-fluent check

Use --all to check all locales, not just the fallback language. Use --ignore <CRATE> to skip specific crates; it can be repeated or passed as a comma-separated list. Use --force-run to bypass the staleness cache. FTL variables that are not declared by Rust code are reported as errors. Rust-declared variables omitted by a translation are reported as warnings; any reported validation issue makes check exit non-zero for CI enforcement.

Clean

Remove orphan keys and groups that are no longer present in your source code:

cargo es-fluent clean

Use --dry-run to preview changes without writing them. Use --all to clean all locales. Use --force-run to bypass the staleness cache. clean --all only targets canonical locale directories from the configured assets_dir; invalid or non-canonical locale directory names are reported instead of cleaned.

When the main crate file no longer has any non-namespaced registered Rust types, clean deletes that stale main file. When a namespaced file no longer has any registered Rust types, clean also removes that stale namespace file in the locale being cleaned so discovery metadata stays in sync with code.

Clean Orphaned Files

Remove FTL files that are no longer tied to any registered types (e.g., when all items are now namespaced or the crate was deleted):

cargo es-fluent clean --orphaned --all

This compares files in non-fallback locales against the configured fallback locale (en in the executable README example). Files that exist in non-fallback locales but have no corresponding file in the fallback locale are considered orphaned and will be removed. The fallback locale itself is never modified.

Use --dry-run to preview which files would be removed without actually deleting them.

Fmt

Standardize the formatting of your FTL files using fluent-syntax rules:

cargo es-fluent fmt

Use --dry-run to preview changes and print diffs without writing them. Use --all to format all locales.

Sync

Propagate keys from your fallback language to other languages (e.g., from en to fr-FR and zh-CN), creating placeholders for missing translations:

cargo es-fluent sync --all

Use --locale <LANG> to sync a specific locale, or --all to sync all non-fallback locales. Running sync without either option exits non-zero with an actionable message. Use --dry-run to preview changes and print diffs without writing them. Use --create with --locale <LANG> to create missing locale directories before seeding them from the fallback locale.

The sync command properly handles namespaced FTL files, creating matching subdirectories in target locales when syncing from the fallback locale.

Add Locale

Create one or more locale directories and seed them from the fallback locale:

cargo es-fluent add-locale fr-FR zh-CN

This is equivalent to sync --create --locale <LANG> for each requested locale. Use --dry-run to preview the files that would be created.

Tree

Inspect the discovered FTL file layout and message IDs for a crate:

cargo es-fluent tree

Use --all to show all locales instead of just the fallback language. Use --attributes to include message and term attributes, and --variables to list the Fluent variables referenced by each entry.

Doctor

Diagnose setup issues without changing files:

cargo es-fluent doctor

doctor checks discovered i18n crates for common setup problems such as missing library targets, unreadable locale assets, manager dependency mismatches, and missing build-script asset tracking.

Status

Run a read-only workflow summary before committing or in CI:

cargo es-fluent status --all

status reports whether generation would change fallback files, formatting is needed, non-fallback locales need synced keys, orphaned files exist, or validation would fail. It exits non-zero when attention is required.

Structured Output

Machine-readable output is available for commands intended for CI and editor integrations:

cargo es-fluent check --all --output json
cargo es-fluent status --all --output json

--output json is supported by check, fmt, sync, tree, doctor, and status.

CI/CD Integration

GitHub Actions

name: es-fluent
on: [pull_request]

jobs:
  es-fluent:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - name: Check FTL files
        uses: stayhydated/es-fluent/crates/es-fluent-cli@v0.16.0
        with:
          path: .
          all: true

Inputs:

  • version: Optional es-fluent-cli version to install from crates.io. If omitted, the action installs the CLI from the pinned action ref. Use latest to install the newest crates.io release.
  • path: Path to the crate or workspace root (passed as --path). Default: ..
  • package: Package name filter for workspaces (passed as --package). Default: empty.
  • all: Check all locales, not just the fallback language. Default: false.
  • ignore: Crates to skip during validation (comma-separated). Default: empty.
  • force_run: Run the generated runner through Cargo, ignoring the staleness cache. Default: false.
  • toolchain: Rust toolchain to install for the action. Default: stable.

This action always runs cargo es-fluent check. Pin the uses ref to a release tag or commit SHA for reproducible builds. Omit version to run the CLI from that ref, or set version when you intentionally want a crates.io release.

Limitations

The CLI runner links workspace crates as library targets only. If you define #[derive(EsFluent*)] types exclusively in a binary target, they won’t be registered in the inventory, and commands like generate or clean may miss or remove their keys.

Workarounds:

  • Add a lib.rs target and move derived types into it.
  • Move shared localization types into a small library crate and depend on it from your binary.