data(hanja): words, not only syllables

The import kept only readings of one syllable, so the Hanja search could
convert 한 but never 한자, 학교, or 대한민국 — the conversion every other
Korean input method offers.  libhangul's table has 187k readings; both
scripts now keep them all, and the search finds a word as readily as a
syllable.  The daemon pays for it: 24 MB instead of 12, and 170 ms to
start instead of 30.
This commit is contained in:
2026-08-17 12:50:05 +09:00
parent 3527bcd489
commit 2e53627b7d
7 changed files with 424873 additions and 45 deletions

View File

@@ -3,18 +3,19 @@
## Korean Hanja data
`hanja.src` is the tracked, reviewable source for Korean Hanja conversion.
Every data row is exactly one Hanja character, a tab, and one modern Hangul
syllable:
Every data row is Hanja, a tab, and its modern Hangul reading, a character
or a word:
```
漢 한
漢字 한자
```
It contains no word rows such as `견출지` or `방학`. `mkhanja` validates this
contract and groups rows by their Hangul reading to produce the existing
runtime dictionary format. Candidate order follows source order. The source
keeps all retained pairs for review; the generated dictionary stores the first
128 candidates per reading because that is the engine's lookup limit.
`mkhanja` validates that contract and groups rows by their reading to
produce the runtime dictionary. Candidate order follows source order. The
source keeps all retained pairs for review; the generated dictionary stores
the first 128 candidates per reading because that is the engine's lookup
limit.
The source is derived from libhangul release tag `libhangul-0.2.0`. The
annotated tag object is `20afc38922e3595ee3ed5b186f2ea05afe663763`, its
@@ -30,11 +31,10 @@ map/libhangul2hanja upstream >map/hanja.src
map/mkhanja >map/hanja.dict
```
`libhangul2hanja` retains only rows whose reading is one modern Hangul
syllable and whose value is one Hanja character supported by the current
popup: U+3400U+4DBF, U+4E00U+9FFF, or U+F900U+FAFF. Thus word readings,
multi-character values, jamo readings, and supplementary-plane ideographs
are omitted. The import preserves the upstream license header and row order.
`libhangul2hanja` retains only rows whose reading is modern Hangul syllables
throughout and whose value is Hanja the popup can draw: U+3400U+4DBF,
U+4E00U+9FFF, or U+F900U+FAFF. Thus jamo readings, mixed values, and
supplementary-plane ideographs are omitted. The import preserves the upstream license header and row order.
The retained data is BSD 3-Clause licensed by Choe Hwanjin; the complete text
is in `LICENSES/BSD-3-Clause-libhangul-hanja.txt`.

File diff suppressed because it is too large Load Diff

238096
map/hanja.src

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +1,18 @@
#!/usr/bin/env python3
"""Extract single-character Hanja readings from libhangul data."""
"""Extract Hanja readings from libhangul data."""
import sys
from pathlib import Path
def ishangul(s):
return len(s) == 1 and 0xAC00 <= ord(s) <= 0xD7A3
return s != "" and all(0xAC00 <= ord(c) <= 0xD7A3 for c in s)
def ishanja(s):
if len(s) != 1:
return False
c = ord(s)
return (0x3400 <= c <= 0x4DBF
or 0x4E00 <= c <= 0x9FFF
or 0xF900 <= c <= 0xFAFF)
return s != "" and all(0x3400 <= ord(c) <= 0x4DBF
or 0x4E00 <= ord(c) <= 0x9FFF
or 0xF900 <= ord(c) <= 0xFAFF for c in s)
def extract(src, name):
@@ -44,7 +41,7 @@ def extract(src, name):
seen.add(pair)
entries.append(pair)
if not entries:
raise ValueError(f"{name}: no single-character Hanja readings")
raise ValueError(f"{name}: no Hanja readings")
return comments, entries

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Write hanja.dict from a one-Hanja-per-row UTF-8 source."""
"""Write hanja.dict from a Hanja-per-row UTF-8 source."""
import sys
from pathlib import Path
@@ -10,16 +10,13 @@ MAXCANDIDATES = 128
def ishangul(s):
return len(s) == 1 and 0xAC00 <= ord(s) <= 0xD7A3
return s != "" and all(0xAC00 <= ord(c) <= 0xD7A3 for c in s)
def ishanja(s):
if len(s) != 1:
return False
c = ord(s)
return (0x3400 <= c <= 0x4DBF
or 0x4E00 <= c <= 0x9FFF
or 0xF900 <= c <= 0xFAFF)
return s != "" and all(0x3400 <= ord(c) <= 0x4DBF
or 0x4E00 <= ord(c) <= 0x9FFF
or 0xF900 <= ord(c) <= 0xFAFF for c in s)
def read(path):
@@ -42,9 +39,9 @@ def read(path):
raise ValueError(f"{path}:{lineno}: need Hanja<TAB>reading")
hanja, reading = fields
if not ishanja(hanja):
raise ValueError(f"{path}:{lineno}: need one BMP Hanja character")
raise ValueError(f"{path}:{lineno}: need BMP Hanja")
if not ishangul(reading):
raise ValueError(f"{path}:{lineno}: need one Hangul syllable")
raise ValueError(f"{path}:{lineno}: need Hangul syllables")
pair = (hanja, reading)
if pair in seen:
raise ValueError(f"{path}:{lineno}: duplicate Hanja reading")