Define parameterized i18n semantics for daw_error/v1 messages #8

Closed
opened 2026-08-14 14:37:41 +02:00 by michael · 3 comments
Owner

Problem

The first real daw_i18n/v1 migration in DAW/daw-lua-common
revealed an ambiguity for parameterized runtime messages.

Example from base64.lua:

fail("invalid_padding", "invalid padding position in group " .. g)

daw_i18n/v1 correctly forbids fragment concatenation for translatable
resources. The corresponding canonical resource therefore needs to be
a complete statement with a named placeholder, for example:

{
  "text": "invalid padding position in group {group}",
  "parameters": {
    "group": "Padding group number"
  }
}

However, the runtime error still needs a directly usable canonical
English diagnostic message such as:

invalid padding position in group 3

The contracts do not yet define how a parameterized
daw_i18n/v1 resource relates to the materialized message of a
daw_error/v1 instance, nor where the placeholder values required for
later translation are represented.

Intended direction

daw_error/v1.message should remain a complete, directly usable
canonical English diagnostic message.

It MUST NOT become an unresolved i18n template merely because a
resource_id is present.

For a parameterized resource, the intended model is conceptually:

{
  schema      = "daw_error/v1",
  code        = "invalid_padding",
  message     = "invalid padding position in group 3",
  resource_id = "<uuid>",
  data = {
    group = 3
  }
}

with a canonical daw_i18n/v1 resource such as:

{
  "text": "invalid padding position in group {group}",
  "parameters": {
    "group": "Padding group number"
  }
}

The materialized runtime message and the canonical resource template
are therefore not byte-identical when placeholders are present.

Open contract point

The contracts need to define how the named placeholders declared by
the referenced daw_i18n/v1 resource obtain their values from the
runtime object.

Using fields from daw_error/v1.data with matching names is the
current candidate, but this MUST be decided explicitly rather than
introduced as an implementation convention.

In particular, the contract should clarify whether:

  • each resource parameter name maps directly to a same-named field in
    daw_error/v1.data, or
  • an explicit parameter mapping/container is required.

The solution should preserve structured error data without implicitly
declaring every data field to be an i18n parameter.

Scope

Clarify the interaction between:

  • daw_error/v1.message
  • daw_error/v1.resource_id
  • parameterized daw_i18n/v1 resources
  • structured runtime values used to resolve named placeholders

This issue does not define a language-specific resolver API or fallback
policy.

Acceptance criteria

  • The contracts define the meaning of message when resource_id
    references a parameterized resource.
  • message remains a complete, directly usable canonical English
    diagnostic string with runtime values materialized.
  • Unresolved {placeholder} syntax is not required in
    daw_error/v1.message.
  • The contracts define where the values for referenced
    daw_i18n/v1 placeholders are obtained from a daw_error/v1
    instance.
  • The relationship between i18n parameters and daw_error/v1.data
    is explicit and does not accidentally make every data field an
    i18n parameter.
  • A normative example covers a parameterized error such as
    invalid padding position in group {group}.
  • Existing non-parameterized message semantics remain unchanged.
  • No language-specific resolver API or fallback policy is introduced.
  • DAW/daw-lua-common#48
  • DAW/daw-contracts#6
  • DAW/daw-contracts#7
