diff --git a/backend/scripts/create-german-for-bisaya-course-content.js b/backend/scripts/create-german-for-bisaya-course-content.js new file mode 100644 index 0000000..5e297b4 --- /dev/null +++ b/backend/scripts/create-german-for-bisaya-course-content.js @@ -0,0 +1,735 @@ +#!/usr/bin/env node +/** + * Script zum Erstellen von Übungen für Deutschkurse aus Sicht von Bisaya-Lernenden. + * + * Verwendung: + * node backend/scripts/create-german-for-bisaya-course-content.js + */ + +import { sequelize } from '../utils/sequelize.js'; +import VocabCourseLesson from '../models/community/vocab_course_lesson.js'; +import VocabGrammarExercise from '../models/community/vocab_grammar_exercise.js'; +import User from '../models/community/user.js'; +import { GERMAN_FOR_BISAYA_PHASE1_DIDACTICS } from './german-for-bisaya-phase1.js'; +import { GERMAN_FOR_BISAYA_PHASE3_DIDACTICS } from './german-for-bisaya-phase3-extension.js'; +import { GERMAN_FOR_BISAYA_PHASE4_DIDACTICS } from './german-for-bisaya-phase4-extension.js'; +import { GERMAN_FOR_BISAYA_PHASE5_DIDACTICS } from './german-for-bisaya-phase5-extension.js'; + +function withTypeName(exerciseTypeName, exercise) { + return { + ...exercise, + exerciseTypeName + }; +} + +const GERMAN_DIDACTICS = { + ...GERMAN_FOR_BISAYA_PHASE1_DIDACTICS, + ...GERMAN_FOR_BISAYA_PHASE3_DIDACTICS, + ...GERMAN_FOR_BISAYA_PHASE4_DIDACTICS, + ...GERMAN_FOR_BISAYA_PHASE5_DIDACTICS +}; + +const GENERIC_DISTRACTOR_PATTERNS = Array.from(new Set( + Object.values(GERMAN_DIDACTICS) + .flatMap((entry) => Array.isArray(entry?.corePatterns) ? entry.corePatterns : []) + .map((pattern) => String(pattern || '').trim()) + .filter(Boolean) +)).slice(0, 300); + +function normalizeText(value) { + return String(value || '') + .trim() + .replace(/\s+/g, ' '); +} + +function simpleHash(value) { + return Array.from(String(value || '')).reduce((sum, char) => sum + char.charCodeAt(0), 0); +} + +function rotateArray(values, offset) { + if (!Array.isArray(values) || values.length === 0) return []; + const normalizedOffset = ((offset % values.length) + values.length) % values.length; + return values.slice(normalizedOffset).concat(values.slice(0, normalizedOffset)); +} + +function getLessonDidactics(lesson) { + const staticDidactics = GERMAN_DIDACTICS[lesson.title] || {}; + + return { + learningGoals: Array.isArray(lesson.learningGoals) ? lesson.learningGoals : (staticDidactics.learningGoals || []), + corePatterns: (Array.isArray(lesson.corePatterns) ? lesson.corePatterns : (staticDidactics.corePatterns || [])) + .map((entry) => normalizeText(entry)) + .filter(Boolean), + grammarFocus: Array.isArray(lesson.grammarFocus) ? lesson.grammarFocus : (staticDidactics.grammarFocus || []), + speakingPrompts: Array.isArray(lesson.speakingPrompts) ? lesson.speakingPrompts : (staticDidactics.speakingPrompts || []), + practicalTasks: Array.isArray(lesson.practicalTasks) ? lesson.practicalTasks : (staticDidactics.practicalTasks || []) + }; +} + +function getScenarioPrompt(lesson, didactics) { + const speakingPrompt = Array.isArray(didactics.speakingPrompts) ? didactics.speakingPrompts[0] : null; + const practicalTask = Array.isArray(didactics.practicalTasks) ? didactics.practicalTasks[0] : null; + + if (speakingPrompt?.prompt) return speakingPrompt.prompt; + if (practicalTask?.text) return practicalTask.text; + if (lesson.description) return lesson.description; + return `Reagiere passend in einer Alltagssituation aus der Lektion "${lesson.title}".`; +} + +function getChoiceQuestion(lesson, didactics) { + const scenarioPrompt = getScenarioPrompt(lesson, didactics); + + switch (lesson.lessonType) { + case 'conversation': + return `${scenarioPrompt} Welche deutsche Formulierung passt am besten?`; + case 'grammar': + return `Welche deutsche Struktur passt am besten zum Schwerpunkt "${lesson.title}"?`; + case 'review': + return `Welche Formulierung solltest du aus "${lesson.title}" sicher wiedererkennen?`; + case 'culture': + return `Welche Formulierung passt besonders gut zum kulturellen Schwerpunkt "${lesson.title}"?`; + case 'vocab': + default: + return `Welche Formulierung gehört thematisch zu "${lesson.title}"?`; + } +} + +function pickDistractors(pattern, allPatterns, count) { + return allPatterns + .filter((entry) => entry !== pattern) + .slice(0, count); +} + +function buildChoiceExercise(lesson, didactics, pattern, allPatterns, variant = 0) { + const distractors = pickDistractors(pattern, allPatterns, 3); + if (distractors.length < 3) return null; + + const options = rotateArray([pattern, ...distractors], simpleHash(`${lesson.title}:${pattern}:${variant}`) % 4); + + return { + exerciseTypeId: 2, + title: `${lesson.title}: Passende Formulierung wählen`, + instruction: 'Wähle die natürlichste deutsche Formulierung für die Situation oder den Schwerpunkt der Lektion.', + questionData: { + type: 'multiple_choice', + question: getChoiceQuestion(lesson, didactics), + options + }, + answerData: { + type: 'multiple_choice', + correctAnswer: options.indexOf(pattern) + }, + explanation: `"${pattern}" ist ein zentrales deutsches Muster dieser Lektion.` + }; +} + +function pickGapTarget(pattern) { + const tokens = normalizeText(pattern) + .split(' ') + .map((token) => token.trim()) + .filter(Boolean); + + const candidates = tokens + .map((token, index) => ({ token, index, score: token.replace(/[.,?!]/g, '').length })) + .filter(({ token }) => token.replace(/[.,?!]/g, '').length >= 3); + + if (candidates.length === 0) return null; + + candidates.sort((left, right) => right.score - left.score); + return candidates[0]; +} + +function buildGapExercise(lessonTitle, pattern) { + const gapTarget = pickGapTarget(pattern); + if (!gapTarget) return null; + + const tokens = normalizeText(pattern).split(' '); + tokens[gapTarget.index] = '{gap}'; + + return { + exerciseTypeId: 1, + title: `${lessonTitle}: Muster vervollständigen`, + instruction: 'Fülle die Lücke mit dem passenden deutschen Ausdruck.', + questionData: { + type: 'gap_fill', + text: tokens.join(' '), + gaps: 1 + }, + answerData: { + type: 'gap_fill', + answers: [gapTarget.token.replace(/[.,?!]/g, '')] + }, + explanation: `Das vollständige deutsche Kernmuster lautet: "${pattern}".` + }; +} + +function buildSentenceExercise(lessonTitle, pattern) { + const tokens = normalizeText(pattern) + .replace(/[?!]/g, '') + .split(' ') + .filter(Boolean); + + if (tokens.length < 2) return null; + + return { + exerciseTypeId: 3, + title: `${lessonTitle}: Satz bauen`, + instruction: 'Ordne die Wörter zu einem korrekten deutschen Satz.', + questionData: { + type: 'sentence_building', + question: `Baue das Kernmuster aus der Lektion "${lessonTitle}".`, + tokens + }, + answerData: { + correct: [normalizeText(pattern)] + }, + explanation: `Dieses Kernmuster gehört zur Lektion "${lessonTitle}".` + }; +} + +function buildSpeakingExercise(lessonTitle, didactics, fallbackPattern) { + const speakingPrompt = Array.isArray(didactics.speakingPrompts) ? didactics.speakingPrompts[0] : null; + const expectedText = normalizeText(speakingPrompt?.cue || fallbackPattern); + if (!expectedText) return null; + + const keywords = expectedText + .toLowerCase() + .replace(/[.,?!]/g, '') + .split(' ') + .filter((token) => token.length >= 3) + .slice(0, 5); + + return { + exerciseTypeId: 8, + title: `${lessonTitle}: Frei sprechen`, + instruction: 'Sprich das zentrale deutsche Muster frei nach.', + questionData: { + type: 'speaking_from_memory', + question: speakingPrompt?.prompt || `Sprich ein zentrales Muster aus der Lektion "${lessonTitle}".`, + expectedText, + keywords + }, + answerData: { + type: 'speaking_from_memory' + }, + explanation: 'Wichtig sind ein flüssiger Abruf und die zentralen deutschen Schlüsselwörter.' + }; +} + +function buildSituationalExercise(lessonTitle, didactics, fallbackPattern) { + const speakingPrompt = Array.isArray(didactics.speakingPrompts) ? didactics.speakingPrompts[0] : null; + const modelAnswer = normalizeText(speakingPrompt?.cue || fallbackPattern); + if (!modelAnswer) return null; + + const keywords = modelAnswer + .toLowerCase() + .replace(/[.,?!]/g, '') + .split(' ') + .filter((token) => token.length >= 3) + .slice(0, 5); + + return withTypeName('situational_response', { + title: `${lessonTitle}: Situativ reagieren`, + instruction: 'Antworte kurz und passend auf Deutsch.', + questionData: { + type: 'situational_response', + question: speakingPrompt?.prompt || `Reagiere passend mit einem Ausdruck aus der Lektion "${lessonTitle}".`, + keywords + }, + answerData: { + modelAnswer, + keywords + }, + explanation: `Das Kernmuster "${modelAnswer}" passt natürlich zu dieser Situation.` + }); +} + +function buildCultureExercise(lesson, pattern, allPatterns) { + const distractors = pickDistractors(pattern, allPatterns, 3); + if (distractors.length < 3) return null; + + const options = rotateArray([pattern, ...distractors], simpleHash(`${lesson.title}:culture`) % 4); + const culturalNote = normalizeText(lesson.culturalNotes || ''); + + return { + exerciseTypeId: 2, + title: `${lesson.title}: Kulturell einordnen`, + instruction: 'Ordne den Ausdruck dem kulturellen Schwerpunkt der Lektion zu.', + questionData: { + type: 'multiple_choice', + question: culturalNote + ? `${culturalNote} Welche Formulierung passt dazu besonders gut?` + : `Welche Formulierung passt besonders gut zum Schwerpunkt "${lesson.title}"?`, + options + }, + answerData: { + type: 'multiple_choice', + correctAnswer: options.indexOf(pattern) + }, + explanation: `"${pattern}" ist eng mit dem kulturellen Schwerpunkt dieser Lektion verbunden.` + }; +} + +function generateExercisesFromDidactics(lesson) { + const didactics = getLessonDidactics(lesson); + const corePatterns = didactics.corePatterns; + if (corePatterns.length === 0) return []; + + const patternA = corePatterns[0]; + const patternB = corePatterns[1] || corePatterns[0]; + const pool = Array.from(new Set([...corePatterns, ...GENERIC_DISTRACTOR_PATTERNS])); + + if (lesson.lessonType === 'conversation') { + return [ + buildChoiceExercise(lesson, didactics, patternA, pool, 0), + buildGapExercise(lesson.title, patternA), + buildSentenceExercise(lesson.title, patternB), + buildSituationalExercise(lesson.title, didactics, patternA), + buildSpeakingExercise(lesson.title, didactics, patternB) + ].filter(Boolean); + } + + if (lesson.lessonType === 'grammar') { + return [ + buildChoiceExercise(lesson, didactics, patternA, pool, 0), + buildChoiceExercise(lesson, didactics, patternB, pool, 1), + buildGapExercise(lesson.title, patternA), + buildSentenceExercise(lesson.title, patternB), + buildSpeakingExercise(lesson.title, didactics, patternA) + ].filter(Boolean); + } + + if (lesson.lessonType === 'review' || lesson.didacticMode === 'intensive_review') { + return [ + buildChoiceExercise(lesson, didactics, patternA, pool, 0), + buildChoiceExercise(lesson, didactics, patternB, pool, 1), + buildGapExercise(lesson.title, patternA), + buildSentenceExercise(lesson.title, patternB), + buildSituationalExercise(lesson.title, didactics, patternA), + buildSpeakingExercise(lesson.title, didactics, patternB) + ].filter(Boolean); + } + + if (lesson.lessonType === 'culture') { + return [ + buildCultureExercise(lesson, patternA, pool), + buildGapExercise(lesson.title, patternA), + buildSpeakingExercise(lesson.title, didactics, patternB) + ].filter(Boolean); + } + + return [ + buildChoiceExercise(lesson, didactics, patternA, pool, 0), + buildChoiceExercise(lesson, didactics, patternB, pool, 1), + buildGapExercise(lesson.title, patternA), + buildSentenceExercise(lesson.title, patternB) + ].filter(Boolean); +} + +const GERMAN_EXERCISES = { + 'Begrüßung & Vorstellung': [ + { + exerciseTypeId: 2, + title: 'Vorstellung erkennen', + instruction: 'Wähle die passendste deutsche Vorstellung.', + questionData: { + type: 'multiple_choice', + question: 'Du triffst jemanden zum ersten Mal. Welche Formulierung passt?', + options: ['Hallo, ich heiße Maria.', 'Ich habe Maria.', 'Wo Maria?', 'Nicht Maria.'] + }, + answerData: { + type: 'multiple_choice', + correctAnswer: 0 + }, + explanation: '"Hallo, ich heiße Maria." ist eine natürliche deutsche Selbstvorstellung.' + }, + { + exerciseTypeId: 1, + title: 'Herkunft ergänzen', + instruction: 'Fülle die Lücke mit dem passenden Ausdruck.', + questionData: { + type: 'gap_fill', + text: 'Ich komme {gap} Cebu.', + gaps: 1 + }, + answerData: { + type: 'gap_fill', + answers: ['aus'] + }, + explanation: 'Im Deutschen sagt man "Ich komme aus Cebu."' + }, + withTypeName('situational_response', { + title: 'Kurz reagieren bei der Begrüßung', + instruction: 'Antworte kurz und passend auf Deutsch.', + questionData: { + type: 'situational_response', + question: 'Jemand sagt: "Hallo, ich heiße Anna." Wie reagierst du mit deiner eigenen Vorstellung?', + keywords: ['hallo', 'heiße', 'ich'] + }, + answerData: { + modelAnswer: 'Hallo, ich heiße Maria.', + keywords: ['hallo', 'heiße', 'ich'] + }, + explanation: 'Die sicherste frühe Reaktion ist eine eigene kurze Vorstellung mit "ich heiße".' + }) + ], + 'der / die / das - Einstieg': [ + { + exerciseTypeId: 2, + title: 'Artikel als Chunk erkennen', + instruction: 'Wähle die Form mit dem richtigen Artikel.', + questionData: { + type: 'multiple_choice', + question: 'Welche Form ist als Lern-Chunk richtig?', + options: ['der Tisch', 'die Tisch', 'das Tasche', 'der Kind'] + }, + answerData: { + type: 'multiple_choice', + correctAnswer: 0 + }, + explanation: 'Nomen sollen möglichst zusammen mit dem Artikel gelernt werden.' + }, + { + exerciseTypeId: 1, + title: 'Artikel ergänzen', + instruction: 'Fülle den passenden Artikel ein.', + questionData: { + type: 'gap_fill', + text: '{gap} Tasche', + gaps: 1 + }, + answerData: { + type: 'gap_fill', + answers: ['die'] + }, + explanation: 'Es heißt "die Tasche".' + }, + { + exerciseTypeId: 3, + title: 'Chunk richtig bauen', + instruction: 'Ordne die Wörter zu einem korrekten Lern-Chunk.', + questionData: { + type: 'sentence_building', + question: 'Baue den richtigen Chunk für "child".', + tokens: ['das', 'Kind'] + }, + answerData: { + correct: ['das Kind'] + }, + explanation: 'Auch sehr kurze Strukturen sollen als vollständiger Chunk gelernt werden.' + } + ], + 'nicht / kein - Einstieg': [ + { + exerciseTypeId: 2, + title: 'Negation unterscheiden', + instruction: 'Wähle die passende deutsche Negation.', + questionData: { + type: 'multiple_choice', + question: 'Welche Formulierung ist richtig?', + options: ['Ich habe kein Geld.', 'Ich habe nicht Geld.', 'Ich bin kein müde.', 'Ich kein habe Geld.'] + }, + answerData: { + type: 'multiple_choice', + correctAnswer: 0 + }, + explanation: '"Kein" steht hier vor dem Nomen, deshalb ist "Ich habe kein Geld." richtig.' + }, + { + exerciseTypeId: 2, + title: 'Adjektiv negieren', + instruction: 'Wähle die richtige Form für eine Aussage mit Adjektiv.', + questionData: { + type: 'multiple_choice', + question: 'Wie sagst du korrekt: "Ich bin nicht müde"?', + options: ['Ich bin nicht müde.', 'Ich bin kein müde.', 'Ich nicht bin müde.', 'Ich bin müde kein.'] + }, + answerData: { + type: 'multiple_choice', + correctAnswer: 0 + }, + explanation: 'Adjektive und Aussagen werden im Deutschen oft mit "nicht" negiert.' + }, + { + exerciseTypeId: 1, + title: 'Kein ergänzen', + instruction: 'Ergänze die passende Negation.', + questionData: { + type: 'gap_fill', + text: 'Ich habe {gap} Zeit.', + gaps: 1 + }, + answerData: { + type: 'gap_fill', + answers: ['keine'] + }, + explanation: 'Bei "Zeit" braucht man hier "keine".' + } + ], + 'wo / wohin - Kontrast': [ + { + exerciseTypeId: 2, + title: 'Ort oder Richtung?', + instruction: 'Wähle die Frage nach einem Ziel.', + questionData: { + type: 'multiple_choice', + question: 'Welche Frage passt, wenn du nach dem Ziel fragst?', + options: ['Wohin gehst du?', 'Wo gehst du?', 'Wie gehst du?', 'Wann gehst du?'] + }, + answerData: { + type: 'multiple_choice', + correctAnswer: 0 + }, + explanation: '"Wohin" fragt nach einer Richtung oder einem Ziel.' + }, + { + exerciseTypeId: 1, + title: 'Fragewort ergänzen', + instruction: 'Setze das passende Fragewort ein.', + questionData: { + type: 'gap_fill', + text: '{gap} bist du? Ich bin zu Hause.', + gaps: 1 + }, + answerData: { + type: 'gap_fill', + answers: ['Wo'] + }, + explanation: 'Bei einem Ort fragt man im Deutschen mit "Wo".' + } + ], + 'du / Sie - Einstieg': [ + { + exerciseTypeId: 2, + title: 'Höfliche Anrede erkennen', + instruction: 'Wähle die höfliche deutsche Form.', + questionData: { + type: 'multiple_choice', + question: 'Du sprichst eine fremde erwachsene Person höflich an. Welche Frage passt?', + options: ['Wie heißen Sie?', 'Wie heißt du?', 'Wo bist du?', 'Wie bist du?'] + }, + answerData: { + type: 'multiple_choice', + correctAnswer: 0 + }, + explanation: '"Sie" markiert im Deutschen die höfliche Anrede.' + }, + { + exerciseTypeId: 1, + title: 'Anredeform ergänzen', + instruction: 'Fülle die passende Anredeform ein.', + questionData: { + type: 'gap_fill', + text: 'Können {gap} mir helfen?', + gaps: 1 + }, + answerData: { + type: 'gap_fill', + answers: ['Sie'] + }, + explanation: 'In einer höflichen Bitte heißt es "Können Sie mir helfen?".' + } + ], + 'Perfekt - Einstieg': [ + { + exerciseTypeId: 2, + title: 'Perfektform erkennen', + instruction: 'Wähle die passende deutsche Vergangenheitsform.', + questionData: { + type: 'multiple_choice', + question: 'Welche Form ist ein korrektes frühes Perfekt?', + options: ['Ich habe gearbeitet.', 'Ich habe arbeiten.', 'Ich bin gearbeitet.', 'Ich arbeitete habe.'] + }, + answerData: { + type: 'multiple_choice', + correctAnswer: 0 + }, + explanation: 'Viele frühe Alltagsverben bilden das Perfekt mit "haben" + Partizip II.' + }, + { + exerciseTypeId: 1, + title: 'Hilfsverb ergänzen', + instruction: 'Ergänze das passende Hilfsverb.', + questionData: { + type: 'gap_fill', + text: 'Ich {gap} gestern gearbeitet.', + gaps: 1 + }, + answerData: { + type: 'gap_fill', + answers: ['habe'] + }, + explanation: 'Im Perfekt braucht man hier das Hilfsverb "habe".' + } + ] +}; + +async function resolveExerciseTypeId(exercise) { + if (exercise.exerciseTypeId) { + return exercise.exerciseTypeId; + } + + const trimmedName = + exercise.exerciseTypeName != null && exercise.exerciseTypeName !== '' + ? String(exercise.exerciseTypeName).trim() + : ''; + + if (!trimmedName) { + throw new Error(`Kein exerciseTypeId oder exerciseTypeName für Übung "${exercise.title || 'unbenannt'}" definiert`); + } + + const [type] = await sequelize.query( + `SELECT id FROM community.vocab_grammar_exercise_type WHERE name = :name LIMIT 1`, + { + replacements: { name: trimmedName }, + type: sequelize.QueryTypes.SELECT + } + ); + + if (!type) { + throw new Error(`Übungstyp "${trimmedName}" nicht gefunden`); + } + + return Number(type.id); +} + +async function findOrCreateSystemUser() { + let systemUser = await User.findOne({ where: { username: 'system' } }); + if (!systemUser) { + systemUser = await User.findOne({ where: { username: 'admin' } }); + } + if (!systemUser) { + throw new Error('System user not found'); + } + return systemUser; +} + +function getExercisesForLesson(lesson) { + if (GERMAN_EXERCISES[lesson.title]) { + return GERMAN_EXERCISES[lesson.title]; + } + + for (const [key, exercises] of Object.entries(GERMAN_EXERCISES)) { + if (lesson.title.includes(key) || key.includes(lesson.title)) { + return exercises; + } + } + + return generateExercisesFromDidactics(lesson); +} + +async function createGermanForBisayaCourseContent() { + await sequelize.authenticate(); + console.log('Datenbankverbindung erfolgreich hergestellt.\n'); + + const systemUser = await findOrCreateSystemUser(); + console.log(`Verwende System-Benutzer: ${systemUser.username} (ID: ${systemUser.id})\n`); + + const [germanLanguage] = await sequelize.query( + `SELECT id FROM community.vocab_language WHERE name = 'Deutsch' LIMIT 1`, + { + type: sequelize.QueryTypes.SELECT + } + ); + + const [bisayaLanguage] = await sequelize.query( + `SELECT id FROM community.vocab_language WHERE name = 'Bisaya' LIMIT 1`, + { + type: sequelize.QueryTypes.SELECT + } + ); + + if (!germanLanguage || !bisayaLanguage) { + throw new Error('Deutsch oder Bisaya als Sprache nicht gefunden'); + } + + const courses = await sequelize.query( + `SELECT id, title, owner_user_id AS "ownerUserId" + FROM community.vocab_course + WHERE language_id = :languageId + AND native_language_id = :nativeLanguageId + AND title LIKE 'Deutsch für Bisaya-Lernende%'`, + { + replacements: { + languageId: germanLanguage.id, + nativeLanguageId: bisayaLanguage.id + }, + type: sequelize.QueryTypes.SELECT + } + ); + + console.log(`Gefunden: ${courses.length} Deutsch-für-Bisaya-Kurse\n`); + + let totalExercisesAdded = 0; + let totalLessonsProcessed = 0; + const replaceLessons = new Set(Object.keys(GERMAN_EXERCISES)); + + for (const course of courses) { + console.log(`📚 Kurs: ${course.title} (ID: ${course.id})`); + + const lessons = await VocabCourseLesson.findAll({ + where: { courseId: course.id }, + order: [['lessonNumber', 'ASC']] + }); + + console.log(` ${lessons.length} Lektionen gefunden\n`); + + for (const lesson of lessons) { + const exercises = getExercisesForLesson(lesson); + if (exercises.length === 0) { + console.log(` ⚠️ Lektion ${lesson.lessonNumber}: "${lesson.title}" - keine Übungen definiert`); + continue; + } + + const existingCount = await VocabGrammarExercise.count({ where: { lessonId: lesson.id } }); + const replaceExisting = replaceLessons.has(lesson.title); + + if (existingCount > 0 && !replaceExisting) { + console.log(` ⏭️ Lektion ${lesson.lessonNumber}: "${lesson.title}" - bereits ${existingCount} Übung(en) vorhanden`); + continue; + } + + if (replaceExisting && existingCount > 0) { + const deleted = await VocabGrammarExercise.destroy({ where: { lessonId: lesson.id } }); + console.log(` 🗑️ Lektion ${lesson.lessonNumber}: "${lesson.title}" - ${deleted} bestehende Übung(en) entfernt`); + } + + let exerciseNumber = 1; + for (const exerciseData of exercises) { + const exerciseTypeId = await resolveExerciseTypeId(exerciseData); + await VocabGrammarExercise.create({ + lessonId: lesson.id, + exerciseTypeId, + exerciseNumber: exerciseNumber++, + title: exerciseData.title, + instruction: exerciseData.instruction, + questionData: JSON.stringify(exerciseData.questionData), + answerData: JSON.stringify(exerciseData.answerData), + explanation: exerciseData.explanation, + createdByUserId: course.ownerUserId || systemUser.id + }); + totalExercisesAdded++; + } + + console.log(` ✅ Lektion ${lesson.lessonNumber}: "${lesson.title}" - ${exercises.length} Übung(en) erstellt`); + totalLessonsProcessed++; + } + + console.log(''); + } + + console.log('\n🎉 Zusammenfassung:'); + console.log(` ${totalLessonsProcessed} Lektionen bearbeitet`); + console.log(` ${totalExercisesAdded} Übungen erstellt`); +} + +createGermanForBisayaCourseContent() + .then(() => { + sequelize.close(); + process.exit(0); + }) + .catch((error) => { + console.error('❌ Fehler:', error); + sequelize.close(); + process.exit(1); + }); diff --git a/backend/scripts/create-german-for-bisaya-course.js b/backend/scripts/create-german-for-bisaya-course.js new file mode 100644 index 0000000..36a0a89 --- /dev/null +++ b/backend/scripts/create-german-for-bisaya-course.js @@ -0,0 +1,145 @@ +#!/usr/bin/env node +/** + * Erstellt den Phase-1-Deutschkurs für Bisaya-Lernende. + * + * Verwendung: + * node backend/scripts/create-german-for-bisaya-course.js + */ + +import crypto from 'crypto'; +import { Op } from 'sequelize'; +import { sequelize } from '../utils/sequelize.js'; +import VocabCourse from '../models/community/vocab_course.js'; +import VocabCourseLesson from '../models/community/vocab_course_lesson.js'; +import User from '../models/community/user.js'; +import { GERMAN_FOR_BISAYA_PHASE1_DIDACTICS, GERMAN_FOR_BISAYA_PHASE1_LESSONS } from './german-for-bisaya-phase1.js'; +import { GERMAN_FOR_BISAYA_PHASE3_DIDACTICS, GERMAN_FOR_BISAYA_PHASE3_LESSONS } from './german-for-bisaya-phase3-extension.js'; +import { GERMAN_FOR_BISAYA_PHASE4_DIDACTICS, GERMAN_FOR_BISAYA_PHASE4_LESSONS } from './german-for-bisaya-phase4-extension.js'; +import { GERMAN_FOR_BISAYA_PHASE5_DIDACTICS, GERMAN_FOR_BISAYA_PHASE5_LESSONS } from './german-for-bisaya-phase5-extension.js'; +import { getGermanForBisayaLessonPedagogy } from './german-for-bisaya-phase2-pedagogy.js'; + +const ALL_GERMAN_FOR_BISAYA_LESSONS = [ + ...GERMAN_FOR_BISAYA_PHASE1_LESSONS, + ...GERMAN_FOR_BISAYA_PHASE3_LESSONS, + ...GERMAN_FOR_BISAYA_PHASE4_LESSONS, + ...GERMAN_FOR_BISAYA_PHASE5_LESSONS +]; + +const ALL_GERMAN_FOR_BISAYA_DIDACTICS = { + ...GERMAN_FOR_BISAYA_PHASE1_DIDACTICS, + ...GERMAN_FOR_BISAYA_PHASE3_DIDACTICS, + ...GERMAN_FOR_BISAYA_PHASE4_DIDACTICS, + ...GERMAN_FOR_BISAYA_PHASE5_DIDACTICS +}; + +async function getLanguageId(languageName) { + const [language] = await sequelize.query( + `SELECT id FROM community.vocab_language WHERE name = :name LIMIT 1`, + { + replacements: { name: languageName }, + type: sequelize.QueryTypes.SELECT + } + ); + + if (!language) { + throw new Error(`Sprache "${languageName}" nicht gefunden`); + } + + return Number(language.id); +} + +async function getOwnerByHashedId(ownerHashedId) { + const user = await User.findOne({ where: { hashedId: ownerHashedId } }); + if (!user) { + throw new Error(`Benutzer mit hashedId "${ownerHashedId}" nicht gefunden`); + } + return user; +} + +async function createGermanForBisayaCourse(ownerHashedId) { + await sequelize.authenticate(); + + const owner = await getOwnerByHashedId(ownerHashedId); + const germanLanguageId = await getLanguageId('Deutsch'); + const bisayaLanguageId = await getLanguageId('Bisaya'); + + const existingCourse = await VocabCourse.findOne({ + where: { + ownerUserId: owner.id, + languageId: germanLanguageId, + nativeLanguageId: bisayaLanguageId, + title: { + [Op.like]: 'Deutsch für Bisaya-Lernende%' + } + } + }); + + if (existingCourse) { + console.log(`Kurs bereits vorhanden: ${existingCourse.title} (ID: ${existingCourse.id})`); + return existingCourse; + } + + const shareCode = crypto.randomBytes(8).toString('hex'); + const course = await VocabCourse.create({ + ownerUserId: owner.id, + title: 'Deutsch für Bisaya-Lernende - Alltag & Stabilisierung', + description: '15 Wochen Deutsch-Lernpfad aus Sicht von Bisaya-Lernenden mit Alltag, Fehlertraining, Intensivwiederholung und Stabilisierung.', + languageId: germanLanguageId, + nativeLanguageId: bisayaLanguageId, + difficultyLevel: 1, + isPublic: false, + shareCode + }); + + for (const lessonData of ALL_GERMAN_FOR_BISAYA_LESSONS) { + const didactics = ALL_GERMAN_FOR_BISAYA_DIDACTICS[lessonData.title] || {}; + const pedagogy = getGermanForBisayaLessonPedagogy(lessonData.num, lessonData.type); + + await VocabCourseLesson.create({ + courseId: course.id, + chapterId: null, + lessonNumber: lessonData.num, + title: lessonData.title, + description: lessonData.desc, + weekNumber: lessonData.week, + dayNumber: lessonData.day, + lessonType: lessonData.type, + didacticMode: pedagogy.didacticMode, + phaseLabel: pedagogy.phaseLabel, + blockNumber: pedagogy.blockNumber, + difficultyWeight: pedagogy.difficultyWeight, + newUnitTarget: pedagogy.newUnitTarget, + reviewWeight: pedagogy.reviewWeight, + isIntensiveReview: pedagogy.isIntensiveReview, + culturalNotes: lessonData.cultural, + learningGoals: didactics.learningGoals || null, + corePatterns: didactics.corePatterns || null, + grammarFocus: didactics.grammarFocus || null, + speakingPrompts: didactics.speakingPrompts || null, + practicalTasks: didactics.practicalTasks || null, + targetMinutes: lessonData.targetMin, + targetScorePercent: lessonData.targetScore, + requiresReview: lessonData.review + }); + } + + console.log(`Kurs erstellt: ${course.title} (ID: ${course.id})`); + console.log(`Lektionen erstellt: ${ALL_GERMAN_FOR_BISAYA_LESSONS.length}`); + return course; +} + +if (import.meta.url === `file://${process.argv[1]}`) { + const ownerHashedId = process.argv[2]; + + if (!ownerHashedId) { + console.error('Verwendung: node backend/scripts/create-german-for-bisaya-course.js '); + process.exit(1); + } + + createGermanForBisayaCourse(ownerHashedId) + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); +} diff --git a/backend/scripts/extend-german-for-bisaya-course-phase3.js b/backend/scripts/extend-german-for-bisaya-course-phase3.js new file mode 100644 index 0000000..f20e836 --- /dev/null +++ b/backend/scripts/extend-german-for-bisaya-course-phase3.js @@ -0,0 +1,84 @@ +#!/usr/bin/env node + +import { sequelize } from '../utils/sequelize.js'; +import VocabCourse from '../models/community/vocab_course.js'; +import VocabCourseLesson from '../models/community/vocab_course_lesson.js'; +import { GERMAN_FOR_BISAYA_PHASE3_DIDACTICS, GERMAN_FOR_BISAYA_PHASE3_LESSONS } from './german-for-bisaya-phase3-extension.js'; +import { getGermanForBisayaLessonPedagogy } from './german-for-bisaya-phase2-pedagogy.js'; + +async function getLanguageId(name) { + const [language] = await sequelize.query( + `SELECT id FROM community.vocab_language WHERE name = :name LIMIT 1`, + { replacements: { name }, type: sequelize.QueryTypes.SELECT } + ); + if (!language) throw new Error(`Sprache "${name}" nicht gefunden`); + return Number(language.id); +} + +async function extendGermanForBisayaPhase3() { + await sequelize.authenticate(); + const germanLanguageId = await getLanguageId('Deutsch'); + const bisayaLanguageId = await getLanguageId('Bisaya'); + + const courses = await VocabCourse.findAll({ + where: { + languageId: germanLanguageId, + nativeLanguageId: bisayaLanguageId + } + }); + + for (const course of courses) { + if (!String(course.title || '').startsWith('Deutsch für Bisaya-Lernende')) continue; + + for (const lessonData of GERMAN_FOR_BISAYA_PHASE3_LESSONS) { + const existing = await VocabCourseLesson.findOne({ + where: { courseId: course.id, lessonNumber: lessonData.num } + }); + if (existing) continue; + + const didactics = GERMAN_FOR_BISAYA_PHASE3_DIDACTICS[lessonData.title] || {}; + const pedagogy = getGermanForBisayaLessonPedagogy(lessonData.num, lessonData.type); + + await VocabCourseLesson.create({ + courseId: course.id, + chapterId: null, + lessonNumber: lessonData.num, + title: lessonData.title, + description: lessonData.desc, + weekNumber: lessonData.week, + dayNumber: lessonData.day, + lessonType: lessonData.type, + didacticMode: pedagogy.didacticMode, + phaseLabel: pedagogy.phaseLabel, + blockNumber: pedagogy.blockNumber, + difficultyWeight: pedagogy.difficultyWeight, + newUnitTarget: pedagogy.newUnitTarget, + reviewWeight: pedagogy.reviewWeight, + isIntensiveReview: pedagogy.isIntensiveReview, + culturalNotes: lessonData.cultural, + learningGoals: didactics.learningGoals || null, + corePatterns: didactics.corePatterns || null, + grammarFocus: didactics.grammarFocus || null, + speakingPrompts: didactics.speakingPrompts || null, + practicalTasks: didactics.practicalTasks || null, + targetMinutes: lessonData.targetMin, + targetScorePercent: lessonData.targetScore, + requiresReview: lessonData.review + }); + } + + course.title = 'Deutsch für Bisaya-Lernende - Alltagspfad'; + course.description = '9 Wochen Deutsch-Lernpfad aus Sicht von Bisaya-Lernenden mit Schnellstart, Alltag und typischen Fehlerfeldern.'; + await course.save(); + console.log(`Phase 3 erweitert: ${course.title} (ID: ${course.id})`); + } +} + +if (import.meta.url === `file://${process.argv[1]}`) { + extendGermanForBisayaPhase3() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); +} diff --git a/backend/scripts/extend-german-for-bisaya-course-phase4.js b/backend/scripts/extend-german-for-bisaya-course-phase4.js new file mode 100644 index 0000000..acdd381 --- /dev/null +++ b/backend/scripts/extend-german-for-bisaya-course-phase4.js @@ -0,0 +1,84 @@ +#!/usr/bin/env node + +import { sequelize } from '../utils/sequelize.js'; +import VocabCourse from '../models/community/vocab_course.js'; +import VocabCourseLesson from '../models/community/vocab_course_lesson.js'; +import { GERMAN_FOR_BISAYA_PHASE4_DIDACTICS, GERMAN_FOR_BISAYA_PHASE4_LESSONS } from './german-for-bisaya-phase4-extension.js'; +import { getGermanForBisayaLessonPedagogy } from './german-for-bisaya-phase2-pedagogy.js'; + +async function getLanguageId(name) { + const [language] = await sequelize.query( + `SELECT id FROM community.vocab_language WHERE name = :name LIMIT 1`, + { replacements: { name }, type: sequelize.QueryTypes.SELECT } + ); + if (!language) throw new Error(`Sprache "${name}" nicht gefunden`); + return Number(language.id); +} + +async function extendGermanForBisayaPhase4() { + await sequelize.authenticate(); + const germanLanguageId = await getLanguageId('Deutsch'); + const bisayaLanguageId = await getLanguageId('Bisaya'); + + const courses = await VocabCourse.findAll({ + where: { + languageId: germanLanguageId, + nativeLanguageId: bisayaLanguageId + } + }); + + for (const course of courses) { + if (!String(course.title || '').startsWith('Deutsch für Bisaya-Lernende')) continue; + + for (const lessonData of GERMAN_FOR_BISAYA_PHASE4_LESSONS) { + const existing = await VocabCourseLesson.findOne({ + where: { courseId: course.id, lessonNumber: lessonData.num } + }); + if (existing) continue; + + const didactics = GERMAN_FOR_BISAYA_PHASE4_DIDACTICS[lessonData.title] || {}; + const pedagogy = getGermanForBisayaLessonPedagogy(lessonData.num, lessonData.type); + + await VocabCourseLesson.create({ + courseId: course.id, + chapterId: null, + lessonNumber: lessonData.num, + title: lessonData.title, + description: lessonData.desc, + weekNumber: lessonData.week, + dayNumber: lessonData.day, + lessonType: lessonData.type, + didacticMode: pedagogy.didacticMode, + phaseLabel: pedagogy.phaseLabel, + blockNumber: pedagogy.blockNumber, + difficultyWeight: pedagogy.difficultyWeight, + newUnitTarget: pedagogy.newUnitTarget, + reviewWeight: pedagogy.reviewWeight, + isIntensiveReview: pedagogy.isIntensiveReview, + culturalNotes: lessonData.cultural, + learningGoals: didactics.learningGoals || null, + corePatterns: didactics.corePatterns || null, + grammarFocus: didactics.grammarFocus || null, + speakingPrompts: didactics.speakingPrompts || null, + practicalTasks: didactics.practicalTasks || null, + targetMinutes: lessonData.targetMin, + targetScorePercent: lessonData.targetScore, + requiresReview: lessonData.review + }); + } + + course.title = 'Deutsch für Bisaya-Lernende - Alltagspfad'; + course.description = '12 Wochen Deutsch-Lernpfad aus Sicht von Bisaya-Lernenden mit Schnellstart, Alltag und gezieltem Fehlertraining.'; + await course.save(); + console.log(`Phase 4 erweitert: ${course.title} (ID: ${course.id})`); + } +} + +if (import.meta.url === `file://${process.argv[1]}`) { + extendGermanForBisayaPhase4() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); +} diff --git a/backend/scripts/extend-german-for-bisaya-course-phase5.js b/backend/scripts/extend-german-for-bisaya-course-phase5.js new file mode 100644 index 0000000..3d01e26 --- /dev/null +++ b/backend/scripts/extend-german-for-bisaya-course-phase5.js @@ -0,0 +1,84 @@ +#!/usr/bin/env node + +import { sequelize } from '../utils/sequelize.js'; +import VocabCourse from '../models/community/vocab_course.js'; +import VocabCourseLesson from '../models/community/vocab_course_lesson.js'; +import { GERMAN_FOR_BISAYA_PHASE5_DIDACTICS, GERMAN_FOR_BISAYA_PHASE5_LESSONS } from './german-for-bisaya-phase5-extension.js'; +import { getGermanForBisayaLessonPedagogy } from './german-for-bisaya-phase2-pedagogy.js'; + +async function getLanguageId(name) { + const [language] = await sequelize.query( + `SELECT id FROM community.vocab_language WHERE name = :name LIMIT 1`, + { replacements: { name }, type: sequelize.QueryTypes.SELECT } + ); + if (!language) throw new Error(`Sprache "${name}" nicht gefunden`); + return Number(language.id); +} + +async function extendGermanForBisayaPhase5() { + await sequelize.authenticate(); + const germanLanguageId = await getLanguageId('Deutsch'); + const bisayaLanguageId = await getLanguageId('Bisaya'); + + const courses = await VocabCourse.findAll({ + where: { + languageId: germanLanguageId, + nativeLanguageId: bisayaLanguageId + } + }); + + for (const course of courses) { + if (!String(course.title || '').startsWith('Deutsch für Bisaya-Lernende')) continue; + + for (const lessonData of GERMAN_FOR_BISAYA_PHASE5_LESSONS) { + const existing = await VocabCourseLesson.findOne({ + where: { courseId: course.id, lessonNumber: lessonData.num } + }); + if (existing) continue; + + const didactics = GERMAN_FOR_BISAYA_PHASE5_DIDACTICS[lessonData.title] || {}; + const pedagogy = getGermanForBisayaLessonPedagogy(lessonData.num, lessonData.type); + + await VocabCourseLesson.create({ + courseId: course.id, + chapterId: null, + lessonNumber: lessonData.num, + title: lessonData.title, + description: lessonData.desc, + weekNumber: lessonData.week, + dayNumber: lessonData.day, + lessonType: lessonData.type, + didacticMode: pedagogy.didacticMode, + phaseLabel: pedagogy.phaseLabel, + blockNumber: pedagogy.blockNumber, + difficultyWeight: pedagogy.difficultyWeight, + newUnitTarget: pedagogy.newUnitTarget, + reviewWeight: pedagogy.reviewWeight, + isIntensiveReview: pedagogy.isIntensiveReview, + culturalNotes: lessonData.cultural, + learningGoals: didactics.learningGoals || null, + corePatterns: didactics.corePatterns || null, + grammarFocus: didactics.grammarFocus || null, + speakingPrompts: didactics.speakingPrompts || null, + practicalTasks: didactics.practicalTasks || null, + targetMinutes: lessonData.targetMin, + targetScorePercent: lessonData.targetScore, + requiresReview: lessonData.review + }); + } + + course.title = 'Deutsch für Bisaya-Lernende - Alltag & Stabilisierung'; + course.description = '15 Wochen Deutsch-Lernpfad aus Sicht von Bisaya-Lernenden mit Alltag, Fehlertraining, Intensivwiederholung und Stabilisierung.'; + await course.save(); + console.log(`Phase 5 erweitert: ${course.title} (ID: ${course.id})`); + } +} + +if (import.meta.url === `file://${process.argv[1]}`) { + extendGermanForBisayaPhase5() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); +} diff --git a/backend/scripts/german-for-bisaya-phase1.js b/backend/scripts/german-for-bisaya-phase1.js new file mode 100644 index 0000000..bb5d6a0 --- /dev/null +++ b/backend/scripts/german-for-bisaya-phase1.js @@ -0,0 +1,345 @@ +export const GERMAN_FOR_BISAYA_PHASE1_LESSONS = [ + { week: 1, day: 1, num: 1, type: 'conversation', title: 'Begrüßung & Vorstellung', desc: 'Name, Herkunft und erste Standardreaktionen', targetMin: 18, targetScore: 80, review: false, cultural: 'Im Deutschen sind kurze klare Vorstellungsformeln sehr wichtig.' }, + { week: 1, day: 1, num: 2, type: 'vocab', title: 'Name, Land, Sprache', desc: 'Die wichtigsten Wörter für Identität und Herkunft', targetMin: 18, targetScore: 85, review: true, cultural: null }, + { week: 1, day: 2, num: 3, type: 'grammar', title: 'Ich bin / ich heiße / ich komme', desc: 'Frühe Kontraste zwischen sein, heißen und kommen', targetMin: 22, targetScore: 78, review: true, cultural: null }, + { week: 1, day: 2, num: 4, type: 'conversation', title: 'Familie vorstellen', desc: 'Über Mutter, Vater, Kinder und Partner sprechen', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 1, day: 3, num: 5, type: 'vocab', title: 'Familie & Zuhause', desc: 'Familienrollen und zentrale Wörter für Zuhause', targetMin: 18, targetScore: 85, review: true, cultural: null }, + { week: 1, day: 3, num: 6, type: 'conversation', title: 'Im Haus fragen und antworten', desc: 'Wo ist was? Wer ist da? Was machst du?', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 1, day: 4, num: 7, type: 'grammar', title: 'der / die / das - Einstieg', desc: 'Erster Einstieg in Artikel und Genus', targetMin: 22, targetScore: 76, review: true, cultural: 'Artikel müssen früh als Wortbausteine mitgelernt werden.' }, + { week: 1, day: 4, num: 8, type: 'conversation', title: 'Bitte, danke, Entschuldigung', desc: 'Die wichtigsten Höflichkeitsmuster im Alltag', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 1, day: 5, num: 9, type: 'review', title: 'Woche 1 - Intensivwiederholung', desc: 'Abruf von Begrüßung, Familie, Zuhause und Höflichkeit', targetMin: 28, targetScore: 82, review: false, cultural: null }, + { week: 1, day: 5, num: 10, type: 'vocab', title: 'Woche 1 - Checkpoint', desc: 'Diagnose der wichtigsten Schnellstartmuster', targetMin: 16, targetScore: 84, review: true, cultural: null }, + + { week: 2, day: 1, num: 11, type: 'conversation', title: 'Essen & Trinken im Alltag', desc: 'Hunger, Durst, Wünsche und Reaktionen', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 2, day: 1, num: 12, type: 'vocab', title: 'Essen, Getränke, Mengen', desc: 'Wichtige Wörter für Mahlzeiten und Getränke', targetMin: 18, targetScore: 85, review: true, cultural: null }, + { week: 2, day: 2, num: 13, type: 'grammar', title: 'ich habe / ich möchte / ich brauche', desc: 'Frühe Kontraste für Besitz, Wunsch und Bedarf', targetMin: 22, targetScore: 78, review: true, cultural: null }, + { week: 2, day: 2, num: 14, type: 'conversation', title: 'Im Café oder Laden', desc: 'Etwas bestellen und höflich reagieren', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 2, day: 3, num: 15, type: 'vocab', title: 'Zahlen, Geld, Preise', desc: 'Zahlen und Preisfragen für den Alltag', targetMin: 20, targetScore: 85, review: true, cultural: null }, + { week: 2, day: 3, num: 16, type: 'conversation', title: 'Einkaufen & Bezahlen', desc: 'Nach Preis, Menge und Bezahlung fragen', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 2, day: 4, num: 17, type: 'grammar', title: 'nicht / kein - Einstieg', desc: 'Negation in kurzen alltagsnahen Sätzen', targetMin: 22, targetScore: 76, review: true, cultural: null }, + { week: 2, day: 4, num: 18, type: 'conversation', title: 'Bedürfnisse & Probleme', desc: 'Ich bin müde, krank, hungrig, beschäftigt', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 2, day: 5, num: 19, type: 'review', title: 'Woche 2 - Intensivwiederholung', desc: 'Mischblock zu Essen, Preisen, Negation und Bedürfnissen', targetMin: 28, targetScore: 82, review: false, cultural: null }, + { week: 2, day: 5, num: 20, type: 'vocab', title: 'Woche 2 - Checkpoint', desc: 'Schnelldiagnose zu den Alltagsmustern der zweiten Woche', targetMin: 16, targetScore: 84, review: true, cultural: null }, + + { week: 3, day: 1, num: 21, type: 'conversation', title: 'Wege & Orientierung', desc: 'Nach Orten fragen und kurze Richtungen verstehen', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 3, day: 1, num: 22, type: 'vocab', title: 'Orte, Verkehr, Richtungen', desc: 'Bahnhof, Straße, links, rechts und mehr', targetMin: 18, targetScore: 85, review: true, cultural: null }, + { week: 3, day: 2, num: 23, type: 'grammar', title: 'wo / wohin - Kontrast', desc: 'Ort und Richtung sauber unterscheiden', targetMin: 22, targetScore: 78, review: true, cultural: null }, + { week: 3, day: 2, num: 24, type: 'conversation', title: 'Mit Bus und Bahn', desc: 'Fahrkarten, Ziel und einfache Wege klären', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 3, day: 3, num: 25, type: 'vocab', title: 'Zeit, Uhrzeit, Termine', desc: 'Heute, morgen, Uhrzeiten und Verabredungen', targetMin: 18, targetScore: 85, review: true, cultural: null }, + { week: 3, day: 3, num: 26, type: 'conversation', title: 'Treffen und Planen', desc: 'Zeit ausmachen und Pläne besprechen', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 3, day: 4, num: 27, type: 'grammar', title: 'Verbzweitstellung - Einstieg', desc: 'Ich gehe heute..., Heute gehe ich...', targetMin: 22, targetScore: 76, review: true, cultural: 'Deutsche Satzstellung muss früh als Muster trainiert werden.' }, + { week: 3, day: 4, num: 28, type: 'conversation', title: 'Tagesablauf erzählen', desc: 'Morgens, mittags, abends und einfache Routinen', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 3, day: 5, num: 29, type: 'review', title: 'Woche 3 - Intensivwiederholung', desc: 'Orte, Zeiten, Wege und Satzstellung bündeln', targetMin: 28, targetScore: 82, review: false, cultural: null }, + { week: 3, day: 5, num: 30, type: 'vocab', title: 'Woche 3 - Checkpoint', desc: 'Diagnose zu Ort, Richtung, Zeit und Planen', targetMin: 16, targetScore: 84, review: true, cultural: null }, + + { week: 4, day: 1, num: 31, type: 'conversation', title: 'Arzt & Beschwerden', desc: 'Schmerzen, Termin und Hilfe beschreiben', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 4, day: 1, num: 32, type: 'vocab', title: 'Körper, Symptome, Gesundheit', desc: 'Wichtige Wörter für Arzt und Krankheit', targetMin: 18, targetScore: 85, review: true, cultural: null }, + { week: 4, day: 2, num: 33, type: 'grammar', title: 'ich bin / ich habe - Gesundheit', desc: 'Ich bin krank vs. ich habe Schmerzen', targetMin: 22, targetScore: 78, review: true, cultural: null }, + { week: 4, day: 2, num: 34, type: 'conversation', title: 'Hilfe holen & erklären', desc: 'Probleme beschreiben und um Hilfe bitten', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 4, day: 3, num: 35, type: 'vocab', title: 'Kinder, Schule, Alltag', desc: 'Kind, Schule, Tasche, Hausaufgaben und Routine', targetMin: 18, targetScore: 85, review: true, cultural: null }, + { week: 4, day: 3, num: 36, type: 'conversation', title: 'Mit Kindern sprechen', desc: 'Einfache Fragen, Fürsorge und Alltag mit Kindern', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 4, day: 4, num: 37, type: 'grammar', title: 'du / Sie - Einstieg', desc: 'Informell und höflich unterscheiden', targetMin: 20, targetScore: 76, review: true, cultural: 'Im Deutschen ist die Anrede sozial stark markiert.' }, + { week: 4, day: 4, num: 38, type: 'conversation', title: 'Höflich fragen & reagieren', desc: 'Können Sie..., ich hätte gern..., danke', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 4, day: 5, num: 39, type: 'review', title: 'Woche 4 - Intensivwiederholung', desc: 'Gesundheit, Kinder, Hilfe und Höflichkeit bündeln', targetMin: 28, targetScore: 82, review: false, cultural: null }, + { week: 4, day: 5, num: 40, type: 'vocab', title: 'Woche 4 - Checkpoint', desc: 'Diagnose zu Gesundheit, Schule und Anrede', targetMin: 16, targetScore: 84, review: true, cultural: null }, + + { week: 5, day: 1, num: 41, type: 'conversation', title: 'Formulare & Termine', desc: 'Name, Adresse, Termin und einfache Behördenmuster', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 5, day: 1, num: 42, type: 'vocab', title: 'Dokumente & Angaben', desc: 'Adresse, Formular, Nummer, Termin und Unterschrift', targetMin: 18, targetScore: 85, review: true, cultural: null }, + { week: 5, day: 2, num: 43, type: 'grammar', title: 'der / die / das - Alltagskontraste', desc: 'Artikeltraining mit typischen Alltagsnomen', targetMin: 22, targetScore: 78, review: true, cultural: null }, + { week: 5, day: 2, num: 44, type: 'conversation', title: 'Arbeit & Aufgaben', desc: 'Über Arbeit, To-dos und Verpflichtungen sprechen', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 5, day: 3, num: 45, type: 'vocab', title: 'Haushalt & Ordnung', desc: 'Zimmer, Dinge, aufräumen, putzen, reparieren', targetMin: 18, targetScore: 85, review: true, cultural: null }, + { week: 5, day: 3, num: 46, type: 'conversation', title: 'Nachbarschaft & Besuch', desc: 'Besuche ankündigen, Hilfe anbieten und reagieren', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 5, day: 4, num: 47, type: 'grammar', title: 'Akkusativ im Alltag - Einstieg', desc: 'Ich sehe den..., ich brauche die..., ich kaufe das...', targetMin: 22, targetScore: 76, review: true, cultural: null }, + { week: 5, day: 4, num: 48, type: 'conversation', title: 'Bitten, verschieben, ablehnen', desc: 'Höflich zusagen, absagen oder später vorschlagen', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 5, day: 5, num: 49, type: 'review', title: 'Woche 5 - Intensivwiederholung', desc: 'Dokumente, Arbeit, Besuch und Kasus bündeln', targetMin: 28, targetScore: 82, review: false, cultural: null }, + { week: 5, day: 5, num: 50, type: 'vocab', title: 'Woche 5 - Checkpoint', desc: 'Diagnose zu Formularen, Arbeit und Bitten', targetMin: 16, targetScore: 84, review: true, cultural: null }, + + { week: 6, day: 1, num: 51, type: 'conversation', title: 'Freies Gespräch - Alltag', desc: 'Mehrere bekannte Alltagsthemen freier verbinden', targetMin: 22, targetScore: 78, review: false, cultural: null }, + { week: 6, day: 1, num: 52, type: 'vocab', title: 'Mischtraining - Kernwortschatz', desc: 'Wichtige Wörter aus den ersten fünf Wochen gemischt', targetMin: 20, targetScore: 85, review: true, cultural: null }, + { week: 6, day: 2, num: 53, type: 'grammar', title: 'Fehlertraining - Satzstellung & Negation', desc: 'Typische Hauptsatzfehler gezielt korrigieren', targetMin: 22, targetScore: 78, review: true, cultural: null }, + { week: 6, day: 2, num: 54, type: 'conversation', title: 'Konflikte & Missverständnisse', desc: 'Nachfragen, korrigieren, höflich klären', targetMin: 20, targetScore: 80, review: false, cultural: null }, + { week: 6, day: 3, num: 55, type: 'review', title: 'Spiralwiederholung - Wochen 1 bis 3', desc: 'Frühe Muster wieder in neuen Situationen abrufen', targetMin: 28, targetScore: 82, review: false, cultural: null }, + { week: 6, day: 3, num: 56, type: 'review', title: 'Spiralwiederholung - Wochen 4 bis 5', desc: 'Spätere Alltagsfelder mit Fehlerfokus verdichten', targetMin: 28, targetScore: 82, review: false, cultural: null }, + { week: 6, day: 4, num: 57, type: 'conversation', title: 'Freies Sprechen - Familie, Alltag, Hilfe', desc: 'Längere Minidialoge mit weniger Stütze', targetMin: 24, targetScore: 78, review: false, cultural: null }, + { week: 6, day: 4, num: 58, type: 'vocab', title: 'Abschlusstest - Schnellstart', desc: 'Wortschatztest über den 6-Wochen-Schnellstart', targetMin: 18, targetScore: 84, review: true, cultural: null }, + { week: 6, day: 5, num: 59, type: 'review', title: 'Abschlussprüfung - Schnellstart', desc: 'Gemischte Abschlussprüfung über Muster, Fehlerfelder und Alltag', targetMin: 30, targetScore: 84, review: false, cultural: 'Im Vordergrund steht kommunikative Alltagssicherheit, nicht perfekte Grammatik.' }, + { week: 6, day: 5, num: 60, type: 'culture', title: 'Kulturelle Orientierung in Deutschland', desc: 'Anrede, Direktheit, Termine und Alltagserwartungen', targetMin: 16, targetScore: 0, review: false, cultural: 'Direktheit, Pünktlichkeit und klare Formulierungen wirken im Deutschen oft normal und nicht unhöflich.' } +]; + +function makeDidactics({ goals = [], patterns = [], grammar = [], speaking = [], tasks = [] }) { + return { + learningGoals: goals, + corePatterns: patterns, + grammarFocus: grammar, + speakingPrompts: speaking, + practicalTasks: tasks + }; +} + +export const GERMAN_FOR_BISAYA_PHASE1_DIDACTICS = { + 'Begrüßung & Vorstellung': makeDidactics({ + goals: [ + 'Sich kurz auf Deutsch vorstellen.', + 'Name, Herkunft und Sprache sagen.', + 'Auf eine Begrüßung sicher reagieren.' + ], + patterns: ['Hallo, ich heiße ...', 'Ich komme aus ...', 'Ich spreche Bisaya.', 'Freut mich.'], + speaking: [ + { title: 'Mini-Vorstellung', prompt: 'Stell dich mit Name und Herkunft kurz vor.', cue: 'Hallo, ich heiße Ana. Ich komme aus Cebu.' } + ] + }), + 'Name, Land, Sprache': makeDidactics({ + goals: ['Wichtige Identitätswörter verstehen.', 'Land und Sprache korrekt nennen.'], + patterns: ['der Name', 'das Land', 'die Sprache', 'Ich spreche Deutsch.'] + }), + 'Ich bin / ich heiße / ich komme': makeDidactics({ + goals: ['Frühe Standardverben unterscheiden.', 'Typische Mischfehler vermeiden.'], + patterns: ['Ich bin müde.', 'Ich heiße Maria.', 'Ich komme aus Bohol.'], + grammar: [ + { title: 'Standardkontraste', text: 'Deutsch nutzt hier drei unterschiedliche Verben mit klaren Funktionen.', example: 'Ich bin ..., ich heiße ..., ich komme aus ...' } + ] + }), + 'Familie vorstellen': makeDidactics({ + goals: ['Über enge Familie sprechen.', 'Einfache Vorstellungsdialoge führen.'], + patterns: ['Das ist meine Mutter.', 'Das ist mein Vater.', 'Ich habe zwei Kinder.'] + }), + 'Familie & Zuhause': makeDidactics({ + goals: ['Zentrale Familien- und Hauswörter lernen.'], + patterns: ['die Mutter', 'der Vater', 'das Kind', 'das Haus'] + }), + 'Im Haus fragen und antworten': makeDidactics({ + goals: ['Im Haus nach Personen und Dingen fragen.'], + patterns: ['Wo ist ...?', 'Er ist in der Küche.', 'Sie ist zu Hause.'] + }), + 'der / die / das - Einstieg': makeDidactics({ + goals: ['Artikel nicht isoliert, sondern als Chunk lernen.'], + patterns: ['der Tisch', 'die Tasche', 'das Kind'], + grammar: [ + { title: 'Artikel als Teil des Wortes', text: 'Lerne Nomen im Deutschen möglichst mit Artikel zusammen.', example: 'nicht nur Tisch, sondern der Tisch' } + ] + }), + 'Bitte, danke, Entschuldigung': makeDidactics({ + goals: ['Höfliche Standardreaktionen beherrschen.'], + patterns: ['Bitte.', 'Danke.', 'Entschuldigung.', 'Kein Problem.'] + }), + 'Woche 1 - Intensivwiederholung': makeDidactics({ + goals: ['Begrüßung, Vorstellung und Familie aktiv wiederholen.'], + patterns: ['Hallo, ich heiße ...', 'Das ist meine Mutter.', 'Danke.', 'Entschuldigung.'] + }), + 'Woche 1 - Checkpoint': makeDidactics({ + goals: ['Schnellstartmuster der ersten Woche prüfen.'], + patterns: ['ich heiße', 'ich komme aus', 'meine Familie', 'bitte'] + }), + 'Essen & Trinken im Alltag': makeDidactics({ + goals: ['Essen und Trinken im Alltag ansprechen.'], + patterns: ['Ich habe Hunger.', 'Ich habe Durst.', 'Ich möchte Wasser.', 'Möchtest du essen?'] + }), + 'Essen, Getränke, Mengen': makeDidactics({ + goals: ['Nahrungs- und Mengenwörter lernen.'], + patterns: ['das Wasser', 'der Reis', 'das Brot', 'eine Flasche'] + }), + 'ich habe / ich möchte / ich brauche': makeDidactics({ + goals: ['Besitz, Wunsch und Bedarf kontrastieren.'], + patterns: ['Ich habe Zeit.', 'Ich möchte Kaffee.', 'Ich brauche Hilfe.'], + grammar: [ + { title: 'Alltagsverben', text: 'Diese drei Verben tragen sehr viel Kommunikation im frühen Deutschkurs.', example: 'Ich habe ..., ich möchte ..., ich brauche ...' } + ] + }), + 'Im Café oder Laden': makeDidactics({ + goals: ['Bestellen und reagieren lernen.'], + patterns: ['Ich hätte gern ...', 'Noch etwas?', 'Das macht ... Euro.'] + }), + 'Zahlen, Geld, Preise': makeDidactics({ + goals: ['Preis- und Zahlmuster trainieren.'], + patterns: ['Wie viel kostet das?', 'Das kostet ... Euro.', 'zu teuer', 'billiger'] + }), + 'Einkaufen & Bezahlen': makeDidactics({ + goals: ['Im Laden sprachlich handeln.'], + patterns: ['Ich nehme das.', 'Kann ich mit Karte zahlen?', 'Haben Sie es billiger?'] + }), + 'nicht / kein - Einstieg': makeDidactics({ + goals: ['Negation sauber unterscheiden.'], + patterns: ['Ich habe kein Geld.', 'Ich bin nicht müde.'], + grammar: [ + { title: 'Negation im Alltag', text: 'Kein negiert meist Nomen, nicht negiert anderes.', example: 'kein Wasser / nicht hier' } + ] + }), + 'Bedürfnisse & Probleme': makeDidactics({ + goals: ['Bedürfnisse und kleine Probleme ausdrücken.'], + patterns: ['Ich bin müde.', 'Ich bin krank.', 'Ich brauche Ruhe.', 'Ich habe ein Problem.'] + }), + 'Woche 2 - Intensivwiederholung': makeDidactics({ + goals: ['Essen, Bedürfnisse und Negation wiederholen.'], + patterns: ['Ich habe Hunger.', 'Ich möchte Wasser.', 'Ich habe kein Geld.', 'Ich brauche Hilfe.'] + }), + 'Woche 2 - Checkpoint': makeDidactics({ + goals: ['Alltagsmuster der zweiten Woche prüfen.'], + patterns: ['ich möchte', 'ich brauche', 'kein', 'Preis'] + }), + 'Wege & Orientierung': makeDidactics({ + goals: ['Nach Wegen fragen und verstehen.'], + patterns: ['Wo ist der Bahnhof?', 'Geradeaus.', 'Links.', 'Rechts.'] + }), + 'Orte, Verkehr, Richtungen': makeDidactics({ + goals: ['Orts- und Verkehrswortschatz aufbauen.'], + patterns: ['der Bahnhof', 'die Straße', 'der Bus', 'die Haltestelle'] + }), + 'wo / wohin - Kontrast': makeDidactics({ + goals: ['Ort und Richtung sauber trennen.'], + patterns: ['Wo bist du?', 'Wohin gehst du?', 'Ich bin hier.', 'Ich gehe nach Hause.'] + }), + 'Mit Bus und Bahn': makeDidactics({ + goals: ['Fahrten im Alltag organisieren.'], + patterns: ['Ich brauche eine Fahrkarte.', 'Wann fährt der Bus?', 'Ich fahre zum Bahnhof.'] + }), + 'Zeit, Uhrzeit, Termine': makeDidactics({ + goals: ['Uhrzeit und Termine ansprechen.'], + patterns: ['heute', 'morgen', 'um drei Uhr', 'am Montag'] + }), + 'Treffen und Planen': makeDidactics({ + goals: ['Zeit ausmachen und planen.'], + patterns: ['Hast du morgen Zeit?', 'Wir treffen uns um sechs.', 'Das passt gut.'] + }), + 'Verbzweitstellung - Einstieg': makeDidactics({ + goals: ['Hauptsatzstellung trainieren.'], + patterns: ['Ich gehe heute arbeiten.', 'Heute gehe ich arbeiten.'], + grammar: [ + { title: 'Verb an Position zwei', text: 'Im deutschen Hauptsatz bleibt das Verb sehr stabil an zweiter Position.', example: 'Heute gehe ich ...' } + ] + }), + 'Tagesablauf erzählen': makeDidactics({ + goals: ['Einfache Routinen erzählen.'], + patterns: ['Morgens arbeite ich.', 'Am Abend koche ich.', 'Danach schlafe ich.'] + }), + 'Woche 3 - Intensivwiederholung': makeDidactics({ + goals: ['Ort, Zeit und Satzbau bündeln.'], + patterns: ['Wo ist ...?', 'Wohin gehst du?', 'Heute gehe ich ...', 'Wir treffen uns ...'] + }), + 'Woche 3 - Checkpoint': makeDidactics({ + goals: ['Wichtige Muster aus Woche 3 prüfen.'], + patterns: ['wo', 'wohin', 'Uhrzeit', 'Satzstellung'] + }), + 'Arzt & Beschwerden': makeDidactics({ + goals: ['Schmerzen und Termine beim Arzt ausdrücken.'], + patterns: ['Ich habe Schmerzen.', 'Ich bin krank.', 'Ich habe einen Termin.'] + }), + 'Körper, Symptome, Gesundheit': makeDidactics({ + goals: ['Wichtige Gesundheitswörter lernen.'], + patterns: ['der Kopf', 'der Bauch', 'Fieber', 'Husten'] + }), + 'ich bin / ich habe - Gesundheit': makeDidactics({ + goals: ['Gesundheitsmuster kontrastieren.'], + patterns: ['Ich bin krank.', 'Ich habe Fieber.', 'Ich habe Kopfschmerzen.'] + }), + 'Hilfe holen & erklären': makeDidactics({ + goals: ['Probleme erklären und Hilfe holen.'], + patterns: ['Ich brauche Hilfe.', 'Können Sie mir helfen?', 'Es tut weh.'] + }), + 'Kinder, Schule, Alltag': makeDidactics({ + goals: ['Schul- und Kinderalltag benennen.'], + patterns: ['das Kind', 'die Schule', 'die Tasche', 'die Hausaufgabe'] + }), + 'Mit Kindern sprechen': makeDidactics({ + goals: ['Einfache Fragen an Kinder stellen.'], + patterns: ['Bist du müde?', 'Hast du Hunger?', 'Wo ist deine Tasche?'] + }), + 'du / Sie - Einstieg': makeDidactics({ + goals: ['Anredeformen unterscheiden.'], + patterns: ['Wie heißt du?', 'Wie heißen Sie?', 'Kannst du ...?', 'Können Sie ...?'] + }), + 'Höflich fragen & reagieren': makeDidactics({ + goals: ['Höfliche Standardmuster trainieren.'], + patterns: ['Können Sie das wiederholen?', 'Ich hätte gern ...', 'Vielen Dank.'] + }), + 'Woche 4 - Intensivwiederholung': makeDidactics({ + goals: ['Gesundheit, Kinder und Höflichkeit bündeln.'], + patterns: ['Ich habe Schmerzen.', 'Können Sie ...?', 'das Kind', 'Hilfe'] + }), + 'Woche 4 - Checkpoint': makeDidactics({ + goals: ['Woche 4 diagnostisch prüfen.'], + patterns: ['ich bin / ich habe', 'du / Sie', 'Schule', 'Arzt'] + }), + 'Formulare & Termine': makeDidactics({ + goals: ['Einfache Verwaltungsdialoge verstehen.'], + patterns: ['Wie ist Ihre Adresse?', 'Hier ist mein Name.', 'Ich habe einen Termin.'] + }), + 'Dokumente & Angaben': makeDidactics({ + goals: ['Dokumenten- und Angabewortschatz aufbauen.'], + patterns: ['das Formular', 'die Adresse', 'die Nummer', 'die Unterschrift'] + }), + 'der / die / das - Alltagskontraste': makeDidactics({ + goals: ['Artikel mit häufigen Alltagswörtern stabilisieren.'], + patterns: ['der Termin', 'die Adresse', 'das Formular'] + }), + 'Arbeit & Aufgaben': makeDidactics({ + goals: ['Über Arbeit und Pflichten sprechen.'], + patterns: ['Ich arbeite heute.', 'Ich habe viel zu tun.', 'Ich muss das machen.'] + }), + 'Haushalt & Ordnung': makeDidactics({ + goals: ['Haushaltswortschatz lernen.'], + patterns: ['die Küche', 'das Zimmer', 'aufräumen', 'putzen'] + }), + 'Nachbarschaft & Besuch': makeDidactics({ + goals: ['Besuche und Nachbarschaft beschreiben.'], + patterns: ['Der Nachbar kommt später.', 'Wir haben Besuch.', 'Komm doch rein.'] + }), + 'Akkusativ im Alltag - Einstieg': makeDidactics({ + goals: ['Akkusativ in häufigen Mustern erkennen.'], + patterns: ['Ich sehe den Bus.', 'Ich brauche die Tasche.', 'Ich kaufe das Brot.'] + }), + 'Bitten, verschieben, ablehnen': makeDidactics({ + goals: ['Höflich zusagen, verschieben oder ablehnen.'], + patterns: ['Heute geht es nicht.', 'Vielleicht morgen.', 'Ja, gern.', 'Leider nicht.'] + }), + 'Woche 5 - Intensivwiederholung': makeDidactics({ + goals: ['Block 5 verdichten.'], + patterns: ['der Termin', 'die Adresse', 'Ich arbeite ...', 'Ich brauche den ...'] + }), + 'Woche 5 - Checkpoint': makeDidactics({ + goals: ['Block 5 prüfen.'], + patterns: ['Formular', 'Arbeit', 'Besuch', 'Akkusativ'] + }), + 'Freies Gespräch - Alltag': makeDidactics({ + goals: ['Bekannte Themen freier verbinden.'], + patterns: ['Heute ...', 'Danach ...', 'Ich brauche ...', 'Wir gehen ...'] + }), + 'Mischtraining - Kernwortschatz': makeDidactics({ + goals: ['Frühen Kernwortschatz mischen.'], + patterns: ['Familie', 'Essen', 'Weg', 'Arzt'] + }), + 'Fehlertraining - Satzstellung & Negation': makeDidactics({ + goals: ['Häufige Hauptsatzfehler reduzieren.'], + patterns: ['Heute gehe ich nicht ...', 'Ich habe kein ...', 'Morgen komme ich ...'] + }), + 'Konflikte & Missverständnisse': makeDidactics({ + goals: ['Nachfragen und korrigieren lernen.'], + patterns: ['Ich verstehe das nicht.', 'Können Sie das bitte wiederholen?', 'So meine ich das nicht.'] + }), + 'Spiralwiederholung - Wochen 1 bis 3': makeDidactics({ + goals: ['Frühe Schnellstartmuster zurückholen.'], + patterns: ['Ich heiße ...', 'Ich habe Hunger.', 'Wo ist ...?', 'Heute gehe ich ...'] + }), + 'Spiralwiederholung - Wochen 4 bis 5': makeDidactics({ + goals: ['Spätere Alltagsfelder verdichten.'], + patterns: ['Ich habe Schmerzen.', 'Wie ist Ihre Adresse?', 'Ich brauche den Termin.'] + }), + 'Freies Sprechen - Familie, Alltag, Hilfe': makeDidactics({ + goals: ['Längere Minidialoge sprechen.'], + patterns: ['Meine Familie ...', 'Heute ...', 'Ich brauche Hilfe.'] + }), + 'Abschlusstest - Schnellstart': makeDidactics({ + goals: ['Kernwortschatz des Schnellstarts prüfen.'], + patterns: ['Begrüßung', 'Familie', 'Preis', 'Arzt'] + }), + 'Abschlussprüfung - Schnellstart': makeDidactics({ + goals: ['Muster, Alltag und Fehlerfelder gemischt prüfen.'], + patterns: ['Vorstellung', 'Bedarf', 'Weg', 'Hilfe'] + }), + 'Kulturelle Orientierung in Deutschland': makeDidactics({ + goals: ['Deutsche Alltagskultur sprachlich einordnen.'], + patterns: ['pünktlich', 'direkt', 'Termin', 'Sie'], + speaking: [ + { title: 'Kulturreflexion', prompt: 'Sprich kurz darüber, wann im Deutschen eher Sie statt du verwendet wird.', cue: 'Im Amt und bei fremden Personen benutzt man oft Sie.' } + ] + }) +}; diff --git a/backend/scripts/german-for-bisaya-phase2-pedagogy.js b/backend/scripts/german-for-bisaya-phase2-pedagogy.js new file mode 100644 index 0000000..29ec50b --- /dev/null +++ b/backend/scripts/german-for-bisaya-phase2-pedagogy.js @@ -0,0 +1,40 @@ +export function getGermanForBisayaLessonPedagogy(lessonNumber, lessonType) { + const phaseLabel = lessonNumber <= 60 ? 'quickstart' : lessonNumber <= 120 ? 'daily_life' : 'stabilization'; + const blockNumber = Math.ceil(lessonNumber / 10); + + let didacticMode = 'core_input'; + if (lessonType === 'conversation') didacticMode = 'guided_dialogue'; + if (lessonType === 'grammar') didacticMode = 'pattern_drill'; + if (lessonType === 'review') didacticMode = 'intensive_review'; + if (lessonType === 'culture') didacticMode = 'real_life_scenario'; + + const difficultyWeight = + lessonType === 'grammar' ? 3 : + lessonType === 'review' ? 2 : + lessonType === 'conversation' ? 2 : + 1; + + const newUnitTarget = + lessonType === 'review' ? 2 : + lessonType === 'grammar' ? 4 : + phaseLabel === 'quickstart' ? 6 : + phaseLabel === 'daily_life' ? 5 : + 4; + + const reviewWeight = + lessonType === 'review' ? 90 : + lessonType === 'grammar' ? 60 : + lessonType === 'vocab' ? 55 : + lessonType === 'culture' ? 20 : + 35; + + return { + phaseLabel, + blockNumber, + didacticMode, + difficultyWeight, + newUnitTarget, + reviewWeight, + isIntensiveReview: lessonType === 'review' + }; +} diff --git a/backend/scripts/german-for-bisaya-phase3-extension.js b/backend/scripts/german-for-bisaya-phase3-extension.js new file mode 100644 index 0000000..022e29d --- /dev/null +++ b/backend/scripts/german-for-bisaya-phase3-extension.js @@ -0,0 +1,75 @@ +function makeDidactics(goals, patterns, speaking = null, grammar = null) { + return { + learningGoals: goals, + corePatterns: patterns, + speakingPrompts: speaking || [], + grammarFocus: grammar || [], + practicalTasks: [] + }; +} + +export const GERMAN_FOR_BISAYA_PHASE3_DIDACTICS = { + 'Wohnung & Nachbarn': makeDidactics(['Über Wohnung und Nachbarschaft sprechen.'], ['die Wohnung', 'der Nachbar', 'Wir wohnen hier.'], [{ title: 'Nachbarschaft', prompt: 'Sag, wo du wohnst und wer dein Nachbar ist.', cue: 'Ich wohne hier. Das ist mein Nachbar.' }]), + 'Zimmer, Möbel & Hausarbeit': makeDidactics(['Wichtige Wohnwörter lernen.'], ['das Zimmer', 'der Tisch', 'das Bett', 'putzen']), + 'Akkusativ & Artikel vertiefen': makeDidactics(['Häufige Alltagsmuster mit Akkusativ trainieren.'], ['Ich sehe den Bus.', 'Ich putze die Küche.', 'Ich kaufe das Brot.'], null, [{ title: 'Akkusativ im Alltag', text: 'Der Akkusativ taucht in vielen häufigen Sätzen auf.', example: 'Ich brauche den Termin.' }]), + 'Besuch empfangen': makeDidactics(['Besuch begrüßen und reagieren.'], ['Komm doch rein.', 'Setz dich bitte.', 'Möchtest du etwas trinken?'], [{ title: 'Besuch', prompt: 'Empfange einen Gast kurz und freundlich.', cue: 'Komm doch rein. Setz dich bitte.' }]), + 'Gefühle, Reaktionen & Beziehungen': makeDidactics(['Gefühle und soziale Reaktionen vertiefen.'], ['Ich freue mich.', 'Ich bin nervös.', 'Das ist wichtig.']), + 'Hilfe, Bitte, Ablehnung vertiefen': makeDidactics(['Bitten und Ablehnen natürlicher machen.'], ['Kannst du mir helfen?', 'Heute geht es leider nicht.', 'Vielleicht morgen.'], [{ title: 'Bitte', prompt: 'Bitte höflich um Hilfe und verschiebe etwas auf morgen.', cue: 'Kannst du mir helfen? Vielleicht morgen.' }]), + 'Modalverben im Alltag': makeDidactics(['können, müssen, wollen im Alltag trainieren.'], ['Ich kann kommen.', 'Ich muss arbeiten.', 'Ich will schlafen.'], null, [{ title: 'Modalverben', text: 'Modalverben tragen viele Alltagsaussagen.', example: 'Ich muss morgen arbeiten.' }]), + 'Arbeit & Aufgaben organisieren': makeDidactics(['Über Aufgaben und Arbeit sprechen.'], ['Ich muss das machen.', 'Ich arbeite heute.', 'Ich habe viel zu tun.']), + 'Woche 7 - Intensivwiederholung': makeDidactics(['Woche 7 gemischt wiederholen.'], ['die Wohnung', 'Kannst du mir helfen?', 'Ich muss arbeiten.', 'Komm doch rein.']), + 'Woche 7 - Checkpoint': makeDidactics(['Woche 7 diagnostisch prüfen.'], ['Wohnung', 'Akkusativ', 'Modalverb', 'Besuch']), + 'Arzt, Apotheke, Termin': makeDidactics(['Arzttermine und Apotheke sprachlich tragen.'], ['Ich habe einen Termin.', 'Ich brauche Medikamente.', 'Wo ist die Apotheke?'], [{ title: 'Arzttermin', prompt: 'Sag, dass du einen Termin hast und Medikamente brauchst.', cue: 'Ich habe einen Termin. Ich brauche Medikamente.' }]), + 'Gesundheit vertiefen': makeDidactics(['Gesundheitswortschatz ausbauen.'], ['die Tablette', 'das Rezept', 'der Husten', 'die Schmerzen']), + 'Perfekt - Einstieg': makeDidactics(['Vergangenheit im Alltag einführen.'], ['Ich habe gearbeitet.', 'Ich bin nach Hause gegangen.'], null, [{ title: 'Perfekt', text: 'Im gesprochenen Deutsch ist das Perfekt sehr wichtig.', example: 'Ich habe gegessen.' }]), + 'Über gestern sprechen': makeDidactics(['Kurze Erzählungen über gestern führen.'], ['Gestern habe ich gearbeitet.', 'Danach bin ich nach Hause gegangen.']), + 'Schule & Betreuung': makeDidactics(['Schule und Betreuung thematisieren.'], ['die Schule', 'die Lehrerin', 'die Hausaufgabe', 'das Kind']), + 'Mit Lehrkraft oder Kita sprechen': makeDidactics(['Kurze Schulsituationen auf Deutsch tragen.'], ['Mein Kind ist krank.', 'Wir haben eine Frage.', 'Wann beginnt die Schule?']), + 'nicht / kein vertiefen': makeDidactics(['Negation mit Kontrasten festigen.'], ['Ich habe kein Geld.', 'Ich bin nicht müde.', 'Das ist nicht richtig.'], null, [{ title: 'Negation vertiefen', text: 'Deutsch trennt Nomennegation und Satznegation klarer.', example: 'kein Termin / nicht hier' }]), + 'Essen, Kochen, Haushalt': makeDidactics(['Kochen und Haushalt verbinden.'], ['Ich koche heute.', 'Wir brauchen Wasser.', 'Die Küche ist sauber.']), + 'Woche 8 - Intensivwiederholung': makeDidactics(['Woche 8 gemischt wiederholen.'], ['Ich habe einen Termin.', 'Gestern habe ich ...', 'kein', 'die Schule']), + 'Woche 8 - Checkpoint': makeDidactics(['Woche 8 diagnostisch prüfen.'], ['Arzt', 'Perfekt', 'Schule', 'Negation']), + 'Amt, Dokumente, Anmeldung': makeDidactics(['Einfache Amtssprache vorbereiten.'], ['Ich möchte mich anmelden.', 'Hier ist mein Formular.', 'Was ist Ihre Adresse?']), + 'Formulare & Angaben vertiefen': makeDidactics(['Wichtige Dokumentwörter vertiefen.'], ['das Formular', 'die Adresse', 'die Nummer', 'die Unterschrift']), + 'Dativ im Alltag - Einstieg': makeDidactics(['Erste Dativmuster lernen.'], ['mit dem Bus', 'bei der Schule', 'ich gebe dem Kind Wasser'], null, [{ title: 'Dativ im Alltag', text: 'Dativ erscheint oft mit Präpositionen oder als Empfänger.', example: 'mit dem Bus / dem Kind helfen' }]), + 'Mit Bus, Bahn, Ticket': makeDidactics(['Fahrten sprachlich organisieren.'], ['Ich brauche ein Ticket.', 'Wann fährt der Zug?', 'Mit der Bahn ist es schneller.']), + 'Verkehr, Fahrten, Uhrzeiten': makeDidactics(['Verkehrs- und Zeitwortschatz mischen.'], ['der Zug', 'der Bus', 'um acht Uhr', 'spät']), + 'Weg beschreiben und fragen': makeDidactics(['Wegbeschreibungen vertiefen.'], ['Gehen Sie geradeaus.', 'Dann links.', 'Der Bahnhof ist dort.']), + 'Nebensätze mit weil - Einstieg': makeDidactics(['weil-Sätze früh als Muster einführen.'], ['Ich bleibe zu Hause, weil ich krank bin.', 'Ich komme später, weil ich arbeite.'], null, [{ title: 'weil-Satz', text: 'Mit weil verschiebt sich das Verb im Nebensatz.', example: '..., weil ich krank bin.' }]), + 'Probleme erklären und begründen': makeDidactics(['Probleme mit einfachen Begründungen erklären.'], ['Ich komme später, weil ...', 'Ich brauche Hilfe, weil ...']), + 'Woche 9 - Intensivwiederholung': makeDidactics(['Woche 9 bündeln.'], ['das Formular', 'mit dem Bus', 'weil ich ...', 'der Bahnhof']), + 'Woche 9 - Checkpoint': makeDidactics(['Woche 9 prüfen.'], ['Amt', 'Dativ', 'weil', 'Ticket']) +}; + +export const GERMAN_FOR_BISAYA_PHASE3_LESSONS = [ + { week: 7, day: 1, num: 61, type: 'conversation', title: 'Wohnung & Nachbarn', desc: 'Über Wohnung und Nachbarschaft sprechen', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 7, day: 1, num: 62, type: 'vocab', title: 'Zimmer, Möbel & Hausarbeit', desc: 'Zimmer, Möbel und einfache Hausarbeit benennen', targetMin: 18, targetScore: 85, review: true, cultural: null }, + { week: 7, day: 2, num: 63, type: 'grammar', title: 'Akkusativ & Artikel vertiefen', desc: 'Alltagsnomen mit Artikel und Akkusativ trainieren', targetMin: 22, targetScore: 78, review: true, cultural: null }, + { week: 7, day: 2, num: 64, type: 'conversation', title: 'Besuch empfangen', desc: 'Gäste begrüßen und freundlich reagieren', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 7, day: 3, num: 65, type: 'vocab', title: 'Gefühle, Reaktionen & Beziehungen', desc: 'Wortschatz für Reaktionen und Beziehungen vertiefen', targetMin: 18, targetScore: 85, review: true, cultural: null }, + { week: 7, day: 3, num: 66, type: 'conversation', title: 'Hilfe, Bitte, Ablehnung vertiefen', desc: 'Bitten, Verschieben und Ablehnen natürlicher machen', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 7, day: 4, num: 67, type: 'grammar', title: 'Modalverben im Alltag', desc: 'können, müssen und wollen früh festigen', targetMin: 22, targetScore: 78, review: true, cultural: null }, + { week: 7, day: 4, num: 68, type: 'conversation', title: 'Arbeit & Aufgaben organisieren', desc: 'Über Arbeit, Aufgaben und Zeitdruck sprechen', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 7, day: 5, num: 69, type: 'review', title: 'Woche 7 - Intensivwiederholung', desc: 'Wohnung, Besuch, Arbeit und Hilfe verdichten', targetMin: 28, targetScore: 82, review: false, cultural: null }, + { week: 7, day: 5, num: 70, type: 'vocab', title: 'Woche 7 - Checkpoint', desc: 'Diagnose zu den Inhalten von Woche 7', targetMin: 16, targetScore: 84, review: true, cultural: null }, + { week: 8, day: 1, num: 71, type: 'conversation', title: 'Arzt, Apotheke, Termin', desc: 'Arzt, Apotheke und Termine im Alltag klären', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 8, day: 1, num: 72, type: 'vocab', title: 'Gesundheit vertiefen', desc: 'Medikamente, Symptome und Gesundheit vertiefen', targetMin: 18, targetScore: 85, review: true, cultural: null }, + { week: 8, day: 2, num: 73, type: 'grammar', title: 'Perfekt - Einstieg', desc: 'Vergangenheit in der gesprochenen Sprache aufbauen', targetMin: 22, targetScore: 78, review: true, cultural: null }, + { week: 8, day: 2, num: 74, type: 'conversation', title: 'Über gestern sprechen', desc: 'Kurze Erzählungen über gestern führen', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 8, day: 3, num: 75, type: 'vocab', title: 'Schule & Betreuung', desc: 'Schul- und Betreuungswortschatz vertiefen', targetMin: 18, targetScore: 85, review: true, cultural: null }, + { week: 8, day: 3, num: 76, type: 'conversation', title: 'Mit Lehrkraft oder Kita sprechen', desc: 'Kurze Schulsituationen meistern', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 8, day: 4, num: 77, type: 'grammar', title: 'nicht / kein vertiefen', desc: 'Negation in mehreren Kontexten sauber trennen', targetMin: 22, targetScore: 78, review: true, cultural: null }, + { week: 8, day: 4, num: 78, type: 'conversation', title: 'Essen, Kochen, Haushalt', desc: 'Kochen und Haushalt im Alltag besprechen', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 8, day: 5, num: 79, type: 'review', title: 'Woche 8 - Intensivwiederholung', desc: 'Gesundheit, Schule, Perfekt und Negation bündeln', targetMin: 28, targetScore: 82, review: false, cultural: null }, + { week: 8, day: 5, num: 80, type: 'vocab', title: 'Woche 8 - Checkpoint', desc: 'Diagnose zu Woche 8', targetMin: 16, targetScore: 84, review: true, cultural: null }, + { week: 9, day: 1, num: 81, type: 'conversation', title: 'Amt, Dokumente, Anmeldung', desc: 'Dokumente und Anmeldung im Alltag vorbereiten', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 9, day: 1, num: 82, type: 'vocab', title: 'Formulare & Angaben vertiefen', desc: 'Dokumentwörter und Angaben vertiefen', targetMin: 18, targetScore: 85, review: true, cultural: null }, + { week: 9, day: 2, num: 83, type: 'grammar', title: 'Dativ im Alltag - Einstieg', desc: 'Frühe Dativmuster mit Präpositionen und Empfängern', targetMin: 22, targetScore: 78, review: true, cultural: null }, + { week: 9, day: 2, num: 84, type: 'conversation', title: 'Mit Bus, Bahn, Ticket', desc: 'Fahrten mit Bus und Bahn organisieren', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 9, day: 3, num: 85, type: 'vocab', title: 'Verkehr, Fahrten, Uhrzeiten', desc: 'Verkehr und Uhrzeiten im Alltag mischen', targetMin: 18, targetScore: 85, review: true, cultural: null }, + { week: 9, day: 3, num: 86, type: 'conversation', title: 'Weg beschreiben und fragen', desc: 'Weg fragen und kurze Richtungen geben', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 9, day: 4, num: 87, type: 'grammar', title: 'Nebensätze mit weil - Einstieg', desc: 'Einfache Begründungen mit weil bilden', targetMin: 22, targetScore: 78, review: true, cultural: null }, + { week: 9, day: 4, num: 88, type: 'conversation', title: 'Probleme erklären und begründen', desc: 'Probleme mit kurzen Begründungen erklären', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 9, day: 5, num: 89, type: 'review', title: 'Woche 9 - Intensivwiederholung', desc: 'Amt, Verkehr, Dativ und weil bündeln', targetMin: 28, targetScore: 82, review: false, cultural: null }, + { week: 9, day: 5, num: 90, type: 'vocab', title: 'Woche 9 - Checkpoint', desc: 'Diagnose zu Woche 9', targetMin: 16, targetScore: 84, review: true, cultural: null } +]; diff --git a/backend/scripts/german-for-bisaya-phase4-extension.js b/backend/scripts/german-for-bisaya-phase4-extension.js new file mode 100644 index 0000000..3d7aa3b --- /dev/null +++ b/backend/scripts/german-for-bisaya-phase4-extension.js @@ -0,0 +1,75 @@ +function makeDidactics(goals, patterns, speaking = null, grammar = null) { + return { + learningGoals: goals, + corePatterns: patterns, + speakingPrompts: speaking || [], + grammarFocus: grammar || [], + practicalTasks: [] + }; +} + +export const GERMAN_FOR_BISAYA_PHASE4_DIDACTICS = { + 'Arbeitssuche & Termine': makeDidactics(['Über Arbeit und Termine sprechen.'], ['Ich suche Arbeit.', 'Ich habe morgen einen Termin.', 'Wann kann ich anfangen?']), + 'Beruf, Zeiten, Aufgaben': makeDidactics(['Wortschatz zu Arbeit und Zeiten vertiefen.'], ['der Beruf', 'die Aufgabe', 'die Schicht', 'früh / spät']), + 'Perfekt mit haben / sein': makeDidactics(['haben und sein im Perfekt kontrastieren.'], ['Ich habe gearbeitet.', 'Ich bin gefahren.'], null, [{ title: 'Perfekt-Hilfsverben', text: 'Im Deutschen wechseln die Hilfsverben im Perfekt.', example: 'habe gemacht / bin gegangen' }]), + 'Beim Arzt genauer beschreiben': makeDidactics(['Beschwerden genauer erklären.'], ['Ich habe seit gestern Schmerzen.', 'Es tut hier weh.', 'Es ist besser geworden.']), + 'Körper, Symptome, Medikamente': makeDidactics(['Gesundheitswortschatz vertiefen.'], ['die Tablette', 'das Medikament', 'der Rücken', 'der Husten']), + 'Konflikte höflich klären': makeDidactics(['Konflikte sprachlich entschärfen.'], ['So meine ich das nicht.', 'Können wir darüber sprechen?', 'Entschuldigung.']), + 'Trennbare Verben - Einstieg': makeDidactics(['Trennbare Verben im Alltag erkennen.'], ['Ich stehe auf.', 'Ich rufe an.', 'Ich mache das Licht aus.'], null, [{ title: 'Trennbare Verben', text: 'Viele deutsche Alltagsverben trennen sich im Hauptsatz.', example: 'Ich rufe dich an.' }]), + 'Tagesablauf mit Trennverben': makeDidactics(['Routinen mit trennbaren Verben erzählen.'], ['Ich stehe um sechs Uhr auf.', 'Dann räume ich auf.']), + 'Woche 10 - Intensivwiederholung': makeDidactics(['Woche 10 bündeln.'], ['Arbeit', 'Perfekt', 'Arzt', 'trennbares Verb']), + 'Woche 10 - Checkpoint': makeDidactics(['Woche 10 prüfen.'], ['Beruf', 'Perfekt', 'Schmerz', 'anrufen']), + 'Kinder, Schule, Freizeit': makeDidactics(['Kinderalltag in mehreren Bereichen beschreiben.'], ['Mein Kind spielt gern.', 'Die Schule beginnt um acht.', 'Am Nachmittag spielen wir.']), + 'Schule, Spiel, Familie vertiefen': makeDidactics(['Kinder- und Schulwortschatz mischen.'], ['die Pause', 'das Spiel', 'die Familie', 'der Lehrer']), + 'Possessivartikel mein / dein / sein': makeDidactics(['Besitzwörter im Alltag trainieren.'], ['mein Kind', 'deine Tasche', 'sein Zimmer'], null, [{ title: 'Possessivartikel', text: 'Besitz wird im Deutschen häufig mit kleinen Artikelformen markiert.', example: 'mein Bus / deine Adresse' }]), + 'Zuhause organisieren': makeDidactics(['Haushalt und Zuhause organisieren.'], ['Ich räume das Zimmer auf.', 'Das ist mein Schlüssel.', 'Wo ist deine Tasche?']), + 'Wohnen, Reparaturen, Nachbarschaft': makeDidactics(['Wohnen und kleine Probleme benennen.'], ['Die Lampe ist kaputt.', 'Der Nachbar hilft uns.', 'Wir brauchen einen Termin.']), + 'Einladungen & soziale Treffen': makeDidactics(['Einladen und verabreden.'], ['Hast du Zeit?', 'Kommst du morgen?', 'Wir treffen uns um sechs.']), + 'du / Sie vertiefen & Bitten': makeDidactics(['Anrede und Höflichkeit vertiefen.'], ['Kannst du ...?', 'Können Sie ...?', 'Ich hätte gern ...'], null, [{ title: 'Anrede vertiefen', text: 'Die Wahl zwischen du und Sie verändert den Ton stark.', example: 'Kannst du helfen? / Können Sie helfen?' }]), + 'Telefon, Nachricht, Termin verschieben': makeDidactics(['Telefonisch reagieren und verschieben.'], ['Ich rufe später an.', 'Können wir den Termin verschieben?', 'Ich schreibe dir.']), + 'Woche 11 - Intensivwiederholung': makeDidactics(['Woche 11 bündeln.'], ['mein / dein', 'Treffen', 'du / Sie', 'Termin verschieben']), + 'Woche 11 - Checkpoint': makeDidactics(['Woche 11 prüfen.'], ['Possessiv', 'Einladung', 'Telefon', 'Anrede']), + 'Einkauf, Reklamation, Rückgabe': makeDidactics(['Im Laden reklamieren und zurückgeben.'], ['Das ist kaputt.', 'Ich möchte das zurückgeben.', 'Haben Sie einen Kassenbon?']), + 'Geld, Rechnung, Vertrag': makeDidactics(['Wortschatz zu Geld und Dokumenten vertiefen.'], ['die Rechnung', 'der Vertrag', 'bar zahlen', 'das Konto']), + 'würde / hätte gern - Einstieg': makeDidactics(['Höfliche Wünsche früher ausbauen.'], ['Ich würde gern ...', 'Ich hätte gern ...'], null, [{ title: 'Höfliche Wünsche', text: 'Diese Formen sind im Deutschen sehr nützlich für höfliche Kommunikation.', example: 'Ich hätte gern einen Termin.' }]), + 'Wünsche höflich formulieren': makeDidactics(['Höfliche Bitten und Wünsche trainieren.'], ['Ich hätte gern Hilfe.', 'Ich würde gern einen Termin machen.']), + 'Gefühle, Sorgen, Hoffnungen': makeDidactics(['Über Gefühle und Sorgen differenzierter sprechen.'], ['Ich bin besorgt.', 'Ich hoffe das.', 'Ich freue mich darauf.']), + 'Freies Erzählen - Alltag & Familie': makeDidactics(['Alltag und Familie freier verbinden.'], ['Heute ...', 'Danach ...', 'Meine Familie ...'], [{ title: 'Freier Bericht', prompt: 'Erzähle kurz von Familie, Arbeit und Plänen.', cue: 'Heute arbeite ich. Danach sehe ich meine Familie.' }]), + 'Satzstellung mit Zeit / Ort': makeDidactics(['Zeit- und Ortsangaben im Satz ordnen.'], ['Heute bin ich zu Hause.', 'Am Abend fahre ich nach Hause.'], null, [{ title: 'Zeit / Ort im Satz', text: 'Zeit- und Ortsfelder beeinflussen die deutsche Satzstellung früh spürbar.', example: 'Heute arbeite ich zu Hause.' }]), + 'Alltag mit mehreren Schritten': makeDidactics(['Mehrschrittige Alltagsfolgen erzählen.'], ['Zuerst ...', 'Dann ...', 'Danach ...', 'Am Ende ...']), + 'Woche 12 - Intensivwiederholung': makeDidactics(['Woche 12 bündeln.'], ['Rückgabe', 'würde gern', 'Sorgen', 'danach']), + 'Woche 12 - Checkpoint': makeDidactics(['Woche 12 prüfen.'], ['Rechnung', 'Wunsch', 'Gefühl', 'Satzstellung']) +}; + +export const GERMAN_FOR_BISAYA_PHASE4_LESSONS = [ + { week: 10, day: 1, num: 91, type: 'conversation', title: 'Arbeitssuche & Termine', desc: 'Über Arbeit und Termine sprechen', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 10, day: 1, num: 92, type: 'vocab', title: 'Beruf, Zeiten, Aufgaben', desc: 'Berufswortschatz und Aufgaben vertiefen', targetMin: 18, targetScore: 85, review: true, cultural: null }, + { week: 10, day: 2, num: 93, type: 'grammar', title: 'Perfekt mit haben / sein', desc: 'Die zwei Perfekt-Hilfsverben alltagsnah trainieren', targetMin: 22, targetScore: 78, review: true, cultural: null }, + { week: 10, day: 2, num: 94, type: 'conversation', title: 'Beim Arzt genauer beschreiben', desc: 'Beschwerden und Entwicklung genauer beschreiben', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 10, day: 3, num: 95, type: 'vocab', title: 'Körper, Symptome, Medikamente', desc: 'Gesundheitswortschatz weiter ausbauen', targetMin: 18, targetScore: 85, review: true, cultural: null }, + { week: 10, day: 3, num: 96, type: 'conversation', title: 'Konflikte höflich klären', desc: 'Missverständnisse und Konflikte höflich klären', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 10, day: 4, num: 97, type: 'grammar', title: 'Trennbare Verben - Einstieg', desc: 'Trennbare Verben im Alltag erkennen und verwenden', targetMin: 22, targetScore: 78, review: true, cultural: null }, + { week: 10, day: 4, num: 98, type: 'conversation', title: 'Tagesablauf mit Trennverben', desc: 'Routinen mit trennbaren Verben erzählen', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 10, day: 5, num: 99, type: 'review', title: 'Woche 10 - Intensivwiederholung', desc: 'Arbeit, Arzt, Perfekt und Trennverben bündeln', targetMin: 28, targetScore: 82, review: false, cultural: null }, + { week: 10, day: 5, num: 100, type: 'vocab', title: 'Woche 10 - Checkpoint', desc: 'Diagnose zu Woche 10', targetMin: 16, targetScore: 84, review: true, cultural: null }, + { week: 11, day: 1, num: 101, type: 'conversation', title: 'Kinder, Schule, Freizeit', desc: 'Kinderalltag in mehreren Bereichen beschreiben', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 11, day: 1, num: 102, type: 'vocab', title: 'Schule, Spiel, Familie vertiefen', desc: 'Schul- und Familienwortschatz mischen', targetMin: 18, targetScore: 85, review: true, cultural: null }, + { week: 11, day: 2, num: 103, type: 'grammar', title: 'Possessivartikel mein / dein / sein', desc: 'Besitz und Beziehungen sprachlich markieren', targetMin: 22, targetScore: 78, review: true, cultural: null }, + { week: 11, day: 2, num: 104, type: 'conversation', title: 'Zuhause organisieren', desc: 'Haushalt, Dinge und Fragen zu Hause organisieren', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 11, day: 3, num: 105, type: 'vocab', title: 'Wohnen, Reparaturen, Nachbarschaft', desc: 'Wortschatz zu Wohnen und kleinen Problemen', targetMin: 18, targetScore: 85, review: true, cultural: null }, + { week: 11, day: 3, num: 106, type: 'conversation', title: 'Einladungen & soziale Treffen', desc: 'Einladungen machen und auf Treffen reagieren', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 11, day: 4, num: 107, type: 'grammar', title: 'du / Sie vertiefen & Bitten', desc: 'Anrede und Bitten weiter festigen', targetMin: 22, targetScore: 78, review: true, cultural: null }, + { week: 11, day: 4, num: 108, type: 'conversation', title: 'Telefon, Nachricht, Termin verschieben', desc: 'Telefonisch reagieren und Termine verschieben', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 11, day: 5, num: 109, type: 'review', title: 'Woche 11 - Intensivwiederholung', desc: 'Kinder, Zuhause, Anrede und Einladungen bündeln', targetMin: 28, targetScore: 82, review: false, cultural: null }, + { week: 11, day: 5, num: 110, type: 'vocab', title: 'Woche 11 - Checkpoint', desc: 'Diagnose zu Woche 11', targetMin: 16, targetScore: 84, review: true, cultural: null }, + { week: 12, day: 1, num: 111, type: 'conversation', title: 'Einkauf, Reklamation, Rückgabe', desc: 'Reklamieren und Rückgaben im Alltag bewältigen', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 12, day: 1, num: 112, type: 'vocab', title: 'Geld, Rechnung, Vertrag', desc: 'Wortschatz zu Geld, Rechnung und Vertrag', targetMin: 18, targetScore: 85, review: true, cultural: null }, + { week: 12, day: 2, num: 113, type: 'grammar', title: 'würde / hätte gern - Einstieg', desc: 'Höfliche Wünsche im Alltag aufbauen', targetMin: 22, targetScore: 78, review: true, cultural: null }, + { week: 12, day: 2, num: 114, type: 'conversation', title: 'Wünsche höflich formulieren', desc: 'Höfliche Bitten und Wünsche im Alltag trainieren', targetMin: 18, targetScore: 80, review: false, cultural: null }, + { week: 12, day: 3, num: 115, type: 'vocab', title: 'Gefühle, Sorgen, Hoffnungen', desc: 'Emotionen, Sorgen und Hoffnungen sprachlich erweitern', targetMin: 18, targetScore: 85, review: true, cultural: null }, + { week: 12, day: 3, num: 116, type: 'conversation', title: 'Freies Erzählen - Alltag & Familie', desc: 'Freier über Familie und Alltag sprechen', targetMin: 20, targetScore: 78, review: false, cultural: null }, + { week: 12, day: 4, num: 117, type: 'grammar', title: 'Satzstellung mit Zeit / Ort', desc: 'Zeit- und Ortsfelder im Satz festigen', targetMin: 22, targetScore: 78, review: true, cultural: null }, + { week: 12, day: 4, num: 118, type: 'conversation', title: 'Alltag mit mehreren Schritten', desc: 'Mehrschrittige Abläufe klar erzählen', targetMin: 20, targetScore: 78, review: false, cultural: null }, + { week: 12, day: 5, num: 119, type: 'review', title: 'Woche 12 - Intensivwiederholung', desc: 'Wünsche, Gefühle und Satzbau bündeln', targetMin: 28, targetScore: 82, review: false, cultural: null }, + { week: 12, day: 5, num: 120, type: 'vocab', title: 'Woche 12 - Checkpoint', desc: 'Diagnose zu Woche 12', targetMin: 16, targetScore: 84, review: true, cultural: null } +]; diff --git a/backend/scripts/german-for-bisaya-phase5-extension.js b/backend/scripts/german-for-bisaya-phase5-extension.js new file mode 100644 index 0000000..f451724 --- /dev/null +++ b/backend/scripts/german-for-bisaya-phase5-extension.js @@ -0,0 +1,75 @@ +function makeDidactics(goals, patterns, speaking = null, grammar = null) { + return { + learningGoals: goals, + corePatterns: patterns, + speakingPrompts: speaking || [], + grammarFocus: grammar || [], + practicalTasks: [] + }; +} + +export const GERMAN_FOR_BISAYA_PHASE5_DIDACTICS = { + 'Dialogtag - Alltag': makeDidactics(['Mehrere Alltagsthemen in längeren Dialogen verbinden.'], ['Heute muss ich ...', 'Danach gehe ich ...', 'Ich brauche noch ...']), + 'Mischtraining - Kernwortschatz I': makeDidactics(['Frühe Kernwörter gemischt aktivieren.'], ['Familie', 'Arzt', 'Preis', 'Termin']), + 'Fehlertraining - Artikel & Kasus I': makeDidactics(['Artikel und Kasus gezielt kontrastieren.'], ['der Termin', 'die Tasche', 'das Formular', 'mit dem Bus'], null, [{ title: 'Kontrastblock', text: 'Schwache Formen werden direkt nebeneinandergestellt.', example: 'der Termin / den Termin / dem Termin' }]), + 'Rollenspiel - Amt, Arzt, Einkauf': makeDidactics(['Mehrere Alltagsszenen verbinden.'], ['Ich habe einen Termin.', 'Ich brauche Hilfe.', 'Ich möchte das zurückgeben.']), + 'Stabilisierungsblock 1 - Intensiv I': makeDidactics(['Erste Stabilisierungsschleife.'], ['ich heiße', 'ich brauche', 'wo / wohin', 'du / Sie']), + 'Fehlerschwerpunkte Satzstellung': makeDidactics(['Satzstellungsfehler gezielt reduzieren.'], ['Heute gehe ich ...', 'Morgen habe ich ...', 'Danach komme ich ...']), + 'Freies Erzählen - Familie, Arbeit, Termine': makeDidactics(['Mehrere Themen freier verbinden.'], ['Meine Familie ...', 'Ich arbeite ...', 'Morgen habe ich einen Termin.'], [{ title: 'Freier Bericht', prompt: 'Erzähle kurz von Familie, Arbeit und einem Termin.', cue: 'Ich arbeite heute. Morgen habe ich einen Termin.' }]), + 'Mischtraining - Reale Situationen I': makeDidactics(['Reale Situationen mischen.'], ['der Bus', 'der Termin', 'die Schule', 'die Apotheke']), + 'Stabilisierungsblock 1 - Intensiv II': makeDidactics(['Erste Stabilisierung verdichten.'], ['kein', 'würde gern', 'Akkusativ', 'Perfekt']), + 'Stabilisierungsblock 1 - Checkpoint': makeDidactics(['Ersten Stabilisierungsschritt prüfen.'], ['Artikel', 'Satzstellung', 'Termin', 'Arzt']), + 'Dialogtag - Konflikt, Hilfe, Planung': makeDidactics(['Konflikte, Hilfe und Planen verbinden.'], ['Können wir darüber sprechen?', 'Ich brauche Hilfe.', 'Morgen machen wir ...']), + 'Mischtraining - Kernwortschatz II': makeDidactics(['Zweite Kernwortschatzrunde.'], ['Weg', 'Familie', 'Arbeit', 'Gesundheit']), + 'Fehlertraining - Zeitformen & Modalverben': makeDidactics(['Zeitformen und Modalverben kontrastieren.'], ['Ich habe gearbeitet.', 'Ich muss arbeiten.', 'Ich kann kommen.'], null, [{ title: 'Zeit und Modalität', text: 'Zeitformen und Modalverben werden leicht vermischt und müssen bewusst getrennt werden.', example: 'Ich habe gearbeitet. / Ich muss arbeiten.' }]), + 'Rollenspiel - Missverständnisse lösen': makeDidactics(['Missverständnisse ruhig klären.'], ['Ich verstehe das nicht.', 'So meine ich das nicht.', 'Können Sie das bitte wiederholen?']), + 'Stabilisierungsblock 2 - Intensiv I': makeDidactics(['Zweite Stabilisierungsrunde verdichten.'], ['Perfekt', 'Modalverb', 'weil', 'Rückgabe']), + 'Fehlerschwerpunkte Negation & Artikel': makeDidactics(['nicht/kein und Artikel zusammen trainieren.'], ['kein Termin', 'nicht hier', 'die Tasche', 'den Bus']), + 'Freies Sprechen - Alltag ohne Stütze': makeDidactics(['Freier mit minimaler Stütze sprechen.'], ['Zuerst ...', 'Dann ...', 'Später ...', 'Am Ende ...'], [{ title: 'Freie Routine', prompt: 'Erzähle frei von einem typischen Tag.', cue: 'Zuerst ..., dann ..., später ...' }]), + 'Mischtraining - Reale Situationen II': makeDidactics(['Zweite Mischrunde realer Situationen.'], ['Apotheke', 'Nachbar', 'Formular', 'Kasse']), + 'Stabilisierungsblock 2 - Intensiv II': makeDidactics(['Zweite Stabilisierung verdichten.'], ['du / Sie', 'kein / nicht', 'Perfekt', 'Akkusativ']), + 'Stabilisierungsblock 2 - Checkpoint': makeDidactics(['Zweiten Stabilisierungsschritt prüfen.'], ['Negation', 'Kasus', 'Missverständnis', 'Arbeit']), + 'Großes Mischreview I': makeDidactics(['Frühe und mittlere Inhalte breit wiederholen.'], ['Vorstellung', 'Essen', 'Weg', 'Arzt']), + 'Großes Mischreview II': makeDidactics(['Zweite große Mischrunde mit höherer Dichte.'], ['Formular', 'Rückgabe', 'Treffen', 'Schule']), + 'Fehlertraining - letzte Schwächen': makeDidactics(['Die letzten typischen Fehler fokussieren.'], ['ich habe / ich bin', 'der / die / das', 'nicht / kein']), + 'Freies Sprechen - längere Antworten': makeDidactics(['Längere Antworten mit mehr Zusammenhalt geben.'], ['Erstens ...', 'Dann ...', 'Außerdem ...', 'Deshalb ...'], [{ title: 'Längere Antwort', prompt: 'Gib eine längere Antwort zu Alltag, Arbeit und Familie.', cue: 'Erstens ..., dann ..., außerdem ...' }]), + 'Langzeitreview I': makeDidactics(['Früh gelernte Inhalte gegen Vergessen absichern.'], ['Hallo, ich heiße ...', 'Ich habe Hunger.', 'Wo ist ...?']), + 'Langzeitreview II': makeDidactics(['Frühe und späte Inhalte gemeinsam zurückholen.'], ['Ich habe einen Termin.', 'Können Sie ...?', 'Ich brauche Hilfe.']), + 'Rollenspiele - echte Situationen': makeDidactics(['Alltagssituationen offen kombinieren.'], ['Ich brauche Hilfe.', 'Ich hätte gern ...', 'Ich komme später, weil ...']), + 'Abschlusstest - Stabilisierung': makeDidactics(['Wortschatz der Stabilisierung prüfen.'], ['Artikel', 'Negation', 'Arzt', 'Formular']), + 'Abschlussprüfung - Gesamtpfad': makeDidactics(['Gesamten Schnellstart bis Stabilisierung prüfen.'], ['Vorstellung', 'Arbeit', 'Arzt', 'Alltag']), + 'Kulturelle Orientierung in Deutschland vertieft': makeDidactics(['Alltagskultur vertieft reflektieren.'], ['Pünktlichkeit', 'Termin', 'Direktheit', 'Sie'], [{ title: 'Kulturreflexion', prompt: 'Erkläre kurz, warum Pünktlichkeit und klare Absprachen im deutschen Alltag wichtig wirken.', cue: 'Termine und Pünktlichkeit sind in Deutschland oft sehr wichtig.' }]) +}; + +export const GERMAN_FOR_BISAYA_PHASE5_LESSONS = [ + { week: 13, day: 1, num: 121, type: 'conversation', title: 'Dialogtag - Alltag', desc: 'Mehrere Alltagsthemen in längeren Dialogen verbinden', targetMin: 22, targetScore: 78, review: false, cultural: null }, + { week: 13, day: 1, num: 122, type: 'vocab', title: 'Mischtraining - Kernwortschatz I', desc: 'Frühe Kernwörter blockübergreifend mischen', targetMin: 20, targetScore: 85, review: true, cultural: null }, + { week: 13, day: 2, num: 123, type: 'grammar', title: 'Fehlertraining - Artikel & Kasus I', desc: 'Artikel und Kasus in problematischen Mustern kontrastieren', targetMin: 22, targetScore: 78, review: true, cultural: null }, + { week: 13, day: 2, num: 124, type: 'conversation', title: 'Rollenspiel - Amt, Arzt, Einkauf', desc: 'Mehrere reale Alltagsszenen verbinden', targetMin: 22, targetScore: 78, review: false, cultural: null }, + { week: 13, day: 3, num: 125, type: 'review', title: 'Stabilisierungsblock 1 - Intensiv I', desc: 'Erste verdichtete Stabilisierungsrunde', targetMin: 30, targetScore: 82, review: false, cultural: null }, + { week: 13, day: 3, num: 126, type: 'vocab', title: 'Fehlerschwerpunkte Satzstellung', desc: 'Satzstellungsfehler gezielt reduzieren', targetMin: 20, targetScore: 85, review: true, cultural: null }, + { week: 13, day: 4, num: 127, type: 'conversation', title: 'Freies Erzählen - Familie, Arbeit, Termine', desc: 'Mehrere Themen freier verbinden', targetMin: 24, targetScore: 78, review: false, cultural: null }, + { week: 13, day: 4, num: 128, type: 'vocab', title: 'Mischtraining - Reale Situationen I', desc: 'Gemischtes Training realer Situationen', targetMin: 20, targetScore: 85, review: true, cultural: null }, + { week: 13, day: 5, num: 129, type: 'review', title: 'Stabilisierungsblock 1 - Intensiv II', desc: 'Zweite verdichtete Wiederholung des ersten Stabilisierungsblocks', targetMin: 30, targetScore: 82, review: false, cultural: null }, + { week: 13, day: 5, num: 130, type: 'vocab', title: 'Stabilisierungsblock 1 - Checkpoint', desc: 'Diagnose zum ersten Stabilisierungsblock', targetMin: 18, targetScore: 84, review: true, cultural: null }, + { week: 14, day: 1, num: 131, type: 'conversation', title: 'Dialogtag - Konflikt, Hilfe, Planung', desc: 'Konflikt, Hilfe und Planung in Dialogen verbinden', targetMin: 22, targetScore: 78, review: false, cultural: null }, + { week: 14, day: 1, num: 132, type: 'vocab', title: 'Mischtraining - Kernwortschatz II', desc: 'Zweite Kernwortschatzrunde über Alltagsthemen', targetMin: 20, targetScore: 85, review: true, cultural: null }, + { week: 14, day: 2, num: 133, type: 'grammar', title: 'Fehlertraining - Zeitformen & Modalverben', desc: 'Zeitformen und Modalverben gezielt kontrastieren', targetMin: 22, targetScore: 78, review: true, cultural: null }, + { week: 14, day: 2, num: 134, type: 'conversation', title: 'Rollenspiel - Missverständnisse lösen', desc: 'Missverständnisse ruhig und höflich klären', targetMin: 22, targetScore: 78, review: false, cultural: null }, + { week: 14, day: 3, num: 135, type: 'review', title: 'Stabilisierungsblock 2 - Intensiv I', desc: 'Erste verdichtete Wiederholung des zweiten Stabilisierungsblocks', targetMin: 30, targetScore: 82, review: false, cultural: null }, + { week: 14, day: 3, num: 136, type: 'vocab', title: 'Fehlerschwerpunkte Negation & Artikel', desc: 'Negation und Artikel gezielt zusammen trainieren', targetMin: 20, targetScore: 85, review: true, cultural: null }, + { week: 14, day: 4, num: 137, type: 'conversation', title: 'Freies Sprechen - Alltag ohne Stütze', desc: 'Mit wenig Stütze frei über Alltag sprechen', targetMin: 24, targetScore: 78, review: false, cultural: null }, + { week: 14, day: 4, num: 138, type: 'vocab', title: 'Mischtraining - Reale Situationen II', desc: 'Zweite große Mischrunde realer Situationen', targetMin: 20, targetScore: 85, review: true, cultural: null }, + { week: 14, day: 5, num: 139, type: 'review', title: 'Stabilisierungsblock 2 - Intensiv II', desc: 'Zweite verdichtete Wiederholung des zweiten Stabilisierungsblocks', targetMin: 30, targetScore: 82, review: false, cultural: null }, + { week: 14, day: 5, num: 140, type: 'vocab', title: 'Stabilisierungsblock 2 - Checkpoint', desc: 'Diagnose zum zweiten Stabilisierungsblock', targetMin: 18, targetScore: 84, review: true, cultural: null }, + { week: 15, day: 1, num: 141, type: 'review', title: 'Großes Mischreview I', desc: 'Breite Wiederholung des Gesamtpfads', targetMin: 30, targetScore: 82, review: false, cultural: null }, + { week: 15, day: 1, num: 142, type: 'vocab', title: 'Großes Mischreview II', desc: 'Zweite große Mischrunde mit höherer Dichte', targetMin: 20, targetScore: 85, review: true, cultural: null }, + { week: 15, day: 2, num: 143, type: 'grammar', title: 'Fehlertraining - letzte Schwächen', desc: 'Die letzten typischen Fehlerfelder gezielt bearbeiten', targetMin: 22, targetScore: 78, review: true, cultural: null }, + { week: 15, day: 2, num: 144, type: 'conversation', title: 'Freies Sprechen - längere Antworten', desc: 'Längere Antworten mit mehr Struktur geben', targetMin: 24, targetScore: 78, review: false, cultural: null }, + { week: 15, day: 3, num: 145, type: 'review', title: 'Langzeitreview I', desc: 'Frühe Inhalte gezielt gegen Vergessen absichern', targetMin: 30, targetScore: 82, review: false, cultural: null }, + { week: 15, day: 3, num: 146, type: 'vocab', title: 'Langzeitreview II', desc: 'Frühe und spätere Inhalte gemeinsam reaktivieren', targetMin: 20, targetScore: 85, review: true, cultural: null }, + { week: 15, day: 4, num: 147, type: 'conversation', title: 'Rollenspiele - echte Situationen', desc: 'Echte Situationen flexibel kombinieren', targetMin: 24, targetScore: 78, review: false, cultural: null }, + { week: 15, day: 4, num: 148, type: 'vocab', title: 'Abschlusstest - Stabilisierung', desc: 'Finaler Wortschatztest der Stabilisierungsphase', targetMin: 20, targetScore: 84, review: true, cultural: null }, + { week: 15, day: 5, num: 149, type: 'review', title: 'Abschlussprüfung - Gesamtpfad', desc: 'Große Abschlussprüfung über den gesamten Deutschpfad', targetMin: 32, targetScore: 84, review: false, cultural: 'Kommunikative Alltagssicherheit ist wichtiger als perfekte Formbeherrschung.' }, + { week: 15, day: 5, num: 150, type: 'culture', title: 'Kulturelle Orientierung in Deutschland vertieft', desc: 'Direktheit, Termine, Siezen und Alltagserwartungen vertiefen', targetMin: 16, targetScore: 0, review: false, cultural: 'Klare Aussagen, Termine und Pünktlichkeit werden im deutschen Alltag oft sehr ernst genommen.' } +]; diff --git a/backend/services/falukantService.js b/backend/services/falukantService.js index 6413d7a..69829ce 100644 --- a/backend/services/falukantService.js +++ b/backend/services/falukantService.js @@ -3542,6 +3542,7 @@ class FalukantService extends BaseService { return { relationshipId: r.id, name: partner.firstName || 'Unknown', + age: partner.age != null ? partner.age : null, gender: partner.gender || null, title: partner.nobleTitle || '', role: state.loverRole || 'lover', diff --git a/docs/GERMAN_FOR_BISAYA_IMPLEMENTATION_SPEC.md b/docs/GERMAN_FOR_BISAYA_IMPLEMENTATION_SPEC.md new file mode 100644 index 0000000..32933f7 --- /dev/null +++ b/docs/GERMAN_FOR_BISAYA_IMPLEMENTATION_SPEC.md @@ -0,0 +1,493 @@ +# Deutschkurs für Bisaya-Lernende: Umsetzungsdokument + +## 1. Ziel + +Der Deutschkurs für Bisaya-Muttersprachler soll nicht wie ein generischer A1-Wortschatzkurs aufgebaut sein, sondern gezielt auf die typischen Hürden von Bisaya-Lernenden reagieren. + +Das Ziel ist: + +- schnelle alltagsnahe Sprechfähigkeit +- frühes Verstehen typischer deutscher Satzmuster +- systematische Fehlerreduktion bei genau den Strukturen, die für Bisaya-Lernende schwer sind +- starke Wiederholung ohne stumpfes Kreisen +- schrittweise Entwicklung von freier Produktion + +Der Kurs soll damit drei Dinge zugleich leisten: + +- neuen Stoff effizient aufbauen +- alte Inhalte tief verankern +- typische Transferfehler aus der Erstsprache gezielt abfangen + +## 2. Besondere Perspektive: Deutsch aus Sicht von Bisaya-Lernenden + +Der Kurs muss didaktisch anders gebaut werden als ein allgemeiner Deutschkurs. + +Typische Spannungsfelder: + +- Deutsch hat Artikel, Genus und Kasus; Bisaya nicht in derselben Weise +- Deutsch markiert Zeitformen anders und oft expliziter +- deutsche Satzstellung ist viel rigider +- trennbare Verben, Verbzweitstellung und Nebensätze sind ungewohnt +- Funktionswörter wie `der`, `die`, `das`, `den`, `dem`, `ein`, `eine` wirken für Lernende anfangs wenig greifbar +- Höflichkeit, Direktheit und Standardreaktionen sind kulturell anders verteilt + +Das heißt: + +- der Kurs darf nicht nur Vokabel + Übersetzung sein +- er muss stark mit Mustern, Kontrasten und Minimalpaaren arbeiten +- Fehlertraining muss früh anfangen, nicht erst spät + +## 3. Produktentscheidung + +Der Deutschkurs wird als eigener Lernpfad konzipiert: + +- `Deutsch für Bisaya-Lernende - Alltag & Stabilisierung` + +Er soll wie beim Bisaya-Ausbau in feste Lernphasen aufgeteilt werden, aber die Inhalte speziell auf deutsche Hürden für Bisaya-Lernende zuschneiden. + +## 4. Zielbild des Kursmodells + +Der neue Deutschpfad besteht aus drei fachlichen Lernphasen. + +### 4.1 Phase A: Schnellstart + +Ziel: + +- sofort auf elementare Alltagssituationen reagieren +- Begrüßung, Familie, Essen, Bedürfnisse, Wege und Hilfe ausdrücken +- erste deutsche Standardsätze ohne langes Nachdenken verwenden + +Dauer: + +- `6 Wochen` + +Fokus: + +- feste Alltagsmuster +- hochfrequente Sätze +- frühe Hör- und Sprechaktivierung +- noch reduzierte Grammatiklast + +### 4.2 Phase B: Alltag + +Ziel: + +- Alltag auf Deutsch in mehr Bereichen tragen +- mit Schule, Arzt, Einkauf, Formularen, Nachbarschaft und Arbeit umgehen +- häufige Satzmuster produktiv variieren + +Dauer: + +- `8 bis 12 Wochen` + +Fokus: + +- deutsche Satzstellung +- Verbformen im Alltag +- Artikel und Pluralformen +- Fragen, Antworten, Bitten, Erklären + +### 4.3 Phase C: Stabilisierung + +Ziel: + +- Langzeitverfügbarkeit +- automatische Reaktion auf Standardsituationen +- deutliche Reduktion typischer Bisaya-Deutsch-Fehler +- mehr freie und zusammenhängende Produktion + +Dauer: + +- `3 bis 6 Monate` + +Fokus: + +- Kontrasttraining +- gemischte Situationen +- Fehlerschwerpunkte +- freie Produktion +- langsame Langzeitwiederholung + +## 5. Technische Phasenlabels + +Wie beim Bisaya-Pfad werden drei feste Phasenlabels gespeichert: + +- `quickstart` +- `daily_life` +- `stabilization` + +## 6. Didaktische Grundprinzipien + +### 6.1 Muster vor Einzelsammlung + +Deutsch soll zuerst über verwendbare Muster gelernt werden: + +- `Ich heiße ...` +- `Ich komme aus ...` +- `Ich habe ...` +- `Ich brauche ...` +- `Wo ist ...?` +- `Ich möchte ...` +- `Können Sie das bitte wiederholen?` + +Nicht: + +- lange Wortlisten ohne direkte Verwendbarkeit + +### 6.2 Fehler früh sichtbar machen + +Deutschfehler von Bisaya-Lernenden sollten nicht erst im Fortgeschrittenenbereich Thema werden. + +Schon früh müssen Kontraste kommen wie: + +- `ich bin` vs. `ich habe` +- `der / die / das` +- `ein / eine` +- `ich gehe` vs. `ich bin gegangen` +- `ich will` vs. `ich möchte` +- `wo` vs. `wohin` +- `nicht` vs. `kein` + +### 6.3 Wiederholung in Wellen + +Wie beim Bisaya-Kurs: + +- kein reines Vokabel-Hämmern +- aber auch keine endlose Dauerschleife ohne Fortschritt + +Stattdessen: + +1. Mikro-Wiederholung innerhalb der Lektion +2. Kurzintervall-Wiederholung nach 1 / 3 / 7 Tagen +3. Intensiv-Wiederholungsphasen nach jedem Block +4. Spiral-Wiederholung alter Inhalte in neuen Kontexten + +## 7. Typische Fehlerfelder für Bisaya-Lernende im Deutschen + +Diese Fehlerfelder müssen eigene Lektions- und Reviewblöcke bekommen. + +### 7.1 Artikel und Genus + +Schwierigkeit: + +- `der`, `die`, `das` +- `ein`, `eine` +- Artikel erscheinen oft willkürlich + +Konsequenz: + +- nicht isoliert lehren +- immer als Chunk: + - `der Tisch` + - `die Tasche` + - `das Kind` + +### 7.2 Kasus im Alltag + +Schwierigkeit: + +- `ich sehe den Mann` +- `ich gebe dem Kind Wasser` +- `mit dem Bus` + +Konsequenz: + +- erst alltagsnah +- nicht über Grammatikdefinitionen, sondern über wiederkehrende Satzmuster + +### 7.3 Satzstellung + +Schwierigkeit: + +- Verbzweitstellung +- Verbendstellung im Nebensatz +- Inversion nach Vorfeld + +Konsequenz: + +- sehr viele Mini-Kontraste +- Satzbau-Drills mit echten Alltagssätzen + +### 7.4 Zeitformen + +Schwierigkeit: + +- Präsens, Perfekt, Modalverben +- deutsche Hilfsverben + +Konsequenz: + +- Präsens zuerst als Alltagsbasis +- Perfekt früh für häufige Erzählungen +- keine Überladung mit allen Formen gleichzeitig + +### 7.5 Höflichkeit und Register + +Schwierigkeit: + +- `du` vs. `Sie` +- deutsche Direktheit +- höfliche Bitten + +Konsequenz: + +- soziale Szenarien mit klarer Registertrennung + +### 7.6 Negation + +Schwierigkeit: + +- `nicht` vs. `kein` + +Konsequenz: + +- eigener Kontrastblock +- viele kurze Minimalpaare + +## 8. Kursstruktur + +### Phase 1: Schnellstart, 6 Wochen + +Ziel: + +- elementare mündliche Handlungsfähigkeit + +Module: + +1. Begrüßung, Name, Herkunft +2. Familie und Zuhause +3. Essen, Trinken, Fürsorge +4. Bedürfnisse und Gefühle +5. Orte, Wege, Verkehr +6. Zahlen, Geld, Preise +7. Gesundheit und Hilfe +8. Bitte, Danke, Entschuldigung +9. Tagesablauf +10. erste Wiederholungs- und Checkpointblöcke + +### Phase 2: Alltag, 8 bis 12 Wochen + +Module: + +1. Kinder, Schule, Betreuung +2. Arzt, Apotheke, Termine +3. Einkauf, Markt, Mengen +4. Formulare, Amt, Dokumente +5. Nachbarschaft und Besuch +6. Hilfe, Bitte, Ablehnung +7. Arbeit und Aufgaben +8. Wohnung, Haushalt, Reparaturen +9. soziale Situationen und Konflikte +10. gemischte Alltagsszenarien + +### Phase 3: Stabilisierung, 3 bis 6 Monate + +Module: + +1. Fehlerschwerpunkte Artikel +2. Fehlerschwerpunkte Satzstellung +3. Fehlerschwerpunkte Zeitformen +4. Mischtraining realer Situationen +5. freies Erzählen +6. Langzeitreview +7. große Abschlussdiagnosen + +## 9. Lektionstypen + +Die vorhandenen Typen können weiter genutzt werden: + +- `vocab` +- `conversation` +- `grammar` +- `review` +- `culture` + +Didaktisch sollten sie aber stärker spezialisiert werden: + +- `core_input` +- `pattern_drill` +- `guided_dialogue` +- `contrast_training` +- `real_life_scenario` +- `intensive_review` +- `checkpoint` + +## 10. Wiederholungsmodell + +### 10.1 Verteilung von neuem und altem Stoff + +- Start eines neuen Blocks: + - `70 % neu` + - `30 % alt` +- Mitte des Blocks: + - `50 % neu` + - `50 % alt` +- vor Intensivphase: + - `30 % neu` + - `70 % alt` +- Intensivphase: + - `10 % neu` + - `90 % alt` + +### 10.2 Intensiv-Wiederholungsphasen + +Nach jedem größeren Block: + +- `2 bis 3` Lektionen ohne viel neuen Stoff +- Fokus auf: + - Abruf + - Kontrast + - typische Fehler + - gemischte Situationen + +### 10.3 Langzeitreview + +Frühe Inhalte müssen später wiederkehren, aber nicht in derselben Form. + +Beispiel: + +- früh: + - `Wo ist der Bahnhof?` +- später: + - `Entschuldigung, wo ist der Bahnhof?` + - `Wo ist der Bahnhof? Ich muss dorthin.` + - `Können Sie mir bitte sagen, wo der Bahnhof ist?` + +## 11. UI- und Produktlogik + +Die App sollte den Kurs nicht als lineare Liste von Lektionen darstellen, sondern als täglichen Lernfluss: + +1. fällige Wiederholungen +2. aktueller Block +3. Intensivphase, wenn fällig +4. freie Sprech-/Anwendungsphase + +Der Startscreen sollte zeigen: + +- heute fällige Wiederholungen +- neuer Stoff verfügbar +- typische Fehler von gestern +- nächster Checkpoint + +## 12. Konkrete Fehlertrainingsblöcke + +Für Deutsch aus Bisaya-Sicht besonders wichtig: + +1. `der / die / das` +2. `ein / eine` +3. `ich bin / ich habe` +4. `ich gehe / ich bin gegangen` +5. `wo / wohin` +6. `nicht / kein` +7. `du / Sie` +8. deutsche Wortstellung im Hauptsatz +9. Nebensatz mit `weil`, `dass`, `wenn` +10. Modalverben im Alltag + +## 13. Phase-1-Umsetzungsvorschlag + +Die erste technische Umsetzung sollte nicht sofort den ganzen Deutschkurs schreiben, sondern die Grundlage legen. + +### Phase 1A + +- Kursstruktur für `Deutsch für Bisaya-Lernende` definieren +- Phasen- und Blocklogik analog zum Bisaya-System übernehmen +- deutsche Didaktikfelder pro Lektion pflegen + +### Phase 1B + +- erste 4 bis 6 Wochen inhaltlich aufbauen +- frühe Fehlerkontraste schon einbauen +- Übungen für Begrüßung, Familie, Bedürfnisse, Wege, Hilfe + +### Phase 1C + +- spezielles Fehlertraining für: + - Artikel + - Satzstellung + - Negation +- dazu eigene `review`- und `grammar`-Lektionen + +## 14. Empfohlene erste Module + +Für die erste reale Bauphase: + +1. `Begrüßung & Vorstellung` +2. `Familie & Zuhause` +3. `Essen & Trinken` +4. `Bedürfnisse & Gefühle` +5. `Weg & Verkehr` +6. `Bitte, Danke, Entschuldigung` +7. `Arzt & Hilfe` +8. `Zahlen, Geld, Preise` +9. `Tagesablauf` +10. `Intensivwiederholung Schnellstart` + +## 15. Nächste Umsetzungsschritte + +1. deutsches Kursziel und Titel festlegen +2. Wochen 1 bis 6 als echten Schnellstart definieren +3. deutsch-spezifische Fehlerblöcke pro Woche einplanen +4. Intensiv-Wiederholungsphasen pro Block festlegen +5. danach Übungen und Content-Skripte analog zum Bisaya-System aufbauen + +## 15.1 Aktueller Implementierungsstand + +Die Phasen 1 bis 5 sind jetzt als technische Struktur angelegt: + +- [german-for-bisaya-phase1.js](/mnt/share/torsten/Programs/YourPart3/backend/scripts/german-for-bisaya-phase1.js) + - Wochen `1 bis 6` + - `60` Lektionen + - Schnellstart und erste Fehlerfelder +- [german-for-bisaya-phase2-pedagogy.js](/mnt/share/torsten/Programs/YourPart3/backend/scripts/german-for-bisaya-phase2-pedagogy.js) + - technische Phasen-/Blocklogik + - `quickstart`, `daily_life`, `stabilization` +- [german-for-bisaya-phase3-extension.js](/mnt/share/torsten/Programs/YourPart3/backend/scripts/german-for-bisaya-phase3-extension.js) + - Wochen `7 bis 9` + - Alltagsausbau +- [german-for-bisaya-phase4-extension.js](/mnt/share/torsten/Programs/YourPart3/backend/scripts/german-for-bisaya-phase4-extension.js) + - Wochen `10 bis 12` + - vertiefter Alltag und Fehlertraining +- [german-for-bisaya-phase5-extension.js](/mnt/share/torsten/Programs/YourPart3/backend/scripts/german-for-bisaya-phase5-extension.js) + - Wochen `13 bis 15` + - Stabilisierung, Mischtraining und Abschluss +- [create-german-for-bisaya-course.js](/mnt/share/torsten/Programs/YourPart3/backend/scripts/create-german-for-bisaya-course.js) + - erstellt direkt den kompletten `15-Wochen`-Pfad +- [create-german-for-bisaya-course-content.js](/mnt/share/torsten/Programs/YourPart3/backend/scripts/create-german-for-bisaya-course-content.js) + - erzeugt Übungen für den Gesamtpfad + - mit handgeschriebenen Kernlektionen und generischem Fallback +- [extend-german-for-bisaya-course-phase3.js](/mnt/share/torsten/Programs/YourPart3/backend/scripts/extend-german-for-bisaya-course-phase3.js) +- [extend-german-for-bisaya-course-phase4.js](/mnt/share/torsten/Programs/YourPart3/backend/scripts/extend-german-for-bisaya-course-phase4.js) +- [extend-german-for-bisaya-course-phase5.js](/mnt/share/torsten/Programs/YourPart3/backend/scripts/extend-german-for-bisaya-course-phase5.js) + +### Neuer Kurs von Grund auf + +```bash +node backend/scripts/create-german-for-bisaya-course.js +node backend/scripts/create-german-for-bisaya-course-content.js +``` + +### Bestehenden Kurs stufenweise erweitern + +```bash +node backend/scripts/extend-german-for-bisaya-course-phase3.js +node backend/scripts/extend-german-for-bisaya-course-phase4.js +node backend/scripts/extend-german-for-bisaya-course-phase5.js +node backend/scripts/create-german-for-bisaya-course-content.js +``` + +## 16. Ergebnis + +Der Deutschkurs für Bisaya-Lernende sollte nicht einfach ein übersetzter Standardkurs sein. + +Er muss: + +- alltagsnah +- kontrastiv +- wiederholungsstark +- satzmusterorientiert +- fehlerbewusst + +gebaut sein. + +Dann entsteht kein bloßer Vokabelkurs, sondern ein echter Lernpfad, der auf die Ausgangssprache und die typischen Schwierigkeiten der Lernenden reagiert. diff --git a/frontend/src/i18n/locales/de/falukant.json b/frontend/src/i18n/locales/de/falukant.json index 793b454..9db51b9 100644 --- a/frontend/src/i18n/locales/de/falukant.json +++ b/frontend/src/i18n/locales/de/falukant.json @@ -677,6 +677,7 @@ "lovers": { "title": "Liebhaber und Mätressen", "none": "Keine Liebhaber vorhanden.", + "age": "Alter", "affection": "Zuneigung", "visibility": "Sichtbarkeit", "discretion": "Diskretion", diff --git a/frontend/src/i18n/locales/en/falukant.json b/frontend/src/i18n/locales/en/falukant.json index 4629eea..38d1cf3 100644 --- a/frontend/src/i18n/locales/en/falukant.json +++ b/frontend/src/i18n/locales/en/falukant.json @@ -728,6 +728,7 @@ "lovers": { "title": "Lovers and Mistresses", "none": "No lovers present.", + "age": "Age", "affection": "Affection", "visibility": "Visibility", "discretion": "Discretion", diff --git a/frontend/src/i18n/locales/es/falukant.json b/frontend/src/i18n/locales/es/falukant.json index 2dc5692..3e4c737 100644 --- a/frontend/src/i18n/locales/es/falukant.json +++ b/frontend/src/i18n/locales/es/falukant.json @@ -658,6 +658,7 @@ "lovers": { "title": "Amantes y favoritas", "none": "No hay amantes.", + "age": "Edad", "affection": "Afecto", "visibility": "Visibilidad", "discretion": "Discreción", diff --git a/frontend/src/views/falukant/FamilyView.vue b/frontend/src/views/falukant/FamilyView.vue index f9f0233..c2e4fb2 100644 --- a/frontend/src/views/falukant/FamilyView.vue +++ b/frontend/src/views/falukant/FamilyView.vue @@ -347,6 +347,9 @@
{{ $t('falukant.titles.' + lover.gender + '.' + lover.title) }} {{ lover.name }} +
+ {{ $t('falukant.family.lovers.age') }}: {{ lover.age }} +
{{ $t('falukant.family.lovers.role.' + (lover.role || 'lover')) }}
@@ -421,7 +424,7 @@
{{ $t('falukant.titles.' + candidate.gender + '.' + candidate.title) }} {{ candidate.name }} - {{ $t('falukant.family.spouse.age') }}: {{ candidate.age }} + {{ $t('falukant.family.lovers.age') }}: {{ candidate.age }} {{ $t('falukant.family.lovers.statusFit') }}: {{ candidate.statusFit }} {{ $t('falukant.family.lovers.monthlyCost') }}: {{ formatCost(candidate.estimatedMonthlyCost || 0) }}
@@ -1276,6 +1279,12 @@ export default { margin-bottom: 12px; } +.lover-card__age { + font-size: 0.9rem; + color: rgba(0, 0, 0, 0.62); + margin-top: 4px; +} + .lover-card__role { margin-top: 4px; color: var(--color-text-secondary);