feat(app): kanji and kana in the dictionary, beside the words
Where the instance holds the characters of a studyset's language, the dictionary screen has a tab for them, Kanji & kana: search by character, reading (kana or romaji) or meaning, and whole levels to add, under their names: the kana (hiragana, katakana), the JLPT levels and the school grades. A character becomes a character card, its meanings (a kana's romaji) and readings, tagged with its level (JLPT-N5), school grade (Grade 1 … Grade 6, Secondary school) and script (Hiragana, Katakana). A character card's Look up searches the characters, and an entry looked up again on a card swaps the tags the one before brought, as words do. A dictionary's entries are now words or characters (DictionaryItem), as its kind says. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
2fc4205010
commit
cec518350a
2 changed files with 1034 additions and 158 deletions
|
|
@ -27,6 +27,68 @@ final terebi = word(
|
|||
);
|
||||
|
||||
void main() {
|
||||
group('a character', () {
|
||||
test('reads its meanings, readings, grade, strokes and frequency from '
|
||||
'the entry', () {
|
||||
final read = DictionaryCharacter.fromEntry(
|
||||
ref: (source: 'kanjidic', id: '食'),
|
||||
headword: '食',
|
||||
labels: ['jlpt:n5', 'grade:2'],
|
||||
entry:
|
||||
'{"meanings":["eat","food"],"on":["ショク","ジキ"],'
|
||||
'"kun":["く.う","た.べる"],"grade":2,"strokes":9,'
|
||||
'"frequency":328,"jlpt":5}',
|
||||
);
|
||||
|
||||
expect(read.meanings, ['eat', 'food']);
|
||||
expect(read.onReadings, ['ショク', 'ジキ']);
|
||||
expect(read.kunReadings, ['く.う', 'た.べる']);
|
||||
expect((read.grade, read.strokes, read.frequency), (2, 9, 328));
|
||||
expect(read.jlpt, 5);
|
||||
expect(read.isKana, isFalse);
|
||||
});
|
||||
|
||||
test('becomes a character card that keeps where it is from; a kana '
|
||||
'means its romaji', () {
|
||||
final eat = character(
|
||||
'食',
|
||||
meanings: ['eat'],
|
||||
on: ['ショク'],
|
||||
kun: ['た.べる'],
|
||||
).card;
|
||||
expect(eat.type, CardType.character);
|
||||
expect(eat.front.plainText.trim(), '食');
|
||||
expect(eat.meanings, ['eat']);
|
||||
expect(eat.onReadings, ['ショク']);
|
||||
expect(eat.kunReadings, ['た.べる']);
|
||||
expect(eat.entry, (source: 'kanjidic', id: '食'));
|
||||
expect(eat.isComplete, isTrue);
|
||||
|
||||
final a = character('あ', labels: ['hiragana'], romaji: 'a').card;
|
||||
expect(a.meanings, ['a']);
|
||||
expect(a.isComplete, isTrue);
|
||||
});
|
||||
|
||||
test('names the tags by JLPT level, school grade and script', () {
|
||||
expect(
|
||||
character('食', labels: ['jlpt:n5', 'grade:2'], grade: 2).tagNames,
|
||||
['JLPT-N5', 'Grade 2'],
|
||||
);
|
||||
expect(
|
||||
character(
|
||||
'亜',
|
||||
labels: ['jlpt:n1', 'grade:secondary'],
|
||||
grade: 8,
|
||||
).tagNames,
|
||||
['JLPT-N1', 'Secondary school'],
|
||||
);
|
||||
expect(character('ア', labels: ['katakana']).tagNames, ['Katakana']);
|
||||
expect(isDictionaryTagName('grade 2'), isTrue);
|
||||
expect(isDictionaryTagName('Hiragana'), isTrue);
|
||||
expect(isDictionaryTagName('Kanji I like'), isFalse);
|
||||
});
|
||||
});
|
||||
|
||||
group('a word', () {
|
||||
test('reads its senses from the entry, the codes with them', () {
|
||||
final read = DictionaryWord.fromEntry(
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import 'package:flutter_test/flutter_test.dart';
|
|||
import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/data/studyset_payload.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/model/studyset_content.dart';
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
|
|
@ -36,6 +37,23 @@ final toukyou = word(
|
|||
pos: ['n'],
|
||||
);
|
||||
|
||||
final shoku = character(
|
||||
'食',
|
||||
meanings: ['eat', 'food'],
|
||||
on: ['ショク'],
|
||||
kun: ['た.べる'],
|
||||
labels: ['jlpt:n5', 'grade:2'],
|
||||
grade: 2,
|
||||
strokes: 9,
|
||||
);
|
||||
final a = character('あ', labels: ['hiragana'], romaji: 'a');
|
||||
|
||||
/// An instance with JMdict and KANJIDIC2.
|
||||
FakeDictionaryGateway withKanji() => FakeDictionaryGateway(
|
||||
available: [jmdict, kanjidic],
|
||||
words: [taberu, terebi, shoku, a],
|
||||
);
|
||||
|
||||
void main() {
|
||||
late FakeCardGateway cardGateway;
|
||||
late CardRepository cards;
|
||||
|
|
@ -203,6 +221,72 @@ void main() {
|
|||
);
|
||||
});
|
||||
|
||||
testWidgets('beside the words, kanji and kana: their levels in groups, '
|
||||
'one added as character cards tagged with level and grade', (
|
||||
tester,
|
||||
) async {
|
||||
await show(tester, dictionaries: withKanji());
|
||||
await addMaterial(tester, 'From the dictionary');
|
||||
|
||||
expect(find.text('Words'), findsOneWidget);
|
||||
await tester.tap(find.text('Kanji & kana'));
|
||||
await settle(tester);
|
||||
expect(find.text('Kana'), findsOneWidget);
|
||||
expect(find.text('JLPT levels'), findsOneWidget);
|
||||
expect(find.text('School grades'), findsOneWidget);
|
||||
expect(find.text('Grade 2'), findsOneWidget);
|
||||
await tester.tap(find.text('N5').hitTestable());
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Add 1 character'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Added 1 character.'), findsOneWidget);
|
||||
final made = (await cards.cardsOf('japanese')).single;
|
||||
expect(made.content.type, CardType.character);
|
||||
expect(made.content.onReadings, ['ショク']);
|
||||
expect(made.content.entry, (source: 'kanjidic', id: '食'));
|
||||
});
|
||||
|
||||
testWidgets('a character found shows its readings and strokes, and is '
|
||||
'added as a card', (tester) async {
|
||||
await show(tester, dictionaries: withKanji());
|
||||
await addMaterial(tester, 'From the dictionary');
|
||||
await tester.tap(find.text('Kanji & kana'));
|
||||
await settle(tester);
|
||||
|
||||
await tester.enterText(find.byType(TextField).hitTestable(), 'ショク');
|
||||
await tester.pump(const Duration(milliseconds: 300));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('ショク · た.べる'), findsOneWidget);
|
||||
expect(find.text('9 strokes'), findsOneWidget);
|
||||
await tester.tap(find.text('Add').hitTestable());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Added “食”.'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Look up fills a character card with its meanings, readings '
|
||||
'and tags', (tester) async {
|
||||
await show(tester, dictionaries: withKanji());
|
||||
await addMaterial(tester, 'Create flashcards');
|
||||
await tester.tap(find.text('Character'));
|
||||
await settle(tester);
|
||||
|
||||
await tester.tap(find.byTooltip('Look up in the dictionary'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.enterText(find.byType(TextField).last, 'eat');
|
||||
await tester.pump(const Duration(milliseconds: 300));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('eat; food'));
|
||||
await settle(tester);
|
||||
|
||||
expect(find.widgetWithText(TextField, 'eat'), findsOneWidget);
|
||||
expect(find.widgetWithText(TextField, 'ショク'), findsOneWidget);
|
||||
expect(find.widgetWithText(TextField, 'た.べる'), findsOneWidget);
|
||||
expect(find.text('JLPT-N5'), findsOneWidget);
|
||||
expect(find.text('Grade 2'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Look up fills a vocabulary card with a word of the '
|
||||
'dictionary', (tester) async {
|
||||
await show(tester);
|
||||
|
|
|
|||
Loading…
Reference in a new issue