Skip to content

feat!: route every lookup through a replaceable message formatter - #31

Merged
rent-a-developer merged 1 commit into
release/issue-29-v2.0.0from
feature/issue-30-message-formatter
Oct 13, 2026
Merged

rent-a-developer merged 1 commit into
release/issue-29-v2.0.0from
feature/issue-30-message-formatter

Conversation

@rent-a-developer

@rent-a-developer rent-a-developer commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

Part of #29
Refs #30

Every lookup, with or without arguments, now passes its resolved value through ILocalizer.MessageFormatter (default: StringFormatMessageFormatter). A failed lookup raises ILocalizer.LookupFailed and returns the failure text from ILocalizer.LookupFailureTextBuilder. RefreshTriggeringRevision changes on every change that can change a lookup's result.

BREAKING CHANGE: a formatting failure no longer throws FormatException. Get returns the failure text (by default !key!) and raises ILocalizer.LookupFailed, and XAML renders the failure text. Get(key, args) with an empty array returns the resource value unformatted. ILocalizer has new members (MessageFormatter, LookupFailed, RefreshTriggeringRevision, a hidden Get overload) that custom implementations must add. TranslationNotFound and TranslationNotFoundEventArgs are replaced by LookupFailed and LookupFailedEventArgs, which also report formatting failures. MissingTranslationFormat is replaced by LookupFailureTextBuilder, a Func<LookupFailure, string> that also serves formatting failures. See docs/guides/migration-2.0.md.

Checklist

Everything under Always applies to every pull request. The rest applies only when its trigger does —
tick it, or leave it and say why in the description. A box that does not apply is not a box to tick.

Always

  • pwsh -File scripts/pre-commit-gate.ps1 passes. It runs the public-API reminder, checks line endings,
    style, formatting and member ordering, builds Release and runs the test suites. Use -Fix to have it
    apply the tidying rather than only report it.
  • The Release build produces zero warnings. TreatWarningsAsErrors is on, so this also covers style,
    member ordering, trim and public-API diagnostics.
  • Branch name follows <type>/issue-<number>-<slug>, or <type>/<slug> when there is no issue — see
    CONTRIBUTING.md.

If the change adds behaviour or fixes a bug

  • It is covered by tests.
  • CHANGELOG.md has an entry under ## [Unreleased]. Do not bump a version — the version, the
    release date and the tag are the maintainer's, at release time.

If the change touches one UI package

  • It is mirrored into the other, or it genuinely does not apply there — see
    CONTRIBUTING.md. The Avalonia and WPF markup
    extensions and converters are deliberate mirrors of each other.

If the change touches resource lookup, the generated keys, or the packaging that carries them

Culture fallback, satellite discovery, the source generator's output, the buildTransitive wiring, or a
dependency version. Nothing is trimmed on the just-in-time compiler, so no other check in this repository
can see the damage a mistake here does to a trimmed application.

  • pwsh -File scripts/verify-package-aot.ps1 -Pack passes for both frameworks
    (-Framework net8.0 and the net10.0 default).
  • No new IL2xxx / IL3xxx diagnostics, and none suppressed.

If the change touches the public API

  • The affected PublicAPI.Unshipped.txt is updated with pwsh -File scripts/update-public-api.ps1, and
    the diff was reviewed line by line. A *REMOVED* entry is a breaking change.
  • The XML documentation and the affected pages under docs/ are updated.

Every lookup, with or without arguments, now passes its resolved value
through ILocalizer.MessageFormatter (default: StringFormatMessageFormatter).
A failed lookup raises ILocalizer.LookupFailed and returns the failure text
from ILocalizer.LookupFailureTextBuilder. RefreshTriggeringRevision changes
on every change that can change a lookup's result.

Refs #30

BREAKING CHANGE: a formatting failure no longer throws FormatException. Get
returns the failure text (by default !key!) and raises ILocalizer.LookupFailed,
and XAML renders the failure text. Get(key, args) with an empty array returns
the resource value unformatted. ILocalizer has new members (MessageFormatter,
LookupFailed, RefreshTriggeringRevision, a hidden Get overload) that custom
implementations must add. TranslationNotFound and TranslationNotFoundEventArgs
are replaced by LookupFailed and LookupFailedEventArgs, which also report
formatting failures. MissingTranslationFormat is replaced by
LookupFailureTextBuilder, a Func<LookupFailure, string> that also serves
formatting failures. See docs/guides/migration-2.0.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rent-a-developer rent-a-developer added this to the v2.0.0 milestone Oct 13, 2026
@rent-a-developer rent-a-developer self-assigned this Oct 13, 2026
@codecov

codecov Bot commented Oct 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@rent-a-developer
rent-a-developer merged commit 6f76000 into release/issue-29-v2.0.0 Oct 13, 2026
17 checks passed
@rent-a-developer
rent-a-developer deleted the feature/issue-30-message-formatter branch October 13, 2026 01:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant