feat: breid taalbibliotheek uit met 1000 woorden (v0.4.43-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 59s

This commit is contained in:
Ramon 2026-07-21 18:50:47 +02:00
parent 17ee47dd09
commit ba4483d5d3
6 changed files with 284 additions and 27 deletions

View file

@ -1 +1 @@
0.4.42-beta
0.4.43-beta

View file

@ -2,7 +2,7 @@
"use strict";
/* version shown until /api/version resolves (or if the fetch fails, e.g. offline).
Kept in sync by hand with the VERSION file at the repo root on every release. */
const VERSION = "0.4.42-beta";
const VERSION = "0.4.43-beta";
(function(){
const tag = document.getElementById("verTag");
tag.textContent = "v"+VERSION;

View file

@ -71,7 +71,15 @@ const LANGUAGE_THEME_DEFS = [
{id:"home", icon:"🎒", nl:"Thuis & school", en:"Home & school", colors:["#f1ecff","#d9ccff"]},
{id:"body", icon:"👕", nl:"Lichaam & kleding", en:"Body & clothes", colors:["#ffeaf1","#ffcbdc"]},
{id:"actions", icon:"🏃", nl:"Doen", en:"Actions", colors:["#fff8d9","#f6e8a4"]},
{id:"feelings", icon:"🙂", nl:"Gevoelens", en:"Feelings", colors:["#e9f5ff","#c8e4fa"]}
{id:"feelings", icon:"🙂", nl:"Gevoelens", en:"Feelings", colors:["#e9f5ff","#c8e4fa"]},
{id:"people", icon:"👨‍👩‍👧", nl:"Mensen & familie", en:"People & family", colors:["#fff0f4","#ffd3df"]},
{id:"play", icon:"🏀", nl:"Spelen & sport", en:"Play & sports", colors:["#fff4dc","#ffdca8"]},
{id:"places", icon:"🏘️", nl:"Plekken & beroepen", en:"Places & jobs", colors:["#e8f3ff","#c9e2fb"]},
{id:"time", icon:"📅", nl:"Tijd & feest", en:"Time & celebrations", colors:["#f2edff","#ddd1ff"]},
{id:"arts", icon:"🎨", nl:"Kunst & muziek", en:"Arts & music", colors:["#fff0fa","#f4ccec"]},
{id:"science", icon:"🔬", nl:"Onderzoeken & techniek", en:"Science & technology", colors:["#e7f7f5","#bfe9e3"]},
{id:"learning", icon:"📚", nl:"Taal & rekenen", en:"Language & maths", colors:["#fff7df","#f3dfa6"]},
{id:"society", icon:"🌍", nl:"Samenleven & wereld", en:"Society & world", colors:["#e8f1ff","#c8daf7"]}
];
const LANGUAGE_CONCEPTS = [
["monkey","animals","aap","monkey","🐵"],
@ -218,9 +226,230 @@ const LANGUAGE_CONCEPTS = [
["proud","feelings","trots","proud","😌"],
["calm","feelings","rustig","calm","🙂"],
["surprised","feelings","verrast","surprised","😮"],
["excited","feelings","enthousiast","excited","🤩"]
["excited","feelings","enthousiast","excited","🤩"],
["bird","animals","vogel","bird","🐦"],
["rabbit","animals","konijn","rabbit","🐰"],
["fox","animals","vos","fox","🦊"],
["butterfly","animals","vlinder","butterfly","🦋"],
["snail","animals","slak","snail","🐌"],
["ladybird","animals","lieveheersbeestje","ladybird","🐞"],
["spider","animals","spin","spider","🕷️"],
["turtle","animals","schildpad","turtle","🐢"],
["dolphin","animals","dolfijn","dolphin","🐬"],
["elephant","animals","olifant","elephant","🐘"],
["giraffe","animals","giraf","giraffe","🦒"],
["sheep","animals","schaap","sheep","🐑"],
["banana","food","banaan","banana","🍌"],
["orange","food","sinaasappel","orange","🍊"],
["watermelon","food","watermeloen","watermelon","🍉"],
["cherry","food","kers","cherry","🍒"],
["tomato","food","tomaat","tomato","🍅"],
["potato","food","aardappel","potato","🥔"],
["pancake","food","pannenkoek","pancake","🥞"],
["honey","food","honing","honey","🍯"],
["chocolate","food","chocolade","chocolate","🍫"],
["ice","food","ijs","ice","🍦"],
["sandwich","food","boterham","sandwich","🥪"],
["yoghurt","food","yoghurt","yoghurt","🥣"],
["mountain","nature","berg","mountain","⛰️"],
["island","nature","eiland","island","🏝️"],
["beach","nature","strand","beach","🏖️"],
["river","nature","rivier","river","🏞️"],
["lightning","nature","bliksem","lightning","⚡"],
["fog","nature","mist","fog","🌫️"],
["autumn","nature","herfst","autumn","🍂"],
["spring","nature","lente","spring","🌼"],
["summer","nature","zomer","summer","🌞"],
["winter","nature","winter","winter","⛄"],
["seed","nature","zaad","seed","🌰"],
["motorcycle","travel","motor","motorcycle","🏍️"],
["ambulance","travel","ambulance","ambulance","🚑"],
["firetruck","travel","brandweerauto","firetruck","🚒"],
["policecar","travel","politieauto","policecar","🚓"],
["subway","travel","metro","subway","🚇"],
["canoe","travel","kano","canoe","🛶"],
["bridge","travel","brug","bridge","🌉"],
["tunnel","travel","tunnel","tunnel","🚇"],
["trafficlight","travel","stoplicht","trafficlight","🚦"],
["station","travel","station","station","🚉"],
["airport","travel","vliegveld","airport","🛫"],
["mirror","home","spiegel","mirror","🪞"],
["sofa","home","bank","sofa","🛋️"],
["toilet","home","wc","toilet","🚽"],
["shower","home","douche","shower","🚿"],
["broom","home","bezem","broom","🧹"],
["spoon","home","lepel","spoon","🥄"],
["fork","home","vork","fork","🍴"],
["plate","home","bord","plate","🍽️"],
["computer","home","computer","computer","💻"],
["phone","home","telefoon","phone","📱"],
["window","home","raam","window","🪟"],
["finger","body","vinger","finger","☝️"],
["thumb","body","duim","thumb","👍"],
["knee","body","knie","knee","🦵"],
["heart","body","hart","heart","❤️"],
["brain","body","hersenen","brain","🧠"],
["bone","body","bot","bone","🦴"],
["tongue","body","tong","tongue","👅"],
["blood","body","bloed","blood","🩸"],
["hat","body","hoed","hat","👒"],
["boot","body","laars","boot","🥾"],
["scarf","body","sjaal","scarf","🧣"],
["cook","actions","koken","cook","🧑‍🍳"],
["bake","actions","bakken","bake","🧁"],
["cycle","actions","fietsen","cycle","🚴"],
["drive","actions","rijden","drive","🚙"],
["open","actions","openen","open","📂"],
["close","actions","sluiten","close","📕"],
["wash","actions","wassen","wash","🧼"],
["help","actions","helpen","help","🤝"],
["listen","actions","luisteren","listen","👂"],
["smell","actions","ruiken","smell","👃"],
["shy","feelings","verlegen","shy","😊"],
["curious","feelings","nieuwsgierig","curious","🧐"],
["confused","feelings","verward","confused","😕"],
["lonely","feelings","eenzaam","lonely","😔"],
["friendly","feelings","vriendelijk","friendly","🙂"],
["brave","feelings","dapper","brave","🦸"],
["jealous","feelings","jaloers","jealous","😒"],
["bored","feelings","verveeld","bored","🥱"],
["relieved","feelings","opgelucht","relieved","😌"],
["silly","feelings","grappig","silly","🤪"],
["baby","people","baby","baby","👶"],
["child","people","kind","child","🧒"],
["boy","people","jongen","boy","👦"],
["girl","people","meisje","girl","👧"],
["mother","people","moeder","mother","👩"],
["father","people","vader","father","👨"],
["sister","people","zus","sister","👧"],
["brother","people","broer","brother","👦"],
["grandmother","people","oma","grandmother","👵"],
["grandfather","people","opa","grandfather","👴"],
["family","people","familie","family","👨‍👩‍👧‍👦"],
["friend","people","vriend","friend","🧑‍🤝‍🧑"],
["teacher","people","leerkracht","teacher","🧑‍🏫"],
["pupil","people","leerling","pupil","🧑‍🎓"],
["neighbor","people","buur","neighbor","🏘️"],
["woman","people","vrouw","woman","👩"],
["man","people","man","man","👨"],
["person","people","mens","person","🧑"],
["twins","people","tweeling","twins","👯"],
["group","people","groep","group","👥"],
["football","play","voetbal","football","⚽"],
["basketball","play","basketbal","basketball","🏀"],
["tennis","play","tennis","tennis","🎾"],
["hockey","play","hockey","hockey","🏑"],
["volleyball","play","volleybal","volleyball","🏐"],
["skating","play","schaatsen","skating","⛸️"],
["ski","play","ski","ski","🎿"],
["cycling","play","wielrennen","cycling","🚴"],
["running","play","hardlopen","running","🏃"],
["playground","play","speeltuin","playground","🛝"],
["bowling","play","bowlen","bowling","🎳"],
["slide","play","glijbaan","slide","🛝"],
["kite","play","vlieger","kite","🪁"],
["puzzle","play","puzzel","puzzle","🧩"],
["game","play","spel","game","🎮"],
["dice","play","dobbelsteen","dice","🎲"],
["marble","play","knikker","marble","🔵"],
["teddy","play","knuffel","teddy","🧸"],
["cards","play","kaarten","cards","🃏"],
["chess","play","schaken","chess","♟️"],
["medal","play","medaille","medal","🏅"],
["shop","places","winkel","shop","🏪"],
["bakery","places","bakkerij","bakery","🥐"],
["hospital","places","ziekenhuis","hospital","🏥"],
["doctor","places","dokter","doctor","🧑‍⚕️"],
["police","places","politie","police","👮"],
["firefighter","places","brandweer","firefighter","🧑‍🚒"],
["farm","places","boerderij","farm","🏡"],
["farmer","places","boer","farmer","🧑‍🌾"],
["library","places","bibliotheek","library","📚"],
["librarian","places","bibliothecaris","librarian","📚"],
["park","places","park","park","🏞️"],
["zoo","places","dierentuin","zoo","🦒"],
["museum","places","museum","museum","🏛️"],
["restaurant","places","restaurant","restaurant","🍽️"],
["kitchen","places","keuken","kitchen","🍳"],
["classroom","places","klas","classroom","🏫"],
["office","places","kantoor","office","🏢"],
["garage","places","garage","garage","🚗"],
["postman","places","postbode","postman","📬"],
["nurse","places","verpleegkundige","nurse","🧑‍⚕️"],
["dentist","places","tandarts","dentist","🦷"],
["builder","places","bouwer","builder","👷"],
["chef","places","kok","chef","🧑‍🍳"],
["hairdresser","places","kapper","hairdresser","💇"],
["market","places","markt","market","🛍️"],
["city","places","stad","city","🏙️"],
["village","places","dorp","village","🏘️"],
["church","places","kerk","church","⛪"],
["pool","places","zwembad","pool","🏊"],
["morning","time","ochtend","morning","🌅"],
["afternoon","time","middag","afternoon","🌤️"],
["evening","time","avond","evening","🌇"],
["night","time","nacht","night","🌙"],
["today","time","vandaag","today","📅"],
["tomorrow","time","morgen","tomorrow","➡️"],
["yesterday","time","gisteren","yesterday","⬅️"],
["day","time","dag","day","☀️"],
["week","time","week","week","🗓️"],
["month","time","maand","month","📆"],
["year","time","jaar","year","🎆"],
["birthday","time","verjaardag","birthday","🎂"],
["party","time","feest","party","🥳"],
["candle","time","kaars","candle","🕯️"],
["balloon","time","ballon","balloon","🎈"],
["flag","time","vlag","flag","🚩"],
["crown","time","kroon","crown","👑"],
["christmas","time","kerst","christmas","🎄"],
["easter","time","pasen","easter","🐣"],
["garland","time","slinger","garland","🎊"],
["calendar","time","kalender","calendar","📅"],
["hour","time","uur","hour","🕐"],
["minute","time","minuut","minute","⏱️"],
["season","time","seizoen","season","🍁"]
].map(([id,theme,nl,en,pic])=>({id,theme,nl,en,pic}));
/* Ruim duizend aanvullende Nederlandse woorden, geordend in woordfamilies.
Iedere regel loopt bewust van een kort, concreet basiswoord naar langere
afleidingen, samenstellingen en schooltaal. De positie op de regel bepaalt
het eerste leerjaar waarin het woord wordt aangeboden; hogere leerjaren
behouden de eerder aangeboden woorden. Woorden die al in LANGUAGE_CONCEPTS
staan worden hieronder automatisch overgeslagen. */
const LANGUAGE_NL_PROGRESSION_FAMILIES={
animals:"🐈|kat,poes,kitten,kater,katten,poesje,kattenbak,kattenvoer,huiskat,kattenluik,zwerfkat,kattenkwaad\n🐕|hond,pup,reu,teef,honden,pupje,hondenhok,hondenriem,waakhond,hondensport,geleidehond,hondenbezitter\n🐴|paard,pony,veulen,merrie,hengst,paardenstal,paardrijden,renpaard,paardensprong,paardentrailer,paardenverzorging,paardrijwedstrijd\n🐦|vogel,mus,merel,kraai,duif,nestje,vogelvoer,zangvogel,trekvogel,vogelkooi,vogelkenner,vogelbescherming\n🐟|vis,karper,baars,haai,rog,visje,vijvervis,zeedier,visnet,vissersboot,onderwaterdier,waterdierenverblijf\n🐞|mier,bij,wesp,kever,vlinder,rups,insectenhotel,bijenkorf,sprinkhaan,lieveheersbeestje,insectenkenner,insectenonderzoek\n🐄|koe,kalf,geit,schaap,varken,lammetje,kippenhok,melkkoe,boerderijdier,dierenweide,dierenverzorger,veehouderij",
food:"🍞|brood,bol,toast,korst,kruimel,broodje,volkorenbrood,broodmes,bakkerij,broodbeleg,broodtrommel,broodbereiding\n🍎|fruit,appel,peer,pruim,kers,appeltje,fruitsap,fruitmand,boomgaard,vruchtvlees,fruitsoorten,fruitteeltbedrijf\n🥛|melk,karnemelk,room,boter,vla,melkpak,melkglas,zuivel,melkboer,melkproduct,zuivelfabriek,voedingsmiddel\n🧀|kaas,brie,gaten,plak,korst,kaasje,kaasplank,kaasschaaf,kaaswinkel,smeerkaas,kaasbereiding,zuivelproduct\n🥔|aardappel,friet,puree,schil,knol,pieper,aardappelmes,stamppot,aardappelveld,aardappelplant,aardappelgerecht,aardappelverwerking\n🥕|groente,wortel,prei,kool,boon,erwtje,groentesoep,snijplank,moestuin,groentekraam,groentesoorten,groentegerecht\n🍰|taart,koek,snoep,cake,wafel,gebakje,slagroom,taartpunt,bakvorm,koekjesdeeg,feestmaaltijd,suikergehalte",
nature:"🌳|boom,stam,tak,blad,wortel,boompje,boomschors,boomkroon,loofboom,naaldboom,boomsoorten,bomenonderzoek\n🌷|bloem,roos,tulp,stengel,knop,bloempje,bloempot,bloembol,boeket,zonnebloem,bloemenweide,bloembestuiving\n💧|water,druppel,plas,sloot,beek,waterkant,drinkwater,waterput,grondwater,waterleiding,waterzuivering,waterkringloop\n☀|zon,maan,ster,licht,schaduw,zonnetje,zonlicht,maansikkel,sterrenhemel,zonsopgang,zonnestraling,sterrenstelsel\n🌧|regen,wolk,wind,mist,bui,regendruppel,regenjas,wolkbreuk,weersbericht,regenboog,neerslaggebied,weersverwachting\n❄|sneeuw,ijs,vorst,hagel,vlok,sneeuwpop,ijskoud,sneeuwbal,winterweer,sneeuwstorm,temperatuurdaling,winterlandschap\n🌱|aarde,grond,zaad,kiem,plant,zaadje,potgrond,plantengroei,bodemlaag,bodemvruchtbaarheid,voedingsstoffen,kiemingsproces",
travel:"🚗|auto,bus,taxi,rit,band,autootje,autodeur,autoruit,parkeerplek,autoweg,automobilist,verkeersveiligheid\n🚲|fiets,step,pedaal,bel,stuur,fietsje,fietspad,fietspomp,bakfiets,fietstocht,fietsenmaker,fietsverlichting\n🚂|trein,tram,rail,spoor,rit,treintje,perron,station,spoorlijn,treinreis,treinmachinist,spoorwegovergang\n⛵|boot,schip,kano,zeil,roeispaan,bootje,zeilboot,reddingsboei,haven,scheepsdek,binnenvaartschip,scheepvaartverkeer\n✈|vliegtuig,jet,vleugel,piloot,vlucht,vliegveld,landingsbaan,luchtvaart,vliegreis,verkeerstoren,passagiersvliegtuig,luchtvaartmaatschappij\n🛣|weg,pad,brug,tunnel,bocht,wegdek,kruispunt,rotonde,rijbaan,wegwijzer,infrastructuur,verkeersknooppunt\n🚦|licht,bord,streep,file,route,stoplicht,verkeersbord,zebrapad,voorrang,weggebruiker,verkeersregelaar,verkeerssituatie",
home:"🏠|huis,flat,hut,dak,muur,huisje,voordeur,woonkamer,rijtjeshuis,woonwijk,huizenblok,woninginrichting\n🛏|bed,kast,lamp,deken,kussen,bedje,nachtkast,bedlamp,slaapkamer,dekbedovertrek,slaapvertrek,slaapkamerraam\n🪑|stoel,tafel,bank,kruk,kast,stoeltje,tafelblad,eettafel,boekenkast,stoelzitting,meubelwinkel,woonkamermeubel\n🚪|deur,raam,slot,sleutel,bel,deurtje,deurklink,vensterbank,raamkozijn,voordeurbel,inbraakbeveiliging,toegangssysteem\n🍳|pan,pot,kom,bord,lepel,pannetje,koekenpan,keukenkast,aanrecht,afwasborstel,keukengerei,voedselbereiding\n🚿|bad,douche,wc,kraan,zeep,badje,handdoek,wastafel,badkamer,tandenborstel,waterbesparing,badkamertegel\n🧹|stof,vuil,bezem,dweil,spons,stofdoek,afwasmiddel,prullenbak,schoonmaak,schoonmaker,schoonmaakmiddel,huishoudelijkehulp",
body:"✋|hand,duim,vinger,nagel,pols,handje,vingertop,handpalm,wijsvinger,vingerafdruk,handbeweging,handvaardigheid\n🦶|voet,teen,hiel,enkel,knie,voetje,voetzool,knieschijf,onderbeen,voetstap,lichaamshouding,bewegingsapparaat\n🙂|hoofd,wang,kin,nek,keel,hoofdje,voorhoofd,achterhoofd,gezicht,gezichtsvorm,gelaatsuitdrukking,hoofdbescherming\n👁|oog,oor,neus,mond,lip,oogje,ooglid,neusgat,wenkbrauw,zintuig,gehoororgaan,waarnemingsvermogen\n🦷|tand,kies,tong,speeksel,gebit,tandje,tandvlees,tandenborstel,tandarts,mondzorg,tandheelkunde,gebitsverzorging\n👕|jas,shirt,trui,broek,rok,jasje,regenbroek,winterjas,kledingkast,kledingmaat,kledingstuk,kledingindustrie\n❤|hart,long,bloed,bot,spier,hartslag,bloedvat,longinhoud,skelet,spierkracht,bloedsomloop,ademhalingsstelsel",
actions:"🚶|gaan,lopen,stap,staan,zitten,wandelen,voetstap,doorlopen,oversteken,vooruitgaan,verplaatsing,bewegingsrichting\n🏃|rennen,racen,sprinten,haasten,vluchten,hardlopen,renbaan,wedloop,marathon,snelheidsmeter,hardloopwedstrijd,uithoudingsvermogen\n🤸|springen,hupsen,bukken,rollen,draaien,sprongetje,springtouw,koprol,evenwicht,lichaamsbeweging,bewegingsreeks,bewegingsoefening\n📖|lezen,boek,blad,zin,tekst,leesboek,voorlezen,leesbril,leeshoek,leesstrategie,leesvaardigheid,tekstinterpretatie\n✍|schrijven,pen,schrift,letter,woord,opschrijven,handschrift,schrijfboek,schrijfregel,schrijfopdracht,schrijfvaardigheid,tekstproductie\n🗣|praten,zeggen,vragen,roepen,fluisteren,vertellen,spreekbeurt,gesprek,uitspraak,woordkeuze,taalgebruik,gespreksvaardigheid\n🤝|helpen,delen,geven,halen,brengen,hulpje,samenwerken,meehelpen,ondersteunen,hulpverlening,samenwerkingsvorm,verantwoordelijkheid",
feelings:"😀|blij,leuk,fijn,lachen,vrolijk,glimlach,plezier,opgewekt,tevreden,blijheid,enthousiasme,geluksgevoel\n😢|sip,moe,verdriet,pijn,huilen,traantje,teleurgesteld,verdrietig,somber,eenzaamheid,teleurstelling,verdrietverwerking\n😠|boos,kwaad,ruzie,mopperen,grommen,boosheid,driftig,prikkelbaar,woedend,meningsverschil,frustratie,conflictbeheersing\n😨|bang,eng,schrik,zorgen,trillen,angstig,schrikken,onzeker,bezorgd,angstgevoel,onveiligheid,zelfbescherming\n🙂|rust,kalm,stil,zacht,ademen,rustig,ontspannen,geduldig,vertrouwd,ontspanning,zelfbeheersing,ademhalingsoefening\n😌|trots,dapper,sterk,durven,winnen,trotsheid,moedig,zelfstandig,zelfvertrouwen,doorzetter,doorzettingsvermogen,zelfwaardering\n🤩|nieuw,raar,wauw,vragen,kijken,verrast,benieuwd,nieuwsgierig,verwondering,belangstelling,onderzoekshouding,ontdekkingsvreugde",
people:"👨‍👩‍👧|familie,gezin,kind,baby,ouders,gezinnetje,familielid,stiefouder,grootouder,familieband,gezinssituatie,familiegeschiedenis\n👩|mama,moeder,vrouw,tante,dochter,moedertje,schoonmoeder,moederdag,moederfiguur,moederschap,ouderverzorging,gezinsverantwoordelijkheid\n👨|papa,vader,man,oom,zoon,vadertje,schoonvader,vaderdag,vaderfiguur,vaderschap,oudercontact,opvoedingsverantwoordelijkheid\n🧒|kind,jongen,meisje,kleuter,puber,kindje,schoolkind,leeftijdgenoot,jeugd,ontwikkeling,kinderrechten,identiteitsontwikkeling\n🧑🤝🧑|vriend,maat,buur,gast,club,vriendje,vriendschap,buurkind,kennismaking,groepsgenoot,vertrouwenspersoon,vriendschapsrelatie\n🧑🏫|juf,meester,leraar,mentor,coach,leerkracht,lesgever,schoolteam,begeleider,onderwijsassistent,onderwijsprofessional,leerlingbegeleiding\n👥|groep,team,klas,paar,kring,groepje,klasgenoot,teamlid,samenkomst,groepswerk,groepsvorming,groepsdynamiek",
play:"⚽|bal,doel,trap,team,veld,balletje,voetbal,doelman,wedstrijd,voetballer,voetbaltraining,competitiewedstrijd\n🏀|basket,korf,worp,score,punt,gooien,basketbal,dribbelen,balbezit,teamsport,sportvereniging,wedstrijdreglement\n🧩|puzzel,stuk,vorm,rand,plaat,puzzelstuk,legpuzzel,denkspel,oplossing,puzzelboek,probleemoplossing,denkstrategie\n🧸|pop,beer,blok,trein,auto,popje,knuffel,bouwblok,speelgoed,speelkamer,speelgoedwinkel,fantasiespel\n🎲|spel,beurt,dobbelsteen,kaart,pion,spelletje,spelregel,bordspel,tegenstander,winnaar,spelstrategie,gezelschapsspel\n🏊|zwemmen,duiken,bad,plons,slag,zwemband,zwembad,schoolslag,duikplank,zwemles,zwemvaardigheid,waterveiligheid\n🏅|sport,spel,prijs,beker,medaille,sporter,sportdag,training,kampioen,sportprestatie,sportiviteit,wedstrijdorganisatie",
places:"🏫|school,klas,plein,les,gym,schooltje,schoolbank,schoolplein,schoolgebouw,schooljaar,schoolorganisatie,onderwijsomgeving\n🏪|winkel,kassa,kar,mand,klant,winkeltje,winkelier,winkelstraat,boodschap,winkelcentrum,klantenservice,detailhandel\n🏥|arts,dokter,bed,zaal,zorg,ziekenhuis,verpleegkundige,wachtkamer,behandeling,zorgverlening,gezondheidszorg,ziekenhuisafdeling\n🏡|boer,stal,schuur,land,erf,boerderij,landbouwer,akkerbouw,boerenbedrijf,landbouwgrond,landbouwmachine,voedselproductie\n📚|boek,kast,balie,pas,lezer,bibliotheek,boekenkast,bibliothecaris,uitleentermijn,boekenafdeling,informatiedienst,mediatheekbeheer\n🏙|stad,dorp,straat,plein,buurt,stadje,dorpsplein,woonplaats,stadscentrum,gemeentehuis,stedelijkgebied,bevolkingsdichtheid\n🏞|park,bos,veld,pad,vijver,parkje,speelweide,natuurpark,wandelroute,recreatiegebied,landschapsbeheer,natuurvoorziening",
time:"☀️|dag,uur,tijd,klok,middag,dagje,daglicht,dagdeel,dagritme,tijdstip,dagindeling,tijdswaarneming\n🌅|ochtend,vroeg,wakker,ontbijt,zonsopgang,ochtendlicht,ochtendritueel,schoolochtend,dagbegin,ochtendspits,ochtendprogramma,ochtendactiviteit\n🌇|avond,laat,eten,donker,zonsondergang,avondlicht,avondeten,bedtijd,dagafsluiting,avondprogramma,avondschemering,avondvoorstelling\n🗓|week,maand,jaar,datum,agenda,weekje,weekdag,maandblad,jaartal,kalenderjaar,tijdrekening,kalenderindeling\n🎂|feest,taart,kaars,cadeau,ballon,feestje,verjaardag,feestmuts,feestdag,uitnodiging,feestversiering,verjaardagsviering\n🍂|lente,zomer,herfst,winter,seizoen,lentebloem,zomerdag,herfstblad,winterjas,seizoenswissel,jaargetijde,seizoensverschijnsel\n⏱|minuut,seconde,kwart,half,duur,minuutje,tijdsduur,stopwatch,kwartier,wachttijd,tijdseenheid,tijdsberekening",
arts:"🎨|kleur,verf,kwast,doek,palet,kleurtje,verfpot,schilderen,schilderij,kunstschilder,kleurgebruik,schildertechniek\n🖍|tekening,lijn,vorm,punt,vak,tekenen,kleurplaat,schetsboek,omtrek,tekenmateriaal,beeldverhaal,illustratietechniek\n🎵|toon,lied,stem,klank,maat,liedje,muziek,nootbalk,melodie,muzikant,muziekstuk,muziekuitvoering\n🥁|trom,fluit,snaar,toets,bel,trommel,instrument,gitaarspel,slagwerk,muziekles,muziekinstrument,instrumentenfamilie\n💃|dans,stap,sprong,draai,pas,dansje,dansvloer,dansgroep,choreografie,dansvoorstelling,bewegingspatroon,podiumkunst\n🎭|toneel,rol,pak,tekst,podium,toneelstuk,acteur,optreden,voorstelling,toneelspeler,podiumverlichting,theatervoorstelling\n📷|foto,film,beeld,lens,flits,fotootje,camera,fotoboek,filmopname,fotograaf,beeldbewerking,mediaproductie",
science:"🔍|kijken,zoeken,vraag,vondst,spoor,onderzoek,waarnemen,vergrootglas,ontdekking,onderzoeker,waarneming,onderzoeksresultaat\n🧪|proef,test,buis,stof,mengen,proefje,reactie,meetglas,experiment,laboratorium,proefopstelling,onderzoeksverslag\n🧲|magneet,ijzer,pool,kracht,veld,aantrekken,afstoten,magnetisch,kompasnaald,magneetkracht,magnetisme,natuurverschijnsel\n⚙|wiel,as,tand,veer,hefboom,tandwiel,overbrenging,mechaniek,machine,werktuig,bewegingsenergie,machinewerking\n💡|licht,stroom,draad,lamp,stekker,schakelaar,batterij,stroomkring,elektrisch,elektriciteit,energieverbruik,energievoorziening\n💻|computer,muis,toets,scherm,bestand,computerprogramma,toetsenbord,beeldscherm,digitaal,internet,programmeren,informatietechnologie\n🚀|ruimte,maan,planeet,ster,raket,ruimtevaart,satelliet,heelal,astronaut,zonnestelsel,ruimtestation,ruimteonderzoek",
learning:"🔤|letter,klank,woord,naam,teken,alfabet,lettergreep,klinker,medeklinker,lettervolgorde,klankstructuur,spellingspatroon\n📝|zin,punt,komma,vraag,tekst,zinnetje,hoofdletter,leesteken,zinsdeel,zinsbouw,tekstverband,taalbeschouwing\n📖|boek,blad,kaft,titel,prent,boekje,hoofdstuk,inhoudsopgave,schrijver,boekbespreking,literatuur,leeservaring\n🗣|taal,stem,spraak,gesprek,verhaal,vertellen,luisteraar,spreker,spreekbeurt,gespreksregel,taalgebruik,communicatievaardigheid\n🔢|nul,een,twee,drie,getal,cijfer,reeks,getallenlijn,getalbeeld,plaatswaarde,getalstructuur,getalnotatie\n|som,plus,min,keer,delen,optellen,aftrekken,uitkomst,bewerking,rekenregel,rekenstrategie,bewerkingsvolgorde\n📐|vorm,lijn,hoek,vlak,lengte,driehoek,rechthoek,meetlat,oppervlakte,omtrekberekening,meetinstrument,meetkundigevorm",
society:"🤝|samen,hulp,beurt,afspraak,regel,samenwerken,overleggen,taakverdeling,deelname,samenleving,samenwerkingsverband,maatschappijleer\n🏘|buurt,wijk,dorp,stad,straat,buurthuis,woonwijk,gemeente,provincie,stadsbestuur,gemeenteraad,gebiedsindeling\n🌍|wereld,land,zee,kaart,grens,wereldbol,landkaart,continent,werelddeel,bevolking,wereldbevolking,wereldburgerschap\n🗺|noord,zuid,oost,west,plek,richting,kompas,kaartteken,plattegrond,schaalverdeling,herkenningspunt,kaartvaardigheid\n📰|nieuws,krant,bericht,radio,media,nieuwsblad,journalist,nieuwsbron,actualiteit,nieuwsbericht,betrouwbaarheid,mediageletterdheid\n♻|afval,bak,glas,papier,natuur,recyclen,hergebruik,afvalbak,milieuzorg,afvalscheiding,duurzaamheid,grondstoffengebruik\n⚖|recht,plicht,keus,stem,wet,eerlijk,verkiezing,mening,besluit,democratie,grondrecht,besluitvorming"
};
const LANGUAGE_PROGRESSION_YEARS=[1,2,3,3,4,5,5,6,7,8,7,8];
const LANGUAGE_NL_PROGRESSION_WORDS=(()=>{
const seen=new Set(LANGUAGE_CONCEPTS.map(item=>item.nl)),out=[];
Object.entries(LANGUAGE_NL_PROGRESSION_FAMILIES).forEach(([theme,rows])=>rows.split("\n").forEach(row=>{
const [pic,list]=row.split("|");let familyPosition=0;
list.split(",").forEach(raw=>{
const word=raw.trim();if(!word||seen.has(word))return;seen.add(word);
const slug=word.normalize("NFD").replace(/[\u0300-\u036f]/g,"").replace(/[^a-z]/g,"");
const yearGroup=LANGUAGE_PROGRESSION_YEARS[Math.min(familyPosition++,LANGUAGE_PROGRESSION_YEARS.length-1)];
out.push({id:"nl-"+theme+"-"+slug,theme,nl:word,pic,yearGroup});
});
}));
return out
})();
/* De vertrouwde startkaart blijft exact dezelfde 30 woorden houden. */
const START_WORD_KEYS = {
nl:["monkey","fish","sun","cat","ball","egg","tree","house","moon","boat","hen","mouse","book","star","rose","fire","bike","cake","coat","glasses","clock","pear","cheese","duck","hand","foot","nose","eye","ear","mouth"],
@ -233,12 +462,16 @@ const WORDS = Object.fromEntries(["nl","en"].map(lang=>[
})
]));
function languageBuiltinWords(lang=LANG){
return lang==="nl"?LANGUAGE_CONCEPTS.concat(LANGUAGE_NL_PROGRESSION_WORDS):LANGUAGE_CONCEPTS
}
function languageThemeList(lang=LANG){
return LANGUAGE_THEME_DEFS.map(theme=>({
...theme,
name:theme[lang],
words:LANGUAGE_CONCEPTS.filter(item=>item.theme===theme.id).map(item=>[item[lang],item.pic])
}));
words:languageBuiltinWords(lang).filter(item=>item.theme===theme.id)
.map(item=>[item[lang]||item.nl,item.pic,item.yearGroup||null])
})).filter(theme=>theme.words.length);
}
function languageEmojiDataUrl(emoji,colors=["#f4f8ff","#dce9fa"]){
const svg=`<svg xmlns="http://www.w3.org/2000/svg" width="320" height="320" viewBox="0 0 320 320"><defs><linearGradient id="g" x2="1" y2="1"><stop stop-color="${colors[0]}"/><stop offset="1" stop-color="${colors[1]}"/></linearGradient></defs><rect width="320" height="320" rx="48" fill="url(#g)"/><circle cx="160" cy="160" r="112" fill="#fff" opacity=".92"/><text x="160" y="177" text-anchor="middle" dominant-baseline="middle" font-family="Apple Color Emoji,Segoe UI Emoji,Noto Color Emoji,sans-serif" font-size="148">${emoji}</text></svg>`;
@ -250,13 +483,14 @@ function languagePictureCatalog(lang=LANG){
languageBuiltin:true,wordPicture:true,icon:theme.icon
}));
const themeIds=new Map(LANGUAGE_THEME_DEFS.map((theme,index)=>[theme.id,-200-index]));
const images=LANGUAGE_CONCEPTS.map((item,index)=>{
const images=languageBuiltinWords(lang).map((item,index)=>{
const theme=LANGUAGE_THEME_DEFS.find(def=>def.id===item.theme);
const name=item[lang]||item.nl;
return {
id:-1000-index,name:item[lang],folder:`Taal/Woordenschat/${theme[lang]}`,
scope:"global",themes:[themeIds.get(item.theme)],tags:[item.nl,item.en,theme.nl,theme.en],
id:-1000-index,name,folder:`Taal/Woordenschat/${theme[lang]}`,
scope:"global",themes:[themeIds.get(item.theme)],tags:[item.nl,item.en,theme.nl,theme.en].filter(Boolean),
src:languageEmojiDataUrl(item.pic,theme.colors),value:item.pic,canManage:false,canDelete:false,
wordPicture:true,builtin:true
wordPicture:true,builtin:true,yearGroup:item.yearGroup||null
};
});
const ownThemes=[];

View file

@ -76,10 +76,12 @@ function languageWordComplexity(word){
function languageWordsForYear(words,yearGroup,level){
if(!Array.isArray(words)||!words.length)return[];
const year=learningYearGroup(yearGroup);if(!year)return words;
const introduced=words.filter(item=>!Array.isArray(item)||item[2]==null||Number(item[2])<=year);
const available=introduced.length?introduced:words;
const caps=[0,5,7,9,12,15,18,22,99],cap=caps[year]+(learningLevel(level)-2)*2;
const filtered=words.filter(item=>languageWordComplexity(Array.isArray(item)?item[0]:item)<=cap);
if(filtered.length>=Math.min(4,words.length))return filtered;
return [...words].sort((a,b)=>languageWordComplexity(a[0])-languageWordComplexity(b[0])).slice(0,Math.min(4,words.length))
const filtered=available.filter(item=>languageWordComplexity(Array.isArray(item)?item[0]:item)<=cap);
if(filtered.length>=Math.min(4,available.length))return filtered;
return [...available].sort((a,b)=>languageWordComplexity(a[0])-languageWordComplexity(b[0])).slice(0,Math.min(4,available.length))
}
function languageTaskRules(yearGroup,level){
const year=learningYearGroup(yearGroup);if(!year)return null;const l=learningLevel(level);

View file

@ -9,6 +9,7 @@ async function languageData(extra=''){
runInNewContext(source+`; ${extra}
globalThis.__language={
concepts:LANGUAGE_CONCEPTS,
progression:LANGUAGE_NL_PROGRESSION_WORDS,
themesNl:languageThemeList("nl"),
themesEn:languageThemeList("en"),
words:WORDS,
@ -17,20 +18,34 @@ async function languageData(extra=''){
return JSON.parse(JSON.stringify(context.__language));
}
test('taalwidgets delen een ruime tweetalige woordenschat in acht duidelijke themas',async()=>{
test('taalwidgets delen ruim duizend extra Nederlandse woorden over leerjaar en thema',async()=>{
const data=await languageData();
assert.ok(data.concepts.length>=140,'verwacht minimaal 140 vaste woordplaatjes');
assert.equal(data.themesNl.length,8);
assert.equal(data.themesEn.length,8);
assert.equal(new Set(data.concepts.map(item=>item.id)).size,data.concepts.length);
assert.ok(data.concepts.length>=300,'verwacht minimaal 300 vaste woordplaatjes');
assert.ok(data.progression.length>=1000,'verwacht minimaal 1000 aanvullende Nederlandse woorden');
assert.equal(data.themesNl.length,16);
assert.equal(data.themesEn.length,12);
assert.deepEqual(data.themesNl.map(theme=>theme.id),[
'animals','food','nature','travel','home','body','actions','feelings',
'people','play','places','time','arts','science','learning','society'
]);
const all=data.concepts.concat(data.progression);
assert.equal(new Set(all.map(item=>item.id)).size,all.length);
assert.equal(new Set(all.map(item=>item.nl)).size,all.length);
for(const concept of data.concepts){
assert.ok(concept.nl&&concept.en&&concept.pic,concept.id);
assert.ok(data.themesNl.some(theme=>theme.id===concept.theme),concept.theme);
}
for(const themes of [data.themesNl,data.themesEn]){
for(const theme of themes)assert.ok(theme.words.length>=10,theme.id);
assert.equal(themes.reduce((sum,theme)=>sum+theme.words.length,0),data.concepts.length);
for(const word of data.progression){
assert.ok(word.nl&&word.pic,word.id);
assert.ok(word.yearGroup>=1&&word.yearGroup<=8,word.nl);
assert.ok(data.themesNl.some(theme=>theme.id===word.theme),word.theme);
}
for(let year=1;year<=8;year++)
assert.ok(data.progression.filter(word=>word.yearGroup===year).length>=60,'te weinig nieuwe woorden voor groep '+year);
for(const theme of data.themesNl)assert.ok(theme.words.length>=60,theme.id);
for(const theme of data.themesEn)assert.ok(theme.words.length>=18,theme.id);
assert.equal(data.themesNl.reduce((sum,theme)=>sum+theme.words.length,0),all.length);
assert.equal(data.themesEn.reduce((sum,theme)=>sum+theme.words.length,0),data.concepts.length);
});
test('alle oude startwoorden blijven staan en krijgen ook een bibliotheekplaatje',async()=>{
@ -46,19 +61,23 @@ test('alle oude startwoorden blijven staan en krijgen ook een bibliotheekplaatje
test('vaste en eigen woordplaatjes worden als herbruikbare catalogusitems aangeboden',async()=>{
const data=await languageData('GENERAL_EXTRA.nl=[["test","🧪"]]; THEMES.nl=[{name:"Boerderij",folder:"School",words:[["stal","🏚️"]]}];');
const builtins=data.catalogNl.images.filter(image=>image.builtin);
assert.equal(builtins.length,data.concepts.length);
assert.equal(data.catalogNl.themes.filter(theme=>theme.languageBuiltin).length,8);
assert.equal(builtins.length,data.concepts.length+data.progression.length);
assert.equal(data.catalogNl.themes.filter(theme=>theme.languageBuiltin).length,16);
for(const image of builtins){
assert.ok(image.id<0);
assert.ok(image.src.startsWith('data:image/svg+xml;charset=utf-8,'));
assert.ok(image.value&&image.themes.length===1);
assert.equal(image.canManage,false);
}
const testPic=data.catalogNl.images.find(image=>image.name==='test');
const stablePic=data.catalogNl.images.find(image=>image.name==='stal');
const testPic=data.catalogNl.images.find(image=>!image.builtin&&image.name==='test');
const stablePic=data.catalogNl.images.find(image=>!image.builtin&&image.name==='stal');
assert.equal(testPic.value,'🧪');
assert.equal(stablePic.value,'🏚️');
assert.equal(stablePic.folder,'Taal/Mijn themas/School/Boerderij');
for(const word of ['familiegeschiedenis','competitiewedstrijd','mediageletterdheid','waterkringloop']){
const image=data.catalogNl.images.find(item=>item.name===word);
assert.ok(image&&image.yearGroup>=7&&image.src.startsWith('data:image/svg+xml'),word);
}
});
test('alle vier taalspellen gebruiken themabronnen en kunnen de bibliotheek openen',async()=>{

View file

@ -52,12 +52,14 @@ test('alleen leerkracht en systeemmanager kunnen het leerjaar op hun eigen bord
test('taalprofiel selecteert eenvoudiger woorden in de onderbouw en bouwt taaksteun af',async()=>{
const c=await progressionContext();
const words=[['kat','x'],['vis','x'],['pen','x'],['maan','x'],['boek','x'],['fiets','x'],
['brandweerauto','x'],['onderzoeker','x'],['verantwoordelijkheid','x']];
const words=[['kat','x',1],['vis','x',1],['pen','x',1],['maan','x',2],['boek','x',2],['fiets','x',3],
['brandweerauto','x',4],['onderzoeker','x',6],['verantwoordelijkheid','x',8]];
const early=JSON.parse(JSON.stringify(c.__languageWordsForYear(words,2,2)));
const late=JSON.parse(JSON.stringify(c.__languageWordsForYear(words,8,2)));
assert.ok(early.length<late.length);
assert.ok(early.every(([word])=>!['brandweerauto','verantwoordelijkheid'].includes(word)));
assert.ok(early.every(([, ,introduced])=>introduced<=2));
assert.ok(late.some(([word])=>word==='verantwoordelijkheid'));
assert.equal(c.__languageTaskRules(2,2).flashSeconds,3);
assert.equal(c.__languageTaskRules(7,2).flashSeconds,.5);
assert.ok(c.__languageTaskRules(2,2).lives>c.__languageTaskRules(7,2).lives);