# Woordenlijsten

Een woordenlijst is een set woordparen (`bron → doel`) die een leerling oefent.
Bron en doel hebben elk een taal; de standaard is `nl-NL` → `en-US`. Er is ook
een tweede setsoort, het **rollenspel**, waarbij de rijen vaste gespreksbeurten
zijn in plaats van losse woordparen (zie "Soort set" hieronder).

Deze pagina beschrijft woordenlijsten functioneel. Technische details staan in
`/llm/datamodel.md` en `/llm/api.md`.

## Maken

- De docent geeft een naam, een brontaal en een doeltaal op.
- Elke lijst krijgt automatisch een willekeurige, privacy-vriendelijke `slug`
  (wordt gebruikt in de oefen-URL).
- Een lijst kan publiek zijn (`is_public`, standaard `true`) — publieke lijsten
  van anderen verschijnen onder "Ontdekken".

## Soort set: woordjes of rollenspel

- De **soort set** kies je bij het aanmaken: **Woordjes** (standaard) of
  **Rollenspel**. De gekozen soort wordt als `soort` opgeslagen en is daarna
  vast — in het bewerkscherm staat alleen nog een badge, geen keuzekaart.
- Bij een **rollenspel** is elke rij een vaste gespreksbeurt met een **rol**:
  - **Ik** (`rol = "leerling"`): de leerling ziet de Nederlandse zin en zegt de
    doeltaal hardop; de doeltaal is verborgen.
  - **Tegenstander** (`rol = "tegenstander"`): de gesprekspartner; de doeltaal
    wordt voorgelezen en is zichtbaar.
  In de bubble-editor typ je per beurt alleen de doeltaal-zin; de Nederlandse
  vertaling (wat de leerling straks ziet) maakt de AI automatisch bij via
  `POST /api/ai/vertaal-zinnen` (letterlijke batchvertaling, ~0,9 s debounce)
  en is handmatig te bewerken — met een handmatige wijziging stopt de
  automatische vertaling voor die beurt.
- De leerling oefent een rollenspel op `/rollenspel/{slug}`. Op het setdetail
  heet de startknop dan "Start rollenspel"; bij gewone woordjessets opent één
  "Oefenen"-knop een modus-blad met Slim, Uitspraak, Flitskaarten, Spreken,
  Typen, Meerkeuze (en Gesprek als de gespreksmodus aan staat én de set een
  gespreksinstructie heeft — `heeft_draaiboek` via
  `GET /api/gesprek/status?set=`). Dezelfde knop op
  een setkaart in "Mijn sets" opent hetzelfde blad.

## Woorden toevoegen

- Elk woord is een paar: `bron_woord` (wat de leerling ziet) en `doel_woord`
  (wat de leerling zegt/schrijft).
- Synoniemen gescheiden door `/` worden automatisch opgesplitst in één paar met
  alternatieven: `de hond / het hondje → the dog / the hound` wordt één woord
  met `bron_alt` en `doel_alt`.
- De docent kan een woord als **"moeilijk"** markeren. Zo'n woord krijgt in de
  slimme route een typvraag en bovenaan de ladder een verplichte spellingscheck
  (zie `/llm/herhaalsysteem.md`).

## Importeren

Plak-tekst, één paar per regel, gescheiden door ` - `, `=` of een tab:

```text
de hond - the dog
de kat - the cat
het huis = the house
```

De app maakt daar een nieuwe lijst van (of de docent plakt in een bestaande
lijst). Synoniemen met `/` worden ook hier herkend.

## Foto-import

Van één foto maakt de app automatisch woordparen (bij woordjessets) of
dialoogregels (bij rollenspelsets). De volledige afbeelding gaat in één keer
naar Mistral — de browser verdeelt of splitst niets meer:

1. moderatie (Azure Content Safety),
2. Mistral (`pixtral-12b-2409`, strikte OCR, temperature 0) transcribeert
   letterlijk wat er staat: bij woordjes de woordparen (meestal twee kolommen,
   links bron, rechts doel), bij een rollenspel de gesproken dialoogregels met
   rol (leerling/tegenstander). Niets wordt vertaald, aangevuld of verzonnen;
   onduidelijk leesbare tekst valt weg.
3. bij woordjes detecteert de app daarna lokaal (fastText) de doeltaal.

Voor de Mistral-aanroepen staat **Zero Data Retention** aan en is de
**trainingsuitsluiting** actief: de afbeelding en de gevonden woorden worden
niet langer dan nodig bewaard en niet voor modeltraining gebruikt. Zie ook
`/subprocessoren`.

De docent kan het resultaat controleren en aanpassen voordat het wordt
opgeslagen; bij een rollenspel vullen de geïmporteerde regels direct de
bubbels en worden ze daarna automatisch van Nederlandse vertalingen voorzien.
Beperkingen: 20 verzoeken/uur, maximaal 8 MB per foto.

## Woordparen en alternatieven

- `bron_woord` / `doel_woord` zijn het primaire antwoord.
- `bron_alt` / `doel_alt` bevatten extra acceptabele antwoorden, gescheiden
  door `|`. De leerling mag elk van die alternatieven geven.
