chore: Halkyon Learn moves into apps/learn
Its server, client, app and curator, and the tools that write its assets (maps, Anki fixtures, icons), as they are; paths are fixed in the commits that follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
11abd9afce
commit
649499be23
1 changed files with 0 additions and 0 deletions
173
apps/learn/halkyon_learn_curator/test/unihan_test.dart
Normal file
173
apps/learn/halkyon_learn_curator/test/unihan_test.dart
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:archive/archive.dart';
|
||||
import 'package:halkyon_learn_curator/src/sources/cedict.dart';
|
||||
import 'package:halkyon_learn_curator/src/sources/unihan.dart';
|
||||
import 'package:halkyon_learn_curator/src/wordfreq.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
final unihan = Unihan.parse(
|
||||
File('test/fixtures/unihan-sample.txt').readAsLinesSync(),
|
||||
);
|
||||
final cedict = File('test/fixtures/cedict-characters-sample.txt')
|
||||
.readAsStringSync();
|
||||
// The words' places as wordfreq's list has them (的 0, 了 3, 后 41, …).
|
||||
final frequencies = WordFrequencies(const {
|
||||
'了': 3,
|
||||
'我们': 14,
|
||||
'说': 24,
|
||||
'为了': 170,
|
||||
'以后': 160,
|
||||
'后': 41,
|
||||
'学习': 380,
|
||||
'了解': 520,
|
||||
'这': 15,
|
||||
'水': 600,
|
||||
'银行': 1900,
|
||||
'皇后': 9000,
|
||||
});
|
||||
final places = cedictCharacterPlaces(cedict, frequencies);
|
||||
|
||||
/// The entries of a dictionary file's [lines], by character.
|
||||
Map<String, Map<String, Object?>> entriesOf(Iterable<String> lines) => {
|
||||
for (final line in lines.skip(1))
|
||||
if (jsonDecode(line) case final Map<String, Object?> entry)
|
||||
entry['headword']! as String: entry,
|
||||
};
|
||||
|
||||
test("the simplified characters are the standard's, with pinyin, the "
|
||||
'usual reading first, meanings, strokes, radical and HSK level', () {
|
||||
final lines = chineseCharacters(
|
||||
unihan,
|
||||
traditional: false,
|
||||
sourceVersion: '18.0.0',
|
||||
hsk: const {'说': 1, '行': 1, '后': 1},
|
||||
places: places,
|
||||
).toList();
|
||||
|
||||
final header = jsonDecode(lines.first) as Map<String, Object?>;
|
||||
expect(header['source'], 'unihan-hans');
|
||||
expect(header['kind'], 'characters');
|
||||
expect(header['language'], 'zh-Hans');
|
||||
expect(header['sourceVersion'], '18.0.0');
|
||||
final entries = entriesOf(lines);
|
||||
// No traditional character among them.
|
||||
expect(entries.keys, isNot(contains('說')));
|
||||
expect(entries.keys, containsAll(['说', '这', '后', '了', '行', '水']));
|
||||
final xing = entries['行']!;
|
||||
expect(((xing['entry']! as Map)['pinyin'] as List).take(2), [
|
||||
'xíng',
|
||||
'háng',
|
||||
]);
|
||||
expect((xing['entry']! as Map)['strokes'], 6);
|
||||
expect(xing['labels'], ['hsk:1', 'common', 'radical:144']);
|
||||
final shuo = entries['说']!;
|
||||
expect((shuo['entry']! as Map)['meanings'], [
|
||||
'speak',
|
||||
'say',
|
||||
'talk',
|
||||
'scold',
|
||||
'upbraid',
|
||||
]);
|
||||
expect((shuo['entry']! as Map)['traditional'], ['說', '説']);
|
||||
// Found by its pinyin with marks, without, and numbered; written in
|
||||
// the other script, loosely.
|
||||
expect(shuo['keys'], containsAll(['shuō', 'shuo', 'shuo1', 'speak']));
|
||||
expect(shuo['looseKeys'], ['說', '説']);
|
||||
});
|
||||
|
||||
test('the traditional characters are those words in use write: 這 and 了, '
|
||||
'not 这; 後 the usual form of 后, ranked and levelled as it', () {
|
||||
final entries = entriesOf(
|
||||
chineseCharacters(
|
||||
unihan,
|
||||
traditional: true,
|
||||
hsk: const {'后': 1, '了': 1},
|
||||
places: places,
|
||||
),
|
||||
);
|
||||
|
||||
expect(entries.keys, containsAll(['這', '了', '瞭', '後', '后', '說']));
|
||||
expect(entries.keys, isNot(contains('这')));
|
||||
// 後 takes 后's place and level; 后, the queen, ranks by its own words.
|
||||
expect(entries['後']!['labels'], contains('hsk:1'));
|
||||
expect(entries['后']!['labels'], isNot(contains('hsk:1')));
|
||||
expect(
|
||||
entries['後']!['rank']! as int,
|
||||
lessThan(entries['后']!['rank']! as int),
|
||||
);
|
||||
// 了 stands for itself; 瞭 is a form of it of its own.
|
||||
expect(entries['了']!['labels'], contains('hsk:1'));
|
||||
expect(entries['瞭']!['labels'], isNot(contains('hsk:1')));
|
||||
expect((entries['說']!['entry']! as Map)['simplified'], ['说']);
|
||||
// Hong Kong's primary schools teach 說 in the first grade.
|
||||
expect(entries['說']!['labels'], contains('grade:1'));
|
||||
});
|
||||
|
||||
test("CC-CEDICT's words tell each script's characters their places: a "
|
||||
"lone simplified character's to the traditional form longer words use "
|
||||
'most', () {
|
||||
expect(places.simplified['后'], 41);
|
||||
expect(places.traditional['後'], 41);
|
||||
expect(places.traditional['后'], 9000);
|
||||
expect(places.traditional['了'], 3);
|
||||
// 瞭解 is only a variant of 了解 there, and 了 the form 為了 uses.
|
||||
expect(places.traditional, isNot(contains('瞭')));
|
||||
expect(places.traditional, isNot(contains('这')));
|
||||
});
|
||||
|
||||
test('Korean Hanja with their sounds, the taught one first, their native '
|
||||
'readings from Wiktionary, and the Hangul letters before them', () {
|
||||
final lines = koreanHanja(
|
||||
unihan,
|
||||
sourceVersion: '18.0.0',
|
||||
eumhun: const {
|
||||
'水': [(hun: '물', eum: '수')],
|
||||
},
|
||||
).toList();
|
||||
|
||||
final header = jsonDecode(lines.first) as Map<String, Object?>;
|
||||
expect(header['language'], 'ko');
|
||||
expect(header['license'], 'CC-BY-SA-4.0');
|
||||
expect([
|
||||
for (final credit in header['credits']! as List) (credit as Map)['name'],
|
||||
], containsAll(['Unihan Database', 'Wiktionary']));
|
||||
final entries = entriesOf(lines);
|
||||
final water = entries['水']!;
|
||||
expect(water['entry'], {
|
||||
'meanings': ['water', 'liquid', 'lotion', 'juice'],
|
||||
'eum': ['수'],
|
||||
'hun': ['물'],
|
||||
'eumhun': ['물 수'],
|
||||
'strokes': 4,
|
||||
});
|
||||
expect(water['labels'], ['education', 'radical:85']);
|
||||
expect(water['keys'], containsAll(['水', '수', '물', '물 수', 'water']));
|
||||
// 行 is taught as 행, also read 항.
|
||||
expect((entries['行']!['entry']! as Map)['eum'], ['행', '항']);
|
||||
final giyeok = entries['ㄱ']!;
|
||||
expect(giyeok['labels'], ['hangul']);
|
||||
expect(giyeok['entry'], {
|
||||
'romaji': 'g, k',
|
||||
'script': 'hangul',
|
||||
'name': '기역',
|
||||
});
|
||||
expect(giyeok['rank']! as int, lessThan(water['rank']! as int));
|
||||
});
|
||||
|
||||
test("Unihan's zip is read whole, with the version of Unicode it is of", () {
|
||||
final archive = Archive()
|
||||
..add(
|
||||
ArchiveFile.bytes(
|
||||
'Unihan_Readings.txt',
|
||||
File('test/fixtures/unihan-sample.txt').readAsBytesSync(),
|
||||
),
|
||||
);
|
||||
final (:unihan, :version) = Unihan.ofZip(ZipEncoder().encodeBytes(archive));
|
||||
|
||||
expect(version, '18.0.0');
|
||||
expect(unihan.characters, contains('水'));
|
||||
});
|
||||
}
|
||||
Loading…
Reference in a new issue