feat(VocabService, VocabPracticeDialog): enhance text analysis and SRS validation
All checks were successful
Deploy to production / deploy (push) Successful in 1m50s

- Improved text analysis methods in VocabService to better identify instructional-like texts and task-related prompts, enhancing vocabulary training quality.
- Updated VocabPracticeDialog to include a new method for checking instruction-like text, ensuring more accurate filtering of trainable pairs.
- Incremented SRS session storage version to reflect changes in session management and data handling.
- Refactored normalization and validation logic to maintain consistency across vocabulary entries and improve user experience.
This commit is contained in:
Torsten Schulz (local)
2026-04-20 08:30:58 +02:00
parent 71f2ee7c97
commit f64c923db6
2 changed files with 37 additions and 3 deletions

View File

@@ -72,7 +72,17 @@ export default class VocabService {
return false;
}
return /^(sage|sag|frage|frag|bitte|stelle|sprich|erzähle|beschreibe|bilde|wähle|ordne|übersetze|nenne|nenn|beginne|verwende|reagiere|kombiniere|spiele|löse|beantworte|ergänze|formuliere)\b/i.test(text);
const normalized = text.toLowerCase().normalize('NFKC');
const startsWithTaskVerb = /^(sage|sag|frage|frag|bitte|stelle|sprich|erzähle|erzaehle|beschreibe|bilde|wähle|waehle|ordne|übersetze|uebersetze|nenne|nenn|beginne|verwende|nutze|reagiere|kombiniere|spiele|löse|loese|beantworte|ergänze|ergaenze|formuliere|lies|entscheide|zeige)\b/i.test(normalized);
const startsWithTakeTask = /^nimm\b/i.test(normalized)
&& (
/\b(ein|eine|einen|zwei|drei|vier|fünf|fuenf|sechs|sieben|acht|neun|zehn|\d+)\b/i.test(normalized)
|| /\b(w[oö]rter|verben|gegenstände|gegenstaende|sätze|saetze|muster|beispiele)\b/i.test(normalized)
);
const containsTaskChain = /\b(und|,)\s*(sage|sag|frage|frag|bitte|stelle|sprich|erzähle|erzaehle|beschreibe|bilde|wähle|waehle|ordne|übersetze|uebersetze|nenne|nenn|verwende|nutze|reagiere|kombiniere|spiele|löse|loese|beantworte|ergänze|ergaenze|formuliere|lies|entscheide|zeige)\b/i.test(normalized);
const containsPracticeMarker = /\b(laut|jeweils|zu jedem|zu jeder|umgebung|alltagsszene|rollenspiel|mini-dialog|szene)\b/i.test(normalized);
return startsWithTaskVerb || startsWithTakeTask || (containsTaskChain && containsPracticeMarker);
}
_isTrainableSrsPair(entry) {

View File

@@ -138,7 +138,7 @@ import DialogWidget from '@/components/DialogWidget.vue';
import apiClient from '@/utils/axios.js';
const PRACTICE_MIN_EXPOSURES = 3;
const SRS_SESSION_STORAGE_VERSION = 1;
const SRS_SESSION_STORAGE_VERSION = 2;
export default {
name: 'VocabPracticeDialog',
@@ -407,13 +407,37 @@ export default {
.trim();
return normalized.replace(/\s+/g, '');
},
isInstructionLikeText(value) {
const text = String(value || '').trim();
if (!text) return false;
const wordCount = text.split(/\s+/).filter(Boolean).length;
if (wordCount < 3) return false;
const normalized = text.toLowerCase().normalize('NFKC');
const startsWithTaskVerb = /^(sage|sag|frage|frag|bitte|stelle|sprich|erzähle|erzaehle|beschreibe|bilde|wähle|waehle|ordne|übersetze|uebersetze|nenne|nenn|beginne|verwende|nutze|reagiere|kombiniere|spiele|löse|loese|beantworte|ergänze|ergaenze|formuliere|lies|entscheide|zeige)\b/i.test(normalized);
const startsWithTakeTask = /^nimm\b/i.test(normalized)
&& (
/\b(ein|eine|einen|zwei|drei|vier|fünf|fuenf|sechs|sieben|acht|neun|zehn|\d+)\b/i.test(normalized)
|| /\b(w[oö]rter|verben|gegenstände|gegenstaende|sätze|saetze|muster|beispiele)\b/i.test(normalized)
);
const containsTaskChain = /\b(und|,)\s*(sage|sag|frage|frag|bitte|stelle|sprich|erzähle|erzaehle|beschreibe|bilde|wähle|waehle|ordne|übersetze|uebersetze|nenne|nenn|verwende|nutze|reagiere|kombiniere|spiele|löse|loese|beantworte|ergänze|ergaenze|formuliere|lies|entscheide|zeige)\b/i.test(normalized);
const containsPracticeMarker = /\b(laut|jeweils|zu jedem|zu jeder|umgebung|alltagsszene|rollenspiel|mini-dialog|szene)\b/i.test(normalized);
return startsWithTaskVerb || startsWithTakeTask || (containsTaskChain && containsPracticeMarker);
},
isTrainablePair(learning, reference) {
if (!learning || !reference || this.normalize(learning) === this.normalize(reference)) {
return false;
}
return !this.isInstructionLikeText(learning) && !this.isInstructionLikeText(reference);
},
normalizePool(items = []) {
const seen = new Set();
return (Array.isArray(items) ? items : [])
.map((item, index) => {
const learning = String(item?.learning || '').trim();
const reference = String(item?.reference || '').trim();
if (!learning || !reference || this.normalize(learning) === this.normalize(reference)) {
if (!this.isTrainablePair(learning, reference)) {
return null;
}
const key = `${this.normalize(learning)}|${this.normalize(reference)}`;