add language learner project
Some checks failed
Test and deploy staging / staging (push) Has been cancelled

Dutch and Korean (explained in English) and English (explained in Dutch)
from A1 to C1, with a Hangul level for Korean. Lessons with notes, words
and sentences generate multiple-choice, typing, sentence building, cloze,
listening and matching exercises. Endless challenges: a seeded daily
challenge, sprint, survival, mixed practice, level tests, spaced-repetition
reviews and generated drills (numbers, clock times, de/het, plurals, Dutch
and Korean conjugation, particles, Hangul reading, irregular verbs).
Progress is stored in localStorage with export/import.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-24 17:34:56 +00:00
parent afb1f191df
commit 438623d857
69 changed files with 12669 additions and 4 deletions

View File

@@ -0,0 +1,40 @@
import { describe, expect, it } from 'vitest';
import { compose, decompose, particle, romanize } from './hangul';
describe('hangul', () => {
it('composes and decomposes syllables', () => {
expect(decompose('한')).toEqual({ initial: 'ㅎ', medial: 'ㅏ', final: 'ㄴ' });
expect(compose({ initial: 'ㄱ', medial: 'ㅡ', final: 'ㄹ' })).toBe('글');
});
it.each([
['안녕하세요', 'annyeonghaseyo'],
['감사합니다', 'gamsahamnida'],
['한국어', 'hangugeo'],
['맛있어요', 'masisseoyo'],
['읽어요', 'ilgeoyo'],
['많이', 'mani'],
['괜찮아요', 'gwaenchanayo'],
['없어요', 'eopseoyo'],
['같이', 'gachi'],
['좋다', 'jota'],
['신라', 'silla'],
['설날', 'seollal'],
['종로', 'jongno'],
['라면', 'ramyeon'],
['서울 사람', 'seoul saram'],
['의사', 'uisa']
])('romanizes %s as %s', (hangul, roman) => {
expect(romanize(hangul)).toBe(roman);
});
it('chooses particles by the final consonant', () => {
expect(particle('책', 'object')).toBe('책을');
expect(particle('사과', 'object')).toBe('사과를');
expect(particle('학생', 'topic')).toBe('학생은');
expect(particle('저', 'topic')).toBe('저는');
expect(particle('서울', 'direction')).toBe('서울로');
expect(particle('집', 'direction')).toBe('집으로');
expect(particle('의사', 'copula')).toBe('의사예요');
});
});