自分を活かして 相手を活かして 今を活かす

【GAS】カタカナ表記をパスポートでも使えるヘボン式ローマ字に変換するスクリプト

こんにちは、おかちゃんせんせいです!

これまで番外編として、業務効率化のために作業を自動化する話を何回かピックアップしました。

【解決】Excel 2019 for MacのVBAにて、複数ファイルを選択する方法 【業務効率化】Windows版Excel VBAでChromeを自動操作して、Webスクレイピングする方法(初期設定編) 【解決】Power AutomateでExcel Onlineにある日付と実行日が一致する場合に、処理を実行するようにする

もちろん自動化すればいいってものでもないですが、自動化することでミスや作業時間を減らし、他に生産的な仕事に時間を割くことができるようになることもあります。

自動化という意味では、プログラムを組まなくてもGoogleスプレッドシートの関数だけで、なんとかやりくりする方法もあります。

今回は、Googleスプレッドシートでマクロを組む際に使用する開発言語Google Apps Script(GAS)についての記事を特集します。

◆お知らせ◆

【まとめ記事】

現在、これまで書いてきた記事をテーマ別にまとめています。

詳しくはこちらから

GASで実現したい作業

メールアドレス作成の際に必要となるアカウント名

今回GASで実現したいことは、
メールアドレス作成の際に必要となるアカウント名を、特定のデータがあれば自動作成すること
です。

本当はそれ以外にもやりたいことはありますが、まずは上述の部分であれば一定のルールのもとで自動化できると考えました。

今のところは、フリガナを見てローマ字に変換して、メールアドレスのアカウント名にしていましたが……

ローマ字で表現するときに、
「H」なのか「F」なのか?
「N」なのか「M」なのか?
「Z」なのか「J」なのか?
よくわからなくなってしまうことがあるので、そんなときには下記サイトにかなり助けられました。

ただ、毎回サイト開いてコピペするのも面倒だし、同じ作業の繰り返し、かつ、一定のルールがあるから自動化できないかと思い、ネットを検索。

カタカナではないですが、GASでローマ字変換するスクリプトが公開されているサイトがありましたので、参考にさせていただきました。

問題は『ヘボン式』になっていないこと

ただ、スクリプトでひらがなの部分をカタカナに変えて、
「はい、終わり!」
というわけにはいかなかったのです。

なぜかというと、参考サイトで紹介されているスクリプトはあくまで訓令式ローマ字に変換するものであって、ヘボン式ではないから。
社内ではアカウント名をヘボン式で表記するルールがあったため、そのままでは使えません。

ちなみに、ヘボン式とはパスポートなどにも使えるローマ字表記で、たとえば下記のような例外ルールがあります。

※その他、個別・例外ルール
①「 ん 」 ・・・ 「 N 」に変換。ただし、「 NB 」・「 NM 」・「 NP 」となる場合は「 N→M 」で表記。
  (例: かんだ→KANDA 、 なんば→NAMBA 、 へんみ→HEMMI 、 らんぽ→RAMPO)
②「 っ 」 ・・・ 後に続く子音を重ねる。ただし、「 CH 」が続く場合は「 TCH 」となる。
  (例: べっぷ→BEPPU 、 こっち→KOTCHI )
③長音について ・・・ 母音の重なりを1文字で表記します。「 おお = OH 」表現も可能なようですが、任意表記のようなので変換君では考慮していません。
  (例: とおる→TORU 、 おおにし→ONISHI )

ヘボン式変換君サイトより引用

他にも微妙に違う表記方法の文字があるので、そういったことは下記サイトを確認しながら編集することに決めました。

サンプルコード

自分が考えたコードではないので、詳しい説明は割愛しますが、、、

要は、まず変換したい文字列(string)を受け取って、splitで分割。該当するカタカナの組み合わせが見つかったら(3文字→2文字→1文字)、紐付けたローマ字で変換していく流れ。

基本的な流れは、すべて参考サイトからの引用になりますが、カタカナの組み合わせはヘボン式用に追加・修正しています。

