feat(dictionary): Japanese cut into words and looked up on the device
Once the person agrees, the device holds MeCab's IPADIC and the
instance's JMdict and cuts lines as Jisho.org does, so their text never
leaves it (ADR 0012). MeCab comes through mecab_for_dart, whose build
hook compiles it; its tokens are grouped by Ve's rules, written anew in
Dart, with に kept a word of its own. Each word is found as it stands or
as a form of a word, else by its dictionary form, else as the longest
words it holds from token to token; longer words over token boundaries
are offered as before.
The curator packs Ubuntu's compiled IPADIC beside the dictionary files
(curate ipadic, and every update run). The server exports its JMdict
from its tables, so the device finds the same keys and keeps the same
versions, and serves both at /device-dictionaries/, named in
dictionary.deviceFiles('ja') with their hashes; Caddy forwards the path.
The app checks each file against its hash, unpacks IPADIC, builds a
SQLite database from the export on an isolate, and asks the instance
for newer files every 30 days, downloading only what changed. The Watch
screen offers the download once per device with its size (34 MB, some
150 MB once set up); Settings downloads and removes them. Without them,
or should cutting on the device fail, lines go to the instance as
before. MeCab's, IPADIC's and Ve's licences join the licence list.
Every build now needs a C++ compiler; mecab_for_dart holds code_assets
at 1.x.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
8fe2cf3436
commit
1d2011a676
1 changed files with 5487 additions and 245 deletions
|
|
@ -3,6 +3,7 @@ import 'dart:io';
|
|||
|
||||
import 'package:archive/archive.dart';
|
||||
import 'package:args/args.dart';
|
||||
import 'package:halkyon_learn_curator/src/mecab.dart';
|
||||
import 'package:halkyon_learn_curator/src/sources/cedict.dart';
|
||||
import 'package:halkyon_learn_curator/src/sources/hangul_strokes.dart';
|
||||
import 'package:halkyon_learn_curator/src/sources/jlpt.dart';
|
||||
|
|
@ -52,6 +53,14 @@ import 'package:halkyon_learn_curator/src/wordfreq.dart';
|
|||
/// --input raw-wiktextract-data.jsonl.gz --languages de,el,en \
|
||||
/// --output ../halkyon_learn_server/dictionaries
|
||||
///
|
||||
/// MeCab's compiled IPADIC into the pack the app's devices download (ADR
|
||||
/// 0012), from Ubuntu's `mecab-ipadic-utf8` unless another folder is given
|
||||
/// (`update` packs it too, where that package is installed):
|
||||
///
|
||||
/// dart run halkyon_learn_curator:curate ipadic \
|
||||
/// [--input /var/lib/mecab/dic/ipadic-utf8] \
|
||||
/// --output ../halkyon_learn_server/dictionaries
|
||||
///
|
||||
/// CC-CEDICT into its two dictionary files, simplified and traditional:
|
||||
///
|
||||
/// dart run halkyon_learn_curator:curate cedict \
|
||||
|
|
@ -202,6 +211,26 @@ Future<void> main(List<String> args) async {
|
|||
);
|
||||
return;
|
||||
}
|
||||
if (source == 'ipadic') {
|
||||
final output = options.option('output');
|
||||
if (output == null) {
|
||||
_fail(
|
||||
'Usage: curate ipadic [--input <compiled IPADIC>] --output <folder>'
|
||||
'\n${parser.usage}',
|
||||
);
|
||||
}
|
||||
final packed = await packIpadic(
|
||||
Directory(options.option('input') ?? systemIpadic),
|
||||
Directory(output),
|
||||
licence: File(systemIpadicLicence),
|
||||
);
|
||||
stdout.writeln(
|
||||
packed == null
|
||||
? '$ipadicFile in $output is up to date.'
|
||||
: 'Wrote ${packed.path}.',
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (source == 'tatoeba') {
|
||||
final input = options.option('input');
|
||||
final output = options.option('output');
|
||||
|
|
@ -602,6 +631,18 @@ Future<void> _update(
|
|||
greek: greek,
|
||||
),
|
||||
);
|
||||
// MeCab's IPADIC for the devices, where the image installed it; packed
|
||||
// again only when it changed.
|
||||
Future<bool> ipadicOnce() async =>
|
||||
!Directory(systemIpadic).existsSync() ||
|
||||
await attempt('IPADIC', () async {
|
||||
final packed = await packIpadic(
|
||||
Directory(systemIpadic),
|
||||
into,
|
||||
licence: File(systemIpadicLicence),
|
||||
);
|
||||
return [?packed];
|
||||
});
|
||||
// In this order: Korean's words and Hanja take what Wiktionary kept.
|
||||
// What is no longer wanted goes last, once what replaces it is there.
|
||||
Future<bool> languagesOnce() async {
|
||||
|
|
@ -609,7 +650,8 @@ Future<void> _update(
|
|||
!(!await chineseOnce() |
|
||||
!await wiktionaryOnce() |
|
||||
!await koreanOnce() |
|
||||
!await charactersOnce());
|
||||
!await charactersOnce() |
|
||||
!await ipadicOnce());
|
||||
final removed = removesOthers
|
||||
? removeUnwanted(into, languages)
|
||||
: const <File>[];
|
||||
|
|
|
|||
Loading…
Reference in a new issue