Product & Technical Specification: Sproochentest Vocabulary Engine

Product & Technical Specification: Sproochentest Vocabulary Engine

Target Path: /sproochentest/vocab/
Version: 1.0
Last Updated: 2026-05-31


1. Executive Summary

This module adds a gamified, interactive vocabulary acquisition engine to the existing Sproochentest platform at hasanjaffal.com. The engine trains users to recall Luxembourgish vocabulary under time pressure (sub-2.5 seconds), directly targeting the conversational freezing problem that causes exam failure.

The system uses a Leitner-box spaced repetition algorithm, three progressive learning layers (recognition → production → speed recall), and behavioral gamification (XP, streaks, daily decks) to drive daily return visits.


2. Business Requirements

2.1 Target User

Expats in Luxembourg preparing for the Sproochentest oral exam who need structured, high-frequency vocabulary practice rather than generic word lists.

2.2 Success Metrics

| Metric | Target | |——–|——–| | Daily Active Users (DAU) | Measurable via streak data | | Session completion rate | >70% of started decks finished | | Conversion to registered user | >30% of trial users register | | Return rate (D7) | >40% of registered users active after 7 days |

2.3 Conversion Model

  • Free tier (guest): 10-word trial per category, localStorage-only progress, no streaks/XP persistence.
  • Registered tier: Full access to all categories, spaced repetition tracking, streaks, XP history, audio playback.
  • Registration uses Firebase Authentication (email/password + Google sign-in). User does not have to pay to registered.

3. Architecture & Technical Constraints

3.1 Frontend

  • Jekyll static page at /sproochentest/vocab/
  • Uses existing base.html layout → sproochentest-header.html / sproochentest-footer.html
  • Page-specific CSS: assets/css/vocab.css (loaded via front matter css: array)
  • Page-specific JS: assets/js/vocab-engine.js (loaded at bottom of page)
  • Firebase Auth SDK loaded only on this page (not globally)

3.2 Backend

  • Google Cloud Functions (Node.js 20), same project as existing functions (hasanjaffal)
  • Firestore for user progress, SRS state, XP ledger, streak data
  • New functions module: functions/vocab/
  • Function URLs added to _data/firebase.yml

3.3 Hard Constraints (from project steering)

  • No Firestore usage for the Future-Proof Skills Assessment (this is a separate module — Firestore IS allowed for vocab)
  • Firebase client code stays isolated to the vocab page
  • No secrets in frontend code
  • No @import url() in CSS files
  • No inline styles
  • Existing URLs must not break
  • No global CSS/JS bloat — vocab assets load only on /sproochentest/vocab/

3.4 Data Storage

Data Location Reason
Vocabulary content (words, translations, categories) _data/vocab/ YAML files Static, version-controlled, no backend needed
Guest session progress localStorage No auth required
Authenticated user SRS state Firestore users/{uid}/vocab/{wordId} Persistent across devices
XP ledger Firestore users/{uid}/xp Server-validated
Streak data Firestore users/{uid}/streaks Server-validated

4. Vocabulary Content Structure

4.1 Content Source

All vocabulary is sourced from a curated master word list (52 sections, ~1500+ entries). Categories follow the source document’s own structure — NOT the speaking topics. The source organizes words into:

  • Foundation modules — core words, grammar building blocks, pronouns, articles
  • Functional modules — fillers, connectors, sentence launchers, question words
  • Thematic modules — exam-relevant topics (Sport, Wunnen, Transport, Hobbyen, Vakanz, Gesondheet, Medien, Sproochen, Kaddoen, Summer/Wanter, Aarbecht)
  • Grammar reference — verb conjugations, declensions, prepositions, past participles
  • Cultural module — truly Luxembourgish idioms and colloquial expressions

4.2 Categories

Categories are grouped into three tiers by learning progression:

Tier 1: Foundations (unlock first)

Category Slug Source Sections Display Name Entry Count
essential-words §1 Essential Words 44
articles-pronouns §2, §4, §17, §18, §33, §35, §37, §38, §40, §47 Articles & Pronouns ~80
starter-verbs §5, §6 Core Verbs ~50
introductions §7 Introduce Yourself ~45
numbers-dates §3, §8, §14 Numbers, Dates & Time ~100

