G2G

Chapter Forty-Three

The Foreword

Das Vorwort

A layered landscape unfolds. Time becomes space. The prefix "vor-" shows what comes before, both spatially and temporally. Then its opposite: "nach-" (after), revealing how languages encode temporal sequences. Above and below come "über-" and "unter-", transforming from spatial concepts into abstract meaning. These prefixes are dual-natured: separable when literal, inseparable when abstract.

· · ·

Picture a landscape in layers. Mountains rise in the distance. Valleys carve the middle ground. Paths wind forward. Footprints mark what has been behind. This is the landscape of time, and it is the landscape that German prefixes encode in their visual roots.

The prefix vor- means "before," both spatially and temporally. It is the foreground, what comes before you in space. But it is also what comes before you in time — what is ahead, what must be done first, what precedes. The spatial and temporal are woven together so tightly that the language does not distinguish between them.

The prefix nach- means "after," also both spatial and temporal. It is what follows behind, what comes next in sequence, what you pursue. It is the footprints you follow, the path already taken, the trail that leads behind you.

And towering above are über- and unter-, "over" and "under," which begin as purely spatial concepts but transform, through the prefix-verb system, into abstract meanings of superiority, excess, subtlety, and support.

This chapter is about the dual nature of German prefixes. Some are separable in literal contexts but inseparable in abstract ones. This duality mirrors the duality of space and time, of literal and abstract, of physical and conceptual.

· · ·

The word vor- comes from deep in Proto-Germanic, originally meaning "forward" or "in front of." When you stand vor someone, you stand in front of them, in their presence, facing them. The word encodes physical proximity combined with directionality.

But when vor- becomes a prefix, it extends into abstract domains. Vorstellen is "to introduce" or "to imagine," literally "to place before." When you introduce someone, you place them before others, present them, make them known. But when you imagine, you place images before your mind, you conjure them mentally. The same word, the same prefix, creates two different meanings because context transforms the root.

Vorteil is "advantage," literally "before-part" or "fore-part." An advantage is what stands before the disadvantage, what comes first in the race of outcomes. This is not a verb but a noun, yet the prefix still carries its original meaning: what is ahead, what is in front, what comes first in value or timing.

vor- /foːɐ̯/
prefix — spatial "in front of," "before"; temporal "earlier," "ahead"; abstract "presiding over," "superior to"
PIE *per- — root meaning "forward," "through"
ENG fore, for — cognate; English "fore-" as in "foreground," "foresee"
DEU vor- — inseparable when abstract, separable when literal (vorstellen can be separable in passive constructions)
ZHO 前 / 向前 — qián / xiàngqián — "front," "forward" — the spatial concept of before and ahead
The prefix vor- preserves the ancient spatial concept of standing in front, of being ahead, of precedence. When German speakers use vorstellen, they are literally "placing before" — whether that is presenting someone, or placing images before the mind in imagination. The metaphor of spatial placement becomes abstract imagination. Vorteil (advantage) is "before-part" because an advantage is what comes first, what stands ahead. The vor- prefix encodes a worldview where superiority is literalized as being-in-front, where precedence is physical.

The key insight: vor- carries the sense of PRECEDENCE, both spatial and temporal. Whether literal or abstract, something with the vor- prefix comes FIRST, stands AHEAD, or is presented BEFORE others.

· · ·

If vor- is what comes before, then nach- is everything that follows. The word originally meant "near," then "after," tracking the temporal trail of what has gone before. When you nach someone, you follow them, you pursue their path, you walk in their wake.

Nachdenken is "to reflect" or "to think about," literally "to think-after." This is one of the most beautiful German words. When you reflect on something, you are thinking after the moment has passed, revisiting it in thought, following the trail of the experience backward through time. The footprints are metaphorical, but the image is clear.

Nachricht is "message" or "news," literally "after-direction" or "after-news." A message reaches you after an event has occurred. News is always information that comes after the happening, following in the wake of events. The German word structure encodes the temporal lag inherent in communication.

