feat: erweitere Didaktik-Fragmente und verbessere die Übersetzungen in mehreren Sprachen
All checks were successful
Deploy to production / deploy (push) Successful in 2m6s

This commit is contained in:
Torsten Schulz (local)
2026-05-21 14:45:22 +02:00
parent fa6164f93f
commit 8e5b990cf5
8 changed files with 196 additions and 13 deletions

View File

@@ -154,6 +154,7 @@ export const BISAYA_DIDACTICS_FRAGMENTS = {
{ target: 'Dinhi ko.', gloss: 'Ich bin hier.' },
{ target: 'Naa ko dinhi.', gloss: 'Ich bin hier.' },
{ target: 'Didto siya.', gloss: 'Er/sie ist dort.' },
{ target: 'Adto ko sa merkado.', gloss: 'Ich gehe zum Markt.' },
{ target: 'Adto ta didto.', gloss: 'Lass uns dorthin gehen.' },
{ target: 'Padulong ko sa merkado.', gloss: 'Ich bin auf dem Weg zum Markt.' },
{ target: 'Padulong ta didto.', gloss: 'Lass uns dorthin aufbrechen.' },
@@ -169,6 +170,16 @@ export const BISAYA_DIDACTICS_FRAGMENTS = {
title: 'dinhi / didto',
text: '„dinhi“ = hier, „didto“ = dort (weg vom Sprecher).',
example: 'Naa ko dinhi. Didto ang simbahan.'
},
{
title: 'Naa + Person + Ort',
text: 'Mit „naa“ sagst du, dass jemand an einem Ort ist. Danach steht der Ort oder das Ortswort.',
example: 'Naa ko dinhi. Naa siya didto.'
},
{
title: 'adto vs. padulong',
text: '„adto“ ist gehen/fahren zu einem Ziel. „padulong“ betont, dass jemand bereits unterwegs in diese Richtung ist.',
example: 'Adto ko sa merkado. Padulong ko sa merkado.'
}
],
speakingPrompts: [

View File

@@ -0,0 +1,77 @@
#!/usr/bin/env node
import fs from 'fs';
import path from 'path';
function readFile(p) {
return fs.readFileSync(path.resolve(p), 'utf8');
}
const base = readFile('./backend/scripts/create-bisaya-course.js');
const bisaya = readFile('./backend/scripts/bisaya-course-plan-24-43.js');
function extractLessons(text) {
const lessons = new Map();
const re = /\{[\s\S]*?num:\s*(\d+),[\s\S]*?title:\s*'([^']+)'[\s\S]*?\}/g;
let m;
while ((m = re.exec(text))) {
const num = Number(m[1]);
const title = m[2];
if (num >= 1 && num <= 30) lessons.set(num, title);
}
return lessons;
}
function extractCorePatternsForTitle(title, text) {
// Try to find 'title': { ... corePatterns: [ ... ] }
const escTitle = title.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
const re = new RegExp("'" + escTitle + "'\\s*:\\s*\\{[\\s\\S]*?corePatterns\\s*:\\s*\\[([\\s\\S]*?)\\]", 'm');
const m = re.exec(text);
if (!m) return null;
const inside = m[1];
// Count entries: either objects { target: ... } or string entries
const lines = inside.split(/\r?\n/).map(l => l.trim()).filter(l => l.length > 0);
let count = 0;
for (const ln of lines) {
if (ln.startsWith('{') || ln.startsWith("'") || ln.startsWith('"')) count++;
}
return count;
}
const lessons = extractLessons(base + '\n' + bisaya);
const results = [];
for (let i = 1; i <= 30; i++) {
const title = lessons.get(i);
if (!title) {
results.push({ num: i, title: null, count: 0, note: 'title not found' });
continue;
}
let count = extractCorePatternsForTitle(title, base);
let source = 'create-bisaya-course.js';
if (count === null) {
count = extractCorePatternsForTitle(title, bisaya);
source = 'bisaya-course-plan-24-43.js';
}
if (count === null) {
// try relationship anchor
const relText = bisaya;
count = extractCorePatternsForTitle(title, relText);
source = 'bisaya-course-plan-24-43.js';
}
if (count === null) {
results.push({ num: i, title, count: 0, note: 'corePatterns not found' });
} else {
results.push({ num: i, title, count, source });
}
}
let total = 0;
for (const r of results) {
total += r.count || 0;
}
console.log('Core pattern counts for lessons 1..30:');
results.forEach(r => {
console.log(`${r.num}. ${r.title || '<missing>'} -> ${r.count} ${r.source ? '('+r.source+')' : ''} ${r.note?'- '+r.note:''}`);
});
console.log('---');
console.log('Total core patterns (1..30):', total);

View File

