前提
- 繁体字と簡体字と日本語、それぞれ用のフォントがある。
- 元の文章の言語はわからない。
- でも適切なフォントを割り当てないといけない。
- 見た目が変じゃなければオッケー(じゅうよう)
という状況のときにうまくやる方法。
繁体字とは
むかしっからある中国の文字(雑w)
(くわしくは 繁体字 - Wikipedia をどうぞ)
簡体字とは
↑繁体字むずいから簡単にしたやつ(雑w)(簡体字 - Wikipedia)
でも全部の繁体字が簡化されてるわけじゃない(←じゅうよう)
日本の漢字との関係
中国語の学習には繁体字も簡体字も欠かすことができない - GIGAZINE
https://gigazine.net/news/20171022-chinese-traditional-simplified-characters/
| 日本語 | 繁体字 | 簡体字 | |
|:--:|:--:|:--:|:--:|
| 図 | 圖 | 图 | 三者三様 |
| 作 | 作 | 作 | 三者同一 |
| 黒 | 黑 | 黑 | 日本語だけ違う |
| 痴 | 癡 | 痴 | 繁体字だけ違う |
| 義 | 義 | 义 | 簡体字だけ違う |
しかも
| 日本語 | 繁体字 | 簡体字 |
|:--:|:--:|:--:|
| 瀋 | 瀋 | 沈 |
| 沈 | 沉 | 沉 |
沈 は言語間で対応してない。
Unicode における漢字
CJK統合漢字 - Wikipedia っていうやつで、要するに「中国語、日本語、朝鮮語で使われている漢字をひとまとめにした」やつ。同じみための漢字は言語問わずおなじコードポイント。ここからここまでのコードが日本語で、ここからここまでのコードが繁体字で、みたいなことにはなってない。↑の表の「作」はどの言語の文章にでてきても U+4F5C というコード。
判別するには
- 日本語にしかない文字(ひらがな・カタカナ)が含まれてたら → もちろん日本語
- 繁体字にしかない文字が含まれてたら → もちろん繁体字(↑の表の「繁体字だけ違う」パターン)
- 簡体字にしかない文字が含まれてたら → もちろん簡体字(↑の表の「簡体字だけ違う」パターン)
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");
}
つー感じ。すげえでっかい正規表現になっているのでパフォーマンスが多少気にはなるのだけど、まー、たぶんだいじょぶ。