nach- /naːx/
prefix — spatial "after," "behind"; temporal "subsequently," "following"; pursuit "in pursuit of," "in the wake of"
PIE *neh₂- — root meaning "to come near," "to draw near"
ENG nigh, next — cognate; "nigh" means near, "next" implies following sequence
DEU nach- — separable prefix in many contexts; originally "after," "near," now emphasizing following or pursuit
ZHO 后 / 之后 — hòu / zhīhòu — "after," "afterward" — temporal and sequential meaning
The prefix nach- encodes the concept of following, pursuing, and temporal succession. Nachdenken (to reflect) is thinking that comes after the moment, revisiting experience in the mind. Nachricht (news/message) is information that always comes after events, forever trailing the reality it reports. The nach- prefix creates a systematic way of expressing what follows, what comes next, what is left behind. It is the footprints in the snow, the wake behind a ship, the memory following the moment.

The key insight: nach- carries the sense of FOLLOWING, PURSUIT, and TEMPORAL SEQUENCE. It expresses what comes after, what you pursue, what trails behind in time.

· · ·

Imagine standing in a valley looking up at the mountains that surround you. The highest peak stands über everything else — over, above, superior in position. This is the original meaning of über-. It is the peak, the summit, what towers above the rest.

But when über- becomes a prefix, it transforms into concepts of excess, exaggeration, and transcendence. Übersetzen is "to translate," literally "to set-over" or "to carry-over." When you translate, you carry language over a barrier, you take words from one language and place them in another, transforming them as you cross. The spatial metaphor of crossing — going over — becomes the conceptual act of linguistic transformation.

Übertreiben is "to exaggerate," literally "to drive-over" or "to exceed." When you exaggerate, you drive the truth over the limit, beyond the boundary of plausibility. The prefix captures this sense of exceeding, overstepping, going too far.

über- /ˈʏbɐ/
prefix — spatial "over," "above"; excess "beyond," "exceeding"; transformation "across," "over"
PIE *upo — root meaning "over," "above," "up toward"
ENG over — direct cognate; same root and meaning in English
DEU über- — inseparable prefix; encodes both spatial "over" and abstract "exceeding," "transcending"
ZHO 超 / 过 — chāo / guò — "exceed," "surpass," "over" — expressing transcendence and excess
The prefix über- is visual: you see it towering above, dominating the landscape. When it becomes abstract, it maintains this sense of superiority and transcendence. Übersetzen (to translate) is setting language over the barrier between tongues. Übertreiben (to exaggerate) is driving truth over its proper limits. The über- prefix transforms spatial superiority into abstract concepts of excess, transformation, and transcendence. It is the peak that dominates the view, the limit that is exceeded, the boundary that is crossed.

The key insight: über- carries the sense of SUPERIORITY, EXCESS, and TRANSCENDENCE. It expresses what stands above, what exceeds limits, what transforms by crossing barriers.

· · ·

If über- is the peak, then unter- is the foundation. It is what lies beneath, what supports, what is subordinate in position but essential in function. The word originally meant "below" or "under," the spatial opposite of über-.

Unterschied is "difference," literally "under-cut" or "under-section." This is a remarkable word. When you make a difference between two things, you cut beneath them, you separate them at the root level. The distinction is a cut made underneath, revealing what divides them from the foundation up. This is why the German word is so precise: a real difference cuts to the root, beneath the surface.

Unterstützen is "to support," literally "to prop-under." When you support something, you place yourself beneath it, you provide the foundation, you become the ground upon which it stands. The metaphor is structural: support is literally what holds things up from below.

