PHPでかな(カナ)からヘボン式ローマ字へと変換する

PHPでかな(カナ)からヘボン式ローマ字へと変換する

ふりがな(フリガナ)からヘボン式ローマ字へと変換する方法を紹介します。

既存のウェブツールなど各種あるのですが、どこもコードを公開してくれていなかったので仕方なく作ることになった次第です。

ヘボン式ローマ字について知らない人は各都道府県のパスポート発行ページなど身にいくとわかりやすく解説されたページがあります。(神奈川県パスポートセンター)

基本的な変換ルールに加えて「長音」「撥音」「促音」への対応が必要になります。

手っ取り早く使いたい人はgithubの方を見てください。空白区切りまたは配列で突っ込めば、配列でヘボン式ローマ字が返ってくるようになってます。

変換方法の流れ

以下の流れで変換をしていきます。

  • 文字列先頭から探索開始
  • 2文字読んで一致するものがあれば取得
  • 2文字でなければ1文字で取得
  • 撥音の処理
  • 促音の処理
  • 長音の処理
  • 変換文字列に結合
  • 次の文字へ

のような流れで処理を行っていきます。

通常の変換

「2文字読んで一致するものがあれば取得、なければ1文字取得」の変換はいたって単純です。

あらかじめ以下のように列挙しておきます。

public const KANA_ALPHABET_TABLE = [
        'あ' => 'A',  'い' => 'I',   'う' => 'U',   'え' => 'E',  'お' => 'O',
        'か' => 'KA', 'き' => 'KI',  'く' => 'KU',  'け' => 'KE', 'こ' => 'KO',
        'さ' => 'SA', 'し' => 'SHI', 'す' => 'SU',  'せ' => 'SE', 'そ' => 'SO',
        'た' => 'TA', 'ち' => 'CHI', 'つ' => 'TSU', 'て' => 'TE', 'と' => 'TO',
        'な' => 'NA', 'に' => 'NI',  'ぬ' => 'NU',  'ね' => 'NE', 'の' => 'NO',
        'は' => 'HA', 'ひ' => 'HI',  'ふ' => 'HU',  'へ' => 'HE', 'ほ' => 'HO',
        'ま' => 'MA', 'み' => 'MI',  'む' => 'MU',  'め' => 'ME', 'も' => 'MO',
        'や' => 'YA', 'ゆ' => 'YU',  'よ' => 'YO',
        'ら' => 'RA', 'り' => 'RI',  'る' => 'RU',  'れ' => 'RE', 'ろ' => 'RO',
        'わ' => 'WA', 'ゐ' => 'I',   'ゑ' => 'E',   'を' => 'O',
        'が' => 'GA', 'ぎ' => 'GI',  'ぐ' => 'GU',  'げ' => 'GE', 'ご' => 'GO',
        'ざ' => 'ZA', 'じ' => 'JI',  'ず' => 'ZU',  'ぜ' => 'ZE', 'ぞ' => 'ZO',
        'だ' => 'DA', 'ぢ' => 'JI',  'づ' => 'ZU',  'で' => 'DE', 'ど' => 'DO',
        'ば' => 'BA', 'び' => 'BI',  'ぶ' => 'BU',  'べ' => 'BE', 'ぼ' => 'BO',
        'ぱ' => 'PA', 'ぴ' => 'PI',  'ぷ' => 'PU',  'ぺ' => 'PE', 'ぽ' => 'PO',
        'きゃ' => 'KYA', 'きゅ' => 'KYU', 'きょ' => 'KYO',
        'しゃ' => 'SHA', 'しゅ' => 'SHU', 'しょ' => 'SHO',
        'ちゃ' => 'CHA', 'ちゅ' => 'CHU', 'ちょ' => 'CHO',
        'にゃ' => 'NYA', 'にゅ' => 'NYU', 'にょ' => 'NYO',
        'ひゃ' => 'HYA', 'ひゅ' => 'HYU', 'ひょ' => 'HYO',
        'みゃ' => 'MYA', 'みゅ' => 'MYU', 'みゅ' => 'MYO',
        'りゃ' => 'RYA', 'りゅ' => 'RYU', 'りょ' => 'RYO',
        'ぎゃ' => 'GYA', 'ぎゅ' => 'GYU', 'ぎょ' => 'GYO',
        'じゃ' => 'JA',  'じゅ' => 'JU',  'じょ' => 'JO',
        'びゃ' => 'BYA', 'びゅ' => 'BYU', 'びょ' => 'BYO',
        'ぴゃ' => 'PYA', 'ぴゅ' => 'PYU', 'ぴょ' => 'PYO',
    ];

現在位置の文字が上の配列のキーに該当しているかチェックしていきます。

日本語取り扱いのため、文字列の取得は「mb_」系の関数を使用します。

