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 choicesA 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.
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 choicesFind 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 resultTwo source messages. One German catalog. A release check that tells you exactly what needs attention.
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}"
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.
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}"
{
"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
}
}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.
%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.
Machine translation can start the work. A review report helps you see what happened next.
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.
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.
Add the umbrella crate to your application. Start with PO parsing, then reach for catalog updates, review and compilation as you need them.
cargo add ferrocatInstall the CLI, point it at your source and target locales, and let its exit status carry the result into CI.
cargo install ferrocat-cliFerrocat owns catalog semantics in Rust. Palamedes owns JavaScript and TypeScript extraction, macros, framework adapters and bindings. You can use the Rust crate or CLI independently, and keep application-specific choices in your host tool.
How the responsibilities fit together · Meet the Ferramenta family