feat(curator): KANJIDIC2 and the kana as a dictionary of characters; KanjiVG's strokes for the app
A dictionary file now says whether it holds words or characters (`kind`). The kanjidic adapter turns KANJIDIC2 into one entry per kanji that has a Japanese reading or an English meaning (12,633), with its readings, meanings, school grade, strokes, frequency and JLPT level (Jonathan Waller's, through kanji-data's kanji.json), labelled `jlpt:nX`, `grade:N`/`grade:secondary`, followed by the 71 hiragana and katakana of the kana table with their romaji. A kun reading is found whole; its kanji part only as a loose key, so typing "ta" brings た before every kanji read so. `curate kanjivg` writes KanjiVG's strokes and components as the app's stroke files, one gzipped JSON file per 256 code points, keyed by the code point in each entry's id (KanjiVG's 橅 names itself 機). 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
42c57bd40a
commit
e42cdfbfcb
1 changed files with 1036 additions and 29 deletions
|
|
@ -4,6 +4,7 @@ Turns dictionary downloads into the dictionary files a Halkyon Learn server
|
|||
loads. It reads each source with an adapter of its own and writes one format
|
||||
for all of them, so a new source or language needs a new adapter, never a
|
||||
change to the server or the app. It doesn't depend on Serverpod or the server.
|
||||
It also makes the stroke files the app writes characters with (below).
|
||||
|
||||
## Making a dictionary file
|
||||
|
||||
|
|
@ -27,8 +28,25 @@ JMdict in English, with the JLPT levels and example sentences on top:
|
|||
The whole of JMdict (about 219,000 entries) takes some 20 seconds and
|
||||
300 MB of memory: entries are read one at a time.
|
||||
|
||||
Put the file into the server's `dictionaries` folder. The server loads it on
|
||||
its next start, and again whenever the file changes (see the server's README).
|
||||
KANJIDIC2 in English, with the JLPT levels of its kanji, and the kana:
|
||||
|
||||
1. Download `kanjidic2.xml.gz` from
|
||||
[the EDRDG](https://www.edrdg.org/wiki/index.php/KANJIDIC_Project).
|
||||
2. Download `kanji.json` from
|
||||
[kanji-data](https://github.com/davidluzgouveia/kanji-data): Jonathan
|
||||
Waller's JLPT levels by kanji (`jlpt_new`; nothing else is read).
|
||||
3. Convert, in this package:
|
||||
|
||||
```sh
|
||||
dart run halkyon_learn_curator:curate kanjidic \
|
||||
--input kanjidic2.xml.gz --jlpt kanji.json \
|
||||
--output kanjidic-en.jsonl.gz
|
||||
```
|
||||
|
||||
About 12,800 characters, in some 5 seconds.
|
||||
|
||||
Put the files into the server's `dictionaries` folder. The server loads each on
|
||||
its next start, and again whenever it changes (see the server's README).
|
||||
|
||||
## The format (`halkyon-dictionary`, version 1)
|
||||
|
||||
|
|
@ -39,6 +57,7 @@ The **first line** says what the file holds:
|
|||
| Field | |
|
||||
|---|---|
|
||||
| `format`, `formatVersion` | `"halkyon-dictionary"`, `1` |
|
||||
| `kind` | `words` (a dictionary of words, JMdict) or `characters` (of the characters words are written in, KANJIDIC2). The app shows them apart. |
|
||||
| `source`, `sourceVersion` | The source's name (`jmdict`) and version (`3.6.2 (2026-10-05)`). An instance holds one version of a source for each meaning language. |
|
||||
| `language`, `glossLanguage` | BCP 47 tags: the words' language (`ja`) and the meanings' (`en`). |
|
||||
| `license` | The data's licence, an SPDX identifier (`CC-BY-SA-4.0`). |
|
||||
|
|
@ -50,11 +69,33 @@ The **first line** says what the file holds:
|
|||
|---|---|
|
||||
| `id` | Its id in the source; with the source and meaning language, it names the entry for good. |
|
||||
| `headword`, `reading` | How the word is shown, and how it is read; no `reading` when the headword is its own (a word written in kana). |
|
||||
| `labels` | What it can be picked by: `common`, `jlpt:n5` … `jlpt:n1` (later e.g. `hsk:3`, `topik:1`). |
|
||||
| `rank` | Its order among the source's entries, lower first; JMdict: N5 to N1 in their lists' order, then other common words, then the rest. |
|
||||
| `labels` | What it can be picked by: `common`, `jlpt:n5` … `jlpt:n1` (later e.g. `hsk:3`, `topik:1`); for characters also the school grade (`grade:1` … `grade:6`, `grade:secondary` for the rest of the Jōyō kanji) and the script (`hiragana`, `katakana`). |
|
||||
| `rank` | Its order among the source's entries, lower first; JMdict: N5 to N1 in their lists' order, then other common words, then the rest; KANJIDIC2: hiragana and katakana in the kana table's order, then kanji by JLPT level, then by school grade, then the rest, each by frequency. |
|
||||
| `keys` | What a search matches, lower case: written forms, readings in their own script and romanised, meanings. Everything specific to a language happens here, so the server's search knows no language. |
|
||||
| `looseKeys` | What a search matches after every `keys` match: spellings people type that aren't quite right (romaji with long vowels typed once, `sushi` for すうし). Left out when empty. |
|
||||
| `entry` | The entry itself, as cards take it: `forms` and `readings` (`text`, `common`, the source's `tags`; for a reading the `forms` it belongs to), `senses` (`glosses`; the source's codes in `pos`, `misc`, `field`, `dialect`; `info`; the `forms` and `readings` a sense is limited to; `examples`, sentences using the word in that sense: `text`, the form it takes there, `ja`, the sentence, `en`, its translation, and the source's id, e.g. `tatoeba`). Lists are left out when empty. |
|
||||
| `entry` | The entry itself, as cards take it: `forms` and `readings` (`text`, `common`, the source's `tags`; for a reading the `forms` it belongs to), `senses` (`glosses`; the source's codes in `pos`, `misc`, `field`, `dialect`; `info`; the `forms` and `readings` a sense is limited to; `examples`, sentences using the word in that sense: `text`, the form it takes there, `ja`, the sentence, `en`, its translation, and the source's id, e.g. `tatoeba`). Lists are left out when empty. A character's: `meanings`; `on` and `kun`, its readings as KANJIDIC2 writes them (a kun reading's okurigana after a `.`, a dash where it attaches to others); `grade`, `strokes`, `frequency` (its place among the 2,500 most used in newspapers) and `jlpt` where it has them. A kana's: `romaji` and `script`. |
|
||||
|
||||
## The app's stroke files
|
||||
|
||||
How every kanji and kana is written comes from
|
||||
[KanjiVG](https://kanjivg.tagaini.net) and is bundled with the app, so writing
|
||||
works without a server. To update it, download `kanjivg-<date>.xml.gz` from
|
||||
[KanjiVG's releases](https://github.com/KanjiVG/kanjivg/releases) and, in this
|
||||
package:
|
||||
|
||||
```sh
|
||||
dart run halkyon_learn_curator:curate kanjivg \
|
||||
--input kanjivg-20260714.xml.gz \
|
||||
--output ../halkyon_learn_flutter/assets/kanjivg
|
||||
```
|
||||
|
||||
It writes one gzipped JSON file for every 256 code points that have any,
|
||||
named by the code point's upper bits in hexadecimal (食, U+98DF, is in
|
||||
`098.json.gz`; 94 files, 2.7 MB), so the app reads only the part it needs.
|
||||
Each maps a character to its strokes (`s`, SVG paths in KanjiVG's 109 × 109
|
||||
box, in the order they're written) and the components KanjiVG first splits it
|
||||
into (`c`, when it has any). Then update the release named in the folder's
|
||||
`ATTRIBUTION.md`.
|
||||
|
||||
## Adding a source
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue