「参拾萬」と打たれても慌てない。日本の数値入力を快適にするVue 3 / TypeScript 向けライブラリ

日本のWEBフォームにおいて、数値入力ほどユーザーに不親切な箇所はない。

「半角数字で入力してください」というエラー。全角の「1」を打つと弾かれる。カンマを入れると消される。「1.5万」なんて論外。この「システム側の都合をユーザーに押し付ける」仕様が、入力体験を著しく損なわせていると感じていた。

そこで、日本語特有の数値入力をよしなに処理し、Vue 3 / TypeScript 環境でスマートに扱うためのライブラリを作成した。

目次

解決したい悩み

  • 全角・記号のバリデーションが煩わしい: ユーザーに半角を強要するのではなく、システム側で正規化したい。
  • 「1.5万」や「参拾萬」を扱いたい: 日本語話者にとって自然な単位表記を無視せず、正確な数値としてパースしたい。
  • IME変換を壊したくない: 入力中にリアルタイムで値を書き換えてしまい、変換中の文字が消える現象を防ぎたい。
  • Vue 3でロジックが散らばる: 監視、バリデーション、整形といった一連の流れを使い回したい。

数値入力の「タイミング」を設計する

このライブラリの核は、処理を「入力中」「停止中」「確定時」の3フェーズに分離したことにある。

1. 入力中:ユーザーを自由にさせる

入力イベント(input)の最中には、データの書き換えを一切行わない。全角、漢字、記号、何を入力しても許容する。
ここを制限したり自動置換したりすると、IME(日本語入力)の挙動と衝突し、ユーザーは激しいストレスを感じるからだ。

2. 停止中:そっと診断する

ユーザーの手が止まってから debounce を介してバリデーションを走らせる。
ここで初めて「内部的に」パースを行い、エラーがあれば表示する。ポイントは、画面上の入力ボックスの文字はまだ書き換えないこと。「裏側でこっそりチェックする」のがミソだ。

3. 確定時(Blur):体裁を整える

フォーカスが外れた瞬間に、初めて正規化された値を画面に書き戻す。
ここで全角は半角になり、「1.5万」は「15000」になり、指定された小数点桁数にピシッと整う。ユーザーがその項目を離れたとき、データがクリーンになる。この「離脱時」というタイミングが、ユーザーにとってもシステムにとっても最も安全で納得感がある。

内部で行われる3段階の処理パイプライン

「汚い入力文字列」が「クリーンな数値」になるまで、ライブラリ内部では以下の3つのステップを順に踏んでいる。ここを分離したことで、柔軟なカスタマイズが可能になった。

  1. 正規化レイヤー(Normalization)
    全角から半角への置換、不要な記号の除去、漢数字や「万・億」といった単位を絶対値に展開する。ここでは「意味を変えずに、プログラムが読める形」に整える。
  2. 修正処理レイヤー(Correction)
    小数点以下の桁数制限、切り捨て、四捨五入といった「丸め」を行う。ユーザーの曖昧な入力を、ビジネスルールに基づいた正確な数値に補正するステップ。
  3. バリデーションレイヤー(Validation)
    確定した最終的な値に対して、最小値・最大値・必須チェックなどの制約を検証し、エラーメッセージを確定させる。

モバイルでの入力しやすさを担保する

HTML側の属性についても触れておく。数値入力だからといって type="number" を使うのは、実はおすすめしない。PCブラウザで予期せぬスピンボタン(上下矢印)が出たり、モバイルでの挙動が不安定になるからだ。

今回のライブラリを使用する場合は、以下の属性指定を推奨する。

<input
    type="tel"
    inputmode="numeric"
/>

type="tel" はモバイル端末で数字キーパッドを呼び出すために最も安定した指定であり、inputmode="numeric" を組み合わせることで、OSに対して「数値入力である」ことを明確に伝えられる。

漢数字パース

「情報量を落とさない」ための工夫として、漢数字や位単位(兆・億・万)、大字(壱萬、参拾萬)の解析を入れている。

// 解析例
parseKanjiNumerals("1.5億"); // "150000000"
parseKanjiNumerals("参拾萬"); // "300000"
parseKanjiNumerals("壱萬弐拾"); // "10020"

これは単なる多機能自慢ではない。「万」と打った瞬間にそれを「ノイズ」として消去してしまう既存の正規化へのアンチテーゼだ。ユーザーが単位まで含めて入力したなら、その意図を汲み取って数値化する。

ライブラリの構成と使い方

ソースコードは GitHub に公開した。

導入

Vue 3 の Composable (useNumericField) を使い、設定を渡すだけで「自由な入力・一呼吸置いた診断・離脱時の整形」がセットで手に入る。

import { useNumericField } from 'moglog-numeric-input-kit';

const amount = useNumericField("0", "資産額", {
  decimalPlaces: 0, // 整数モード
  mode: "trunc",    // 切り捨て
  min: 0            // 0以上
});

テンプレートへの紐付け

v-modelblurfocus のイベントをバインドする。

<input
  type="tel"
  inputmode="numeric"
  v-model="amount.rawInput.value"
  @blur="amount.onBlur"
  @focus="amount.onFocus"
/>
<p v-if="amount.error.value">{{ amount.error.value }}</p>

良い点と微妙な点

自作してみて感じた正直な感想。

良い点:

  • 入力中に勝手に文字が消えたり書き換わったりしない安心感。
  • Vueのコンポーネント側が驚くほどスッキリする。
  • 「1.5万」や「壱千万」のようなコピペ入力に耐えられる。

微妙な点:

  • 「一億」が「10000(万円)」か「100000000(円)」かはプロジェクトの単位設計に依存するため、表示側の baseUnit 設定で調整が必要になる。
  • 単純な正規表現に比べればコード量は増えるが、保守性とUXの向上を考えれば妥当なトレードオフだと思う。

まとめ

数値入力は、突き詰めると「どこまでユーザーを信頼し、どのタイミングでデータをクリーンにするか」という設計の話になる。  
制約でユーザーを縛るのではなく、入力の自由を保証した上で、離脱時にシステムが責任を持ってデータを整える。この「タイミングの設計」と「3段階の処理フロー」こそが、日本語入力における数値フォームの最適解だと考えている。

コメント

コメントする

目次