「日本語(というか漢字)は表意文字なので読めなくても意味はわかる。表音文字は意味はわからなくても読める」 という話がある。 前者の主張はわかるが、正直なところラテン文字以外は発音を推定することは難しい(私の場合)。
そういうわけで、色々な言語をローマ字に変換する仕組みを考える。
言語ごと直接変換するのはコストが高いため、まずは文字を 国際音声記号(IPA) に変換した上でローマ字に変換する。
国際音声記号 (IPA) に変換
国際音声記号(IPA) に変換するのには epitran を使う。 多言語対応のライブラリは Python だとこれ一択の模様。
研究成果として公開されたもののようだが (https://aclanthology.org/L18-1429/ ) 、今もメンテナンスされている。ありがたい。
言語のサポート状況は README に記載されている。
使いたい言語 (Code) を指定して Epitran クラスで宣言する。
import epitran epi = epitran.Epitran("code")
日本語を発音記号に
日本語対応もしているので試しに実行する。
日本語は Code が複数ある。 ひらがな (jpn-Hira)、カタカナ (jpn-Kana) に関してはそれぞれの文字種のみ変換する。
>>> epi = epitran.Epitran("jpn-Hira") >>> epi.transliterate("とうきょうと、トウキョウ、東京") 'toːkʲoːto、トウキョウ、東京' >>> epi = epitran.Epitran("jpn-Kana") >>> epi.transliterate("とうきょうと、トウキョウ、東京") 'とうきょうと、toːkʲoː、東京' >>> epi = epitran.Epitran("jpn-Jpan")
漢字を含む jpn-Jpan に関しては open-dict-data の ja.txt を辞書データとして使っている。
ひらがな・カタカナ部分について微妙に出力が変わる。 また、辞書ベースの仕組みのため変換できない可能性がある。
>>> epi = epitran.Epitran("jpn-Jpan") >>> epi.transliterate("とうきょうと、トウキョウ、東京") 'toɯきょɯto、toɯkiョɯ、toɯkjoɯ' >>> epi.transliterate("私は夢洲に行った") 'ɰᵝataihajɯmeɕɯɯnigjoɯっta' >>> epi.transliterate("大規模言語モデル、つまりLLMのことだ") 'daikibogeɴkjomodeɾɯ、tsɯmaりLLMnokotoda'
ロシア語をローマ字っぽくする
ここから本題。ギリギリ読めないキリル文字で実験する。
ロシアの地名を Wikipedia ページ Список городов России с населением более 100 тысяч человек (人口10万人を超えるロシアの都市の一覧) のテーブルから取得。
日本語表記を正解データとして扱えるよう、 langlinks で対応する日本語ページも取得する。
日本語がない地名は落として、 156個 のデータを取得した。
// ロシア語地名と日本語 [ { 'target': 'Москва', 'ja': 'モスクワ' }, { 'target': 'Санкт-Петербург', 'ja': 'サンクトペテルブルク' }, { 'target': 'Новосибирск', 'ja': 'ノヴォシビルスク' }, { 'target': 'Екатеринбург', 'ja': 'エカテリンブルク' }, ]
MediaWiki API にリクエストし、 その結果を wikitextparser で色々操作しただけなので実装の詳細は割愛。
発音記号をローマ字に変換
epitran の変換結果をローマ字に変換する。
国際音声記号の文字一覧 を参考にルールベースで変換する。
変換を単純化するために母音と子音以外は変換の対象から除外する実装とした。
ipa_to_roman の実装は gist にアップロードした。
検証
日本語カタカナもローマ字に変換した上でコサイン類似度を算出する。
カタカナからローマ字の変換は jaconv、 コサイン類似度は textdistance を使う。
def katakana2roman(text): """カタカナをローマ字に変換""" text = jaconv.kata2hira(text) # ゔ を v に text = text.replace("ゔぁ", "va").replace("ゔぃ", "vi").replace("ゔぅ", "vu") text = text.replace("ゔぇ", "ve").replace("ゔぉ", "vo").replace("ゔ", "v") # 長音・記号を空白に return jaconv.kana2alphabet(text).replace("ー", " ").replace("・", " ") epi_ru = epitran.Epitran("rus-Cyrl") sim_result = [] for d in data["data"]: # カッコははぶく ru_text = d["target"].split(" (")[0] ja_text = d["ja"].split(" (")[0] ru_ipa = epi_ru.transliterate(ru_text) # ja_ipa = epi_ja.transliterate(ja_text) ja_roman = katakana2roman(ja_text) ru_roman = ipa_to_roman(ru_ipa) sim = textdistance.cosine(ru_roman, ja_roman) sim_result.append([ru_text, ja_text, ru_roman, ja_roman, sim])
結果は以下のようになった。
ru_roman がロシア語のローマ字表記、 ja_roman が日本語のローマ字表記。
| ru | ja | ru_roman | ja_roman | similarity | |
|---|---|---|---|---|---|
| 0 | Москва | モスクワ | moskva | mosukuwa | 0.721688 |
| 1 | Санкт-Петербург | サンクトペテルブルク | sankt-petervurɡ | sankutopeteruburuku | 0.710819 |
| 2 | Новосибирск | ノヴォシビルスク | novosivirsk | novoshibirusuku | 0.778499 |
| 3 | Екатеринбург | エカテリンブルク | yekaterinvurɡ | ekaterinburuku | 0.741249 |
| 4 | Казань | カザン | kazan | kazan | 1 |
類似度 (similarity) の平均は 0.767622 でそこまで低くない印象。
# similarity (pd.describe) count 156.000000 mean 0.767622 std 0.112924 min 0.462910 25% 0.683370 50% 0.771517 75% 0.843744 max 1.000000
実例
類似度の高い上位10個
| ru | ja | ru_roman | ja_roman | similarity | |
|---|---|---|---|---|---|
| 8 | Уфа | ウファ | ufa | ufa | 1 |
| 38 | Пенза | ペンザ | penza | penza | 1 |
| 32 | Кемерово | ケメロヴォ | kemerovo | kemerovo | 1 |
| 4 | Казань | カザン | kazan | kazan | 1 |
| 51 | Иваново | イヴァノヴォ | ivanovo | ivanovo | 1 |
| 137 | Раменское | ラメンスコエ | ramenskoe | ramensukoe | 0.948683 |
| 150 | Камышин | カムイシン | kamishin | kamuishin | 0.942809 |
| 125 | Коломна | コロムナ | koromna | koromuna | 0.935414 |
| 115 | Салават | サラヴァト | saravat | saravato | 0.935414 |
| 13 | Воронеж | ヴォロネジ | voronej | voroneji | 0.935414 |
6文字ぐらいの単語でも類似度が高く表記できている。
類似度の低い上位10個
| ru | ja | ru_roman | ja_roman | similarity | |
|---|---|---|---|---|---|
| 76 | Тамбов | タンボフ | tamvov | tanbofu | 0.46291 |
| 99 | Бийск | ビイスク | viysk | biisuku | 0.507093 |
| 113 | Керчь | ケルチ | kerty | keruchi | 0.507093 |
| 86 | Братск | ブラーツク | vratxk | bura tsuku | 0.516398 |
| 133 | Рубцовск | ルプツォフスク | ruvtxovsk | ruputsuofusuku | 0.534522 |
| 126 | Кызыл | クズル | kizir | kuzuru | 0.547723 |
| 140 | Черкесск | チェルケスク | tyerkessk | chierukesuku | 0.57735 |
| 85 | Великий Новгород | ノヴゴロド | verikiy novɡorod | novgorodo | 0.583333 |
| 66 | Владикавказ | ウラジカフカス | vradikavkaz | urajikafukasu | 0.585369 |
| 144 | Артём | アルチョーム | artom | arucho mu | 0.596285 |
日本語ではバ行表記 (b) 、原語だと v 表記の場合は類似度が低くなってしまう。
他は母音の不一致が多い。
Кызыл の変換結果 kizir と クズル は母音が全く違うので詳しく調べてみる。
ロシア語での発音は kɨzɨl で母音 ɨ は 非円唇中舌狭母音。
日本語では表記できない発音らしい。
このあたりは日本語の限界を感じる。 音で聞くと「クィズィル」みたいに聞こえた。










