0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【python】ARPAbet-日本語カナ変換

0
Last updated at Posted at 2025-12-07

はじめに

ARPAbetを日本語カナに変換するライブラリarpakanaを作ったので、解説します。
https://github.com/jiroshimaya/arpakana

使い方

ARPAbetのリストまたは空白区切りの文字列を渡すと、対応するカタカナ文字列が返ります。

uv add arpakana
from arpakana import arpabet_to_kana

print(arpabet_to_kana("HH AH0 L OW1"))  # ハロウ

背景

ARPAbetは、英語の発音記号をASCII文字のみで表現するために設計された記法です。

ARPAbetをカタカナ表記へ変換する試みはこれまでにもいくつか存在し、主に次の2つのアプローチに分類できます。

  • ルールベース手法
    • ARPAbet を子音×母音の組み合わせ規則に基づいてカタカナへ変換し、さらに挿入母音(/u, o/ など)、撥音・促音、r 音化(ER など)といった特殊処理も行います。具体的なルールや実装例としては「英語をカタカナ表記に変換してみる」が参考になります。
  • 機械学習ベース手法
    • RNN などを用いて「音素列 → カタカナ」を直接学習するアプローチです。ARPAbet には日本語と対応させにくい曖昧母音が存在するため、このような手法に一定の意義があります。e2kでは ARPAbet をカタカナへ変換する機能が実装されています。

pip経由で使えるpythonライブラリは探した範囲では機械学習ベースのe2kだけでしたが、e2kのARPAbet–カナ変換機能が長めの入力に対して安定しにくそうでした。そこで、多くのケースではルールベースでも十分な性能が期待できることもあり、新たにルールベースのPythonライブラリを開発することにしました。実装にあたっては、上記の関連手法を参考にさせていただきました。

実装

arpakanaライブラリでは、ARPAbetを日本語のカナ文字へ変換するために、以下の手順で処理を行っています。

  1. 音素の正規化(大文字化、ストレス記号除去)
  2. ER/AXRの展開(AX R に分解)
  3. 母音の正規化
  4. 促音の挿入
  5. R音素の変換規則適用
  6. 子音+母音の組み合わせ変換
  7. 単独子音の変換
  8. 未知音素の置換

以下、各ステップの詳細を説明します。

音素の正規化

入力された音素を大文字化し、末尾のストレス番号(0, 1, 2)を削除します。例えば、"AH0" は "AH" に変換されます。

ER/AXRの展開

"ER" と "AXR" は "AX R" に分解されます。 これにより、後続のR音素変換規則が適用しやすくなります。

母音の正規化

母音音素をaiueoのいずれかに標準化します。変換規則は以下によります。二重母音や長音についてもこの時点で考慮します。

_VOWEL_MAP: dict[str, tuple[str, ...]] = {
    "AA": ("a",),
    "AE": ("a",),
    "AH": ("a",),
    "AO": ("o",),
    "AW": ("a", ""),
    "AX": ("a",),
    "AXR": ("a", ""),  # 互換性維持(事前に "AX R" へ展開する想定)
    "AY": ("a", ""),
    "EH": ("e",),
    "ER": ("a", ""),  # 互換性維持(事前に "AX R" へ展開する想定)
    "EY": ("e", ""),
    "IH": ("i",),
    "IX": ("i",),
    "IY": ("i", ""),
    "OW": ("o", ""),
    "OY": ("o", ""),
    "OH": ("o", ""),
    "UH": ("u",),
    "UW": ("u", ""),
    "UX": ("u",),
}

促音の挿入

特定の子音(CH、SHなど)の前が短母音の場合、「ッ」を挿入します。
例えば、"K Y AE CH" は "キャッチ" に変換されます。
長母音や子音に続く場合には促音は挿入されません。
例えば、"K Y ER CH" は "キャーチ" になります。

R音素の変換規則適用

