feat(VocabService, VocabPracticeDialog, VocabLessonView): enhance vocabulary training logic and UI feedback
All checks were successful
Deploy to production / deploy (push) Successful in 2m17s

- Introduced methods for improved text analysis and validation in VocabService, including `_wordCount` and `_looksLikeFragmentMismatch`, to better assess learning and reference pairs.
- Updated VocabPracticeDialog to display submitted answers and correct solutions, enhancing user feedback during practice sessions.
- Enhanced VocabLessonView to ensure only trainable vocabulary pairs are processed, improving the quality of vocabulary training.
- Added localization entries for new UI elements in both English and German, ensuring clarity in user interactions.
This commit is contained in:
Torsten Schulz (local)
2026-04-20 08:48:39 +02:00
parent 553f132184
commit e28ed7bdb5
5 changed files with 130 additions and 9 deletions

View File

@@ -1452,7 +1452,7 @@ export default {
const oriented = orientPair(entry?.learning, entry?.reference);
const reference = String(oriented.reference || '').trim();
const learning = String(oriented.learning || '').trim();
if (!reference) return;
if (!this.isTrainableLessonVocabPair(learning, reference)) return;
const key = this.normalizeLessonVocabTerm(reference);
if (!vocabByReference.has(key)) {
const variants = new Set();
@@ -1523,7 +1523,7 @@ export default {
return { from, to, page, pages: totalPages, total: n };
},
trainableLessonVocab() {
return this.lessonVocab.filter((entry) => entry.learning && entry.reference && entry.learning !== entry.reference);
return this.lessonVocab.filter((entry) => this.isTrainableLessonVocabPair(entry.learning, entry.reference));
},
lessonDidactics() {
return this.lesson?.didactics || {
@@ -1610,7 +1610,7 @@ export default {
const oriented = orientPair(target, gloss);
const t = String(oriented.target || '').trim();
const g = String(oriented.gloss || '').trim();
if (!t || !g) return;
if (!this.isTrainableLessonVocabPair(g, t)) return;
const key = `${this.normalizeLessonVocabTerm(t)}|${this.normalizeLessonVocabTerm(g)}`;
if (seen.has(key)) return;
seen.add(key);
@@ -2125,6 +2125,34 @@ export default {
.replace(/^[.,!?;:]+|[.,!?;:]+$/g, '')
.trim();
},
lessonVocabWordCount(value) {
return String(value || '')
.trim()
.replace(/[\p{P}\p{S}]+/gu, ' ')
.split(/\s+/)
.filter(Boolean)
.length;
},
looksLikeLessonVocabFragmentMismatch(left, right) {
const leftWords = this.lessonVocabWordCount(left);
const rightWords = this.lessonVocabWordCount(right);
const leftText = String(left || '').trim();
const rightText = String(right || '').trim();
const leftLooksSentence = leftWords >= 3 || /[?!.].+\S/.test(leftText);
const rightLooksSentence = rightWords >= 3 || /[?!.].+\S/.test(rightText);
const leftLooksShortFragment = leftWords <= 1 && leftText.length <= 12;
const rightLooksShortFragment = rightWords <= 1 && rightText.length <= 12;
return (leftLooksShortFragment && rightLooksSentence) || (rightLooksShortFragment && leftLooksSentence);
},
isTrainableLessonVocabPair(learning, reference) {
const l = String(learning || '').trim();
const r = String(reference || '').trim();
if (!l || !r || this.normalizeLessonVocabTerm(l) === this.normalizeLessonVocabTerm(r)) {
return false;
}
return !this.looksLikeLessonVocabFragmentMismatch(l, r);
},
normalizeCorePatternEntry(p) {
if (p && typeof p === 'object' && p.target) {
return {