feat(l10n): vergelijk de taaltabellen onderling in plaats van alleen tegen het gebruik #1529

Merged
brenno merged 5 commits from feat/l10n-tabelgelijkheid into main 2026-08-17 14:58:51 +00:00
Owner

Wat er mis was

De twee bestaande vertaalpoorten redeneren allebei vanuit het gebruik in lib/: make l10n-check / app_localizations_test eisen een vertaling voor elke d('…')-literal die daar staat, en check_l10n_orphans vraagt of een sleutel nog wordt opgehaald (en leest daarvoor alleen en.dart).

Een sleutel die wél in de tabellen staat maar even nergens wordt opgevraagd valt door beide mazen. Hij mag dan in de ene taal bestaan en in de andere ontbreken zonder dat iets klaagt — en dat was ook zo. (#1523)

De poort

tool/check_l10n_table_parity.dart + make check-l10n-parity: elke sleutel die in één taaltabel staat moet in álle staan, ongeacht of hij nu wordt opgevraagd. AST-gemeten, net als de wezenpoort.

Twee families, geen drie. _dutchSource* en _dutchSourceAdd* zijn één naamruimte — make add-l10n schrijft in de toevoegingen, d() leest uit allebei, en waar de knip ligt verschilt per taal puur door volgorde (de meeste 1266/2014, de/es/fr/it 501/2777, tr 1949/1331). Per tabel vergelijken zou ruim duizend betekenisloze verschillen per taal melden. _strings* staat apart.

nl is de brontaal en heeft geen brontabellen (d() geeft voor nl de brontekst meteen terug). Die uitzondering zit in de poort zelf, niet in een uitzonderingslijst.

In check, niet in check-full — anders dan zusterpoort check-l10n-orphans. Die leunt op tekstbewijs en kan zich vergissen; daarom draagt ze een basislijn en draait ze in de trage ronde. Dit is een exacte verzamelingsvergelijking: niets te interpreteren, dus geen valse melding mogelijk en geen basislijn nodig — nul is de enige stand. 32 bestanden, klaar in een seconde. Dat hoort in de ronde die élke commit tegenhoudt.

Wat de poort bij invoering vond, en wat ermee is gebeurd

Geen basislijn aangelegd — bij #1520 is er juist één weggewerkt. Alle drie de gevallen zijn in deze tak opgeruimd.

Gat Wat het was Keuze
4 bronsleutels ontbraken in de, es, fr, fy, it, pap (24 regels) 'Subtitel', 'Kies een video', 'Kies een audiobestand', 'Platte tekst' — stonden in de andere 25 talen en worden wél opgehaald (mediakiezer in image_service.dart, taalkeuze van het codeblok in code_editor.dart) Aangevuld. Deze zes talen vielen stil terug op het Engels. De vertalingen volgen de zusterregels die er al stonden ('Ondertitel' / 'Subtitel (H2)', 'Kies een afbeelding', en wat de andere talen bij 'Platte tekst' doen). Voor het Fries 'Gewoane tekst' in plaats van het Nederlandse woord, want een identieke vertaling wordt elders juist bewaakt.
2 bronsleutels stonden alléén in die zes talen (50 ontbrekende regels) 'Toestemming ingetrokken' en 'U moet eerst de privacy- en gebruiksvoorwaarden accepteren…' — nergens opgehaald: geen literal, geen asset, geen sjabloon Weggehaald. Aanvullen zou 50 regels vertaling opleveren voor tekst die niemand ziet, en zou de wezenpoort op 2 zetten terwijl de basislijn nul hoort te blijven. De dialoog gebruikt 'Toestemming intrekken' (de knop); die staat in alle 31 talen.
28 t()-sleutels stonden alléén in nl (868 ontbrekende regels) Identiteitsregels van de LibrePlan-connector (#1240) in _stringsNl; één zelfs met een Engelse waarde ("dia's geïmporteerd.": 'slides imported.') Weggehaald. Onbereikbaar: d() raadpleegt voor nl geen tabel, en niets roept t() met een Nederlandse zin aan — de strings worden via d() opgehaald en staan als bronsleutel netjes in alle 31 vertaaltabellen. Patroon van een restant: in precies één taal, in geen enkele andere.

Toetsing

test/l10n_table_parity_test.dart toetst in twee richtingen op mini-repo's: gelijke tabellen leveren géén melding op (ook met nl zonder brontabellen, en met de knip op een andere plek), en elk geslagen gat — sleutel weg bij één taal, sleutel bij maar één taal, taal zonder brontabellen — moet gemeld worden, met de juiste familie en de juiste talenlijst. Plus twee metingen op de echte boom.

Poorten

make check groen (exit 0, dekking 86,9%, per-bestandsvloer 0 onder), make check-static groen, make check-registrations groen, make check-l10n-orphans groen (0, basislijn 0), make l10n-check groen. check-secrets/sast/DAST niet gedraaid — deze wijziging voegt geen afhankelijkheid toe, raakt geen geserveerd oppervlak en bevat geen geheimen.

Bewaker-stap bewust overgeslagen: dit raakt het bestandsformaat, de opslag, een afhankelijkheid, uitgaand verkeer noch een publieke belofte — het is een interne poort plus vier vertalingen.

## Wat er mis was De twee bestaande vertaalpoorten redeneren allebei vanuit het **gebruik in `lib/`**: `make l10n-check` / `app_localizations_test` eisen een vertaling voor elke `d('…')`-literal die daar staat, en `check_l10n_orphans` vraagt of een sleutel nog wordt opgehaald (en leest daarvoor alleen `en.dart`). Een sleutel die wél in de tabellen staat maar even nergens wordt opgevraagd valt door beide mazen. Hij mag dan in de ene taal bestaan en in de andere ontbreken zonder dat iets klaagt — en dat was ook zo. (#1523) ## De poort `tool/check_l10n_table_parity.dart` + `make check-l10n-parity`: elke sleutel die in één taaltabel staat moet in álle staan, ongeacht of hij nu wordt opgevraagd. AST-gemeten, net als de wezenpoort. **Twee families, geen drie.** `_dutchSource*` en `_dutchSourceAdd*` zijn één naamruimte — `make add-l10n` schrijft in de toevoegingen, `d()` leest uit allebei, en waar de knip ligt verschilt per taal puur door volgorde (de meeste 1266/2014, de/es/fr/it 501/2777, tr 1949/1331). Per tabel vergelijken zou ruim duizend betekenisloze verschillen per taal melden. `_strings*` staat apart. **nl is de brontaal** en heeft geen brontabellen (`d()` geeft voor nl de brontekst meteen terug). Die uitzondering zit in de poort zelf, niet in een uitzonderingslijst. **In `check`, niet in `check-full`** — anders dan zusterpoort `check-l10n-orphans`. Die leunt op tekstbewijs en kan zich vergissen; daarom draagt ze een basislijn en draait ze in de trage ronde. Dit is een exacte verzamelingsvergelijking: niets te interpreteren, dus geen valse melding mogelijk en geen basislijn nodig — nul is de enige stand. 32 bestanden, klaar in een seconde. Dat hoort in de ronde die élke commit tegenhoudt. ## Wat de poort bij invoering vond, en wat ermee is gebeurd Geen basislijn aangelegd — bij #1520 is er juist één weggewerkt. Alle drie de gevallen zijn in deze tak opgeruimd. | Gat | Wat het was | Keuze | | --- | --- | --- | | **4 bronsleutels ontbraken in de, es, fr, fy, it, pap** (24 regels) | `'Subtitel'`, `'Kies een video'`, `'Kies een audiobestand'`, `'Platte tekst'` — stonden in de andere 25 talen en worden wél opgehaald (mediakiezer in `image_service.dart`, taalkeuze van het codeblok in `code_editor.dart`) | **Aangevuld.** Deze zes talen vielen stil terug op het Engels. De vertalingen volgen de zusterregels die er al stonden (`'Ondertitel'` / `'Subtitel (H2)'`, `'Kies een afbeelding'`, en wat de andere talen bij `'Platte tekst'` doen). Voor het Fries `'Gewoane tekst'` in plaats van het Nederlandse woord, want een identieke vertaling wordt elders juist bewaakt. | | **2 bronsleutels stonden alléén in die zes talen** (50 ontbrekende regels) | `'Toestemming ingetrokken'` en `'U moet eerst de privacy- en gebruiksvoorwaarden accepteren…'` — nergens opgehaald: geen literal, geen asset, geen sjabloon | **Weggehaald.** Aanvullen zou 50 regels vertaling opleveren voor tekst die niemand ziet, en zou de wezenpoort op 2 zetten terwijl de basislijn nul hoort te blijven. De dialoog gebruikt `'Toestemming intrekken'` (de knop); die staat in alle 31 talen. | | **28 t()-sleutels stonden alléén in nl** (868 ontbrekende regels) | Identiteitsregels van de LibrePlan-connector (#1240) in `_stringsNl`; één zelfs met een Engelse waarde (`"dia's geïmporteerd.": 'slides imported.'`) | **Weggehaald.** Onbereikbaar: `d()` raadpleegt voor nl geen tabel, en niets roept `t()` met een Nederlandse zin aan — de strings worden via `d()` opgehaald en staan als bronsleutel netjes in alle 31 vertaaltabellen. Patroon van een restant: in precies één taal, in geen enkele andere. | ## Toetsing `test/l10n_table_parity_test.dart` toetst in twee richtingen op mini-repo's: gelijke tabellen leveren géén melding op (ook met nl zonder brontabellen, en met de knip op een andere plek), en elk geslagen gat — sleutel weg bij één taal, sleutel bij maar één taal, taal zonder brontabellen — moet gemeld worden, met de juiste familie en de juiste talenlijst. Plus twee metingen op de echte boom. ## Poorten `make check` groen (exit 0, dekking 86,9%, per-bestandsvloer 0 onder), `make check-static` groen, `make check-registrations` groen, `make check-l10n-orphans` groen (0, basislijn 0), `make l10n-check` groen. `check-secrets`/`sast`/DAST niet gedraaid — deze wijziging voegt geen afhankelijkheid toe, raakt geen geserveerd oppervlak en bevat geen geheimen. Bewaker-stap bewust overgeslagen: dit raakt het bestandsformaat, de opslag, een afhankelijkheid, uitgaand verkeer noch een publieke belofte — het is een interne poort plus vier vertalingen.
De LibrePlan-connector (#1240) heeft 28 Nederlandse bronstrings als
identiteitsregels in `_stringsNl` achtergelaten: sleutel en waarde gelijk,
en één ('dia's geïmporteerd.') zelfs met een Engelse waarde. Ze zijn
onbereikbaar. `d()` geeft voor nl de brontekst meteen terug zonder ook maar
één tabel te raadplegen, en niets roept `t()` met een Nederlandse zin aan —
de strings worden allemaal via `d()` opgehaald en staan als bronsleutel
netjes in alle 31 vertaaltabellen.

Gevonden door de tabelgelijkheidspoort verderop in deze tak (#1523): ze
stonden in precies één taal en in geen enkele andere. Dat is het patroon
van een restant, niet van een gat dat gevuld moet worden.
'Toestemming ingetrokken' en 'U moet eerst de privacy- en
gebruiksvoorwaarden accepteren voordat u OciDeck kunt gebruiken.' stonden
alleen in de, es, fr, fy, it en pap — en worden nergens opgehaald: geen
literal, geen asset, geen sjabloon. De poort tegen wezen (#1520) zag ze
niet, want die leest alleen en.dart en daar stonden ze niet in.

Weghalen in plaats van aanvullen in de andere 25 talen: aanvullen zou 50
regels vertaling opleveren voor tekst die niemand ziet, en zou bovendien de
wezenpoort op 2 zetten terwijl de basislijn nul hoort te blijven. De
toestemmingsdialoog gebruikt 'Toestemming intrekken' (de knop), die staat
er in alle 31 talen gewoon in.
'Subtitel', 'Kies een video', 'Kies een audiobestand' en 'Platte tekst'
stonden in 25 talen en ontbraken in de, es, fr, fy, it en pap. Ze worden
wél opgehaald (de mediakiezer in image_service.dart, de taalkeuze van het
codeblok in code_editor.dart), dus deze zes talen vielen stil terug op het
Engels.

De vertalingen volgen de zusterregels die er al stonden, zodat de app in
één stem blijft praten: 'Subtitel' naar 'Ondertitel'/'Subtitel (H2)', de
twee kiezers naar 'Kies een afbeelding', en 'Platte tekst' naar wat de
andere talen daar doen ('Texto simples', 'Düz metin', …). Voor het Fries
'Gewoane tekst' in plaats van het Nederlandse 'Platte tekst', want een
identieke vertaling wordt elders juist bewaakt.
De twee bestaande vertaalpoorten redeneren allebei vanuit het GEBRUIK in
lib/: `l10n-check` en app_localizations_test eisen een vertaling voor elke
`d('…')`-literal die ze daar vinden, en check_l10n_orphans vraagt of een
sleutel nog wordt opgehaald. Een sleutel die wél in de tabellen staat maar
even nergens wordt opgevraagd valt door beide mazen — en mag dan in de ene
taal bestaan en in de andere ontbreken zonder dat iets klaagt. Zo misten
zes talen al een tijd vier bronsleutels (zie de drie commits hiervoor).

Deze poort kijkt niet naar gebruik maar naar de tabellen zelf: elke sleutel
die in één taal bestaat hoort in alle talen te bestaan.

Twee families, geen drie. `_dutchSource*` en `_dutchSourceAdd*` zijn één
naamruimte — `make add-l10n` schrijft in de toevoegingen, `d()` leest uit
allebei, en waar de knip ligt verschilt per taal puur door volgorde (de
meeste 1266/2014, de/es/fr/it 501/2777, tr 1949/1331). Per tabel
vergelijken zou ruim duizend betekenisloze verschillen per taal melden.
`_strings*` staat apart. nl is de brontaal en heeft geen brontabellen; die
uitzondering zit in de poort in plaats van in een uitzonderingslijst.

IN `check`, NIET IN `check-full`, anders dan zusterpoort
check-l10n-orphans. Die leunt op tekstbewijs en kan zich vergissen; daarom
draagt ze een basislijn en draait ze in de trage ronde. Dit is een exacte
verzamelingsvergelijking: niets te interpreteren, dus geen valse melding
mogelijk, geen basislijn nodig — nul is de enige stand. 32 bestanden, klaar
in een seconde. Dat hoort in de ronde die élke commit tegenhoudt.

De test toetst in twee richtingen: gelijke mini-repo's leveren geen melding
op (ook met nl zonder brontabellen, en met de knip op een andere plek), en
elk geslagen gat — sleutel weg bij één taal, sleutel bij maar één taal,
taal zonder brontabellen — moet gemeld worden.
docs(changelog): tabelgelijkheidspoort en de vier hervonden vertalingen (#1523)
All checks were successful
scans / scans (pull_request) Successful in 5m9s
static-gate / static-gate (pull_request) Successful in 8m33s
95e76c8f0c
brenno merged commit f3bac2a458 into main 2026-08-17 14:58:51 +00:00
Sign in to join this conversation.
No description provided.