From ba4483d5d332f36a42a937625a0086bbb96468e4 Mon Sep 17 00:00:00 2001 From: Ramon Date: Tue, 21 Jul 2026 18:50:47 +0200 Subject: [PATCH] feat: breid taalbibliotheek uit met 1000 woorden (v0.4.43-beta) --- VERSION | 2 +- public/js/core.js | 2 +- public/js/data.js | 250 ++++++++++++++++++++++++++++- public/js/learning-progressions.js | 8 +- test/language-library.test.js | 43 +++-- test/learning-progressions.test.js | 6 +- 6 files changed, 284 insertions(+), 27 deletions(-) diff --git a/VERSION b/VERSION index 177aff4..22314b3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.4.42-beta +0.4.43-beta diff --git a/public/js/core.js b/public/js/core.js index 06af6e6..bab0a9c 100644 --- a/public/js/core.js +++ b/public/js/core.js @@ -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; diff --git a/public/js/data.js b/public/js/data.js index 01bca88..4b24da1 100644 --- a/public/js/data.js +++ b/public/js/data.js @@ -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=`${emoji}`; @@ -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=[]; diff --git a/public/js/learning-progressions.js b/public/js/learning-progressions.js index 277e030..090b16c 100644 --- a/public/js/learning-progressions.js +++ b/public/js/learning-progressions.js @@ -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); diff --git a/test/language-library.test.js b/test/language-library.test.js index ae3046d..b8a8ba2 100644 --- a/test/language-library.test.js +++ b/test/language-library.test.js @@ -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 themaโ€™s',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 themaโ€™s/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()=>{ diff --git a/test/learning-progressions.test.js b/test/learning-progressions.test.js index 88faadf..46b2c36 100644 --- a/test/learning-progressions.test.js +++ b/test/learning-progressions.test.js @@ -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!['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);