Skip to content
Ferrocat

Keep your translations ready to ship.

A Rust-native translation catalog engine for teams who keep translations in their repository. Read PO and ICU messages, update catalogs, review changes and catch release problems before they reach your users.

Keep the catalog. Improve the workflow.

Bring existing PO files or choose ICU-native FCL for machine-managed catalogs. One Rust core handles parsing, updates, review and runtime compilation.

Compare the storage choices
Version
3.4.2

Turn translation gaps into useful diagnostics.

Find missing translations, stale targets and ICU mistakes before release. Source-attributed conformance fixtures keep the behavior tied to real catalog tools and message formats.

Inspect a real CLI result

Find the missing words before your users do.

Two source messages. One German catalog. A release check that tells you exactly what needs attention.

German catalogde.po
msgid ""
msgstr ""
"Language: de\n"
"Content-Type: text/plain; charset=UTF-8\n"

msgctxt "checkout"
msgid "Pay now"
msgstr ""

msgctxt "greeting"
msgid "Hello {name}"
msgstr "Hallo {name}"
Native CLI result · exit 1

Catalog audit: 2 source messages, 1 target locales, 1 diagnostics (1 errors, 0 warnings, 0 info)

error catalog.empty_translation [de "checkout" / "Pay now"; de]: Locale `de` has an empty translation.

These are saved runs of the real Rust CLI. The audit compares the catalog with an English source containing the same two keys. JSON diagnostics carry the locale, message identity and severity into your CI tooling.

Inspect the source catalog and JSON diagnostic
English sourceen.po
msgid ""
msgstr ""
"Language: en\n"
"Content-Type: text/plain; charset=UTF-8\n"

msgctxt "checkout"
msgid "Pay now"
msgstr "Pay now"

msgctxt "greeting"
msgid "Hello {name}"
msgstr "Hello {name}"
Structured audit output
{
  "diagnostics": [
    {
      "code": "catalog.empty_translation",
      "message": "Locale `de` has an empty translation.",
      "name": "de",
      "severity": "error",
      "source_key": {
        "locale": "de",
        "msgctxt": "checkout",
        "msgid": "Pay now"
      }
    }
  ],
  "summary": {
    "diagnostics": 1,
    "errors": 1,
    "infos": 0,
    "source_messages": 2,
    "target_locales": 1,
    "warnings": 0
  }
}

Translator-friendly files. Git-friendly changes.

PO connects to the translation tools your team already knows. FCL gives machine-managed ICU catalogs a line of their own.

Ferrocat Catalog Lines puts each message on one deterministically ordered line. Independent changes stay separate in a Git diff, and an entry cannot be split across several lines during a merge. When two branches edit the same entry, the conflict stays visible.

FCL · one tab-separated record per messagede.fcl
%FCL1	source=en	locale=de	order=collated
Hello {name}	greeting	Hallo {name}
Pay now	checkout	Jetzt bezahlen

FCL is written by tools and uses ICU MessageFormat semantics. Keep classic PO for Gettext plurals, or ICU-native PO when external translation tools need PO files. You choose the mode explicitly; Ferrocat keeps storage and message semantics together.

A human correction should leave a trace.

Machine translation can start the work. A review report helps you see what happened next.

From a machine-written value to an informed review
  1. Record the value
    An integrity lock fingerprints the machine-written translation. Optional metadata records the model and confidence.
  2. Keep the human edit visible
    If someone improves the translation, its value no longer matches the stored lock.
  3. Choose what happens next
    Your application can use that evidence in its review or retranslation policy. Ferrocat exposes the intervention rather than guessing your policy.

Review reports also distinguish new messages, removed messages and changed translations. Share a catalog-state comparison before a translator handoff, or use it to make an automated update explainable in a pull request.

From a catalog file to something your app can use.

Updates with a clear identity

Merge newly extracted messages by exact identity, preserving existing translations. No fuzzy ID changes or silent conflict resolution.

Rich messages, checked

Inspect ICU placeholders, plurals, selects and tags. Compare source and translation structure before compiling.

Runtime delivery

Compile locale-resolved payloads with stable keys, explicit fallback, missing-message records and provenance for host tools.

Pseudo-locales that keep working

Expand and transform display text while preserving ICU placeholders, selectors, formatters and rich-text tags.

Catalog work should keep up with your build.

The parser borrows from the input where it can, and high-level catalog operations build on the same Rust core. Recorded comparisons cover parsing and updates across Rust, Node.js, PHP, Python and GNU Gettext.

The detailed results keep task differences, source revisions and measurement dates visible. Use the fixture suite to measure the workload that matters to your application.

Build with the Rust core.

Add the umbrella crate to your application. Start with PO parsing, then reach for catalog updates, review and compilation as you need them.

Add the library
cargo add ferrocat
Start with Rust

Make catalog audits a release check.

Install the CLI, point it at your source and target locales, and let its exit status carry the result into CI.

Install the audit CLI
cargo install ferrocat-cli
Set up an audit