- Een optioneel deel tussen haakjes geeft twee acceptabele varianten:
  `l'appli(cation)` levert zowel de volledige vorm (`l'application`) als de
  korte vorm (`l'appli`); beide mag de leerling zeggen. De haakjes zelf gaan
  nooit naar de uitspraak-engine.
- Alternatieven zijn belangrijk voor de uitspraakbeoordeling: zie
  `/llm/uitspraak-engine.md` (meerdere alternatieven → eigen engine leidend).

## Taalkeuze

- Bron- en doeltaal worden als BCP-47-locale opgeslagen (bijv. `nl-NL`,
  `en-US`, `fr-FR`). TTS (Piper) en het Azure-vangnet werken met die locale.
- De eigen uitspraak-engine wordt alleen ingezet voor `en`, `fr`, `es`, `de`,
  `it`, `pt` en `nl` (2-lettercode); andere talen vallen terug op Azure, en
  alleen als de leerling Azure zelf heeft aangezet (opt-in; nooit voor
  docent-accounts, accounts onder de 16 en gastleerlingen).

## Thumbnails

- Een docent kan een afbeelding als omslagfoto uploaden.
- De afbeelding wordt gevalideerd (alleen PNG/JPG/GIF/WebP, max 10 MB, max
  25 megapixels), verkleind, gemodereerd (Azure Content Safety) en opgeslagen.

## Delen

- De docent maakt één of meer deellinks (`POST /api/woordenlijsten/{id}/deellink`
  met `{modus}`) en deelt ze met de klas. Een link kan aan een vaste oefenmodus
  hangen (`uitspraak`, `spreken`, `meerkeuze`, `typen` of `flitskaarten`; `slim`
  of leeg = de standaard slimme route). Een leerling die zo'n link opent, begint
  direct in die modus.
- Het deelvenster toont de actieve links (`GET /api/woordenlijsten/{id}/deellinks`)
  met per link een kopieer- en uitzetknop (`DELETE /api/woordenlijsten/{id}/deellink/{link_id}`).
- De link blijft actief totdat de docent hem deactiveert. Zie de flow in
  `/llm/over-zeg-t.md` ("Delen").
- Een leerling die de link claimt krijgt een `LijstDeelname`; zonder eigen
  account wordt een gastaccount aangemaakt.

## Offline oefenen (native app)

Een set kan ook in de native Android-app offline geoefend worden. De app haalt
via `GET /api/woordenlijsten/{id}/offline-bundel` een manifest op met per woord
de referentiebestanden (`.wav`/`.emb`/`.f0`/`.ipa`) en de meerkeuze-opties, en
downloadt de losse bestanden via `.../offline-bundel/bestand?pad=…` (met `ETag`,
`Range` en 304 bij `If-None-Match`, dus hervatbaar). Het manifest
bevat ook `inhouds_hash`, dezelfde 32-tekens set-hash als
`GET /api/woordenlijsten`; verandert de inhoud van de set, dan ziet de app dat en
werkt bij. De server bouwt het manifest zelf samen maar bouwt daarbij geen
referentiebestanden. Ontbrekende bestanden worden pas bij de losse aanvraag
gemaakt en daarna op de VPS gecached onder een sleutel van referentiebackend,
doeltaal en setinhoud; sets met dezelfde inhoud delen die cache. Technische
details staan in `/llm/api.md` (sectie mobiel-offline).

## Verwijderen

- **Een woord verwijderen** wist het woord niet echt: het wordt losgekoppeld
  (`lijst_id = NULL`). De voortgang (`SrsVoortgang`) en oefenpogingen op dat
  woord blijven bewaard.
- **Een woord opnieuw toevoegen** (met dezelfde bron+doel) koppelt het
  oude woord terug, zodat de bewaarde voortgang weer meetelt.
- **Een hele lijst verwijderen** verwijdert de lijst; de woorden en hun
  voortgang blijven (als losgekoppelde woorden) bestaan, tenzij ze nergens
  meer naar verwijzen.

Dit "loskoppelen in plaats van wissen" is een bewuste ontwerpkeuze: progressie
wordt nooit gewist, zodat een leerling niet opnieuw hoeft te beginnen als de
docent een lijst herschikt of opschoont.

## Voortgang resetten

De docent kan per lijst de voortgang van zichzelf (eigenaar) terugzetten
(`POST /api/woordenlijsten/{id}/reset-voortgang`): alle `SrsVoortgang`-rijen
voor die leerling en die lijst worden gewist; de woorden beginnen weer op
trede 0.

## Voortgang

- Voortgang is per **leerling** per **woord** (`SrsVoortgang`): trede,
  streak, beheerst, openstaande herhalingen en volgende herhaling.
- Het overzicht toont per lijst hoeveel van de woorden de leerling al heeft
  geoefend (`voortgang` als percentage) en wanneer hij/zij voor het laatst
  oefende.
- De docent ziet dezelfde voortgang per leerling in het dashboard.
