fix(import): de import sprak Nederlands tegen iedereen — reden-codes voor foutmeldingen, een vertaalnaad voor de notitiedia's, en een AST-poort #826

Merged
brenno merged 2 commits from fix/import-lokalisatie-806 into main 2026-07-24 22:09:31 +00:00
Owner

Closes #806.

De presentatie-import (pptx/odp/key) sprak Nederlands tegen iedereen, ongeacht de ingestelde taal. Twee gaten die de vertaalpoort niet zag, want beide reisden als een kale bronstring door de servicelaag in plaats van als een letterlijke d('…').

1. Foutmeldingen — reden-code in plaats van een kale message

"dit bestand is geen herkende presentatie", "te groot", "beschadigd" kwamen via ImportFailure.message rechtstreeks op het scherm (presentation_import_action.dart, de wachtrijdialoog). Een Franse gebruiker las Nederlands.

ImportFailure draagt nu een ImportFailureReason plus {bestand}/{formaat}-argumenten; de UI kiest en vertaalt de tekst in importFailureText, langs dezelfde weg als webdavErrorMessage/s3ErrorMessage/gitForgeErrorMessage. De ruwe technische tekst blijft bestaan voor het log — hij komt nooit meer op het scherm. FormatValidation kreeg dezelfde reden-code zodat de service hem kan doorgeven, en de service vult bestand aan op een fout die diep uit een importer terugkomt (die kent de bestandsnaam niet).

De bulk-rij verpakt een schrijffout (volle schijf, geen rechten) als een other-fout met de exception in cause; importFailureText valt daarvoor terug op userFacingError, zodat "De schijf is vol." behouden blijft in plaats van een generieke melding.

2. Notitiedia's — het ergste, want blijvend

De "niet overgenomen"-dia die de import na een verliesgevende conversie toevoegt, werd in het Nederlands ín het .md van de gebruiker geschreven. Een Griekse gebruiker hield zo permanent Nederlandse alinea's in zijn eigen bestand, die meereizen naar elk ander Marp-gereedschap.

DeckBuilder en UnconvertedTracker krijgen een vertaalnaad (ImportTextTranslator, standaard identiteit) die de UI met l10n.d vult. De notitie wordt op importmoment in de taal van de gebruiker gezet en zó opgeslagen — wat klopt, want het is inhoud en geen interface. De variabele delen (bronnummer, bestandsnaam, linktekst) gaan als {naam}-plaatshouder door de keten en worden ná het vertalen ingevuld, zodat elke taal zelf bepaalt waar ze in de zin landen.

Twee code-opruimingen die dit mogelijk maakten:

  • ClassifiedSlide.issues was een List<String> met een fragiele ' — '-splitsing die _issueFromString weer uit elkaar haalde. Nu List<ConversionIssue>, net als SourceDeck.issues al was — de splitsing (die lokalisatie in de weg zat) is weg.
  • De Keynote-salvage bouwde salvagedBits.join(' + ') — een samengestelde string is per taal onvindbaar. Nu drie vaste sleutels (voorbeeldafbeelding, tekst, voorbeeldafbeelding en tekst).

De poort

