オブジェクトの広場はオージス総研グループのエンジニアによる技術発表サイトです

プログラミング

(プログラマのための)
いまさら聞けない標準規格の話

第3回 ローマ字編
オージス総研 技術部 アドバンストテクノロジセンター
伊藤 喜一
2022年2月21日

プログラマがシステム開発において共通で必要となる、技術と業務の狭間の共通知識を解説します。連載第3回はローマ字編です。

0. 今回の概要

システム開発で必要となる標準規格の話、第3回はローマ字編です。
ローマ字は、訓令式、日本式、ヘボン式など、いくつかの表記法が存在することで、現在混乱した状況にあり、誤った利用をされている場面もときどき見かけます。本記事ではまず各方式の差異を整理し、正しいローマ字を理解していただこうと思います。続いて、システムにおいて、カナ・ローマ字変換を設計・実装する際のポイントを紹介します。

1. ローマ字とは

ローマ字は日本語をアルファベット (ラテン文字) で表記する方法です。
駅名標や道路標識などの外国人向け案内や、外国語の文章中で日本語を表記する際に用いられることが多いですが、日本語の文章全体をローマ字で書くことも可能です。
コンピュータプログラムで、変数名やメソッド名などの識別子に用いられることもありますね。

ローマ字には訓令式、日本式、ヘボン式の大きく3種類があります。
どの方式も1つの音を「子音+母音」に分解し、それぞれにアルファベットを割り当てるのが基本です。
内閣訓令・告示や国際規格の ISO 3602 では訓令式を推奨しており、小学校でも最初に訓令式を習いますが、社会へ出ると実際にはヘボン式が多く使われているのが現状です。

以下に、内閣訓令・告示『ローマ字のつづり方』を抜粋します。
[ ] に記載したカナ、補足は元の資料にはなく、私が追加したものです。

前書き

  1. 一般に国語を書き表わす場合は、第1表に掲げたつづり方によるものとする。
  2. 国際的関係その他従来の慣例をにわかに改めがたい事情にある場合に限り、第2表に掲げたつづり方によってもさしつかえない。
  3. 前二項のいずれの場合においても、おおむねそえがきを適用する。

第1表 〔( ) は重出を示す。〕

[訓令式]

[ア] [イ] [ウ] [エ] [オ]
a i u e o
[カ] [キ] [ク] [ケ] [コ] [キャ] [キュ] [キョ]
ka ki ku ke ko kya kyu kyo
[サ] [シ] [ス] [セ] [ソ] [シャ] [シュ] [ショ]
sa si su se so sya syu syo
[タ] [チ] [ツ] [テ] [ト] [チャ] [チュ] [チョ]
ta ti tu te to tya tyu tyo
[ナ] [ニ] [ヌ] [ネ] [ノ] [ニャ] [ニュ] [ニョ]
na ni nu ne no nya nyu nyo
[ハ] [ヒ] [フ] [ヘ] [ホ] [ヒャ] [ヒュ] [ヒョ]
ha hi hu he ho hya hyu hyo
[マ] [ミ] [ム] [メ] [モ] [ミャ] [ミュ] [ミョ]
ma mi mu me mo mya myu myo
[ヤ] [ユ] [ヨ]
ya (i) yu (e) yo
[ラ] [リ] [ル] [レ] [ロ] [リャ] [リュ] [リョ]
ra ri ru re ro rya ryu ryo
[ワ] [ヰ] [ヱ] [ヲ]
wa (i) (u) (e) (o)
[ガ] [ギ] [グ] [ゲ] [ゴ] [ギャ] [ギュ] [ギョ]
ga gi gu ge go gya gyu gyo
[ザ] [ジ] [ズ] [ゼ] [ゾ] [ジャ] [ジュ] [ジョ]
za zi zu ze zo zya zyu zyo
[ダ] [ヂ] [ヅ] [デ] [ド] [ヂャ] [ヂュ] [ヂョ]
da (zi) (zu) de do (zya) (zyu) (zyo)
[バ] [ビ] [ブ] [ベ] [ボ] [ビャ] [ビュ] [ビョ]
ba bi bu be bo bya byu byo
[パ] [ピ] [プ] [ペ] [ポ] [ピャ] [ピュ] [ピョ]
pa pi pu pe po pya pyu pyo

第2表

[ヘボン式]

[シャ] [シ] [シュ] [ショ]
sha shi shu sho
[ツ]
tsu
[チャ] [チ] [チュ] [チョ]
cha chi chu cho
[フ]
fu
[ジャ/ヂャ] [ジ/ヂ] [ジュ/ヂュ] [ジョ/ヂョ]
ja ji ju jo

[日本式]