Tier 2: Exam Building Blocks (unlock after Tier 1 progress)

Category Slug Source Sections Display Name Entry Count
fillers-connectors §10, §11, §13 Fillers, Connectors & Sentence Launchers ~80
question-words §21 Question Words ~13
locators §12 Locators & Spatial Words ~14
describe-objects §19 Describing Objects ~60
describe-people §24 Describing People ~55
adjective-declension §20, §45 Adjective Forms ~20
prepositions §16 Prepositions ~35

Tier 3: Thematic Modules (exam topics)

Category Slug Source Sections Display Name Entry Count
sport §9 Sport ~65
wunnen §15 Wunnen (Housing) ~55
transport §23 Transport ~75
hobbyen §27 Hobbyen (Hobbies) ~70
vakanz §31, §34 Vakanz (Holidays & Beach) ~80
gesondheet §36, §39 Gesond Liewen (Health & Market) ~75
medien-technologien §46, §48 Medien & Technologien ~65
sproochen §42, §44 Sproochen & Léieren ~60
kaddoen §30 Kaddoen (Gifts) ~30
summer-wanter §49 Summer & Wanter ~55
aarbecht §50 Meng Aarbecht (Work) ~65
stot-maachen §29 De Stot Maachen (Housework) ~25
family §22, §41 Family & Relations ~65

Tier 4: Advanced & Cultural

Category Slug Source Sections Display Name Entry Count
frequent-verbs §28 Frequent Verbs ~130
irregular-verbs §25 Verbs with Irregularities ~45
simple-past §32, §52 Past Tenses ~50
truly-luxembourgish §51 Truly Luxembourgish (Idioms & Slang) ~180

4.3 Word Data Schema (_data/vocab/{category}.yml)

Each entry type has a slightly different schema depending on its nature:

Standard word entry

- id: "sport_001"
  lb: "Sport maachen"
  en: "to exercise"
  pos: "verb-phrase"        # noun, verb, adjective, adverb, phrase, verb-phrase, connector
  difficulty: 1             # 1 = beginner, 2 = intermediate, 3 = advanced
  example_lb: "Ech maache gär Sport."
  example_en: "I like to exercise."

Noun entry (with gender/plural)

- id: "wunnen_003"
  lb: "d'Appartement"
  en: "flat, apartment"
  pos: "noun"
  gender: "n"              # m, f, n
  plural: "Appartementer"
  difficulty: 1
  example_lb: "Mir wunnen an engem Appartement."
  example_en: "We live in an apartment."

Verb entry (with conjugation hint)

- id: "starter-verbs_005"
  lb: "fueren"
  en: "to drive/ride"
  pos: "verb"
  conjugation: "Ech fueren, du fiers, hien/si fiert"
  irregular: true
  difficulty: 1
  example_lb: "Ech fuere mam Bus op d'Aarbecht."
  example_en: "I take the bus to work."

Phrase/expression entry

- id: "fillers_002"
  lb: "Majo et ass esou, ..."
  en: "Well, in fact ..."
  pos: "filler"
  difficulty: 1
  usage_note: "Use to introduce a factual statement or explanation in conversation."

4.4 Content Rules

  • All vocabulary is sourced from the curated master list — no AI-generated words
  • Each entry must have: id, lb, en, pos, difficulty
  • Example sentences come directly from the source document where available
  • IDs follow pattern: {category-slug}_{three-digit-number}
  • Difficulty is assigned based on tier: Tier 1 = difficulty 1, Tier 2 = difficulty 1–2, Tier 3 = difficulty 2, Tier 4 = difficulty 2–3
  • Entries with multiple meanings get the most exam-relevant translation first
  • Colloquial markers (coll.) and pejorative markers (pej.) are preserved in a register field where applicable

4.5 Tier Unlocking Logic

  • Tier 1 is always available (free + registered users)
  • Tier 2 unlocks when user has ≥50% of any Tier 1 category at Box 3+
  • Tier 3 unlocks when user has ≥30% of Tier 2 categories started
  • Tier 4 unlocks when user has ≥3 Tier 3 categories with ≥50% at Box 3+
  • Guest users can only access Tier 1 (10-word limit per category still applies)