unter- /ˈʊntɐ/
prefix — spatial "under," "below," "beneath"; support "supporting," "upholding"; separation "dividing," "distinguishing"
PIE *ndher- — root meaning "under," "below"
ENG under — direct cognate; identical meaning and usage as prefix
DEU unter- — inseparable prefix; expresses subordination, support, and distinction
ZHO 下 / 底下 — xià / dǐxia — "below," "under," "bottom" — spatial and foundational
The prefix unter- is about what lies beneath, what supports, what divides at the root. Unterschied (difference) cuts beneath the surface to reveal fundamental distinctions. Unterstützen (to support) is propping up, becoming the foundation. The unter- prefix transforms the spatial concept of "below" into abstract concepts of support, foundation, and deep-level distinction. What is beneath may be hidden from view, but it is essential to what stands above.

The key insight: unter- carries the sense of FOUNDATION, SUPPORT, and DEEP DISTINCTION. It expresses what lies beneath, what upholds, what divides at the fundamental level.

· · ·

Now we reach a crucial point in understanding German grammar. Some of these prefixes are SEPARABLE, and some are INSEPARABLE. This is not arbitrary; it reflects a deep pattern in how German encodes meaning.

When a prefix is SEPARABLE, it means that in certain conjugations — particularly in the present tense — the prefix splits from the verb and moves to the end of the clause. So "Ich stelle dich vor" (I introduce you) literally becomes "I you introduce before," with "vor" detached at the end. This happens precisely in contexts where the spatial meaning is still active, still visible.

When a prefix is INSEPARABLE, it stays locked to the root verb in all conjugations. "Ich verstehe das" (I understand this) never becomes "Ich stehe das ver" because the meaning is fully abstract, the prefix is no longer truly a separate element but rather a component of a new word whose spatial meaning has been completely transformed.

This distinction reveals something profound about language: the grammar itself encodes the degree to which meaning has been abstracted. Separable prefixes are still somewhat attached to their spatial origins. Inseparable prefixes have been fully absorbed into abstract meaning. The morphology mirrors the semantics.

· · ·

Look at the Chinese word 翻译 (fānyì), which means "to translate." It is composed of 翻 (fān), meaning "to turn" or "to flip," and 译 (yì), meaning "to translate" or "to interpret." The German übersetzen and the Chinese 翻译 approach translation through different metaphors, but the underlying logic is similar: both languages conceptualize translation as a transformation, a turning, a carrying-over of meaning.

This is the beauty of comparative linguistics. Different languages build the concept differently, but they converge on similar metaphorical foundations. German uses spatial orientation: "over-set" (übersetzen). Chinese uses movement: "flip-translate" (fānyì). But both recognize that translation is a transformation, a carrying of something across a boundary, a metamorphosis.

· · ·

Vorstellen — to introduce, to imagine [vor- (before) + stellen (to place)] — to place before

Vorteil — advantage [vor- (before) + Teil (part)] — what comes before, what stands ahead

Nachdenken — to reflect [nach- (after) + denken (to think)] — to think after the moment

Nachricht — news, message [nach- (after) + Richt (direction)] — what follows, comes after events

Übersetzen — to translate [über- (over) + setzen (to set)] — to set across, to carry over

Übertreiben — to exaggerate [über- (over) + treiben (to drive)] — to drive beyond limits

Unterschied — difference [unter- (under) + Schied (cut, separation)] — what cuts beneath

Unterstützen — to support [unter- (under) + stützen (to prop)] — to prop up from below

· · ·

Check Your Understanding

Test your mastery of Das Vorwort. 80% required to continue.

Patterns Discovered
Spatial-Temporal Duality — Vor- (before), nach- (after), über- (over), and unter- (under) encode spatial position but extend into temporal sequence and abstract hierarchy. The landscape becomes a timeline; the geography becomes a conceptual map. Space and time are linguistically equivalent, both expressed through the same prefixes.

Separability Reflects Abstraction Degree — Some vor-/nach- compounds are separable (vorstellen can split: "stelle vor"), while über- and unter- are typically inseparable (übersetzen never splits). This morphological distinction mirrors semantic abstraction: separable prefixes retain spatial meaning; inseparable ones are fully abstract.