[ヂ] [ヅ] [ヂャ] [ヂュ] [ヂョ]
di du dya dyu dyo
[クヮ]
kwa
[グヮ]
gwa
[ヲ]
wo

そえがき

  1. はねる音 [撥音]「ン」はすべて n と書く。
  2. はねる音を表わす n と次にくる母音字または y とを切り離す必要がある場合には、n の次に ’ [アポストロフィ] を入れる。
  3. つまる音 [促音] は、最初の子音字を重ねて表わす。
  4. 長音は母音字の上に ^ [サーカムフレックス] をつけて表わす。なお、大文字の場合は、母音字を並べてもよい。
  5. 特殊音の書き表わし方は自由とする。
  6. 文の書きはじめ、および固有名詞は語頭を大文字で書く。なお、固有名詞以外の名詞の語頭を大文字で書いてもよい。

ときどき第2表をヘボン式と誤解されることがありますが、『ローマ字のつづり方 解説』に記載されているように6行目以下は日本式の表記になります。

「ローマ字のつづり方」は、「まえがき,第1表,第2表,そえがき」から成っています。第1表にはいわゆる訓令式のつづり方、第2表にはヘボン式(表の上から5列目まで)と日本式(6列目以下)のつづり方のうち訓令式と異なるものだけを掲げてあり、一般に国語を書き表す場合は第1表に掲げたつづり方により、国際的関係その他従来の慣例をにわかに改めがたい事情にある場合に限り、第2表によっても差し支えない旨を「まえがき」で述べています。

なお、『ローマ字のつづり方』のそえがきに記載されていませんが、ヘボン式では一般に次の通り表記します。

  1. はねる音 (撥音)「ン」は n と書く。ただし、m、b、p の前では m を使う。
  2. はねる音を表わす n と次にくる母音字または y とを切り離す必要がある場合には、n の次に - (ハイフン) を入れる。
  3. つまる音 (促音) は、最初の子音字を重ねて表わす。ただし、直後が ch の場合は tch とする。
  4. 長音は母音字の上に ¯ (マクロン) をつけて表わす。

また、訓令式、ヘボン式では、助詞の「は」「へ」「を」を、発音の通り waeo と書きます。

2. ローマ字の種類と誤ったローマ字

本章では、日本で使われているローマ字の種類と、それらの特徴、簡単な歴史を説明します。

2-1. 訓令式・日本式

訓令式・日本式は、発音の正確さよりも、日本語の構造、つづりの規則性を重視したローマ字表記法です。

日本式

1885年に物理学者の田中舘愛橘 (たなかだて あいきつ) が『理學協會雑誌』で提唱した方式で、かなのつづり方との対応を重視したローマ字表記法です。
サ行、タ行、ザ行は s、t、z を用いて、sa si su se so、ta ti tu te to、za zi zu ze zo と書きます。
ダ行の「ヂ」「ヅ」を di du、「ヂャ」「ヂュ」「ヂョ」を dya dyu dyo と書き、ザ行と書き分けます。

清音 濁音
s sa se so z za ze zo
si su sya syu syo zi zu zya zyu zyo
t ti tu tya tyu tyo d di du dya dyu dyo
ta te to da de do

訓令式

1937年に内閣訓令第3号として公的に定められたローマ字表記法です。
サ行、タ行、ザ行は s、t、z を用いて、sa si su se so、ta ti tu te to、za zi zu ze zo と書きます。
ダ行の「ヂ」「ヅ」はザ行の「ジ」「ズ」と同じ発音のため、zi zu と書きます。

清音 濁音
s sa se so z za ze zo
si su sya syu syo zi zu zya zyu zyo
t ti tu tya tyu tyo
ta te to d da de do

新日本式

1979年に、言語学者の服部四郎が『新版 音韻論と正書法 ―新日本式つづり方の提唱―』(大修館書店) で提唱したローマ字表記法です。
訓令式とほぼ同じですが、タ行の「チ」「ツ」を ci cu、「チャ」「チュ」「チョ」を cya cyu cyo と書きます。日本語で、「チ」「ツ」と「ティ」「トゥ」は異なる音として識別されているという主張らしいです。
結果として、訓令式ではサ行・タ行とザ行・ダ行の関係が s,t → z、t → d,z と多対多になるのに対して、新日本式では s,c → z、t → d と多対1になります。

清音 濁音
s sa se so z za ze zo
si su sya syu syo zi zu zya zyu zyo
c ci cu cya cyu cyo
t ta te to d da de do

ISO 3602

1989年に国際標準化機構で承認された国際規格で、訓令式を採用しています。
ただし、厳密翻字に限って日本式ローマ字を使用するとしています。
ISO 3602 をヘボン式に変更する動きがあるようですが、現時点の状況は不明です。

