Posted at

繁体字と簡体字と日本語を区別する

More than 1 year has passed since last update.


前提


  • 繁体字と簡体字と日本語、それぞれ用のフォントがある。

  • 元の文章の言語はわからない。

  • でも適切なフォントを割り当てないといけない。

  • 見た目が変じゃなければオッケー(じゅうよう)

という状況のときにうまくやる方法。


繁体字とは

むかしっからある中国の文字(雑w)

(くわしくは 繁体字 - Wikipedia をどうぞ)


簡体字とは

↑繁体字むずいから簡単にしたやつ(雑w)(簡体字 - Wikipedia

でも全部の繁体字が簡化されてるわけじゃない(←じゅうよう)


日本の漢字との関係


中国語の学習には繁体字も簡体字も欠かすことができない - GIGAZINE

https://gigazine.net/news/20171022-chinese-traditional-simplified-characters/

日本語
繁体字
簡体字




三者三様




三者同一




日本語だけ違う




繁体字だけ違う




簡体字だけ違う

しかも

日本語
繁体字
簡体字





は言語間で対応してない。


Unicode における漢字

CJK統合漢字 - Wikipedia っていうやつで、要するに「中国語、日本語、朝鮮語で使われている漢字をひとまとめにした」やつ。同じみための漢字は言語問わずおなじコードポイント。ここからここまでのコードが日本語で、ここからここまでのコードが繁体字で、みたいなことにはなってない。↑の表の「作」はどの言語の文章にでてきても U+4F5C というコード。


判別するには


  1. 日本語にしかない文字(ひらがな・カタカナ)が含まれてたら → もちろん日本語

  2. 繁体字にしかない文字が含まれてたら → もちろん繁体字(↑の表の「繁体字だけ違う」パターン)

  3. 簡体字にしかない文字が含まれてたら → もちろん簡体字(↑の表の「簡体字だけ違う」パターン)


Unihan_Variants.txt

繁体字・簡体字にしかない文字のデータベースどっかにないの? → あります。


cjk - Simplified Chinese Unicode table - Stack Overflow https://stackoverflow.com/questions/4596576/simplified-chinese-unicode-table/4596760#4596760


Unihan Database っていう漢字データベースの奥の方にある Unihan.zip のなかにある Unihan_Variants.txt というやつが異体字のリストになってて、繁体字←→簡体字のペアもここに書いてある。

U+3469  kTraditionalVariant U+5138

U+346F kSimplifiedVariant U+3454
U+3473 kSimplifiedVariant U+3447
U+3487 kSemanticVariant U+511B<kMatthews

こんな感じになっていて、U+3469 の繁体字バージョンは U+5138 ですよ、と。

つーことで、↓みたいなスクリプトを書いて抽出すると、繁体字・簡体字にしかない文字リストができあがる。

# filter = 'kTraditionalVariant'

filter = 'kSimplifiedVariant' # 簡体字版がある文字=繁体字

codes = []
with open('Unihan_Variants.txt') as f:
for line in f:
line = line.strip()
tokens = line.split('\t')
if len(tokens) < 3:
continue
if tokens[1] == filter and tokens[0] != tokens[2]:
codes.append(tokens[0].replace('U+', '\u'))
print('|'.join(codes))

最後の print は C# で使う正規表現用。


ようやく判定用のコード

using System.Text.RegularExpressions;

public class LanguageDetector
{

public static string Detect(string text)
{
// ほんとはここでひらがなカタカナだけじゃなくて、日本語にしか使われてない漢字も含めたほうが精度あがる
if (JapaneseKana.IsMatch(text))
{
return "ja";
}

var sc = SimplifiedChinese.IsMatch(text);
var tc = TraditionalChinese.IsMatch(text);
if (sc && tc)
{
// たまに繁体字・簡体字両方含んでるやつがあるので、そのときは多い方にする
var scount = 0;
var tcount = 0;
var n = System.Math.Min(text.Length, 100);
for (var i = 0; i < n; i++)
{
var t = text.Substring(i, 1);
if (SimplifiedChinese.IsMatch(t))
{
scount++;
}
else if (TraditionalChinese.IsMatch(t))
{
tcount++;
}
}
if (scount > tcount)
{
return "zh-cn";
}
else
{
return "zh-tw";
}
}
else if (sc)
{
return "zh-cn";
}
else if (tc)
{
return "zh-tw";
}

// ここに来た時点ではまだ日本語・繁体字・簡体字どれも可能性あるんだけど、
// どれにも含まれる漢字しか使われてないので、日本語フォントでも問題なく表示される。
if (Kanji.IsMatch(text))
{
return "ja";
}

return "en";
}

static Regex JapaneseKana = new Regex(@"\p{IsHiragana}|\p{IsKatakana}");
static Regex Kanji = new Regex(@"\p{IsCJKUnifiedIdeographs}");
// ↓Pythonスクリプトで生成したやつをコピペ
static Regex SimplifiedChinese = new Regex(@"\u343D|\u3447|\u3448...{省略}...\u2B6F6|\u2B6F8|\u2C88D");
static Regex TraditionalChinese = new Regex(@"\u346F|\u3473|\u3493...{省略}...\u2A535|\u2A600|\u2A62F");

}

つー感じ。すげえでっかい正規表現になっているのでパフォーマンスが多少気にはなるのだけど、まー、たぶんだいじょぶ。