From 9a78bc7c4b89c8835d86fb5fc0c3a7087e461a79 Mon Sep 17 00:00:00 2001
From: "Torsten Schulz (local)"
Date: Tue, 31 Mar 2026 08:50:56 +0200
Subject: [PATCH] feat(admin): add potential fathers retrieval for character
management
- Implemented a new method in AdminService to fetch potential fathers for a given character based on existing relationships.
- Updated AdminController to expose this functionality via a new API endpoint.
- Enhanced adminRouter to include the route for retrieving potential fathers.
- Modified frontend components to allow selection of potential fathers during pregnancy and birth management.
- Updated internationalization files to include new translation keys related to father selection.
---
backend/controllers/adminController.js | 13 +
...1000000-add-vocab-lesson-phase1-fields.cjs | 44 +
.../models/community/vocab_course_lesson.js | 36 +
backend/routers/adminRouter.js | 1 +
backend/scripts/add-bisaya-week1-lessons.js | 12 +-
.../scripts/apply-bisaya-course-refresh.js | 22 +-
.../scripts/bisaya-course-phase2-pedagogy.js | 156 +++
.../scripts/bisaya-course-phase3-extension.js | 254 ++++
.../scripts/bisaya-course-phase4-extension.js | 594 ++++++++++
.../scripts/bisaya-course-phase5-extension.js | 274 +++++
.../scripts/create-bisaya-course-content.js | 363 +++++-
backend/scripts/create-bisaya-course.js | 33 +-
.../scripts/extend-bisaya-course-phase3.js | 102 ++
.../scripts/extend-bisaya-course-phase4.js | 102 ++
.../scripts/extend-bisaya-course-phase5.js | 102 ++
backend/scripts/update-bisaya-didactics.js | 22 +-
backend/services/adminService.js | 93 +-
backend/services/vocabService.js | 167 ++-
.../sql/add_vocab_lesson_phase1_fields.sql | 23 +
.../sql/backfill_bisaya_phase2_pedagogy.sql | 147 +++
...extend_bisaya_course_phase3_to_6_weeks.sql | 12 +
...xtend_bisaya_course_phase4_to_3_months.sql | 11 +
..._bisaya_course_phase5_to_stabilization.sql | 11 +
...YA_COURSE_EXPANSION_IMPLEMENTATION_SPEC.md | 1038 +++++++++++++++++
frontend/src/i18n/locales/de/admin.json | 18 +-
frontend/src/i18n/locales/en/admin.json | 18 +-
frontend/src/i18n/locales/es/admin.json | 18 +-
.../src/views/admin/falukant/EditUserView.vue | 114 +-
frontend/src/views/social/VocabCourseView.vue | 72 ++
frontend/src/views/social/VocabLessonView.vue | 80 ++
30 files changed, 3907 insertions(+), 45 deletions(-)
create mode 100644 backend/migrations/20260331000000-add-vocab-lesson-phase1-fields.cjs
create mode 100644 backend/scripts/bisaya-course-phase2-pedagogy.js
create mode 100644 backend/scripts/bisaya-course-phase3-extension.js
create mode 100644 backend/scripts/bisaya-course-phase4-extension.js
create mode 100644 backend/scripts/bisaya-course-phase5-extension.js
create mode 100644 backend/scripts/extend-bisaya-course-phase3.js
create mode 100644 backend/scripts/extend-bisaya-course-phase4.js
create mode 100644 backend/scripts/extend-bisaya-course-phase5.js
create mode 100644 backend/sql/add_vocab_lesson_phase1_fields.sql
create mode 100644 backend/sql/backfill_bisaya_phase2_pedagogy.sql
create mode 100644 backend/sql/extend_bisaya_course_phase3_to_6_weeks.sql
create mode 100644 backend/sql/extend_bisaya_course_phase4_to_3_months.sql
create mode 100644 backend/sql/extend_bisaya_course_phase5_to_stabilization.sql
create mode 100644 docs/BISAYA_COURSE_EXPANSION_IMPLEMENTATION_SPEC.md
diff --git a/backend/controllers/adminController.js b/backend/controllers/adminController.js
index f6919cf..b7d821b 100644
--- a/backend/controllers/adminController.js
+++ b/backend/controllers/adminController.js
@@ -16,6 +16,7 @@ class AdminController {
this.adminForceFalukantPregnancy = this.adminForceFalukantPregnancy.bind(this);
this.adminClearFalukantPregnancy = this.adminClearFalukantPregnancy.bind(this);
this.adminForceFalukantBirth = this.adminForceFalukantBirth.bind(this);
+ this.adminGetPotentialFathersForCharacter = this.adminGetPotentialFathersForCharacter.bind(this);
this.getFalukantUserBranches = this.getFalukantUserBranches.bind(this);
this.updateFalukantStock = this.updateFalukantStock.bind(this);
this.addFalukantStock = this.addFalukantStock.bind(this);
@@ -390,6 +391,18 @@ class AdminController {
}
}
+ async adminGetPotentialFathersForCharacter(req, res) {
+ try {
+ const { userid: userId } = req.headers;
+ const { characterId } = req.params;
+ const response = await AdminService.adminGetPotentialFathersForCharacter(userId, characterId);
+ res.status(200).json(response);
+ } catch (error) {
+ console.log(error);
+ res.status(400).json({ error: error.message });
+ }
+ }
+
async adminClearFalukantPregnancy(req, res) {
try {
const { userid: userId } = req.headers;
diff --git a/backend/migrations/20260331000000-add-vocab-lesson-phase1-fields.cjs b/backend/migrations/20260331000000-add-vocab-lesson-phase1-fields.cjs
new file mode 100644
index 0000000..d64531c
--- /dev/null
+++ b/backend/migrations/20260331000000-add-vocab-lesson-phase1-fields.cjs
@@ -0,0 +1,44 @@
+'use strict';
+
+module.exports = {
+ async up(queryInterface, Sequelize) {
+ await queryInterface.sequelize.query(`
+ ALTER TABLE community.vocab_course_lesson
+ ADD COLUMN IF NOT EXISTS didactic_mode TEXT,
+ ADD COLUMN IF NOT EXISTS phase_label TEXT,
+ ADD COLUMN IF NOT EXISTS block_number INTEGER,
+ ADD COLUMN IF NOT EXISTS difficulty_weight INTEGER,
+ ADD COLUMN IF NOT EXISTS new_unit_target INTEGER,
+ ADD COLUMN IF NOT EXISTS review_weight INTEGER,
+ ADD COLUMN IF NOT EXISTS is_intensive_review BOOLEAN NOT NULL DEFAULT FALSE;
+
+ COMMENT ON COLUMN community.vocab_course_lesson.didactic_mode IS
+ 'Didaktischer Modus der Lektion, z.B. core_input, guided_dialogue, intensive_review oder checkpoint.';
+ COMMENT ON COLUMN community.vocab_course_lesson.phase_label IS
+ 'Übergeordnete Lernphase, z.B. quickstart, daily_life oder stabilization.';
+ COMMENT ON COLUMN community.vocab_course_lesson.block_number IS
+ 'Inhaltlicher Block für Konsolidierungs- und Wiederholungswellen.';
+ COMMENT ON COLUMN community.vocab_course_lesson.difficulty_weight IS
+ 'Grobe relative Schwierigkeit der Lektion von leicht bis schwer.';
+ COMMENT ON COLUMN community.vocab_course_lesson.new_unit_target IS
+ 'Empfohlene Zahl neuer Spracheinheiten in dieser Lektion.';
+ COMMENT ON COLUMN community.vocab_course_lesson.review_weight IS
+ 'Wie stark Wiederholung in dieser Lektion dominieren soll, typischerweise 0 bis 100.';
+ COMMENT ON COLUMN community.vocab_course_lesson.is_intensive_review IS
+ 'Markiert Lektionen, die als intensive Wiederholungsphase gedacht sind.';
+ `);
+ },
+
+ async down(queryInterface, Sequelize) {
+ await queryInterface.sequelize.query(`
+ ALTER TABLE community.vocab_course_lesson
+ DROP COLUMN IF EXISTS is_intensive_review,
+ DROP COLUMN IF EXISTS review_weight,
+ DROP COLUMN IF EXISTS new_unit_target,
+ DROP COLUMN IF EXISTS difficulty_weight,
+ DROP COLUMN IF EXISTS block_number,
+ DROP COLUMN IF EXISTS phase_label,
+ DROP COLUMN IF EXISTS didactic_mode;
+ `);
+ }
+};
diff --git a/backend/models/community/vocab_course_lesson.js b/backend/models/community/vocab_course_lesson.js
index 62df690..e06b7a8 100644
--- a/backend/models/community/vocab_course_lesson.js
+++ b/backend/models/community/vocab_course_lesson.js
@@ -48,6 +48,42 @@ VocabCourseLesson.init({
defaultValue: 'vocab',
field: 'lesson_type'
},
+ didacticMode: {
+ type: DataTypes.TEXT,
+ allowNull: true,
+ field: 'didactic_mode'
+ },
+ phaseLabel: {
+ type: DataTypes.TEXT,
+ allowNull: true,
+ field: 'phase_label'
+ },
+ blockNumber: {
+ type: DataTypes.INTEGER,
+ allowNull: true,
+ field: 'block_number'
+ },
+ difficultyWeight: {
+ type: DataTypes.INTEGER,
+ allowNull: true,
+ field: 'difficulty_weight'
+ },
+ newUnitTarget: {
+ type: DataTypes.INTEGER,
+ allowNull: true,
+ field: 'new_unit_target'
+ },
+ reviewWeight: {
+ type: DataTypes.INTEGER,
+ allowNull: true,
+ field: 'review_weight'
+ },
+ isIntensiveReview: {
+ type: DataTypes.BOOLEAN,
+ allowNull: false,
+ defaultValue: false,
+ field: 'is_intensive_review'
+ },
audioUrl: {
type: DataTypes.TEXT,
allowNull: true,
diff --git a/backend/routers/adminRouter.js b/backend/routers/adminRouter.js
index a8bdec9..5a7a25b 100644
--- a/backend/routers/adminRouter.js
+++ b/backend/routers/adminRouter.js
@@ -43,6 +43,7 @@ router.post('/contacts/answer', authenticate, adminController.answerContact);
router.post('/falukant/searchuser', authenticate, adminController.searchUser);
router.get('/falukant/getuser/:id', authenticate, adminController.getFalukantUserById);
router.post('/falukant/edituser', authenticate, adminController.changeFalukantUser);
+router.get('/falukant/character/:characterId/potential-fathers', authenticate, adminController.adminGetPotentialFathersForCharacter);
router.post('/falukant/character/force-pregnancy', authenticate, adminController.adminForceFalukantPregnancy);
router.post('/falukant/character/clear-pregnancy', authenticate, adminController.adminClearFalukantPregnancy);
router.post('/falukant/character/force-birth', authenticate, adminController.adminForceFalukantBirth);
diff --git a/backend/scripts/add-bisaya-week1-lessons.js b/backend/scripts/add-bisaya-week1-lessons.js
index 6288709..bb05344 100644
--- a/backend/scripts/add-bisaya-week1-lessons.js
+++ b/backend/scripts/add-bisaya-week1-lessons.js
@@ -9,6 +9,7 @@
import { sequelize } from '../utils/sequelize.js';
import VocabCourseLesson from '../models/community/vocab_course_lesson.js';
+import { getBisayaLessonPedagogy } from './bisaya-course-phase2-pedagogy.js';
const LESSONS_TO_ADD = [
{
@@ -98,6 +99,8 @@ async function addBisayaWeek1Lessons() {
continue;
}
+ const pedagogy = getBisayaLessonPedagogy(lessonData.lessonNumber) || {};
+
await VocabCourseLesson.create({
courseId: course.id,
chapterId: null,
@@ -113,7 +116,14 @@ async function addBisayaWeek1Lessons() {
speakingPrompts: lessonData.speakingPrompts || [],
targetMinutes: lessonData.targetMinutes,
targetScorePercent: lessonData.targetScorePercent,
- requiresReview: lessonData.requiresReview
+ requiresReview: lessonData.requiresReview,
+ didacticMode: pedagogy.didacticMode || null,
+ phaseLabel: pedagogy.phaseLabel || null,
+ blockNumber: pedagogy.blockNumber ?? null,
+ difficultyWeight: pedagogy.difficultyWeight ?? null,
+ newUnitTarget: pedagogy.newUnitTarget ?? null,
+ reviewWeight: pedagogy.reviewWeight ?? null,
+ isIntensiveReview: Boolean(pedagogy.isIntensiveReview)
});
console.log(` ✅ Lektion ${lessonData.lessonNumber}: "${lessonData.title}" hinzugefügt`);
diff --git a/backend/scripts/apply-bisaya-course-refresh.js b/backend/scripts/apply-bisaya-course-refresh.js
index 099d2a6..8cad4ce 100644
--- a/backend/scripts/apply-bisaya-course-refresh.js
+++ b/backend/scripts/apply-bisaya-course-refresh.js
@@ -12,6 +12,7 @@ import VocabGrammarExercise from '../models/community/vocab_grammar_exercise.js'
import VocabCourseProgress from '../models/community/vocab_course_progress.js';
import VocabGrammarExerciseProgress from '../models/community/vocab_grammar_exercise_progress.js';
import { Op } from 'sequelize';
+import { getBisayaLessonPedagogy } from './bisaya-course-phase2-pedagogy.js';
const LESSON_DIDACTICS = {
'Begrüßungen & Höflichkeit': {
@@ -171,14 +172,23 @@ async function applyBisayaCourseRefresh() {
for (const lesson of lessons) {
const didactics = LESSON_DIDACTICS[lesson.title];
- if (!didactics) continue;
+ const pedagogy = getBisayaLessonPedagogy(lesson.lessonNumber);
+ if (!didactics && !pedagogy) continue;
+ const normalizedDidactics = didactics || {};
await lesson.update({
- learningGoals: didactics.learningGoals || [],
- corePatterns: didactics.corePatterns || [],
- grammarFocus: didactics.grammarFocus || [],
- speakingPrompts: didactics.speakingPrompts || [],
- practicalTasks: didactics.practicalTasks || []
+ learningGoals: normalizedDidactics.learningGoals || [],
+ corePatterns: normalizedDidactics.corePatterns || [],
+ grammarFocus: normalizedDidactics.grammarFocus || [],
+ speakingPrompts: normalizedDidactics.speakingPrompts || [],
+ practicalTasks: normalizedDidactics.practicalTasks || [],
+ didacticMode: pedagogy?.didacticMode || null,
+ phaseLabel: pedagogy?.phaseLabel || null,
+ blockNumber: pedagogy?.blockNumber ?? null,
+ difficultyWeight: pedagogy?.difficultyWeight ?? null,
+ newUnitTarget: pedagogy?.newUnitTarget ?? null,
+ reviewWeight: pedagogy?.reviewWeight ?? null,
+ isIntensiveReview: Boolean(pedagogy?.isIntensiveReview)
});
updatedLessons++;
}
diff --git a/backend/scripts/bisaya-course-phase2-pedagogy.js b/backend/scripts/bisaya-course-phase2-pedagogy.js
new file mode 100644
index 0000000..a782211
--- /dev/null
+++ b/backend/scripts/bisaya-course-phase2-pedagogy.js
@@ -0,0 +1,156 @@
+export const BISAYA_LESSON_PEDAGOGY = {
+ 1: { phaseLabel: 'quickstart', blockNumber: 1, didacticMode: 'guided_dialogue', difficultyWeight: 2, newUnitTarget: 5, reviewWeight: 15, isIntensiveReview: false },
+ 2: { phaseLabel: 'quickstart', blockNumber: 1, didacticMode: 'core_input', difficultyWeight: 3, newUnitTarget: 8, reviewWeight: 20, isIntensiveReview: false },
+ 3: { phaseLabel: 'quickstart', blockNumber: 1, didacticMode: 'core_input', difficultyWeight: 2, newUnitTarget: 7, reviewWeight: 20, isIntensiveReview: false },
+ 4: { phaseLabel: 'quickstart', blockNumber: 1, didacticMode: 'guided_dialogue', difficultyWeight: 3, newUnitTarget: 5, reviewWeight: 25, isIntensiveReview: false },
+ 5: { phaseLabel: 'quickstart', blockNumber: 1, didacticMode: 'guided_dialogue', difficultyWeight: 3, newUnitTarget: 4, reviewWeight: 25, isIntensiveReview: false },
+ 6: { phaseLabel: 'quickstart', blockNumber: 1, didacticMode: 'core_input', difficultyWeight: 3, newUnitTarget: 7, reviewWeight: 25, isIntensiveReview: false },
+ 7: { phaseLabel: 'quickstart', blockNumber: 1, didacticMode: 'guided_dialogue', difficultyWeight: 3, newUnitTarget: 5, reviewWeight: 30, isIntensiveReview: false },
+ 8: { phaseLabel: 'quickstart', blockNumber: 1, didacticMode: 'core_input', difficultyWeight: 2, newUnitTarget: 6, reviewWeight: 30, isIntensiveReview: false },
+ 9: { phaseLabel: 'quickstart', blockNumber: 1, didacticMode: 'intensive_review', difficultyWeight: 3, newUnitTarget: 0, reviewWeight: 90, isIntensiveReview: true },
+ 10: { phaseLabel: 'quickstart', blockNumber: 1, didacticMode: 'checkpoint', difficultyWeight: 3, newUnitTarget: 0, reviewWeight: 95, isIntensiveReview: false },
+ 11: { phaseLabel: 'quickstart', blockNumber: 2, didacticMode: 'guided_dialogue', difficultyWeight: 3, newUnitTarget: 5, reviewWeight: 25, isIntensiveReview: false },
+ 12: { phaseLabel: 'quickstart', blockNumber: 2, didacticMode: 'core_input', difficultyWeight: 2, newUnitTarget: 7, reviewWeight: 25, isIntensiveReview: false },
+ 13: { phaseLabel: 'quickstart', blockNumber: 2, didacticMode: 'guided_dialogue', difficultyWeight: 3, newUnitTarget: 5, reviewWeight: 30, isIntensiveReview: false },
+ 14: { phaseLabel: 'quickstart', blockNumber: 2, didacticMode: 'core_input', difficultyWeight: 3, newUnitTarget: 6, reviewWeight: 30, isIntensiveReview: false },
+ 15: { phaseLabel: 'quickstart', blockNumber: 2, didacticMode: 'pattern_drill', difficultyWeight: 4, newUnitTarget: 4, reviewWeight: 40, isIntensiveReview: false },
+ 16: { phaseLabel: 'quickstart', blockNumber: 2, didacticMode: 'core_input', difficultyWeight: 3, newUnitTarget: 6, reviewWeight: 35, isIntensiveReview: false },
+ 17: { phaseLabel: 'quickstart', blockNumber: 2, didacticMode: 'guided_dialogue', difficultyWeight: 4, newUnitTarget: 5, reviewWeight: 35, isIntensiveReview: false },
+ 18: { phaseLabel: 'quickstart', blockNumber: 2, didacticMode: 'core_input', difficultyWeight: 4, newUnitTarget: 7, reviewWeight: 35, isIntensiveReview: false },
+ 19: { phaseLabel: 'quickstart', blockNumber: 2, didacticMode: 'intensive_review', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 92, isIntensiveReview: true },
+ 20: { phaseLabel: 'quickstart', blockNumber: 2, didacticMode: 'checkpoint', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 96, isIntensiveReview: false },
+ 21: { phaseLabel: 'daily_life', blockNumber: 3, didacticMode: 'guided_dialogue', difficultyWeight: 3, newUnitTarget: 5, reviewWeight: 35, isIntensiveReview: false },
+ 22: { phaseLabel: 'daily_life', blockNumber: 3, didacticMode: 'core_input', difficultyWeight: 3, newUnitTarget: 7, reviewWeight: 35, isIntensiveReview: false },
+ 23: { phaseLabel: 'daily_life', blockNumber: 3, didacticMode: 'guided_dialogue', difficultyWeight: 4, newUnitTarget: 5, reviewWeight: 40, isIntensiveReview: false },
+ 24: { phaseLabel: 'daily_life', blockNumber: 3, didacticMode: 'core_input', difficultyWeight: 4, newUnitTarget: 6, reviewWeight: 40, isIntensiveReview: false },
+ 25: { phaseLabel: 'daily_life', blockNumber: 3, didacticMode: 'pattern_drill', difficultyWeight: 4, newUnitTarget: 4, reviewWeight: 45, isIntensiveReview: false },
+ 26: { phaseLabel: 'daily_life', blockNumber: 3, didacticMode: 'guided_dialogue', difficultyWeight: 4, newUnitTarget: 5, reviewWeight: 45, isIntensiveReview: false },
+ 27: { phaseLabel: 'daily_life', blockNumber: 3, didacticMode: 'guided_dialogue', difficultyWeight: 3, newUnitTarget: 5, reviewWeight: 40, isIntensiveReview: false },
+ 28: { phaseLabel: 'daily_life', blockNumber: 3, didacticMode: 'core_input', difficultyWeight: 3, newUnitTarget: 6, reviewWeight: 40, isIntensiveReview: false },
+ 29: { phaseLabel: 'daily_life', blockNumber: 3, didacticMode: 'intensive_review', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 94, isIntensiveReview: true },
+ 30: { phaseLabel: 'daily_life', blockNumber: 3, didacticMode: 'checkpoint', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 97, isIntensiveReview: false },
+ 31: { phaseLabel: 'stabilization', blockNumber: 4, didacticMode: 'real_life_scenario', difficultyWeight: 4, newUnitTarget: 4, reviewWeight: 55, isIntensiveReview: false },
+ 32: { phaseLabel: 'stabilization', blockNumber: 4, didacticMode: 'intensive_review', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 95, isIntensiveReview: true },
+ 33: { phaseLabel: 'stabilization', blockNumber: 4, didacticMode: 'real_life_scenario', difficultyWeight: 4, newUnitTarget: 4, reviewWeight: 60, isIntensiveReview: false },
+ 34: { phaseLabel: 'stabilization', blockNumber: 4, didacticMode: 'intensive_review', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 95, isIntensiveReview: true },
+ 35: { phaseLabel: 'stabilization', blockNumber: 4, didacticMode: 'real_life_scenario', difficultyWeight: 5, newUnitTarget: 3, reviewWeight: 65, isIntensiveReview: false },
+ 36: { phaseLabel: 'stabilization', blockNumber: 4, didacticMode: 'intensive_review', difficultyWeight: 5, newUnitTarget: 0, reviewWeight: 98, isIntensiveReview: true },
+ 37: { phaseLabel: 'stabilization', blockNumber: 4, didacticMode: 'real_life_scenario', difficultyWeight: 5, newUnitTarget: 3, reviewWeight: 70, isIntensiveReview: false },
+ 38: { phaseLabel: 'stabilization', blockNumber: 4, didacticMode: 'checkpoint', difficultyWeight: 5, newUnitTarget: 0, reviewWeight: 99, isIntensiveReview: false },
+ 39: { phaseLabel: 'stabilization', blockNumber: 4, didacticMode: 'checkpoint', difficultyWeight: 5, newUnitTarget: 0, reviewWeight: 100, isIntensiveReview: false },
+ 40: { phaseLabel: 'stabilization', blockNumber: 4, didacticMode: 'real_life_scenario', difficultyWeight: 2, newUnitTarget: 2, reviewWeight: 50, isIntensiveReview: false },
+ 41: { phaseLabel: 'daily_life', blockNumber: 5, didacticMode: 'guided_dialogue', difficultyWeight: 3, newUnitTarget: 5, reviewWeight: 40, isIntensiveReview: false },
+ 42: { phaseLabel: 'daily_life', blockNumber: 5, didacticMode: 'core_input', difficultyWeight: 3, newUnitTarget: 6, reviewWeight: 40, isIntensiveReview: false },
+ 43: { phaseLabel: 'daily_life', blockNumber: 5, didacticMode: 'pattern_drill', difficultyWeight: 4, newUnitTarget: 4, reviewWeight: 45, isIntensiveReview: false },
+ 44: { phaseLabel: 'daily_life', blockNumber: 5, didacticMode: 'guided_dialogue', difficultyWeight: 3, newUnitTarget: 5, reviewWeight: 45, isIntensiveReview: false },
+ 45: { phaseLabel: 'daily_life', blockNumber: 5, didacticMode: 'intensive_review', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 95, isIntensiveReview: true },
+ 46: { phaseLabel: 'daily_life', blockNumber: 5, didacticMode: 'intensive_review', difficultyWeight: 3, newUnitTarget: 0, reviewWeight: 90, isIntensiveReview: true },
+ 47: { phaseLabel: 'daily_life', blockNumber: 5, didacticMode: 'guided_dialogue', difficultyWeight: 4, newUnitTarget: 5, reviewWeight: 45, isIntensiveReview: false },
+ 48: { phaseLabel: 'daily_life', blockNumber: 5, didacticMode: 'core_input', difficultyWeight: 3, newUnitTarget: 6, reviewWeight: 45, isIntensiveReview: false },
+ 49: { phaseLabel: 'daily_life', blockNumber: 5, didacticMode: 'intensive_review', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 96, isIntensiveReview: true },
+ 50: { phaseLabel: 'daily_life', blockNumber: 5, didacticMode: 'checkpoint', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 97, isIntensiveReview: false },
+ 51: { phaseLabel: 'stabilization', blockNumber: 6, didacticMode: 'guided_dialogue', difficultyWeight: 4, newUnitTarget: 5, reviewWeight: 50, isIntensiveReview: false },
+ 52: { phaseLabel: 'stabilization', blockNumber: 6, didacticMode: 'core_input', difficultyWeight: 3, newUnitTarget: 6, reviewWeight: 50, isIntensiveReview: false },
+ 53: { phaseLabel: 'stabilization', blockNumber: 6, didacticMode: 'guided_dialogue', difficultyWeight: 4, newUnitTarget: 5, reviewWeight: 55, isIntensiveReview: false },
+ 54: { phaseLabel: 'stabilization', blockNumber: 6, didacticMode: 'core_input', difficultyWeight: 3, newUnitTarget: 6, reviewWeight: 55, isIntensiveReview: false },
+ 55: { phaseLabel: 'stabilization', blockNumber: 6, didacticMode: 'real_life_scenario', difficultyWeight: 5, newUnitTarget: 3, reviewWeight: 65, isIntensiveReview: false },
+ 56: { phaseLabel: 'stabilization', blockNumber: 6, didacticMode: 'intensive_review', difficultyWeight: 5, newUnitTarget: 0, reviewWeight: 98, isIntensiveReview: true },
+ 57: { phaseLabel: 'stabilization', blockNumber: 6, didacticMode: 'real_life_scenario', difficultyWeight: 5, newUnitTarget: 3, reviewWeight: 70, isIntensiveReview: false },
+ 58: { phaseLabel: 'stabilization', blockNumber: 6, didacticMode: 'checkpoint', difficultyWeight: 5, newUnitTarget: 0, reviewWeight: 99, isIntensiveReview: false },
+ 59: { phaseLabel: 'stabilization', blockNumber: 6, didacticMode: 'checkpoint', difficultyWeight: 5, newUnitTarget: 0, reviewWeight: 100, isIntensiveReview: false },
+ 60: { phaseLabel: 'stabilization', blockNumber: 6, didacticMode: 'real_life_scenario', difficultyWeight: 2, newUnitTarget: 2, reviewWeight: 60, isIntensiveReview: false },
+ 61: { phaseLabel: 'daily_life', blockNumber: 7, didacticMode: 'guided_dialogue', difficultyWeight: 3, newUnitTarget: 5, reviewWeight: 45, isIntensiveReview: false },
+ 62: { phaseLabel: 'daily_life', blockNumber: 7, didacticMode: 'core_input', difficultyWeight: 3, newUnitTarget: 6, reviewWeight: 45, isIntensiveReview: false },
+ 63: { phaseLabel: 'daily_life', blockNumber: 7, didacticMode: 'pattern_drill', difficultyWeight: 4, newUnitTarget: 4, reviewWeight: 50, isIntensiveReview: false },
+ 64: { phaseLabel: 'daily_life', blockNumber: 7, didacticMode: 'guided_dialogue', difficultyWeight: 3, newUnitTarget: 5, reviewWeight: 50, isIntensiveReview: false },
+ 65: { phaseLabel: 'daily_life', blockNumber: 7, didacticMode: 'intensive_review', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 95, isIntensiveReview: true },
+ 66: { phaseLabel: 'daily_life', blockNumber: 7, didacticMode: 'intensive_review', difficultyWeight: 3, newUnitTarget: 0, reviewWeight: 92, isIntensiveReview: true },
+ 67: { phaseLabel: 'daily_life', blockNumber: 7, didacticMode: 'guided_dialogue', difficultyWeight: 3, newUnitTarget: 5, reviewWeight: 50, isIntensiveReview: false },
+ 68: { phaseLabel: 'daily_life', blockNumber: 7, didacticMode: 'core_input', difficultyWeight: 3, newUnitTarget: 6, reviewWeight: 50, isIntensiveReview: false },
+ 69: { phaseLabel: 'daily_life', blockNumber: 7, didacticMode: 'intensive_review', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 96, isIntensiveReview: true },
+ 70: { phaseLabel: 'daily_life', blockNumber: 7, didacticMode: 'checkpoint', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 97, isIntensiveReview: false },
+ 71: { phaseLabel: 'daily_life', blockNumber: 8, didacticMode: 'guided_dialogue', difficultyWeight: 4, newUnitTarget: 5, reviewWeight: 50, isIntensiveReview: false },
+ 72: { phaseLabel: 'daily_life', blockNumber: 8, didacticMode: 'core_input', difficultyWeight: 3, newUnitTarget: 6, reviewWeight: 50, isIntensiveReview: false },
+ 73: { phaseLabel: 'daily_life', blockNumber: 8, didacticMode: 'pattern_drill', difficultyWeight: 4, newUnitTarget: 4, reviewWeight: 55, isIntensiveReview: false },
+ 74: { phaseLabel: 'daily_life', blockNumber: 8, didacticMode: 'guided_dialogue', difficultyWeight: 4, newUnitTarget: 5, reviewWeight: 55, isIntensiveReview: false },
+ 75: { phaseLabel: 'daily_life', blockNumber: 8, didacticMode: 'intensive_review', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 95, isIntensiveReview: true },
+ 76: { phaseLabel: 'daily_life', blockNumber: 8, didacticMode: 'intensive_review', difficultyWeight: 3, newUnitTarget: 0, reviewWeight: 92, isIntensiveReview: true },
+ 77: { phaseLabel: 'daily_life', blockNumber: 8, didacticMode: 'guided_dialogue', difficultyWeight: 3, newUnitTarget: 5, reviewWeight: 55, isIntensiveReview: false },
+ 78: { phaseLabel: 'daily_life', blockNumber: 8, didacticMode: 'core_input', difficultyWeight: 3, newUnitTarget: 6, reviewWeight: 55, isIntensiveReview: false },
+ 79: { phaseLabel: 'daily_life', blockNumber: 8, didacticMode: 'intensive_review', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 96, isIntensiveReview: true },
+ 80: { phaseLabel: 'daily_life', blockNumber: 8, didacticMode: 'checkpoint', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 97, isIntensiveReview: false },
+ 81: { phaseLabel: 'daily_life', blockNumber: 9, didacticMode: 'guided_dialogue', difficultyWeight: 4, newUnitTarget: 5, reviewWeight: 50, isIntensiveReview: false },
+ 82: { phaseLabel: 'daily_life', blockNumber: 9, didacticMode: 'core_input', difficultyWeight: 3, newUnitTarget: 6, reviewWeight: 50, isIntensiveReview: false },
+ 83: { phaseLabel: 'daily_life', blockNumber: 9, didacticMode: 'pattern_drill', difficultyWeight: 4, newUnitTarget: 4, reviewWeight: 55, isIntensiveReview: false },
+ 84: { phaseLabel: 'daily_life', blockNumber: 9, didacticMode: 'guided_dialogue', difficultyWeight: 4, newUnitTarget: 5, reviewWeight: 55, isIntensiveReview: false },
+ 85: { phaseLabel: 'daily_life', blockNumber: 9, didacticMode: 'intensive_review', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 95, isIntensiveReview: true },
+ 86: { phaseLabel: 'daily_life', blockNumber: 9, didacticMode: 'intensive_review', difficultyWeight: 3, newUnitTarget: 0, reviewWeight: 92, isIntensiveReview: true },
+ 87: { phaseLabel: 'daily_life', blockNumber: 9, didacticMode: 'guided_dialogue', difficultyWeight: 4, newUnitTarget: 5, reviewWeight: 55, isIntensiveReview: false },
+ 88: { phaseLabel: 'daily_life', blockNumber: 9, didacticMode: 'core_input', difficultyWeight: 3, newUnitTarget: 6, reviewWeight: 55, isIntensiveReview: false },
+ 89: { phaseLabel: 'daily_life', blockNumber: 9, didacticMode: 'intensive_review', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 96, isIntensiveReview: true },
+ 90: { phaseLabel: 'daily_life', blockNumber: 9, didacticMode: 'checkpoint', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 97, isIntensiveReview: false },
+ 91: { phaseLabel: 'daily_life', blockNumber: 10, didacticMode: 'guided_dialogue', difficultyWeight: 3, newUnitTarget: 5, reviewWeight: 50, isIntensiveReview: false },
+ 92: { phaseLabel: 'daily_life', blockNumber: 10, didacticMode: 'core_input', difficultyWeight: 3, newUnitTarget: 6, reviewWeight: 50, isIntensiveReview: false },
+ 93: { phaseLabel: 'daily_life', blockNumber: 10, didacticMode: 'pattern_drill', difficultyWeight: 4, newUnitTarget: 4, reviewWeight: 55, isIntensiveReview: false },
+ 94: { phaseLabel: 'daily_life', blockNumber: 10, didacticMode: 'guided_dialogue', difficultyWeight: 3, newUnitTarget: 5, reviewWeight: 55, isIntensiveReview: false },
+ 95: { phaseLabel: 'daily_life', blockNumber: 10, didacticMode: 'intensive_review', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 95, isIntensiveReview: true },
+ 96: { phaseLabel: 'daily_life', blockNumber: 10, didacticMode: 'intensive_review', difficultyWeight: 3, newUnitTarget: 0, reviewWeight: 92, isIntensiveReview: true },
+ 97: { phaseLabel: 'daily_life', blockNumber: 10, didacticMode: 'guided_dialogue', difficultyWeight: 4, newUnitTarget: 5, reviewWeight: 55, isIntensiveReview: false },
+ 98: { phaseLabel: 'daily_life', blockNumber: 10, didacticMode: 'core_input', difficultyWeight: 3, newUnitTarget: 6, reviewWeight: 55, isIntensiveReview: false },
+ 99: { phaseLabel: 'daily_life', blockNumber: 10, didacticMode: 'intensive_review', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 96, isIntensiveReview: true },
+ 100: { phaseLabel: 'daily_life', blockNumber: 10, didacticMode: 'checkpoint', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 97, isIntensiveReview: false },
+ 101: { phaseLabel: 'stabilization', blockNumber: 11, didacticMode: 'guided_dialogue', difficultyWeight: 4, newUnitTarget: 5, reviewWeight: 60, isIntensiveReview: false },
+ 102: { phaseLabel: 'stabilization', blockNumber: 11, didacticMode: 'core_input', difficultyWeight: 3, newUnitTarget: 6, reviewWeight: 60, isIntensiveReview: false },
+ 103: { phaseLabel: 'stabilization', blockNumber: 11, didacticMode: 'pattern_drill', difficultyWeight: 4, newUnitTarget: 4, reviewWeight: 65, isIntensiveReview: false },
+ 104: { phaseLabel: 'stabilization', blockNumber: 11, didacticMode: 'guided_dialogue', difficultyWeight: 4, newUnitTarget: 5, reviewWeight: 65, isIntensiveReview: false },
+ 105: { phaseLabel: 'stabilization', blockNumber: 11, didacticMode: 'intensive_review', difficultyWeight: 5, newUnitTarget: 0, reviewWeight: 97, isIntensiveReview: true },
+ 106: { phaseLabel: 'stabilization', blockNumber: 11, didacticMode: 'intensive_review', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 94, isIntensiveReview: true },
+ 107: { phaseLabel: 'stabilization', blockNumber: 11, didacticMode: 'guided_dialogue', difficultyWeight: 4, newUnitTarget: 5, reviewWeight: 65, isIntensiveReview: false },
+ 108: { phaseLabel: 'stabilization', blockNumber: 11, didacticMode: 'core_input', difficultyWeight: 3, newUnitTarget: 6, reviewWeight: 65, isIntensiveReview: false },
+ 109: { phaseLabel: 'stabilization', blockNumber: 11, didacticMode: 'intensive_review', difficultyWeight: 5, newUnitTarget: 0, reviewWeight: 98, isIntensiveReview: true },
+ 110: { phaseLabel: 'stabilization', blockNumber: 11, didacticMode: 'checkpoint', difficultyWeight: 5, newUnitTarget: 0, reviewWeight: 99, isIntensiveReview: false },
+ 111: { phaseLabel: 'stabilization', blockNumber: 12, didacticMode: 'real_life_scenario', difficultyWeight: 5, newUnitTarget: 3, reviewWeight: 70, isIntensiveReview: false },
+ 112: { phaseLabel: 'stabilization', blockNumber: 12, didacticMode: 'intensive_review', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 95, isIntensiveReview: true },
+ 113: { phaseLabel: 'stabilization', blockNumber: 12, didacticMode: 'real_life_scenario', difficultyWeight: 5, newUnitTarget: 3, reviewWeight: 72, isIntensiveReview: false },
+ 114: { phaseLabel: 'stabilization', blockNumber: 12, didacticMode: 'pattern_drill', difficultyWeight: 5, newUnitTarget: 2, reviewWeight: 80, isIntensiveReview: false },
+ 115: { phaseLabel: 'stabilization', blockNumber: 12, didacticMode: 'intensive_review', difficultyWeight: 5, newUnitTarget: 0, reviewWeight: 99, isIntensiveReview: true },
+ 116: { phaseLabel: 'stabilization', blockNumber: 12, didacticMode: 'intensive_review', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 96, isIntensiveReview: true },
+ 117: { phaseLabel: 'stabilization', blockNumber: 12, didacticMode: 'real_life_scenario', difficultyWeight: 5, newUnitTarget: 3, reviewWeight: 75, isIntensiveReview: false },
+ 118: { phaseLabel: 'stabilization', blockNumber: 12, didacticMode: 'checkpoint', difficultyWeight: 5, newUnitTarget: 0, reviewWeight: 99, isIntensiveReview: false },
+ 119: { phaseLabel: 'stabilization', blockNumber: 12, didacticMode: 'checkpoint', difficultyWeight: 5, newUnitTarget: 0, reviewWeight: 100, isIntensiveReview: false },
+ 120: { phaseLabel: 'stabilization', blockNumber: 12, didacticMode: 'real_life_scenario', difficultyWeight: 2, newUnitTarget: 2, reviewWeight: 65, isIntensiveReview: false },
+ 121: { phaseLabel: 'stabilization', blockNumber: 13, didacticMode: 'real_life_scenario', difficultyWeight: 5, newUnitTarget: 3, reviewWeight: 72, isIntensiveReview: false },
+ 122: { phaseLabel: 'stabilization', blockNumber: 13, didacticMode: 'intensive_review', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 94, isIntensiveReview: true },
+ 123: { phaseLabel: 'stabilization', blockNumber: 13, didacticMode: 'pattern_drill', difficultyWeight: 5, newUnitTarget: 2, reviewWeight: 82, isIntensiveReview: false },
+ 124: { phaseLabel: 'stabilization', blockNumber: 13, didacticMode: 'real_life_scenario', difficultyWeight: 5, newUnitTarget: 3, reviewWeight: 74, isIntensiveReview: false },
+ 125: { phaseLabel: 'stabilization', blockNumber: 13, didacticMode: 'intensive_review', difficultyWeight: 5, newUnitTarget: 0, reviewWeight: 98, isIntensiveReview: true },
+ 126: { phaseLabel: 'stabilization', blockNumber: 13, didacticMode: 'intensive_review', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 95, isIntensiveReview: true },
+ 127: { phaseLabel: 'stabilization', blockNumber: 13, didacticMode: 'real_life_scenario', difficultyWeight: 5, newUnitTarget: 3, reviewWeight: 75, isIntensiveReview: false },
+ 128: { phaseLabel: 'stabilization', blockNumber: 13, didacticMode: 'intensive_review', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 95, isIntensiveReview: true },
+ 129: { phaseLabel: 'stabilization', blockNumber: 13, didacticMode: 'intensive_review', difficultyWeight: 5, newUnitTarget: 0, reviewWeight: 99, isIntensiveReview: true },
+ 130: { phaseLabel: 'stabilization', blockNumber: 13, didacticMode: 'checkpoint', difficultyWeight: 5, newUnitTarget: 0, reviewWeight: 99, isIntensiveReview: false },
+ 131: { phaseLabel: 'stabilization', blockNumber: 14, didacticMode: 'real_life_scenario', difficultyWeight: 5, newUnitTarget: 3, reviewWeight: 74, isIntensiveReview: false },
+ 132: { phaseLabel: 'stabilization', blockNumber: 14, didacticMode: 'intensive_review', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 94, isIntensiveReview: true },
+ 133: { phaseLabel: 'stabilization', blockNumber: 14, didacticMode: 'pattern_drill', difficultyWeight: 5, newUnitTarget: 2, reviewWeight: 84, isIntensiveReview: false },
+ 134: { phaseLabel: 'stabilization', blockNumber: 14, didacticMode: 'real_life_scenario', difficultyWeight: 5, newUnitTarget: 3, reviewWeight: 76, isIntensiveReview: false },
+ 135: { phaseLabel: 'stabilization', blockNumber: 14, didacticMode: 'intensive_review', difficultyWeight: 5, newUnitTarget: 0, reviewWeight: 98, isIntensiveReview: true },
+ 136: { phaseLabel: 'stabilization', blockNumber: 14, didacticMode: 'intensive_review', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 95, isIntensiveReview: true },
+ 137: { phaseLabel: 'stabilization', blockNumber: 14, didacticMode: 'real_life_scenario', difficultyWeight: 5, newUnitTarget: 3, reviewWeight: 78, isIntensiveReview: false },
+ 138: { phaseLabel: 'stabilization', blockNumber: 14, didacticMode: 'intensive_review', difficultyWeight: 4, newUnitTarget: 0, reviewWeight: 95, isIntensiveReview: true },
+ 139: { phaseLabel: 'stabilization', blockNumber: 14, didacticMode: 'intensive_review', difficultyWeight: 5, newUnitTarget: 0, reviewWeight: 99, isIntensiveReview: true },
+ 140: { phaseLabel: 'stabilization', blockNumber: 14, didacticMode: 'checkpoint', difficultyWeight: 5, newUnitTarget: 0, reviewWeight: 99, isIntensiveReview: false },
+ 141: { phaseLabel: 'stabilization', blockNumber: 15, didacticMode: 'intensive_review', difficultyWeight: 5, newUnitTarget: 0, reviewWeight: 99, isIntensiveReview: true },
+ 142: { phaseLabel: 'stabilization', blockNumber: 15, didacticMode: 'intensive_review', difficultyWeight: 5, newUnitTarget: 0, reviewWeight: 99, isIntensiveReview: true },
+ 143: { phaseLabel: 'stabilization', blockNumber: 15, didacticMode: 'pattern_drill', difficultyWeight: 5, newUnitTarget: 1, reviewWeight: 88, isIntensiveReview: false },
+ 144: { phaseLabel: 'stabilization', blockNumber: 15, didacticMode: 'real_life_scenario', difficultyWeight: 5, newUnitTarget: 2, reviewWeight: 80, isIntensiveReview: false },
+ 145: { phaseLabel: 'stabilization', blockNumber: 15, didacticMode: 'intensive_review', difficultyWeight: 5, newUnitTarget: 0, reviewWeight: 100, isIntensiveReview: true },
+ 146: { phaseLabel: 'stabilization', blockNumber: 15, didacticMode: 'intensive_review', difficultyWeight: 5, newUnitTarget: 0, reviewWeight: 100, isIntensiveReview: true },
+ 147: { phaseLabel: 'stabilization', blockNumber: 15, didacticMode: 'real_life_scenario', difficultyWeight: 5, newUnitTarget: 2, reviewWeight: 82, isIntensiveReview: false },
+ 148: { phaseLabel: 'stabilization', blockNumber: 15, didacticMode: 'checkpoint', difficultyWeight: 5, newUnitTarget: 0, reviewWeight: 100, isIntensiveReview: false },
+ 149: { phaseLabel: 'stabilization', blockNumber: 15, didacticMode: 'checkpoint', difficultyWeight: 5, newUnitTarget: 0, reviewWeight: 100, isIntensiveReview: false },
+ 150: { phaseLabel: 'stabilization', blockNumber: 15, didacticMode: 'real_life_scenario', difficultyWeight: 2, newUnitTarget: 1, reviewWeight: 70, isIntensiveReview: false }
+};
+
+export function getBisayaLessonPedagogy(lessonNumber) {
+ return BISAYA_LESSON_PEDAGOGY[Number(lessonNumber)] || null;
+}
diff --git a/backend/scripts/bisaya-course-phase3-extension.js b/backend/scripts/bisaya-course-phase3-extension.js
new file mode 100644
index 0000000..8a8003b
--- /dev/null
+++ b/backend/scripts/bisaya-course-phase3-extension.js
@@ -0,0 +1,254 @@
+export const BISAYA_PHASE3_DIDACTICS = {
+ 'Besuch & Gastfreundschaft': {
+ learningGoals: [
+ 'Einfache Besuchssituationen sprachlich eröffnen.',
+ 'Gäste begrüßen und höflich ins Haus bitten.',
+ 'Kurze Standardformeln für Einladung und Reaktion verwenden.'
+ ],
+ corePatterns: ['Sulod lang.', 'Lingkod sa.', 'Gimingaw mi nimo.', 'Salamat sa pag-anhi.'],
+ speakingPrompts: [
+ {
+ title: 'Gast begrüßen',
+ prompt: 'Begrüße einen Gast, bitte ihn herein und biete einen Sitzplatz an.',
+ cue: 'Maayong adlaw. Sulod lang. Lingkod sa.'
+ }
+ ],
+ practicalTasks: [
+ {
+ title: 'Mini-Rollenspiel',
+ text: 'Spiele den Moment nach, wenn ein Familienmitglied zu Besuch kommt.'
+ }
+ ]
+ },
+ 'Besuch & Haushalt': {
+ learningGoals: [
+ 'Wichtige Wörter rund um Besuch und Haushalt erkennen.',
+ 'Einfache Dinge im Haus benennen.',
+ 'Wortschatz aus Woche 1 und 2 mit neuen Besuchssituationen verbinden.'
+ ],
+ corePatterns: ['bisita', 'balay', 'lamisa', 'lingkuranan']
+ },
+ 'Fragen im Alltag vertiefen': {
+ learningGoals: [
+ 'Fragen mit mehr Varianten bilden.',
+ 'Rückfragen höflich wiederholen.',
+ 'Zwischen Frage, Bitte und Bestätigung sicher wechseln.'
+ ],
+ corePatterns: ['Asa ka padulong?', 'Unsa imong buhaton?', 'Pwede mangutana?', 'Palihug ka mubalik?'],
+ grammarFocus: [
+ {
+ title: 'Frageketten',
+ text: 'Im Alltag werden oft zwei kurze Fragen nacheinander gestellt statt eines langen Satzes.',
+ example: 'Asa ka padulong? Unsa imong buhaton didto?'
+ }
+ ],
+ speakingPrompts: [
+ {
+ title: 'Nachfragen',
+ prompt: 'Frage nach Ziel, Zeit und Vorhaben einer Person.',
+ cue: 'Asa ka padulong? Kanus-a ka moadto?'
+ }
+ ]
+ },
+ 'Termine & Verabredungen': {
+ learningGoals: [
+ 'Einfache Treffen verabreden.',
+ 'Zeitangaben und Zusagen kombinieren.',
+ 'Kurze Planungsdialoge laut üben.'
+ ],
+ corePatterns: ['Magkita ta ugma.', 'Unsa orasa?', 'Pwede ko karon.', 'Sige, kita ta.'],
+ speakingPrompts: [
+ {
+ title: 'Treffen planen',
+ prompt: 'Vereinbare ein Treffen für morgen und frage nach der Uhrzeit.',
+ cue: 'Magkita ta ugma. Unsa orasa?'
+ }
+ ]
+ },
+ 'Woche 5 - Intensivwiederholung I': {
+ learningGoals: [
+ 'Muster aus Familie, Fürsorge, Zeit und Besuch zusammen abrufen.',
+ 'Zwischen alten und neuen Themen schneller wechseln.',
+ 'Mehrere Mini-Szenen ohne deutsche Stütze lösen.'
+ ],
+ corePatterns: ['Kumusta ka?', 'Nikaon na ka?', 'Magkita ta ugma.', 'Sulod lang.']
+ },
+ 'Spiralwiederholung - Familie & Fürsorge': {
+ learningGoals: [
+ 'Frühe Familien- und Fürsorgemuster aktiv festigen.',
+ 'Bekannte Wörter in neuen Dialogen wiedererkennen.',
+ 'Von Einzelwörtern zu flüssigen Alltagsformeln übergehen.'
+ ],
+ corePatterns: ['Nanay', 'Tatay', 'Palangga taka.', 'Nikaon na ka?']
+ },
+ 'Gesundheit im Alltag': {
+ learningGoals: [
+ 'Einfache Beschwerden und Fürsorge im Gespräch ausdrücken.',
+ 'Nach dem Befinden fragen und Hilfe anbieten.',
+ 'Kurze Gesundheitsdialoge in der Familie üben.'
+ ],
+ corePatterns: ['Sakit imong ulo?', 'Niinom ka og tambal?', 'Magpahuway sa.', 'Maayo ra ko.'],
+ speakingPrompts: [
+ {
+ title: 'Nach dem Befinden fragen',
+ prompt: 'Frage nach Schmerzen und biete Ruhe oder Medizin an.',
+ cue: 'Sakit imong ulo? Magpahuway sa.'
+ }
+ ]
+ },
+ 'Medikamente & Beschwerden': {
+ learningGoals: [
+ 'Wichtige Wörter zu Schmerzen und Medizin erkennen.',
+ 'Zwischen Körperteil, Symptom und Hilfe unterscheiden.',
+ 'Gesundheitswortschatz mit Fürsorge verbinden.'
+ ],
+ corePatterns: ['tambal', 'ubo', 'hilanat', 'kasakit']
+ },
+ 'Woche 5 - Intensivwiederholung II': {
+ learningGoals: [
+ 'Gesundheit, Fragen, Besuch und Organisation gemischt trainieren.',
+ 'Wackelige Muster noch einmal unter Zeitdruck abrufen.',
+ 'Sprechtempo und Reaktionssicherheit erhöhen.'
+ ],
+ corePatterns: ['Pwede mangutana?', 'Magkita ta.', 'Niinom ka og tambal?', 'Salamat sa pag-anhi.']
+ },
+ 'Woche 5 - Checkpoint': {
+ learningGoals: [
+ 'Die wichtigsten Inhalte von Woche 5 abrufen.',
+ 'Sicherheit bei Alltags- und Besuchsdialogen prüfen.',
+ 'Stärken und Lücken vor Woche 6 sichtbar machen.'
+ ],
+ corePatterns: ['Sulod lang.', 'Unsa orasa?', 'Magpahuway sa.', 'Palihug ka mubalik?']
+ },
+ 'Unterwegs & Transport': {
+ learningGoals: [
+ 'Unterwegs nach Weg, Fahrt und Ziel fragen.',
+ 'Kurze Transportsituationen bewältigen.',
+ 'Richtungen und Verkehrswörter im Kontext nutzen.'
+ ],
+ corePatterns: ['Asa ang sakayan?', 'Moadto ko sa lungsod.', 'Hunong lang dinhi.', 'Pila ang plite?'],
+ speakingPrompts: [
+ {
+ title: 'Transport fragen',
+ prompt: 'Frage nach Haltestelle, Preis und Zielort.',
+ cue: 'Asa ang sakayan? Pila ang plite?'
+ }
+ ]
+ },
+ 'Wege & Verkehr': {
+ learningGoals: [
+ 'Weg- und Verkehrswortschatz ausbauen.',
+ 'Transportwörter mit Richtungen verbinden.',
+ 'Häufige Reise- und Bewegungsverben wiederholen.'
+ ],
+ corePatterns: ['sakyanan', 'dalan', 'hunong', 'biyahe']
+ },
+ 'Arbeit & Aufgaben': {
+ learningGoals: [
+ 'Über einfache Tätigkeiten und Pflichten sprechen.',
+ 'Arbeit, Haushalt und Tagesaufgaben benennen.',
+ 'Kurze Aussagen über To-dos machen.'
+ ],
+ corePatterns: ['Naa koy trabaho karon.', 'Aduna koy buhaton.', 'Human na ko.', 'Tabang ta.'],
+ speakingPrompts: [
+ {
+ title: 'Tagesaufgaben',
+ prompt: 'Erkläre kurz, was du heute noch erledigen musst.',
+ cue: 'Aduna koy buhaton karon. Human na ko unya.'
+ }
+ ]
+ },
+ 'Tätigkeiten & Organisation': {
+ learningGoals: [
+ 'Wörter für Aufgaben, Arbeit und Organisation erkennen.',
+ 'Bekannte Verben in neue Alltagssituationen übertragen.',
+ 'Kurze Planungs- und Erledigungssätze vorbereiten.'
+ ],
+ corePatterns: ['trabaho', 'buluhaton', 'lista', 'tabang']
+ },
+ 'Freies Gespräch - Familie & Alltag': {
+ learningGoals: [
+ 'Frühere Themen ohne enge Führung kombinieren.',
+ 'Längere Alltagsantworten aufbauen.',
+ 'Im freien Sprechen bei bekannten Themen stabil bleiben.'
+ ],
+ corePatterns: ['Maayo ra.', 'Naa mi sa balay.', 'Magkita mi unya.', 'Gimingaw ko nimo.'],
+ speakingPrompts: [
+ {
+ title: 'Alltagsgespräch',
+ prompt: 'Erzähle kurz von Familie, Tagesplan und aktuellem Befinden.',
+ cue: 'Naa mi sa balay. Maayo ra ko. Aduna koy buhaton unya.'
+ }
+ ]
+ },
+ 'Spiralwiederholung - Wochen 1 bis 4': {
+ learningGoals: [
+ 'Frühe Kursinhalte blockübergreifend wiederholen.',
+ 'Ähnliche Muster sauber unterscheiden.',
+ 'Vor dem Schnellstart-Abschluss zentrale Formen stabilisieren.'
+ ],
+ corePatterns: ['Kumusta ka?', 'Tagpila ni?', 'Ni-kaon ko.', 'Nalipay ko.']
+ },
+ 'Konflikte & Missverständnisse': {
+ learningGoals: [
+ 'Missverständnisse höflich ansprechen.',
+ 'Um Wiederholung oder langsamere Erklärung bitten.',
+ 'Kleine Konflikte sprachlich entschärfen.'
+ ],
+ corePatterns: ['Wala ko kasabot.', 'Hinay-hinay lang.', 'Pwede nato istoryahan?', 'Pasayloa ko.'],
+ speakingPrompts: [
+ {
+ title: 'Missverständnis lösen',
+ prompt: 'Bitte um Wiederholung und entschuldige dich höflich.',
+ cue: 'Wala ko kasabot. Palihug ka mubalik. Pasayloa ko.'
+ }
+ ]
+ },
+ 'Abschlusstest - Schnellstart': {
+ learningGoals: [
+ 'Wichtige Wörter aus dem gesamten Schnellstart sicher abrufen.',
+ 'Zwischen ähnlichen Antworten sauber unterscheiden.',
+ 'Den aktiven Grundwortschatz stabilisieren.'
+ ],
+ corePatterns: ['Kumusta', 'bisita', 'tambal', 'plite']
+ },
+ 'Abschlussprüfung - Schnellstart': {
+ learningGoals: [
+ 'Den 6-Wochen-Schnellstart in gemischten Aufgaben überprüfen.',
+ 'Abruf, Musterverständnis und situative Anwendung kombinieren.',
+ 'Die Grundlage für die Alltagsphase absichern.'
+ ],
+ corePatterns: ['Sulod lang.', 'Magkita ta ugma.', 'Niinom ka og tambal?', 'Asa ang sakayan?']
+ },
+ 'Kulturelle Vertiefung im Familienalltag': {
+ learningGoals: [
+ 'Sprachgebrauch und Familienkultur besser einordnen.',
+ 'Höflichkeit, Nähe und indirekte Kommunikation bewusster wahrnehmen.',
+ 'Kulturelle Unterschiede mit dem gelernten Sprachmaterial verknüpfen.'
+ ],
+ corePatterns: ['pakikisama', 'respeto', 'amping', 'palihug']
+ }
+};
+
+export const BISAYA_PHASE3_LESSONS = [
+ { week: 5, day: 1, num: 41, type: 'conversation', title: 'Besuch & Gastfreundschaft', desc: 'Besuch empfangen, hereinbitten und freundlich reagieren', targetMin: 18, targetScore: 80, review: false, cultural: 'Gastfreundschaft ist im philippinischen Familienalltag zentral.' },
+ { week: 5, day: 1, num: 42, type: 'vocab', title: 'Besuch & Haushalt', desc: 'Wichtige Wörter für Besuch, Haus und gemeinsame Zeit', targetMin: 18, targetScore: 85, review: true, cultural: null },
+ { week: 5, day: 2, num: 43, type: 'grammar', title: 'Fragen im Alltag vertiefen', desc: 'Rückfragen, Folgefragen und höfliches Nachhaken', targetMin: 22, targetScore: 78, review: true, cultural: null },
+ { week: 5, day: 2, num: 44, type: 'conversation', title: 'Termine & Verabredungen', desc: 'Treffen planen und Uhrzeiten absprechen', targetMin: 18, targetScore: 80, review: false, cultural: null },
+ { week: 5, day: 3, num: 45, type: 'review', title: 'Woche 5 - Intensivwiederholung I', desc: 'Erste große Wiederholung zu Besuch, Alltag und Fürsorge', targetMin: 28, targetScore: 80, review: false, cultural: null },
+ { week: 5, day: 3, num: 46, type: 'vocab', title: 'Spiralwiederholung - Familie & Fürsorge', desc: 'Alte Kernmuster gezielt wiederholen und festigen', targetMin: 20, targetScore: 85, review: true, cultural: null },
+ { week: 5, day: 4, num: 47, type: 'conversation', title: 'Gesundheit im Alltag', desc: 'Nach Beschwerden fragen und Hilfe anbieten', targetMin: 18, targetScore: 80, review: false, cultural: null },
+ { week: 5, day: 4, num: 48, type: 'vocab', title: 'Medikamente & Beschwerden', desc: 'Schmerz, Medizin und Fürsorgewortschatz', targetMin: 18, targetScore: 85, review: true, cultural: null },
+ { week: 5, day: 5, num: 49, type: 'review', title: 'Woche 5 - Intensivwiederholung II', desc: 'Große Mischwiederholung mit Fokus auf Abruf und Tempo', targetMin: 28, targetScore: 80, review: false, cultural: null },
+ { week: 5, day: 5, num: 50, type: 'vocab', title: 'Woche 5 - Checkpoint', desc: 'Checkpoint zu den Inhalten von Woche 5', targetMin: 16, targetScore: 82, review: true, cultural: null },
+ { week: 6, day: 1, num: 51, type: 'conversation', title: 'Unterwegs & Transport', desc: 'Nach Weg, Fahrt, Preis und Ziel fragen', targetMin: 18, targetScore: 80, review: false, cultural: null },
+ { week: 6, day: 1, num: 52, type: 'vocab', title: 'Wege & Verkehr', desc: 'Verkehrs- und Bewegungswortschatz für den Alltag', targetMin: 18, targetScore: 85, review: true, cultural: null },
+ { week: 6, day: 2, num: 53, type: 'conversation', title: 'Arbeit & Aufgaben', desc: 'Über Pflichten, Arbeit und Erledigungen sprechen', targetMin: 18, targetScore: 80, review: false, cultural: null },
+ { week: 6, day: 2, num: 54, type: 'vocab', title: 'Tätigkeiten & Organisation', desc: 'Verben und Wörter für Aufgaben, Listen und Hilfe', targetMin: 18, targetScore: 85, review: true, cultural: null },
+ { week: 6, day: 3, num: 55, type: 'conversation', title: 'Freies Gespräch - Familie & Alltag', desc: 'Freier sprechen über Familie, Zuhause und Tagesablauf', targetMin: 22, targetScore: 78, review: false, cultural: null },
+ { week: 6, day: 3, num: 56, type: 'review', title: 'Spiralwiederholung - Wochen 1 bis 4', desc: 'Frühere Inhalte blockübergreifend wiederholen', targetMin: 28, targetScore: 82, review: false, cultural: null },
+ { week: 6, day: 4, num: 57, type: 'conversation', title: 'Konflikte & Missverständnisse', desc: 'Höflich um Wiederholung bitten und Missverständnisse klären', targetMin: 20, targetScore: 80, review: false, cultural: 'Ruhiger, indirekter Umgang hilft in heiklen Gesprächen.' },
+ { week: 6, day: 4, num: 58, type: 'vocab', title: 'Abschlusstest - Schnellstart', desc: 'Finaler Wortschatztest über den 6-Wochen-Schnellstart', targetMin: 18, targetScore: 82, review: true, cultural: null },
+ { week: 6, day: 5, num: 59, type: 'review', title: 'Abschlussprüfung - Schnellstart', desc: 'Große Abschlussprüfung mit gemischten Inhalten', targetMin: 30, targetScore: 82, review: false, cultural: 'Die Abschlussphase prüft vor allem sichere Alltagsfähigkeit.' },
+ { week: 6, day: 5, num: 60, type: 'culture', title: 'Kulturelle Vertiefung im Familienalltag', desc: 'Vertiefe wichtige kulturelle Muster für Sprache und Familienalltag', targetMin: 16, targetScore: 0, review: false, cultural: 'Sprache, Respekt und Familienrollen sind eng miteinander verbunden.' }
+];
diff --git a/backend/scripts/bisaya-course-phase4-extension.js b/backend/scripts/bisaya-course-phase4-extension.js
new file mode 100644
index 0000000..a16d921
--- /dev/null
+++ b/backend/scripts/bisaya-course-phase4-extension.js
@@ -0,0 +1,594 @@
+export const BISAYA_PHASE4_DIDACTICS = {
+ 'Kinder im Alltag': {
+ learningGoals: [
+ 'Über Kinder im Familienalltag sprechen.',
+ 'Einfache Fragen zu Hunger, Müdigkeit und Schule stellen.',
+ 'Kurze Fürsorge-Dialoge mit Kindern laut üben.'
+ ],
+ corePatterns: ['Nikaon na ang bata?', 'Asa ang bata?', 'Kapoy na ka?', 'Andam na ka sa eskwela?'],
+ speakingPrompts: [
+ {
+ title: 'Mit Kind sprechen',
+ prompt: 'Frage ein Kind nach Essen, Müdigkeit und Schule.',
+ cue: 'Nikaon na ka? Kapoy na ka?'
+ }
+ ]
+ },
+ 'Schule & Betreuung': {
+ learningGoals: [
+ 'Wichtige Wörter zu Schule und Betreuung erkennen.',
+ 'Über Tasche, Hefte, Lehrkraft und Unterricht sprechen.',
+ 'Kinder- und Schulwortschatz mit Familienalltag verbinden.'
+ ],
+ corePatterns: ['eskwela', 'maestra', 'bag', 'leksyon']
+ },
+ 'Fragen an Kinder vereinfachen': {
+ learningGoals: [
+ 'Kurze, einfache Fragen für Kinder bilden.',
+ 'Aufforderungen und Fragen freundlich unterscheiden.',
+ 'Kindgerechte Alltagsmuster wiederholt anwenden.'
+ ],
+ corePatterns: ['Unsa ni?', 'Asa imong bag?', 'Andam na ka?', 'Ali diri.'],
+ grammarFocus: [
+ {
+ title: 'Kurze Fragen',
+ text: 'Mit Kindern funktionieren kurze, klare Fragen oft besser als lange Sätze.',
+ example: 'Asa imong bag? Andam na ka?'
+ }
+ ]
+ },
+ 'Hausaufgaben & Routine': {
+ learningGoals: [
+ 'Über Lernen, Hausaufgaben und Routine sprechen.',
+ 'Einfachen Tagesablauf mit Schule und Zuhause verbinden.',
+ 'Kurze Organisationsgespräche führen.'
+ ],
+ corePatterns: ['Naa kay assignment?', 'Human na ka?', 'Magtuon ta.', 'Sunod, matulog na.']
+ },
+ 'Woche 7 - Intensivwiederholung I': {
+ learningGoals: [
+ 'Kinder-, Familien- und Routinemuster gemeinsam abrufen.',
+ 'Frühere Fürsorgeformen mit Schulalltag verbinden.',
+ 'Schneller zwischen Themen wechseln.'
+ ],
+ corePatterns: ['Nikaon na ka?', 'Asa ang bata?', 'Magtuon ta.', 'Palihug lingkod.']
+ },
+ 'Spiralwiederholung - Familie, Kinder & Fürsorge': {
+ learningGoals: [
+ 'Familien- und Kindermuster spiralig wiederholen.',
+ 'Bekannte Kernwörter schneller abrufen.',
+ 'Alte Inhalte in neuen Situationen festigen.'
+ ],
+ corePatterns: ['Nanay', 'Tatay', 'bata', 'eskwela']
+ },
+ 'Spielen & Freizeit': {
+ learningGoals: [
+ 'Über Spiel, Freizeit und kurze Aktivitäten sprechen.',
+ 'Kinder zu Spiel und Pause begleiten.',
+ 'Leichte Alltagsdialoge mit Bewegung verbinden.'
+ ],
+ corePatterns: ['Magdula ta.', 'Ganahan ka modula?', 'Lingaw ka?', 'Human na ang duwa.']
+ },
+ 'Spielsachen & Aktivitäten': {
+ learningGoals: [
+ 'Wortschatz für Spiel und Freizeit ausbauen.',
+ 'Aktivitäten benennen und vergleichen.',
+ 'Kinderalltag mit neuen Nomen anreichern.'
+ ],
+ corePatterns: ['duwa', 'bola', 'libro', 'kanta']
+ },
+ 'Woche 7 - Intensivwiederholung II': {
+ learningGoals: [
+ 'Kinder-, Spiel- und Schulmuster unter Zeitdruck abrufen.',
+ 'Schwierige Formen gezielt wiederholen.',
+ 'Mehr Sicherheit in Alltagsszenen mit Kindern gewinnen.'
+ ],
+ corePatterns: ['Andam na ka?', 'Magdula ta.', 'Human na ka?', 'Asa imong bag?']
+ },
+ 'Woche 7 - Checkpoint': {
+ learningGoals: [
+ 'Die wichtigsten Inhalte der Kinder- und Schulwoche überprüfen.',
+ 'Abruf und Anwendung im Familienalltag testen.',
+ 'Lücken vor Woche 8 sichtbar machen.'
+ ],
+ corePatterns: ['eskwela', 'assignment', 'bata', 'magdula']
+ },
+ 'Arzt & Termin': {
+ learningGoals: [
+ 'Arzttermine sprachlich vorbereiten.',
+ 'Über Beschwerden, Termin und Wartezeit sprechen.',
+ 'Einfache Gesundheitsgespräche strukturieren.'
+ ],
+ corePatterns: ['Adto ta sa doktor.', 'Naa moy appointment?', 'Unsay gibati nimo?', 'Maghulat ta.']
+ },
+ 'Apotheke & Medikamente': {
+ learningGoals: [
+ 'Wortschatz aus Apotheke und Medizin sicher erkennen.',
+ 'Zwischen Medikament, Rezept und Dosierung unterscheiden.',
+ 'Gesundheitswortschatz in Erledigungen einordnen.'
+ ],
+ corePatterns: ['tambal', 'botika', 'reseta', 'dose']
+ },
+ 'Beschwerden genauer beschreiben': {
+ learningGoals: [
+ 'Beschwerden konkreter benennen.',
+ 'Zwischen leicht, stark und wiederkehrend unterscheiden.',
+ 'Fragen und Antworten zu Schmerzen vertiefen.'
+ ],
+ corePatterns: ['Sakit kaayo.', 'Gamaya ra ang sakit.', 'Nagsugod ganiha.', 'Mas maayo na karon.'],
+ grammarFocus: [
+ {
+ title: 'Beschwerden abstufen',
+ text: 'Im Alltag helfen kurze Verstärker und Zeitangaben, um Schmerzen genauer zu beschreiben.',
+ example: 'Sakit kaayo. Nagsugod ganiha.'
+ }
+ ]
+ },
+ 'Notfälle & Hilfe': {
+ learningGoals: [
+ 'In einfachen Notlagen Hilfe rufen und organisieren.',
+ 'Klare, kurze Anweisungen geben.',
+ 'Grundmuster für schnelle Reaktion beherrschen.'
+ ],
+ corePatterns: ['Tabang!', 'Tawag ug doktor.', 'Dali lang.', 'Asa ang tambal?']
+ },
+ 'Woche 8 - Intensivwiederholung I': {
+ learningGoals: [
+ 'Arzt-, Apotheke- und Hilfemuster gebündelt wiederholen.',
+ 'Beschwerden schneller abrufen und zuordnen.',
+ 'Gesundheitskommunikation stabilisieren.'
+ ],
+ corePatterns: ['doktor', 'tambal', 'tabang', 'sakit kaayo']
+ },
+ 'Spiralwiederholung - Gesundheit': {
+ learningGoals: [
+ 'Frühere Fürsorge- und Gesundheitsmuster zusammenführen.',
+ 'Bekannte Wörter in neuen Gesundheitsdialogen wiederholen.',
+ 'Gesundheitswortschatz langfristig verankern.'
+ ],
+ corePatterns: ['Nikaon na ka?', 'Magpahuway sa.', 'doktor', 'tambal']
+ },
+ 'Essen, Ruhe & Genesung': {
+ learningGoals: [
+ 'Über Ruhe, Essen und Erholung bei Krankheit sprechen.',
+ 'Kurze Pflegegespräche im Familienalltag führen.',
+ 'Fürsorge und Gesundheit verbinden.'
+ ],
+ corePatterns: ['Magpahuway sa.', 'Mokaon sa ka.', 'Uminom og tubig.', 'Mas maayo na ka?']
+ },
+ 'Körper, Symptome & Pflege': {
+ learningGoals: [
+ 'Körperteile, Symptome und Pflegewörter ausbauen.',
+ 'Zwischen Ort und Art einer Beschwerde unterscheiden.',
+ 'Pflegealltag mit konkreterem Wortschatz stützen.'
+ ],
+ corePatterns: ['ulo', 'tiyan', 'hilanat', 'alaga']
+ },
+ 'Woche 8 - Intensivwiederholung II': {
+ learningGoals: [
+ 'Gesundheitswortschatz in Mischsituationen wiederholen.',
+ 'Fragen, Antworten und Reaktionen zügig abrufen.',
+ 'Vor dem Wochenabschluss Sicherheit erhöhen.'
+ ],
+ corePatterns: ['Mas maayo na ka?', 'Adto ta sa doktor.', 'Uminom og tubig.', 'Tabang!']
+ },
+ 'Woche 8 - Checkpoint': {
+ learningGoals: [
+ 'Die Gesundheitswoche diagnostisch abschließen.',
+ 'Abruf, Verständnis und situative Anwendung prüfen.',
+ 'Stärken und Lücken für Woche 9 festhalten.'
+ ],
+ corePatterns: ['doktor', 'botika', 'hilanat', 'tabang']
+ },
+ 'Einkaufen vertiefen': {
+ learningGoals: [
+ 'Komplexere Einkaufssituationen meistern.',
+ 'Zwischen Auswahl, Menge und Preis sicher wechseln.',
+ 'Kürzere und längere Kaufdialoge kombinieren.'
+ ],
+ corePatterns: ['Pila ni tanan?', 'Pwede tulo kabuok?', 'Naa moy mas barato?', 'Kuhaon na nako.']
+ },
+ 'Markt & Mengen': {
+ learningGoals: [
+ 'Mengen- und Marktwortschatz ausbauen.',
+ 'Zwischen Stück, Kilo und Portion unterscheiden.',
+ 'Preise und Mengen natürlicher kombinieren.'
+ ],
+ corePatterns: ['kilo', 'buok', 'merkado', 'sobra']
+ },
+ 'Wünsche, Bedarf und Bitte': {
+ learningGoals: [
+ 'Wünsche höflich formulieren.',
+ 'Bedarf, Bitte und Auswahl sprachlich unterscheiden.',
+ 'Kauf- und Alltagsbitten variieren.'
+ ],
+ corePatterns: ['Gusto ko ani.', 'Kinahanglan nako ni.', 'Pwede palihug?', 'Mas maayo ni para nako.'],
+ grammarFocus: [
+ {
+ title: 'Wunsch vs. Bedarf',
+ text: 'Mit "gusto" und "kinahanglan" lassen sich Wunsch und Notwendigkeit klar unterscheiden.',
+ example: 'Gusto ko ani. Kinahanglan nako ni.'
+ }
+ ]
+ },
+ 'Behördengänge & Formulare': {
+ learningGoals: [
+ 'Einfache Behördensituationen sprachlich vorbereiten.',
+ 'Nach Formularen, Schaltern und Terminen fragen.',
+ 'Erledigungssprache über den Einkauf hinaus ausbauen.'
+ ],
+ corePatterns: ['Asa ang porma?', 'Asa ko mulinya?', 'Naa koy appointment.', 'Unsa ang sunod?']
+ },
+ 'Woche 9 - Intensivwiederholung I': {
+ learningGoals: [
+ 'Einkaufs- und Erledigungsmuster zusammen wiederholen.',
+ 'Mengen, Preise und Bitten schneller abrufen.',
+ 'Alltagsorganisation sprachlich absichern.'
+ ],
+ corePatterns: ['Pila ni tanan?', 'Kinahanglan nako ni.', 'Asa ang porma?', 'Pwede palihug?']
+ },
+ 'Spiralwiederholung - Preise & Erledigungen': {
+ learningGoals: [
+ 'Preis- und Erledigungsmuster blockübergreifend verknüpfen.',
+ 'Alte und neue Einkaufsformen sauber unterscheiden.',
+ 'Häufige Alltagsfragen festigen.'
+ ],
+ corePatterns: ['Tagpila ni?', 'Pila ni tanan?', 'appointment', 'linya']
+ },
+ 'Bank, Geld & Bezahlen': {
+ learningGoals: [
+ 'Über Bargeld, Wechselgeld und Bezahlen sprechen.',
+ 'Einfache Geldsituationen im Alltag bewältigen.',
+ 'Einkauf und Finanzalltag verbinden.'
+ ],
+ corePatterns: ['Mubayad ko.', 'Naa kay sinsilyo?', 'Wala koy sukli.', 'Asa ang ATM?']
+ },
+ 'Dokumente & Termine': {
+ learningGoals: [
+ 'Wichtige Wörter für Dokumente und Termine lernen.',
+ 'Papierkram und Terminorganisation sprachlich stützen.',
+ 'Erledigungssituationen besser strukturieren.'
+ ],
+ corePatterns: ['ID', 'papel', 'appointment', 'resibo']
+ },
+ 'Woche 9 - Intensivwiederholung II': {
+ learningGoals: [
+ 'Erledigungen, Bezahlen und Behördensprache verdichten.',
+ 'Fehleranfällige Muster gezielt festigen.',
+ 'Vor dem Checkpoint die Abrufsicherheit erhöhen.'
+ ],
+ corePatterns: ['Mubayad ko.', 'Wala koy sukli.', 'Asa ko mulinya?', 'Naa koy appointment.']
+ },
+ 'Woche 9 - Checkpoint': {
+ learningGoals: [
+ 'Die wichtigsten Inhalte rund um Erledigungen überprüfen.',
+ 'Sprachliche Sicherheit bei Kauf, Formular und Termin messen.',
+ 'Übergang zur Sozialphase vorbereiten.'
+ ],
+ corePatterns: ['merkado', 'ATM', 'resibo', 'porma']
+ },
+ 'Nachbarschaft & Besuche': {
+ learningGoals: [
+ 'Über Nachbarn, Besuche und kurze Begegnungen sprechen.',
+ 'Freundliche Alltagsgespräche im Umfeld führen.',
+ 'Soziale Nähe sprachlich ausdrücken.'
+ ],
+ corePatterns: ['Kumusta mo?', 'Niadto mi sa silingan.', 'Moadto ko didto unya.', 'Bisita mo unya.']
+ },
+ 'Hilfe & Unterstützung': {
+ learningGoals: [
+ 'Um Hilfe bitten und Hilfe anbieten.',
+ 'Unterstützung im Alltag sprachlich strukturieren.',
+ 'Kurze soziale Hilfsdialoge üben.'
+ ],
+ corePatterns: ['Pwede ka motabang?', 'Tabangan tika.', 'Salamat sa tabang.', 'Kinahanglan ko og tabang.']
+ },
+ 'Höflich reagieren und ablehnen': {
+ learningGoals: [
+ 'Höflich zusagen, verschieben oder ablehnen.',
+ 'Soziale Reaktionen natürlicher gestalten.',
+ 'Hilfe und Einladungen fein abstufen.'
+ ],
+ corePatterns: ['Sige lang.', 'Dili lang sa karon.', 'Sunod na lang.', 'Salamat pero dili ko mahimo.'],
+ grammarFocus: [
+ {
+ title: 'Sanft ablehnen',
+ text: 'Im Alltag klingt eine weiche Ablehnung oft natürlicher als ein direktes Nein.',
+ example: 'Dili lang sa karon. Sunod na lang.'
+ }
+ ]
+ },
+ 'Feste & Einladungen': {
+ learningGoals: [
+ 'Über Feiern und Einladungen sprechen.',
+ 'Einladungen aussprechen, annehmen oder verschieben.',
+ 'Soziale Ereignisse sprachlich einordnen.'
+ ],
+ corePatterns: ['Aduna moy handa?', 'Moadto ka sa pista?', 'Giinvite tika.', 'Magkita ta didto.']
+ },
+ 'Woche 10 - Intensivwiederholung I': {
+ learningGoals: [
+ 'Soziale Muster mit Hilfe, Besuch und Einladung bündeln.',
+ 'Schneller auf soziale Situationen reagieren.',
+ 'Höfliche Ablehnung und Zusage wiederholen.'
+ ],
+ corePatterns: ['Pwede ka motabang?', 'Bisita mo unya.', 'Sunod na lang.', 'Giinvite tika.']
+ },
+ 'Spiralwiederholung - Soziale Situationen': {
+ learningGoals: [
+ 'Mehrere Sozialthemen gemischt wiederholen.',
+ 'Hilfe, Besuch und Fürsorge verbinden.',
+ 'Reaktionssicherheit im Gespräch erhöhen.'
+ ],
+ corePatterns: ['tabang', 'bisita', 'amping', 'sunod na lang']
+ },
+ 'Konflikte ruhig lösen': {
+ learningGoals: [
+ 'Kleine Spannungen ruhig ansprechen.',
+ 'Missverständnisse sozial abfedern.',
+ 'Bitten, Erklärung und Entschuldigung kombinieren.'
+ ],
+ corePatterns: ['Pwede nato istoryahan?', 'Pasayloa ko.', 'Dili mao ang akong pasabot.', 'Sabta lang ko.']
+ },
+ 'Gefühle im Gespräch vertiefen': {
+ learningGoals: [
+ 'Gefühle sozial differenzierter ausdrücken.',
+ 'Auf Sorge, Enttäuschung oder Freude reagieren.',
+ 'Emotionen mit Beziehungssprache verbinden.'
+ ],
+ corePatterns: ['Naguol ko.', 'Nalipay ko.', 'Nasayod ko sa imong gibati.', 'Salamat sa pagsabot.']
+ },
+ 'Woche 10 - Intensivwiederholung II': {
+ learningGoals: [
+ 'Soziale und emotionale Gesprächsmuster verdichten.',
+ 'Höflich reagieren, helfen und erklären trainieren.',
+ 'Vor dem Wochenabschluss Sicherheit erhöhen.'
+ ],
+ corePatterns: ['Pasayloa ko.', 'Tabangan tika.', 'Nalipay ko.', 'Sunod na lang.']
+ },
+ 'Woche 10 - Checkpoint': {
+ learningGoals: [
+ 'Die Sozialwoche abschließen.',
+ 'Hilfe, Einladung, Ablehnung und Emotionen gemischt prüfen.',
+ 'Stabilität für die Schlussmodule vorbereiten.'
+ ],
+ corePatterns: ['giinvite', 'tabang', 'pasayloa', 'nalipay']
+ },
+ 'Zuhause organisieren': {
+ learningGoals: [
+ 'Haushalt, Ordnung und Aufgaben zuhause besprechen.',
+ 'Kurze Organisationsgespräche führen.',
+ 'Familienalltag mit Haushaltssprache stützen.'
+ ],
+ corePatterns: ['Ato ning limpyohan.', 'Asa nato ibutang?', 'Humanon nato karon.', 'Ayaw kalimot.']
+ },
+ 'Haushalt & Reparaturen': {
+ learningGoals: [
+ 'Wörter zu Haushalt und kleinen Reparaturen lernen.',
+ 'Kaputte Dinge und Lösungen benennen.',
+ 'Haus- und Alltagswortschatz erweitern.'
+ ],
+ corePatterns: ['limpyo', 'guba', 'ayo', 'susi']
+ },
+ 'Vergangenes und Pläne im Alltag': {
+ learningGoals: [
+ 'Vergangenes und Bevorstehendes im Alltag flüssiger ausdrücken.',
+ 'Ni- und Mo-/Mag- in Routinen wiederholen.',
+ 'Kurze Erzähl- und Planungssequenzen verbinden.'
+ ],
+ corePatterns: ['Nihapit ko ganiha.', 'Moadto ko unya.', 'Nahuman na namo.', 'Magluto mi ugma.'],
+ grammarFocus: [
+ {
+ title: 'Alltagspläne verbinden',
+ text: 'In Alltagserzählungen wechseln Vergangenheit und Plan oft direkt nacheinander.',
+ example: 'Nihapit ko ganiha. Moadto ko unya.'
+ }
+ ]
+ },
+ 'Arbeit, Schule und Termine verbinden': {
+ learningGoals: [
+ 'Mehrere Tagesbereiche in einem Gespräch kombinieren.',
+ 'Arbeit, Schule und Termine als Ablauf beschreiben.',
+ 'Längere Alltagsketten sprachlich organisieren.'
+ ],
+ corePatterns: ['Una, moadto ko sa trabaho.', 'Pagkahuman, kuhaon nako ang bata.', 'Aduna mi appointment unya.', 'Busy kaayo ang adlaw.']
+ },
+ 'Woche 11 - Intensivwiederholung I': {
+ learningGoals: [
+ 'Haushalt, Planung und Alltagslogistik zusammen wiederholen.',
+ 'Mehrschrittige Tagesabläufe stabilisieren.',
+ 'Organisation natürlicher versprachlichen.'
+ ],
+ corePatterns: ['Asa nato ibutang?', 'Moadto ko unya.', 'Aduna mi appointment.', 'Busy kaayo ang adlaw.']
+ },
+ 'Spiralwiederholung - Alltagsmodule': {
+ learningGoals: [
+ 'Module 6 bis 10 zusammenziehen.',
+ 'Thematische Übergänge zwischen Familie, Gesundheit, Erledigungen und Sozialem üben.',
+ 'Den aktiven Alltagswortschatz stabilisieren.'
+ ],
+ corePatterns: ['bata', 'doktor', 'ATM', 'tabang']
+ },
+ 'Unterwegs im Familienalltag': {
+ learningGoals: [
+ 'Transport, Termine und Familie in einer Situation verbinden.',
+ 'Unterwegs spontan reagieren.',
+ 'Alltagswege mit Personen und Aufgaben koppeln.'
+ ],
+ corePatterns: ['Kuhaon nako sila.', 'Moadto mi sa lungsod.', 'Asa ta manaog?', 'Dali lang, hapit na ta.']
+ },
+ 'Nachbarschaft & Orte': {
+ learningGoals: [
+ 'Wichtige Orte im Wohnumfeld beschreiben.',
+ 'Nachbarschaftswortschatz mit Bewegung und Besuch verknüpfen.',
+ 'Alltagswege präziser benennen.'
+ ],
+ corePatterns: ['silingan', 'merkado', 'eskwelahan', 'botika']
+ },
+ 'Woche 11 - Intensivwiederholung II': {
+ learningGoals: [
+ 'Die Alltagsmodule vor der Schlussphase verdichten.',
+ 'Schwache Bereiche gezielt wiederholen.',
+ 'Sicherheit im größeren Alltagskontext erhöhen.'
+ ],
+ corePatterns: ['kuhaon nako sila.', 'Ato ning limpyohan.', 'Aduna mi appointment.', 'Pwede ka motabang?']
+ },
+ 'Woche 11 - Checkpoint': {
+ learningGoals: [
+ 'Den Übergang zur großen Integrationswoche absichern.',
+ 'Vernetztes Alltagsverständnis messen.',
+ 'Restliche Lücken vor Woche 12 sichtbar machen.'
+ ],
+ corePatterns: ['limpyo', 'appointment', 'silingan', 'busy']
+ },
+ 'Freies Gespräch - Alltag I': {
+ learningGoals: [
+ 'Längere Alltagsgespräche freier führen.',
+ 'Mehrere Themen in einer Antwort verbinden.',
+ 'Mit wenig Stütze sprachlich handlungsfähig bleiben.'
+ ],
+ corePatterns: ['Maayo ra ang adlaw.', 'Daghan kog buhaton.', 'Moadto pa ko unya.', 'Human na mi sa balay.']
+ },
+ 'Mischtraining - Kernwortschatz': {
+ learningGoals: [
+ 'Den Kernwortschatz quer über alle Module wiederholen.',
+ 'Ähnliche Wörter sicher unterscheiden.',
+ 'Abrufgeschwindigkeit erhöhen.'
+ ],
+ corePatterns: ['palihug', 'tabang', 'tambal', 'resibo']
+ },
+ 'Freies Gespräch - Alltag II': {
+ learningGoals: [
+ 'Freies Sprechen unter leichtem Druck fortsetzen.',
+ 'Alltagserzählung, Fürsorge und Organisation kombinieren.',
+ 'Längere Reaktionen mit mehr Eigenproduktion wagen.'
+ ],
+ corePatterns: ['Gikapoy ko pero okay ra.', 'Naa pa koy lakaw.', 'Magkita mi unya.', 'Nasabtan na nako.']
+ },
+ 'Wiederholung schwieriger Muster': {
+ learningGoals: [
+ 'Fehleranfällige Strukturen gezielt wiederholen.',
+ 'Verwechslungen bei Zeit, Bitte und Reaktion reduzieren.',
+ 'Vor der Schlussprüfung Stabilität erhöhen.'
+ ],
+ corePatterns: ['Niadto ko.', 'Moadto ko.', 'Palihug ka mubalik.', 'Dili lang sa karon.'],
+ grammarFocus: [
+ {
+ title: 'Schwierige Kontraste',
+ text: 'Kurzformen, Zeitmarker und höfliche Reaktionen werden leicht verwechselt und sollten direkt kontrastiert werden.',
+ example: 'Niadto ko ganiha. Moadto ko ugma.'
+ }
+ ]
+ },
+ 'Großes Alltagsreview I': {
+ learningGoals: [
+ 'Den Alltagswortschatz großflächig abrufen.',
+ 'Mehrere Themenblöcke in gemischter Form wiederholen.',
+ 'Auf die Schlussaufgaben vorbereiten.'
+ ],
+ corePatterns: ['doktor', 'merkado', 'tabang', 'eskwela']
+ },
+ 'Fehlerschwerpunkte Alltag': {
+ learningGoals: [
+ 'Typische Fehlercluster gezielt trainieren.',
+ 'Unsichere Antworten in stabilere Muster überführen.',
+ 'Schwächen vor dem Abschluss konzentriert bearbeiten.'
+ ],
+ corePatterns: ['salamat', 'palihug', 'appointment', 'mas maayo']
+ },
+ 'Rollenspiele - echte Situationen': {
+ learningGoals: [
+ 'Mehrere reale Alltagsszenen zusammenhängend durchspielen.',
+ 'Mit spontanen Situationen flexibler umgehen.',
+ 'Alltagsfähigkeit vor dem Abschluss verdichten.'
+ ],
+ corePatterns: ['Pwede ka motabang?', 'Asa ang sakayan?', 'Adto ta sa doktor.', 'Kuhaon nako ang bata.']
+ },
+ 'Abschlusstest - Alltagspfad': {
+ learningGoals: [
+ 'Den Wortschatz des Alltagskurses diagnostisch überprüfen.',
+ 'Ähnliche Bedeutungen und Wortfelder sauber trennen.',
+ 'Die aktive Basis für die Stabilisierungsphase absichern.'
+ ],
+ corePatterns: ['silingan', 'resibo', 'guba', 'eskwelahan']
+ },
+ 'Abschlussprüfung - Alltagsphase': {
+ learningGoals: [
+ 'Den 3-Monats-Alltagspfad gemischt überprüfen.',
+ 'Abruf, Dialogfähigkeit und situative Flexibilität kombinieren.',
+ 'Die Grundlage für Phase 5 sichern.'
+ ],
+ corePatterns: ['Moadto mi sa lungsod.', 'Aduna mi appointment.', 'Pasayloa ko.', 'Ato ning limpyohan.']
+ },
+ 'Kultur, Höflichkeit & Familienleben vertiefen': {
+ learningGoals: [
+ 'Höflichkeit, indirekte Kommunikation und Familienrollen tiefer verstehen.',
+ 'Sprachmuster kulturell besser einordnen.',
+ 'Den Übergang zur Stabilisierungsphase kulturell absichern.'
+ ],
+ corePatterns: ['respeto', 'pakikisama', 'palihug', 'amping']
+ }
+};
+
+export const BISAYA_PHASE4_LESSONS = [
+ { week: 7, day: 1, num: 61, type: 'conversation', title: 'Kinder im Alltag', desc: 'Mit Kindern sprechen und Fürsorge im Alltag ausdrücken', targetMin: 18, targetScore: 80, review: false, cultural: null },
+ { week: 7, day: 1, num: 62, type: 'vocab', title: 'Schule & Betreuung', desc: 'Wortschatz für Schule, Tasche, Unterricht und Betreuung', targetMin: 18, targetScore: 85, review: true, cultural: null },
+ { week: 7, day: 2, num: 63, type: 'grammar', title: 'Fragen an Kinder vereinfachen', desc: 'Kurze, klare Fragen für Kinder und Betreuungssituationen', targetMin: 20, targetScore: 78, review: true, cultural: null },
+ { week: 7, day: 2, num: 64, type: 'conversation', title: 'Hausaufgaben & Routine', desc: 'Über Schule, Lernen und Routinen zuhause sprechen', targetMin: 18, targetScore: 80, review: false, cultural: null },
+ { week: 7, day: 3, num: 65, type: 'review', title: 'Woche 7 - Intensivwiederholung I', desc: 'Kinder, Schule und Familienroutine intensiv wiederholen', targetMin: 28, targetScore: 80, review: false, cultural: null },
+ { week: 7, day: 3, num: 66, type: 'vocab', title: 'Spiralwiederholung - Familie, Kinder & Fürsorge', desc: 'Frühe Kernmuster mit Kinderalltag verbinden', targetMin: 20, targetScore: 85, review: true, cultural: null },
+ { week: 7, day: 4, num: 67, type: 'conversation', title: 'Spielen & Freizeit', desc: 'Mit Kindern über Spiel, Pause und Freizeit sprechen', targetMin: 18, targetScore: 80, review: false, cultural: null },
+ { week: 7, day: 4, num: 68, type: 'vocab', title: 'Spielsachen & Aktivitäten', desc: 'Spiel- und Freizeitwortschatz für Familienalltag', targetMin: 18, targetScore: 85, review: true, cultural: null },
+ { week: 7, day: 5, num: 69, type: 'review', title: 'Woche 7 - Intensivwiederholung II', desc: 'Große Mischwiederholung zur Kinder- und Schulwoche', targetMin: 28, targetScore: 80, review: false, cultural: null },
+ { week: 7, day: 5, num: 70, type: 'vocab', title: 'Woche 7 - Checkpoint', desc: 'Checkpoint zu Familie, Kindern und Schule', targetMin: 16, targetScore: 82, review: true, cultural: null },
+ { week: 8, day: 1, num: 71, type: 'conversation', title: 'Arzt & Termin', desc: 'Arzttermine vereinbaren und Gesundheitsfragen stellen', targetMin: 18, targetScore: 80, review: false, cultural: null },
+ { week: 8, day: 1, num: 72, type: 'vocab', title: 'Apotheke & Medikamente', desc: 'Wichtiger Wortschatz für Apotheke und Medizin', targetMin: 18, targetScore: 85, review: true, cultural: null },
+ { week: 8, day: 2, num: 73, type: 'grammar', title: 'Beschwerden genauer beschreiben', desc: 'Schmerz, Verlauf und Stärke konkreter ausdrücken', targetMin: 20, targetScore: 78, review: true, cultural: null },
+ { week: 8, day: 2, num: 74, type: 'conversation', title: 'Notfälle & Hilfe', desc: 'In einfachen Notfällen Hilfe holen und reagieren', targetMin: 18, targetScore: 80, review: false, cultural: null },
+ { week: 8, day: 3, num: 75, type: 'review', title: 'Woche 8 - Intensivwiederholung I', desc: 'Gesundheit, Arzt und Hilfe intensiv wiederholen', targetMin: 28, targetScore: 80, review: false, cultural: null },
+ { week: 8, day: 3, num: 76, type: 'vocab', title: 'Spiralwiederholung - Gesundheit', desc: 'Frühere Fürsorge mit Gesundheit und Pflege verbinden', targetMin: 20, targetScore: 85, review: true, cultural: null },
+ { week: 8, day: 4, num: 77, type: 'conversation', title: 'Essen, Ruhe & Genesung', desc: 'Pflege, Ruhe und Essen bei Krankheit besprechen', targetMin: 18, targetScore: 80, review: false, cultural: null },
+ { week: 8, day: 4, num: 78, type: 'vocab', title: 'Körper, Symptome & Pflege', desc: 'Körper- und Pflegewortschatz ausbauen', targetMin: 18, targetScore: 85, review: true, cultural: null },
+ { week: 8, day: 5, num: 79, type: 'review', title: 'Woche 8 - Intensivwiederholung II', desc: 'Große Mischwiederholung zu Gesundheit und Hilfe', targetMin: 28, targetScore: 80, review: false, cultural: null },
+ { week: 8, day: 5, num: 80, type: 'vocab', title: 'Woche 8 - Checkpoint', desc: 'Checkpoint zu Arzt, Apotheke und Pflege', targetMin: 16, targetScore: 82, review: true, cultural: null },
+ { week: 9, day: 1, num: 81, type: 'conversation', title: 'Einkaufen vertiefen', desc: 'Komplexere Einkaufs- und Auswahlgespräche führen', targetMin: 18, targetScore: 80, review: false, cultural: null },
+ { week: 9, day: 1, num: 82, type: 'vocab', title: 'Markt & Mengen', desc: 'Wortschatz für Markt, Mengen und Auswahl', targetMin: 18, targetScore: 85, review: true, cultural: null },
+ { week: 9, day: 2, num: 83, type: 'grammar', title: 'Wünsche, Bedarf und Bitte', desc: 'Wunsch, Notwendigkeit und höfliche Bitte unterscheiden', targetMin: 20, targetScore: 78, review: true, cultural: null },
+ { week: 9, day: 2, num: 84, type: 'conversation', title: 'Behördengänge & Formulare', desc: 'Nach Formularen, Schaltern und Terminen fragen', targetMin: 18, targetScore: 80, review: false, cultural: null },
+ { week: 9, day: 3, num: 85, type: 'review', title: 'Woche 9 - Intensivwiederholung I', desc: 'Einkaufen und Erledigungen intensiv wiederholen', targetMin: 28, targetScore: 80, review: false, cultural: null },
+ { week: 9, day: 3, num: 86, type: 'vocab', title: 'Spiralwiederholung - Preise & Erledigungen', desc: 'Preis- und Erledigungsmuster spiralig festigen', targetMin: 20, targetScore: 85, review: true, cultural: null },
+ { week: 9, day: 4, num: 87, type: 'conversation', title: 'Bank, Geld & Bezahlen', desc: 'Über Bezahlen, Wechselgeld und Geldsituationen sprechen', targetMin: 18, targetScore: 80, review: false, cultural: null },
+ { week: 9, day: 4, num: 88, type: 'vocab', title: 'Dokumente & Termine', desc: 'Papierkram, Termine und Dokumente benennen', targetMin: 18, targetScore: 85, review: true, cultural: null },
+ { week: 9, day: 5, num: 89, type: 'review', title: 'Woche 9 - Intensivwiederholung II', desc: 'Große Mischwiederholung zu Geld, Formularen und Terminen', targetMin: 28, targetScore: 80, review: false, cultural: null },
+ { week: 9, day: 5, num: 90, type: 'vocab', title: 'Woche 9 - Checkpoint', desc: 'Checkpoint zu Einkauf, Bezahlen und Erledigungen', targetMin: 16, targetScore: 82, review: true, cultural: null },
+ { week: 10, day: 1, num: 91, type: 'conversation', title: 'Nachbarschaft & Besuche', desc: 'Mit Nachbarn sprechen und Besuche im Umfeld einordnen', targetMin: 18, targetScore: 80, review: false, cultural: null },
+ { week: 10, day: 1, num: 92, type: 'vocab', title: 'Hilfe & Unterstützung', desc: 'Wortschatz für Hilfe, Unterstützung und soziale Nähe', targetMin: 18, targetScore: 85, review: true, cultural: null },
+ { week: 10, day: 2, num: 93, type: 'grammar', title: 'Höflich reagieren und ablehnen', desc: 'Einladungen, Hilfe und Vorschläge fein abstufen', targetMin: 20, targetScore: 78, review: true, cultural: null },
+ { week: 10, day: 2, num: 94, type: 'conversation', title: 'Feste & Einladungen', desc: 'Über Feste, Besuche und Einladungen sprechen', targetMin: 18, targetScore: 80, review: false, cultural: null },
+ { week: 10, day: 3, num: 95, type: 'review', title: 'Woche 10 - Intensivwiederholung I', desc: 'Soziale Situationen und Hilfe intensiv wiederholen', targetMin: 28, targetScore: 80, review: false, cultural: null },
+ { week: 10, day: 3, num: 96, type: 'vocab', title: 'Spiralwiederholung - Soziale Situationen', desc: 'Besuche, Hilfe und Reaktionen blockübergreifend festigen', targetMin: 20, targetScore: 85, review: true, cultural: null },
+ { week: 10, day: 4, num: 97, type: 'conversation', title: 'Konflikte ruhig lösen', desc: 'Missverständnisse ansprechen und Spannungen abfedern', targetMin: 18, targetScore: 80, review: false, cultural: null },
+ { week: 10, day: 4, num: 98, type: 'vocab', title: 'Gefühle im Gespräch vertiefen', desc: 'Emotionen in sozialen Gesprächen genauer ausdrücken', targetMin: 18, targetScore: 85, review: true, cultural: null },
+ { week: 10, day: 5, num: 99, type: 'review', title: 'Woche 10 - Intensivwiederholung II', desc: 'Große Mischwiederholung zu sozialen Situationen und Emotionen', targetMin: 28, targetScore: 80, review: false, cultural: null },
+ { week: 10, day: 5, num: 100, type: 'vocab', title: 'Woche 10 - Checkpoint', desc: 'Checkpoint zu Hilfe, Besuchen und Gefühlen', targetMin: 16, targetScore: 82, review: true, cultural: null },
+ { week: 11, day: 1, num: 101, type: 'conversation', title: 'Zuhause organisieren', desc: 'Ordnung, Haushalt und kleine Aufgaben zuhause besprechen', targetMin: 18, targetScore: 80, review: false, cultural: null },
+ { week: 11, day: 1, num: 102, type: 'vocab', title: 'Haushalt & Reparaturen', desc: 'Wörter für Haushalt, Ordnung und kleine Reparaturen', targetMin: 18, targetScore: 85, review: true, cultural: null },
+ { week: 11, day: 2, num: 103, type: 'grammar', title: 'Vergangenes und Pläne im Alltag', desc: 'Vergangenheit und kommende Schritte in Routinen verbinden', targetMin: 20, targetScore: 78, review: true, cultural: null },
+ { week: 11, day: 2, num: 104, type: 'conversation', title: 'Arbeit, Schule und Termine verbinden', desc: 'Mehrere Alltagsbereiche in einer Erzählung kombinieren', targetMin: 18, targetScore: 80, review: false, cultural: null },
+ { week: 11, day: 3, num: 105, type: 'review', title: 'Woche 11 - Intensivwiederholung I', desc: 'Haushalt, Planung und Alltagslogistik intensiv wiederholen', targetMin: 28, targetScore: 80, review: false, cultural: null },
+ { week: 11, day: 3, num: 106, type: 'vocab', title: 'Spiralwiederholung - Alltagsmodule', desc: 'Module 6 bis 10 zusammenziehen und wiederholen', targetMin: 20, targetScore: 85, review: true, cultural: null },
+ { week: 11, day: 4, num: 107, type: 'conversation', title: 'Unterwegs im Familienalltag', desc: 'Familie, Wege und Termine im Alltag verbinden', targetMin: 18, targetScore: 80, review: false, cultural: null },
+ { week: 11, day: 4, num: 108, type: 'vocab', title: 'Nachbarschaft & Orte', desc: 'Wichtige Orte und Nachbarschaftswortschatz vertiefen', targetMin: 18, targetScore: 85, review: true, cultural: null },
+ { week: 11, day: 5, num: 109, type: 'review', title: 'Woche 11 - Intensivwiederholung II', desc: 'Große Mischwiederholung zu Organisation und Familienlogistik', targetMin: 28, targetScore: 80, review: false, cultural: null },
+ { week: 11, day: 5, num: 110, type: 'vocab', title: 'Woche 11 - Checkpoint', desc: 'Checkpoint zu Organisation, Haushalt und Alltagslogistik', targetMin: 16, targetScore: 82, review: true, cultural: null },
+ { week: 12, day: 1, num: 111, type: 'conversation', title: 'Freies Gespräch - Alltag I', desc: 'Längere Alltagsgespräche freier und stabiler führen', targetMin: 22, targetScore: 78, review: false, cultural: null },
+ { week: 12, day: 1, num: 112, type: 'vocab', title: 'Mischtraining - Kernwortschatz', desc: 'Kernwortschatz aus allen Modulen gemischt trainieren', targetMin: 20, targetScore: 85, review: true, cultural: null },
+ { week: 12, day: 2, num: 113, type: 'conversation', title: 'Freies Gespräch - Alltag II', desc: 'Freies Sprechen mit mehr eigener Produktion vertiefen', targetMin: 22, targetScore: 78, review: false, cultural: null },
+ { week: 12, day: 2, num: 114, type: 'grammar', title: 'Wiederholung schwieriger Muster', desc: 'Zeit, Bitte und Reaktion kontrastiv wiederholen', targetMin: 20, targetScore: 78, review: true, cultural: null },
+ { week: 12, day: 3, num: 115, type: 'review', title: 'Großes Alltagsreview I', desc: 'Große Wiederholung aller Alltagsmodule', targetMin: 30, targetScore: 82, review: false, cultural: null },
+ { week: 12, day: 3, num: 116, type: 'vocab', title: 'Fehlerschwerpunkte Alltag', desc: 'Typische Fehlercluster gezielt wiederholen', targetMin: 20, targetScore: 85, review: true, cultural: null },
+ { week: 12, day: 4, num: 117, type: 'conversation', title: 'Rollenspiele - echte Situationen', desc: 'Mehrere reale Alltagsszenen in Rollenspielen üben', targetMin: 24, targetScore: 78, review: false, cultural: null },
+ { week: 12, day: 4, num: 118, type: 'vocab', title: 'Abschlusstest - Alltagspfad', desc: 'Finaler Wortschatztest über den 3-Monats-Alltagspfad', targetMin: 20, targetScore: 82, review: true, cultural: null },
+ { week: 12, day: 5, num: 119, type: 'review', title: 'Abschlussprüfung - Alltagsphase', desc: 'Große Abschlussprüfung zum 3-Monats-Alltagspfad', targetMin: 30, targetScore: 82, review: false, cultural: null },
+ { week: 12, day: 5, num: 120, type: 'culture', title: 'Kultur, Höflichkeit & Familienleben vertiefen', desc: 'Kulturelle Muster für Sprache und Familienalltag weiter vertiefen', targetMin: 16, targetScore: 0, review: false, cultural: 'Die Alltagsphase endet mit einem stärkeren kulturellen Verständnis für Höflichkeit, Familie und indirekte Kommunikation.' }
+];
diff --git a/backend/scripts/bisaya-course-phase5-extension.js b/backend/scripts/bisaya-course-phase5-extension.js
new file mode 100644
index 0000000..3af222d
--- /dev/null
+++ b/backend/scripts/bisaya-course-phase5-extension.js
@@ -0,0 +1,274 @@
+export const BISAYA_PHASE5_DIDACTICS = {
+ 'Dialogtag - Familie & Planung': {
+ learningGoals: [
+ 'Mehrere Familienthemen in längeren Dialogen verbinden.',
+ 'Planung, Fürsorge und Organisation ohne enge Stütze kombinieren.',
+ 'Sprechfluss über mehrere Züge stabil halten.'
+ ],
+ corePatterns: ['Unsa atong plano karon?', 'Kuhaon nato ang bata unya.', 'Nikaon na ba silang tanan?', 'Pagkahuman, mouli ta.']
+ },
+ 'Mischtraining - Familie, Gesundheit, Alltag': {
+ learningGoals: [
+ 'Kernwortschatz aus mehreren Modulen gemischt abrufen.',
+ 'Zwischen ähnlichen Alltagssituationen schneller unterscheiden.',
+ 'Stabile Wiedererkennung in gemischten Aufgaben sichern.'
+ ],
+ corePatterns: ['tambal', 'eskwela', 'merkado', 'silingan']
+ },
+ 'Fehlertraining - häufige Verwechslungen I': {
+ learningGoals: [
+ 'Typische Verwechslungen bei Zeit, Bitte und Reaktion reduzieren.',
+ 'Kontrastpaare aktiv gegeneinander trainieren.',
+ 'Unsichere Routinemuster gezielt festigen.'
+ ],
+ corePatterns: ['Niadto ko.', 'Moadto ko.', 'Palihug.', 'Pasayloa ko.'],
+ grammarFocus: [
+ {
+ title: 'Kontrasttraining',
+ text: 'Ähnliche Alltagsformen werden in der Stabilisierung bewusst direkt gegenübergestellt.',
+ example: 'Niadto ko ganiha. Moadto ko ugma.'
+ }
+ ]
+ },
+ 'Rollenspiel - Schule, Arzt, Besuch': {
+ learningGoals: [
+ 'Mehrere reale Situationen in längeren Rollenspielen verbinden.',
+ 'Schnell zwischen Schule, Arzt und Familienbesuch wechseln.',
+ 'Sprachliche Flexibilität im Alltag ausbauen.'
+ ],
+ corePatterns: ['Andam na ka sa eskwela?', 'Adto ta sa doktor.', 'Sulod lang.', 'Maghulat ta gamay.']
+ },
+ 'Stabilisierungsblock 1 - Intensiv I': {
+ learningGoals: [
+ 'Zentrale Schnellstart- und Alltagsmuster in einem engen Block wiederholen.',
+ 'Abrufgeschwindigkeit und Reaktionssicherheit erhöhen.',
+ 'Schwache Felder sichtbar machen.'
+ ],
+ corePatterns: ['Kumusta ka?', 'Nikaon na ka?', 'Asa ang sakayan?', 'Pwede ka motabang?']
+ },
+ 'Fehlerschwerpunkte - Familie & Fürsorge': {
+ learningGoals: [
+ 'Fehler bei Familien- und Fürsorgemustern gezielt abbauen.',
+ 'Nahe Bedeutungen in stabilere Antworten überführen.',
+ 'Häufige Stolperstellen transparent machen.'
+ ],
+ corePatterns: ['Palangga taka.', 'Mingaw ko nimo.', 'Magpahuway sa.', 'Andam na ka?']
+ },
+ 'Freies Erzählen - Mein Alltag': {
+ learningGoals: [
+ 'Den eigenen Alltag zusammenhängend erzählen.',
+ 'Mehrere Tagesbereiche in einer freien Antwort verbinden.',
+ 'Mit weniger Stütze längere Produktion halten.'
+ ],
+ corePatterns: ['Sa buntag...', 'Pagkahuman...', 'Sa hapon...', 'Sa gabii...']
+ },
+ 'Mischtraining - Reale Situationen I': {
+ learningGoals: [
+ 'Reale Alltagssituationen ohne Themenblockgrenzen mischen.',
+ 'Abruf in wechselnden Kontexten stabilisieren.',
+ 'Transfer über Modulgrenzen hinweg trainieren.'
+ ],
+ corePatterns: ['appointment', 'tabang', 'resibo', 'amping']
+ },
+ 'Stabilisierungsblock 1 - Intensiv II': {
+ learningGoals: [
+ 'Fehlercluster und Mischtraining verdichten.',
+ 'Aufgaben schneller und sicherer lösen.',
+ 'Vor dem Checkpoint Stabilität erhöhen.'
+ ],
+ corePatterns: ['Pasayloa ko.', 'Dili lang sa karon.', 'Mubayad ko.', 'Mas maayo na ka?']
+ },
+ 'Stabilisierungsblock 1 - Checkpoint': {
+ learningGoals: [
+ 'Den ersten Stabilisierungsschritt diagnostisch abschließen.',
+ 'Langzeitabruf, Transfer und Fehlerrisiken messen.',
+ 'Die zweite Stabilisierungsschleife vorbereiten.'
+ ],
+ corePatterns: ['appointment', 'palihug', 'silingan', 'eskwela']
+ },
+ 'Dialogtag - Organisation & Wege': {
+ learningGoals: [
+ 'Organisation, Termine und Wege in längeren Dialogen verbinden.',
+ 'Planung über mehrere Schritte sprachlich strukturieren.',
+ 'Reaktionsfähigkeit in logistischer Alltagssprache stärken.'
+ ],
+ corePatterns: ['Una moadto ko didto.', 'Pagkahuman, mubalik ko diri.', 'Asa ta manaog?', 'Unsa ang sunod?']
+ },
+ 'Mischtraining - Gesundheit, Schule, Erledigungen': {
+ learningGoals: [
+ 'Drei große Themenblöcke gemischt wiederholen.',
+ 'Kontextwechsel in Aufgaben trainieren.',
+ 'Langsamere Vergessenskurven abfangen.'
+ ],
+ corePatterns: ['doktor', 'assignment', 'resibo', 'botika']
+ },
+ 'Fehlertraining - häufige Verwechslungen II': {
+ learningGoals: [
+ 'Zweite Runde der häufigsten Verwechslungen gezielt trainieren.',
+ 'Schwierige Antworten über Kontrast und Wiederholung stabilisieren.',
+ 'Präzisere Produktion fördern.'
+ ],
+ corePatterns: ['Gusto ko.', 'Kinahanglan nako.', 'Sunod na lang.', 'Dili mao ang akong pasabot.']
+ },
+ 'Rollenspiel - Konflikt und Hilfe': {
+ learningGoals: [
+ 'Konflikt, Hilfe und Erklärung in Rollenspielen zusammenführen.',
+ 'Höflichkeit unter Druck bewahren.',
+ 'Soziale Reparaturmuster festigen.'
+ ],
+ corePatterns: ['Pwede nato istoryahan?', 'Tabangan tika.', 'Pasayloa ko.', 'Sabta lang ko.']
+ },
+ 'Stabilisierungsblock 2 - Intensiv I': {
+ learningGoals: [
+ 'Schwierige Alltagsblöcke nochmals verdichtet wiederholen.',
+ 'Langzeitabruf gezielt trainieren.',
+ 'Vor dem Schlussabschnitt Fehlerquellen reduzieren.'
+ ],
+ corePatterns: ['Kinahanglan nako ni.', 'Adto ta sa doktor.', 'Giinvite tika.', 'Ato ning limpyohan.']
+ },
+ 'Fehlerschwerpunkte - Termine, Zeit, Reaktion': {
+ learningGoals: [
+ 'Termine, Zeitbezüge und Reaktionen enger kontrastieren.',
+ 'Fehleranfällige Übergänge abbauen.',
+ 'Bessere Sicherheit in Planungsdialogen aufbauen.'
+ ],
+ corePatterns: ['ugma', 'ganiha', 'unya', 'sunod na lang']
+ },
+ 'Freies Erzählen - Familie, Sorgen, Pläne': {
+ learningGoals: [
+ 'Familiensituation, Sorgen und Zukunftsplan zusammenhängend erzählen.',
+ 'Emotion, Organisation und Hilfe verbinden.',
+ 'Freie Produktion vertiefen.'
+ ],
+ corePatterns: ['Naguol ko gamay.', 'Pero okay ra.', 'Aduna koy plano unya.', 'Tabangan mi nila.']
+ },
+ 'Mischtraining - Reale Situationen II': {
+ learningGoals: [
+ 'Eine zweite große Mischrunde mit neuen Kombinationen durchführen.',
+ 'Transfer über die ganze Alltagsphase absichern.',
+ 'Robustheit bei ungeordnetem Themenwechsel erhöhen.'
+ ],
+ corePatterns: ['doktor', 'ATM', 'duwa', 'linya']
+ },
+ 'Stabilisierungsblock 2 - Intensiv II': {
+ learningGoals: [
+ 'Die zweite Stabilisierungsschleife verdichten.',
+ 'Restliche Schwächen noch einmal fokussieren.',
+ 'Vor dem Gesamtabschluss maximale Sicherheit aufbauen.'
+ ],
+ corePatterns: ['Palihug ka mubalik.', 'Moadto ko unya.', 'Salamat sa tabang.', 'Asa ang porma?']
+ },
+ 'Stabilisierungsblock 2 - Checkpoint': {
+ learningGoals: [
+ 'Die zweite Stabilisierungsrunde abschließen.',
+ 'Diagnostisch prüfen, welche Langzeitmuster noch wackeln.',
+ 'Den Schlussblock vorbereiten.'
+ ],
+ corePatterns: ['porma', 'sukli', 'pasayloa', 'amping']
+ },
+ 'Großes Mischreview I': {
+ learningGoals: [
+ 'Den gesamten bisherigen Kurs breit wiederholen.',
+ 'Thematische Grenzen in der Wiederholung auflösen.',
+ 'Schnelles, flexibles Abrufen vorbereiten.'
+ ],
+ corePatterns: ['bata', 'doktor', 'merkado', 'tabang']
+ },
+ 'Großes Mischreview II': {
+ learningGoals: [
+ 'Eine zweite große Mischrunde mit höherer Dichte trainieren.',
+ 'Ähnliche Muster unter Zeitdruck stabilisieren.',
+ 'Abschlussreife aufbauen.'
+ ],
+ corePatterns: ['appointment', 'resibo', 'palihug', 'silingan']
+ },
+ 'Fehlertraining - letzte Schwächen': {
+ learningGoals: [
+ 'Die letzten wackeligen Muster gezielt bearbeiten.',
+ 'Unsichere Antworten in belastbare Routinen überführen.',
+ 'Vor dem Abschluss die Fehlerquote senken.'
+ ],
+ corePatterns: ['Niadto ko.', 'Moadto ko.', 'Dili lang sa karon.', 'Mas maayo na ka?']
+ },
+ 'Freies Sprechen - Alltag ohne Stütze': {
+ learningGoals: [
+ 'Alltagsgespräche mit minimaler Hilfe frei führen.',
+ 'Eigenständige Produktion gegenüber Wiedererkennung priorisieren.',
+ 'Selbstsicherheit in längeren Antworten ausbauen.'
+ ],
+ corePatterns: ['Sa tinuod...', 'Kasagaran...', 'Usahay...', 'Apan...']
+ },
+ 'Langzeitreview - Intensiv I': {
+ learningGoals: [
+ 'Früh gelernte Inhalte gezielt gegen Vergessen absichern.',
+ 'Langfristige Wiederaufnahme trainieren.',
+ 'Kernmuster mit hoher Alltagsrelevanz stabil halten.'
+ ],
+ corePatterns: ['Kumusta ka?', 'Salamat.', 'Nikaon na ka?', 'Tagpila ni?']
+ },
+ 'Langzeitreview - Intensiv II': {
+ learningGoals: [
+ 'Zweite Langzeitwiederholung mit Schwerpunkt auf Transfer.',
+ 'Frühe und späte Inhalte gemeinsam verankern.',
+ 'Vergessene Muster reaktivieren.'
+ ],
+ corePatterns: ['doktor', 'eskwela', 'tabang', 'resibo']
+ },
+ 'Abschlusstest - Stabilisierung': {
+ learningGoals: [
+ 'Den gesamten Stabilisierungspfad diagnostisch überprüfen.',
+ 'Wortschatz, Muster und Kontrastpaare testen.',
+ 'Die Abschlussprüfung vorbereiten.'
+ ],
+ corePatterns: ['appointment', 'amping', 'sukli', 'palangga']
+ },
+ 'Abschlussprüfung - Gesamtpfad': {
+ learningGoals: [
+ 'Den gesamten Bisaya-Pfad bis zur Stabilisierungsphase abschließen.',
+ 'Abruf, freie Produktion und Alltagstransfer kombinieren.',
+ 'Das Fundament für späteres Langzeitlernen sichern.'
+ ],
+ corePatterns: ['Moadto mi sa lungsod.', 'Aduna mi appointment.', 'Pasayloa ko.', 'Tabangan tika.']
+ },
+ 'Kultur, Familie & Sprache langfristig': {
+ learningGoals: [
+ 'Sprachgebrauch, Höflichkeit und Familienrollen langfristig reflektieren.',
+ 'Kulturelle Muster mit stabilem Sprachwissen verbinden.',
+ 'Den Übergang in offenes Weiterlernen begleiten.'
+ ],
+ corePatterns: ['respeto', 'pakikisama', 'amping', 'palihug']
+ }
+};
+
+export const BISAYA_PHASE5_LESSONS = [
+ { week: 13, day: 1, num: 121, type: 'conversation', title: 'Dialogtag - Familie & Planung', desc: 'Längere Dialoge zu Familie, Planung und Fürsorge', targetMin: 22, targetScore: 78, review: false, cultural: null },
+ { week: 13, day: 1, num: 122, type: 'vocab', title: 'Mischtraining - Familie, Gesundheit, Alltag', desc: 'Gemischter Wortschatz aus zentralen Alltagsfeldern', targetMin: 20, targetScore: 85, review: true, cultural: null },
+ { week: 13, day: 2, num: 123, type: 'grammar', title: 'Fehlertraining - häufige Verwechslungen I', desc: 'Kontrasttraining für typische Zeit- und Reaktionsfehler', targetMin: 20, targetScore: 78, review: true, cultural: null },
+ { week: 13, day: 2, num: 124, type: 'conversation', title: 'Rollenspiel - Schule, Arzt, Besuch', desc: 'Mehrere reale Situationen in Rollenspielen verbinden', targetMin: 22, targetScore: 78, review: false, cultural: null },
+ { week: 13, day: 3, num: 125, type: 'review', title: 'Stabilisierungsblock 1 - Intensiv I', desc: 'Erste große Stabilisierungsschleife', targetMin: 30, targetScore: 82, review: false, cultural: null },
+ { week: 13, day: 3, num: 126, type: 'vocab', title: 'Fehlerschwerpunkte - Familie & Fürsorge', desc: 'Gezieltes Fehlertraining zu Nähe und Fürsorge', targetMin: 20, targetScore: 85, review: true, cultural: null },
+ { week: 13, day: 4, num: 127, type: 'conversation', title: 'Freies Erzählen - Mein Alltag', desc: 'Den eigenen Alltag freier und länger erzählen', 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 Alltagssituationen', 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 im ersten Stabilisierungsschritt', targetMin: 30, targetScore: 82, review: false, cultural: null },
+ { week: 13, day: 5, num: 130, type: 'vocab', title: 'Stabilisierungsblock 1 - Checkpoint', desc: 'Checkpoint zum ersten Stabilisierungsschritt', targetMin: 18, targetScore: 84, review: true, cultural: null },
+ { week: 14, day: 1, num: 131, type: 'conversation', title: 'Dialogtag - Organisation & Wege', desc: 'Planung, Wege und Termine in längeren Dialogen verbinden', targetMin: 22, targetScore: 78, review: false, cultural: null },
+ { week: 14, day: 1, num: 132, type: 'vocab', title: 'Mischtraining - Gesundheit, Schule, Erledigungen', desc: 'Gemischter Wortschatz aus drei großen Themenblöcken', targetMin: 20, targetScore: 85, review: true, cultural: null },
+ { week: 14, day: 2, num: 133, type: 'grammar', title: 'Fehlertraining - häufige Verwechslungen II', desc: 'Zweite Runde Kontrasttraining für schwierige Alltagsmuster', targetMin: 20, targetScore: 78, review: true, cultural: null },
+ { week: 14, day: 2, num: 134, type: 'conversation', title: 'Rollenspiel - Konflikt und Hilfe', desc: 'Konflikt, Hilfe und Erklärung in Rollenspielen zusammenführen', 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 im zweiten Stabilisierungsschritt', targetMin: 30, targetScore: 82, review: false, cultural: null },
+ { week: 14, day: 3, num: 136, type: 'vocab', title: 'Fehlerschwerpunkte - Termine, Zeit, Reaktion', desc: 'Gezieltes Fehlertraining zu Zeit, Reaktion und Terminmustern', targetMin: 20, targetScore: 85, review: true, cultural: null },
+ { week: 14, day: 4, num: 137, type: 'conversation', title: 'Freies Erzählen - Familie, Sorgen, Pläne', desc: 'Familie, Sorgen und Zukunftspläne frei verbinden', 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 Alltagssituationen', 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 im zweiten Stabilisierungsschritt', targetMin: 30, targetScore: 82, review: false, cultural: null },
+ { week: 14, day: 5, num: 140, type: 'vocab', title: 'Stabilisierungsblock 2 - Checkpoint', desc: 'Checkpoint zum zweiten Stabilisierungsschritt', targetMin: 18, targetScore: 84, review: true, cultural: null },
+ { week: 15, day: 1, num: 141, type: 'review', title: 'Großes Mischreview I', desc: 'Große blockübergreifende 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: 'Letzte typische Fehler vor dem Gesamtabschluss bearbeiten', targetMin: 20, targetScore: 78, review: true, cultural: null },
+ { week: 15, day: 2, num: 144, type: 'conversation', title: 'Freies Sprechen - Alltag ohne Stütze', desc: 'Alltagsgespräche mit minimaler Hilfe frei führen', targetMin: 24, targetScore: 78, review: false, cultural: null },
+ { week: 15, day: 3, num: 145, type: 'review', title: 'Langzeitreview - Intensiv 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 - Intensiv II', desc: 'Frühe und späte Inhalte gemeinsam reaktivieren', targetMin: 20, targetScore: 85, review: true, cultural: null },
+ { week: 15, day: 4, num: 147, type: 'conversation', title: 'Rollenspiele - echte Situationen', desc: 'Mehrere reale Alltagsszenen in längeren Rollenspielen üben', targetMin: 24, targetScore: 78, review: false, cultural: null },
+ { week: 15, day: 4, num: 148, type: 'vocab', title: 'Abschlusstest - Stabilisierung', desc: 'Finaler Test über den Stabilisierungspfad', 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 Bisaya-Pfad', targetMin: 32, targetScore: 84, review: false, cultural: null },
+ { week: 15, day: 5, num: 150, type: 'culture', title: 'Kultur, Familie & Sprache langfristig', desc: 'Kultur, Familie und Höflichkeit langfristig einordnen', targetMin: 16, targetScore: 0, review: false, cultural: 'Die Stabilisierung endet mit einem bewussten Blick auf Sprache, Familie und kulturelle Langzeitmuster.' }
+];
diff --git a/backend/scripts/create-bisaya-course-content.js b/backend/scripts/create-bisaya-course-content.js
index b4b8294..0cdab3e 100644
--- a/backend/scripts/create-bisaya-course-content.js
+++ b/backend/scripts/create-bisaya-course-content.js
@@ -13,6 +13,9 @@ import VocabCourseLesson from '../models/community/vocab_course_lesson.js';
import VocabGrammarExercise from '../models/community/vocab_grammar_exercise.js';
import VocabCourse from '../models/community/vocab_course.js';
import User from '../models/community/user.js';
+import { BISAYA_PHASE3_DIDACTICS } from './bisaya-course-phase3-extension.js';
+import { BISAYA_PHASE4_DIDACTICS } from './bisaya-course-phase4-extension.js';
+import { BISAYA_PHASE5_DIDACTICS } from './bisaya-course-phase5-extension.js';
function withTypeName(exerciseTypeName, exercise) {
return {
@@ -21,6 +24,356 @@ function withTypeName(exerciseTypeName, exercise) {
};
}
+const GENERATED_BISAYA_DIDACTICS = {
+ ...BISAYA_PHASE3_DIDACTICS,
+ ...BISAYA_PHASE4_DIDACTICS,
+ ...BISAYA_PHASE5_DIDACTICS
+};
+
+const GENERIC_DISTRACTOR_PATTERNS = Array.from(new Set(
+ Object.values(GENERATED_BISAYA_DIDACTICS)
+ .flatMap((entry) => Array.isArray(entry?.corePatterns) ? entry.corePatterns : [])
+ .map((pattern) => String(pattern || '').trim())
+ .filter(Boolean)
+)).slice(0, 200);
+
+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 = GENERATED_BISAYA_DIDACTICS[lesson.title] || {};
+ const learningGoals = Array.isArray(lesson.learningGoals) ? lesson.learningGoals : (staticDidactics.learningGoals || []);
+ const corePatterns = Array.isArray(lesson.corePatterns) ? lesson.corePatterns : (staticDidactics.corePatterns || []);
+ const grammarFocus = Array.isArray(lesson.grammarFocus) ? lesson.grammarFocus : (staticDidactics.grammarFocus || []);
+ const speakingPrompts = Array.isArray(lesson.speakingPrompts) ? lesson.speakingPrompts : (staticDidactics.speakingPrompts || []);
+ const practicalTasks = Array.isArray(lesson.practicalTasks) ? lesson.practicalTasks : (staticDidactics.practicalTasks || []);
+
+ return {
+ learningGoals,
+ corePatterns: corePatterns.map((entry) => normalizeText(entry)).filter(Boolean),
+ grammarFocus,
+ speakingPrompts,
+ 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 Situation aus der Lektion "${lesson.title}".`;
+}
+
+function getChoiceQuestion(lesson, didactics) {
+ const scenarioPrompt = getScenarioPrompt(lesson, didactics);
+
+ switch (lesson.lessonType) {
+ case 'conversation':
+ return `${scenarioPrompt} Welche Bisaya-Formulierung passt am besten?`;
+ case 'grammar':
+ return `Welche Formulierung passt als kurze Alltagsstruktur zu "${lesson.title}"?`;
+ case 'review':
+ return `Welche Formulierung solltest du aus "${lesson.title}" sicher wiedererkennen?`;
+ case 'culture':
+ return `Welcher Ausdruck gehört am ehesten zum kulturellen Schwerpunkt "${lesson.title}"?`;
+ case 'vocab':
+ default:
+ return `Welcher Ausdruck 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 seed = simpleHash(`${lesson.title}:${pattern}:${variant}`);
+ const options = rotateArray([pattern, ...distractors], seed % 4);
+ const correctAnswer = options.indexOf(pattern);
+
+ return {
+ exerciseTypeId: 2,
+ title: `${lesson.title}: Passende Formulierung wählen`,
+ instruction: 'Wähle die natürlichste Formulierung für die Situation oder den Schwerpunkt der Lektion.',
+ questionData: {
+ type: 'multiple_choice',
+ question: getChoiceQuestion(lesson, didactics),
+ options
+ },
+ answerData: {
+ type: 'multiple_choice',
+ correctAnswer
+ },
+ explanation: `"${pattern}" ist ein zentrales 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 >= 4);
+
+ 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 Bisaya-Ausdruck.',
+ questionData: {
+ type: 'gap_fill',
+ text: tokens.join(' '),
+ gaps: 1
+ },
+ answerData: {
+ type: 'gap_fill',
+ answers: [gapTarget.token.replace(/[.,?!]/g, '')]
+ },
+ explanation: `Das Kernmuster lautet: "${pattern}".`
+ };
+}
+
+function buildContextGapExercise(lesson, didactics, pattern) {
+ const gapExercise = buildGapExercise(lesson.title, pattern);
+ if (!gapExercise) return null;
+
+ return {
+ ...gapExercise,
+ title: `${lesson.title}: Kernmuster ergänzen`,
+ instruction: `Vervollständige die Formulierung passend zur Situation: ${getScenarioPrompt(lesson, didactics)}`
+ };
+}
+
+function buildSentenceExercise(lessonTitle, pattern) {
+ const tokens = normalizeText(pattern)
+ .replace(/[?!]/g, '')
+ .split(' ')
+ .filter(Boolean);
+
+ if (tokens.length < 2) return null;
+
+ return {
+ exerciseTypeId: 3,
+ title: `${lessonTitle}: Satzmuster bauen`,
+ instruction: 'Ordne die Wörter zu einem korrekten Bisaya-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 buildTaskSentenceExercise(lesson, didactics, pattern) {
+ const sentenceExercise = buildSentenceExercise(lesson.title, pattern);
+ if (!sentenceExercise) return null;
+
+ const practicalTask = Array.isArray(didactics.practicalTasks) ? didactics.practicalTasks[0] : null;
+
+ return {
+ ...sentenceExercise,
+ title: `${lesson.title}: Satz aus dem Alltag bauen`,
+ instruction: practicalTask?.text
+ ? `Baue die passende Formulierung für diese Aufgabe: ${practicalTask.text}`
+ : 'Ordne die Wörter zu einem natürlichen Bisaya-Satz aus dem Alltag.'
+ };
+}
+
+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 >= 4)
+ .slice(0, 4);
+
+ return {
+ exerciseTypeId: 8,
+ title: `${lessonTitle}: Laut sprechen`,
+ instruction: 'Sprich das zentrale Muster oder den Dialog 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 hier ein flüssiger Abruf und die zentralen Schlüsselwörter.'
+ };
+}
+
+function buildReviewChoiceExercise(lesson, didactics, pattern, allPatterns) {
+ const choiceExercise = buildChoiceExercise(lesson, didactics, pattern, allPatterns, 1);
+ if (!choiceExercise) return null;
+
+ return {
+ ...choiceExercise,
+ title: `${lesson.title}: Sicheren Abruf prüfen`,
+ instruction: 'Wähle die Formulierung, die du aus dem aktiven Wiederholungsblock sicher können solltest.'
+ };
+}
+
+function buildCultureExercise(lesson, didactics, pattern, allPatterns) {
+ const distractors = pickDistractors(pattern, allPatterns, 3);
+ if (distractors.length < 3) return null;
+
+ const culturalNote = normalizeText(lesson.culturalNotes || '');
+ const options = rotateArray([pattern, ...distractors], simpleHash(`${lesson.title}:culture`) % 4);
+
+ return {
+ exerciseTypeId: 2,
+ title: `${lesson.title}: Ausdruck kulturell einordnen`,
+ instruction: 'Ordne den Ausdruck dem kulturellen Schwerpunkt der Lektion zu.',
+ questionData: {
+ type: 'multiple_choice',
+ question: culturalNote
+ ? `${culturalNote} Welcher Ausdruck passt dazu besonders gut?`
+ : `Welcher Ausdruck gehört besonders gut zum Schwerpunkt "${lesson.title}"?`,
+ options
+ },
+ answerData: {
+ type: 'multiple_choice',
+ correctAnswer: options.indexOf(pattern)
+ },
+ explanation: `Der Ausdruck "${pattern}" gehört eng zum kulturellen Schwerpunkt dieser Lektion.`
+ };
+}
+
+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 >= 4)
+ .slice(0, 4);
+
+ return withTypeName('situational_response', {
+ title: `${lessonTitle}: Situativ reagieren`,
+ instruction: 'Antworte kurz und passend auf die Situation.',
+ 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 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 lessonPool = Array.from(new Set([
+ ...corePatterns,
+ ...GENERIC_DISTRACTOR_PATTERNS
+ ]));
+ let generated = [];
+
+ if (lesson.lessonType === 'conversation') {
+ generated = [
+ buildChoiceExercise(lesson, didactics, patternA, lessonPool, 0),
+ buildContextGapExercise(lesson, didactics, patternA),
+ buildTaskSentenceExercise(lesson, didactics, patternB),
+ buildSituationalExercise(lesson.title, didactics, patternA),
+ buildSpeakingExercise(lesson.title, didactics, patternB)
+ ];
+ } else if (lesson.lessonType === 'grammar') {
+ generated = [
+ buildChoiceExercise(lesson, didactics, patternA, lessonPool, 0),
+ buildChoiceExercise(lesson, didactics, patternB, lessonPool, 1),
+ buildContextGapExercise(lesson, didactics, patternA),
+ buildTaskSentenceExercise(lesson, didactics, patternB),
+ buildSpeakingExercise(lesson.title, didactics, patternA)
+ ];
+ } else if (lesson.lessonType === 'review' || lesson.didacticMode === 'intensive_review') {
+ generated = [
+ buildReviewChoiceExercise(lesson, didactics, patternA, lessonPool),
+ buildReviewChoiceExercise(lesson, didactics, patternB, lessonPool),
+ buildContextGapExercise(lesson, didactics, patternA),
+ buildTaskSentenceExercise(lesson, didactics, patternB),
+ buildSituationalExercise(lesson.title, didactics, patternA),
+ buildSpeakingExercise(lesson.title, didactics, patternB)
+ ];
+ } else if (lesson.lessonType === 'culture') {
+ generated = [
+ buildCultureExercise(lesson, didactics, patternA, lessonPool),
+ buildContextGapExercise(lesson, didactics, patternA),
+ buildSpeakingExercise(lesson.title, didactics, patternB)
+ ];
+ } else {
+ generated = [
+ buildChoiceExercise(lesson, didactics, patternA, lessonPool, 0),
+ buildChoiceExercise(lesson, didactics, patternB, lessonPool, 1),
+ buildContextGapExercise(lesson, didactics, patternA),
+ buildTaskSentenceExercise(lesson, didactics, patternB)
+ ];
+ }
+
+ return generated.filter(Boolean);
+}
+
// Bisaya-spezifische Übungen basierend auf Lektionsthemen
const BISAYA_EXERCISES = {
// Lektion 1: Begrüßungen & Höflichkeit
@@ -1489,7 +1842,8 @@ async function findOrCreateSystemUser() {
return systemUser;
}
-function getExercisesForLesson(lessonTitle) {
+function getExercisesForLesson(lesson) {
+ const lessonTitle = lesson.title;
// Suche nach exaktem Titel
if (BISAYA_EXERCISES[lessonTitle]) {
return BISAYA_EXERCISES[lessonTitle];
@@ -1501,9 +1855,8 @@ function getExercisesForLesson(lessonTitle) {
return exercises;
}
}
-
- // Keine Übungen für unbekannte Lektionen (statt Dummy-Übungen)
- return [];
+
+ return generateExercisesFromDidactics(lesson);
}
async function createBisayaCourseContent() {
@@ -1550,7 +1903,7 @@ async function createBisayaCourseContent() {
console.log(` ${lessons.length} Lektionen gefunden\n`);
for (const lesson of lessons) {
- const exercises = getExercisesForLesson(lesson.title);
+ const exercises = getExercisesForLesson(lesson);
if (exercises.length === 0) {
const existingCount = await VocabGrammarExercise.count({ where: { lessonId: lesson.id } });
if (existingCount > 0) {
diff --git a/backend/scripts/create-bisaya-course.js b/backend/scripts/create-bisaya-course.js
index bc596be..9c8154c 100755
--- a/backend/scripts/create-bisaya-course.js
+++ b/backend/scripts/create-bisaya-course.js
@@ -1,6 +1,6 @@
#!/usr/bin/env node
/**
- * Script zum Erstellen eines vollständigen 4-Wochen Bisaya-Kurses
+ * Script zum Erstellen eines vollständigen 6-Wochen Bisaya-Kurses
*
* Verwendung:
* node backend/scripts/create-bisaya-course.js
@@ -11,6 +11,10 @@ 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 crypto from 'crypto';
+import { getBisayaLessonPedagogy } from './bisaya-course-phase2-pedagogy.js';
+import { BISAYA_PHASE3_DIDACTICS, BISAYA_PHASE3_LESSONS } from './bisaya-course-phase3-extension.js';
+import { BISAYA_PHASE4_DIDACTICS, BISAYA_PHASE4_LESSONS } from './bisaya-course-phase4-extension.js';
+import { BISAYA_PHASE5_DIDACTICS, BISAYA_PHASE5_LESSONS } from './bisaya-course-phase5-extension.js';
const LESSON_DIDACTICS = {
'Begrüßungen & Höflichkeit': {
@@ -192,7 +196,10 @@ const LESSON_DIDACTICS = {
'Lami',
'Mingaw ko nimo'
]
- }
+ },
+ ...BISAYA_PHASE3_DIDACTICS,
+ ...BISAYA_PHASE4_DIDACTICS,
+ ...BISAYA_PHASE5_DIDACTICS
};
const LESSONS = [
@@ -398,7 +405,11 @@ const LESSONS = [
{ week: 4, day: 5, num: 40, type: 'culture', title: 'Kulturelle Tipps & Tricks',
desc: 'Wichtige kulturelle Hinweise für den Alltag',
targetMin: 15, targetScore: 0, review: false,
- cultural: 'Kulturelles Verständnis ist genauso wichtig wie die Sprache selbst.' }
+ cultural: 'Kulturelles Verständnis ist genauso wichtig wie die Sprache selbst.' },
+
+ ...BISAYA_PHASE3_LESSONS,
+ ...BISAYA_PHASE4_LESSONS,
+ ...BISAYA_PHASE5_LESSONS
];
async function createBisayaCourse(languageId, ownerHashedId) {
@@ -422,8 +433,8 @@ async function createBisayaCourse(languageId, ownerHashedId) {
const shareCode = crypto.randomBytes(8).toString('hex');
const course = await VocabCourse.create({
ownerUserId: user.id,
- title: 'Bisaya für Familien - Schnellstart in 4 Wochen',
- description: 'Lerne Bisaya (Cebuano) schnell und praktisch für den Familienalltag. Fokus auf Sprechen & Hören mit strukturiertem 4-Wochen-Plan.',
+ title: 'Bisaya für Familien - Alltag & Stabilisierung',
+ description: 'Lerne Bisaya (Cebuano) praxisnah für den Familienalltag. Der Pfad verbindet Schnellstart, Alltagsmodule und Stabilisierungsblöcke mit Spiralwiederholung, Fehlertraining und freier Produktion.',
languageId: Number(languageId),
difficultyLevel: 1,
isPublic: true,
@@ -435,6 +446,7 @@ async function createBisayaCourse(languageId, ownerHashedId) {
// Erstelle Lektionen
for (const lessonData of LESSONS) {
+ const pedagogy = getBisayaLessonPedagogy(lessonData.num) || {};
const lesson = await VocabCourseLesson.create({
courseId: course.id,
chapterId: null, // Wird später mit Vokabeln verknüpft
@@ -452,7 +464,14 @@ async function createBisayaCourse(languageId, ownerHashedId) {
practicalTasks: LESSON_DIDACTICS[lessonData.title]?.practicalTasks || [],
targetMinutes: lessonData.targetMin,
targetScorePercent: lessonData.targetScore,
- requiresReview: lessonData.review
+ requiresReview: lessonData.review,
+ didacticMode: pedagogy.didacticMode || null,
+ phaseLabel: pedagogy.phaseLabel || null,
+ blockNumber: pedagogy.blockNumber ?? null,
+ difficultyWeight: pedagogy.difficultyWeight ?? null,
+ newUnitTarget: pedagogy.newUnitTarget ?? null,
+ reviewWeight: pedagogy.reviewWeight ?? null,
+ isIntensiveReview: Boolean(pedagogy.isIntensiveReview)
});
console.log(` ✅ Lektion ${lessonData.num}: ${lessonData.title} (Woche ${lessonData.week}, Tag ${lessonData.day})`);
}
@@ -464,7 +483,7 @@ async function createBisayaCourse(languageId, ownerHashedId) {
console.log(` - Konversations-Lektionen: ${LESSONS.filter(l => l.type === 'conversation').length}`);
console.log(` - Grammatik-Lektionen: ${LESSONS.filter(l => l.type === 'grammar').length}`);
console.log(` - Wiederholungs-Lektionen: ${LESSONS.filter(l => l.type === 'review').length}`);
- console.log(` - Durchschnittliche Zeit pro Tag: ~${Math.round(LESSONS.reduce((sum, l) => sum + l.targetMin, 0) / (4 * 5))} Minuten`);
+ console.log(` - Durchschnittliche Zeit pro Tag: ~${Math.round(LESSONS.reduce((sum, l) => sum + l.targetMin, 0) / (15 * 5))} Minuten`);
console.log(`\n💡 Nächste Schritte:`);
console.log(` 1. Füge Vokabeln zu den Vokabel-Lektionen hinzu`);
console.log(` 2. Erstelle Grammatik-Übungen für die Grammatik-Lektionen`);
diff --git a/backend/scripts/extend-bisaya-course-phase3.js b/backend/scripts/extend-bisaya-course-phase3.js
new file mode 100644
index 0000000..b7aace9
--- /dev/null
+++ b/backend/scripts/extend-bisaya-course-phase3.js
@@ -0,0 +1,102 @@
+#!/usr/bin/env node
+/**
+ * Erweitert bestehende Bisaya-Kurse von 4 auf 6 Wochen.
+ *
+ * Verwendung:
+ * node backend/scripts/extend-bisaya-course-phase3.js
+ */
+
+import { sequelize } from '../utils/sequelize.js';
+import VocabCourse from '../models/community/vocab_course.js';
+import VocabCourseLesson from '../models/community/vocab_course_lesson.js';
+import { getBisayaLessonPedagogy } from './bisaya-course-phase2-pedagogy.js';
+import { BISAYA_PHASE3_DIDACTICS, BISAYA_PHASE3_LESSONS } from './bisaya-course-phase3-extension.js';
+
+async function extendBisayaCoursePhase3() {
+ await sequelize.authenticate();
+
+ const [bisayaLanguage] = await sequelize.query(
+ `SELECT id FROM community.vocab_language WHERE name = 'Bisaya' LIMIT 1`,
+ { type: sequelize.QueryTypes.SELECT }
+ );
+
+ if (!bisayaLanguage) {
+ console.error('❌ Bisaya-Sprache nicht gefunden.');
+ return;
+ }
+
+ const courses = await VocabCourse.findAll({
+ where: { languageId: bisayaLanguage.id }
+ });
+
+ let addedLessons = 0;
+
+ for (const course of courses) {
+ await course.update({
+ title: 'Bisaya für Familien - Schnellstart in 6 Wochen',
+ description: 'Lerne Bisaya (Cebuano) schnell und praktisch für den Familienalltag. Fokus auf Sprechen, Hören, Spiralwiederholung und einem strukturierten 6-Wochen-Plan.'
+ });
+
+ for (const lessonData of BISAYA_PHASE3_LESSONS) {
+ const existing = await VocabCourseLesson.findOne({
+ where: {
+ courseId: course.id,
+ lessonNumber: lessonData.num
+ }
+ });
+
+ if (existing) {
+ continue;
+ }
+
+ const didactics = BISAYA_PHASE3_DIDACTICS[lessonData.title] || {};
+ const pedagogy = getBisayaLessonPedagogy(lessonData.num) || {};
+
+ 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,
+ culturalNotes: lessonData.cultural,
+ learningGoals: didactics.learningGoals || [],
+ corePatterns: didactics.corePatterns || [],
+ grammarFocus: didactics.grammarFocus || [],
+ speakingPrompts: didactics.speakingPrompts || [],
+ practicalTasks: didactics.practicalTasks || [],
+ targetMinutes: lessonData.targetMin,
+ targetScorePercent: lessonData.targetScore,
+ requiresReview: lessonData.review,
+ didacticMode: pedagogy.didacticMode || null,
+ phaseLabel: pedagogy.phaseLabel || null,
+ blockNumber: pedagogy.blockNumber ?? null,
+ difficultyWeight: pedagogy.difficultyWeight ?? null,
+ newUnitTarget: pedagogy.newUnitTarget ?? null,
+ reviewWeight: pedagogy.reviewWeight ?? null,
+ isIntensiveReview: Boolean(pedagogy.isIntensiveReview)
+ });
+
+ addedLessons++;
+ console.log(`✅ Kurs ${course.id}: Lektion ${lessonData.num} - ${lessonData.title} ergänzt`);
+ }
+ }
+
+ console.log(`\n🎉 Phase 3 vorbereitet.`);
+ console.log(` Kurse: ${courses.length}`);
+ console.log(` Neue Lektionen ergänzt: ${addedLessons}`);
+ console.log(' Danach create-bisaya-course-content.js ausführen, wenn für die neuen Lektionen zusätzliche Übungen eingespielt werden sollen.');
+}
+
+extendBisayaCoursePhase3()
+ .then(() => {
+ sequelize.close();
+ process.exit(0);
+ })
+ .catch((error) => {
+ console.error('❌ Fehler:', error);
+ sequelize.close();
+ process.exit(1);
+ });
diff --git a/backend/scripts/extend-bisaya-course-phase4.js b/backend/scripts/extend-bisaya-course-phase4.js
new file mode 100644
index 0000000..ffb7209
--- /dev/null
+++ b/backend/scripts/extend-bisaya-course-phase4.js
@@ -0,0 +1,102 @@
+#!/usr/bin/env node
+/**
+ * Erweitert bestehende Bisaya-Kurse auf den 3-Monats-Alltagspfad.
+ *
+ * Verwendung:
+ * node backend/scripts/extend-bisaya-course-phase4.js
+ */
+
+import { sequelize } from '../utils/sequelize.js';
+import VocabCourse from '../models/community/vocab_course.js';
+import VocabCourseLesson from '../models/community/vocab_course_lesson.js';
+import { getBisayaLessonPedagogy } from './bisaya-course-phase2-pedagogy.js';
+import { BISAYA_PHASE4_DIDACTICS, BISAYA_PHASE4_LESSONS } from './bisaya-course-phase4-extension.js';
+
+async function extendBisayaCoursePhase4() {
+ await sequelize.authenticate();
+
+ const [bisayaLanguage] = await sequelize.query(
+ `SELECT id FROM community.vocab_language WHERE name = 'Bisaya' LIMIT 1`,
+ { type: sequelize.QueryTypes.SELECT }
+ );
+
+ if (!bisayaLanguage) {
+ console.error('❌ Bisaya-Sprache nicht gefunden.');
+ return;
+ }
+
+ const courses = await VocabCourse.findAll({
+ where: { languageId: bisayaLanguage.id }
+ });
+
+ let addedLessons = 0;
+
+ for (const course of courses) {
+ await course.update({
+ title: 'Bisaya für Familien - Alltag in 3 Monaten',
+ description: 'Lerne Bisaya (Cebuano) praxisnah für den Familienalltag. Fokus auf Sprechen, Hören, Spiralwiederholung und einem strukturierten 3-Monats-Pfad vom Schnellstart bis zur stabilen Alltagskommunikation.'
+ });
+
+ for (const lessonData of BISAYA_PHASE4_LESSONS) {
+ const existing = await VocabCourseLesson.findOne({
+ where: {
+ courseId: course.id,
+ lessonNumber: lessonData.num
+ }
+ });
+
+ if (existing) {
+ continue;
+ }
+
+ const didactics = BISAYA_PHASE4_DIDACTICS[lessonData.title] || {};
+ const pedagogy = getBisayaLessonPedagogy(lessonData.num) || {};
+
+ 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,
+ culturalNotes: lessonData.cultural,
+ learningGoals: didactics.learningGoals || [],
+ corePatterns: didactics.corePatterns || [],
+ grammarFocus: didactics.grammarFocus || [],
+ speakingPrompts: didactics.speakingPrompts || [],
+ practicalTasks: didactics.practicalTasks || [],
+ targetMinutes: lessonData.targetMin,
+ targetScorePercent: lessonData.targetScore,
+ requiresReview: lessonData.review,
+ didacticMode: pedagogy.didacticMode || null,
+ phaseLabel: pedagogy.phaseLabel || null,
+ blockNumber: pedagogy.blockNumber ?? null,
+ difficultyWeight: pedagogy.difficultyWeight ?? null,
+ newUnitTarget: pedagogy.newUnitTarget ?? null,
+ reviewWeight: pedagogy.reviewWeight ?? null,
+ isIntensiveReview: Boolean(pedagogy.isIntensiveReview)
+ });
+
+ addedLessons++;
+ console.log(`✅ Kurs ${course.id}: Lektion ${lessonData.num} - ${lessonData.title} ergänzt`);
+ }
+ }
+
+ console.log(`\n🎉 Phase 4 vorbereitet.`);
+ console.log(` Kurse: ${courses.length}`);
+ console.log(` Neue Lektionen ergänzt: ${addedLessons}`);
+ console.log(' Das Einspielen in die DB kann später gesammelt mit den übrigen Phasen erfolgen.');
+}
+
+extendBisayaCoursePhase4()
+ .then(() => {
+ sequelize.close();
+ process.exit(0);
+ })
+ .catch((error) => {
+ console.error('❌ Fehler:', error);
+ sequelize.close();
+ process.exit(1);
+ });
diff --git a/backend/scripts/extend-bisaya-course-phase5.js b/backend/scripts/extend-bisaya-course-phase5.js
new file mode 100644
index 0000000..fd055db
--- /dev/null
+++ b/backend/scripts/extend-bisaya-course-phase5.js
@@ -0,0 +1,102 @@
+#!/usr/bin/env node
+/**
+ * Erweitert bestehende Bisaya-Kurse um die Stabilisierungsphase.
+ *
+ * Verwendung:
+ * node backend/scripts/extend-bisaya-course-phase5.js
+ */
+
+import { sequelize } from '../utils/sequelize.js';
+import VocabCourse from '../models/community/vocab_course.js';
+import VocabCourseLesson from '../models/community/vocab_course_lesson.js';
+import { getBisayaLessonPedagogy } from './bisaya-course-phase2-pedagogy.js';
+import { BISAYA_PHASE5_DIDACTICS, BISAYA_PHASE5_LESSONS } from './bisaya-course-phase5-extension.js';
+
+async function extendBisayaCoursePhase5() {
+ await sequelize.authenticate();
+
+ const [bisayaLanguage] = await sequelize.query(
+ `SELECT id FROM community.vocab_language WHERE name = 'Bisaya' LIMIT 1`,
+ { type: sequelize.QueryTypes.SELECT }
+ );
+
+ if (!bisayaLanguage) {
+ console.error('❌ Bisaya-Sprache nicht gefunden.');
+ return;
+ }
+
+ const courses = await VocabCourse.findAll({
+ where: { languageId: bisayaLanguage.id }
+ });
+
+ let addedLessons = 0;
+
+ for (const course of courses) {
+ await course.update({
+ title: 'Bisaya für Familien - Alltag & Stabilisierung',
+ description: 'Lerne Bisaya (Cebuano) praxisnah für den Familienalltag. Der Pfad verbindet Schnellstart, Alltagsmodule und Stabilisierungsblöcke mit Spiralwiederholung, Fehlertraining und freier Produktion.'
+ });
+
+ for (const lessonData of BISAYA_PHASE5_LESSONS) {
+ const existing = await VocabCourseLesson.findOne({
+ where: {
+ courseId: course.id,
+ lessonNumber: lessonData.num
+ }
+ });
+
+ if (existing) {
+ continue;
+ }
+
+ const didactics = BISAYA_PHASE5_DIDACTICS[lessonData.title] || {};
+ const pedagogy = getBisayaLessonPedagogy(lessonData.num) || {};
+
+ 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,
+ culturalNotes: lessonData.cultural,
+ learningGoals: didactics.learningGoals || [],
+ corePatterns: didactics.corePatterns || [],
+ grammarFocus: didactics.grammarFocus || [],
+ speakingPrompts: didactics.speakingPrompts || [],
+ practicalTasks: didactics.practicalTasks || [],
+ targetMinutes: lessonData.targetMin,
+ targetScorePercent: lessonData.targetScore,
+ requiresReview: lessonData.review,
+ didacticMode: pedagogy.didacticMode || null,
+ phaseLabel: pedagogy.phaseLabel || null,
+ blockNumber: pedagogy.blockNumber ?? null,
+ difficultyWeight: pedagogy.difficultyWeight ?? null,
+ newUnitTarget: pedagogy.newUnitTarget ?? null,
+ reviewWeight: pedagogy.reviewWeight ?? null,
+ isIntensiveReview: Boolean(pedagogy.isIntensiveReview)
+ });
+
+ addedLessons++;
+ console.log(`✅ Kurs ${course.id}: Lektion ${lessonData.num} - ${lessonData.title} ergänzt`);
+ }
+ }
+
+ console.log(`\n🎉 Phase 5 vorbereitet.`);
+ console.log(` Kurse: ${courses.length}`);
+ console.log(` Neue Lektionen ergänzt: ${addedLessons}`);
+ console.log(' Das Einspielen in die DB kann später gesammelt mit den übrigen Phasen erfolgen.');
+}
+
+extendBisayaCoursePhase5()
+ .then(() => {
+ sequelize.close();
+ process.exit(0);
+ })
+ .catch((error) => {
+ console.error('❌ Fehler:', error);
+ sequelize.close();
+ process.exit(1);
+ });
diff --git a/backend/scripts/update-bisaya-didactics.js b/backend/scripts/update-bisaya-didactics.js
index 882f3eb..2666812 100644
--- a/backend/scripts/update-bisaya-didactics.js
+++ b/backend/scripts/update-bisaya-didactics.js
@@ -8,6 +8,7 @@
import { sequelize } from '../utils/sequelize.js';
import VocabCourseLesson from '../models/community/vocab_course_lesson.js';
+import { getBisayaLessonPedagogy } from './bisaya-course-phase2-pedagogy.js';
const LESSON_DIDACTICS = {
'Begrüßungen & Höflichkeit': {
@@ -129,14 +130,23 @@ async function updateBisayaDidactics() {
for (const row of lessons) {
const lesson = await VocabCourseLesson.findByPk(row.id);
const didactics = LESSON_DIDACTICS[lesson.title];
- if (!didactics) continue;
+ const pedagogy = getBisayaLessonPedagogy(lesson.lessonNumber);
+ if (!didactics && !pedagogy) continue;
+ const normalizedDidactics = didactics || {};
await lesson.update({
- learningGoals: didactics.learningGoals || [],
- corePatterns: didactics.corePatterns || [],
- grammarFocus: didactics.grammarFocus || [],
- speakingPrompts: didactics.speakingPrompts || [],
- practicalTasks: didactics.practicalTasks || []
+ learningGoals: normalizedDidactics.learningGoals || [],
+ corePatterns: normalizedDidactics.corePatterns || [],
+ grammarFocus: normalizedDidactics.grammarFocus || [],
+ speakingPrompts: normalizedDidactics.speakingPrompts || [],
+ practicalTasks: normalizedDidactics.practicalTasks || [],
+ didacticMode: pedagogy?.didacticMode || null,
+ phaseLabel: pedagogy?.phaseLabel || null,
+ blockNumber: pedagogy?.blockNumber ?? null,
+ difficultyWeight: pedagogy?.difficultyWeight ?? null,
+ newUnitTarget: pedagogy?.newUnitTarget ?? null,
+ reviewWeight: pedagogy?.reviewWeight ?? null,
+ isIntensiveReview: Boolean(pedagogy?.isIntensiveReview)
});
updated++;
console.log(`✅ Didaktik aktualisiert: Lektion ${lesson.lessonNumber} - ${lesson.title}`);
diff --git a/backend/services/adminService.js b/backend/services/adminService.js
index 2071b93..d43d239 100644
--- a/backend/services/adminService.js
+++ b/backend/services/adminService.js
@@ -29,6 +29,9 @@ import EroticVideo from '../models/community/erotic_video.js';
import EroticContentReport from '../models/community/erotic_content_report.js';
import TitleOfNobility from "../models/falukant/type/title_of_nobility.js";
import ChildRelation from "../models/falukant/data/child_relation.js";
+import Relationship from "../models/falukant/data/relationship.js";
+import RelationshipType from "../models/falukant/type/relationship.js";
+import RelationshipState from "../models/falukant/data/relationship_state.js";
import { sequelize } from '../utils/sequelize.js';
import npcCreationJobService from './npcCreationJobService.js';
import { v4 as uuidv4 } from 'uuid';
@@ -668,7 +671,7 @@ class AdminService {
required: true,
attributes: ['money', 'certificate', 'id'],
include: [{
- model: FalukantCharacter,
+ model: FalukantCharacter.unscoped(),
as: 'character',
include: [{
model: FalukantPredefineFirstname,
@@ -945,10 +948,85 @@ class AdminService {
await character.save();
}
+ /**
+ * Ehepartner/Verlobte/Liebhaber eines Charakters (für Admin-Auswahl Vater).
+ */
+ async adminGetPotentialFathersForCharacter(userId, motherCharacterId) {
+ if (!(await this.hasUserAccess(userId, 'falukantusers'))) {
+ throw new Error('noaccess');
+ }
+ const mid = Number(motherCharacterId);
+ if (!Number.isFinite(mid)) {
+ throw new Error('invalidCharacter');
+ }
+ const mother = await FalukantCharacter.findByPk(mid, { attributes: ['id'] });
+ if (!mother) {
+ throw new Error('notfound');
+ }
+
+ const rels = await Relationship.findAll({
+ where: {
+ [Op.or]: [
+ { character1Id: mid },
+ { character2Id: mid }
+ ]
+ },
+ include: [
+ { model: RelationshipType, as: 'relationshipType', attributes: ['tr'] },
+ { model: RelationshipState, as: 'state', required: false },
+ {
+ model: FalukantCharacter,
+ as: 'character1',
+ attributes: ['id'],
+ required: true,
+ include: [{ model: FalukantPredefineFirstname, as: 'definedFirstName', attributes: ['name'] }]
+ },
+ {
+ model: FalukantCharacter,
+ as: 'character2',
+ attributes: ['id'],
+ required: true,
+ include: [{ model: FalukantPredefineFirstname, as: 'definedFirstName', attributes: ['name'] }]
+ }
+ ]
+ });
+
+ const allowedTypes = new Set(['married', 'engaged', 'lover']);
+ const typeOrder = { married: 0, engaged: 1, lover: 2 };
+ const seen = new Set();
+ const options = [];
+
+ for (const rel of rels) {
+ const typeTr = rel.relationshipType?.tr;
+ if (!typeTr || !allowedTypes.has(typeTr)) {
+ continue;
+ }
+ if (typeTr === 'lover' && rel.state && rel.state.active === false) {
+ continue;
+ }
+ const partnerId = rel.character1Id === mid ? rel.character2Id : rel.character1Id;
+ if (partnerId === mid || seen.has(partnerId)) {
+ continue;
+ }
+ seen.add(partnerId);
+ const partner = rel.character1Id === mid ? rel.character2 : rel.character1;
+ const displayName = partner?.definedFirstName?.name?.trim() || `Charakter #${partnerId}`;
+ options.push({
+ characterId: partnerId,
+ displayName,
+ relationshipType: typeTr
+ });
+ }
+
+ options.sort((a, b) => (typeOrder[a.relationshipType] ?? 9) - (typeOrder[b.relationshipType] ?? 9));
+
+ return { options };
+ }
+
/**
* Admin: Charakter als schwanger markieren (erwarteter Geburtstermin).
* @param {number} fatherCharacterId - optional; Vater-Charakter-ID
- * @param {number} dueInDays - Tage bis zur „Geburt“ (Default 21)
+ * @param {number} dueInDays - Tage bis zum Termin (0 = heute; Default 21)
*/
async adminForceFalukantPregnancy(userId, characterId, { fatherCharacterId = null, dueInDays = 21 } = {}) {
if (!(await this.hasUserAccess(userId, 'falukantusers'))) {
@@ -960,7 +1038,16 @@ class AdminService {
const father = await FalukantCharacter.findByPk(Number(fatherCharacterId));
if (!father) throw new Error('fatherNotFound');
}
- const days = Math.max(1, Math.min(365, Number(dueInDays) || 21));
+ let rawDays = dueInDays;
+ if (rawDays === undefined || rawDays === null || rawDays === '') {
+ rawDays = 21;
+ } else {
+ rawDays = Number(rawDays);
+ if (!Number.isFinite(rawDays)) {
+ rawDays = 21;
+ }
+ }
+ const days = Math.max(0, Math.min(365, rawDays));
const due = new Date();
due.setDate(due.getDate() + days);
await mother.update({
diff --git a/backend/services/vocabService.js b/backend/services/vocabService.js
index 1d59415..89d3b77 100644
--- a/backend/services/vocabService.js
+++ b/backend/services/vocabService.js
@@ -188,6 +188,149 @@ export default class VocabService {
return [];
}
+ _normalizeOptionalInteger(value) {
+ if (value === undefined || value === null || value === '') {
+ return null;
+ }
+ const parsed = Number(value);
+ return Number.isFinite(parsed) ? parsed : null;
+ }
+
+ _normalizeOptionalString(value) {
+ if (value === undefined || value === null) {
+ return null;
+ }
+ const trimmed = String(value).trim();
+ return trimmed || null;
+ }
+
+ _inferLessonPhaseLabel(plainLesson) {
+ if (plainLesson.phaseLabel) {
+ return plainLesson.phaseLabel;
+ }
+ const weekNumber = Number(plainLesson.weekNumber) || 0;
+ if (weekNumber > 0 && weekNumber <= 2) {
+ return 'quickstart';
+ }
+ if (weekNumber === 3) {
+ return 'daily_life';
+ }
+ if (weekNumber >= 4) {
+ return 'stabilization';
+ }
+ return 'quickstart';
+ }
+
+ _inferLessonDidacticMode(plainLesson) {
+ if (plainLesson.didacticMode) {
+ return plainLesson.didacticMode;
+ }
+ const lessonType = String(plainLesson.lessonType || '').toLowerCase();
+ const title = String(plainLesson.title || '').toLowerCase();
+ if (title.includes('abschluss') || title.includes('prüfung') || title.includes('test')) {
+ return 'checkpoint';
+ }
+ if (plainLesson.isIntensiveReview || lessonType === 'review' || lessonType === 'vocab_review' || title.includes('wiederholung')) {
+ return 'intensive_review';
+ }
+ if (lessonType === 'grammar') {
+ return 'pattern_drill';
+ }
+ if (lessonType === 'conversation' || lessonType === 'dialogue' || lessonType === 'phrases' || lessonType === 'survival') {
+ return 'guided_dialogue';
+ }
+ if (lessonType === 'culture') {
+ return 'real_life_scenario';
+ }
+ return 'core_input';
+ }
+
+ _inferLessonDifficultyWeight(plainLesson, didacticMode) {
+ if (plainLesson.difficultyWeight != null) {
+ return plainLesson.difficultyWeight;
+ }
+ switch (didacticMode) {
+ case 'pattern_drill':
+ return 3;
+ case 'guided_dialogue':
+ case 'real_life_scenario':
+ return 2;
+ case 'intensive_review':
+ case 'checkpoint':
+ return 2;
+ default:
+ return 1;
+ }
+ }
+
+ _inferLessonNewUnitTarget(plainLesson, didacticMode) {
+ if (plainLesson.newUnitTarget != null) {
+ return plainLesson.newUnitTarget;
+ }
+ switch (didacticMode) {
+ case 'core_input':
+ return 8;
+ case 'guided_dialogue':
+ return 5;
+ case 'pattern_drill':
+ return 4;
+ case 'real_life_scenario':
+ return 3;
+ case 'checkpoint':
+ return 2;
+ case 'intensive_review':
+ return 1;
+ default:
+ return 4;
+ }
+ }
+
+ _inferLessonReviewWeight(plainLesson, didacticMode) {
+ if (plainLesson.reviewWeight != null) {
+ return plainLesson.reviewWeight;
+ }
+ switch (didacticMode) {
+ case 'intensive_review':
+ return 90;
+ case 'checkpoint':
+ return 70;
+ case 'pattern_drill':
+ return 55;
+ case 'real_life_scenario':
+ return 45;
+ case 'guided_dialogue':
+ return 40;
+ default:
+ return 30;
+ }
+ }
+
+ _inferLessonBlockNumber(plainLesson) {
+ if (plainLesson.blockNumber != null) {
+ return plainLesson.blockNumber;
+ }
+ const weekNumber = Number(plainLesson.weekNumber) || 1;
+ return Math.max(1, Math.ceil(weekNumber / 2));
+ }
+
+ _buildLessonPedagogy(plainLesson) {
+ const didacticMode = this._inferLessonDidacticMode(plainLesson);
+ const phaseLabel = this._inferLessonPhaseLabel(plainLesson);
+ const isIntensiveReview = plainLesson.isIntensiveReview != null
+ ? Boolean(plainLesson.isIntensiveReview)
+ : didacticMode === 'intensive_review';
+
+ return {
+ didacticMode,
+ phaseLabel,
+ blockNumber: this._inferLessonBlockNumber(plainLesson),
+ difficultyWeight: this._inferLessonDifficultyWeight(plainLesson, didacticMode),
+ newUnitTarget: this._inferLessonNewUnitTarget(plainLesson, didacticMode),
+ reviewWeight: this._inferLessonReviewWeight(plainLesson, didacticMode),
+ isIntensiveReview
+ };
+ }
+
_buildLessonDidactics(plainLesson) {
const grammarExercises = Array.isArray(plainLesson.grammarExercises) ? plainLesson.grammarExercises : [];
const grammarExplanations = [];
@@ -1020,6 +1163,11 @@ export default class VocabService {
return a.lessonNumber - b.lessonNumber;
});
+ courseData.lessons = courseData.lessons.map((lesson) => ({
+ ...lesson,
+ pedagogy: this._buildLessonPedagogy(lesson)
+ }));
+
return courseData;
}
@@ -1129,6 +1277,7 @@ export default class VocabService {
}
plainLesson.didactics = this._buildLessonDidactics(plainLesson);
+ plainLesson.pedagogy = this._buildLessonPedagogy(plainLesson);
return plainLesson;
}
@@ -1301,7 +1450,7 @@ export default class VocabService {
return exercises.map(e => e.get({ plain: true }));
}
- async addLessonToCourse(hashedUserId, courseId, { chapterId, lessonNumber, title, description, weekNumber, dayNumber, lessonType, audioUrl, culturalNotes, learningGoals, corePatterns, grammarFocus, speakingPrompts, practicalTasks, targetMinutes, targetScorePercent, requiresReview }) {
+ async addLessonToCourse(hashedUserId, courseId, { chapterId, lessonNumber, title, description, weekNumber, dayNumber, lessonType, didacticMode, phaseLabel, blockNumber, difficultyWeight, newUnitTarget, reviewWeight, isIntensiveReview, audioUrl, culturalNotes, learningGoals, corePatterns, grammarFocus, speakingPrompts, practicalTasks, targetMinutes, targetScorePercent, requiresReview }) {
const user = await this._getUserByHashedId(hashedUserId);
const course = await VocabCourse.findByPk(courseId);
@@ -1343,6 +1492,13 @@ export default class VocabService {
weekNumber: weekNumber ? Number(weekNumber) : null,
dayNumber: dayNumber ? Number(dayNumber) : null,
lessonType: lessonType || 'vocab',
+ didacticMode: this._normalizeOptionalString(didacticMode),
+ phaseLabel: this._normalizeOptionalString(phaseLabel),
+ blockNumber: this._normalizeOptionalInteger(blockNumber),
+ difficultyWeight: this._normalizeOptionalInteger(difficultyWeight),
+ newUnitTarget: this._normalizeOptionalInteger(newUnitTarget),
+ reviewWeight: this._normalizeOptionalInteger(reviewWeight),
+ isIntensiveReview: isIntensiveReview !== undefined ? Boolean(isIntensiveReview) : false,
audioUrl: audioUrl || null,
culturalNotes: culturalNotes || null,
learningGoals: this._normalizeStringList(learningGoals),
@@ -1358,7 +1514,7 @@ export default class VocabService {
return lesson.get({ plain: true });
}
- async updateLesson(hashedUserId, lessonId, { title, description, lessonNumber, weekNumber, dayNumber, lessonType, audioUrl, culturalNotes, learningGoals, corePatterns, grammarFocus, speakingPrompts, practicalTasks, targetMinutes, targetScorePercent, requiresReview }) {
+ async updateLesson(hashedUserId, lessonId, { title, description, lessonNumber, weekNumber, dayNumber, lessonType, didacticMode, phaseLabel, blockNumber, difficultyWeight, newUnitTarget, reviewWeight, isIntensiveReview, audioUrl, culturalNotes, learningGoals, corePatterns, grammarFocus, speakingPrompts, practicalTasks, targetMinutes, targetScorePercent, requiresReview }) {
const user = await this._getUserByHashedId(hashedUserId);
const lesson = await VocabCourseLesson.findByPk(lessonId, {
include: [{ model: VocabCourse, as: 'course' }]
@@ -1383,6 +1539,13 @@ export default class VocabService {
if (weekNumber !== undefined) updates.weekNumber = weekNumber ? Number(weekNumber) : null;
if (dayNumber !== undefined) updates.dayNumber = dayNumber ? Number(dayNumber) : null;
if (lessonType !== undefined) updates.lessonType = lessonType;
+ if (didacticMode !== undefined) updates.didacticMode = this._normalizeOptionalString(didacticMode);
+ if (phaseLabel !== undefined) updates.phaseLabel = this._normalizeOptionalString(phaseLabel);
+ if (blockNumber !== undefined) updates.blockNumber = this._normalizeOptionalInteger(blockNumber);
+ if (difficultyWeight !== undefined) updates.difficultyWeight = this._normalizeOptionalInteger(difficultyWeight);
+ if (newUnitTarget !== undefined) updates.newUnitTarget = this._normalizeOptionalInteger(newUnitTarget);
+ if (reviewWeight !== undefined) updates.reviewWeight = this._normalizeOptionalInteger(reviewWeight);
+ if (isIntensiveReview !== undefined) updates.isIntensiveReview = Boolean(isIntensiveReview);
if (audioUrl !== undefined) updates.audioUrl = audioUrl;
if (culturalNotes !== undefined) updates.culturalNotes = culturalNotes;
if (learningGoals !== undefined) updates.learningGoals = this._normalizeStringList(learningGoals);
diff --git a/backend/sql/add_vocab_lesson_phase1_fields.sql b/backend/sql/add_vocab_lesson_phase1_fields.sql
new file mode 100644
index 0000000..3122df4
--- /dev/null
+++ b/backend/sql/add_vocab_lesson_phase1_fields.sql
@@ -0,0 +1,23 @@
+ALTER TABLE community.vocab_course_lesson
+ADD COLUMN IF NOT EXISTS didactic_mode TEXT,
+ADD COLUMN IF NOT EXISTS phase_label TEXT,
+ADD COLUMN IF NOT EXISTS block_number INTEGER,
+ADD COLUMN IF NOT EXISTS difficulty_weight INTEGER,
+ADD COLUMN IF NOT EXISTS new_unit_target INTEGER,
+ADD COLUMN IF NOT EXISTS review_weight INTEGER,
+ADD COLUMN IF NOT EXISTS is_intensive_review BOOLEAN NOT NULL DEFAULT FALSE;
+
+COMMENT ON COLUMN community.vocab_course_lesson.didactic_mode IS
+ 'Didaktischer Modus der Lektion, z.B. core_input, guided_dialogue, intensive_review oder checkpoint.';
+COMMENT ON COLUMN community.vocab_course_lesson.phase_label IS
+ 'Übergeordnete Lernphase, z.B. quickstart, daily_life oder stabilization.';
+COMMENT ON COLUMN community.vocab_course_lesson.block_number IS
+ 'Inhaltlicher Block für Konsolidierungs- und Wiederholungswellen.';
+COMMENT ON COLUMN community.vocab_course_lesson.difficulty_weight IS
+ 'Grobe relative Schwierigkeit der Lektion von leicht bis schwer.';
+COMMENT ON COLUMN community.vocab_course_lesson.new_unit_target IS
+ 'Empfohlene Zahl neuer Spracheinheiten in dieser Lektion.';
+COMMENT ON COLUMN community.vocab_course_lesson.review_weight IS
+ 'Wie stark Wiederholung in dieser Lektion dominieren soll, typischerweise 0 bis 100.';
+COMMENT ON COLUMN community.vocab_course_lesson.is_intensive_review IS
+ 'Markiert Lektionen, die als intensive Wiederholungsphase gedacht sind.';
diff --git a/backend/sql/backfill_bisaya_phase2_pedagogy.sql b/backend/sql/backfill_bisaya_phase2_pedagogy.sql
new file mode 100644
index 0000000..53e595b
--- /dev/null
+++ b/backend/sql/backfill_bisaya_phase2_pedagogy.sql
@@ -0,0 +1,147 @@
+UPDATE community.vocab_course_lesson AS lesson
+SET
+ phase_label = CASE lesson.lesson_number
+ WHEN 1 THEN 'quickstart'
+ WHEN 2 THEN 'quickstart'
+ WHEN 3 THEN 'quickstart'
+ WHEN 4 THEN 'quickstart'
+ WHEN 5 THEN 'quickstart'
+ WHEN 6 THEN 'quickstart'
+ WHEN 7 THEN 'quickstart'
+ WHEN 8 THEN 'quickstart'
+ WHEN 9 THEN 'quickstart'
+ WHEN 10 THEN 'quickstart'
+ WHEN 11 THEN 'quickstart'
+ WHEN 12 THEN 'quickstart'
+ WHEN 13 THEN 'quickstart'
+ WHEN 14 THEN 'quickstart'
+ WHEN 15 THEN 'quickstart'
+ WHEN 16 THEN 'quickstart'
+ WHEN 17 THEN 'quickstart'
+ WHEN 18 THEN 'quickstart'
+ WHEN 19 THEN 'quickstart'
+ WHEN 20 THEN 'quickstart'
+ WHEN 21 THEN 'daily_life'
+ WHEN 22 THEN 'daily_life'
+ WHEN 23 THEN 'daily_life'
+ WHEN 24 THEN 'daily_life'
+ WHEN 25 THEN 'daily_life'
+ WHEN 26 THEN 'daily_life'
+ WHEN 27 THEN 'daily_life'
+ WHEN 28 THEN 'daily_life'
+ WHEN 29 THEN 'daily_life'
+ WHEN 30 THEN 'daily_life'
+ WHEN 31 THEN 'stabilization'
+ WHEN 32 THEN 'stabilization'
+ WHEN 33 THEN 'stabilization'
+ WHEN 34 THEN 'stabilization'
+ WHEN 35 THEN 'stabilization'
+ WHEN 36 THEN 'stabilization'
+ WHEN 37 THEN 'stabilization'
+ WHEN 38 THEN 'stabilization'
+ WHEN 39 THEN 'stabilization'
+ WHEN 40 THEN 'stabilization'
+ ELSE lesson.phase_label
+ END,
+ block_number = CASE lesson.lesson_number
+ WHEN 1 THEN 1 WHEN 2 THEN 1 WHEN 3 THEN 1 WHEN 4 THEN 1 WHEN 5 THEN 1
+ WHEN 6 THEN 1 WHEN 7 THEN 1 WHEN 8 THEN 1 WHEN 9 THEN 1 WHEN 10 THEN 1
+ WHEN 11 THEN 2 WHEN 12 THEN 2 WHEN 13 THEN 2 WHEN 14 THEN 2 WHEN 15 THEN 2
+ WHEN 16 THEN 2 WHEN 17 THEN 2 WHEN 18 THEN 2 WHEN 19 THEN 2 WHEN 20 THEN 2
+ WHEN 21 THEN 3 WHEN 22 THEN 3 WHEN 23 THEN 3 WHEN 24 THEN 3 WHEN 25 THEN 3
+ WHEN 26 THEN 3 WHEN 27 THEN 3 WHEN 28 THEN 3 WHEN 29 THEN 3 WHEN 30 THEN 3
+ WHEN 31 THEN 4 WHEN 32 THEN 4 WHEN 33 THEN 4 WHEN 34 THEN 4 WHEN 35 THEN 4
+ WHEN 36 THEN 4 WHEN 37 THEN 4 WHEN 38 THEN 4 WHEN 39 THEN 4 WHEN 40 THEN 4
+ ELSE lesson.block_number
+ END,
+ didactic_mode = CASE lesson.lesson_number
+ WHEN 1 THEN 'guided_dialogue'
+ WHEN 2 THEN 'core_input'
+ WHEN 3 THEN 'core_input'
+ WHEN 4 THEN 'guided_dialogue'
+ WHEN 5 THEN 'guided_dialogue'
+ WHEN 6 THEN 'core_input'
+ WHEN 7 THEN 'guided_dialogue'
+ WHEN 8 THEN 'core_input'
+ WHEN 9 THEN 'intensive_review'
+ WHEN 10 THEN 'checkpoint'
+ WHEN 11 THEN 'guided_dialogue'
+ WHEN 12 THEN 'core_input'
+ WHEN 13 THEN 'guided_dialogue'
+ WHEN 14 THEN 'core_input'
+ WHEN 15 THEN 'pattern_drill'
+ WHEN 16 THEN 'core_input'
+ WHEN 17 THEN 'guided_dialogue'
+ WHEN 18 THEN 'core_input'
+ WHEN 19 THEN 'intensive_review'
+ WHEN 20 THEN 'checkpoint'
+ WHEN 21 THEN 'guided_dialogue'
+ WHEN 22 THEN 'core_input'
+ WHEN 23 THEN 'guided_dialogue'
+ WHEN 24 THEN 'core_input'
+ WHEN 25 THEN 'pattern_drill'
+ WHEN 26 THEN 'guided_dialogue'
+ WHEN 27 THEN 'guided_dialogue'
+ WHEN 28 THEN 'core_input'
+ WHEN 29 THEN 'intensive_review'
+ WHEN 30 THEN 'checkpoint'
+ WHEN 31 THEN 'real_life_scenario'
+ WHEN 32 THEN 'intensive_review'
+ WHEN 33 THEN 'real_life_scenario'
+ WHEN 34 THEN 'intensive_review'
+ WHEN 35 THEN 'real_life_scenario'
+ WHEN 36 THEN 'intensive_review'
+ WHEN 37 THEN 'real_life_scenario'
+ WHEN 38 THEN 'checkpoint'
+ WHEN 39 THEN 'checkpoint'
+ WHEN 40 THEN 'real_life_scenario'
+ ELSE lesson.didactic_mode
+ END,
+ difficulty_weight = CASE lesson.lesson_number
+ WHEN 1 THEN 2 WHEN 2 THEN 3 WHEN 3 THEN 2 WHEN 4 THEN 3 WHEN 5 THEN 3
+ WHEN 6 THEN 3 WHEN 7 THEN 3 WHEN 8 THEN 2 WHEN 9 THEN 3 WHEN 10 THEN 3
+ WHEN 11 THEN 3 WHEN 12 THEN 2 WHEN 13 THEN 3 WHEN 14 THEN 3 WHEN 15 THEN 4
+ WHEN 16 THEN 3 WHEN 17 THEN 4 WHEN 18 THEN 4 WHEN 19 THEN 4 WHEN 20 THEN 4
+ WHEN 21 THEN 3 WHEN 22 THEN 3 WHEN 23 THEN 4 WHEN 24 THEN 4 WHEN 25 THEN 4
+ WHEN 26 THEN 4 WHEN 27 THEN 3 WHEN 28 THEN 3 WHEN 29 THEN 4 WHEN 30 THEN 4
+ WHEN 31 THEN 4 WHEN 32 THEN 4 WHEN 33 THEN 4 WHEN 34 THEN 4 WHEN 35 THEN 5
+ WHEN 36 THEN 5 WHEN 37 THEN 5 WHEN 38 THEN 5 WHEN 39 THEN 5 WHEN 40 THEN 2
+ ELSE lesson.difficulty_weight
+ END,
+ new_unit_target = CASE lesson.lesson_number
+ WHEN 1 THEN 5 WHEN 2 THEN 8 WHEN 3 THEN 7 WHEN 4 THEN 5 WHEN 5 THEN 4
+ WHEN 6 THEN 7 WHEN 7 THEN 5 WHEN 8 THEN 6 WHEN 9 THEN 0 WHEN 10 THEN 0
+ WHEN 11 THEN 5 WHEN 12 THEN 7 WHEN 13 THEN 5 WHEN 14 THEN 6 WHEN 15 THEN 4
+ WHEN 16 THEN 6 WHEN 17 THEN 5 WHEN 18 THEN 7 WHEN 19 THEN 0 WHEN 20 THEN 0
+ WHEN 21 THEN 5 WHEN 22 THEN 7 WHEN 23 THEN 5 WHEN 24 THEN 6 WHEN 25 THEN 4
+ WHEN 26 THEN 5 WHEN 27 THEN 5 WHEN 28 THEN 6 WHEN 29 THEN 0 WHEN 30 THEN 0
+ WHEN 31 THEN 4 WHEN 32 THEN 0 WHEN 33 THEN 4 WHEN 34 THEN 0 WHEN 35 THEN 3
+ WHEN 36 THEN 0 WHEN 37 THEN 3 WHEN 38 THEN 0 WHEN 39 THEN 0 WHEN 40 THEN 2
+ ELSE lesson.new_unit_target
+ END,
+ review_weight = CASE lesson.lesson_number
+ WHEN 1 THEN 15 WHEN 2 THEN 20 WHEN 3 THEN 20 WHEN 4 THEN 25 WHEN 5 THEN 25
+ WHEN 6 THEN 25 WHEN 7 THEN 30 WHEN 8 THEN 30 WHEN 9 THEN 90 WHEN 10 THEN 95
+ WHEN 11 THEN 25 WHEN 12 THEN 25 WHEN 13 THEN 30 WHEN 14 THEN 30 WHEN 15 THEN 40
+ WHEN 16 THEN 35 WHEN 17 THEN 35 WHEN 18 THEN 35 WHEN 19 THEN 92 WHEN 20 THEN 96
+ WHEN 21 THEN 35 WHEN 22 THEN 35 WHEN 23 THEN 40 WHEN 24 THEN 40 WHEN 25 THEN 45
+ WHEN 26 THEN 45 WHEN 27 THEN 40 WHEN 28 THEN 40 WHEN 29 THEN 94 WHEN 30 THEN 97
+ WHEN 31 THEN 55 WHEN 32 THEN 95 WHEN 33 THEN 60 WHEN 34 THEN 95 WHEN 35 THEN 65
+ WHEN 36 THEN 98 WHEN 37 THEN 70 WHEN 38 THEN 99 WHEN 39 THEN 100 WHEN 40 THEN 50
+ ELSE lesson.review_weight
+ END,
+ is_intensive_review = CASE lesson.lesson_number
+ WHEN 9 THEN TRUE
+ WHEN 19 THEN TRUE
+ WHEN 29 THEN TRUE
+ WHEN 32 THEN TRUE
+ WHEN 34 THEN TRUE
+ WHEN 36 THEN TRUE
+ ELSE FALSE
+ END
+FROM community.vocab_course AS course
+JOIN community.vocab_language AS language
+ ON language.id = course.language_id
+WHERE lesson.course_id = course.id
+ AND language.name = 'Bisaya'
+ AND lesson.lesson_number BETWEEN 1 AND 40;
diff --git a/backend/sql/extend_bisaya_course_phase3_to_6_weeks.sql b/backend/sql/extend_bisaya_course_phase3_to_6_weeks.sql
new file mode 100644
index 0000000..35c3f42
--- /dev/null
+++ b/backend/sql/extend_bisaya_course_phase3_to_6_weeks.sql
@@ -0,0 +1,12 @@
+UPDATE community.vocab_course AS course
+SET
+ title = 'Bisaya für Familien - Schnellstart in 6 Wochen',
+ description = 'Lerne Bisaya (Cebuano) schnell und praktisch für den Familienalltag. Fokus auf Sprechen, Hören, Spiralwiederholung und einem strukturierten 6-Wochen-Plan.'
+FROM community.vocab_language AS language
+WHERE course.language_id = language.id
+ AND language.name = 'Bisaya';
+
+-- Für die eigentlichen neuen Lektionen 41-60 wird das Skript
+-- backend/scripts/extend-bisaya-course-phase3.js empfohlen, weil dort
+-- auch JSON-Felder wie learning_goals, core_patterns, grammar_focus,
+-- speaking_prompts und practical_tasks konsistent gepflegt werden.
diff --git a/backend/sql/extend_bisaya_course_phase4_to_3_months.sql b/backend/sql/extend_bisaya_course_phase4_to_3_months.sql
new file mode 100644
index 0000000..ab72ccd
--- /dev/null
+++ b/backend/sql/extend_bisaya_course_phase4_to_3_months.sql
@@ -0,0 +1,11 @@
+UPDATE community.vocab_course AS course
+SET
+ title = 'Bisaya für Familien - Alltag in 3 Monaten',
+ description = 'Lerne Bisaya (Cebuano) praxisnah für den Familienalltag. Fokus auf Sprechen, Hören, Spiralwiederholung und einem strukturierten 3-Monats-Pfad vom Schnellstart bis zur stabilen Alltagskommunikation.'
+FROM community.vocab_language AS language
+WHERE course.language_id = language.id
+ AND language.name = 'Bisaya';
+
+-- Für die neuen Lektionen 61-120 wird das Skript
+-- backend/scripts/extend-bisaya-course-phase4.js empfohlen, weil dort
+-- auch die JSON-Felder der Lektionen konsistent gepflegt werden.
diff --git a/backend/sql/extend_bisaya_course_phase5_to_stabilization.sql b/backend/sql/extend_bisaya_course_phase5_to_stabilization.sql
new file mode 100644
index 0000000..c1706c0
--- /dev/null
+++ b/backend/sql/extend_bisaya_course_phase5_to_stabilization.sql
@@ -0,0 +1,11 @@
+UPDATE community.vocab_course AS course
+SET
+ title = 'Bisaya für Familien - Alltag & Stabilisierung',
+ description = 'Lerne Bisaya (Cebuano) praxisnah für den Familienalltag. Der Pfad verbindet Schnellstart, Alltagsmodule und Stabilisierungsblöcke mit Spiralwiederholung, Fehlertraining und freier Produktion.'
+FROM community.vocab_language AS language
+WHERE course.language_id = language.id
+ AND language.name = 'Bisaya';
+
+-- Für die neuen Lektionen 121-150 wird das Skript
+-- backend/scripts/extend-bisaya-course-phase5.js empfohlen, weil dort
+-- auch die JSON-Felder der Lektionen konsistent gepflegt werden.
diff --git a/docs/BISAYA_COURSE_EXPANSION_IMPLEMENTATION_SPEC.md b/docs/BISAYA_COURSE_EXPANSION_IMPLEMENTATION_SPEC.md
new file mode 100644
index 0000000..a807fcb
--- /dev/null
+++ b/docs/BISAYA_COURSE_EXPANSION_IMPLEMENTATION_SPEC.md
@@ -0,0 +1,1038 @@
+# Bisaya-Kursausbau: Umsetzungsdokument
+
+## 1. Ziel
+
+Der bestehende Bisaya-Kurs soll von einem kompakten Schnellstart zu einem belastbaren Sprachlernangebot ausgebaut werden.
+
+Das Ziel ist nicht nur:
+
+- Wörter schnell einprägen
+- einzelne Sätze wiedererkennen
+
+Sondern:
+
+- häufige Muster aktiv abrufen
+- Alltagssituationen sicher bewältigen
+- alte Inhalte vertiefen statt nur wiederholen
+- neue Inhalte dosiert und adaptiv einführen
+
+Der Kurs soll damit zwischen zwei schlechten Extremen vermeiden:
+
+- zu viel neuer Stoff ohne Konsolidierung
+- zu viel stumpfe Wiederholung ohne Fortschritt
+
+## 2. Aktueller Stand
+
+Der aktuelle Bisaya-Kurs ist im System klar als kompakter Einstieg angelegt.
+
+Quelle:
+- [create-bisaya-course.js](/mnt/share/torsten/Programs/YourPart3/backend/scripts/create-bisaya-course.js)
+
+Der Kurs umfasst nach Phase 5:
+
+- `15 Wochen`
+- `150 Lektionen`
+- Fokus auf:
+ - Begrüßung
+ - Familie
+ - Fürsorge
+ - Alltag
+ - Preise
+ - einfache Zeitformen
+ - erste freie Gespräche
+
+Die aktuelle Bezeichnung ist:
+
+- `Bisaya für Familien - Alltag & Stabilisierung`
+
+Das ist als Startkurs sinnvoll, aber nicht ausreichend für stabile Sprachbeherrschung.
+
+## 3. Produktentscheidung
+
+Der bisherige Kurs bleibt fachlich erhalten, wird aber neu eingeordnet:
+
+- nicht mehr als „der Bisaya-Kurs“
+- sondern als `Phase 1: Schnellstart / Grundkommunikation`
+
+Darauf bauen weitere Phasen auf.
+
+## 4. Zielbild des neuen Kursmodells
+
+Der neue Bisaya-Lernpfad besteht aus drei Ebenen.
+
+### 4.1 Phase A: Schnellstart
+
+Ziel:
+
+- erste Gespräche führen
+- Fürsorge, Familie und Höflichkeit verstehen
+- typische Alltagssätze direkt benutzen
+
+Dauer:
+
+- `6 Wochen`
+
+Lernfokus:
+
+- hochfrequente Phrasen
+- feste Muster
+- erste freie Antworten
+
+### 4.2 Phase B: Alltag
+
+Ziel:
+
+- Alltagskommunikation verbreitern
+- mehr Variabilität im Ausdruck
+- längere Gespräche verstehen und mittragen
+
+Dauer:
+
+- `8 bis 12 Wochen`
+
+Lernfokus:
+
+- Wohnen
+- Kinder
+- Gesundheit
+- Wege
+- Zeit
+- Organisation
+- soziale Situationen
+
+### 4.3 Phase C: Stabilisierung
+
+Ziel:
+
+- Inhalte langfristig verfügbar machen
+- alte Muster flexibel in neuen Situationen verwenden
+- freie Produktion deutlich stärken
+
+Dauer:
+
+- `3 bis 6 Monate`
+
+Lernfokus:
+
+- Mischtraining
+- Wiederaufnahme alter Inhalte
+- aktives Formulieren
+- Fehlerreduktion bei ähnlichen Mustern
+
+## 4.4 Konkrete Phasenlabels im System
+
+Für die technische Umsetzung werden drei feste Phasenlabels verwendet:
+
+- `quickstart`
+- `daily_life`
+- `stabilization`
+
+Diese Phasen sind nicht nur Beschreibung, sondern sollen in Lektionen gespeichert und in der UI sichtbar gemacht werden.
+
+### `quickstart`
+
+Zeitfenster:
+
+- ungefähr Woche `1 bis 6`
+
+Ziel:
+
+- schnell sprechfähige Kernmuster aufbauen
+
+Didaktischer Schwerpunkt:
+
+- häufige Phrasen
+- gebrauchsfertige Satzmuster
+- einfache Dialoge
+- wenig Grammatikballast
+
+Typische Modi:
+
+- `core_input`
+- `guided_dialogue`
+- erste `pattern_drill`
+
+### `daily_life`
+
+Zeitfenster:
+
+- ungefähr Woche `7 bis 18`
+
+Ziel:
+
+- Alltagssituationen verbreitern und kombinieren
+
+Didaktischer Schwerpunkt:
+
+- Familie
+- Besuch
+- Kinder
+- Gesundheit
+- Termine
+- Wege
+- soziale Reaktionen
+
+Typische Modi:
+
+- `guided_dialogue`
+- `pattern_drill`
+- `real_life_scenario`
+
+### `stabilization`
+
+Zeitfenster:
+
+- ab ungefähr Woche `19`
+
+Ziel:
+
+- Langzeitverfügbarkeit und Transfer
+
+Didaktischer Schwerpunkt:
+
+- Mischtraining
+- Abruf
+- Kontrastierung ähnlicher Muster
+- freie Produktion
+- blockübergreifende Wiederholung
+
+Typische Modi:
+
+- `intensive_review`
+- `checkpoint`
+- `real_life_scenario`
+
+## 4.5 Konkrete Blocklogik
+
+Die Inhalte werden in Blöcken organisiert.
+
+Empfohlene Größe je Block:
+
+- `6 bis 10` Inhaltslektionen
+- danach `1 bis 3` Konsolidierungslektionen
+- danach `1` Checkpoint
+
+Blockeinteilung für Bisaya:
+
+1. `Block 1`
+ - Begrüßung, Höflichkeit, Familie
+2. `Block 2`
+ - Zuhause, Essen, Fürsorge, Wege
+3. `Block 3`
+ - Alltag, Zeit, Preise, Organisation
+4. `Block 4`
+ - Gefühle, Gesundheit, Beziehungen
+5. `Block 5`
+ - Alltagsvertiefung und freie Situationen
+6. `Block 6+`
+ - Stabilisierung und Transfer
+
+## 5. Grundprinzipien für den Ausbau
+
+### 5.1 Nicht nach starrer Vokabelzahl planen
+
+Die Lernmenge pro Lektion wird nicht mehr pauschal über „x Vokabeln“ definiert.
+
+Stattdessen wird pro Lektion die `Lernlast` geschätzt.
+
+Einflussfaktoren:
+
+- Anzahl neuer Einheiten
+- Abstraktionsgrad
+- Nähe zu bereits Bekanntem
+- Grammatikkomplexität
+- Produktionsanforderung
+
+Empfehlung:
+
+- leichte Lektion: `6 bis 10` neue Einheiten
+- mittlere Lektion: `4 bis 7` neue Einheiten
+- schwere Lektion: `2 bis 5` neue Einheiten
+
+Eine Einheit kann sein:
+
+- Wort
+- Phrase
+- Satzmuster
+- Mini-Dialogformel
+
+### 5.2 Muster vor Einzelwörtern
+
+Bisaya soll stärker über gebrauchsfertige Muster gelernt werden.
+
+Beispiel:
+
+- nicht nur `kaon`
+- sondern `Nikaon na ka?`
+- `Kaon ta.`
+- `Gusto ka mokaon?`
+
+### 5.3 Jede Einheit durchläuft vier Stufen
+
+Jede wichtige Spracheinheit soll im Kurs mehrfach auftauchen:
+
+1. `Erkennen`
+2. `Abrufen`
+3. `Anwenden`
+4. `Variieren`
+
+Beispiel:
+
+- erkennen: richtige Übersetzung auswählen
+- abrufen: Phrase selbst tippen
+- anwenden: passende Dialogantwort wählen
+- variieren: ähnliche Situation frei beantworten
+
+## 6. Wiederholungsmodell
+
+## 6.1 Ziel
+
+Wiederholung soll intensiv genug sein, um Vergessen zu verhindern, aber nicht so dominant, dass Lernende stehenbleiben.
+
+## 6.2 Vier Ebenen der Wiederholung
+
+### A. Mikro-Wiederholung innerhalb derselben Lektion
+
+Neuer Stoff taucht in derselben Lektion mehrfach wieder auf, aber in anderer Form.
+
+Beispiel:
+
+- Einführung
+- Multiple Choice
+- Lückentext
+- Dialog
+- Sprechimpuls
+
+### B. Kurzintervall-Wiederholung
+
+Neue Inhalte werden nach kurzen Abständen erneut aufgegriffen:
+
+- nach `1 Tag`
+- nach `3 Tagen`
+- nach `7 Tagen`
+
+Allerdings nur, wenn sie noch nicht stabil wirken.
+
+### C. Intensiv-Wiederholungsphasen
+
+Nach jedem inhaltlichen Block folgt eine gezielte Konsolidierungsphase.
+
+Empfehlung:
+
+- `2 Wochen Aufbau`
+- danach `3 bis 5 Tage Intensiv-Wiederholung`
+
+Merkmale:
+
+- kaum neuer Stoff
+- hoher Abrufanteil
+- viele Mischaufgaben
+- mehr aktive Produktion
+
+### D. Spiral-Wiederholung
+
+Frühere Inhalte kommen in späteren Modulen wieder, aber in neuen Kontexten.
+
+Nicht:
+
+- dieselbe Karte nochmal
+
+Sondern:
+
+- neues Umfeld
+- neue Satzkombination
+- neue Gesprächssituation
+
+Beispiel:
+
+- `Nanay = Mutter`
+- später:
+ - `Asa si Nanay?`
+ - `Nikaon na si Nanay?`
+ - `Si Nanay naa sa balay.`
+
+## 6.3 Zielverhältnis neu vs. alt
+
+Die App soll neue und alte Inhalte dynamisch mischen.
+
+Empfohlene Verteilung:
+
+- Start einer neuen Lektionsphase:
+ - `70 % neu`
+ - `30 % alt`
+
+- Mitte eines Blocks:
+ - `50 % neu`
+ - `50 % alt`
+
+- kurz vor Blockabschluss:
+ - `30 % neu`
+ - `70 % alt`
+
+- Intensiv-Wiederholungsphase:
+ - `10 % neu`
+ - `90 % alt`
+
+Wichtig:
+
+Nicht alles Alte wiederholen, sondern vor allem:
+
+- fällige Inhalte
+- oft falsch beantwortete Inhalte
+- ähnliche/verwechselbare Inhalte
+- Kernmuster mit hoher Alltagshäufigkeit
+
+## 7. Neue Kursstruktur für Bisaya
+
+Der Ausbau soll den Kurs in inhaltliche Module statt nur in lineare Wochen gliedern.
+
+## 7.1 Phase A: Schnellstart
+
+### Modul 1: Begrüßen, reagieren, höflich sein
+
+- Begrüßungen
+- Verabschiedungen
+- Danke / Bitte
+- nach dem Befinden fragen
+- einfache Reaktionen
+
+### Modul 2: Familie und Nähe
+
+- Familienbezeichnungen
+- Anredeformen
+- Fürsorge
+- Nähe und Zuneigung
+
+### Modul 3: Zuhause und Alltag
+
+- Haus
+- Räume
+- Gegenstände
+- Routinen
+- Essen und Trinken
+
+### Modul 4: Unterwegs und organisieren
+
+- Richtungen
+- Orte
+- Fragen nach Wegen
+- Zeit und Tagesablauf
+- Zahlen, Preise, Einkauf
+
+### Modul 5: Schnellstart-Konsolidierung
+
+- große Wiederholungsblöcke
+- Mischdialoge
+- erste freie Aufgaben
+- blockübergreifender Checkpoint
+
+## 7.2 Phase B: Alltag
+
+### Modul 6: Familie, Kinder, Besuch
+
+- mit Verwandten sprechen
+- Kinder und Betreuung
+- Besuche und gemeinsamer Alltag
+
+### Modul 7: Gefühle, Bedürfnisse, soziale Reaktionen
+
+- Sorgen
+- Freude
+- Müdigkeit
+- Hunger
+- Unsicherheit
+- Trost und Reaktion
+
+### Modul 8: Gesundheit und Hilfe
+
+- Beschwerden
+- Medikamente
+- Arzt / Hilfe holen
+- erklären, was los ist
+
+### Modul 9: Termine, Vergangenheit, Zukunft
+
+- gestern / heute / morgen
+- Vorhaben
+- einfache Erzählung
+- Vergangenes und Zukünftiges im Alltag
+
+### Modul 10: Alltag stabilisieren
+
+- längere Szenarien
+- Rollenspiele
+- Fehlerspots
+- Wortschatzfestigung
+
+## 7.3 Phase C: Stabilisierung
+
+### Modul 11: Freies Alltagsgespräch
+
+- längere Reaktionsketten
+- Folgefragen
+- Umschreiben
+- Rückfragen
+
+### Modul 12: Tiefenwiederholung
+
+- Kernmuster aus Phase A und B
+- Kontrasttraining
+- häufige Fehler
+- aktive Produktionsphasen
+
+### Modul 13: Praxis- und Transferphase
+
+- offene Situationen
+- variierte Antworten
+- Mini-Gespräche
+- Abschlussdiagnosen
+
+## 8. Lektionstypen
+
+Die bisherigen Typen `vocab`, `conversation`, `grammar`, `review`, `culture` reichen technisch als Basis, sind didaktisch aber zu grob.
+
+Empfohlen ist eine Erweiterung um fachliche Typen.
+
+### Neue didaktische Typen
+
+- `core_input`
+- `pattern_drill`
+- `guided_dialogue`
+- `recall_session`
+- `contrast_session`
+- `real_life_scenario`
+- `intensive_review`
+- `checkpoint`
+
+Diese können technisch entweder:
+
+- als neue `lessonType`-Werte eingeführt werden
+
+oder:
+
+- als neues Feld `didactic_mode`
+
+Erste Empfehlung:
+
+- bestehendes `lessonType` beibehalten
+- zusätzlich `didacticMode` ergänzen
+
+So bleiben bestehende Inhalte kompatibel.
+
+## 9. Vorschlag für Datenmodell-Erweiterung
+
+Aktuell vorhanden in [vocab_course_lesson.js](/mnt/share/torsten/Programs/YourPart3/backend/models/community/vocab_course_lesson.js):
+
+- `weekNumber`
+- `dayNumber`
+- `lessonType`
+- `learningGoals`
+- `corePatterns`
+- `grammarFocus`
+- `speakingPrompts`
+- `practicalTasks`
+- `targetMinutes`
+- `targetScorePercent`
+- `requiresReview`
+
+Für den Ausbau werden zusätzliche Felder empfohlen.
+
+## 9.1 Neue Felder auf Lektionsebene
+
+- `difficulty_weight`
+ - numerischer Schwierigkeitswert
+ - Grundlage für adaptive Freigabe
+
+- `new_unit_target`
+ - wie viele neue Einheiten diese Lektion ungefähr einführt
+
+- `review_weight`
+ - wie stark Wiederholung hier dominieren soll
+
+- `didactic_mode`
+ - z. B. `core_input`, `intensive_review`, `checkpoint`
+
+- `block_number`
+ - ordnet Lektionen einem Konsolidierungsblock zu
+
+- `phase_label`
+ - z. B. `quickstart`, `daily_life`, `stabilization`
+
+## 9.2 Neue Felder auf Kurs- oder Fortschrittsebene
+
+Empfehlung für `course progress` bzw. neue itembasierte Progress-Tabelle:
+
+- `item_strength`
+- `error_rate`
+- `last_seen_at`
+- `next_due_at`
+- `confusion_group`
+- `production_level_reached`
+
+Wichtig:
+
+Langfristig reicht reiner Lektionsfortschritt nicht aus. Sprachlernen muss itembasiert werden.
+
+## 10. Fortschrittslogik
+
+Die Kursseite in [VocabCourseView.vue](/mnt/share/torsten/Programs/YourPart3/frontend/src/views/social/VocabCourseView.vue) ist aktuell stark linear aufgebaut:
+
+- Lektion 1
+- Lektion 2
+- Lektion 3
+
+Für den Ausbau gilt:
+
+- lineare Struktur darf bleiben
+- aber die eigentliche Tageslogik darf nicht nur „nächste Lektion“ sein
+
+## 10.1 Neuer Tagesablauf
+
+Der empfohlene Tagesablauf:
+
+1. `fällige Wiederholungen`
+2. `Intensivphase`, falls aktiv
+3. `neuer Stoff`, wenn Kapazität frei
+4. `kurze Abschlussaktivierung`
+
+## 10.2 Freigabe neuer Inhalte
+
+Neue Lektionen werden nicht mehr nur über die vorige Lektion freigeschaltet.
+
+Stattdessen soll die Freigabe berücksichtigen:
+
+- ob kritische Wiederholungen offen sind
+- ob Kernmuster des letzten Blocks grob sitzen
+- ob der Nutzer gerade in einer Intensivphase ist
+
+Aber:
+
+- keine totale Blockade
+- Fortschritt muss motivierend bleiben
+
+Empfehlung:
+
+- `soft gate` statt `hard gate`
+
+## 11. Trainerlogik
+
+Die Traineransicht soll drei Zustände unterscheiden:
+
+- `neu`
+- `wiederholen`
+- `vertiefen`
+
+## 11.1 Vertiefen ist kein normales Wiederholen
+
+`Vertiefen` bedeutet:
+
+- alte Inhalte in neuer Form
+- mehr Produktion
+- mehr Kontrast
+- mehr Dialognähe
+
+Beispiele:
+
+- aus MC wird freie Antwort
+- aus Wortpaar wird Dialogergänzung
+- aus kurzer Phrase wird Mini-Szenario
+
+## 11.2 Intensiv-Wiederholungsphase
+
+Während einer Intensivphase sollen Nutzer nicht das Gefühl haben, festzustecken.
+
+Darum braucht die Oberfläche:
+
+- klare Kennzeichnung
+- sichtbaren Sinn
+- sichtbares Ende
+
+Beispiel:
+
+- `Intensivphase 2/4`
+- `Heute festigst du Woche 1 und 2`
+- `Danach öffnet sich Modul 3`
+
+## 12. UI-Anpassungen
+
+## 12.1 Kursübersicht
+
+In [VocabCourseView.vue](/mnt/share/torsten/Programs/YourPart3/frontend/src/views/social/VocabCourseView.vue):
+
+- statt nur `x Lektionen`
+- auch sichtbar machen:
+ - Phase
+ - Modul
+ - Intensivblöcke
+ - Checkpoints
+
+## 12.2 Lektion
+
+In [VocabLessonView.vue](/mnt/share/torsten/Programs/YourPart3/frontend/src/views/social/VocabLessonView.vue):
+
+- stärker unterscheiden zwischen:
+ - neuer Stoff
+ - vertiefender Stoff
+ - Wiederholung
+ - Intensivphase
+
+- sichtbare Lernabsicht pro Lektion:
+ - `Heute neu`
+ - `Heute festigen`
+ - `Heute abrufen`
+
+## 12.3 Startseite für Sprachkurse
+
+Empfohlen:
+
+- `Heute fällig`
+- `Heute neu möglich`
+- `kritische Muster`
+- `nächster Intensivblock`
+
+## 13. Inhalte für Bisaya konkret
+
+Für Bisaya sollen zunächst diese Ausbaupakete entstehen.
+
+## Paket A: Schnellstart vertiefen
+
+Ziel:
+
+- bestehenden 4-Wochen-Kurs auf `6 Wochen` erweitern
+- mehr Muster
+- mehr freie Antworten
+- erste echte Intensivphasen
+
+Umfang:
+
+- vorhandene Lektionen überarbeiten
+- zusätzliche Lektionen für:
+ - Hör-/Sprechmuster
+ - Kontrasttraining
+ - Alltagsszenarien
+
+## Paket B: Alltagsmodule ergänzen
+
+Neue Module:
+
+- Besuche und Gastgeber-Situationen
+- Kinder und Familie
+- Gesundheit
+- Erledigungen und Termine
+- soziale Fragen und Hilfen
+
+## Paket C: Stabilisierung
+
+Neue Blöcke:
+
+- Dialogtage
+- Mischtraining
+- Fehlerschwerpunkte
+- große Wiederholungsphasen
+
+## 14. Empfohlene Umsetzung in Phasen
+
+## Phase 1: Didaktische Infrastruktur
+
+Ziel:
+
+- System auf adaptive Kurslogik vorbereiten
+
+Umfang:
+
+- neue didaktische Felder ergänzen
+- Intensivphasen im Progress-Modell vorbereiten
+- UI-Kennzeichnung für `neu`, `wiederholen`, `vertiefen`
+
+Konkreter Lieferumfang:
+
+- Phase- und Fokusfelder auf Lektionsebene
+- Datenbankmigration
+- SQL-Skript für manuelle Einspielung
+- Service-Fallbacks für bestehende Lektionen ohne gepflegte Felder
+- Kursübersicht zeigt Phase, Fokus, Block und Intensivwiederholung
+- Lektionsansicht zeigt Phase, Fokus, neue Einheiten und Wiederholungsgewicht
+
+Bewusst noch nicht enthalten:
+
+- adaptive itembasierte Terminierung
+- neue große Bisaya-Contentpakete
+- kompletter Umbau der Progress-Logik
+
+## Phase 2: Bestehenden Bisaya-Kurs überarbeiten
+
+Ziel:
+
+- vorhandenen Kurs nicht wegwerfen, sondern fachlich umbauen
+
+Umfang:
+
+- 40 Lektionen neu clustern
+- Intensivblöcke einziehen
+- leichte und schwere Lektionen neu gewichten
+- mehr Produktionsaufgaben ergänzen
+
+Konkrete Zuordnung für den bestehenden 4-Wochen-Kurs:
+
+1. `Block 1 / quickstart / Lektion 1-10`
+ - Begrüßung, Überlebenssätze, Familie, Fürsorge
+ - Abschluss mit `Woche 1 - Wiederholung` als `intensive_review`
+ - Abschluss mit `Woche 1 - Vokabeltest` als `checkpoint`
+2. `Block 2 / quickstart / Lektion 11-20`
+ - Alltag, Wege, Zeit, Einkauf
+ - Abschluss mit `Woche 2 - Wiederholung` als `intensive_review`
+ - Abschluss mit `Woche 2 - Vokabeltest` als `checkpoint`
+3. `Block 3 / daily_life / Lektion 21-30`
+ - Gefühle, Gesundheit, Höflichkeit, Kinder
+ - Abschluss mit `Woche 3 - Wiederholung` als `intensive_review`
+ - Abschluss mit `Woche 3 - Vokabeltest` als `checkpoint`
+4. `Block 4 / stabilization / Lektion 31-40`
+ - freie Gespräche, Transfer, große Wiederholungswellen
+ - `32`, `34` und `36` als intensive Wiederholungsphasen
+ - `38` und `39` als abschließende Checkpoints
+
+Technische Umsetzung von Phase 2:
+
+- neues Mapping für alle 40 Bisaya-Lektionen in
+ - [bisaya-course-phase2-pedagogy.js](/mnt/share/torsten/Programs/YourPart3/backend/scripts/bisaya-course-phase2-pedagogy.js)
+- Erzeugung neuer Kurse schreibt diese Felder direkt mit
+ - [create-bisaya-course.js](/mnt/share/torsten/Programs/YourPart3/backend/scripts/create-bisaya-course.js)
+- Refresh bestehender Kurse pflegt dieselben Werte nach
+ - [apply-bisaya-course-refresh.js](/mnt/share/torsten/Programs/YourPart3/backend/scripts/apply-bisaya-course-refresh.js)
+ - [update-bisaya-didactics.js](/mnt/share/torsten/Programs/YourPart3/backend/scripts/update-bisaya-didactics.js)
+- manuelles SQL-Backfill für bestehende Datenbanken
+ - [backfill_bisaya_phase2_pedagogy.sql](/mnt/share/torsten/Programs/YourPart3/backend/sql/backfill_bisaya_phase2_pedagogy.sql)
+
+## Phase 3: Kurs in 6-Wochen-Schnellstart ausbauen
+
+Ziel:
+
+- aktuelles Niveau sinnvoll vertiefen
+
+Umfang:
+
+- zusätzliche Lektionen
+- mehr Dialoge
+- mehr Abruftraining
+- mehr Spiralwiederholung
+
+Konkrete Umsetzung:
+
+1. `Woche 5`
+ - Besuch & Gastfreundschaft
+ - Besuch & Haushalt
+ - Fragen im Alltag vertiefen
+ - Termine & Verabredungen
+ - zwei intensive Wiederholungswellen plus Checkpoint
+2. `Woche 6`
+ - Unterwegs & Transport
+ - Arbeit & Aufgaben
+ - freies Gespräch zu Familie und Alltag
+ - Konflikte & Missverständnisse
+ - große Spiralwiederholung
+ - Schnellstart-Abschlusstest und Abschlussprüfung
+
+Technische Umsetzung:
+
+- neue Lektionen `41` bis `60` in
+ - [bisaya-course-phase3-extension.js](/mnt/share/torsten/Programs/YourPart3/backend/scripts/bisaya-course-phase3-extension.js)
+- bestehender Kursgenerator erweitert
+ - [create-bisaya-course.js](/mnt/share/torsten/Programs/YourPart3/backend/scripts/create-bisaya-course.js)
+- Nachrüstskript für bestehende Kurse
+ - [extend-bisaya-course-phase3.js](/mnt/share/torsten/Programs/YourPart3/backend/scripts/extend-bisaya-course-phase3.js)
+- SQL für Titel-/Beschreibungsumstellung
+ - [extend_bisaya_course_phase3_to_6_weeks.sql](/mnt/share/torsten/Programs/YourPart3/backend/sql/extend_bisaya_course_phase3_to_6_weeks.sql)
+
+## Phase 4: Alltagsphase ergänzen
+
+Ziel:
+
+- aus Einstieg einen echten Alltagskurs machen
+
+Umfang:
+
+- neue Module 6 bis 10
+- Ausbau auf 3-Monats-Pfad
+
+Konkrete Umsetzung:
+
+1. `Woche 7`
+ - Kinder im Alltag
+ - Schule & Betreuung
+ - Hausaufgaben, Routine und Spiel
+2. `Woche 8`
+ - Arzt, Apotheke und Pflege
+ - Beschwerden, Hilfe und Genesung
+3. `Woche 9`
+ - Einkaufen vertiefen
+ - Markt, Bank, Dokumente und Behördengänge
+4. `Woche 10`
+ - Nachbarschaft, Hilfe, Einladungen und soziale Reaktionen
+ - Konflikte und Gefühle im Gespräch
+5. `Woche 11`
+ - Zuhause organisieren
+ - Haushalt, Reparaturen, Planung und Alltagslogistik
+6. `Woche 12`
+ - freie Alltagsgespräche
+ - Mischtraining
+ - Fehlerschwerpunkte
+ - Abschlussprüfung der Alltagsphase
+
+Technische Umsetzung:
+
+- neue Lektionen `61` bis `120` in
+ - [bisaya-course-phase4-extension.js](/mnt/share/torsten/Programs/YourPart3/backend/scripts/bisaya-course-phase4-extension.js)
+- Pädagogik-Mapping erweitert bis Lektion `120`
+ - [bisaya-course-phase2-pedagogy.js](/mnt/share/torsten/Programs/YourPart3/backend/scripts/bisaya-course-phase2-pedagogy.js)
+- bestehender Kursgenerator erweitert
+ - [create-bisaya-course.js](/mnt/share/torsten/Programs/YourPart3/backend/scripts/create-bisaya-course.js)
+- Nachrüstskript für bestehende Kurse
+ - [extend-bisaya-course-phase4.js](/mnt/share/torsten/Programs/YourPart3/backend/scripts/extend-bisaya-course-phase4.js)
+- SQL für Titel-/Beschreibungsumstellung
+ - [extend_bisaya_course_phase4_to_3_months.sql](/mnt/share/torsten/Programs/YourPart3/backend/sql/extend_bisaya_course_phase4_to_3_months.sql)
+
+## Phase 5: Stabilisierungsphase ergänzen
+
+Ziel:
+
+- langfristige Verfügbarkeit
+
+Umfang:
+
+- Tiefenwiederholung
+- freie Produktion
+- große Misch- und Checkpointformate
+
+Konkrete Umsetzung:
+
+1. `Woche 13`
+ - erster Stabilisierungsblock
+ - große Dialogtage
+ - Fehlerschwerpunkte
+ - freie Produktion
+2. `Woche 14`
+ - zweiter Stabilisierungsblock
+ - erneutes Fehlertraining
+ - mehr Rollenspiele und Transfer
+3. `Woche 15`
+ - großes Mischreview
+ - Langzeitreview
+ - Gesamtabschluss des Pfads
+
+Technische Umsetzung:
+
+- neue Lektionen `121` bis `150` in
+ - [bisaya-course-phase5-extension.js](/mnt/share/torsten/Programs/YourPart3/backend/scripts/bisaya-course-phase5-extension.js)
+- Pädagogik-Mapping erweitert bis Lektion `150`
+ - [bisaya-course-phase2-pedagogy.js](/mnt/share/torsten/Programs/YourPart3/backend/scripts/bisaya-course-phase2-pedagogy.js)
+- bestehender Kursgenerator erweitert
+ - [create-bisaya-course.js](/mnt/share/torsten/Programs/YourPart3/backend/scripts/create-bisaya-course.js)
+- Nachrüstskript für bestehende Kurse
+ - [extend-bisaya-course-phase5.js](/mnt/share/torsten/Programs/YourPart3/backend/scripts/extend-bisaya-course-phase5.js)
+- SQL für Titel-/Beschreibungsumstellung
+ - [extend_bisaya_course_phase5_to_stabilization.sql](/mnt/share/torsten/Programs/YourPart3/backend/sql/extend_bisaya_course_phase5_to_stabilization.sql)
+
+## 15. Konkrete nächste technische Schritte
+
+### 15.1 Backend
+
+- Migration für zusätzliche Lektionsfelder
+- Anpassung der Course-/Lesson-APIs
+- Vorbereitung itembasierten Fortschritts
+
+### 15.1.1 Echte SQLs für Phase 1
+
+Die SQL-Datei liegt in:
+
+- [add_vocab_lesson_phase1_fields.sql](/mnt/share/torsten/Programs/YourPart3/backend/sql/add_vocab_lesson_phase1_fields.sql)
+
+Verwendeter SQL-Block:
+
+```sql
+ALTER TABLE community.vocab_course_lesson
+ADD COLUMN IF NOT EXISTS didactic_mode TEXT,
+ADD COLUMN IF NOT EXISTS phase_label TEXT,
+ADD COLUMN IF NOT EXISTS block_number INTEGER,
+ADD COLUMN IF NOT EXISTS difficulty_weight INTEGER,
+ADD COLUMN IF NOT EXISTS new_unit_target INTEGER,
+ADD COLUMN IF NOT EXISTS review_weight INTEGER,
+ADD COLUMN IF NOT EXISTS is_intensive_review BOOLEAN NOT NULL DEFAULT FALSE;
+```
+
+Kommentare:
+
+```sql
+COMMENT ON COLUMN community.vocab_course_lesson.didactic_mode IS
+ 'Didaktischer Modus der Lektion, z.B. core_input, guided_dialogue, intensive_review oder checkpoint.';
+COMMENT ON COLUMN community.vocab_course_lesson.phase_label IS
+ 'Übergeordnete Lernphase, z.B. quickstart, daily_life oder stabilization.';
+COMMENT ON COLUMN community.vocab_course_lesson.block_number IS
+ 'Inhaltlicher Block für Konsolidierungs- und Wiederholungswellen.';
+COMMENT ON COLUMN community.vocab_course_lesson.difficulty_weight IS
+ 'Grobe relative Schwierigkeit der Lektion von leicht bis schwer.';
+COMMENT ON COLUMN community.vocab_course_lesson.new_unit_target IS
+ 'Empfohlene Zahl neuer Spracheinheiten in dieser Lektion.';
+COMMENT ON COLUMN community.vocab_course_lesson.review_weight IS
+ 'Wie stark Wiederholung in dieser Lektion dominieren soll, typischerweise 0 bis 100.';
+COMMENT ON COLUMN community.vocab_course_lesson.is_intensive_review IS
+ 'Markiert Lektionen, die als intensive Wiederholungsphase gedacht sind.';
+```
+
+### 15.1.2 Echte SQLs für Phase 2
+
+Die SQL-Datei liegt in:
+
+- [backfill_bisaya_phase2_pedagogy.sql](/mnt/share/torsten/Programs/YourPart3/backend/sql/backfill_bisaya_phase2_pedagogy.sql)
+
+Sie pflegt für bestehende Bisaya-Kurse die Felder
+
+- `phase_label`
+- `block_number`
+- `didactic_mode`
+- `difficulty_weight`
+- `new_unit_target`
+- `review_weight`
+- `is_intensive_review`
+
+für die Lektionen `1` bis `40` explizit nach.
+
+### 15.2 Frontend
+
+- Kursübersicht mit Phasen- und Blockdarstellung
+- Traineransicht mit Zustand `neu / vertiefen / wiederholen`
+- Kennzeichnung von Intensivphasen
+
+### 15.3 Content
+
+- Bestandsaudit des Bisaya-Kurses
+- Definition der Kernmuster pro Modul
+- Ausbau der Lektionen um Kontrast- und Produktionsaufgaben
+
+## 16. Entscheidungsempfehlung
+
+Empfohlen ist:
+
+1. den bisherigen Kurs offiziell als `Schnellstart` einordnen
+2. die Wiederholungslogik zuerst verbessern
+3. erst danach große Mengen neuer Inhalte hinzufügen
+
+Begründung:
+
+Ein größerer Kurs ohne bessere Konsolidierung verschärft nur das bestehende Problem.
+
+Die richtige Reihenfolge ist:
+
+- erst didaktische Struktur
+- dann Ausbau
+- dann Langzeitstabilisierung
+
+## 17. Priorisierte erste Lieferung
+
+Wenn nur ein begrenzter erster Schritt umgesetzt werden soll, dann dieser:
+
+1. Bisaya-Kurs auf `6 Wochen` erweitern
+2. nach je 2 Wochen eine `Intensiv-Wiederholungsphase` einbauen
+3. Trainer technisch auf `neu / vertiefen / wiederholen` umstellen
+4. die aktuellen 40 Lektionen nach Lernlast neu gewichten
+
+Das ist der kleinste sinnvolle Ausbau, der didaktisch bereits einen echten Unterschied macht.
diff --git a/frontend/src/i18n/locales/de/admin.json b/frontend/src/i18n/locales/de/admin.json
index e76d231..7eedd29 100644
--- a/frontend/src/i18n/locales/de/admin.json
+++ b/frontend/src/i18n/locales/de/admin.json
@@ -176,17 +176,33 @@
"statusActive": "Schwanger bis",
"statusNone": "Nicht schwanger",
"fatherId": "Vater-Charakter-ID (optional)",
+ "fatherSelect": "Vater (Ehepartner / Verlobter / Liebhaber)",
+ "fatherNone": "— kein Vater gespeichert —",
+ "fatherHintList": "Liste aus Beziehungen dieses Charakters (Ehe, Verlobung, aktive Liebschaft).",
+ "fatherHintManual": "Kein passender Partner in der Datenbank: Vater-Charakter-ID manuell eintragen.",
+ "fatherManualPlaceholder": "Charakter-ID",
"dueDays": "Tage bis zum Termin",
+ "dueDaysHint": "0 = Termin heute (Geburt kann je nach Spiel-Logik zeitnah anstehen).",
"force": "Schwangerschaft setzen",
"clear": "Schwangerschaft entfernen",
"successForce": "Schwangerschaft wurde gesetzt.",
"successClear": "Schwangerschaft wurde entfernt.",
- "error": "Aktion fehlgeschlagen."
+ "error": "Aktion fehlgeschlagen.",
+ "relationship": {
+ "married": "Ehepartner",
+ "engaged": "Verlobter",
+ "lover": "Liebhaber"
+ }
},
"birth": {
"title": "Geburt erzwingen (Admin)",
"motherHint": "Es wird der oben genannte Charakter (Mutter) verwendet.",
"fatherId": "Vater-Charakter-ID",
+ "fatherSelect": "Vater (Ehepartner / Verlobter / Liebhaber)",
+ "fatherChoose": "— Vater wählen —",
+ "fatherHintList": "Liste aus Beziehungen dieses Charakters.",
+ "fatherHintManual": "Kein Partner in der Liste: Vater-Charakter-ID manuell eintragen.",
+ "fatherRequired": "Bitte einen Vater auswählen oder die Charakter-ID angeben.",
"context": "Kontext",
"contextMarriage": "Ehe",
"contextLover": "Liebschaft",
diff --git a/frontend/src/i18n/locales/en/admin.json b/frontend/src/i18n/locales/en/admin.json
index 3072668..987bb50 100644
--- a/frontend/src/i18n/locales/en/admin.json
+++ b/frontend/src/i18n/locales/en/admin.json
@@ -231,17 +231,33 @@
"statusActive": "Expecting until",
"statusNone": "Not pregnant",
"fatherId": "Father character ID (optional)",
+ "fatherSelect": "Father (spouse / fiancé(e) / lover)",
+ "fatherNone": "— no father stored —",
+ "fatherHintList": "From this character’s relationships (marriage, engagement, active affair).",
+ "fatherHintManual": "No matching partner in the database: enter the father’s character ID manually.",
+ "fatherManualPlaceholder": "Character ID",
"dueDays": "Days until due date",
+ "dueDaysHint": "0 = due today (birth may follow depending on game logic).",
"force": "Set pregnancy",
"clear": "Clear pregnancy",
"successForce": "Pregnancy has been set.",
"successClear": "Pregnancy has been cleared.",
- "error": "Action failed."
+ "error": "Action failed.",
+ "relationship": {
+ "married": "Spouse",
+ "engaged": "Engaged partner",
+ "lover": "Lover"
+ }
},
"birth": {
"title": "Force birth (admin)",
"motherHint": "The character listed above is used as the mother.",
"fatherId": "Father character ID",
+ "fatherSelect": "Father (spouse / fiancé(e) / lover)",
+ "fatherChoose": "— choose father —",
+ "fatherHintList": "From this character’s relationships.",
+ "fatherHintManual": "No partner in the list: enter the father’s character ID manually.",
+ "fatherRequired": "Please select a father or enter the character ID.",
"context": "Context",
"contextMarriage": "Marriage",
"contextLover": "Affair",
diff --git a/frontend/src/i18n/locales/es/admin.json b/frontend/src/i18n/locales/es/admin.json
index 4780dca..479bf88 100644
--- a/frontend/src/i18n/locales/es/admin.json
+++ b/frontend/src/i18n/locales/es/admin.json
@@ -176,17 +176,33 @@
"statusActive": "Embarazo hasta",
"statusNone": "No embarazada",
"fatherId": "ID del padre (opcional)",
+ "fatherSelect": "Padre (cónyuge / prometido / amante)",
+ "fatherNone": "— sin padre guardado —",
+ "fatherHintList": "Según las relaciones de este personaje (matrimonio, prometido, amante activo).",
+ "fatherHintManual": "Sin pareja adecuada en la base de datos: introduce manualmente el ID del padre.",
+ "fatherManualPlaceholder": "ID de personaje",
"dueDays": "Días hasta el parto previsto",
+ "dueDaysHint": "0 = parto previsto hoy (el nacimiento puede seguir según la lógica del juego).",
"force": "Establecer embarazo",
"clear": "Quitar embarazo",
"successForce": "Embarazo establecido.",
"successClear": "Embarazo eliminado.",
- "error": "La acción ha fallado."
+ "error": "La acción ha fallado.",
+ "relationship": {
+ "married": "Cónyuge",
+ "engaged": "Prometido",
+ "lover": "Amante"
+ }
},
"birth": {
"title": "Forzar nacimiento (admin)",
"motherHint": "Se usa el personaje indicado arriba como madre.",
"fatherId": "ID del padre",
+ "fatherSelect": "Padre (cónyuge / prometido / amante)",
+ "fatherChoose": "— elegir padre —",
+ "fatherHintList": "Según las relaciones de este personaje.",
+ "fatherHintManual": "Sin pareja en la lista: introduce el ID del padre manualmente.",
+ "fatherRequired": "Elige un padre o introduce el ID de personaje.",
"context": "Contexto",
"contextMarriage": "Matrimonio",
"contextLover": "Amante",
diff --git a/frontend/src/views/admin/falukant/EditUserView.vue b/frontend/src/views/admin/falukant/EditUserView.vue
index eadc376..436308a 100644
--- a/frontend/src/views/admin/falukant/EditUserView.vue
+++ b/frontend/src/views/admin/falukant/EditUserView.vue
@@ -54,13 +54,28 @@
{{ $t('admin.falukant.edituser.pregnancy.statusNone') }}
+ {{ $t('admin.falukant.edituser.pregnancy.fatherHintList') }}
+ {{ $t('admin.falukant.edituser.pregnancy.fatherHintManual') }}
+ {{ $t('admin.falukant.edituser.pregnancy.dueDaysHint') }}
+
+ {{ getPhaseLabel(lesson.pedagogy.phaseLabel) }}
+ {{ getDidacticModeLabel(lesson.pedagogy.didacticMode) }}
+ Block {{ lesson.pedagogy.blockNumber }}
+ Intensive Wiederholung
+
+
+ Phase
+ {{ getPhaseLabel(lessonPedagogy.phaseLabel) }}
+
+
+ Fokus
+ {{ getDidacticModeLabel(lessonPedagogy.didacticMode) }}
+
{{ $t('socialnetwork.vocab.courses.recommendedDuration') }}
{{ formatTargetMinutes(lesson.targetMinutes) }}
@@ -51,9 +59,22 @@
{{ $t('socialnetwork.vocab.courses.exerciseLoad') }}
{{ effectiveExercises?.length || 0 }} {{ $t('socialnetwork.vocab.courses.exercisesShort') }}
+
+ Neue Einheiten
+ {{ lessonPedagogy.newUnitTarget }}
+
+
+ Wiederholung
+ {{ lessonPedagogy.reviewWeight }}%
+
+
+
Intensive Wiederholungsphase
+
Diese Lektion priorisiert Wiederholung und Vertiefung. Neuer Stoff wird bewusst reduziert, damit vorhandene Muster stabil werden.
+
+
{{ $t('socialnetwork.vocab.courses.lessonDescription') }}
@@ -872,6 +893,17 @@ export default {
practicalTasks: []
};
},
+ lessonPedagogy() {
+ return this.lesson?.pedagogy || {
+ didacticMode: null,
+ phaseLabel: null,
+ blockNumber: null,
+ difficultyWeight: null,
+ newUnitTarget: null,
+ reviewWeight: null,
+ isIntensiveReview: false
+ };
+ },
assistantAvailable() {
if (!this.assistantSettings) {
return false;
@@ -1280,6 +1312,36 @@ export default {
};
return labels[lessonType] || lessonType || this.$t('socialnetwork.vocab.courses.lessonTypeVocab');
},
+ getPhaseLabel(phaseLabel) {
+ switch (phaseLabel) {
+ case 'quickstart':
+ return 'Schnellstart';
+ case 'daily_life':
+ return 'Alltag';
+ case 'stabilization':
+ return 'Stabilisierung';
+ default:
+ return 'Lernphase';
+ }
+ },
+ getDidacticModeLabel(didacticMode) {
+ switch (didacticMode) {
+ case 'core_input':
+ return 'Neuer Stoff';
+ case 'guided_dialogue':
+ return 'Geführter Dialog';
+ case 'pattern_drill':
+ return 'Mustertraining';
+ case 'real_life_scenario':
+ return 'Alltagsszenario';
+ case 'intensive_review':
+ return 'Wiederholungsphase';
+ case 'checkpoint':
+ return 'Checkpoint';
+ default:
+ return 'Lernfokus';
+ }
+ },
formatTargetMinutes(targetMinutes) {
const minutes = Number(targetMinutes);
if (!minutes) {
@@ -2187,6 +2249,24 @@ export default {
color: #7a6848;
}
+.lesson-intensity-banner {
+ margin-bottom: 18px;
+ padding: 14px 16px;
+ border-radius: 12px;
+ border: 1px solid rgba(207, 78, 78, 0.24);
+ background: rgba(255, 242, 242, 0.95);
+ color: #8e3d3d;
+}
+
+.lesson-intensity-banner strong,
+.lesson-intensity-banner p {
+ margin: 0;
+}
+
+.lesson-intensity-banner p {
+ margin-top: 6px;
+}
+
.learn-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));