99式

1999年に社団法人日本ローマ字会が提案したローマ字表記法です。
ISO 3602 とほぼ同じですが、長音を母音を重ねて書く点、外来語などの特殊音の書き方を定めている点が異なります。ただし、訓令式・日本式の支持者からも批判があったようで、特殊音の書き方はホームページから削除されています。

2-2. ヘボン式

ヘボン式は英語の発音への準拠を重視したローマ字表記法です。
使用する機会も多いと思いますので、『ローマ字のつづり方』の第1表と第2表を合わせた表を掲示しておきます。太字が訓令式と異なる部分です。

[ア] [イ] [ウ] [エ] [オ]
a i u e o
[カ] [キ] [ク] [ケ] [コ] [キャ] [キュ] [キョ]
ka ki ku ke ko kya kyu kyo
[サ] [シ] [ス] [セ] [ソ] [シャ] [シュ] [ショ]
sa shi su se so sha shu sho
[タ] [チ] [ツ] [テ] [ト] [チャ] [チュ] [チョ]
ta chi tsu te to cha chu cho
[ナ] [ニ] [ヌ] [ネ] [ノ] [ニャ] [ニュ] [ニョ]
na ni nu ne no nya nyu nyo
[ハ] [ヒ] [フ] [ヘ] [ホ] [ヒャ] [ヒュ] [ヒョ]
ha hi fu he ho hya hyu hyo
[マ] [ミ] [ム] [メ] [モ] [ミャ] [ミュ] [ミョ]
ma mi mu me mo mya myu myo
[ヤ] [ユ] [ヨ]
ya (i) yu (e) yo
[ラ] [リ] [ル] [レ] [ロ] [リャ] [リュ] [リョ]
ra ri ru re ro rya ryu ryo
[ワ] [ヰ] [ヱ] [ヲ]
wa (i) (u) (e) (o)
[ガ] [ギ] [グ] [ゲ] [ゴ] [ギャ] [ギュ] [ギョ]
ga gi gu ge go gya gyu gyo
[ザ] [ジ] [ズ] [ゼ] [ゾ] [ジャ] [ジュ] [ジョ]
za ji zu ze zo ja ju jo
[ダ] [ヂ] [ヅ] [デ] [ド] [ヂャ] [ヂュ] [ヂョ]
da (ji) (zu) de do (ja) (ju) (jo)
[バ] [ビ] [ブ] [ベ] [ボ] [ビャ] [ビュ] [ビョ]
ba bi bu be bo bya byu byo
[パ] [ピ] [プ] [ペ] [ポ] [ピャ] [ピュ] [ピョ]
pa pi pu pe po pya pyu pyo

旧ヘボン式

1867年に、アメリカの医療伝道宣教師、ジェームス・カーティス・ヘボン (James Curtis Hepburn) が『和英語林集成』で提唱し、1886年の『和英語林集成 (第3版)』で定義されたローマ字表記法です。
「1. ローマ字とは」でも書きましたが、次の通り表記します。

  1. はねる音 (撥音)「ン」は n と書く。ただし、m、b、p の前では m を使う。
  2. はねる音を表わす n と次にくる母音字または y とを切り離す必要がある場合には、n の次に - (ハイフン) を入れる。
  3. つまる音 (促音) は、最初の子音字を重ねて表わす。ただし、直後が ch の場合は tch とする。
  4. 長音は母音字の上に ¯ (マクロン) をつけて表わす。

修正ヘボン式 (英米規格)

1954年に研究社『新和英大辞典 (第3版)』で定義されたローマ字表記法です。
旧ヘボン式と、はねる音 (撥音) の表記が異なります。

  1. はねる音 (撥音)「ン」はすべて n と書く。
  2. はねる音を表わす n と次にくる母音字または y とを切り離す必要がある場合には、n の次に ’ (アポストロフィ) を入れる。

英国では『アメリカ地名委員会およびイギリス地名常置委員会の1976年合意に基づく修正ヘボン式』、米国では『アメリカ図書館協会およびアメリカ議会図書館のローマ字表記法』において、修正ヘボン式が用いられています。

駅名標

大正時代、駅名標ではヘボン式が用いられており、1938年に鉄道省達第127号でいったん訓令式に変更されましたが、戦後GHQの指令でヘボン式に書き換えられ、1946年に運輸省達第176号による『鉄道掲示規程』改正でヘボン式と明記されました。
旧ヘボン式に準じたローマ字表記法が使用されています。

パスポート

旧ヘボン式に準じたローマ字表記法ですが、長音記号 (^ 、¯) は表記しません。
2000年の改正から、O の長音 OH やヘボン式以外のローマ字表記法も、申請により利用可能になりましたが、一度どちらかを選択すると、その後の変更はできないようです。