private function index_char( $index, $kana_name ) {
        if ( $index + 1 < mb_strlen( $kana_name, 'UTF-8' ) ) {
            if ( array_key_exists( mb_substr( $kana_name, $index, 2, 'UTF-8' ), self::KANA_ALPHABET_TABLE ) ) {
                return [
                    'kana'     => mb_substr( $kana_name, $index, 2, 'UTF-8' ),
                    'alphabet' => self::KANA_ALPHABET_TABLE[ mb_substr( $kana_name, $index, 2, 'UTF-8' ) ]
                ];
            }
        }

        if ( array_key_exists( mb_substr( $kana_name, $index, 1, 'UTF-8' ), self::KANA_ALPHABET_TABLE ) ) {
            return [
                'kana'     => mb_substr( $kana_name, $index, 1, 'UTF-8' ),
                'alphabet' => self::KANA_ALPHABET_TABLE[ mb_substr( $kana_name, $index, 1, 'UTF-8' ) ]
            ];
        }

        if ( preg_match( '/[A-Z]/', mb_substr( $kana_name, $index, 1, 'UTF-8' ) ) ) {
            return [
                'kana'     => mb_substr( $kana_name, $index, 1, 'UTF-8' ),
                'alphabet' => mb_substr( $kana_name, $index, 1, 'UTF-8' )
            ];
        }

        return [
            'kana'     => mb_substr( $kana_name, $index, 1, 'UTF-8' ),
            'alphabet' => ''
        ];
    }

入力は「現在位置」と「かな」です。

返り値は変換対象となった「かな」と変換後の「アルファベット」の連想配列になっています。

  • 2文字チェック
  • 1文字チェック
  • 最初からローマ字ならそのまま(後述例外形など)
  • なにも当てはまらなければ、変換後「アルファベット」は空文字列

と非常にシンプルな処理です。

撥音「ん」の処理

続いて撥音「ん」の処理を行っていきます。

「ん」は列挙した文字列群の中にないので、個別対応していきます。

また、次の文字が「B」「M」「P」の場合には「M」に変えないといけません。

private function hatsuon( $index, $kana_name ) {
        if ( $index + 1 < mb_strlen( $kana_name, 'UTF-8' ) ) {
            $next_index_char = $this->index_char( $index + 1, $kana_name );

            $targets = [
                'B', 'M', 'P'
            ];

            if ( in_array( mb_substr( $next_index_char['alphabet'], 0, 1, 'UTF-8' ), $targets, true ) ) {
                return 'M';
            }
        }

        return 'N';
    }

通常変換用の「index_char」で次の文字を取得しています。

その結果に応じて「N」or「M」を返しています。

メイン処理では「index_char」の返り値の「アルファベット」に撥音処理の返り値を代入しています。

促音「っ」の処理

その次は促音の処理を行います。

撥音の後に行っていますが、撥音、促音は順不同です。

次の文字は「CH」になる場合のみ「T」に変換し、それ以外の場合は次の一文字へと変換します。

private function sokuon( $index, $kana_name ) {
        if (  $index + 1 >= mb_strlen( $kana_name, 'UTF-8' ) ) return '';

        $next_index_char = $this->index_char( $index + 1, $kana_name );

        if ( 0 === strpos( $next_index_char['alphabet'], 'CH', 0 ) ) {
            return 'T';
        }

        return mb_substr( $next_index_char['alphabet'], 0, 1, 'UTF-8' );
    }

撥音と同様の形式を促音バージョンにしただけです。

長音の処理

続いて長音の処理です。

長音の形式になりうるのは「AA」「UU」「EE」「OO」「OU」の5種類です。「II」は長音処理をしなくても良いようです。含めてはいるもの日本人で「AA」「EE」はほとんどないです。

実質「UU」「OO」「OU」の際に表記する表記しないを気をつければOKです。

private function chouon( $index, $kana_name, $last_char, $index_char ) {
        $targets = [
            'AA',
            'UU',
            'EE',
            'OO',
            'OU'
        ];
        $join_char = $last_char['alphabet'] . $index_char['alphabet'];
        
        if ( 'お' === $index_char['kana'] && $index + 1 === mb_strlen( $kana_name, 'UTF-8' ) ) {
            return $index_char['alphabet'];
        }

        foreach ( $targets as $target ) {
            if ( preg_match( '/' . $target . '/', $join_char ) ) {
                return '';
            }
        }

        return $index_char['alphabet'];
    }

まず、一個前の変換アルファベットと現在のアルファベットを結合します。

その結合した文字列でパターン一致した場合、現在文字のアルファベットを空文字列にします。

末尾が「お」の場合は記載しなければいけないので先処理をしています。

例外形の処理

以上の処理でほとんどのパターンは変換可能です。

ただ、どうしても漏れるパターンがあります。

漏れるパターンは長音表記だけど長音ではないパターンです。

具体例を挙げると「いのうえ」さん。「INOUE」となり、上の変換パターンに当てはめると「OU」が長音判定を受けてしまい、「INOE」となります。

しかし、「いのうえ」さんで欲しい表記は「INOUE」という表記です。

そこで例外形に関しては先処理を行う必要があります。

私のプログラムの中では「のうえ」「こうちわ」「まつうら」を先変換しています。

「こうちわ」は「こうち」でも良いかと思ったのですが、「おおこうち(大河内)」は「おーこーち」のようなので完全一致で変換させています。

まとめ

最初は簡単かと思いきや思いのほか長音処理で躓きました。

PHPで使用する場合は github から取得してもらえればと思います。

その他の言語で作成する場合は上の記事内容を参考にして作成してみてください。