5. Functional Requirements

5.1 Page & Navigation

ID Type Requirement
REQ-01 Ubiquitous The system shall serve the vocabulary interface at /sproochentest/vocab/.
REQ-02 Ubiquitous The page shall use the existing Sproochentest header with a new “Vocab” nav link added.
REQ-03 Ubiquitous The page shall display a tier-based category selector showing all available modules grouped by learning progression (Foundations → Exam Building Blocks → Thematic → Advanced), with word counts and user progress per category.

5.2 Authentication & Access Control

ID Type Requirement
REQ-04 Event-Driven When a guest user completes 10 words in any category, then the system shall display a registration modal blocking further progress.
REQ-05 State-Driven While a user is unauthenticated, the system shall store all session progress in localStorage only.
REQ-06 Event-Driven When a guest user registers, then the system shall migrate their localStorage progress to Firestore.
REQ-07 Ubiquitous The registration modal shall offer email/password signup and Google sign-in via Firebase Auth.
REQ-08 State-Driven While a user is authenticated, the system shall sync all progress to Firestore in real-time.

5.3 Learning Layers

The engine presents vocabulary through three progressive layers. A word must pass each layer before advancing to the next.

Layer 1: Recognition (Passive → Active Recognition)

ID Type Requirement
REQ-09 Ubiquitous The system shall present a Luxembourgish word and four English translation options (1 correct + 3 distractors from the same category).
REQ-10 Event-Driven When the user selects the correct answer, then the system shall show a green confirmation, award +10 XP, and advance the card.
REQ-11 Event-Driven When the user selects an incorrect answer, then the system shall highlight the correct answer in green, show the wrong answer in red, and reset the word to Box 1.
REQ-12 Ubiquitous Distractors shall be semantically plausible (same part of speech, same difficulty band).

Layer 2: Production (Active Recall)

ID Type Requirement
REQ-13 Ubiquitous The system shall present an English word and require the user to type the Luxembourgish translation.
REQ-14 Ubiquitous Input matching shall be case-insensitive and shall accept answers with or without the article for nouns.
REQ-15 Event-Driven When the typed answer matches the target (fuzzy: Levenshtein distance ≤ 1 for words > 5 chars), then the system shall accept it as correct with a “close enough” indicator.
REQ-16 Event-Driven When the user submits an incorrect answer, then the system shall display the correct spelling, show the user’s attempt with differences highlighted, and reset to Box 1.

Layer 3: Speed Recall (Timed Production)

ID Type Requirement
REQ-17 Ubiquitous The system shall present an English word with a visible 2.5-second countdown timer.
REQ-18 Event-Driven When the user answers correctly within 2.5 seconds, then the system shall award +10 XP base + 5 XP “Speed Demon” bonus and show a speed animation.
REQ-19 Event-Driven When the timer expires without a correct answer, then the system shall treat it as incorrect (reset to Box 1) and show the correct answer.
REQ-20 Event-Driven When the user answers correctly but after 2.5 seconds, then the system shall award +10 XP (no speed bonus) and show “Correct — but too slow for the exam.”

5.4 Spaced Repetition (SRS) Engine

ID Type Requirement
REQ-21 Ubiquitous The SRS engine shall use a 5-box Leitner system with the following review intervals: Box 1 = same session, Box 2 = 1 day, Box 3 = 3 days, Box 4 = 7 days, Box 5 = 14 days (mastered).
REQ-22 State-Driven While generating a daily study session, the backend shall assemble a 20-card deck: 70% due review cards (next_review_due <= now) + 30% new cards from the active category.
REQ-23 Event-Driven When a user answers incorrectly at any layer, then the engine shall reset that word to Box 1 and re-queue it at the end of the current session.
REQ-24 Event-Driven When a user answers correctly, then the engine shall promote the word to the next box and set next_review_due based on the new box interval.
REQ-25 State-Driven While no review cards are due, the system shall present new cards only (up to 20 per session).
REQ-26 Ubiquitous Words in Box 5 (mastered) shall not appear in daily decks unless the user explicitly resets them.

