Skip to content

Part of the Ferro family · forged in Rust

Make broken translations a build error, not a support ticket.

Ferrocat is a Rust-native catalog engine that parses and merges translation catalogs several times faster than the Node tooling most JS and TS teams run today, then treats that copy as product data you can review, audit, and compile for production. Catalog problems stay in CI, where they belong.

Hand-tuned SIMD and zero-copy scanning under the hood. The kind of parser you don’t write in an afternoon.

Ferro means iron. Every tool here is forged in Rust.

Ferrocat is one of six focused tools from the same workshop. Shared engineering, shared release discipline, each one replacing a slower or heavier dependency in the JavaScript and Rust toolchain.

Localization that behaves like the rest of your codebase.

Most projects treat translations as files to load, not data to review. Ferrocat moves the catalog into the same discipline as your source: explicit identity, real diffs, checks before release.

  • Translations live in loose JSON files nobody reviews.Source text, context, plurals, obsolete messages, and coverage gaps stay visible.
  • Missing or broken strings surface as production bugs.A structured audit fails the build before the release ships.
  • Translator handoffs depend on ad hoc diff reading.Catalog review reports show what changed and which locales need work.
  • Every framework reimplements catalog logic from scratch.One Rust core that Palamedes and other adapters reuse.

Several times faster than Node. An order of magnitude past PHP and Python.

V8 is not a slow target. Node’s JIT is one of the fastest dynamic runtimes ever shipped, which is exactly why most JS and TS i18n tooling feels fine until you put it next to compiled Rust. On the same 10k-message catalog, reading the same files, Ferrocat still parses several times faster than the quickest Node parser—and the PHP and Python stacks, with no JIT that helps here, fall much further back.

Parsing a catalog · MiB/s · higher is better
FerrocatRust, zero-copy455
pofile-tsNode145
gettext/gettextPHP49
gettext-parserNode20
polibPython20
Updating with new strings · MiB/s · the release-time job
FerrocatRust192
pofile-tsNode40
gettext/gettextPHP16
polibPython7
msgmergeGNU gettext4

None of this comes free with picking Rust. The hot path is written by hand: memchr scanning, NEON SIMD on Apple Silicon, borrowed parsing that never copies the source, and a merge that moves data instead of cloning it. Months of low-level work you inherit the moment you add the crate.

Parsing is mostly raw scanning, and a warm JIT is genuinely good at raw scanning, so the parse gap is the narrowest one on this page. The honest part is admitting that. Updating is the real release-time job: parse the existing catalog, parse the freshly extracted strings, merge by identity, and write it back. Once allocation and serialization dominate, the JIT’s edge fades and the zero-copy, move-not-clone hot path pulls further ahead—which is why the update lead is wider than the parse lead. The GNU msgmerge bar is not a launch-cost artifact either: the benchmark records an empty-run baseline, and its fixed process and I/O overhead is about 2% of the measured time on this corpus, so the gap is real work. The Node baseline, pofile-ts, is our own performance fork of the popular pofile—so the fastest JS parser here is one we built, and Rust still leads it ~3x; the unforked original sits about 220x back. The parse chart uses borrowed, zero-copy parsing; reading into a fully owned model still reaches 362 MiB/s. Serialization runs at about 1.16 GiB/s on the same corpus. Median of 10 runs on an Apple M1 Ultra, every tool reading the same files (pofile-ts 4.0.3, gettext-parser 9.0.2, polib 1.2.0, gettext/gettext 5.7.3, GNU gettext 1.0). Methodology and full report.

Everything around the catalog file.

A parser is only the start. The harder work is what happens around it: handoffs, coverage thresholds, runtime provenance, pseudo-locales, and checks your release process can trust.

One catalog core, not ten formats

Keep source text, context, plurals, notes, source origins, and obsolete entries in a single model your application code can reason about.

Updates without guesswork

