fluent.syntax
The Python parser, serializer, and AST toolkit for reading, writing, and transforming Fluent localization files.
Repository Health
Technical Analysis
fluent.syntax is the foundational parsing layer of the Project Fluent Python ecosystem. It turns Fluent (.ftl) source text into a fully-typed abstract syntax tree, serializes that tree back to spec-compliant Fluent source, and exposes Visitor/Transformer classes for walking and rewriting the tree — the same ergonomics Python developers already know from the standard library’s ast module.
It underpins fluent.runtime (the localization library that actually resolves translated strings at runtime) and any tooling — linters, formatters, migration scripts, editor plugins — that needs to read or manipulate .ftl files programmatically rather than treat them as opaque text.
What You Get
- A recursive-descent
FluentParserthat turns Fluent source text into a typedast.Resourcetree - A
FluentSerializerthat converts an AST back into spec-compliant Fluent source text VisitorandTransformerbase classes for read-only traversal or in-place rewriting of the AST, modeled on Python’s ownast.NodeVisitor/ast.NodeTransformer- Full AST coverage of the Fluent grammar: messages, terms, attributes, patterns, placeables, select expressions, comments, and junk (malformed) entries
- Optional source
Spantracking on every node for building tools that need accurate source positions to_json/from_jsonhelpers for serializing the AST itself to and from JSON- Full type hints and a
py.typedmarker for static-analysis-friendly integration
Common Use Cases
- Powering
fluent.runtime’s message resolution by parsing.ftltranslation files into an AST it can evaluate - Building linters or validators that catch malformed Fluent syntax before it ships
- Writing migration or refactoring scripts that programmatically rewrite
.ftlfiles (renaming messages, restructuring attributes) viaTransformer - Building editor tooling (syntax highlighting, autocomplete, go-to-definition) that needs accurate source spans for Fluent files
- Round-tripping Fluent files through automated formatters that reparse and reserialize source to a canonical style
Under The Hood
Architecture
The package is organized as a tight, layered pipeline with a small surface area: stream.py provides a character-level ParserStream/FluentParserStream cursor, parser.py’s FluentParser consumes that stream via a recursive-descent grammar (decorated with a with_span wrapper that attaches source positions to every node), ast.py defines the resulting BaseNode-derived tree (with clone, equals, to_json/from_json built in), and serializer.py’s FluentSerializer walks that same tree back into source text. visitor.py adds an orthogonal Visitor/Transformer layer that traverses any AST via vars() introspection rather than hardcoded per-node logic, so it stays correct as the grammar evolves. The __init__.py facade exposes just parse()/serialize() plus the AST module for typical callers, while fluent.runtime and other consumers reach into the lower layers directly. Because ast.BaseNode’s field introspection is load-bearing for cloning, equality, JSON (de)serialization, and traversal alike, it’s the one abstraction the rest of the package is built around.
Tech Stack
Pure Python 3.9+ with zero runtime dependencies — only the standard library (re, json, typing, sys). It ships a py.typed marker, meaning it’s fully type-hinted and safe for downstream static analysis. The build backend is setuptools via a standard pyproject.toml, and the project (part of the python-fluent monorepo alongside fluent.runtime and fluent.pygments) uses uv for dependency and environment management in CI.
Code Quality
Testing is extensive and fixture-driven: tests/syntax/ pairs .ftl source fixtures with expected .json AST output across dedicated suites for structure, references, literals, entries, serialization, equality, AST JSON round-tripping, and visitor behavior, including deliberately malformed input to exercise junk-node recovery. CI runs flake8 and mypy for linting and type checking, then runs the full pytest suite across a matrix of three operating systems and six Python versions (3.9–3.14), plus a separate compatibility job that installs older published fluent.syntax releases against the current fluent.runtime to catch cross-version breakage. Naming is consistent and idiomatic (snake_case functions, PascalCase AST classes), and parse failures are represented explicitly via a typed ParseError rather than swallowed.
What Makes It Unique
Rather than a generic parser-combinator toolkit, this is a purpose-built implementation of Fluent’s specific grammar — including its distinctive select-expression/plural-variant branching, the message/term distinction, and attribute placeables — with per-node source spans available throughout, which is what makes source-accurate tooling (editors, linters, formatters) practical to build on top of it. Its Visitor/Transformer pair deliberately mirrors the ergonomics of Python’s own ast.NodeVisitor/ast.NodeTransformer, so anyone who has written a Python AST transform already knows how to extend Fluent files programmatically.