feat(bisaya-course): restructure core patterns and enhance vocabulary preparation
All checks were successful
Deploy to production / deploy (push) Successful in 2m48s
All checks were successful
Deploy to production / deploy (push) Successful in 2m48s
- Updated core patterns in various scripts to use an object format with target phrases and glosses, improving clarity and usability for learners. - Enhanced the VocabService to normalize core pattern entries, ensuring consistent handling of vocabulary data. - Introduced new vocabulary preparation steps in the VocabLessonView, guiding users through active review processes before engaging with the vocabulary trainer. - Added localization support for new vocabulary preparation hints and instructions in multiple languages, enhancing user experience across the application.
This commit is contained in:
@@ -22,7 +22,26 @@ const LESSON_DIDACTICS = {
|
||||
'Höfliche Reaktionen wie Danke und Bitte passend einsetzen.',
|
||||
'Ein kurzes Begrüßungs-Mini-Gespräch laut üben.'
|
||||
],
|
||||
corePatterns: ['Kumusta ka?', 'Maayong buntag.', 'Maayong adlaw.', 'Maayong gabii.', 'Maayong gabii, matulog na ta.', 'Katulog og maayo.', 'Kapoy na ka?', 'Matulog na ta.', 'Inom sa og tubig.', 'Patya ang suga.', 'Tabuni ang imong kaugalingon.', 'Ugma nasad.', 'Damgo og nindot.', 'Amping.', 'Babay.', 'Maayo ko.', 'Salamat.', 'Palihug.'],
|
||||
corePatterns: [
|
||||
{ target: 'Kumusta ka?', gloss: 'Wie geht es dir?' },
|
||||
{ target: 'Maayong buntag.', gloss: 'Guten Morgen.' },
|
||||
{ target: 'Maayong adlaw.', gloss: 'Guten Tag.' },
|
||||
{ target: 'Maayong gabii.', gloss: 'Guten Abend.' },
|
||||
{ target: 'Maayong gabii, matulog na ta.', gloss: 'Guten Abend, wir legen uns schlafen.' },
|
||||
{ target: 'Katulog og maayo.', gloss: 'Schlaf gut.' },
|
||||
{ target: 'Kapoy na ka?', gloss: 'Bist du müde?' },
|
||||
{ target: 'Matulog na ta.', gloss: 'Lass uns schlafen gehen.' },
|
||||
{ target: 'Inom sa og tubig.', gloss: 'Trink Wasser.' },
|
||||
{ target: 'Patya ang suga.', gloss: 'Mach das Licht aus.' },
|
||||
{ target: 'Tabuni ang imong kaugalingon.', gloss: 'Deck dich zu.' },
|
||||
{ target: 'Ugma nasad.', gloss: 'Bis morgen wieder.' },
|
||||
{ target: 'Damgo og nindot.', gloss: 'Träum schön.' },
|
||||
{ target: 'Amping.', gloss: 'Pass auf dich auf.' },
|
||||
{ target: 'Babay.', gloss: 'Tschüss.' },
|
||||
{ target: 'Maayo ko.', gloss: 'Mir geht es gut.' },
|
||||
{ target: 'Salamat.', gloss: 'Danke.' },
|
||||
{ target: 'Palihug.', gloss: 'Bitte.' }
|
||||
],
|
||||
grammarFocus: [
|
||||
{ title: 'Kurzantworten mit ko', text: 'Mit "ko" sprichst du über dich selbst: "Maayo ko."', example: 'Maayo ko. = Mir geht es gut.' },
|
||||
{ title: 'Maayong + Tageszeit', text: 'Mit "Maayong" kannst du Grüße für verschiedene Tageszeiten bilden.', example: 'Maayong buntag. / Maayong gabii.' },
|
||||
|
||||
@@ -30,19 +30,46 @@ const GENERATED_BISAYA_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 normalizeCorePatternEntry(entry) {
|
||||
if (entry === null || entry === undefined || entry === '') {
|
||||
return null;
|
||||
}
|
||||
if (typeof entry === 'object' && !Array.isArray(entry)) {
|
||||
const target = normalizeText(entry.target ?? entry.ceb ?? entry.phrase ?? '');
|
||||
const gloss = normalizeText(entry.gloss ?? entry.de ?? entry.translation ?? '');
|
||||
if (!target) return null;
|
||||
return { target, gloss };
|
||||
}
|
||||
const s = normalizeText(entry);
|
||||
if (!s) return null;
|
||||
const pipe = s.indexOf('|');
|
||||
if (pipe !== -1) {
|
||||
const target = normalizeText(s.slice(0, pipe));
|
||||
const gloss = normalizeText(s.slice(pipe + 1));
|
||||
if (!target) return null;
|
||||
return { target, gloss };
|
||||
}
|
||||
return { target: s, gloss: '' };
|
||||
}
|
||||
|
||||
function corePatternTarget(entry) {
|
||||
const n = normalizeCorePatternEntry(entry);
|
||||
return n ? n.target : '';
|
||||
}
|
||||
|
||||
const GENERIC_DISTRACTOR_PATTERNS = Array.from(new Set(
|
||||
Object.values(GENERATED_BISAYA_DIDACTICS)
|
||||
.flatMap((entry) => Array.isArray(entry?.corePatterns) ? entry.corePatterns : [])
|
||||
.map((pattern) => corePatternTarget(pattern))
|
||||
.filter(Boolean)
|
||||
)).slice(0, 200);
|
||||
|
||||
function simpleHash(value) {
|
||||
return Array.from(String(value || '')).reduce((sum, char) => sum + char.charCodeAt(0), 0);
|
||||
}
|
||||
@@ -63,7 +90,9 @@ function getLessonDidactics(lesson) {
|
||||
|
||||
return {
|
||||
learningGoals,
|
||||
corePatterns: corePatterns.map((entry) => normalizeText(entry)).filter(Boolean),
|
||||
corePatterns: corePatterns
|
||||
.map((entry) => normalizeCorePatternEntry(entry))
|
||||
.filter(Boolean),
|
||||
grammarFocus,
|
||||
speakingPrompts,
|
||||
practicalTasks
|
||||
@@ -323,10 +352,10 @@ function generateExercisesFromDidactics(lesson) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const patternA = corePatterns[0];
|
||||
const patternB = corePatterns[1] || corePatterns[0];
|
||||
const patternA = corePatternTarget(corePatterns[0]);
|
||||
const patternB = corePatternTarget(corePatterns[1] || corePatterns[0]);
|
||||
const lessonPool = Array.from(new Set([
|
||||
...corePatterns,
|
||||
...corePatterns.map((p) => corePatternTarget(p)),
|
||||
...GENERIC_DISTRACTOR_PATTERNS
|
||||
]));
|
||||
let generated = [];
|
||||
|
||||
@@ -26,24 +26,24 @@ const LESSON_DIDACTICS = {
|
||||
'Eine kurze Abend- und Schlafensroutine im Familienalltag sprechen.'
|
||||
],
|
||||
corePatterns: [
|
||||
'Kumusta ka?',
|
||||
'Maayong buntag.',
|
||||
'Maayong adlaw.',
|
||||
'Maayong gabii.',
|
||||
'Maayong gabii, matulog na ta.',
|
||||
'Katulog og maayo.',
|
||||
'Kapoy na ka?',
|
||||
'Matulog na ta.',
|
||||
'Inom sa og tubig.',
|
||||
'Patya ang suga.',
|
||||
'Tabuni ang imong kaugalingon.',
|
||||
'Ugma nasad.',
|
||||
'Damgo og nindot.',
|
||||
'Amping.',
|
||||
'Babay.',
|
||||
'Maayo ko.',
|
||||
'Salamat.',
|
||||
'Palihug.'
|
||||
{ target: 'Kumusta ka?', gloss: 'Wie geht es dir?' },
|
||||
{ target: 'Maayong buntag.', gloss: 'Guten Morgen.' },
|
||||
{ target: 'Maayong adlaw.', gloss: 'Guten Tag.' },
|
||||
{ target: 'Maayong gabii.', gloss: 'Guten Abend.' },
|
||||
{ target: 'Maayong gabii, matulog na ta.', gloss: 'Guten Abend, wir legen uns schlafen.' },
|
||||
{ target: 'Katulog og maayo.', gloss: 'Schlaf gut.' },
|
||||
{ target: 'Kapoy na ka?', gloss: 'Bist du müde?' },
|
||||
{ target: 'Matulog na ta.', gloss: 'Lass uns schlafen gehen.' },
|
||||
{ target: 'Inom sa og tubig.', gloss: 'Trink Wasser.' },
|
||||
{ target: 'Patya ang suga.', gloss: 'Mach das Licht aus.' },
|
||||
{ target: 'Tabuni ang imong kaugalingon.', gloss: 'Deck dich zu.' },
|
||||
{ target: 'Ugma nasad.', gloss: 'Bis morgen wieder.' },
|
||||
{ target: 'Damgo og nindot.', gloss: 'Träum schön.' },
|
||||
{ target: 'Amping.', gloss: 'Pass auf dich auf.' },
|
||||
{ target: 'Babay.', gloss: 'Tschüss.' },
|
||||
{ target: 'Maayo ko.', gloss: 'Mir geht es gut.' },
|
||||
{ target: 'Salamat.', gloss: 'Danke.' },
|
||||
{ target: 'Palihug.', gloss: 'Bitte.' }
|
||||
],
|
||||
grammarFocus: [
|
||||
{
|
||||
|
||||
@@ -18,7 +18,26 @@ const LESSON_DIDACTICS = {
|
||||
'Höfliche Reaktionen wie Danke und Bitte passend einsetzen.',
|
||||
'Ein kurzes Begrüßungs-Mini-Gespräch laut üben.'
|
||||
],
|
||||
corePatterns: ['Kumusta ka?', 'Maayong buntag.', 'Maayong adlaw.', 'Maayong gabii.', 'Maayong gabii, matulog na ta.', 'Katulog og maayo.', 'Kapoy na ka?', 'Matulog na ta.', 'Inom sa og tubig.', 'Patya ang suga.', 'Tabuni ang imong kaugalingon.', 'Ugma nasad.', 'Damgo og nindot.', 'Amping.', 'Babay.', 'Maayo ko.', 'Salamat.', 'Palihug.'],
|
||||
corePatterns: [
|
||||
{ target: 'Kumusta ka?', gloss: 'Wie geht es dir?' },
|
||||
{ target: 'Maayong buntag.', gloss: 'Guten Morgen.' },
|
||||
{ target: 'Maayong adlaw.', gloss: 'Guten Tag.' },
|
||||
{ target: 'Maayong gabii.', gloss: 'Guten Abend.' },
|
||||
{ target: 'Maayong gabii, matulog na ta.', gloss: 'Guten Abend, wir legen uns schlafen.' },
|
||||
{ target: 'Katulog og maayo.', gloss: 'Schlaf gut.' },
|
||||
{ target: 'Kapoy na ka?', gloss: 'Bist du müde?' },
|
||||
{ target: 'Matulog na ta.', gloss: 'Lass uns schlafen gehen.' },
|
||||
{ target: 'Inom sa og tubig.', gloss: 'Trink Wasser.' },
|
||||
{ target: 'Patya ang suga.', gloss: 'Mach das Licht aus.' },
|
||||
{ target: 'Tabuni ang imong kaugalingon.', gloss: 'Deck dich zu.' },
|
||||
{ target: 'Ugma nasad.', gloss: 'Bis morgen wieder.' },
|
||||
{ target: 'Damgo og nindot.', gloss: 'Träum schön.' },
|
||||
{ target: 'Amping.', gloss: 'Pass auf dich auf.' },
|
||||
{ target: 'Babay.', gloss: 'Tschüss.' },
|
||||
{ target: 'Maayo ko.', gloss: 'Mir geht es gut.' },
|
||||
{ target: 'Salamat.', gloss: 'Danke.' },
|
||||
{ target: 'Palihug.', gloss: 'Bitte.' }
|
||||
],
|
||||
grammarFocus: [
|
||||
{ title: 'Kurzantworten mit ko', text: 'Mit "ko" sprichst du über dich selbst: "Maayo ko."', example: 'Maayo ko. = Mir geht es gut.' },
|
||||
{ title: 'Maayong + Tageszeit', text: 'Mit "Maayong" kannst du Grüße für verschiedene Tageszeiten bilden.', example: 'Maayong buntag. / Maayong gabii.' },
|
||||
|
||||
@@ -106,7 +106,13 @@ export default class VocabService {
|
||||
`Lektion: ${lesson?.title || 'Unbekannte Lektion'}`,
|
||||
lesson?.description ? `Beschreibung: ${lesson.description}` : '',
|
||||
learningGoals.length ? `Lernziele: ${learningGoals.join(' | ')}` : '',
|
||||
corePatterns.length ? `Kernmuster: ${corePatterns.join(' | ')}` : '',
|
||||
corePatterns.length
|
||||
? `Kernmuster: ${corePatterns.map((p) => {
|
||||
const n = this._normalizeCorePatternEntry(p);
|
||||
if (!n) return '';
|
||||
return n.gloss ? `${n.target} (${n.gloss})` : n.target;
|
||||
}).filter(Boolean).join(' | ')}`
|
||||
: '',
|
||||
speakingPrompts.length
|
||||
? `Sprechaufträge: ${speakingPrompts.map((item) => item.prompt || item.title || '').filter(Boolean).join(' | ')}`
|
||||
: '',
|
||||
@@ -239,14 +245,16 @@ export default class VocabService {
|
||||
|
||||
speakingPrompts.forEach((prompt, index) => {
|
||||
const learning = String(prompt?.prompt || prompt?.title || '').trim();
|
||||
const reference = String(prompt?.cue || corePatterns[index] || corePatterns[0] || '').trim();
|
||||
const refEntry = corePatterns[index] ?? corePatterns[0];
|
||||
const reference = String(prompt?.cue || this._corePatternTarget(refEntry) || '').trim();
|
||||
if (!learning || !reference || learning === reference) return;
|
||||
vocabMap.set(`${learning}-${reference}`, { learning, reference });
|
||||
});
|
||||
|
||||
practicalTasks.forEach((task, index) => {
|
||||
const learning = String(task?.text || task?.title || '').trim();
|
||||
const reference = String(corePatterns[index] || corePatterns[0] || '').trim();
|
||||
const refEntry = corePatterns[index] ?? corePatterns[0];
|
||||
const reference = String(this._corePatternTarget(refEntry) || '').trim();
|
||||
if (!learning || !reference || learning === reference) return;
|
||||
vocabMap.set(`${learning}-${reference}`, { learning, reference });
|
||||
});
|
||||
@@ -270,6 +278,49 @@ export default class VocabService {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Kernmuster: Zielsprachen-Phrase + optionale Glossierung (z. B. Deutsch).
|
||||
* Unterstützt Legacy-Strings, "Phrase|Gloss" und Objekte { target, gloss } / { ceb, de }.
|
||||
*/
|
||||
_normalizeCorePatternEntry(entry) {
|
||||
if (entry === null || entry === undefined || entry === '') {
|
||||
return null;
|
||||
}
|
||||
if (typeof entry === 'object' && !Array.isArray(entry)) {
|
||||
const target = String(entry.target ?? entry.ceb ?? entry.phrase ?? '').trim();
|
||||
const gloss = String(entry.gloss ?? entry.de ?? entry.translation ?? '').trim();
|
||||
if (!target) return null;
|
||||
return { target, gloss };
|
||||
}
|
||||
const s = String(entry).trim();
|
||||
if (!s) return null;
|
||||
const pipe = s.indexOf('|');
|
||||
if (pipe !== -1) {
|
||||
const target = s.slice(0, pipe).trim();
|
||||
const gloss = s.slice(pipe + 1).trim();
|
||||
if (!target) return null;
|
||||
return { target, gloss };
|
||||
}
|
||||
return { target: s, gloss: '' };
|
||||
}
|
||||
|
||||
_normalizeCorePatternList(value) {
|
||||
if (!value) return [];
|
||||
const raw = Array.isArray(value)
|
||||
? value
|
||||
: (typeof value === 'string'
|
||||
? value.split(/\r?\n|;/).map((entry) => entry.trim()).filter(Boolean)
|
||||
: []);
|
||||
return raw
|
||||
.map((entry) => this._normalizeCorePatternEntry(entry))
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
_corePatternTarget(entry) {
|
||||
const n = this._normalizeCorePatternEntry(entry);
|
||||
return n ? n.target : '';
|
||||
}
|
||||
|
||||
_normalizeStructuredList(value, keys = ['title', 'text']) {
|
||||
if (!value) return [];
|
||||
if (Array.isArray(value)) {
|
||||
@@ -482,7 +533,7 @@ export default class VocabService {
|
||||
const uniquePatterns = [...new Set(patterns.map((item) => String(item || '').trim()).filter(Boolean))];
|
||||
|
||||
const learningGoals = this._normalizeStringList(plainLesson.learningGoals);
|
||||
const corePatterns = this._normalizeStringList(plainLesson.corePatterns);
|
||||
const corePatterns = this._normalizeCorePatternList(plainLesson.corePatterns);
|
||||
const grammarFocus = this._normalizeStructuredList(plainLesson.grammarFocus, ['title', 'text', 'example']);
|
||||
const explicitSpeakingPrompts = this._normalizeStructuredList(plainLesson.speakingPrompts, ['title', 'prompt', 'cue']);
|
||||
const practicalTasks = this._normalizeStructuredList(plainLesson.practicalTasks, ['title', 'text']);
|
||||
@@ -495,7 +546,9 @@ export default class VocabService {
|
||||
'Ein bis zwei Satzmuster aktiv anwenden.',
|
||||
'Kurze Sätze oder Mini-Dialoge zum Thema selbst bilden.'
|
||||
],
|
||||
corePatterns: corePatterns.length > 0 ? corePatterns : uniquePatterns.slice(0, 5),
|
||||
corePatterns: corePatterns.length > 0
|
||||
? corePatterns
|
||||
: uniquePatterns.slice(0, 5).map((s) => ({ target: String(s || '').trim(), gloss: '' })).filter((p) => p.target),
|
||||
grammarFocus: grammarFocus.length > 0 ? grammarFocus : uniqueGrammarExplanations.slice(0, 4),
|
||||
speakingPrompts: explicitSpeakingPrompts.length > 0 ? explicitSpeakingPrompts : speakingPrompts.slice(0, 4),
|
||||
practicalTasks: practicalTasks.length > 0
|
||||
@@ -1775,7 +1828,7 @@ export default class VocabService {
|
||||
audioUrl: audioUrl || null,
|
||||
culturalNotes: culturalNotes || null,
|
||||
learningGoals: this._normalizeStringList(learningGoals),
|
||||
corePatterns: this._normalizeStringList(corePatterns),
|
||||
corePatterns: this._normalizeCorePatternList(corePatterns),
|
||||
grammarFocus: this._normalizeStructuredList(grammarFocus, ['title', 'text', 'example']),
|
||||
speakingPrompts: this._normalizeStructuredList(speakingPrompts, ['title', 'prompt', 'cue']),
|
||||
practicalTasks: this._normalizeStructuredList(practicalTasks, ['title', 'text']),
|
||||
@@ -1822,7 +1875,7 @@ export default class VocabService {
|
||||
if (audioUrl !== undefined) updates.audioUrl = audioUrl;
|
||||
if (culturalNotes !== undefined) updates.culturalNotes = culturalNotes;
|
||||
if (learningGoals !== undefined) updates.learningGoals = this._normalizeStringList(learningGoals);
|
||||
if (corePatterns !== undefined) updates.corePatterns = this._normalizeStringList(corePatterns);
|
||||
if (corePatterns !== undefined) updates.corePatterns = this._normalizeCorePatternList(corePatterns);
|
||||
if (grammarFocus !== undefined) updates.grammarFocus = this._normalizeStructuredList(grammarFocus, ['title', 'text', 'example']);
|
||||
if (speakingPrompts !== undefined) updates.speakingPrompts = this._normalizeStructuredList(speakingPrompts, ['title', 'prompt', 'cue']);
|
||||
if (practicalTasks !== undefined) updates.practicalTasks = this._normalizeStructuredList(practicalTasks, ['title', 'text']);
|
||||
|
||||
Reference in New Issue
Block a user