道路標識

1986年に建設省『道路標識令』でヘボン式が採用されました。
修正ヘボン式に準じ、はねる音 (撥音) は n で統一されていますが、母音字や y との区切りに - (ハイフン) を使用し、長音記号 (^ 、¯) は表記しません。
普通名詞はローマ字ではなく英語を使用します (Pref.、City、Sta. など)。

2-3. 誤った表記法

誤ったローマ字

日常生活やシステム開発の現場において、ヘボン式と訓令式を混同した jya jyu jyo、cya cyu cyo などの誤ったローマ字を見かけることが多く、気持ち悪いことこの上ないです。
英語の j には y の音が既に含まれてるため y は不要です。日本を Jyapan、中国を Cyina とはつづりませんよね。
既に廃止されましたが、コンピュータの日本語入力の規格 JIS X 4063 でこれらの表記を許容したことも、誤ったローマ字を拡散させる一因になったと思われます。

異なる表記法の混在

同じ文章または単語の中で、shisya (支社) のようにヘボン式と訓令式が混在している例もたまに見かけます。ヘボン式なら shisha、訓令式なら sisya が正しい表記です。

2-4. 各方式の差異一覧

ローマ字の各方式の表記法の差異の一覧です。太字の箇所が各方式で特徴的な部分です。

(表は横にスクロールしてご覧ください)

カナ 訓令式 日本式 99式 新日本式 ヘボン式 英米規格 駅名標 パスポート 道路標識 誤り
(制定年) 1937 1885 1999 1979 1886 1976 1946
クヮ (ka) kwa (kwa) (ka)
グヮ (ga) gwa (gwa) (ga)
シャ sya sya sya sya sha sha sha sha sha
si si si si shi shi shi shi shi
シュ syu syu syu syu shu shu shu shu shu
ショ syo syo syo syo sho sho sho sho sho
ジャ zya zya zya zya ja ja ja ja ja jya
zi zi zi zi ji ji ji ji ji
ジュ zyu zyu zyu zyu ju ju ju ju ju jyu
ジョ zyo zyo zyo zyo jo jo jo jo jo jyo
チャ tya tya tya cya cha cha cha cha cha (cya)
ti ti ti ci chi chi chi chi chi (ci)
チュ tyu tyu tyu cyu chu chu chu chu chu (cyu)
チョ tyo tyo tyo cyo cho cho cho cho cho (cyo)
ヂャ (zya) dya zya [dya] (zya) (ja) ja ja ja ja jya
(zi) di zi [di] (zi) (ji) ji ji ji ji
ヂュ (zyu) dyu zyu [dyu] (zyu) (ju) ju ju ju ju jyu
ヂョ (zyo) dyo zyo [dyo] (zyo) (jo) jo jo jo jo jyo
tu tu tu cu tsu tsu tsu tsu tsu
(zu) du zu [du] (zu) (zu) zu zu zu zu
hu hu hu hu fu fu fu fu fu
(i) (wi) (wi) (i) (i) (i)
(e) (we) (we) (e) (e) (e)
(o) wo o [wo] (o) o o o o o
ンバ (ン+mbp) nba nba nba nba (mba) nba mba mba nba
ンア (ン+母音) n'a n'a n'a ña (n-a) n'a n-a na n-a
ンヤ (ン+y) n'ya n'ya n'ya ñya (n-ya) n'ya n-ya nya n-ya
ンワ (ン+w) nwa nwa n'wa nwa nwa nwa nwa nwa nwa
ッチ (ッ+ch) tti tti tti cci (tchi) tchi tchi tchi tchi
アー
(長音)
â (Aa) â (Aa) aa â (aa) (ā/Aa) ā ā a a
アア
(長音)
â (Aa) â (Aa) aa â (aa) (ā/Aa) ā ā a a
イー
(長音)
î (Ii) î (Ii) ii î (ii) (ī/Ii) ī (ii) ī (ii) i/ii ii
イイ
(長音)
î (Ii) î (Ii) ii î (ii) (ī/Ii) ī (ii) ī (ii) i/ii ii
ウー
(長音)
û (Uu) û (Uu) uu û (uu) (ū/Uu) ū ū u u
ウウ
(長音)
û (Uu) û (Uu) uu û (uu) (ū/Uu) ū ū u u
エー
(長音)
ê (Ee) ê (Ee) ee ê (ee) (ē/Ee) ē ē e e
エエ
(長音)
ê (Ee) ê (Ee) ee ê (ee) (ē/Ee) ē ē e e
オー
(長音)
ô (oo) ô (Oo) oo ô (Oo) (ō/Oo) ō ō o (OH) o
オオ
(長音)
ô (Oo) ô (Oo) oo ô (oo) (ō/Oo) ō ō o (OH) o
オウ
(長音)
ô (Oo) ô (Oo) ou ô (oo) (ō/Oo) ō ō o (OH) o

