文字列が「中国語かどうか」を判定したい

漢字だけの文字列を渡して、「日本語」なのか「中国語」なのかを判別するのって、かなり難しかった…。

文章がとっちらかっているが、WIP なライブラリの話をしているので、ご容赦をば…。

目次

外国語を判定してみたくなった

特に目的はないのだが、ふと思いついて、ある文字列が外国語の文章かどうかを判定できる正規表現を書きたいなーと思った。「コレはロシア語だ」とか「コレはハングルだ」みたいな。

Unicode の「ブロック」を見ると、ロシア語に使われる「キリル文字」や、韓国語の「ハングル」、「アラビア文字」などは、ブロックが綺麗に別れているので、この Unicode ブロックの範囲で正規表現が書けそうだと気付いた。

なお、ハングルについては以前「ハングルを特定する正規表現」を見つけていて、コレがこの Unicode ブロックにおける「Hangul Syllables ハングル音節文字」(U+AC00 ~ U+D7AF) にほぼ相当することが分かった。

以下にデモページとコードを用意してみた。

ES2018 の Unicode Property Escapes という新機能を知る

さらに調べて行くと、ES2018 で Unicode Property Escapes という機能が追加されていたのを知った。JS の RegExp/\p{Script_Extensions=Hangul}/u と書くことで「ハングルに合致する正規表現」を簡単に書ける、という仕組みだ。

Unicode Property Escapes というのは他のプログラミング言語には既に実装されていて、例えば PHP や Ruby なんかでも、\p{Han} と書くことで「漢字」にマッチする正規表現を簡単に書けるようだ。Unicode コードポイントを大量に列挙したりしなくて良いワケである。

他言語の \p{Han} (Han というのは「漢」のこと) 相当の正規表現を Unicode コードポイントで記そうとすると、以下のようになる。

const HAN_REGEX = /[\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u3005\u3007\u3021-\u3029\u3038-\u303B\u3400-\u4DB5\u4E00-\u9FD5\uF900-\uFA6D\uFA70-\uFAD9]/;

コレが、ES2018 以降では

const HAN_REGEX = /\p{Script=Hani}/u;  // 2つの正規表現は同じ
const HAN_REGEX = /\p{sc=Han}/u;       // コチラは省略記法

と、このように書けるワケである。

「中国語」と「日本語」は Unicode ブロックが別れていない

さて、「キリル文字」や「ハングル」なんかは、Unicode ブロックでの範囲指定なり、それを上手くグループ化した Unicode Property Escapes という機能でほぼほぼ判定できそうな様子が分かってきた。日本語に関しても、ひらがなとカタカナについては Unicode Property Escapes が存在する。

しかし、漢字の中でも「中国語」を判定してみようと思った時、壁にブチ当たった。

Unicode の界隈でよく CJK、「CJK 統合漢字」という言葉を耳にしていた。中国語・日本語・朝鮮語で使われる漢字をひとまとめにした符号表である。

日本語と中国語をひとまとめに…。確かに、日本語の中で使う漢字の多くは、中国の漢字を流用しているので、「CJK」といっしょくたにまとめられているワケだ。しかしそうなると、漢字に関しては、キリル文字やハングルみたいに「この文字がマッチしたからロシア語・韓国語ね!」という簡単な判定ができないことになる。

つまり、膨大な CJK 統合漢字の中から、

とを何とか区別してやらないと、「この文章が中国語かどうか」を判定するプログラムは書けないということだ。

Unihan データベースを使って「中国語で使われる漢字」を判定する

さらに調べていくと、次のページを見つけた。

何やら、Unihan:Unicode Han Database という漢字に関するデータベースがあるらしい。データベースと言いながら実体はテキストファイルなのだが、Unicode コードポイントごと (= 漢字ごと) に「日本語の音読み・訓読み」だとか「北京語・広東語での読み方」だとかを記しているようだ。

また、この Unihan データベースを使って、繁体字と簡体字を区別するような記事もいくつか見かけた。

ココに来て「中国語」とは何か、ということになるのだが、中国語を勉強したことがないので、よく分かっていない。

ただ、今回僕が考える目的からすると、前述のとおり

ということにする。

そういうワケで、先程の参考ページを参考に Unihan データベースを使ってみることにした。

「中国語を判定するプログラム」を作成中

以下の GitHub リポジトリに、作成したコードを置いている。

Node.js で require() してもいいし、HTML から読み込めばブラウザ上でも動作するし、npm install すれば $ detect-chinese コマンドとして動作するような CLI 環境も簡単に用意してみた。以前の記事で少し紹介し、@neos21/ccc でも採用している UMD 形式でコードを書いている。

Unihan データベースの Unihan_Readings.txt というファイルから

を抽出し、Unicode コードポイントを列挙して RegExp を定義するところまではできた。まぁ巨大な正規表現になった…。

そしてその後の判定についても、「日本語の音訓読みを持たず、中国語読みを持つ漢字」を特定したりして、ある程度は「中国語でのみ使われる漢字を含んだ文章かどうか」を判定できている。

判定漏れがある

一部、既知の問題として「つちよし 𠮷」の文字が日本語とも中国語ともみなされない文字として扱われてしまっている。

そういうワケで、判定漏れしている漢字もまだまだありそうなことが分かっている。

「つちよし 𠮷」は「日本語漢字」に含めても良いかも知れないが、異字体は他にも沢山あり、それらについては「日本語漢字」とみなしていいのかどうかなど、もう少し考えないといけないところが多そうだ。

ご意見・アドバイス等ください

中国語をロクに知らないのに、ワケもなく中国語を判定したくなった程度の人間が作っているので、こんな感じで精度はボチボチ。通常の文章なら大抵は「中国語か日本語か (それ以外か)」を判定できているが、もう少しなんとかしていきたい。中国語の有識者のご意見を聴きたい。

最終的に npm パッケージとして配信して汎用的に使えるようにしたいなーと思っているので、どういう機能をライブラリとして提供できれば良いか、要望やアドバイス等あればいつでもご連絡ください。

↑ ご意見がありましたら、GitHub リポジトリの GitHub Issues に起票していただけますと嬉しいです。


興味が続けば今後も改修していきます…。笑