Skip to content

feat(l10n): localize CSV import errors by type - #162

Merged
mrcsin merged 1 commit into
masterfrom
csv-import-errors-typed
Jul 30, 2026
Merged

mrcsin merged 1 commit into
masterfrom
csv-import-errors-typed

Conversation

@mrcsin

@mrcsin mrcsin commented Jul 30, 2026

Copy link
Copy Markdown
Member

Problem

The CSV file-load path (CsvService → CsvFileSerializer → CsvRowConverter) raised free-text Result.Fail(string) / new Error(string) for every deserialize failure, so a CSV import error rendered raw English regardless of culture. Worse, a bad cell showed just "Row 2": the untyped Row N wrapper stringified its inner, and ReasonLocalizer's fallback recursion dropped the column/value detail.

What changed

9 new typed errors + 2 reuses, so both the Localize and panel paths localize by type:

  • AtRowError — a new composing row decorator (mirrors AtStepError). CsvRowConverter's column wrap reuses the slice-3 AtColumnError (byte-identical, already composes Localize(inner)), and unknown-action reuses 4b's ActionByIdNotFoundError. So the value chain composes AtRowError → AtColumnError → typed value error, rendering under ru as Строка 2: Столбец «gas»: Значение 5 больше максимума 4 для «amount» — Russian end to end.
  • Leaf errors for action-column (not-found/empty), action-value-not-integer, CSV-body-empty, header-mismatch, recipe-file-not-found.
  • CsvService IO/access failures → Rule-B exception-envelopes: a typed error carrying filePath, raised with .CausedBy(ex); the localized headline drops the raw ex.Message, which survives in the log via the existing _logger.LogWarning.

Behavior-preserving for English except three flagged changes: AtRowError gains : {inner} (the display fix), the ActionByIdNotFoundError reuse rewords unknown-action, and the two exception-envelopes drop : {ex.Message}. The CsvPropertyValidationTests.FlattenMessage gate asserts stay green (the full chain now lives in the composed .Message). Under ru the CSV import surface reads Russian.

Scope: CSV path only. Clipboard producer errors are slice 5c.

Verification

  • dotnet build SemiStep.slnx — 0 warnings.
  • dotnet test SemiStep/SemiStep.Tests/SemiStep.Tests.csproj — 1629 passed, 0 failed.
  • The composition payoff: CsvImportLocalizationTests deserializes a bad-value CSV through the real CsvFileSerializer and asserts the full Russian chain under ru (both the Localize and the panel ReportFailure paths). ReasonLocalizerTests pins the ru render of all 9 new types + the en Localize == .Message invariant; the coverage test forces a case for each.
  • Review chain: comprehensive (5 agents, all OUTCOME ACHIEVED) → smells → comment audit (Ship) → critical, all clean after two low fixes (ru render pins, a doc path).
  • Manual test (operator): imported a CSV with an out-of-range cell / bad header under the Russian UI — the error shows in Russian with row/column context.
Per-task commits before collapse
1d5a1c8 docs: correct error namespace paths in error-reporting
1d484f3 test(l10n): pin russian render for remaining CSV import errors
7cdb343 test(l10n): cover CSV import error localization end-to-end
c3d9bb8 feat(l10n): type CSV serializer and service errors
f69bd74 feat(l10n): type CSV converter and row errors

🤖 Generated with Claude Code

The CSV file-load path (CsvService -> CsvFileSerializer -> CsvRowConverter)
raised free-text Result.Fail/new Error for every deserialize failure, so a
CSV import error rendered raw English regardless of culture. Worse, a bad
cell showed just "Row 2": the untyped "Row N" wrapper stringified its inner,
and the localizer's fallback recursion dropped the column/value detail.

Type the CSV import errors so both paths localize by type:

- AtRowError -- a new composing row decorator (mirrors AtStepError). The
  CsvRowConverter column wrap reuses the slice-3 AtColumnError (byte-
  identical), and unknown-action reuses 4b's ActionByIdNotFoundError. So the
  value chain composes: AtRowError -> AtColumnError -> typed value error,
  rendering under ru as "Строка 2: Столбец «gas»: Значение 5 больше
  максимума 4 для «amount»" -- Russian end to end.
- Leaf errors for action-column / body-empty / header-mismatch / not-found.
- CsvService IO/access failures become Rule-B exception-envelopes: a typed
  error carrying filePath with CausedBy(ex); the localized headline drops the
  raw ex.Message, which survives in the log via the existing LogWarning.

English is behavior-preserving except three flagged changes: AtRowError
gains ": {inner}" (a display fix), the ActionByIdNotFoundError reuse rewords
unknown-action, and the two envelopes drop ": {ex.Message}". The
FlattenMessage gate asserts stay green (the full chain now lives in the
composed .Message). Under ru the CSV import surface reads Russian.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mrcsin
mrcsin marked this pull request as ready for review July 30, 2026 13:42
@mrcsin
mrcsin merged commit e69b03e into master Jul 30, 2026
1 check passed
@mrcsin
mrcsin deleted the csv-import-errors-typed branch July 30, 2026 13:44
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