A Website to Practice Danish, Building My Own Language Learning Tool

Published on
A Website to Practice Danish, Building My Own Language Learning Tool

Moving to Denmark meant one thing was crystal clear: I needed to learn Danish if I wanted to do more than just point at menu items and hope for the best. While joining a Danish language school was the logical first step, I quickly discovered that traditional learning methods and my programmer brain don’t always see eye to eye.

The Paper Problem

My Danish teacher handed me a printout of the 500 most commonly used Danish verbs. As someone who spends their days staring at screens, wrestling with paper-based practice felt like trying to debug code with a typewriter. Sure, Duolingo was already part of my daily routine (having 600+ days streak), but I wanted something specifically tailored to this verb list.

500 most common verbs in danish

Finding the Source

After some digital detective work, I traced the original source to basby.dk – a Danish language resource that generously allows data usage. Props to them for supporting fellow language learners! The website contained the exact verb list I needed, complete with all the conjugations that make Danish… well, Danish.

Basby website's verb list

Data Extraction: When You Need a Program to Write a Program

Rather than manually copying 500 verbs, I wrote a quick data extraction script. Sometimes the best solution to a tedious problem is a few lines of JavaScript:

verb list in html page

The whole list is in a simple HTML table, so extracting the data can be done with a simple javascript.

(function () {
	const rows = Array.from(document.getElementsByTagName('table')[4].rows);
	const data = [];

	for (r = 1; r < rows.length; r++) {
		const tr = rows[r];
		const nameEle = tr.cells[0].querySelectorAll('[href]:not([href=""])')[0];
		const d = {
			navnem: nameEle.innerText.replaceAll('\n', ' '),
			audio: nameEle.href,
			nutid: tr.cells[1].innerText.replaceAll('\n', ' '),
			datid: tr.cells[2].innerText.replaceAll('\n', ' '),
			førnutid: tr.cells[3].innerText.replaceAll('\n', ' '),
			førdatid: tr.cells[4].innerText.replaceAll('\n', ' '),
			bydem: tr.cells[5].innerText.replaceAll('\n', ' '),
			bøj: tr.cells[6].innerText.replaceAll('\n', ' ')
		};

		data.push(d);
	}

	console.log(JSON.stringify(data));
})();

The script extracts each verb’s

  1. infinitive form (navnem)
  2. present tense (nutid)
  3. past tense (datid)
  4. present perfect (førnutid)
  5. past perfect (førdatid)
  6. imperative (bydem)
  7. and conjugation pattern (bøj)

But here’s the kicker – the website also included audio pronunciations for each verb. As someone still struggling with the Danish “stød” (that glottal stop that makes Danish sound like everyone’s clearing their throat), this was invaluable.

Audio Asset Pipeline

Getting the audio files required another script – because why download 500 files manually when you can automate it?

Below is the NodeJs script to download the audio file and save locally.

import { createWriteStream } from 'fs';
import { Readable } from 'stream';
import data from './danish-verbs.json' with { type: 'json' }; //downloaded verb list from above script

const dataWithFileName = [];

for (let i = 0; i < data.length; i++) {
	const v = data[i];

	// remove slashes in the navem to use that as file name
	const fileName = v.navnem.replaceAll('/', '-').replaceAll('\\', '-').replaceAll(' ', '-');

	console.log(`${i} - ${v.navnem} - file: ${fileName}`); //just to see the download progress
	await download(fileName, v.audio);

	const dWithFileName = { ...v, audioFile: `${fileName}.mp3` };
	dataWithFileName.push(dWithFileName);
}

//save new array in file
const newData = JSON.stringify(dataWithFileName);
var writeStream = createWriteStream('danish-verbs-with-audio-link.json');
writeStream.write(newData);
writeStream.end();

console.log('-------end');

async function download(fileName, url) {
	const resp = await fetch(url);

	if (resp.ok && resp.body) {
		let writer = createWriteStream(`./verb-audio/${fileName}.mp3`);
		Readable.fromWeb(resp.body).pipe(writer);
	}
}

This script sanitizes filenames, downloads each audio file, and updates the JSON data with local file references.

Building the Quiz Application

With data in hand, I built a quiz application using Next.js and deployed it on Cloudflare Pages. The result? A personalized Danish verb practice tool available at learndanish.mkumaran.net.

Features

The application includes several customization options that make practice more targeted and less monotonous:

quiz can be customized

Question Types

  • Fill-in-the-blank questions for active recall
  • Multiple choice questions for recognition practice

question types

Question Accessibility

  • Text-based questions for visual learners
  • Audio-based questions for pronunciation practice

Conversion Modes

  • Danish to English translation
  • English to Danish translation

Verb Form Focus

  • Practice specific conjugations (navnem, nutid, datid, førnutid, førdatid)
  • Mix and match different verb forms

These options allow users to create focused practice sessions. For instance, you might want to focus solely on past tense audio questions, or challenge yourself with fill-in-the-blank exercises for present perfect forms.

The Technical Stack

I used several modern web technologies:

  • Next.js for the React-based frontend framework
  • Cloudflare Pages for fast, global deployment
  • Local JSON data for offline-capable verb storage
  • HTML5 Audio API for pronunciation playback
  • ShadCN components for mobile-friendly UI

What’s Next?

Learning Danish verbs is just the beginning. The application architecture is designed to accommodate additional language learning modules:

Planned Features:

  • Danish Numbers: Practice counting, ordinals, and mathematical expressions
  • Time and Clocks: Master telling time in Danish
  • Days of the Week: Learn weekday vocabulary with pronunciation
  • Months and Dates: Calendar-related vocabulary practice
  • Common Phrases: Everyday expressions and greetings
  • Images: Pick correct danish name by seeing the image

Why Build Your Own Learning Tool?

While commercial language learning apps are excellent, building a custom tool offers several advantages:

  1. Targeted Practice: Focus on exactly what you need to learn (in this tool, I wanted to practice only the 500 most common used danish verbs)
  2. Personalization: Adapt difficulty and question types to your learning style
  3. Offline Capability: Practice without internet connectivity
  4. No Ads or Subscriptions: Complete control over the learning experience
  5. Technical Learning: Improve programming skills while learning a language (I always do this)

Conclusion

What started as frustration with paper-based verb practice evolved into a comprehensive language learning tool. The combination of automated data extraction, modern web technologies, and personalized features created something more valuable than the sum of its parts.

For fellow developers learning new languages, consider building your own practice tools. The technical skills you already possess can significantly enhance your language learning journey – and you might just create something useful for others facing the same challenges.

Now, if you’ll excuse me, I need to get back to figuring out why Danish has three words for “the” and when to use each one. Some problems, it seems, require more than just good code to solve.