## Problem The first real `daw_i18n/v1` migration in `DAW/daw-lua-common` revealed an ambiguity for parameterized runtime messages. Example from `base64.lua`: ```lua fail("invalid_padding", "invalid padding position in group " .. g) ``` `daw_i18n/v1` correctly forbids fragment concatenation for translatable resources. The corresponding canonical resource therefore needs to be a complete statement with a named placeholder, for example: ```json { "text": "invalid padding position in group {group}", "parameters": { "group": "Padding group number" } } ``` However, the runtime error still needs a directly usable canonical English diagnostic message such as: ```text invalid padding position in group 3 ``` The contracts do not yet define how a parameterized `daw_i18n/v1` resource relates to the materialized `message` of a `daw_error/v1` instance, nor where the placeholder values required for later translation are represented. ## Intended direction `daw_error/v1.message` should remain a complete, directly usable canonical English diagnostic message. It MUST NOT become an unresolved i18n template merely because a `resource_id` is present. For a parameterized resource, the intended model is conceptually: ```lua { schema = "daw_error/v1", code = "invalid_padding", message = "invalid padding position in group 3", resource_id = "<uuid>", data = { group = 3 } } ``` with a canonical `daw_i18n/v1` resource such as: ```json { "text": "invalid padding position in group {group}", "parameters": { "group": "Padding group number" } } ``` The materialized runtime `message` and the canonical resource template are therefore not byte-identical when placeholders are present. ## Open contract point The contracts need to define how the named placeholders declared by the referenced `daw_i18n/v1` resource obtain their values from the runtime object. Using fields from `daw_error/v1.data` with matching names is the current candidate, but this MUST be decided explicitly rather than introduced as an implementation convention. In particular, the contract should clarify whether: - each resource parameter name maps directly to a same-named field in `daw_error/v1.data`, or - an explicit parameter mapping/container is required. The solution should preserve structured error data without implicitly declaring every `data` field to be an i18n parameter. ## Scope Clarify the interaction between: - `daw_error/v1.message` - `daw_error/v1.resource_id` - parameterized `daw_i18n/v1` resources - structured runtime values used to resolve named placeholders This issue does not define a language-specific resolver API or fallback policy. ## Acceptance criteria - [ ] The contracts define the meaning of `message` when `resource_id` references a parameterized resource. - [ ] `message` remains a complete, directly usable canonical English diagnostic string with runtime values materialized. - [ ] Unresolved `{placeholder}` syntax is not required in `daw_error/v1.message`. - [ ] The contracts define where the values for referenced `daw_i18n/v1` placeholders are obtained from a `daw_error/v1` instance. - [ ] The relationship between i18n parameters and `daw_error/v1.data` is explicit and does not accidentally make every `data` field an i18n parameter. - [ ] A normative example covers a parameterized error such as `invalid padding position in group {group}`. - [ ] Existing non-parameterized `message` semantics remain unchanged. - [ ] No language-specific resolver API or fallback policy is introduced. ## Related - `DAW/daw-lua-common#48` - `DAW/daw-contracts#6` - `DAW/daw-contracts#7`
Author
Owner

Resolution: parameter binding via data, relational not constructive

Decided model, validated against config_parser.lua's five
parameterized error sites:

  • For a daw_error/v1 carrying a resource_id, every named parameter
    declared by the referenced daw_i18n/v1 canonical resource MUST have
    a same-named direct field in daw_error/v1.data.
  • Parameter names address direct fields only. Nested paths, path
    syntax, expressions, and implicit traversal are not supported in
    v1.
  • Additional fields in data are unrelated to i18n unless their names
    occur in the resource's parameters set (Resource.parameters ->
    determines required names; error.data -> supplies the values; not
    the reverse).
  • This is a relational contract condition, not a construction-time
    obligation. daw_error.new() does not require the referenced
    daw_i18n/v1 resource to be available and does not validate this
    relationship itself.
  • A consumer or validator that has both the error and the referenced
    resource available MUST treat a missing required parameter value as
    an invalid error/resource pairing, and MUST NOT silently omit,
    invent, or leave the placeholder unresolved.
  • message: without parameters, message equals the canonical resource
    text; with parameters, message represents the canonical English
    resource text with all declared placeholders materialized from the
    corresponding data values. message never contains unresolved
    {placeholder} syntax.

Explicitly out of scope for #8, tracked separately as #9:
deterministic representation of non-string parameter values (e.g.
data.line = 17 as a number, booleans). Resolution decided here; not
yet implemented in the contract files.