5.5 Gamification

ID Type Requirement
REQ-27 Event-Driven When a user completes a 20-card daily deck, then the system shall increment current_streak by 1 and trigger a fire/celebration animation.
REQ-28 Event-Driven When a user misses a calendar day without completing a deck, then the system shall reset current_streak to 0 (preserve longest_streak).
REQ-29 Ubiquitous The system shall display a persistent XP counter and streak counter in the session header.
REQ-30 Event-Driven When a user earns XP, then the system shall show a floating “+10 XP” / “+15 XP” animation near the answer area.
REQ-31 Ubiquitous The system shall display a progress bar showing cards completed / total in the current session.

5.6 Session Flow

ID Type Requirement
REQ-32 Event-Driven When a user selects a category, then the system shall generate a session deck and begin with Layer 1 for new words and the appropriate layer for review words.
REQ-33 Event-Driven When a session is interrupted (page close/navigate away), then the system shall save current progress and resume from the same point on return.
REQ-34 Ubiquitous The system shall show a session summary screen on deck completion: words learned, words reviewed, XP earned, streak status, accuracy percentage.
REQ-35 Event-Driven When a user completes a session, then the system shall offer “Continue with another deck” or “Done for today.”

6. User Interface Specification

6.1 Page Layout

┌─────────────────────────────────────────────────┐
│  [Sproochentest Header — existing]              │
├─────────────────────────────────────────────────┤
│                                                 │
│  ┌─────────────────────────────────────────┐    │
│  │  Category Selector (grid of topic cards) │    │
│  │  - Shows progress per category           │    │
│  │  - Word count / mastered count           │    │
│  │  - Locked indicator for guest overflow   │    │
│  └─────────────────────────────────────────┘    │
│                                                 │
│  ── OR (during active session) ──               │
│                                                 │
│  ┌─────────────────────────────────────────┐    │
│  │  Session Header                          │    │
│  │  [Progress: 7/20] [XP: 145] [🔥 3 days] │    │
│  ├─────────────────────────────────────────┤    │
│  │                                         │    │
│  │  Card Area                              │    │
│  │  - Word display (large, centered)       │    │
│  │  - Timer bar (Layer 3 only)             │    │
│  │  - Answer options (Layer 1) or          │    │
│  │    text input (Layer 2 & 3)             │    │
│  │                                         │    │
│  ├─────────────────────────────────────────┤    │
│  │  Feedback Area                          │    │
│  │  - Correct/incorrect indicator          │    │
│  │  - Example sentence                     │    │
│  │  - XP animation                         │    │
│  └─────────────────────────────────────────┘    │
│                                                 │
├─────────────────────────────────────────────────┤
│  [Sproochentest Footer — existing]              │
└─────────────────────────────────────────────────┘

6.2 Visual Design Tokens (from existing system)

Token Value Usage
--accent #9333EA Primary actions, progress, XP
--dark #0F172A Card backgrounds, text
--slate-bg (light gray) Page background
--slate-border (border gray) Card borders
--white #FFFFFF Card surfaces
Success green #10B981 Correct answers
Error red #EF4444 Wrong answers
Speed gold #F59E0B Speed bonus indicator

6.3 Animations

  • Card flip transition between question and feedback (300ms ease)
  • XP float-up animation (+10 XP text rises and fades, 800ms)
  • Progress bar smooth fill (200ms)
  • Timer bar countdown (linear 2.5s, color shifts green → yellow → red)
  • Streak fire animation on deck completion (CSS keyframes, 1.5s)
  • Shake animation on wrong answer (200ms)

6.4 Mobile Responsiveness

  • Single-column layout below 700px
  • Touch-friendly tap targets (minimum 44px)
  • Answer options stack vertically on mobile
  • Timer bar full-width on mobile
  • Category grid: 2 columns on tablet, 1 column on mobile

7. API Specification

7.1 Cloud Functions

All functions use the existing pattern: onRequest with CORS, JSON body, region europe-west1.