(助詞)
(wa) (ha) wa [ha] wa (wa) wa

(助詞)
(e) (he) e [he] e (e) e

(助詞)
(o) (wo) o [wo] o (o) o

2-5. 各方式のメリット・デメリット

ローマ字の各方式のメリット・デメリットを整理します。

訓令式

  • メリット:
    • 日本語の構造と整合し、つづりの規則性があるため、覚えやすく、間違えにくい。
    • si ti を「シ」「チ」、「スィ」「ティ」どちらで発音しても、日本語話者ならば理解可能。
    • 発音に対して表記が一意に決まる (zi、zu)。
    • 内閣訓令・告示や国際規格の ISO 3602 で推奨されている。
  • デメリット:
    • 英語話者に正しく読んでもらいにくい。
    • zi zu がカナの「ジ」「ズ」、「ヂ」「ヅ」のどちらに対応するか分かりにくい。

日本式

  • メリット:
    • 日本語の構造と整合し、つづりの規則性があるため、覚えやすく、間違えにくい。
    • si ti を「シ」「チ」、「スィ」「ティ」どちらで発音しても、日本語話者ならば理解可能。
    • カナのつづりと対応しやすい。
  • デメリット:
    • 英語話者に正しく読んでもらいにくい。
    • 同じ発音に対して zi zu と di du を書き分ける必要がある。

ヘボン式

  • メリット:
    • 英語話者に正しく読んでもらいやすい。
    • 発音に対して表記が一意に決まる (ji、zu)。
    • 実社会で一番広く利用されている。
  • デメリット:
    • 日本語の構造と整合せず、つづりの規則性が乱れているため、覚えにくく、間違えやすい。
    • 撥音、長音などに、いくつかの流儀が存在し、統一されていない。
    • 子音は英語に準拠しているが、母音は英語と異なるため、結局正しく発音してもらえない。
    • 英語以外の外国語話者には正しく読んでもらいにくい (ドイツ語では ch を「ヒ」と発音など)。
    • ji ju がカナの「ジ」「ズ」、「ヂ」「ヅ」のどちらに対応するか分かりにくい。
    • 長音記号を省略することが多いが、Ono (「小野」「大野」) のように日本語を正しく表記できない。
    • 長音記号に h を使った場合、母音、y、h が続くと読みを一意に特定できない (oha、ohya、ohha)。
      → oh'a、oh'ya、oh'ha のように区切る方法も考えられるが一般的でない。

3. ローマ字を使うときの注意点

3-1. 正しいローマ字を使う

本記事では、どの表記法を使うべきという主張はしません。
訓令式が日本語としては合理的で標準規格にも適合しますが、実社会ではヘボン式が一番使われており、おそらくこの状況は余程のことがないと変わらないかと思います。
いずれにしても、どの方式を使うか決めたら一貫してその方式を使用し、誤ったローマ字を使ったり、複数の表記法を混在したりしないようにしましょう。

3-2. ヘボン式を使う場合

ヘボン式を使う場合、以下のようにした方が好ましいと思うのですが、残念ながら、パスポートを筆頭にそのような状況にはなっていません。ただし、可能な場面では意識してもらえると、多少なりとも事態の改善に向かうかもしれません。

m、b、p の前の「ン」を m にしない。

日本語話者は、m、b、p の前であろうがなかろうが、「ン」を同じものと認識し区別しません。また、厳密には「3回」「3台」「3枚」の「ン」はすべて発音が異なるそうで、「3枚」だけ書き分けるのは非合理的です。
他にも以下の理由で、「ン」を n と m で書き分けるのはやめた方がよいと思います。

  • 例えば「シン」を検索するとき、shin、shim で2回検索しなければならない。
  • 辞書順にソートしたときに、「ン」が2ヶ所に分かれ、順序が逆転してしまう。
  • 英語でも rainbow や gunman など、複合語では m、b、p の前が m にならない。
  • 日本語で「ン」を含む単語は「難波」「新橋」「新聞」のように複合語が多い。

区切文字 (‘、-)、長音記号 (^ 、¯) を省略しない。

