react-use-kana
という、フォームでのよみがな入力支援のための npm package の内部実装をガッと書き換え、入力文字列からよみがなを抽出する処理を自前で書いてみた。
背景
同 package を作って公開したことは以前にも記事に書いた。
ReactのContextとHooksで日本語のふりがな入力を支援するコンポーネント書いた - valid,invalid
ReactのuseStateで日本語のふりがな入力を支援するhook書いた - valid,invalid
2019年9月に publish した際には React hooks としてのインタフェースだけ提供し、入力文字列をひらがなに変換する処理は historykana という package に移譲していた。しかしある時から、同 package の依存する別 package に脆弱性が見つかり security alert が GitHub repository 上に表示され続けてしまっていた。
放置するのも精神衛生上よくない(と言いつつ半年近く放置したが…)ので入力文字列をひらがなに変換する処理を自前で書いてみることにした。
実装
基本的なコンセプトとしては「入力文字列と直前の入力文字列を比較して"ひらがな"と"ひらがな以外"の対応表を作る」という方針。
たとえば"やまだ"
という入力の次に"山田"
と続けば{ "山田": "やまだ" }
と記憶しておき、この対応表を使って入力文字列をひらがなに置換する。
react-testing-library
を使って書いた hooks の test case を見るとイメージが伝わりやすいと思う。実際にユーザーが入力する順をエミュレートしている。
describe('when several kanas are converted to kanjis at once', () => { test('returns kana based on user input', () => { const { result } = renderHook(() => useKana()); expect(result.current.kana).toEqual(''); ['や', 'やm', 'やま', 'やまd', 'やまだ', '山田'].forEach(value => { act(() => { result.current.setKanaSource(value); }); }); expect(result.current.kana).toEqual('やまだ'); }); });
これだけであれば実装はかんたんに見えるし実際にそう思ったのだが、"ひらがな"と"ひらがな以外"が混在するケースなどもあり、その場合にどの文字とどのひらがなが対応するのかを判別するロジックを一般化するのに苦労した。
変換パターンの組み合わせ
組み合わせを整理してみると 16 patterns ほどあった(変換前と変換後それぞれにひらがなと非ひらがなが含まれるかどうか? 2 ** 4 = 16
)。とはいえ、すべてに対応する実装が逐一必要なわけではなかった。一般化すれば2通りの実装でおおよそをカバーできた。
漢字の連続変換
厄介だったのが '山だ'
=> '山多'
=> '山田'
のように非ひらがな(この場合は漢字)の変換を繰り返すケース。直前との差分では漢字同士なのでひらがなとの対応表を作れないのだが、{ "田": "だ" }
のように以前に入力されたひらがなと対応づけたい。この問題は、「直前に発生したひらがな => 非ひらがなの変換で使用した事前の入力(前述の例では"山だ"
)」を保存しておき、再利用して比較することで対応した('山だ'
=> '山田'
と変換されたとして対応表を作る)。
できないこと
フォームに値を直接貼り付ける場合など、ひらがなを知り得ない状況では当然ながらひらがなを導出できない。
また、おそらく必要ないと思うが、 "山田"
を貼り付けたあとにひらがなの"やまだ"
に変換しなおすようなケースも実装を省いている。
感想
security alertが消えてすっきりした。
ところで、React時代に皆さんがどのようによみがなの自動入力機能を作っているのか気になっている。もしかして、令和ではもう誰もよみがなを入力させるようなフォームなんて作っていない…?