Metaphorical Coherence — Vorstellen (to place before) becomes both "to introduce" and "to imagine." Übersetzen (to carry over) becomes "to translate." Unterschied (under-cut) becomes "difference." Each prefix generates consistent metaphors: spatial placement becomes intellectual positioning, cutting beneath becomes fundamental distinction.

Universal Conceptual Foundations — Across languages, translation is metaphorically a transformation or crossing (German übersetzen, Chinese 翻译 fānyì). Difference is conceptually a cutting or separation. Support is foundationally bearing weight from below. These prefixes tap into universal spatial metaphors that shape human conceptualization across cultures.
Your Progress
Words Collected 405 / 850 (48%)
Click to see all words ▾
Patterns & Grammar 93 / 145 (64%)
Click to see all patterns ▾

Words from Das Vorwort (The Foreword)

vorstellen
introduce
Vorteil
advantage
nachdenken
reflect
Nachricht
news
übersetzen
translate
übertreiben
exaggerate
Unterschied
difference
unterstützen
support

Bauwerkstatt

Building Workshop — Three Levels of Production Exercises
1 Wortbaukasten — Word Building Kit
Build the German phrase by clicking words in order:
Available words:
Build: "The plan is prepared"
Available words:
Build: "She thinks about the question"
Available words:
Build: "He translates the text"
Available words:
2 Lückensatz — Gap Sentence
Fill in the blank: "Wir _______ den Plan heute vor." (We ___ the plan today.)
Fill in the blank: "Ich _______ über deine Frage _______." (I ___ about your question.)
Fill in the blank: "Der Student _______ das Buch." (The student ___ the book.)
Fill in the blank: "Sie _______ den Vertrag." (She ___ the contract.)
3 Freies Bauen — Free Building
Translate to German: "to prepare"
Translate to German: "to think/reflect"
Translate to German: "to translate"
Translate to German: "to sign"
Your Progress: 0 / 12 Correct

Lesen & Hören — Read and Listen

Wir bereiten den Plan sorgfältig vor.
Der Lehrer denkt über jede Frage nach.
Der Student übersetzt die schwierigen Texte.
Alle unterschreiben das wichtige Dokument.

Verständnisfragen — Comprehension Questions

1. What prefix patterns were used in the passage?
The prefixes from this chapter
Random prefixes
No prefixes at all
2. Identify a verb with a chapter prefix:
Any verb from the passage using this chapter's prefixes
A verb without prefixes
A noun instead of a verb
3. Fill in the blank with a conjugated verb from this chapter:
4. Understand the spatial or temporal relationships expressed:
The prefixes encode different meanings related to time or space
The prefixes have no meaning
The prefixes are decorative

Diktat — Dictation Exercise

Listen to a sentence and type what you hear. Click the button to hear each sentence once.