以下の例のように、日本語では区切文字 ('、-) や長音記号 (^ 、¯) を省略すると、全く別の言葉になってしまうことが多いです。

  • shinai (市内、竹刀)、shin'ai (親愛)
  • hoko (矛)、hōko (宝庫)、hokō (歩行、補講)、hōkō (方向、芳香、奉公)

Unicode が利用可能な場面では、長音記号付きのアルファベットも普通に使え、そのような環境も増えているので、積極的に使っていけたらと思います。

文字 Â Î Û Ê Ô
Unicode (NFC) U+00C2 U+00CE U+00DB U+00CA U+00D4
Unicode (NFD) U+0041 U+0302 U+0049 U+0302 U+0055 U+0302 U+0045 U+0302 U+004F U+0302
HTML Â Î Û Ê Ô
文字 â î û ê ô
Unicode (NFC) U+00E2 U+00EE U+00FB U+00EA U+00F4
Unicode (NFD) U+0061 U+0302 U+0069 U+0302 U+0075 U+0302 U+0065 U+0302 U+006F U+0302
HTML â î û ê ô
文字 Ā Ī Ū Ē Ō
Unicode (NFC) U+0100 U+012A U+016A U+0112 U+014C
Unicode (NFD) U+0041 U+0304 U+0049 U+0304 U+0055 U+0304 U+0045 U+0304 U+004F U+0304
HTML Ā Ī Ū Ē Ō
文字 ā ī ū ē ō
Unicode (NFC) U+0101 U+012B U+016B U+0113 U+014D
Unicode (NFD) U+0061 U+0304 U+0069 U+0304 U+0075 U+0304 U+0065 U+0304 U+006F U+0304
HTML ā ī ū ē ō

4. カナ・ローマ字変換の設計

最初にお断りしておくと、完全な「カナ・ローマ字変換器」は作成できません。
人名や地名など名詞の変換に限定しても、長音か否かの判定が困難です。例えば「ノウチ」を「ノーチ (農地)」と読めば nōchi に、「ノ・ウチ (野内)」と読めば nouchi になります。
過去の経験では、これらの用語を抽出し、統計的に多い方へ寄せる対応を取ったことがあります。その場合のロジックの概略を以下に示します。
ただし、最終的には変換後のローマ字を人間が確認して、必要に応じて修正する必要があります。

  1. 変換表を選択する。

    例) ヘボン式

  2. まとめて処理する単位を切り出す。(「コウチ」「ウチ」「ウミ」「オ」など)

    例1) ナンバハッチ → ナンバハッチ
    例2) ヤマノウチ → ヤマノ + ウチ
    例3) カミコウチ → カミ + コウチ

  3. 文字単位に分割する。(拗音は1文字扱い)

    例1) ナンバハッチ → ナ/ン/バ/ハ/ッ/チ
    例2) ヤマノ + ウチ → ヤ/マ/ノ + ウ/チ
    例3) カミ + コウチ → カ/ミ + コ/ウ/チ

  4. 各文字をローマ字に変換する。

    4-1. 「ン」は次の文字に応じて、n、m、n'、n- 等に変換する。
    4-2. 「ッ」は次の文字に応じて、続く子音または t 等に変換する。
    4-3. 「-」は、前の母音を長音に変換する。
    4-4. 母音で前の文字と合わせて長音になる場合は、前の母音を長音に変換する。
    4-5. 上記以外の文字は、変換表にしたがってローマ字に変換する。

    例1) ナ/ン/バ/ハ/ッ/チ → na/ン/ba/ha/ッ/ch → na/m/ba/ha/t/ch
    例2) ヤ/マ/ノ + ウ/チ → ya/ma/no + ウ/chi → ya/ma/no + u/chi
    例3) カ/ミ + コ/ウ/チ → ka/mi + ko/ウ/chi → ka/mi + kō/chi

  5. 変換後のローマ字を連結する。

    例1) na/m/ba/ha/t/ch → nambahatch
    例2) ya/ma/no + u/chi → yamanouchi
    例3) ka/mi + kō/chi → kamikōchi

文章の変換では、「ハ」「ヘ」「ヲ」が助詞か否か判定する必要があります。この辺りは AI の範疇になると思いますが、本記事では深追いしないことにします。

5. まとめと次回の予告

システム開発で必要となる標準規格の話、連載第3回はローマ字編と題して、訓令式、日本式、ヘボン式など、いくつかあるローマ字の差異、かな・ローマ字変換を設計・実装する際のポイントなどを解説しました。
本記事で、少しでも誤ったローマ字が減り、正しいローマ字の利用が増えてくれれば幸いです。

次回は、ロケール、日付・時間、数値など、文字コード以外の国際化の話題を取り上げようと思います。

A1. 外来語・特殊音の表記

ローマ字の各方式の特殊音の表記法の一覧です。
表1、表2は、内閣訓令・告示『外来語の表記』における位置づけです。
パスポートがヘボン式にこだわる割に外来語の表記がイマイチなのは残念な気がします。

