MessageCatalogue.debug drops UserMessage args, unlike the unresolved-key fallback #51

Open
opened 2026-09-07 09:40:04 +00:00 by mph · 0 comments
Owner

Problem

MessageCatalogue.debug drops a UserMessage's arguments, so a value passed only as a message
argument is invisible in any test that runs on the debug catalogue.

core/shared/src/main/scala/works/iterative/core/MessageCatalogue.scala:82-86:

val debug: MessageCatalogue = new MessageCatalogue:
    override val language: Language = Language.EN
    def get(id: MessageId): Option[String] = Some(id.value)
    def get(msg: UserMessage): Option[String] = Some(msg.id.value)   // args dropped
end debug

Why this is an omission, not a design

The missing-key fallback in the same trait already renders the arguments:

  • MessageCatalogue.apply ends in .getOrElse(id.toString())
  • UserMessage.toString is s"${id}[${args.mkString(", ")}]" (UserMessage.scala:10-11)

So an unresolved key renders plan.kola.lhuta[30], while debug renders plan.kola.lhuta for
the same message. The debug catalogue is strictly less informative than the fallback path, for
no stated reason. Both exist to show the reader a key rather than prose; only one of them keeps the
data.

Consequence for callers

A view spec running on MessageCatalogue.debug cannot assert anything about a value that reaches
the page only as a message argument. Callers work around it by rendering the value as a separate
sibling node and giving the label key no %s at all — which then constrains the i18n bundle
(the label and its value can no longer be reordered by translation) purely because of a testing
artifact.

Observed in medeca-modul-poptavky (MEDECA-478): three date-carrying keys
(plan.kola.zahajeno, .ukonceno, .obnoveni) had to be written without %s so that a spec
could assert the ordering of dates across three cards.

Proposed fix

def get(msg: UserMessage): Option[String] =
    Some(if msg.args.isEmpty then msg.id.value else msg.toString())

This makes debug consistent with the unresolved-key fallback and removes the caller-side
workaround entirely.

Compatibility note

Specs that assert a debug string by exact equality on a key that carries arguments would change
("a.b" becomes "a.b[30]"). contains-style assertions are unaffected, since the key remains a
prefix. Worth a scan of downstream projects before merging.

Provenance: MEDECA-478 lessons triage, L34.

## Problem `MessageCatalogue.debug` drops a `UserMessage`'s arguments, so a value passed only as a message argument is invisible in any test that runs on the debug catalogue. `core/shared/src/main/scala/works/iterative/core/MessageCatalogue.scala:82-86`: ```scala val debug: MessageCatalogue = new MessageCatalogue: override val language: Language = Language.EN def get(id: MessageId): Option[String] = Some(id.value) def get(msg: UserMessage): Option[String] = Some(msg.id.value) // args dropped end debug ``` ## Why this is an omission, not a design The **missing-key fallback** in the same trait already renders the arguments: - `MessageCatalogue.apply` ends in `.getOrElse(id.toString())` - `UserMessage.toString` is `s"${id}[${args.mkString(", ")}]"` (`UserMessage.scala:10-11`) So an *unresolved* key renders `plan.kola.lhuta[30]`, while `debug` renders `plan.kola.lhuta` for the same message. The debug catalogue is strictly **less** informative than the fallback path, for no stated reason. Both exist to show the reader a key rather than prose; only one of them keeps the data. ## Consequence for callers A view spec running on `MessageCatalogue.debug` cannot assert anything about a value that reaches the page only as a message argument. Callers work around it by rendering the value as a separate sibling node and giving the label key no `%s` at all — which then constrains the i18n bundle (the label and its value can no longer be reordered by translation) purely because of a testing artifact. Observed in medeca-modul-poptavky (MEDECA-478): three date-carrying keys (`plan.kola.zahajeno`, `.ukonceno`, `.obnoveni`) had to be written without `%s` so that a spec could assert the ordering of dates across three cards. ## Proposed fix ```scala def get(msg: UserMessage): Option[String] = Some(if msg.args.isEmpty then msg.id.value else msg.toString()) ``` This makes `debug` consistent with the unresolved-key fallback and removes the caller-side workaround entirely. ## Compatibility note Specs that assert a debug string by **exact equality** on a key that carries arguments would change (`"a.b"` becomes `"a.b[30]"`). `contains`-style assertions are unaffected, since the key remains a prefix. Worth a scan of downstream projects before merging. Provenance: MEDECA-478 lessons triage, L34.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
iterative-works/support#51
No description provided.