Skip to content

feat(l10n): localize grid-style editor errors by type - #173

Merged
mrcsin merged 1 commit into
masterfrom
style-editor-errors-typed
Aug 4, 2026
Merged

mrcsin merged 1 commit into
masterfrom
style-editor-errors-typed

Conversation

@mrcsin

@mrcsin mrcsin commented Aug 4, 2026

Copy link
Copy Markdown
Member

Problem

The in-app grid-style editor (GridStyleEditorViewModel) was the last unlocalized operator surface in the error-localization roadmap. Its ErrorMessage was built by joining raw .Message strings, bypassing ReasonLocalizer entirely — ReasonLocalizer wasn't referenced in StyleEditor/ at all. So a load / validate / save failure rendered English regardless of UI culture. The editor opens after startup (culture established), so there's no reason to leave any of it English — leaving loader errors English while validator errors were Russian would show one panel in mixed languages depending on whether the file was missing or a color was malformed.

What changed

Type all 8 grid-style error producers and route the three ErrorMessage join sites through ReasonLocalizer.Localize:

Producer Typed errors
GridStyleValidator config-missing, section-missing (unifies colors + generic), orientation-invalid, key-missing, invalid-hex-color
GridStyleLoader config-not-found; load-failed (Rule-B envelope)
GridStyleWriter save-failed (Rule-B envelope)

Each gets a ReasonLocalizer arm, en/ru resx + Designer, and a coverage sample. The join sites localize by type and fall through to .Message for anything not yet typed. English base messages stay byte-identical except the two Rule-B headlines, which drop the : {ex.Message} tail.

Two design points worth calling out:

  • The orientation error carries its two expected values as properties — GridOrientationValues is internal to Core and invisible to the UI localizer, so the arm can't reference it.
  • The dropped exception detail rides CausedBy(ex) and is logged at both consumers of the shared loader: the editor view model (LogWarning) and the startup path in Program.cs (Log.Error). Without the startup sink, a malformed grid_style.yaml at launch would lose its parse detail entirely.
  • ReportSaveException drops its raw tail to the already-localized Resources.SaveFailed — the exceptions reaching it are pre-write pipeline throws, not file-write failures, so naming a file would mislead.

Scope: pure localization. Issue #118 (the editor's structural refactor — declarative 60-field map, async Save, HexColor.Parse removal) is independent and untouched; only the three join sites move. After this, the operator-facing error surfaces are fully typed — the startup config load (before culture exists) and the two PLC malformed-wire diagnostic edges remain English by design.

Verification

  • dotnet build SemiStep.slnx — 0 warnings; dotnet test — 1685 passed, 0 failed; dotnet format clean.
  • ReasonLocalizerTests pins each of the 8 types' ru render + the en Localize == .Message pins; CoreErrorLocalizationCoverageTests auto-enrolls the 8 public errors; ResourceSyncTests enforces en/ru/Designer key + placeholder parity.
  • GridStyleEditorViewModelTests.LoadAsync_MalformedHexColor overlays a corrupted color on the shipped config and asserts ErrorMessage is the exact Russian hex sentence (a regression to raw .Message fails it). Two log-sink tests assert the CausedBy exception reaches the log — removing the sink call fails them.
  • GridStyleEditorFacadeTests assert GridStyleLoadFailedError/GridStyleSaveFailedError retain their exception on the nested .Reasons.
  • Review chain: comprehensive (5 agents, all OUTCOME ACHIEVED) → smells → comment audit → critical, all clean after fixes. The reviews caught an untested log sink (added tests), a doc overreach ("sole English-by-design surface" — qualified against the PLC edges), and a Path→FilePath rename.
  • Manual test (operator): passed.
Per-task commits before collapse
99524a9 docs(plan): record exec branch and verify steps for slice 7
0ab4468 docs(test): drop misleading Rule-B comment in localizer test
4143204 refactor(l10n): rename GridStyleConfigNotFoundError.Path to FilePath
70409ad test(l10n): cover grid-style exception log sink; fix doc overreach
44a75bf test(l10n): cover grid-style error localization end-to-end
79c7adf feat(l10n): type grid-style file-IO errors
713a808 feat(l10n): type grid-style validation errors

🤖 Generated with Claude Code

The in-app grid-style editor was the last unlocalized operator surface.
Its ErrorMessage was built by joining raw .Message strings, bypassing
ReasonLocalizer entirely, so a load/validate/save failure rendered
English regardless of UI culture. The editor opens after startup, when
the culture is established, so there is no reason to leave any of it
English.

Type all eight grid-style error producers and route the three ErrorMessage
join sites through ReasonLocalizer.Localize:

- GridStyleValidator: config-missing, section-missing (unifying the
  'colors' and generic cases), orientation-invalid, key-missing,
  invalid-hex-color.
- GridStyleLoader: config-not-found, and load-failed as a Rule-B envelope.
- GridStyleWriter: save-failed as a Rule-B envelope.

Each gets a ReasonLocalizer arm, en/ru resx, and a coverage sample; the
join sites localize by type and fall through to .Message for anything not
yet typed. English base messages stay byte-identical except the two Rule-B
headlines, which drop the ": {ex.Message}" tail.

The orientation error carries its two expected values as properties because
GridOrientationValues is internal to Core and invisible to the UI localizer.
The dropped exception detail is preserved on CausedBy and logged at both
consumers of the shared loader: the editor view model (LogWarning) and the
startup path in Program.cs (Log.Error) -- without the startup sink a
malformed grid_style.yaml at launch would lose its parse detail entirely.
ReportSaveException drops its raw tail to the already-localized
Resources.SaveFailed; the exceptions reaching it are pre-write pipeline
throws, not file-write failures, so naming a file would mislead.

After this the operator-facing error surfaces are fully typed. The startup
config load (before the culture exists) and the two PLC malformed-wire
diagnostic edges remain English by design. Issue #118 (the editor's
structural refactor) is independent and unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mrcsin
mrcsin marked this pull request as ready for review August 4, 2026 10:31
@mrcsin
mrcsin merged commit 1cd9835 into master Aug 4, 2026
1 check passed
@mrcsin
mrcsin deleted the style-editor-errors-typed branch August 4, 2026 10:33
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