feat(dictionary): a German verb is found with its particle at the clause's end

"Ich rufe dich morgen an": where a clause ends in a separable particle,
each word before it is also asked for with the particle joined on, as a
subordinate clause writes the verb (anrufe), which Wiktionary lists among
anrufen's forms. The verb is offered beside its stem and its particle,
as the longer words running over a Japanese word are.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-14 12:39:58 +02:00
commit 93c33ce079
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
2 changed files with 168 additions and 4 deletions

View file

@ -0,0 +1,48 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:halkyon_learn_flutter/src/feature/dictionary/model/dictionary.dart';
import 'package:halkyon_learn_flutter/src/feature/dictionary/model/german_words.dart';
import '../../support/fake_dictionary_gateway.dart';
void main() {
final anrufen = germanWord(
'anrufen',
pos: 'verb',
glosses: ['to call'],
forms: ['anrufe', 'anruft', 'anrief'],
);
final rufen = germanWord('rufen', pos: 'verb', glosses: ['to shout']);
final an = germanWord('an', pos: 'preposition', glosses: ['at']);
test('a clause ending in a separable particle asks for each word before '
'it with the particle joined on, as subordinate clauses write it', () {
final keys = germanSentenceKeys('Ich rufe dich morgen an.');
expect(keys, containsAll(['ich', 'rufe', 'an', 'anrufe', 'anich']));
// Only where a particle ends the clause.
expect(germanSentenceKeys('Ich rufe dich.'), isNot(contains('anrufe')));
});
test('the separated verb is offered beside its stem and its particle; '
'clauses each on their own', () {
final found = <String, List<DictionaryItem>>{
'rufe': [rufen],
'an': [an],
'anrufe': [anrufen],
};
final parts = germanSentenceParts(
'Ich rufe dich morgen an, dann gehe ich.',
found,
);
final stem = parts.singleWhere((part) => part.text == 'rufe');
expect(stem.entries, [rufen]);
expect(stem.wider.single.entries, [anrufen]);
expect(stem.wider.single.text, 'rufe … an');
final particle = parts.singleWhere((part) => part.text == 'an');
expect(particle.wider.single.entries, [anrufen]);
// The particle's place in the line is where it stands.
expect(particle.start, 'Ich rufe dich morgen '.length);
});
}

View file

@ -114,7 +114,7 @@ void main() {
test('Japanese is cut by its longest words, the rest at their spaces', () {
expect(WordCutting.of('ja'), WordCutting.longestJapanese);
expect(WordCutting.of('de'), WordCutting.spaces);
expect(WordCutting.of('de'), WordCutting.spacesGerman);
expect(WordCutting.of('el'), WordCutting.spaces);
expect(WordCutting.of('en-GB'), WordCutting.spaces);
});