--- url: /playground.md --- # Playground Loading the converter and dictionary… --- url: /guide/intro.md --- # Introduction Gukhanmun is a hanja-to-hangul converter for Korean text. Given mixed-script input containing hanja (漢字) alongside hangul, it produces hangul-only output or annotated output in several formats: plain text, Markdown, and HTML (including `` markup). The converter is backed by the _Standard Korean Dictionary_ (標準國語大辭典) and supports South Korean (`ko-KR`) and North Korean (`ko-KP`) orthography presets, configurable segmentation, numeral handling, homophone disambiguation, and per-character annotation directives. ## Choosing an interface | Interface | When to use | | ---------- | ----------------------------------------------------- | | CLI | Convert files from the terminal or in shell pipelines | | Rust | Embed the converter in a Rust application or library | | JavaScript | Use from Node.js, Deno, Bun, or a browser | ### CLI The `gukhanmun` command-line tool reads from files or standard input and writes to standard output or a file. It auto-detects the format from the file extension (_.html_, _.md_) and supports all conversion options via flags. Start here: [_Installation_](/guide/cli/install.md). ### Rust The `gukhanmun` crate exposes a `Builder`/`Converter` API. Build a converter once, reuse it across calls, and stream output token by token if needed. Feature flags let you trim the binary size by disabling HTML, Markdown, or the bundled dictionary. Start here: [_Installation_](/guide/rust/install.md). ### JavaScript Two packages cover different environments: - `@gukhanmun/wasm` runs in any JavaScript runtime via WebAssembly. Use it in browsers, serverless functions, or anywhere native addons are not available. - `@gukhanmun/napi` is a native Node.js addon with higher throughput. It supports Node.js 20+, Deno 2.0+, and Bun 1.0+. Both share the same `load()` API and accept identical options. Start here: [_Installation_](/guide/javascript/install.md). --- url: /guide/cli/install.md --- # Installation Gukhanmun is distributed as a single binary with no runtime dependencies. ## Via mise If you use [mise], install a prebuilt binary with a single command: ```sh mise use -g aqua:dahlia/gukhanmun ``` The `-g` flag installs it globally. Omit it to activate the tool only in the current project directory. [mise]: https://mise.jdx.dev/ ## Via Windows Package Manager (`winget`) On Windows, install via `winget`: ```powershell winget install HongMinhee.Gukhanmun ``` ## From crates.io If you have a Rust toolchain installed, install from crates.io: ```sh cargo install gukhanmun-cli gukhanmun-mkdict ``` This compiles the binaries and places them in _\~/.cargo/bin/_. Make sure that directory is on your `PATH`. ## Prebuilt binaries Prebuilt binaries for Linux (x86\_64, aarch64), macOS (x86\_64, aarch64), and Windows (x86\_64) are attached to each release on GitHub: [https://github.com/dahlia/gukhanmun/releases](https://github.com/dahlia/gukhanmun/releases) Download the archive for your platform, extract it, and place the `gukhanmun` binary somewhere on your `PATH`. ## The `gukhanmun-mkdict` companion The mise install and the prebuilt archives also include `gukhanmun-mkdict`, the tool for compiling [custom dictionaries](/guide/cli/dictionary.md). No separate installation step is needed. ## Verify the installation ```sh gukhanmun --help gukhanmun-mkdict --help ``` --- url: /guide/cli/start.md --- # Quick start Gukhanmun reads text containing hanja and writes the same text with hanja replaced by their hangul readings. ## Converting a file Pass a file path as the positional argument: ```sh gukhanmun input.txt ``` Output goes to standard output by default. Use `-o` to write to a file instead: ```sh gukhanmun -o output.txt input.txt ``` To replace a file in-place, pass the same path for both input and output. Gukhanmun writes to a temporary file first and replaces the original atomically, so the original is never left in a partial state: ```sh gukhanmun -o document.txt document.txt ``` ## Reading from standard input Omit the file argument to read from standard input: ```sh echo "漢字를 한글로" | gukhanmun # → 한자를 한글로 ``` ## Specifying the input format Gukhanmun infers the format from the file extension: | Extension | Format | | ------------------ | ---------- | | _.txt_, _(none)_ | Plain text | | _.html_, _.htm_ | HTML | | _.md_, _.markdown_ | Markdown | Override the detected format with `-f`: ```sh gukhanmun -f text/plain input.html # treat as plain text gukhanmun -f text/html snippet.txt # treat as HTML gukhanmun -f text/markdown article.txt # treat as Markdown ``` The Markdown format also accepts variant parameters: ```sh gukhanmun -f "text/markdown; variant=GFM" post.md ``` ## Verbose logging Add `-v` to print debug information to standard error: ```sh gukhanmun -v input.txt ``` --- url: /guide/cli/dictionary.md --- # Dictionaries Gukhanmun uses dictionaries to look up the hangul readings of hanja. The `ko-kr` preset uses the bundled _Standard Korean Dictionary_ (標準國語大辭典), and the `ko-kp` preset uses the bundled _Open Korean Dictionary_ (우리말샘) North Korean category. ## Bundled dictionaries The bundled dictionary selected by the preset is loaded automatically. No extra flags are needed for most Korean text. To disable every bundled dictionary, for example when you want to rely entirely on a custom dictionary, pass `--no-bundled-dictionaries`: ```sh gukhanmun --no-bundled-dictionaries -d my-dict.gukfst input.txt ``` Use the `ko-kp` preset for North Korean orthography. It disables the initial sound law and uses the bundled _Open Korean Dictionary_ (우리말샘) North Korean (北韓語) dictionary: ```sh gukhanmun --preset ko-kp input.txt ``` ## Custom dictionaries Supply one or more custom dictionaries with `-d` (or `--dictionary`). The flag can be repeated: ```sh gukhanmun -d legal.gukfst input.txt gukhanmun -d legal.gukfst -d names.gukcdb input.txt ``` Gukhanmun supports two binary dictionary formats: | Format | Extension | Lookup | Notes | | ------ | --------- | ----------------------- | --------------------------------------------------- | | FST | _.gukfst_ | $O(\text\{key length})$ | Preferred for lattice segmentation; smaller on disk | | CDB | _.gukcdb_ | $O(1)$ | Simpler layout; easier to audit by hand | Dictionaries are tried in the order they appear on the command line, with the bundled dictionary consulted last. The first match wins. ## Building a custom dictionary The _.gukfst_ and _.gukcdb_ files are compiled artifacts, not something you edit by hand. You author your entries as a plain text table and compile them with `gukhanmun-mkdict`. The `gukhanmun-mkdict` builder is installed together with `gukhanmun`, whether you [install via mise](/guide/cli/install.md#via-mise) or [download a prebuilt archive](/guide/cli/install.md#prebuilt-binaries). If you instead built from crates.io, install the builder the same way: ```sh cargo install gukhanmun-mkdict ``` Write your entries as a tab-separated file with a `hanja` key column and a `hangul` reading column: ```tsv hanja hangul 北京 베이징 學校 학교 ``` Two optional columns control how renderers treat each entry: set `require_hanja` to `true` to keep the source hanja visible (for homophones that need disambiguation), and `require_hangul` to `true` to force a hangul gloss in the original-script rendering mode. ```tsv hanja hangul require_hanja require_hangul 北京 베이징 false false 色깔論 색깔론 false true ``` Compile the table into an FST dictionary (the default format): ```sh gukhanmun-mkdict --output legal.gukfst legal.tsv ``` Pass `--format cdb` to produce a _.gukcdb_ file instead. You can supply several input files, which are merged in order; `--merge` selects how duplicate keys are resolved (`error`, `first-wins`, or `last-wins`). Add `--validate` to reopen the output and confirm every entry round-trips, and `--metadata KEY=VAL` to embed provenance such as the source or license. Then load the result like any other custom dictionary: ```sh gukhanmun -d legal.gukfst input.txt ``` CSV and JSON Lines inputs are also accepted, and a few more advanced options are available. See [the internals section](/internals/dictionary-format.md) for the full dictionary file format specification. --- url: /guide/cli/conversion.md --- # Conversion options These flags control the linguistic rules applied during conversion. ## Preset `--preset` selects a preconfigured combination of defaults: | Preset | Dictionary | Initial sound law | Homophone window | Use case | | ----------------- | -------------- | ----------------- | ---------------- | ------------------------ | | `ko-kr` (default) | Bundled stdict | Enabled | Per-block | South Korean orthography | | `ko-kp` | None | Disabled | Off | North Korean orthography | ```sh gukhanmun --preset ko-kp input.txt ``` Individual flags below override the preset's defaults. ## Segmentation strategy `--segmentation` controls how word boundaries are found: - `lattice` (default): finds the globally optimal segmentation by evaluating all dictionary matches at every position with dynamic programming. Best for accuracy. - `eager`: greedy left-to-right longest-match. Faster but may mis-segment compound words. ```sh gukhanmun --segmentation eager input.txt ``` ## Numeral handling `--numerals` controls how hanja numerals are rendered. The CLI defaults to `smart`; pass `--numerals hangul-phonetic` to keep Seonbi-style phonetic readings for calendar words such as `六月`. | Strategy | 二〇一六年 | 十一月 | 一千二百三十四 | | ------------------- | ----- | ----- | ------- | | `hangul-phonetic` | 이공일륙년 | 십일월 | 일천이백삼십사 | | `positional-arabic` | 2016년 | (n/a) | (n/a) | | `additive-arabic` | (n/a) | 11월 | 1234 | | `smart` (default) | 2016년 | 11월 | 1234 | ```sh gukhanmun --numerals hangul-phonetic input.txt ``` ## Initial sound law The initial sound law (頭音法則) is enabled by default for `ko-kr` and disabled for `ko-kp`. It affects character-by-character fallback readings for characters not found in any dictionary; dictionary entries already encode their correct readings. | Input | Law enabled (`ko-kr`) | Law disabled (`ko-kp`) | | ----- | --------------------- | ---------------------- | | 來日 | 내일 | 래일 | | 理由 | 이유 | 리유 | | 女子 | 여자 | 녀자 | Override with explicit flags: ```sh gukhanmun --no-initial-sound-law input.txt # disable gukhanmun --initial-sound-law input.txt # enable (redundant for ko-kr) ``` ## Parenthetical reading annotations Mixed-script input sometimes spells a word together with an explicit parenthetical gloss, whether hanja-first (`庫間(곳간)`) or hangul-first (`곳간(庫間)`). By default Gukhanmun recognizes such a gloss, removes the now redundant parenthetical, and shows the word in both scripts: | Input | Default output | `--no-collapse-parens` | | -------- | -------------- | ---------------------- | | `庫間(곳간)` | `곳간(庫間)` | `곳간(곳간)` | | `곳간(庫間)` | `곳간(庫間)` | `곳간(곳간)` | A parenthetical can also pin an alternative reading. `數字` normally reads `숫자`, but `數字(수자)` fixes the reading to `수자` for that occurrence: ```sh echo '數字(수자)' | gukhanmun # 수자(數字) ``` A reading annotation is told apart from a definition by two rules. A parenthetical that exactly matches the word's reading always collapses (this covers 사이시옷 readings like `庫間(곳간)`). Otherwise, an alternative reading is accepted only when it has one hangul syllable per hanja character, each a valid Sino-Korean reading of that character (as in `數字(수자)`). A definition gloss matches neither rule and passes through untouched: ```sh echo '庫間(물건을 간직하여 두는 곳)' | gukhanmun # 곳간(물건을 간직하여 두는 곳) ``` Foreign transliterations are likewise left alone, because they are not valid per-character readings (for example `蔣介石(장제스)`, where `介` reads `개`, not `제`). Pass `--no-collapse-parens` to disable the behaviour entirely. ## Homophone disambiguation Different hanja words can share the same hangul reading (for example, 連霸 and 連敗 are both 연패). In the default `hangul-only` rendering mode, Gukhanmun can keep the hanja in parentheses for such words so readers can tell them apart. `--disambiguation` sets the scope across which a reading is considered ambiguous: | Value | Behaviour | | --------------------------------- | ------------------------------------------ | | `off` | No disambiguation | | `per-block` (default for `ko-kr`) | Reset at paragraph/list/heading boundaries | | `per-section` | Reset at heading boundaries | | `per-document` | Track across the entire input | ```sh gukhanmun --disambiguation per-section input.txt ``` `--homophone-detection` chooses which readings count as ambiguous within the window: | Value | Behaviour | | ------------------------- | ------------------------------------------------------------------------------------ | | `context-local` (default) | Gloss a word only when a different-meaning homophone actually appears in the window. | | `dictionary-wide` | Also gloss readings shared by other hanja forms anywhere in the dictionary. | ```sh gukhanmun --homophone-detection dictionary-wide input.txt ``` `context-local` keeps hangul-only output clean. `dictionary-wide` is broader, but with the bundled _Standard Korean Dictionary_ nearly every common reading has some homophone, so it glosses most Sino-Korean words. To always gloss a specific word regardless of context, use the `--require-hanja` flag instead (see [_User directives_](/guide/cli/directives.md)). ## Only recognized words are disambiguated Homophone disambiguation operates on words the dictionary recognizes as units. A hanja sequence with no dictionary entry of its own is not treated as a single word, and its fallback (non-dictionary) characters are never glossed; any recognized single-character entries inside it (such as `紫`) are still handled on their own. For example, `自由` and `子游` are both bundled entries read `자유`, so `自由와 子游` becomes `자유(自由)와 자유(子游)`; but `紫楡` has no entry of its own, so under the default context-local strategy `自由와 紫楡` becomes `자유와 자유` with no gloss, because the engine never sees a second `자유` unit to collide with `自由`. To disambiguate the whole term, add it to a custom dictionary and load it with `--dictionary` (see [_Dictionaries_](/guide/cli/dictionary.md)) so the engine treats it as a single unit. ## First-occurrence clearing `--first-occurrence` removes annotations from characters whose presentation was already forced earlier in the window: | Value | Behaviour | | --------------- | -------------------------------- | | `off` (default) | Never clear | | `per-block` | Clear within a paragraph/block | | `per-section` | Clear within a section | | `per-document` | Clear across the entire document | ```sh gukhanmun --first-occurrence per-section input.txt ``` ## Error recovery `--recovery` controls behaviour when an unrecoverable parse error occurs (currently relevant for HTML input only): - `strict` (default) — abort with an error - `lenient` — skip the problematic fragment and continue ```sh gukhanmun -f text/html --recovery lenient input.html ``` --- url: /guide/cli/rendering.md --- # Rendering modes `--rendering` controls how hanja and their hangul readings appear in the output. The default is `hangul-only`. ## Available modes ### `hangul-only` (default) Replaces each hanja word with its hangul reading. The original hanja is discarded. ```sh echo "漢字" | gukhanmun --rendering hangul-only # → 한자 ``` ### `hangul-hanja-parens` Outputs the hangul reading followed by the original hanja in parentheses. ```sh echo "漢字" | gukhanmun --rendering hangul-hanja-parens # → 한자(漢字) ``` ### `hanja-hangul-parens` Outputs the original hanja followed by the hangul reading in parentheses. ```sh echo "漢字" | gukhanmun --rendering hanja-hangul-parens # → 漢字(한자) ``` ### `ruby-on-hangul` Wraps the hangul reading in a `` element with the hanja as the annotation. Requires HTML or Markdown output (`-f text/html` or `-f text/markdown`); in plain text falls back to parentheses. ```sh echo "漢字" | gukhanmun -f text/html --rendering ruby-on-hangul # → 한자(漢字) ``` The `` elements hold parenthesized fallback text. Browsers that support `` hide them and stack the annotation above the base, while browsers without `` support display the gloss in parentheses (`한자(漢字)`) instead of running it into the base text. ### `ruby-on-hanja` Wraps the original hanja in a `` element with the hangul reading as the annotation. ```sh echo "漢字" | gukhanmun -f text/html --rendering ruby-on-hanja # → 漢字(한자) ``` ### `original` Keeps the original hanja in the output and adds a gloss only where needed to disambiguate homophones. Use `--original-gloss` to choose the gloss style: | `--original-gloss` | Output | | ------------------ | ------------------------------------------------ | | `parens` | 漢字(한자) | | `ruby` | `漢字(한자)` | ```sh echo "漢字" | gukhanmun --rendering original --original-gloss parens # → 漢字(한자) ``` --- url: /guide/cli/directives.md --- # Directives Directives let you override the dictionary's annotation marks for specific hanja. You can require that a hanja always show its reading, always show the original hanja, or skip annotation entirely. ## Inline flags ### `--require-hanja` Forces the hanja to always appear in the output (relevant for `hangul-only` mode, where it would otherwise be dropped): ```sh gukhanmun --require-hanja 漢 --require-hanja 字 input.txt ``` ### `--require-hangul` Forces the hangul reading to appear alongside the hanja (relevant for `original` mode): ```sh gukhanmun --rendering original --require-hangul 東 input.txt ``` ### `--skip-annotation` Suppresses any annotation for the hanja, leaving it as-is in the output: ```sh gukhanmun --skip-annotation 中 input.txt ``` ## Glob patterns Each directive has a `-glob` variant that matches a shell-style glob against the hanja key: ```sh gukhanmun --require-hanja-glob "東*" input.txt gukhanmun --require-hangul-glob "北[京津]" input.txt gukhanmun --skip-annotation-glob "中*" input.txt ``` ## Directives file For many directives, use a TSV file with `--directives`: ```sh gukhanmun --directives overrides.tsv input.txt ``` The file has three tab-separated columns: | Column | Values | | --------- | ---------------------------------------------------- | | `action` | `require-hanja`, `require-hangul`, `skip-annotation` | | `pattern` | The hanja string to match | | `kind` | `literal` or `glob` | Lines starting with `#` are comments. Blank lines are ignored. Example _overrides.tsv_: ```tsv # Force reading for proper nouns require-hanja 東京 literal require-hanja 北京 literal # Suppress annotation for all middle-dot separated names skip-annotation *·* glob ``` --- url: /guide/cli/html.md --- # HTML processing Pass `-f text/html` (or use a _.html_/_.htm_ extension) to convert an HTML document or fragment. Gukhanmun parses the HTML, converts hanja in text nodes and attributes, and serialises the result back to HTML while preserving all tags and attributes. ## Elements that are always preserved Gukhanmun never modifies content inside these elements regardless of any other settings: - ``, ``, `
`, `` — code and preformatted text
- `