ひらがな↔カタカナ変換

2025 03 01
6

ひらがなカタカナ変換

既存にはユーザー名は漢字(+ひらがな)の組み合わせで表示していましたが、クライアント側で漢字表記だけでは読み方がわからないため、ふりがな表記を追加してほしいという要望がありました。
マーケティング部にて本件を進める中で、他システムとの連携・統合のためにカタカナ表記にしてほしいという依頼もありました。

しかし、DBにはひらがなのデータしか保持していなかったため、このデータをカタカナに変換する処理を実装する必要がありました。

そこで課題は大きく分けて以下の2点

  • サーバーから取得したひらがなデータをカタカナに変換
  • 既存のユーザー名表示にふりがな表記を追加

ひらがなとカタカナ

まず、ひらがなを判定するためにUnicodeを確認しました。
ひらがなひらがな
カタカナカタカナ
ひらがなとカタカナのUnicode値の差が 0x60 であることがわかります。
(ブロックあたり16文字、16×6なので10進数では96の差)

つまり、ひらがなの'あ'(U+3042)をカタカナの'ア'(U+30A2)に変換するには,'あ'のUnicodeに0x60を加算すれば良いということです。
U+3042 (ひらがな 'あ') + 0x60 = U+30A2 (カタカナ 'ア')

ひらがなUnicod
カタカナUnicod


ひらがなをカタカナに変換

ひらがなの有効対象範囲は3041~3096に設定しました。

  1. ひらがなかを判定
  2. 0x60を換算して返却
// ひらがなをカタカナに変換
export const convertToKatakana = (input: string): string => {
  return input.replace(/[\u3041-\u3096]/g, (match) =>
    String.fromCharCode(match.charCodeAt(0) + 0x60)
  )
}

カタカナをひらがなに変換

今回の要件には含まれませんが、カタカナをひらがなに変換する場合は逆に-0x60を加算します。
カタカナの有効対象範囲は30A1~30F6に指定

// カタカナをひらがなに変換
export const convertToHiragana = (input: string): string => {
  return input.replace(/[\u30A1-\u30F6]/g, (match) =>
    String.fromCharCode(match.charCodeAt(0) - 0x60)
  )
}

ひらがなカタカナ

両方を同時に扱うシステムはほとんどないと思いますが、
こうすれば共通関数として実装できます。

// ひらがなとカタカナを相互に変換
export const toggleKana = (input: string): string => {
  return input.replace(/[\u3041-\u3096\u30A1-\u30F6]/g, (match) => {
    const code = match.charCodeAt(0);
    return String.fromCharCode(code + (code < 0x3097 ? 0x60 : -0x60));
  });
}

ふりがな表記(+ルビタグ)

変換したカタカナをユーザー名の上に表示する必要があります。
ふりがなを表示する際に代表的に使用されるのは、<ruby>ルビタグがあります。

<ruby>平仮名<rp>(</rp><rt>ひらがな</rt><rp>)</rp></ruby>

平仮名(ひらがな)
このように<ruby>, <rt>, <rp>を組み合わせてふりがなを表示できます。

しかし、このルビタグをそのまま使用するには以下のような問題がありました。

  1. デザインのカスタマイズに限界がある
  2. ブラウザ間の差異が発生する可能性がある
  3. 対応する漢字にマッチさせず、左寄せで表示したい
  4. ユーザー情報詳細ページの表示スペースが限られており、名前が長いユーザー(外国人ユーザーなど)の場合は2行表示にする

-> そのため、最終的にはルビタグを採用せず、独自に実装して表示する方法を採用しました。
既存のユーザー名の上にタグを1つ追加し、CSSのみで制御したため、<rt>, <rp>を使用するルビタグよりも簡潔に表現できました。


おわりに

ひらがなとカタカナの変換は、Unicodeの固定オフセット値を利用することで簡単に処理できました。
Unicodeの一定の数値差を利用する方法は簡潔で信頼性も高いため、他の文字処理にも適用可能な柔軟性を備えた手法だと考えています。