Skip to content

Catalog Modes

Mental model for Ferrocat's three supported combinations of storage format and message semantics.

Ferrocat is one catalog engine with a few explicit ways to store and interpret messages. You can start with the format that fits your team today and still keep a path toward richer messages, cleaner diffs, AI translation metadata, release audits, and runtime artifacts.

The two choices Ferrocat keeps visible are simple:

  • Storage: where catalog records live and how they are reviewed.
  • Message behavior: whether a message is plain text, classic plural data, or a richer message with placeholders, formatting, plurals, selects, and tags.

Keeping those choices explicit makes migrations safer, runtime compilation clearer, and translator-tool interoperability easier to reason about.

The three supported modes

ModeStorage formatMessage modelUse when you want to...
Classic Gettext catalog modeGettext POGettext-compatible pluralsstay close to existing PO catalogs and traditional plural workflows
ICU-native Gettext PO modeGettext POICU MessageFormatkeep translator-facing PO files, comments, and tooling, but author richer messages with placeholders, plural/select logic, and formatting
ICU-native FCL catalog modeFCL catalog storageICU MessageFormatstore catalogs as one-entry-per-line, tab-separated records that are easier to diff, merge, batch, and hand to external services

There is intentionally no FCL + gettext-plural mode; gettext plural behavior belongs to PO-shaped catalogs, while FCL is the ICU-native machine storage path for catalog records.

Why FCL matters for larger teams

The painful failure with PO is not a crash. It is a finished translation that quietly disappears. A PO entry spans several lines with repeated anchor lines (msgstr "", blank separators), so when two branches touch the same file, git's line merge mis-anchors and can drop an entry that nobody even edited. Someone retranslates work that was already done, and no error ever fires.

FCL removes that by construction. Every entry is one tab-separated line, sorted by (id, ctxt) behind a single %FCL1 header. Independent edits land on distant lines and merge cleanly; an entry neither side touched stays byte-identical in all three versions, so ordinary git 3-way merges preserve it. No custom merge driver required, so it behaves the same in a hosted pull request or a web merge as it does locally.

Here is the part that usually involves a trade-off, except it doesn't: you do not pay for that safety with speed or size. Against the same catalog stored as PO, FCL parses about 45% faster and the file is roughly 12% smaller. Use it as the machine-owned format: generated, reviewed in diffs, handed to pipelines and external services, never hand-edited. Generate it through CatalogMode::IcuFcl; PO remains the right choice when translator tools need to read the file directly.

Both PO and FCL can carry machine-translation metadata for AI-assisted workflows. Ferrocat keeps the storage compact, verifies the translation hash when writing, and drops stale metadata once the translated text has been edited.

That application-centered path is where Palamedes becomes important: Palamedes can own extraction, framework adapters, and runtime packaging while Ferrocat owns the catalog semantics underneath.

Why the split matters

  • Gettext PO and FCL are storage decisions.
  • Gettext-compatible plurals and ICU MessageFormat are semantics decisions.
  • Explicit combinations make migrations, validation, and runtime compilation easier to review in code.
  • The FCL storage choice is also a collaboration choice: it trades some PO-specific fidelity for cleaner line-level diffs and simpler merge conflict handling.