カナ 慣用表記 99式 新日本式 英米規格 パスポート
キェ (kye) (kye) kye
ギェ (gye) (gye) gye
クァ (表2) クア/カ/クヮ kwa kwa
クィ (表2) クイ/キ kwi kwi
クェ (表2) クエ/ケ kwe kwe
クォ (表2) クオ/コ kwo kwo
グァ (表2) グア/ガ/グヮ gwa gwa
グィ (gwi) gwi
グェ (gwe) gwe
グォ (gwo) gwo
シェ (表1) sye (sye) she (SHIE)
ジェ (表1) zye (zye) je (JIE)
スィ (swi) (si)
ズィ (zwi) (zi)
チェ (表1) tye (cye) che (CHIE)
ヂェ zye [dye] (zye) je (JIE)
ツァ (表1) tsa (ca) tsa
ツィ (表2) tsi tsi
ツェ (表1) tse (ce) tse
ツォ (表1) tso (co) tso
テャ (tja) (tya)
ティ (表1) チ/テ tji (ti) ti (TEI)
テュ (表2) チュ tju (tyu) tyu
テョ (tjo) (tyo)
デャ (dja) (dya)
ディ (表1) ジ/デ dji (di) di (DEI)
デュ (表1) ジュ dju (dyu) dyu (DEYU)
デョ (djo) (dyo)
トゥ (表2) ツ/ト twu (tu) tu
ドゥ (表2) ズ/ド dwu (du) du
ニィ (nyi) (NII)
ニェ (nye) (nye) nye (NIE)
ヒェ (hye) (hye) hye
ビェ (bye) (bye) bye
ピェ (pye) (pye) pye
ファ (表1) ハ/フア fa (fa) fa (FUA)
フィ (表1) ヒ/フイ fi (fi) fi (FUI)
フェ (表1) ヘ/フエ fe (fe) fe (FUE)
フォ (表1) fo (fo) fo (FUO)
フャ (fya)
フュ (表2) ヒュ fyu (fyu)
フョ (fyo)
ミェ (mye) (mye) mye
イェ (表2) イエ/エ ye (ye) ye
リェ (rye) (rye) rye
ウァ (wa)
ウィ (表2) ウイ/イ wi (wi) wi (UI)
ウェ (表2) ウエ we (we) we (UE)
ウォ (表2) ウオ wo (wo) wo (UO)
ヴァ (表2) va va (BA/BUA)
ヴィ (表2) vi vi (BI/BUI)
ヴ (表2) vu vu (BU)
ヴェ (表2) ve ve (BE/BUE)
ヴォ (表2) vo vo (BO/BUO)
ヴャ (vya)
ヴュ (表2) ビュ vyu
ヴョ (vyo)

A2. カナ・ローマ字変換の実装例 (抜粋)

ひらがなや半角カタカナは事前に全角カタカナに変換しておく前提です。
パラメータを設定ファイル等から読み込むようにすれば、柔軟な変換器が作れるかと思います。

/**
 * カナ・ローマ字変換器。
 */
public class RomajiConverter {

    /** モーラの正規表現パターン。 */
    static final Pattern MORA_PATTERN = Pattern.compile(
            "[キギシジチヂニヒビピミリ][ャュェョ]|イェ"
            + "|[クグ][ヮァィェォ]|[スズ]ィ|[ツウ][ァィェォ]|[フヴ][ァィェォャュョ]"
            + "|[テデ][ャィュョ]|[トド]ゥ"
            + "|[\\u30A0-\\u30FF]");

    /** 方式。 */
    final String type;  // = "ヘボン式"

    /** 親コンバータ。 */
    final RomajiConverter parent;

    /** トークンの正規表現。 */
    final Pattern token;  // = Pattern.compile("(?:コウチ|ウチ|ウミ|オ)\\b")

    /** 変換表。 */
    final Map<String, String> map;  // = {ア: a, イ: i, ...}

    /** オプション。 */
    final Options options;

    // コンストラクタ省略。

    /**
     * 全角カタカナをローマ字に変換します。
     */
    public String convert(String kana) {
        List<String> tokens = tokenize(kana);
        StringBuilder sb = new StringBuilder();
        tokens.add("");
        for (String token : tokens) {
            String romaji = convertToken(token);
            if (sb.length() > 0) {
                String prev = sb.substring(sb.length() - 1);
                if ("ン".equals(prev)) {
                    sb.setLength(sb.length() - 1);
                    sb.append(convertN(prev, romaji));
                } else if ("ッ".equals(prev)) {
                    sb.setLength(sb.length() - 1);
                    sb.append(convertQ(prev, romaji));
                }
            }
            sb.append(romaji);
        }
        return sb.toString();
    }