vocabGetDeck

Generates a study session deck for an authenticated user.

POST /vocabGetDeck
Headers: Authorization: Bearer <Firebase ID token>
Body: { "category": "gesondheet" }
Response: {
  "deck": [
    {
      "wordId": "gesondheet_001",
      "lb": "de Kappwéi",
      "en": "headache",
      "layer": 1,
      "box": 2,
      "distractors": ["fever", "cough", "medicine"]  // Layer 1 only
    }
  ],
  "sessionId": "uuid",
  "stats": { "totalXp": 1450, "streak": 3, "wordsLearned": 87 }
}

vocabSubmitAnswer

Records an answer and returns updated SRS state.

POST /vocabSubmitAnswer
Headers: Authorization: Bearer <Firebase ID token>
Body: {
  "sessionId": "uuid",
  "wordId": "gesondheet_001",
  "answer": "headache",
  "correct": true,
  "responseTimeMs": 1800,
  "layer": 1
}
Response: {
  "xpAwarded": 15,
  "newBox": 3,
  "nextReviewDue": "2026-06-03T00:00:00Z",
  "speedBonus": true,
  "sessionProgress": { "completed": 8, "total": 20 }
}

vocabCompleteSession

Finalizes a session, updates streak.

POST /vocabCompleteSession
Headers: Authorization: Bearer <Firebase ID token>
Body: { "sessionId": "uuid" }
Response: {
  "streak": 4,
  "longestStreak": 12,
  "sessionXp": 245,
  "accuracy": 0.85,
  "wordsLearned": 6,
  "wordsReviewed": 14
}

vocabGetProgress

Returns user’s overall progress across all categories.

GET /vocabGetProgress
Headers: Authorization: Bearer <Firebase ID token>
Response: {
  "totalXp": 1695,
  "currentStreak": 4,
  "longestStreak": 12,
  "categories": {
    "gesondheet": { "total": 50, "learned": 32, "mastered": 18 },
    "akafen": { "total": 45, "learned": 12, "mastered": 5 }
  }
}

vocabMigrateGuest

Migrates localStorage progress to Firestore on registration.

POST /vocabMigrateGuest
Headers: Authorization: Bearer <Firebase ID token>
Body: {
  "localProgress": {
    "gesondheet_001": { "box": 2, "layer": 1, "lastSeen": "2026-05-30" },
    "gesondheet_002": { "box": 1, "layer": 1, "lastSeen": "2026-05-30" }
  }
}
Response: { "migrated": 2, "conflicts": 0 }

7.2 Firestore Schema

users/{uid}/
├── vocab_profile/
│   └── stats {
│         totalXp: number,
│         currentStreak: number,
│         longestStreak: number,
│         lastSessionDate: timestamp,
│         wordsLearned: number,
│         wordsMastered: number
│       }
├── vocab_progress/{wordId} {
│     box: number (1-5),
│     layer: number (1-3),
│     nextReviewDue: timestamp,
│     correctCount: number,
│     incorrectCount: number,
│     lastReviewed: timestamp,
│     speedBonusCount: number
│   }
└── vocab_sessions/{sessionId} {
      category: string,
      startedAt: timestamp,
      completedAt: timestamp | null,
      deck: array<wordId>,
      currentIndex: number,
      xpEarned: number,
      accuracy: number
    }

8. Guest Mode (localStorage Schema)

// Key: "sp_vocab_progress"
{
  "words": {
    "gesondheet_001": { "box": 2, "layer": 1, "lastSeen": "2026-05-30" },
    "gesondheet_002": { "box": 1, "layer": 1, "lastSeen": "2026-05-30" }
  },
  "stats": {
    "totalWordsAttempted": 10,
    "sessionsStarted": 1
  },
  "currentSession": {
    "category": "gesondheet",
    "deck": ["gesondheet_001", "gesondheet_002", ...],
    "currentIndex": 5,
    "xpThisSession": 50
  }
}

Guest limitations:

  • No streak tracking (requires server-side date validation)
  • No XP persistence across browser clears
  • Maximum 10 unique words per category
  • No cross-device sync
  • No Layer 3 (speed recall) — requires auth to unlock

