Created
August 21, 2026 07:56
-
-
Save hibiyasleep/de584f9479568ae81043e9d3e36f415f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env zsh | |
| # | |
| # hanfix.zsh — fix commands that were typed with the Korean (2-beolsik) | |
| # keyboard layout switched on by mistake, e.g.: | |
| # | |
| # $ ㅣㄴ -미ㅗ | |
| # -> ls -alh | |
| # | |
| # It works by reversing the standard 2-beolsik key mapping: every Hangul | |
| # syllable is decomposed into its jamo (초성/중성/종성), and each jamo is | |
| # mapped back to the Latin key that produces it. Anything that isn't | |
| # Hangul (already-correct English, digits, symbols, spaces, filenames | |
| # typed in Korean on purpose, etc.) is left completely untouched, so | |
| # mixed input is handled fine. | |
| # | |
| # USAGE | |
| # As a one-off converter/runner: | |
| # ./hanfix.zsh 'ㅣㄴ -미ㅗ' | |
| # | |
| # For automatic correction of "command not found" errors, source this | |
| # file from your .zshrc: | |
| # source /path/to/hanfix.zsh | |
| # After that, mistyped Hangul commands are retried automatically. | |
| emulate -L zsh | |
| # --------------------------------------------------------------------- | |
| # 2-beolsik keymap tables | |
| # --------------------------------------------------------------------- | |
| # 19 initial consonants (초성), in Unicode decomposition order | |
| typeset -ga _CHO=(r R s e E f a q Q t T d w W c z x v g) | |
| # 21 vowels (중성), in Unicode decomposition order. Compound vowels | |
| # (ㅘㅙㅚㅝㅞㅟㅢ) are just two keystrokes typed back to back. | |
| typeset -ga _JUNG=(k o i O j p u P h hk ho hl y n nj np nl b m ml l) | |
| # 28 final consonants (종성); index 1 = "no final consonant" | |
| typeset -ga _JONG=( | |
| "" r R rt s sw sg e f fr | |
| fa fq ft fx fv fg a q qt t | |
| T d w c z x v g | |
| ) | |
| # Standalone compatibility jamo (U+3131-U+3163), for jamo that never got | |
| # combined into a full syllable, e.g. a lone ㄴ or ㅣ. | |
| typeset -ga _JAMO=( | |
| r R rt s sw sg e E f fr fa fq ft fx fv fg a q Q qt t T d w W c z x v g | |
| k o i O j p u P h hk ho hl y n nj np nl b m ml l | |
| ) | |
| # --------------------------------------------------------------------- | |
| # qwerty_from_hangul <string> | |
| # Prints <string> with every Hangul character replaced by the QWERTY | |
| # keystrokes that would have produced it on a 2-beolsik layout. | |
| # --------------------------------------------------------------------- | |
| qwerty_from_hangul() { | |
| emulate -L zsh | |
| local input="$1" out="" ch | |
| local -i cp cho jung jong idx | |
| for ch in "${(@s::)input}"; do | |
| cp=$(( #ch )) # codepoint of this single character | |
| if (( cp >= 0xAC00 && cp <= 0xD7A3 )); then | |
| # Composed syllable (가-힣): decompose into 초성/중성/종성 | |
| idx=$(( cp - 0xAC00 )) | |
| jong=$(( idx % 28 )) | |
| idx=$(( idx / 28 )) | |
| jung=$(( idx % 21 )) | |
| cho=$(( idx / 21 )) | |
| out+="${_CHO[cho+1]}${_JUNG[jung+1]}${_JONG[jong+1]}" | |
| elif (( cp >= 0x3131 && cp <= 0x3163 )); then | |
| # Standalone jamo, not (yet) part of a composed syllable | |
| out+="${_JAMO[cp-0x3131+1]}" | |
| else | |
| # Not Hangul at all -- leave mixed-in input exactly as typed | |
| out+="$ch" | |
| fi | |
| done | |
| print -r -- "$out" | |
| } | |
| # --------------------------------------------------------------------- | |
| # Optional: auto-retry unknown commands after converting them. | |
| # Add `source /path/to/hanfix.zsh` to your .zshrc to enable this. | |
| # --------------------------------------------------------------------- | |
| command_not_found_handler() { | |
| local original="$*" converted | |
| converted="$(qwerty_from_hangul "$original")" | |
| if [[ -n "$converted" && "$converted" != "$original" ]]; then | |
| print -u2 "zsh: command not found: $1 -- retrying as: $converted" | |
| eval "$converted" | |
| return $? | |
| fi | |
| print -u2 "zsh: command not found: $1" | |
| return 127 | |
| } | |
| # --------------------------------------------------------------------- | |
| # Direct execution mode: `./hanfix.zsh 'ㅣㄴ -미ㅗ'` converts and runs it. | |
| # When this file is `source`d instead, this block is skipped and only | |
| # the functions above (plus command_not_found_handler) get defined. | |
| # --------------------------------------------------------------------- | |
| if [[ "$ZSH_EVAL_CONTEXT" == "toplevel" ]]; then | |
| if (( $# == 0 )); then | |
| print -u2 "usage: $0 <mistyped command>" | |
| exit 1 | |
| fi | |
| typeset converted | |
| converted="$(qwerty_from_hangul "$*")" | |
| print -r -- "-> $converted" | |
| eval "$converted" | |
| exit $? | |
| fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment