1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MeCab+UniDicでルビ表示を正しく実装する方法

Posted at

MeCab+UniDicでルビ表示を正しく実装する方法

日本語の形態素解析システムでルビ(振り仮名)を正確に表示することは、単純に見えて実は非常に複雑な問題です。本記事では、MeCab + UniDicを使った形態素解析において、「映画」が「ええが」、「生活」が「せえかつ」と誤って表示される問題を解決する実装方法を詳しく解説します。

背景:なぜルビ表示は難しいのか

日本語の形態素解析でルビ表示を実装する際、最も大きな課題は長音記号「ー」の適切な変換です。MeCabの解析結果では、多くの単語の読みが長音記号を含んだ形で返されます:

  • 「映画」→「エーガ」
  • 「生活」→「セーカツ」
  • 「研究」→「ケンキュー」

しかし、実際のルビとして表示すべき正しい仮名は以下のようになります:

  • 「映画」→「えいが」
  • 「生活」→「せいかつ」
  • 「研究」→「けんきゅう」

問題の原因

複雑な長音変換ロジックの実装

// 間違いやすい実装例
if ('エケゲセゼテデネヘベペメレ'.includes(prevChar)) {
  return '';  // エ段の長音は「イ」に変換
}

このような単純な規則では、日本語の複雑な音韻規則に対応できません。特に:

  • 拗音(ゃゅょ)の処理
  • 連濁の処理
  • 活用形での読みの変化

これらを全て自前で実装すると、コードが複雑になり保守が困難になります。

解決方法:UniDicのformBaseフィールドを活用

UniDicには実は正しい仮名表記を提供するフィールドが既に存在しています。

UniDicの重要フィールド比較

フィールド名 映画 生活 説明
surface_reading エーガ セーカツ 長音記号あり(発音用)
formBase エイガ セイカツ 正しい仮名表記
goshu エーガ セーカツ 語種情報

実装例

改善前:複雑な長音変換ロジック

// 長音記号を変換する複雑なロジック
finalReading = token.surface_reading.replace(/ー/g, (match, offset) => {
  if (offset > 0) {
    const prevChar = token.surface_reading[offset - 1];
    // エ段の長音は「イ」に変換
    if ('エケゲセゼテデネヘベペメレ'.includes(prevChar)) {
      return '';
    }
    // オ段の長音は「ウ」に変換
    if ('オコゴソゾトドノホボポモヨロヲ'.includes(prevChar)) {
      return '';
    }
    // その他多くの条件分岐...
  }
  return match;
});

改善後:UniDicのformBaseを利用

// UniDicのformBaseフィールドを優先的に使用(これが正しい仮名表記)
let finalReading = '';
if (token.formBase && token.formBase !== '*' && !containsKanji(token.formBase)) {
  // formBaseがカタカナで正しい読みを持っている
  finalReading = token.formBase;
} else if (token.conjugated_reading && token.conjugated_reading !== '*') {
  // AWS Lambdaが生成した活用形の読み
  finalReading = token.conjugated_reading;
} else if (reading && !containsKanji(reading)) {
  // 基本的な読み
  finalReading = reading;
} else {
  // フォールバック
  finalReading = '';
}

デバッグ方法

UniDicのフィールドを確認する方法:

# APIエンドポイントで確認
curl -X POST http://localhost:3000/api/analyze \
  -H "Content-Type: application/json" \
  -d '{"text": "映画"}' | jq '.raw_aws_result.tokens[0]'

重要なフィールドの確認:

# Pythonでの確認例
import sys, json
data = json.load(sys.stdin)
token = data.get('raw_aws_result', {}).get('tokens', [])[0]
print(f"surface_reading: {token.get('surface_reading')}")  # エーガ
print(f"formBase: {token.get('formBase')}")                # エイガ(正解!)
print(f"goshu: {token.get('goshu')}")                      # エーガ

Lambda API側の実装

MeCab + UniDicを使用したPython実装:

# UniDicのフィールド定義
unidic_fields = [
    'pos',              # 品詞
    'pos_detail_1',     # 品詞細分類1
    'pos_detail_2',     # 品詞細分類2
    'pos_detail_3',     # 品詞細分類3
    'conjugation_type', # 活用型
    'conjugated_form',  # 活用形
    'base_form',        # 原形
    'reading',          # 原形の読み
    'surface_form',     # 表層形
    'surface_reading',  # 表層形の読み(活用形の読み)
    'lemma',            # 語彙素
    'goshu',            # 語種
    'orthBase',         # 書字形基本形
    'orthVariant',      # 表記
    'formBase',         # 形態素基本形(重要!)
    'formVariant',      # 異形態
]

# フィールドの抽出
for i, field in enumerate(unidic_fields):
    if i < len(features):
        token[field] = features[i]

実装のポイント

1. 優先順位の設定

// 読み仮名の候補を優先順位で取得
const readingCandidates = [
  token.formBase,           // 最優先:正しい仮名表記
  token.conjugated_reading,  // 活用形の読み
  token.reading,            // 基本的な読み
];

2. 漢字判定の実装

function containsKanji(text: string): boolean {
  if (!text) return false;
  return /[\u4e00-\u9faf]/.test(text);
}

3. カタカナ→ひらがな変換

function katakanaToHiragana(text: string): string {
  if (!text) return '';
  return text.replace(/[\u30A1-\u30FF]/g, (match) => {
    const code = match.charCodeAt(0);
    if (code >= 0x30A1 && code <= 0x30F6) {
      return String.fromCharCode(code - 0x60);
    }
    return match;
  });
}

パフォーマンス最適化

// キャッシュを活用
const CACHE_SIZE = 500;
const analysisCache = new Map<string, any>();

// キャッシュチェック
if (analysisCache.has(cacheKey)) {
  return analysisCache.get(cacheKey);
}

// 結果をキャッシュ
analysisCache.set(cacheKey, result);

まとめ

日本語のルビ表示で正確な結果を得るためには:

  1. UniDicのformBaseフィールドを優先的に使用する
  2. 複雑な音韻変換ロジックを自前で実装しない
  3. 信頼できる辞書データソースを最大限活用する

この方法により:

  • コードがシンプルになり保守性が向上
  • 「映画」→「えいが」、「生活」→「せいかつ」など正確な表示が実現
  • 日本語の複雑な特性への個別対応が不要に

UniDicには既に必要なデータが全て揃っているため、それを正しく活用することが重要です。

1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?