Technical Docs
Word Counter の設計
文字数・単語数・行数・バイト数をリアルタイム計測する設計と、TextEncoder API を活用したバイト数計算の仕組みを解説します。
1. 設計思想 — なぜこのツールが必要か
ブログ記事・メタディスクリプション・SNS投稿など、多くのコンテンツには文字数制限があります。 Googleが推奨するメタディスクリプションは120〜160文字、 Twitter(X)は全角140文字相当という具合です。 コンテンツを書きながらリアルタイムで文字数を確認できれば、 別サービスへのコピー&ペーストが不要になります。
また、ファイルサイズや通信量を意識する場面ではバイト数が重要です。 日本語(UTF-8)は1文字3バイト消費するため、 文字数とバイト数の乖離を意識せずに設計すると問題が起きることがあります。
2. 技術アーキテクチャ
analyzeText(input: string): TextStats が唯一の純粋関数です。 引数はテキスト文字列のみで、外部依存も副作用も一切ありません。
Layer 2 の Client Component は textarea の onChange イベントで この関数を呼び出します。Reactの再レンダリングは入力のたびに発生しますが、analyzeText() は計算コストが非常に低いため、 デバウンスなしでもスムーズなリアルタイム更新を実現できます。
返り値の TextStats 型は5つのプロパティ(charsWithSpaces・charsWithoutSpaces・words・lines・bytes)を持ち、 Client Component がそのままUIの各メトリクスカードに対応させています。
3. ロジック解説
各メトリクスの計算方法を解説します。
- スペース込み文字数:
input.lengthをそのまま返します。 JavaScriptのString.lengthは UTF-16 コードユニット数ですが、 日本語・ASCII ともに1文字=1コードユニットのため問題ありません (絵文字等のサロゲートペアは考慮外)。 - スペースなし文字数:
/\s/gで空白文字(スペース・タブ・改行)を 削除してからlengthを取得します。 - 単語数:
input.trim()後に/\s+/で分割します。 空文字列の場合は 0 を返すよう別途ハンドリングしています。 - 行数:
'\n'で分割した配列の長さです。 空文字列は 0 行、改行なしの1行テキストは 1 行となります。 - バイト数:
new TextEncoder().encode(input).byteLengthで UTF-8 エンコード後のバイト数を取得します。ASCII は 1 バイト、 日本語(平仮名・漢字等)は 3 バイトとして計算されます。
4. 品質管理
src/lib/tools/__tests__/word-counter.test.ts に Vitest による単体テストを実装しています。
- ASCII テキスト: 英数字・スペース・改行を含む文字列の 各メトリクスが期待値と一致するか確認。
- 日本語テキスト: ひらがな・漢字の文字列で バイト数が文字数の3倍になることを検証(UTF-8の仕様確認)。
- エッジケース: 空文字列(すべて 0)、空白のみの文字列(単語数 0)、 改行のみの文字列の正確なハンドリングを確認。