📘

【PHP】mb_convert_kana は濁音・半濁音の Unicode 正規化には使えない

2024/07/14に公開

mb_convert_kana で NFD の濁音・半濁音を NFC に変換する用途に使えるのかどうか調べたが、やめたほうがよいという結論になった。濁点・半濁点の結合文字として定義されている U+3099 と U+309A が全角半角の変換対象ではないからである。Unicode の結合文字の仕様が決まる前の古い変換ルールに従っているということなのだろう。

mb_convert_kana に濁音・半濁音の Unicode 正規化の機能をつけるとしたらオプションの文字列をどうするのかが課題である。ずばり mb_convert_kana($str, 'NFC') というオプションを追加するか。Ruby では encode メソッドで UTF-8-MAC をサポートしているが、合成除外の表を管理する必要がある。

<?php

// 濁点(JIS に定義あり)
$str = "\u{309B}";
// 濁点 (結合文字)
$str2 = "\u{3099}";
// 濁点 (JIS に定義あり)
$str3 = "\u{309C}";
// 半濁点 (結合文字)
$str4 = "\u{309A}";

// 全角半角
var_dump(
  // 半角濁点
  "\u{FF9E}" === mb_convert_kana($str, 'k', 'utf-8'),
  // 変わらず
  $str2 === mb_convert_kana($str2, 'k', 'utf-8'),
  // 半角半濁音
  "\u{FF9F}" === mb_convert_kana($str3, 'k', 'utf-8'),
  // 変わらず
  $str4 === mb_convert_kana($str4, 'k', 'utf-8')
);

// 半角濁点
$str5 = "ハ\u{FF9E}";
// 全角濁点
$str6 = "ハ\u{309B}";
$str7 = "ハ\u{3099}";

// 半角半濁点
$str8 = "ハ\u{FF9F}";
// 全角半濁点
$str9 = "ハ\u{309C}";
$str10 = "ハ\u{309A}";

// 半角全角
var_dump(
  // 半角濁点を変換
  "バ" === mb_convert_kana($str5, 'KV', 'utf-8'),
  // 全角濁点は変わらず
  "ハ\u{309B}"=== mb_convert_kana($str6, 'KV', 'utf-8'),
  "ハ\u{3099}"=== mb_convert_kana($str7, 'KV', 'utf-8'),
  // 半角半濁点を変換
  "パ" === mb_convert_kana($str8, 'KV', 'utf-8'),
  // 全角半濁点は変わらず
  "ハ\u{309C}" === mb_convert_kana($str9, 'KV', 'utf-8'),
  "ハ\u{309A}" === mb_convert_kana($str10, 'KV', 'utf-8')
);

Discussion