9. File Structure

# Frontend (Jekyll)
sproochentest-vocab.html                    ← Main page (layout: sproochentest-vocab)
_layouts/sproochentest-vocab.html           ← Layout (extends base.html)
_data/vocab/
├── essential-words.yml                     ← Tier 1: Foundation
├── articles-pronouns.yml
├── starter-verbs.yml
├── introductions.yml
├── numbers-dates.yml
├── fillers-connectors.yml                  ← Tier 2: Exam Building Blocks
├── question-words.yml
├── locators.yml
├── describe-objects.yml
├── describe-people.yml
├── adjective-declension.yml
├── prepositions.yml
├── sport.yml                               ← Tier 3: Thematic Modules
├── wunnen.yml
├── transport.yml
├── hobbyen.yml
├── vakanz.yml
├── gesondheet.yml
├── medien-technologien.yml
├── sproochen.yml
├── kaddoen.yml
├── summer-wanter.yml
├── aarbecht.yml
├── stot-maachen.yml
├── family.yml
├── frequent-verbs.yml                      ← Tier 4: Advanced
├── irregular-verbs.yml
├── simple-past.yml
└── truly-luxembourgish.yml
assets/css/vocab.css                        ← Page-specific styles
assets/js/vocab-engine.js                   ← SRS logic, UI controller, Firebase Auth
_includes/vocab-auth-modal.html             ← Registration/login modal

# Backend (Cloud Functions)
functions/vocab/
├── getDeck.js
├── submitAnswer.js
├── completeSession.js
├── getProgress.js
└── migrateGuest.js
functions/index.js                          ← Add vocab module exports

10. Integration Points

10.1 Sproochentest Header Update

Add “Vocab” link to _includes/sproochentest-header.html:

<a href="/sproochentest/vocab/" class="">Vocab</a>

10.2 Firebase Config

Add to _data/firebase.yml:

fn_vocab_get_deck: "https://vocabgetdeck-vgheoh5xza-ew.a.run.app"
fn_vocab_submit_answer: "https://vocabsubmitanswer-vgheoh5xza-ew.a.run.app"
fn_vocab_complete_session: "https://vocabcompletesession-vgheoh5xza-ew.a.run.app"
fn_vocab_get_progress: "https://vocabgetprogress-vgheoh5xza-ew.a.run.app"
fn_vocab_migrate_guest: "https://vocabmigrateguest-vgheoh5xza-ew.a.run.app"

10.3 Sproochentest Landing Page

Add a new section to sproochentest.html between Speaking and Image Description:

<div class="sp-section" id="vocab">
  <!-- Vocabulary Engine promo section -->
</div>

10.4 CSS Loading

The vocab page loads CSS via front matter — no changes to global.css or _config.yml needed:

css:
  - "/assets/css/sproochentest.css"
  - "/assets/css/vocab.css"

11. Security Requirements

ID Requirement
SEC-01 All Cloud Functions shall validate Firebase ID tokens before processing requests.
SEC-02 XP and streak calculations shall be server-side only. The frontend displays values but cannot set them.
SEC-03 Answer correctness shall be validated server-side (frontend sends the answer, backend checks it).
SEC-04 Rate limiting: max 100 answer submissions per user per hour.
SEC-05 Firebase API key (already public) is domain-restricted. No additional secrets exposed in frontend.
SEC-06 Guest localStorage data is untrusted. On migration, server validates word IDs exist and box values are within range (1-5).

12. Performance Requirements

ID Requirement
PERF-01 Page load (first contentful paint) shall be under 1.5 seconds on 4G.
PERF-02 Card transition (answer → next card) shall be under 200ms perceived latency.
PERF-03 Firebase Auth SDK shall be loaded asynchronously and not block initial render.
PERF-04 Vocabulary YAML data shall be compiled into a single JSON file at build time (Jekyll plugin or build script) to avoid multiple network requests.
PERF-05 The deck generation API shall respond in under 500ms.
PERF-06 Total page JS bundle (excluding Firebase SDK) shall be under 30KB gzipped.

