ADR 0023: Drop gettext Flags and Merge Comments
Accepted architecture decision record: the catalog model drops the gettext flag concept (fuzzy and *-format) and collapses the two comment kinds into one notes field, while keeping obsolete entries.
- Status: Accepted (2.0.0)
- Date: 2026-06-30
Context
gettext entries carry several pieces of per-entry metadata that Ferrocat
preserved unchanged: the fuzzy flag, format flags (c-format,
python-format, …), two distinct comment kinds (#. extracted and #
translator), and the #~ obsolete marker. Reviewing what the catalog layer
actually reasons about surfaced that most of this is gettext ceremony rather
than information Ferrocat acts on:
fuzzyencodes a guess. gettext'smsgmergesets it when it fuzzy-matches a changed source onto an old translation, expecting a human to verify. Ferrocat deliberately does not fuzzy-match (ADR 0017), precisely because a guessed translation can be semantically wrong; any change must go back to a translator regardless. Carrying afuzzystatus that Ferrocat never produces and that contradicts its model is dead weight.- Format flags target printf, not ICU.
c-formatand friends letmsgfmt --checkvalidate printf specifiers (%s,%d). Ferrocat is ICU-native ({name},{count, plural, …}) and validates ICU placeholders itself, so the printf-oriented flags carry no meaning in an ICU catalog. - The two comment kinds are a provenance split with no consumer. Whether a
note was extracted by the developer (
#.) or written by a translator (#) does not change how Ferrocat treats it: free-form prose, decoupled from identity and from the integritylock. obsoleteis genuinely useful. Temporarily un-mounting a feature should not throw away finished translations; the obsolete marker is the right "kept but inactive" mechanism (git history is impractical for reviving a single entry).
Decision
Trim the catalog model to what Ferrocat understands:
- Drop the
flagsconcept entirely.fuzzyand all*-formatflags are removed from the catalog/FCL layer: theCatalogMessageStatus::Fuzzyvariant, theCatalogAuditChecks::fuzzy_flagscheck and itscatalog.fuzzy_flagdiagnostic, and the coveragefuzzycounter all go away. A PO entry marked#, fuzzyimports as an ordinary translation (the flag is dropped). - Merge the two comment kinds into one.
CatalogMessagekeeps a singlecommentsnotes list;CatalogMessageExtra(which heldtranslator_commentsandflags) is removed. On import,#.and#comments collapse intocomments; on PO export they render as#.. - Keep
obsolete. No change to the obsolete marker,ObsoleteStrategy, or the FCLotag.
Faithful low-level PO is unchanged
This is a catalog-layer decision. The low-level parse_po / stringify_po
round-trip still preserves PoItem::flags and both comment kinds verbatim, so
raw PO I/O stays lossless. Only the opinionated catalog/FCL layer stops elevating
flags and stops distinguishing comment kinds.
Consequences
Positive:
- the per-entry model shrinks to identity, translation, notes, origin, obsolete, and machine metadata — every field is something Ferrocat reasons about
- FCL drops the
tc=andf=tags, leaving the canonical orderr, c, o, lock, ai - omitting
fuzzyand format flags still produces standard-compliant PO; nothing incompatible is invented, only optional details are left out
Negative:
- this is a breaking change to the catalog API and serialization
(
CatalogMessageExtra,CatalogMessageStatus::Fuzzy,CatalogAuditChecks::fuzzy_flags, the coveragefuzzycounter, and the FCLtc=/f=tags are removed); it lands in the 2.0.0 line - a PO round-trip through the catalog layer rewrites
#comments as#.and dropsfuzzy/format flags; callers needing byte-faithful PO must use the low-level API
This continues the same trim as ADR 0021 and ADR 0022: the catalog layer carries only stable, churn-free information Ferrocat understands.