    /**
     * 全角カタカナをトークンに分割します。
     */
    List<String> tokenize(String kana) {
        Matcher m = token.matcher(kana);
        List<String> list = new ArrayList<>();
        int start = 0;
        while (m.find(start)) {
            list.add(kana.substring(start, m.start()));
            list.add(m.group());
            start = m.end();
        }
        list.add(kana.substring(start));
        return list;
    }

    /**
     * 全角カタカナのトークンを変換します。
     */
    String convertToken(String kana) {
        Matcher m = MORA_PATTERN.matcher(kana);
        StringBuilder sb = new StringBuilder();
        int start = 0;
        while (m.find(start)) {
            sb.append(kana.substring(start, m.start()));
            String k = m.group();
            if (k.length() == 1) {
                if ("ン".equals(k)) {
                    String succeedings = convertToken(kana.substring(m.end()));
                    if (succeedings.isEmpty()) {
                        // 変換を保留。
                        sb.append(k);
                    } else {
                        sb.append(convertN(k, succeedings));
                        sb.append(succeedings);
                    }
                    return sb.toString();
                }
                if ("ッ".equals(k)) {
                    String succeedings = convertToken(kana.substring(m.end()));
                    if (succeedings.isEmpty()) {
                        // 変換を保留。
                        sb.append(k);
                    } else {
                        sb.append(convertQ(k, succeedings));
                        sb.append(succeedings);
                    }
                    return sb.toString();
                }
                if ("アイウエオァィゥェォー".contains(k)) {
                    convertH(sb, k);
                    start = m.end();
                    continue;
                }
            }
            sb.append(convertMora(k));
            start = m.end();
        }
        sb.append(kana.substring(start));
        return sb.toString();
    }

    /**
     * 撥音「ン」をローマ字に変換します。
     */
    String convertN(String kana, String succeedings) {
        if (succeedings.length() > 0) {
            char next = Character.toLowerCase(succeedings.charAt(0));
            if ("mbp".indexOf(next) >= 0) {
                return options.n_m;
            }
            next = Normalizer.normalize(String.valueOf(next), Form.NFD).charAt(0);
            if ("aiueoy".indexOf(next) >= 0) {
                return options.n_a;
            }
        }
        return convertMora(kana);
    }

    /**
     * 促音「ッ」をローマ字に変換します。
     */
    String convertQ(String kana, String succeedings) {
        if (succeedings.length() > 0) {
            if (succeedings.startsWith("ch")) {
                return options.q_ch;
            }
            char next = Character.toLowerCase(succeedings.charAt(0));
            if ("kstcnhfmyrwgzjdbpv".indexOf(next) >= 0) {
                return succeedings.substring(0, 1);
            }
        }
        return convertMora(kana);
    }

    /**
     * 長音候補をローマ字に変換します。
     */
    void convertH(StringBuilder sb, String kana) {
        if (sb.length() > 0) {
            char prev = Character.toLowerCase(sb.charAt(sb.length() - 1));
            if (prev == 'a') {
                if ("アァー".contains(kana)) {
                    sb.setLength(sb.length() - 1);
                    sb.append(options.ah);
                    return;
                }

            // i、u、e 省略。

            } else if (prev == 'o') {
                if ("ウオゥォー".contains(kana)) {
                    sb.setLength(sb.length() - 1);
                    sb.append(options.oh);
                    return;
                }
            }
        }
        sb.append(convertMora(kana));
    }

    /**
     * 全角カタカナのモーラをローマ字に変換します。
     */
    String convertMora(String kana) {
        String romaji = map(kana);
        if (romaji != null) {
            return romaji;
        }
        if (kana.length() > 1) {
            // 2文字以上で変換表にない場合。
            return kana.codePoints()
                .mapToObj(n -> convertMora(new String(new int[] {n}, 0, 1)))
                .collect(Collectors.joining());
        }
        return kana;
    }

    /**
     * 変換表により、全角カタカナをローマ字に変換します。
     */
    String map(String kana) {
        String romaji = map.get(kana);
        if (romaji == null && parent != null) {
            return parent.map(kana);
        }
        return romaji;
    }

    /**
     * 変換オプション。
     */
    static class Options {

        /** m、b、p の前の撥音「ン」。 */
        final String n_m;  // = "m"

        /** 母音、y の前の撥音「ン」。 */
        final String n_a;  // = "n'"

        /** ch の前の促音「ッ」。 */
        final String q_ch;  // = "t"

        /** 長音「アー」。 */
        final String ah;  // = "ā"

        // i、u、e 省略。

        /** 長音「オー」。 */
        final String oh;  // = "ō"

        // コンストラクタ省略。

    }

}

A3. 参考文献・URL