13. Accessibility Requirements

ID Requirement
A11Y-01 All interactive elements shall be keyboard-navigable (Tab, Enter, Escape).
A11Y-02 Answer options shall use role="radio" with proper aria-checked states.
A11Y-03 Timer countdown shall have an aria-live="polite" region announcing time remaining.
A11Y-04 Color shall not be the only indicator of correct/incorrect — use icons (✓/✗) and text labels.
A11Y-05 Focus shall move logically: question → answer area → feedback → next card.
A11Y-06 Modal shall trap focus and be dismissible with Escape.

14. SEO Considerations

Concern Mitigation
Interactive page has little crawlable content Include static intro text, category list, and FAQ below the app shell
New URL /sproochentest/vocab/ Add to sitemap, internal link from sproochentest landing page
Page title & meta “Luxembourgish Vocabulary Practice — Sproochentest Prep”
Structured data FAQPage schema for common questions about vocab learning

15. Implementation Phases

Phase 1: Foundation (MVP)

  • Convert Tier 1 vocabulary data into YAML files (essential-words, articles-pronouns, starter-verbs, introductions, numbers-dates)
  • Frontend page with tier/category selector
  • Layer 1 (recognition) — multiple choice
  • Guest mode with localStorage (10-word limit per category)
  • Registration modal with Firebase Auth
  • Basic progress tracking (no SRS, sequential cards)
  • Page-specific CSS and JS

Phase 2: SRS & Production

  • Leitner box algorithm (client-side for guests, server-side for auth users)
  • Layer 2 (production) — typed answers with fuzzy matching
  • Cloud Functions: getDeck, submitAnswer, completeSession
  • Firestore schema and progress persistence
  • Guest → authenticated migration flow
  • Session resume on page reload
  • Convert Tier 2 vocabulary data into YAML files

Phase 3: Speed & Gamification

  • Layer 3 (speed recall) — timed input
  • XP system with animations
  • Streak tracking with server-side validation
  • Session summary screen
  • Celebration animations (streak fire, level up)
  • Progress dashboard per category
  • Tier unlocking logic

Phase 4: Content Expansion

  • Convert Tier 3 vocabulary data (all thematic modules)
  • Convert Tier 4 vocabulary data (advanced + truly Luxembourgish)
  • Audio pronunciation (future — TTS or pre-recorded)
  • Daily push notification reminders (future — requires PWA)
  • Leaderboard (future — requires additional Firestore queries)

16. Open Questions

# Question Impact
1 Should Layer 3 accept typed input or voice input? Voice would be more exam-realistic but adds complexity (Web Speech API). Start with typed, add voice as Phase 5.
2 Should wrong answers demote to Box 1 or demote by one box? Box 1 reset is aggressive but ensures mastery. Recommend Box 1 for MVP, gather user feedback.
3 Audio pronunciation — TTS or pre-recorded? Pre-recorded is higher quality but expensive to produce for 600+ words. TTS (Google Cloud) is scalable. Defer to Phase 4.
4 Should the 10-word guest limit be per category or total? Per category is more generous and lets users sample multiple topics. Recommend per category.
5 Offline/PWA support? Would significantly boost DAU for a daily-habit tool. Defer to post-MVP — requires service worker and IndexedDB.

17. Dependencies

Dependency Version Purpose
Firebase Auth (Web SDK) ^10.x User authentication
Firebase Firestore (Web SDK) ^10.x Progress persistence
firebase-admin ^12.x (existing) Backend token validation
firebase-functions ^5.x (existing) Cloud Function framework

No new backend dependencies required. Frontend Firebase SDKs loaded via CDN (modular tree-shakeable imports).


18. Success Criteria for Launch

  • All 5 Tier 1 categories converted to YAML with full entries
  • Layer 1 and Layer 2 functional for guests and authenticated users
  • Registration flow works (email + Google)
  • Guest progress migrates on registration
  • No impact on existing Sproochentest pages or URLs
  • Mobile-responsive down to 320px width
  • Page loads under 2s on 4G
  • No console errors in production
  • Passes WAVE accessibility check (0 errors)