feat(server): an entry's text keys alone find it in running text

A dictionary file may name an entry's `textKeys`, its own forms: those
alone fill `dictionary_key`, so a German word is found in a subtitle by
its forms, never by its English meanings, which would match English
words in German text; a search still finds it by them. Files without
them (JMdict's) fill it with every key without a space, as before.

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 07:45:15 +02:00
commit 292e369ed3
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
2 changed files with 53 additions and 1 deletions

View file

@ -170,9 +170,13 @@ Future<bool> loadDictionaryFile(Session session, File file) async {
final sourceId = json['id']! as String; final sourceId = json['id']! as String;
final rank = json['rank']! as int; final rank = json['rank']! as int;
final entryKeys = (json['keys']! as List).cast<String>(); final entryKeys = (json['keys']! as List).cast<String>();
// What finds the entry in running text: its own forms where the file
// names them (a German word's, never its English meanings, which
// would match English words in German text), else every key.
final textKeys = (json['textKeys'] as List?)?.cast<String>() ?? entryKeys;
keys.addAll([ keys.addAll([
for (final key in { for (final key in {
for (final key in entryKeys) for (final key in textKeys)
if (!key.contains(RegExp(r'\s')) && !key.contains('|')) if (!key.contains(RegExp(r'\s')) && !key.contains('|'))
key.toLowerCase(), key.toLowerCase(),
}) })

View file

@ -672,6 +672,54 @@ void main() {
); );
}); });
test('withKeys finds an entry that names its text keys by them alone: '
'a German word by its forms, never by its English meanings, which '
'would match English words in German text', () async {
await loadDictionaryFile(
session,
write(
[
{
...entry(
'Haus/noun',
'Haus',
rank: 5584,
keys: ['haus', 'häuser', 'house', 'house, building'],
),
'textKeys': ['haus', 'häuser', 'häusern'],
},
],
name: 'wiktionary-de-en.jsonl',
head: {
...header(source: 'wiktionary-de'),
'language': 'de',
},
),
);
final found = await endpoints.dictionary.withKeys(
signedIn,
'wiktionary-de',
'en',
['Häusern', 'house', 'haus'],
);
expect({for (final hit in found) hit.key}, {'häusern', 'haus'});
// A search still finds it by its meaning.
expect(
[
for (final hit in await endpoints.dictionary.search(
signedIn,
'wiktionary-de',
'en',
'house',
))
hit.headword,
],
['Haus'],
);
});
test('withKeys finds the keys loaded with their entries, and no more ' test('withKeys finds the keys loaded with their entries, and no more '
'once a load replaced them; a dictionary loaded before keys had ' 'once a load replaced them; a dictionary loaded before keys had '
'rows loads again', () async { 'rows loads again', () async {