Sentence 1 of 2
A G2G Advisory Project
/* === BAUWERKSTATT SYSTEM === */ // Levenshtein distance function for fuzzy matching function levenshteinDistance(a, b) { const aL = a.toLowerCase(), bL = b.toLowerCase(); const track = Array(bL.length + 1).fill(null).map(() => Array(aL.length + 1).fill(0)); for (let i = 0; i <= aL.length; i++) track[0][i] = i; for (let j = 0; j <= bL.length; j++) track[j][0] = j; for (let j = 1; j <= bL.length; j++) { for (let i = 1; i <= aL.length; i++) { const indicator = aL[i - 1] === bL[j - 1] ? 0 : 1; track[j][i] = Math.min( track[j][i - 1] + 1, track[j - 1][i] + 1, track[j - 1][i - 1] + indicator ); } } return track[bL.length][aL.length]; } // Bauwerkstatt exercise data (to be filled with chapter-specific data) const bauwerkstattData = { level1: [], level2: [], level3: [] }; // State tracking let bauwerkstattState = { level1: { completed: [false, false, false, false], score: 0 }, level2: { completed: [false, false, false, false], score: 0 }, level3: { completed: [false, false, false, false], score: 0 }, assembled: [[], [], [], []] }; // Initialize Bauwerkstatt function initBauwerkstatt() { if (bauwerkstattData.level1.length === 0) return; // Level 1: Word Assembly bauwerkstattData.level1.forEach((ex) => { const bankId = `word-bank-${ex.id}`; const bank = document.getElementById(bankId); if (bank) { bank.innerHTML = ''; ex.words.forEach(word => { const chip = document.createElement('div'); chip.className = 'word-chip'; chip.textContent = word; chip.onclick = () => addToAssembly(ex.id, word); bank.appendChild(chip); }); } bauwerkstattState.assembled[ex.id - 1] = []; }); updateProgress(); } function addToAssembly(exId, word) { const areaId = `assembly-area-${exId}`; const area = document.getElementById(areaId); const bankId = `word-bank-${exId}`; const bank = document.getElementById(bankId); if (!bauwerkstattState.assembled[exId - 1]) { bauwerkstattState.assembled[exId - 1] = []; } // Add to assembly area const chip = document.createElement('div'); chip.className = 'word-chip placed'; chip.textContent = word; chip.onclick = () => removeFromAssembly(exId, word, chip); area.appendChild(chip); bauwerkstattState.assembled[exId - 1].push(word); // Mark as unavailable in bank const bankChips = bank.querySelectorAll('.word-chip'); bankChips.forEach(c => { if (c.textContent === word) c.classList.add('unavailable'); }); } function removeFromAssembly(exId, word, chipEl) { const bankId = `word-bank-${exId}`; const bank = document.getElementById(bankId); chipEl.remove(); const idx = bauwerkstattState.assembled[exId - 1].indexOf(word); if (idx > -1) bauwerkstattState.assembled[exId - 1].splice(idx, 1); // Re-enable in bank const bankChips = bank.querySelectorAll('.word-chip'); bankChips.forEach(c => { if (c.textContent === word) c.classList.remove('unavailable'); }); } function undoWordAssembly(exId) { const areaId = `assembly-area-${exId}`; const area = document.getElementById(areaId); const bankId = `word-bank-${exId}`; const bank = document.getElementById(bankId); area.innerHTML = ''; bauwerkstattState.assembled[exId - 1] = []; // Reset all chips in bank const bankChips = bank.querySelectorAll('.word-chip'); bankChips.forEach(c => c.classList.remove('unavailable')); const feedbackId = `feedback-${exId}`; const fb = document.getElementById(feedbackId); if (fb) fb.classList.remove('show', 'correct', 'close', 'incorrect'); } function checkWordAssembly(exId) { const ex = bauwerkstattData.level1[exId - 1]; if (!ex) return; const assembled = bauwerkstattState.assembled[exId - 1].join(' ').toLowerCase(); const correct = ex.correct.join(' ').toLowerCase(); const feedbackId = `feedback-${exId}`; const fb = document.getElementById(feedbackId); if (!fb) return; if (assembled === correct) { fb.className = 'exercise-feedback show correct'; fb.innerHTML = `
✓ Correct!
${ex.explanation}
`; if (!bauwerkstattState.level1.completed[exId - 1]) { bauwerkstattState.level1.completed[exId - 1] = true; bauwerkstattState.level1.score++; updateProgress(); } } else { fb.className = 'exercise-feedback show incorrect'; fb.innerHTML = `
Not quite yet.
Try again. The correct phrase is: ${ex.answer}
`; } } // Level 2 & 3: Gap sentences and free building function handleGapKeypress(event, exId) { if (event.key === 'Enter') checkGapSentence(exId); } function handleFreeKeypress(event, exId) { if (event.key === 'Enter') checkFreeBuilding(exId); } function checkGapSentence(exId) { const ex = bauwerkstattData.level2[exId - 1]; if (!ex) return; const input = document.getElementById(`gap-input-${exId}`); const answer = input.value.trim(); const feedbackId = `gap-feedback-${exId}`; const fb = document.getElementById(feedbackId); const accepted = ex.accepted_answers; const isCorrect = accepted.some(a => a.toLowerCase() === answer.toLowerCase()); if (isCorrect) { fb.className = 'exercise-feedback show correct'; fb.innerHTML = `
✓ Correct!
${answer.charAt(0).toUpperCase() + answer.slice(1)}. ${ex.explanation}
`; if (!bauwerkstattState.level2.completed[exId - 1]) { bauwerkstattState.level2.completed[exId - 1] = true; bauwerkstattState.level2.score++; updateProgress(); } } else { // Check for close matches let closest = null; let minDist = Infinity; for (const a of accepted) { const dist = levenshteinDistance(answer, a); if (dist < minDist) { minDist = dist; closest = a; } } if (minDist <= 2) { // Check if there's a specific error message let specificFeedback = null; if (ex.close_errors) { for (const err of ex.close_errors) { if (err.wrong.toLowerCase() === answer.toLowerCase()) { specificFeedback = err.feedback; break; } } } fb.className = 'exercise-feedback show close'; fb.innerHTML = `
Almost!
${specificFeedback || `You wrote '${answer}' — the right form is '${closest}'. ${ex.explanation}`}
`; } else { fb.className = 'exercise-feedback show incorrect'; fb.innerHTML = `
Not quite.
The answer is ${closest}. ${ex.explanation}
`; } } } function checkFreeBuilding(exId) { const ex = bauwerkstattData.level3[exId - 1]; if (!ex) return; const input = document.getElementById(`free-input-${exId}`); const answer = input.value.trim(); const feedbackId = `free-feedback-${exId}`; const fb = document.getElementById(feedbackId); const accepted = ex.accepted_answers; const isCorrect = accepted.some(a => a.toLowerCase() === answer.toLowerCase()); if (isCorrect) { fb.className = 'exercise-feedback show correct'; fb.innerHTML = `
✓ Correct!
${ex.explanation}
`; if (!bauwerkstattState.level3.completed[exId - 1]) { bauwerkstattState.level3.completed[exId - 1] = true; bauwerkstattState.level3.score++; updateProgress(); } } else { // Check for close matches let closest = null; let minDist = Infinity; for (const a of accepted) { const dist = levenshteinDistance(answer, a); if (dist < minDist) { minDist = dist; closest = a; } } if (minDist <= 2) { // Check for specific error feedback let specificFeedback = null; if (ex.close_errors) { for (const err of ex.close_errors) { if (err.wrong.toLowerCase() === answer.toLowerCase()) { specificFeedback = err.feedback; break; } } } fb.className = 'exercise-feedback show close'; fb.innerHTML = `
Almost!
${specificFeedback || `You wrote '${answer}' — the right answer is '${closest}'. ${ex.explanation}`}
`; } else { fb.className = 'exercise-feedback show incorrect'; fb.innerHTML = `
Not quite.
The answer is ${closest}. ${ex.explanation}
`; } } } function updateProgress() { const total = 12; // 4 + 4 + 4 const score = bauwerkstattState.level1.score + bauwerkstattState.level2.score + bauwerkstattState.level3.score; const scoreEl = document.getElementById('bauwerkstatt-score'); if (scoreEl) scoreEl.textContent = score; const bar = document.getElementById('bauwerkstatt-progress-bar'); if (bar) { bar.innerHTML = ''; const allCompleted = [ ...bauwerkstattState.level1.completed, ...bauwerkstattState.level2.completed, ...bauwerkstattState.level3.completed ]; allCompleted.forEach(completed => { const dot = document.createElement('div'); dot.className = 'progress-dot' + (completed ? ' completed' : ''); bar.appendChild(dot); }); } } // Initialize on page load document.addEventListener('DOMContentLoaded', () => { initBauwerkstatt(); }); /* === LESEN & HÖREN SECTION === */ const dictationSentences = []; let currentSpeed = 0.95; let isListening = false; let currentDictationIndex = 0; let isSpeaking = false; /** * Word-level diff for showing what changed */ function wordDiff(actual, expected) { const actualWords = actual.toLowerCase().split(/\s+/); const expectedWords = expected.toLowerCase().split(/\s+/); const result = []; const maxLen = Math.max(actualWords.length, expectedWords.length); for (let i = 0; i < maxLen; i++) { const aWord = actualWords[i] || ''; const eWord = expectedWords[i] || ''; if (aWord === eWord) { result.push(`${eWord}`); } else { result.push(`${aWord}`); } } return result.join(' '); } /** * Start listening to the passage */ function startListening() { if (isSpeaking) return; isListening = true; document.getElementById('listen-btn').style.display = 'none'; document.getElementById('stop-btn').style.display = 'inline-block'; const sentences = document.querySelectorAll('.passage-sentence'); sentences.forEach(s => s.classList.remove('reading')); // Speak with pauses between sentences const utterances = []; sentences.forEach((sentenceEl, idx) => { const text = sentenceEl.textContent.trim(); const utterance = new SpeechSynthesisUtterance(text); utterance.lang = 'de-DE'; utterance.rate = currentSpeed; utterance.onstart = () => { sentenceEl.classList.add('reading'); }; utterance.onend = () => { sentenceEl.classList.remove('reading'); // Add a pause between sentences if (idx < sentences.length - 1) { setTimeout(() => { if (isListening && idx + 1 < utterances.length) { speechSynthesis.speak(utterances[idx + 1]); } }, 300); } }; utterances.push(utterance); }); isSpeaking = true; if (utterances.length > 0) { speechSynthesis.speak(utterances[0]); } } /** * Stop listening */ function stopListening() { isListening = false; isSpeaking = false; speechSynthesis.cancel(); document.getElementById('listen-btn').style.display = 'inline-block'; document.getElementById('stop-btn').style.display = 'none'; document.querySelectorAll('.passage-sentence').forEach(s => s.classList.remove('reading')); } /** * Set speech rate */ function setSpeed(rate) { currentSpeed = rate; document.querySelectorAll('.speed-btn').forEach(btn => btn.classList.remove('active')); if (rate === 0.7) { document.getElementById('speed-slow').classList.add('active'); } else { document.getElementById('speed-normal').classList.add('active'); } // If currently speaking, restart with new rate if (isListening) { stopListening(); setTimeout(() => startListening(), 300); } } /** * Check comprehension question */ function checkComprehension(qNum, element, isCorrect) { // Disable all options for this question const options = element.parentElement.querySelectorAll('.comp-option'); options.forEach(opt => { opt.style.pointerEvents = 'none'; opt.style.opacity = '0.7'; }); const feedback = document.getElementById(`comp-feedback-${qNum}`); if (isCorrect) { element.classList.add('correct'); feedback.textContent = '✓ Correct!'; feedback.classList.add('show', 'correct'); } else { element.classList.add('incorrect'); feedback.textContent = '✗ Incorrect.'; feedback.classList.add('show', 'incorrect'); } } /** * Check fill-in-the-blank answer */ function checkFillBlank() { const input = document.getElementById('fill-blank-input'); const answer = 'kommt'; const userAnswer = input.value.trim().toLowerCase(); const feedback = document.getElementById('comp-feedback-3'); const distance = levenshteinDistance(userAnswer, answer); if (distance === 0) { feedback.textContent = '✓ Perfect!'; feedback.classList.add('show', 'correct'); input.style.borderColor = 'rgba(74, 180, 100, 0.5)'; } else if (distance <= 2) { feedback.textContent = `✓ Very close! The answer is: ${answer}`; feedback.classList.add('show', 'correct'); input.style.borderColor = 'rgba(74, 180, 100, 0.5)'; } else { feedback.textContent = `The correct answer is: ${answer}`; feedback.classList.add('show', 'incorrect'); input.style.borderColor = 'rgba(232, 164, 74, 0.5)'; } input.disabled = true; } /** * Play dictation sentence */ function playDictation() { if (currentDictationIndex >= dictationSentences.length) { return; } const sentence = dictationSentences[currentDictationIndex]; const utterance = new SpeechSynthesisUtterance(sentence); utterance.lang = 'de-DE'; utterance.rate = 0.8; document.getElementById('dictation-play-btn').disabled = true; utterance.onend = () => { document.getElementById('dictation-play-btn').disabled = false; }; speechSynthesis.speak(utterance); } /** * Check dictation answer */ function checkDictation() { const input = document.getElementById('dictation-input'); const userAnswer = input.value.trim(); const expectedAnswer = dictationSentences[currentDictationIndex]; const feedback = document.getElementById('dictation-feedback'); const result = document.getElementById('dictation-result'); if (!userAnswer) { feedback.textContent = 'Please type something first.'; feedback.classList.add('show', 'incorrect'); return; } const distance = levenshteinDistance(userAnswer, expectedAnswer); const maxDistance = Math.ceil(expectedAnswer.length * 0.15); feedback.classList.remove('correct', 'incorrect'); result.classList.remove('show'); if (distance === 0) { feedback.textContent = '✓ Perfect! You got it exactly right.'; feedback.classList.add('show', 'correct'); result.classList.add('show'); result.innerHTML = `Correct sentence: ${expectedAnswer}`; } else if (distance <= 3) { feedback.textContent = `✓ Very close! Only a few words different.`; feedback.classList.add('show', 'correct'); result.classList.add('show'); result.innerHTML = `What you wrote: ${wordDiff(userAnswer, expectedAnswer)}
Expected: ${expectedAnswer}`; } else { feedback.textContent = '✗ Not quite. Try again or listen once more.'; feedback.classList.add('show', 'incorrect'); result.classList.add('show'); result.innerHTML = `Expected: ${expectedAnswer}`; } // Update counter and prepare for next if (currentDictationIndex < dictationSentences.length - 1) { // Show "Next Sentence" button instead of auto-advancing let nextBtn = document.getElementById('dictation-next-btn'); if (!nextBtn) { nextBtn = document.createElement('button'); nextBtn.id = 'dictation-next-btn'; nextBtn.textContent = 'Next Sentence →'; nextBtn.style.cssText = 'margin-top:16px;padding:10px 24px;background:rgba(232,164,74,0.15);color:#e8a44a;border:1px solid rgba(232,164,74,0.3);border-radius:8px;cursor:pointer;font-family:inherit;font-size:0.95rem;display:block;'; nextBtn.onmouseover = function() { this.style.background = 'rgba(232,164,74,0.25)'; }; nextBtn.onmouseout = function() { this.style.background = 'rgba(232,164,74,0.15)'; }; feedback.parentNode.insertBefore(nextBtn, feedback.nextSibling); } nextBtn.style.display = 'block'; nextBtn.onclick = function() { currentDictationIndex++; updateDictationCounter(); input.value = ''; feedback.classList.remove('show'); result.classList.remove('show'); this.style.display = 'none'; }; } else { document.getElementById('dictation-play-btn').textContent = '✓ Dictation Complete!'; document.getElementById('dictation-play-btn').disabled = true; } } /** * Update dictation counter */ function updateDictationCounter() { const counter = document.getElementById('dictation-counter'); counter.textContent = `Sentence ${currentDictationIndex + 1} of ${dictationSentences.length}`; } // Initialize dictation counter on page load document.addEventListener('DOMContentLoaded', () => { updateDictationCounter(); });