feat(vocab): enhance vocabulary exercises and localization support
All checks were successful
Deploy to production / deploy (push) Successful in 2m51s

- Updated core patterns in BISAYA_PHASE5_DIDACTICS to include gloss translations for better understanding.
- Refactored vocabulary exercise generation in update-food-care-exercises.js to improve randomization and user engagement.
- Added new exercise types and improved question structures for vocabulary lessons, enhancing the learning experience.
- Enhanced localization files for German, English, and Spanish to support new exercise features and improve user guidance.
- Updated VocabLessonView to incorporate sequential navigation for exercises, providing a more structured learning flow.
This commit is contained in:
Torsten Schulz (local)
2026-04-07 09:09:43 +02:00
parent d192bcae2d
commit e17f0cdce0
6 changed files with 348 additions and 38 deletions

View File

@@ -52,7 +52,12 @@ export const BISAYA_PHASE5_DIDACTICS = {
'Nahe Bedeutungen in stabilere Antworten überführen.',
'Häufige Stolperstellen transparent machen.'
],
corePatterns: ['Palangga taka.', 'Mingaw ko nimo.', 'Magpahuway sa.', 'Andam na ka?']
corePatterns: [
{ target: 'Palangga taka.', gloss: 'Ich hab dich lieb.' },
{ target: 'Mingaw ko nimo.', gloss: 'Ich vermisse dich.' },
{ target: 'Magpahuway sa.', gloss: 'Ruh dich aus.' },
{ target: 'Andam na ka?', gloss: 'Bist du fertig?' }
]
},
'Freies Erzählen - Mein Alltag': {
learningGoals: [

View File

@@ -652,23 +652,43 @@ async function updateFoodCareExercises() {
totalExercisesCreated++;
}
} else if (lesson.title === 'Essen & Trinken') {
// Vokabular-Übungen für "Essen & Trinken"
for (const vocab of conversations) {
// Multiple Choice: Muttersprache -> Bisaya
// Vokabular: keine strikte Paarung Deutsch→Bisaya direkt gefolgt von Rückrichtung (vermeidet Verräter-Effekt).
const n = conversations.length;
const offset = Math.max(1, Math.floor(n / 2));
const revIdx = (i) => (i + offset) % Math.max(n, 1);
const pickOtherBisaya = (correct, start) => {
for (let t = 1; t <= n; t++) {
const o = conversations[(start + t) % n]?.bisaya;
if (o && o !== correct) return o;
}
return 'Salamat';
};
const pickOtherNative = (correct, start) => {
for (let t = 1; t <= n; t++) {
const o = conversations[(start + t) % n]?.native;
if (o && o !== correct) return o;
}
return 'Danke';
};
for (let i = 0; i < n; i++) {
const vocab = conversations[i];
await VocabGrammarExercise.create({
lessonId: lesson.id,
exerciseTypeId: 2, // multiple_choice
exerciseTypeId: 2,
exerciseNumber: exerciseNumber++,
title: `Wie sagt man "${vocab.native}"?`,
instruction: 'Wähle die richtige Übersetzung.',
questionData: JSON.stringify({
type: 'multiple_choice',
answerLanguage: 'target',
question: `Wie sagt man "${vocab.native}" auf Bisaya?`,
options: [
vocab.bisaya,
conversations[(exerciseNumber - 2 + 1) % conversations.length]?.bisaya || 'Salamat',
conversations[(exerciseNumber - 2 + 2) % conversations.length]?.bisaya || 'Maayo',
conversations[(exerciseNumber - 2 + 3) % conversations.length]?.bisaya || 'Palihug'
pickOtherBisaya(vocab.bisaya, i),
pickOtherBisaya(vocab.bisaya, i + 3),
pickOtherBisaya(vocab.bisaya, i + 6)
]
}),
answerData: JSON.stringify({
@@ -679,22 +699,25 @@ async function updateFoodCareExercises() {
createdByUserId: course.owner_user_id || systemUser.id
});
totalExercisesCreated++;
}
// Multiple Choice: Bisaya -> Muttersprache
for (let i = 0; i < n; i++) {
const vocab = conversations[revIdx(i)];
await VocabGrammarExercise.create({
lessonId: lesson.id,
exerciseTypeId: 2, // multiple_choice
exerciseTypeId: 2,
exerciseNumber: exerciseNumber++,
title: `Was bedeutet "${vocab.bisaya}"?`,
instruction: 'Wähle die richtige Übersetzung.',
questionData: JSON.stringify({
type: 'multiple_choice',
answerLanguage: 'native',
question: `Was bedeutet "${vocab.bisaya}"?`,
options: [
vocab.native,
conversations[(exerciseNumber - 3 + 1) % conversations.length]?.native || 'Danke',
conversations[(exerciseNumber - 3 + 2) % conversations.length]?.native || 'Bitte',
conversations[(exerciseNumber - 3 + 3) % conversations.length]?.native || 'Gut'
pickOtherNative(vocab.native, i),
pickOtherNative(vocab.native, i + 4),
pickOtherNative(vocab.native, i + 8)
]
}),
answerData: JSON.stringify({
@@ -706,6 +729,31 @@ async function updateFoodCareExercises() {
});
totalExercisesCreated++;
}
for (let i = 0; i < n; i++) {
const vocab = conversations[i];
await VocabGrammarExercise.create({
lessonId: lesson.id,
exerciseTypeId: 4,
exerciseNumber: exerciseNumber++,
title: `Tippe auf Bisaya: "${vocab.native}"`,
instruction: 'Übersetze das Wort (Schreibtest).',
questionData: JSON.stringify({
type: 'transformation',
text: vocab.native,
sourceLanguage: nativeLangName,
targetLanguage: 'Bisaya'
}),
answerData: JSON.stringify({
type: 'transformation',
correct: vocab.bisaya,
alternatives: []
}),
explanation: vocab.explanation,
createdByUserId: course.owner_user_id || systemUser.id
});
totalExercisesCreated++;
}
}
}