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:
@@ -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