## Resolution: parameter binding via data, relational not constructive Decided model, validated against config_parser.lua's five parameterized error sites: - For a daw_error/v1 carrying a resource_id, every named parameter declared by the referenced daw_i18n/v1 canonical resource MUST have a same-named direct field in daw_error/v1.data. - Parameter names address direct fields only. Nested paths, path syntax, expressions, and implicit traversal are not supported in v1. - Additional fields in data are unrelated to i18n unless their names occur in the resource's parameters set (Resource.parameters -> determines required names; error.data -> supplies the values; not the reverse). - This is a relational contract condition, not a construction-time obligation. daw_error.new() does not require the referenced daw_i18n/v1 resource to be available and does not validate this relationship itself. - A consumer or validator that has both the error and the referenced resource available MUST treat a missing required parameter value as an invalid error/resource pairing, and MUST NOT silently omit, invent, or leave the placeholder unresolved. - message: without parameters, message equals the canonical resource text; with parameters, message represents the canonical English resource text with all declared placeholders materialized from the corresponding data values. message never contains unresolved {placeholder} syntax. Explicitly out of scope for #8, tracked separately as #9: deterministic representation of non-string parameter values (e.g. data.line = 17 as a number, booleans). Resolution decided here; not yet implemented in the contract files.
Author
Owner

Updated acceptance criteria (supersedes originals, still open)

  • The contracts define the meaning of message when resource_id
    references a parameterized resource.
  • message remains a complete, directly usable canonical English
    diagnostic string with runtime values materialized.
  • Unresolved {placeholder} syntax is not required in
    daw_error/v1.message.
  • The contracts define where the values for referenced
    daw_i18n/v1 placeholders are obtained from a daw_error/v1
    instance (same-named direct fields in data).
  • The relationship between i18n parameters and daw_error/v1.data
    is explicit and does not accidentally make every data field an
    i18n parameter.
  • A normative example covers a parameterized error such as
    invalid_section_type (config_parser.lua real-world case).
  • Existing non-parameterized message semantics remain unchanged.
  • No language-specific resolver API or fallback policy is
    introduced.
  • Nested/path-style parameter references (e.g. {section.name})
    are explicitly out of v1 scope.
  • Value-formatting of non-string parameter values is explicitly
    named as out of scope, tracked as DAW/daw-contracts#9.
## Updated acceptance criteria (supersedes originals, still open) - [ ] The contracts define the meaning of message when resource_id references a parameterized resource. - [ ] message remains a complete, directly usable canonical English diagnostic string with runtime values materialized. - [ ] Unresolved {placeholder} syntax is not required in daw_error/v1.message. - [ ] The contracts define where the values for referenced daw_i18n/v1 placeholders are obtained from a daw_error/v1 instance (same-named direct fields in data). - [ ] The relationship between i18n parameters and daw_error/v1.data is explicit and does not accidentally make every data field an i18n parameter. - [ ] A normative example covers a parameterized error such as invalid_section_type (config_parser.lua real-world case). - [ ] Existing non-parameterized message semantics remain unchanged. - [ ] No language-specific resolver API or fallback policy is introduced. - [ ] Nested/path-style parameter references (e.g. {section.name}) are explicitly out of v1 scope. - [ ] Value-formatting of non-string parameter values is explicitly named as out of scope, tracked as DAW/daw-contracts#9.
Author
Owner

Implemented

daw_error/v1/specification.md (message, resource_id, data sections)
and contract.json now define the parameter binding: resource.parameters
determines required names, daw_error.data supplies values via
same-named direct fields (relational, not construction-time), message
is fully materialized with no unresolved placeholders. Nested/path
parameter references are out of scope. Non-string value formatting is
explicitly deferred to #9.

The example_with_cell_id_and_resource in contract.json previously
contained an unresolved {path} placeholder in message, violating this
rule -- replaced with a materialized config_parser.lua example
(invalid_section_type).

## Implemented daw_error/v1/specification.md (message, resource_id, data sections) and contract.json now define the parameter binding: resource.parameters determines required names, daw_error.data supplies values via same-named direct fields (relational, not construction-time), message is fully materialized with no unresolved placeholders. Nested/path parameter references are out of scope. Non-string value formatting is explicitly deferred to #9. The example_with_cell_id_and_resource in contract.json previously contained an unresolved {path} placeholder in message, violating this rule -- replaced with a materialized config_parser.lua example (invalid_section_type).
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
DAW/daw-contracts#8
No description provided.