@@ -13,6 +13,7 @@ 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_DIDACTICS_FRAGMENTS, BISAYA_PHASE1_DIDACTICS } from './bisaya-course-phase1.js';
import { BISAYA_DIDACTICS_24_43, BISAYA_LESSONS_24_43_BY_NUMBER, BISAYA_RELATIONSHIP_ANCHOR_DIDACTICS } from './bisaya-course-plan-24-43.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';
@@ -26,6 +27,8 @@ function withTypeName(exerciseTypeName, exercise) {
}
const GENERATED_BISAYA_DIDACTICS = {
...BISAYA_PHASE1_DIDACTICS,
...BISAYA_DIDACTICS_FRAGMENTS,
...BISAYA_RELATIONSHIP_ANCHOR_DIDACTICS,
...BISAYA_DIDACTICS_24_43,
...BISAYA_PHASE3_DIDACTICS,
@@ -449,11 +452,23 @@ function rotateArray(values, offset) {
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 || []);
const learningGoals = Array.isArray(lesson.learningGoals) && lesson.learningGoals.length > 0
? lesson.learningGoals
: (staticDidactics.learningGoals || []);
const corePatterns = [
...(staticDidactics.corePatterns || []),
...(Array.isArray(lesson.corePatterns) ? lesson.corePatterns : [])
];
const grammarFocus = mergeGrammarFocusForLesson(
staticDidactics.grammarFocus || [],
Array.isArray(lesson.grammarFocus) ? lesson.grammarFocus : []
);
const speakingPrompts = Array.isArray(lesson.speakingPrompts) && lesson.speakingPrompts.length > 0
? lesson.speakingPrompts
: (staticDidactics.speakingPrompts || []);
const practicalTasks = Array.isArray(lesson.practicalTasks) && lesson.practicalTasks.length > 0
? lesson.practicalTasks
: (staticDidactics.practicalTasks || []);
return {
learningGoals,
@@ -2361,14 +2376,14 @@ const BISAYA_EXERCISES = {
instruction: 'Fülle die Lücken mit den richtigen Bisaya-Wörtern.',
questionData: {
type: 'gap_fill',
text: '{gap} ko sa merkado. (Ich gehe zum Markt.) | {gap} ta didto. (Lass uns dorthin gehen.)',
text: '{gap} ko sa merkado. (Ich bin auf dem Weg zum Markt.) | {gap} ta didto. (Lass uns dorthin gehen.)',
gaps: 2
},
answerData: {
type: 'gap_fill',
answers: ['Padulong', 'Padulong']
answers: ['Padulong', 'Adto']
},
explanation: '\"Padulong\" beschreibt, dass man unterwegs zu einem Ziel ist.'
explanation: 'Padulong betont die Richtung/Bewegung; „Adto ta didto“ ist die natürliche Aufforderung, dorthin zu gehen.'
},
{
exerciseTypeId: 4, // transformation
@@ -2386,6 +2401,86 @@ const BISAYA_EXERCISES = {
alternatives: ['Asa dapit ang simbahan?', 'Asa man ang simbahan?']
},
explanation: '\"simbahan\" = Kirche, \"Asa ang ...?\" = Wo ist ...?'
},
{
exerciseTypeId: 4,
title: 'Nach der Person fragen',
instruction: 'Übersetze ins Bisaya.',
questionData: {
type: 'transformation',
text: 'Wo bist du?',
sourceLanguage: 'Deutsch',
targetLanguage: 'Bisaya'
},
answerData: {
type: 'transformation',
correct: 'Asa ka?',
alternatives: ['Asa man ka?']
},
explanation: '„Asa ka?“ kann je nach Kontext nach Ort oder Ziel fragen.'
},
{
exerciseTypeId: 4,
title: 'Hier antworten',
instruction: 'Übersetze ins Bisaya.',
questionData: {
type: 'transformation',
text: 'Ich bin hier.',
sourceLanguage: 'Deutsch',
targetLanguage: 'Bisaya'
},
answerData: {
type: 'transformation',
correct: 'Naa ko dinhi.',
alternatives: ['Dinhi ko.', 'Naa ko diri.', 'Diri ko.']
},
explanation: '„Naa ko dinhi“ und „Dinhi ko“ sind beide brauchbare Antworten.'
},
{
exerciseTypeId: 4,
title: 'Dort antworten',
instruction: 'Übersetze ins Bisaya.',
questionData: {
type: 'transformation',
text: 'Er ist dort.',
sourceLanguage: 'Deutsch',
targetLanguage: 'Bisaya'
},
answerData: {
type: 'transformation',
correct: 'Didto siya.',
alternatives: ['Naa siya didto.', 'Siya didto.']
},
explanation: '„siya“ kann er oder sie bedeuten; „didto“ markiert dort.'
},
{
exerciseTypeId: 3,
title: 'Mini-Route bauen',
instruction: 'Ordne die Sätze zu einer kurzen Route.',
questionData: {
type: 'sentence_building',
question: 'Baue: Wo ist der Markt? Ich bin hier. Ich bin auf dem Weg zum Markt.',
tokens: ['Asa ang merkado?', 'Naa ko dinhi.', 'Padulong ko sa merkado.']
},
answerData: {
correct: ['Asa ang merkado? Naa ko dinhi. Padulong ko sa merkado.']
},
explanation: 'Die Reihenfolge verbindet Ortsfrage, Standort und Richtung.'
},
{
exerciseTypeId: 10,
title: 'Weg im Alltag',
instruction: 'Antworte kurz mit Ort und Richtung.',
questionData: {
type: 'situational_response',
question: 'Jemand fragt, wo du bist und wohin du gehst. Sage, dass du hier bist und zum Markt unterwegs bist.',
keywords: ['naa', 'dinhi', 'padulong', 'merkado']
},
answerData: {
modelAnswer: 'Naa ko dinhi. Padulong ko sa merkado.',
keywords: ['naa', 'dinhi', 'padulong', 'merkado']
},
explanation: 'Die Antwort kombiniert Standort und Zielrichtung in zwei kurzen Sätzen.'
}
],