Skip to main content

Crate fluent_typed_codegen

Crate fluent_typed_codegen 

Source
Expand description

Generate typed Rust translation APIs from modular Fluent catalogs.

This reference documents the 0.2.0 runtime-loading API. See the setup guide for installation, and the migration guide when upgrading from 0.1.4.

Language directories are discovered automatically. FTL file paths become named Rust types, and message parameters become typed accessor arguments.

Built on fluent-typed, which generates the typed message accessors and provides Fluent formatting. This crate adds module discovery and a typed translation tree around that foundation. Applications choose their own resource loading, active language and presentation layer.

§Feature selection

  • build (default): discovery, schema validation, source generation and the Extension API for custom integrations. Enable in build-dependencies.
  • manifest: optional TOML parsing and file-manifest factory.
  • No default features: translations! and std-only source contracts, with no dependencies. Use this configuration in normal dependencies. The generated code still requires the application’s fluent-typed and fluent-syntax dependencies.

Cargo resolver 2/3 keeps the build and normal feature contexts separate. See the dependency setup for the build and runtime dependency declarations.

§Configure the consuming package

[package.metadata.localization]
asset-root = "assets"
catalog = "localizations/localization.toml"

The asset root is relative to the Cargo package. The configuration path is relative to that root. Its filename is configurable; the TOML contains:

translations-directory = "translations"
source-language = "en"
default-language = "en"

translations-directory is optional and relative to this TOML. Omit it to use locale folders beside the file (default .). The legacy languages-directory alias is accepted, but specifying both names is an error.

With the explicit path above, put matching FTL modules under assets/localizations/translations/en/, es/ and any other locale directories. The source language defines message keys, references and argument annotations. default-language is emitted as DEFAULT_LANGUAGE metadata for application startup; Locale::default() identifies the source language. Each language must provide the same contract.

§Generate and use the API

Return fluent_typed_codegen::build() from the consuming build.rs to run validation and generation. Errors produce readable diagnostics and a failing exit code. cargo check also regenerates for IDE indexing; no application execution is needed. Source files are never rewritten.

For a file ui/menu.ftl containing title = Settings, the generated type is texts::ui::Menu. A directory becomes a namespace/group, a file becomes a leaf type, and each Fluent message has a typed accessor:

ⓘ
fluent_typed_codegen::translations!(pub mod texts);

let manifest = texts::embed_manifest!();
let translations = texts::Translations::from_manifest(texts::Locale::En, &manifest)?;
let ui: &texts::Ui = translations.ui();
let menu: &texts::ui::Menu = ui.menu();
println!("{}", menu.msg_title());

This snippet needs consumer-owned FTL and build output; the runnable Rust example demonstrates the complete setup. There is no public module at a leaf path. Message keys in different files stay independent, including their argument types.

§Output and checked loading

Cargo entrypoints emit Rust, per-module FTL and source metadata into OUT_DIR. Keep that output under target/ and out of source control. generate also accepts an explicit tool-owned output directory for custom build frontends. Failed generation can leave partial output; always propagate build errors.

Generated leaves expose checked new(locale, bytes), safe new_unchecked, and standalone validate. Translations::from_modules validates and parses caller-supplied FTL strings before returning a snapshot. Compatible prose edits can be loaded without rebuilding; changes to the schema or language/module inventory require regeneration. These runtime methods perform no filesystem reads. File loading and replacing snapshots are caller-controlled, not a filesystem watcher. Sources can come from decoded archive entries or any application-owned buffers; module keys remain paths below the locale directory, not storage URLs. The returned snapshot retains no input borrows. Read a coherent revision before parsing and replace application state only after validation succeeds. Archive I/O and pack installation remain application choices; there is no implicit fallback. Generated from_manifest constructors read files through a LocalizationManifest contract, while texts::embed_manifest!() explicitly includes a build-prepared raw source set. Without that macro invocation, generated APIs contain no FTL payload. The embedded macro is crate-local; module = texts::ui::Menu selects one leaf, and module = texts::Ui includes the group’s descendants, in every language. Use a generated type path or a use alias, not a type alias or generic parameter. Unselected FTL is not included even in unoptimized builds without LTO or stripping. See the complete loading recipes for bytes, files, embedding, all languages and explicit module lifetimes.

§Numbers, plural selection and presentation

Native Fluent numeric selectors remain available; this generator does not replace upstream argument types or select-expression behavior. For numbers, percentages, currencies and dates, it’s recommended to use dedicated ICU or ICU4X formatters. Formatting policies and component stability belong to the application. For decimal text, use DecimalFormatter. If grammar must follow visible precision, pass the same prepared Decimal to PluralRules. Annotate the displayed text and category keyword as (String) in source FTL. The runnable example uses ICU directly; no special generated numeric type, generator dependency or feature is required. Respect ICU’s operand limits for arbitrary-precision input. A compact ICU walkthrough shows reusable formatters and a numeric input. Catalogs and formatters can be kept in application state; the application decides when to recompute strings and how to use the result.

String categories match literal Fluent keys such as [one]; unmatched strings select the starred default. Numeric exact matches such as [1] still require a numeric selector. Keep number formatting aligned with the snapshot’s locale. Number formatting and Fluent interpolation isolation do not implement RTL layout, glyph shaping or fonts; those belong to the application’s renderer.

§Custom integrations

With build, Extension adds an entrypoint decorated with typed Rust syntax, while preserving the plain generated API. It can add attributes, imports or application-specific registration code. See the extension guide for its syntax hooks and consumer-compilation requirements. Repository links follow main; this API reference describes the viewed version.

Re-exports§

pub use syn;

Macros§

translations
Declare a module containing this package’s generated Fluent translations.

Structs§

CatalogConfig
Contents of localization.toml, separate from Cargo’s asset-path settings.
LocalizationManifest
A source contract for readable FTL; creating one never parses FTL.
Scope
A generated type and its path of accessor calls from the root snapshot.
Settings
Portable paths locating a consuming package’s assets and catalog configuration.

Enums§

ManifestError
A manifest or requested module could not be obtained.

Traits§

Extension
Syntax-level extension for a consuming framework’s generated entrypoint.

Functions§

build
Generate catalogs from a Cargo build script with readable failure output.
build_with
As build, with an opt-in framework source extension.
from_cargo
Generate using the consuming package’s Cargo.toml metadata and Cargo OUT_DIR.
from_cargo_with
As from_cargo, also emitting the extension’s entrypoint.
generate
Generate from explicit paths, for custom build frontends and tests.
generate_with
As generate, with an additional framework entrypoint.