GAS
 /**
 * フリガナをヘボン式ローマ字に変換
 *
 * @param {string} input - カタカナ
 * @return {string} ローマ字
 */
 function kana2romaji(string) {

    var arrayedString = string.split('');
    var value = '';

    var triTable = {
        'キョウ' : 'kyo','ショウ' : 'sho','チョウ' : 'cho',
        'ニョウ' : 'nyo','ヒョウ' : 'hyo','ミョウ' : 'myo','リョウ' : 'ryo',
        'ギョウ' : 'kyo','ジョウ' : 'sho','ヂョウ' : 'dyo','ビョウ' : 'byo','ピョウ' : 'pyo',
        'ウ゛ァ': 'va','ウ゛ィ' : 'vi', 'ウ゛ゥ' : 'vu','ウ゛ェ' : 've', 'ウ゛ォ' : 'vo'
    };

    var biTable = {
        'ギャ' : 'gya','ギュ' : 'gyu','ギョ' : 'gyo',
        'シャ' : 'sha','シュ' : 'shu','ショ' : 'sho',
        'クウ' : 'ku','スウ' : 'su','ツウ' : 'tsu','ヌウ' : 'nu',
        'フウ' : 'fu','ムウ' : 'mu','ユウ' : 'yu','ルウ' : 'ru',
        'オオ' : 'o','コオ' : 'ko','ソオ' : 'so','トオ' : 'to','ノオ' : 'no',
        'ホオ' : 'ho','モオ' : 'mo','ヨオ' : 'yo','ロオ' : 'ro',
        'コウ' : 'ko','ソウ' : 'so','トウ' : 'to','ノウ' : 'no',
        'ホウ' : 'ho','モウ' : 'mo','ヨウ' : 'yo','ロウ' : 'ro',
        'ゴウ' : 'go','ドウ' : 'do','ボウ' : 'bo','ポウ' : 'po',
        'ジェ' : 'jie','チェ' : 'chie','ティ' : 'tei','ディ' : 'dei','デュ' : 'deyu',
        'ファ' : 'fua','フィ' : 'fui','フェ' : 'fue','フォ' : 'fuo',
        'ヴァ' : 'bua','ヴィ' : 'bui','ヴ' : 'bu','ヴェ' : 'bue','ヴォ' : 'buo',
        'ンバ' : 'mba','ンビ' : 'mbi','ンブ' : 'mbu','ンベ' : 'mbe','ンボ' : 'mbo',
        'ンマ' : 'mma','ンミ' : 'mmi','ンム' : 'mmu','ンメ' : 'mme','ンモ' : 'mmo',
        'ンラ' : 'mra','ンリ' : 'mri','ンル' : 'mru','ンレ' : 'mre','ンロ' : 'mro',        
        'キャ' : 'kya','キュ' : 'kyu','キョ' : 'kyo',
        'ジャ' : 'ja','ジュ' : 'ju','ジョ' : 'jo',
        'チャ' : 'cha','チュ' : 'chu','チョ' : 'cho',
        'ヂャ' : 'dya','ヂュ' : 'dyu','ヂョ' : 'dyo',
        'デャ' : 'dha','デョ' : 'dho',
        'ニャ' : 'nya','ニュ' : 'nyu','ニョ' : 'nyo',
        'ヒャ' : 'hya','ヒュ' : 'hyu','ヒョ' : 'hyo',
        'ビャ' : 'bya','ビュ' : 'byu','ビョ' : 'byo',
        'ピャ' : 'pya','ピュ' : 'pyu','ピョ' : 'pyo',
        'ミャ' : 'mya','ミュ' : 'myu','ミョ' : 'myo',
        'リャ' : 'rya','リュ' : 'ryu','リョ' : 'ryo',
        'テァ' : 'tha','テェ' : 'tee',
        'ウ゛' : 'vu','ア゛' : 'a"',
        'ッカ' : 'kka','ッキ' : 'kki','ック' : 'kku','ッケ' : 'kke','ッコ' : 'kko',
        'ッサ' : 'ssa','ッシ' : 'sshi','ッス' : 'ssu','ッセ' : 'sse','ッソ' : 'sso',
        'ッタ' : 'tta','ッチ' : 'tchi','ッツ' : 'ttu','ッテ' : 'tte','ット' : 'tto',
        'ッナ' : 'nna','ッニ' : 'nni','ッヌ' : 'nnu','ッネ' : 'nne','ッノ' : 'nno',
        'ッハ' : 'hha','ッヒ' : 'hhi','ッフ' : 'ffu','ッヘ' : 'hhe','ッホ' : 'hho',
        'ッマ' : 'mma','ッミ' : 'mmi','ッム' : 'mmu','ッメ' : 'mme','ッモ' : 'mmo',
        'ッヤ' : 'yya', 'ッユ' : 'yyu','ッヨ' : 'yyo',
        'ッラ' : 'rra','ッリ' : 'rri','ッル' : 'rru','ッレ' : 'rre','ッロ' : 'rro',
        'ッワ' : 'wwa',
        'ッガ' : 'gga','ッギ' : 'ggi','ッグ' : 'ggu','ッゲ' : 'gge','ッゴ' : 'ggo',
        'ッザ' : 'zza','ッジ' : 'jji','ッズ' : 'zzu','ッゼ' : 'zze','ッゾ' : 'zzo',
        'ッダ' : 'dda','ッヂ' : 'ddi','ッヅ' : 'ddu','ッデ' : 'dde','ッド' : 'ddo',
        'ッバ' : 'bba','ッビ' : 'bbi','ッブ' : 'bbu','ッベ' : 'bbe','ッボ' : 'bbo',
        'ッパ' : 'ppa','ッピ' : 'ppi','ップ' : 'ppu','ッペ' : 'ppe','ッポ' : 'ppo'
    };

    var uniTable = {
        'ア' : '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','フ' : 'fu','ヘ' : 'he','ホ' : 'ho',
        'マ' : 'ma','ミ' : 'mi','ム' : 'mu','メ' : 'me','モ' : 'mo',
        'ヤ' : 'ya','ユ' : 'yu','ヨ' : 'yo',
        'ラ' : 'ra','リ' : 'ri','ル' : 'ru','レ' : 're','ロ' : 'ro',
        'ワ' : 'wa','ヲ' : 'wo','ン' : 'n',
        'ガ' : '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',
        'ァ' : 'xa','ィ' : 'xi','ゥ' : 'xu','ェ' : 'xe','ォ' : 'xo'
    };

    if(triTable[string] !== undefined){
        return triTable[string];
    } else if(biTable[string] !== undefined) {
        return biTable[string];
    }

    var biCheck = new Object();
    for (var k in biTable){
        var tmp = k.split('');
        biCheck[tmp[0]] = true;
    }

    var triCheck = new Object();
    for (var tk in triTable){
        var tmp = tk.split('');
        triCheck[tmp[0] + tmp[1]] = true;
        biCheck[tmp[0]] = true; 
    }


    var buf = '';
    for(var i = 0; i < arrayedString.length ; i++){
        var str = arrayedString[i];
        buf += str;
        if(buf.length == 3){
            if(triTable[buf] !== undefined){
                value += triTable[buf];
            } else {
                tmp = buf.split('');
                value += biTable[tmp[0] + tmp[1]];
                value += uniTable[tmp[2]] === undefined ? tmp[2] : uniTable[tmp[2]]
            }
            buf = ‘’;
        } else if(buf.length == 2) {
            if(triCheck[buf] !== undefined) { 
            } else if(biTable[buf] !== undefined) {
                    value += biTable[buf];
                    buf = '';
            } else {
                    tmp = buf.split('');
                    value += uniTable[tmp[0]]; 
                    if(uniTable[tmp[1]] !== undefined){
                      value += uniTable[tmp[1]] === undefined ? tmp[1] : uniTable[tmp[1]]; 
                      buf = '';                    
                    }else{
                      buf = ''; 
                      buf += tmp[1];
                    }
            }  
        } else if(biCheck[buf] !== undefined){
        } else { 
                value += uniTable[str] === undefined ? str : uniTable[str];
                buf = '';
        }
    }

    // 母音のoとuが続く場合はuを打ち消すように修正(2024/01/25追記)
    if(value.indexOf('ou') !== -1){
      value = value.replace('ou','o');
    }

    return value;
}

(2022/09/27追記)
テストサンプルをいくつか試していましたら、子音が続く場合(例:テッタ)に正常に動作しないことが判明しました。
修正内容は、下記2箇所です。

・uniTableからャ・ュ・ョ・ッを除外
・buf.length == 2の場合に子音が続くケースを追加条件分岐


(2023/05/31追記)
・文字数の判定で最後が3文字の場合に、bufに値が残ってしまう不具合を解消


(2024/01/25追記)
・クドウの場合、kudouになってしまう不具合を解消

まとめ

まだコードが出来上がったばかりで十分検証ができていませんので、もし不備がありましたらご指摘いただけますと幸いです!


最後まで読んでいただき、ありがとうございました!

4 COMMENTS

ぽんすけ

「くどう」が「kudou」になってしまっております。
調整いただけると助かります。

返信する

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA