From 97b4b01b229ed08639548f66ff8e002e03a54b89 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Tue, 19 May 2026 11:24:01 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20Erweiterung=20der=20Antwortvarianten=20?= =?UTF-8?q?f=C3=BCr=20Vokabel=C3=BCbungen=20zur=20Unterst=C3=BCtzung=20von?= =?UTF-8?q?=20Alternativen=20und=20Phrasen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/views/social/VocabLessonView.vue | 70 +++++++++++++++++-- 1 file changed, 66 insertions(+), 4 deletions(-) diff --git a/frontend/src/views/social/VocabLessonView.vue b/frontend/src/views/social/VocabLessonView.vue index e1971b5..5db5866 100644 --- a/frontend/src/views/social/VocabLessonView.vue +++ b/frontend/src/views/social/VocabLessonView.vue @@ -4152,6 +4152,67 @@ export default { : s; return this.normalizeComparableText(source); }, + expandSingleAnswerVariants(answer) { + const base = String(answer || '').trim(); + if (!base) return []; + + const words = base.split(/\s+/).map((word) => { + if (!word.includes('/')) { + return [word]; + } + + const match = word.match(/^([([{„\"'“‘]*)(.*?)([)\]}.,!?;:»\"'”’]*)$/); + const prefix = match?.[1] || ''; + const core = match?.[2] || word; + const suffix = match?.[3] || ''; + const parts = core + .split('/') + .map((part) => part.trim()) + .filter(Boolean); + + if (parts.length < 2 || parts.length > 8) { + return [word]; + } + + return parts.map((part) => `${prefix}${part}${suffix}`); + }); + + const variants = ['']; + for (const options of words) { + const next = []; + for (const current of variants) { + for (const option of options) { + next.push(`${current}${current ? ' ' : ''}${option}`); + } + } + if (next.length > 48) { + return [base]; + } + variants.splice(0, variants.length, ...next); + } + + return [...new Set([base, ...variants])]; + }, + splitPhraseAlternatives(answer) { + const base = String(answer || '').trim(); + if (!base) return []; + // Splits full-phrase alternatives like "A / B" into ["A", "B"] when they look like phrase-level alternatives. + const parts = base.split(/\s*[\/|;]\s*/).map(p => p.trim()).filter(Boolean); + if (parts.length >= 2 && parts.length <= 6) return parts; + return [base]; + }, + expandAnswerVariants(answer) { + const base = String(answer || '').trim(); + if (!base) return []; + + const phraseAlternatives = this.splitPhraseAlternatives(base); + if (phraseAlternatives.length >= 2) { + const expanded = phraseAlternatives.flatMap((part) => this.expandSingleAnswerVariants(part)); + return [...new Set(expanded)]; + } + + return this.expandSingleAnswerVariants(base); + }, reportSrsReviewForCurrentQuestion(isCorrect) { if (!this.currentVocabQuestion?.vocab || !this.courseId) { return; @@ -4185,10 +4246,11 @@ export default { const normalizedUser = this.normalizeVocab(userAnswer, { ignoreTrailingParentheticalNotes: useTypingNormalization }); - const normalizedCorrectAnswers = (this.currentVocabQuestion.answers || [this.currentVocabQuestion.answer]) - .map(answer => this.normalizeVocab(answer, { - ignoreTrailingParentheticalNotes: useTypingNormalization - })); + const rawAnswers = this.currentVocabQuestion.answers || [this.currentVocabQuestion.answer]; + // Expand alternatives like "A / B" into separate acceptable answers + const expandedAnswers = rawAnswers.flatMap(a => this.expandAnswerVariants(a)); + const normalizedCorrectAnswers = expandedAnswers + .map(answer => this.normalizeVocab(answer, { ignoreTrailingParentheticalNotes: useTypingNormalization })); this.vocabTrainerLastCorrect = normalizedCorrectAnswers.includes(normalizedUser); // Update Stats