Merge freshly extracted messages into existing catalogs by exact identity. No fuzzy matching, no hidden ID changes, no silent conflict resolution.

Release QA with numbers

Audit for missing locales, empty translations, stale targets, ICU errors, metadata conflicts, and obsolete entries. Coverage reports make the gap visible before release day.

Review reports for handoffs

Compare catalog states before a translator handoff. New strings, removed strings, changed translations, and which machine translations a human has edited since: all in one report.

Rich messages that keep their values

Analyze placeholders, formatters, plurals, selects, and tags. Runtime-specific formatter support stays explicit, so unsupported message shapes fail before they ship.

Runtime artifacts you can explain

Compile locale-resolved payloads with stable keys, explicit fallback, missing-message records, and provenance rows for host tools that need to show where a string came from.

Pseudo-locales without broken ICU

Pseudolocalize final ICU messages and compiled artifacts while preserving placeholders, plural selectors, formatter syntax, and rich-text tags.

AI-native metadata that stays honest

Tag any machine-written value (AI model, TMS, or a script) with an integrity lock plus optional model and confidence. Ship it as-is by default; when a human corrects one, the lock stops matching, so your next re-translation run won't silently overwrite their fix.

One catalog, five jobs.

Each job is a Rust API you can call on its own or chain into a release pipeline. Start small, then add the checks that match your risk.

  1. 1

    Parse

    Read PO or FCL into one catalog model. Borrowed parsing keeps the hot path tight on large files.

  2. 2

    Update

    Merge new messages and combine catalogs, preserving existing translations before anything else.

  3. 3

    Review

    Summarize coverage and catalog-state changes before translator handoff or CI thresholds.

  4. 4

    Audit

    Run release checks across source and target locales with diagnostics CI can read.

  5. 5

    Compile

    Emit host-neutral runtime artifacts, provenance reports, and pseudo-locale variants.

Proven formats, one product workflow.

Pick the storage and message model per project. Migrations stay visible in code instead of hiding in tooling.

Translator-friendly PO

Storage
Gettext PO
Semantics
Gettext plurals

The catalog shape translation tools already understand, with comments, references, and plural forms preserved.

Rich-message PO

Storage
Gettext PO
Semantics
ICU MessageFormat

Keep translator-facing PO files while authoring placeholders, formatting, plurals, selects, and structural diagnostics.

Mergeable FCL

Storage
FCL
Semantics
ICU MessageFormat

Ordinary git merges stop losing untouched translations: one canonical, sorted entry per line means only real edits collide. It also parses ~45% faster and stores ~12% smaller than the same catalog as PO.

Use it from Rust, or from JS and TypeScript through Palamedes.

Palamedes is the i18n framework for application teams: macros, message extraction, and adapters for Vite and Next.js. Ferrocat is the catalog engine beneath it, so JS and TS teams get Rust-speed parsing and QA without writing Rust.

PalamedesJS / TS framework

Macros, message extraction, framework adapters, and runtime loading for application developers.

Explore Palamedes
FerrocatRust engine

Parsing, deterministic updates, review reports, release QA, runtime artifacts, and pseudo-locale output. Usable directly from Rust or through the ferrocat-cli audit gate.

How they fit together

Quality is part of the catalog contract.

Ferrocat's behavior is pinned by conformance fixtures derived from upstream gettext, crate-level coverage gates, and benchmark regression checks that run on pull requests.

60conformance casesderived from upstream gettext
454assertionschecked by the harness
95%+library coverage gatefor the main catalog crates

Maintained by Sebastian Software

An independent studio shipping open source for the long run.

Ferrocat and the Ferro family are built and maintained by Sebastian Software. We ship dependable open source for the JavaScript, TypeScript, and Rust ecosystems, and we use every one of these tools in production ourselves.

Put broken translations on the wrong side of your CI.

Start with one catalog and a single audit call. Add coverage, review reports, runtime artifacts, pseudo-locales, and AI metadata when the workflow needs them.