"R" 音素に、直前の音素に応じて異なるカナを割り当てます。
基本は「ア」とします。(例: "B IH R" → "ビア")
直前が"a"または"o"の場合には「ー」とします。(例: "B AA R" → "バー")
直前が長音の場合は、空文字に変換します。「ー」の連続を避けるためです。
直後にaiueoが続く場合は「ラリルレロ」になります。

子音+母音の組み合わせ変換

R以外の子音と母音の組み合わせに対して、対応するカナを割り当てます。

単独子音の変換

残った子音を単独で変換します。例えば、"B" は "ブ" に変換されます。

未知音素の置換

変換できない音素に対しては、指定された置換文字列(デフォルトは空文字)に置き換えます。

変換事例

テストケースの一部を記載します。

from arpakana.arpabet import arpabet_to_kana


def test_正常系_基本単語() -> None:
    # hello
    assert arpabet_to_kana("HH AH0 L OW1") == "ハロウ"
    # sky
    assert arpabet_to_kana("S K AY") == "スカイ"
    # blue
    assert arpabet_to_kana(["B", "L", "UW"]) == "ブルー"
    # train
    assert arpabet_to_kana("T R EY N") == "トゥレイン"
    # bout
    assert arpabet_to_kana("B AW1 T") == "バウトゥ"
    # 'cause
    assert arpabet_to_kana("K AH0 Z") == "カズ"
    # 'course
    assert arpabet_to_kana("K AO1 R S") == "コース"
    # 'm
    assert arpabet_to_kana("AH0 M") == "アン"
    # frisco
    assert arpabet_to_kana("F R IH1 S K OW0") == "フリスコウ"

def test_正常系_複合子音() -> None:
    # cues
    assert arpabet_to_kana("K Y UW1 Z") == "キューズ"
    # aquamarine
    assert arpabet_to_kana("AA K W AH M ER IY N") == "アクワマリーン"


def test_正常系_TS音素() -> None:
    # cats
    assert arpabet_to_kana("K AE1 T S") == "カッツ"
    # watches
    assert arpabet_to_kana("W AA1 CH IH0 Z") == "ワッチズ"
    # abducts
    assert arpabet_to_kana("AE0 B D AH1 K T S") == "アブダクツ"


def test_正常系_NG音素() -> None:
    # quote
    assert arpabet_to_kana("K W OW1 T") == "クウォウトゥ"
    # bengtson
    assert arpabet_to_kana("B EH1 NG T S AH0 N") == "ベンツァン"
    # fourthquarter
    assert arpabet_to_kana("F AO1 R TH K W AO1 R T ER0") == "フォースクウォーター"


def test_正常系_R() -> None:
    # amateurish
    assert arpabet_to_kana("AE1 M AH0 CH ER2 IH0 SH") == "アマッチャリッシュ"
    # ameliorate
    assert arpabet_to_kana("AH0 M IY1 L Y ER0 EY2 T") == "アミーリャレイトゥ"
    # bird
    assert arpabet_to_kana("B ER1 D") == "バード"
    # fear
    assert arpabet_to_kana("F IH1 R") == "フィア"
    # bear
    assert arpabet_to_kana("B EH1 R") == "ベア"
    # before
    assert arpabet_to_kana("B IH0 F AO1 R") == "ビフォー"
    # aboard
    assert arpabet_to_kana("AH0 B AO1 R D") == "アボード"
    # sure
    assert arpabet_to_kana("SH UH1 R") == "シュア"


def test_正常系_未知トークン() -> None:
    assert arpabet_to_kana("XYZ", unknown="*") == "*"


def test_促音挿入ルール() -> None:
    # rich
    assert arpabet_to_kana("R IH1 CH") == "リッチ"
    # beach
    assert arpabet_to_kana("B IY1 CH") == "ビーチ"
    # fish
    assert arpabet_to_kana("F IH1 SH") == "フィッシュ"
    # marsh
    assert arpabet_to_kana("M AA1 R SH") == "マーシュ"
    # boots
    assert arpabet_to_kana("B UW1 T S") == "ブーツ"

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?