test/import_note_l10n_test.dart haalt élke notitie- en vaste voortgangstekst via de AST uit de servicelaag (niet regex — die splitste aangrenzende strings verkeerd en miste ternary-takken) en eist alle 31 vertalingen. Zo ontsnapt ook een toekomstige string niet meer onvertaald. De foutmeldingen worden door de bestaande app_localizations_test gedekt — daarvoor staat elke l10n.d(...) in importFailureText op één regel, want die poort leest per .d( maar één literal.

In totaal 78 bronstrings × 31 talen = 2387 nieuwe vertalingen (69 notitie/voortgang via de AST-poort, 3 Keynote-salvage-waarden die via een variabele lopen, 6 foutmeldingen). De bronstrings zijn byte-voor-byte uit de code geëxtraheerd, zodat de typografie (, “ ”, em-dash) klopt met de sleutels.

Wat er bewust buiten valt

De per-dia voortgangsregel "Slide 3/10" (odp_importer.dart, pptx_importer.dart) blijft onvertaald: vluchtig, vrijwel geheel numeriek, en lokaliseren vraagt een aparte voortgangsnaad. Vastgelegd in docs/KNOWN_LIMITATIONS.md.

Vertaalkwaliteit — eerlijk

De grote talen zijn solide. Minder zeker, gemeld door de vertaler:

  • Klingon (tlh): geen standaardwoorden voor grafiek/tabel/slide; consistente benaderingen, geen idiomatisch Klingon.
  • Latijn (la): neologistisch voor moderne begrippen (diagramma, dispersio, fasciculus).
  • "deck" (Deck opbouwen…): in de Germaanse talen "deck" aangehouden, elders "presentatie" — omdat "deck" daar niet leeft.
  • pap/fy/gsw: strekking klopt, iets minder zeker door minder bronmateriaal.

Eén l10n-kwaliteitspoort sloeg terecht aan (id: "Keynote IWA internal" was byte-gelijk aan het Engels) en is gecorrigeerd.

Tests

  • test/import_note_l10n_test.dart — de nieuwe AST-poort, groen.
  • test/import/unconverted_tracker_test.dart — vier nieuwe tests voor de naad: elke tekst gaat door translate, plaatshouder-invulling ná het vertalen, args-invulling, en een plaatshouder-zonder-waarde blijft staan.
  • test/user_facing_error_test.dartimportFailureText: elke reden een niet-lege melding, placeholder-invulling, de ruwe message nooit op het scherm, en de other+cause-terugval.
  • test/import/presentation_import_service_test.dart — de service zet de juiste reden + args, en vult bestand aan op een importer-fout.

make check groen in een verse worktree.

Closes #806. De presentatie-import (pptx/odp/key) sprak Nederlands tegen iedereen, ongeacht de ingestelde taal. Twee gaten die de vertaalpoort niet zag, want beide reisden als een kale bronstring door de servicelaag in plaats van als een letterlijke `d('…')`. ## 1. Foutmeldingen — reden-code in plaats van een kale message "dit bestand is geen herkende presentatie", "te groot", "beschadigd" kwamen via `ImportFailure.message` rechtstreeks op het scherm (`presentation_import_action.dart`, de wachtrijdialoog). Een Franse gebruiker las Nederlands. `ImportFailure` draagt nu een `ImportFailureReason` plus `{bestand}`/`{formaat}`-argumenten; de UI kiest en vertaalt de tekst in `importFailureText`, langs dezelfde weg als `webdavErrorMessage`/`s3ErrorMessage`/`gitForgeErrorMessage`. De ruwe technische tekst blijft bestaan voor het log — hij komt nooit meer op het scherm. `FormatValidation` kreeg dezelfde reden-code zodat de service hem kan doorgeven, en de service vult `bestand` aan op een fout die diep uit een importer terugkomt (die kent de bestandsnaam niet). De bulk-rij verpakt een schrijffout (volle schijf, geen rechten) als een `other`-fout met de exception in `cause`; `importFailureText` valt daarvoor terug op `userFacingError`, zodat "De schijf is vol." behouden blijft in plaats van een generieke melding. ## 2. Notitiedia's — het ergste, want blijvend De "niet overgenomen"-dia die de import na een verliesgevende conversie toevoegt, werd in het Nederlands **ín het `.md` van de gebruiker** geschreven. Een Griekse gebruiker hield zo permanent Nederlandse alinea's in zijn eigen bestand, die meereizen naar elk ander Marp-gereedschap. `DeckBuilder` en `UnconvertedTracker` krijgen een vertaalnaad (`ImportTextTranslator`, standaard identiteit) die de UI met `l10n.d` vult. De notitie wordt op importmoment in de taal van de gebruiker gezet en zó opgeslagen — wat klopt, want het is inhoud en geen interface. De variabele delen (bronnummer, bestandsnaam, linktekst) gaan als `{naam}`-plaatshouder door de keten en worden ná het vertalen ingevuld, zodat elke taal zelf bepaalt waar ze in de zin landen. Twee code-opruimingen die dit mogelijk maakten: - `ClassifiedSlide.issues` was een `List<String>` met een fragiele `' — '`-splitsing die `_issueFromString` weer uit elkaar haalde. Nu `List<ConversionIssue>`, net als `SourceDeck.issues` al was — de splitsing (die lokalisatie in de weg zat) is weg. - De Keynote-salvage bouwde `salvagedBits.join(' + ')` — een samengestelde string is per taal onvindbaar. Nu drie vaste sleutels (`voorbeeldafbeelding`, `tekst`, `voorbeeldafbeelding en tekst`). ## De poort `test/import_note_l10n_test.dart` haalt élke notitie- en vaste voortgangstekst via de **AST** uit de servicelaag (niet regex — die splitste aangrenzende strings verkeerd en miste ternary-takken) en eist alle 31 vertalingen. Zo ontsnapt ook een toekomstige string niet meer onvertaald. De foutmeldingen worden door de bestaande `app_localizations_test` gedekt — daarvoor staat elke `l10n.d(...)` in `importFailureText` op één regel, want die poort leest per `.d(` maar één literal. In totaal **78 bronstrings × 31 talen = 2387 nieuwe vertalingen** (69 notitie/voortgang via de AST-poort, 3 Keynote-salvage-waarden die via een variabele lopen, 6 foutmeldingen). De bronstrings zijn byte-voor-byte uit de code geëxtraheerd, zodat de typografie (`’`, `“ ”`, em-dash) klopt met de sleutels. ## Wat er bewust buiten valt De per-dia voortgangsregel "Slide 3/10" (`odp_importer.dart`, `pptx_importer.dart`) blijft onvertaald: vluchtig, vrijwel geheel numeriek, en lokaliseren vraagt een aparte voortgangsnaad. Vastgelegd in `docs/KNOWN_LIMITATIONS.md`. ## Vertaalkwaliteit — eerlijk De grote talen zijn solide. Minder zeker, gemeld door de vertaler: - **Klingon (tlh)**: geen standaardwoorden voor grafiek/tabel/slide; consistente benaderingen, geen idiomatisch Klingon. - **Latijn (la)**: neologistisch voor moderne begrippen (`diagramma`, `dispersio`, `fasciculus`). - **"deck"** (`Deck opbouwen…`): in de Germaanse talen "deck" aangehouden, elders "presentatie" — omdat "deck" daar niet leeft. - **pap/fy/gsw**: strekking klopt, iets minder zeker door minder bronmateriaal. Eén l10n-kwaliteitspoort sloeg terecht aan (`id: "Keynote IWA internal"` was byte-gelijk aan het Engels) en is gecorrigeerd. ## Tests - `test/import_note_l10n_test.dart` — de nieuwe AST-poort, groen. - `test/import/unconverted_tracker_test.dart` — vier nieuwe tests voor de naad: elke tekst gaat door `translate`, plaatshouder-invulling ná het vertalen, args-invulling, en een plaatshouder-zonder-waarde blijft staan. - `test/user_facing_error_test.dart` — `importFailureText`: elke reden een niet-lege melding, placeholder-invulling, de ruwe message nooit op het scherm, en de `other`+cause-terugval. - `test/import/presentation_import_service_test.dart` — de service zet de juiste reden + args, en vult `bestand` aan op een importer-fout. `make check` groen in een verse worktree.
78 bronstrings × 31 talen: 69 notitie/voortgang-strings die de
AST-poort eist, 3 Keynote-salvage-waarden die via een variabele lopen,
en 6 importfoutmeldingen. 2387 vertalingen ingevoegd, 31 al aanwezig.

De bronstrings zijn byte-voor-byte uit de code geëxtraheerd zodat de
sleutel-typografie (', " ", em-dash) klopt. id.dart: "Keynote IWA
internal" was byte-gelijk aan het Engels en is IWA-intern herschikt.
fix(import): de import sprak Nederlands tegen iedereen (#806)
All checks were successful
scans / scans (pull_request) Successful in 3m18s
bfeb52b7df
Twee gaten die de vertaalpoort niet zag, want beide reisden als een
kale bronstring door de servicelaag in plaats van als een d('…').

Foutmeldingen: ImportFailure draagt nu een ImportFailureReason plus
{bestand}/{formaat}-args; de UI vertaalt in importFailureText, langs
dezelfde weg als de opslag-meldingen. De ruwe message blijft voor het
log. Een schrijffout uit de bulk-rij reist als een other-fout met de
exception in cause, zodat "De schijf is vol." behouden blijft.

Notitiedia's (het ergste, want blijvend): de "niet overgenomen"-dia
werd in het Nederlands ín het .md van de gebruiker geschreven. Nu een
vertaalnaad in DeckBuilder/UnconvertedTracker die de UI met l10n.d
vult; de notitie wordt op importmoment in de taal van de gebruiker
opgeslagen. Variabele delen gaan als {naam}-plaatshouder en worden ná
het vertalen ingevuld.

Twee opruimingen die dit mogelijk maakten: ClassifiedSlide.issues van
List<String> (met een fragiele ' — '-splitsing) naar List<ConversionIssue>,
en de Keynote-salvage van salvagedBits.join naar vaste sleutels.

Een AST-poort (test/import_note_l10n_test.dart) haalt élke notitie- en
vaste voortgangstekst uit de servicelaag en eist alle 31 vertalingen.
De per-dia "Slide 3/10" blijft bewust onvertaald (vluchtig, numeriek).
brenno merged commit 2f5e5aa669 into main 2026-07-24 22:09:31 +00:00
brenno deleted branch fix/import-lokalisatie-806 2026-07-24 22:09:31 +00:00
Sign in to join this conversation.
No description provided.