1. 5秒で結論:見た目はうさぎ、内部は3文字分の合体事故
Simejiの変換で出てきた、これ。
ꪔ̤̮
どう見ても、ちっちゃいうさぎがこっちを見ている。
ところがUnicode上では、これは「うさぎ」という1文字ではない。土台は Tai Viet Letter Low To(U+AA94)。その下に Combining Diaeresis Below(U+0324) と Combining Breve Below(U+032E) が重なっている。[1][2]
つまり見た目は「🐇」寄りなのに、中身は東南アジア系文字+下付き記号+下付き記号。
かわいい顔をして、内部構造がだいぶエンジニア泣かせである。
2. 30秒で概要:「草」に混ぜたら本当に生息し始めた
最初はただの顔文字だった。
- ̗̀ ꪔ̤̥ꪔ̤̮ꪔ̤̫ ̖́-
「なんやこのうさぎ」で終わるはずだった。
ところが丸くて密度の高いミャンマー文字へ混ぜると、急に草むらから顔だけ出している野生動物に見え始めた。
မြန်မာမြန်မာမြန်မာ ꪔ̤̮ မြန်မာမြန်မာ
そしてタイ語へ入れた瞬間、事態は悪化した。
ภาษาไทยꪔ̤̮ภาษาไทย
「……なんかうさぎいる?」
さらに10匹入れた。
ภาษาไทยꪔ̤̮ภาษาꪔ̤̥ไทยภาษาꪔ̤̫ไทยꪔ̤̮กำลังꪔ̤̥เขียนอยู่ꪔ̤̫ภาษาไทยꪔ̤̮ภาษาꪔ̤̥ไทยภาษาꪔ̤̫ไทยꪔ̤̮
もう文章ではない。草原の生息数調査である。
3. そもそも「うさぎ」の正体は何なのか
3種類を分解するとこうなる。
| 見た目 | 中身 |
|---|---|
ꪔ̤̥ |
U+AA94 TAI VIET LETTER LOW TO + U+0324 COMBINING DIAERESIS BELOW + U+0325 COMBINING RING BELOW |
ꪔ̤̮ |
U+AA94 TAI VIET LETTER LOW TO + U+0324 COMBINING DIAERESIS BELOW + U+032E COMBINING BREVE BELOW |
ꪔ̤̫ |
U+AA94 TAI VIET LETTER LOW TO + U+0324 COMBINING DIAERESIS BELOW + U+032B COMBINING INVERTED DOUBLE ARCH BELOW |
U+AA94 は Tai Viet という実在の文字体系の子音字だ。[1] その下へ、本来は音声記号や転写などで使われる結合記号を重ねている。[2]
だから「うさぎに見える」のはUnicode規格がうさぎを仕込んだわけではない。人間の脳が勝手に顔を発見しただけだ。
顔文字文化、強い。
4. なぜタイ語に混ぜると異様に強いのか
ここは「タイ文字が変」という話ではない。読めない側の視覚認知の話だ。
タイ文字は、母音記号や声調記号などが基字の上下に配置されることがある。さらに通常のタイ語文章は、英語のように全単語をスペースで区切る方式ではない。Unicodeの行分割仕様でも、タイ文字は単純な「スペースを探して切る」では済まず、言語依存の解析が必要な複雑文脈の文字群として扱われる。[4]
そこへ、下側に記号を2個ぶら下げた ꪔ̤̮ が入る。
読む人には当然「別の文字が混入した」と分かる。しかしタイ文字を読めない人には、周囲も上下へ形が広がって見えるため、うさぎの輪郭が文章の植生へ妙に馴染む。
結果、
ภาษาไทยꪔ̤̮ภาษาไทย
が、
「草、草、草……ん? 今なんか目合った?」
になる。
5. ミャンマー・クメール・マラヤーラムも強い。ただし優勝は主観
同じ遊びは他の文字でも成立する。
ミャンマー文字:
မြန်မာမြန်မာ ꪔ̤̮ မြန်မာမြန်မာ
クメール文字:
ខ្មែរខ្មែរអក្សរ ꪔ̤̮ ខ្មែរខ្មែរ
マラヤーラム文字:
മലയാളം മലയാളം ꪔ̤̮ മലയാളം
シンハラ文字:
සිංහල භාෂාව ꪔ̤̮ සිංහල
タイ文字:
ภาษาไทยกำลังเขียนอยู่ ꪔ̤̮ ภาษาไทย
視覚ネタとしての個人的な印象は、タイ語がかなり強い。上下の記号と連続する曲線の中へ ꪔ̤̮ が入ると、「隠した」より「もともと生息していた」感が出る。
ただしこれは文字体系の優劣ではない。「知らない文字を模様として見たとき、顔文字がどれだけ迷彩になるか」という完全にどうでもいい競技である。
世界選手権を開催してはいけない。
6. 翻訳に入れたら本当に崩れた。でも犯人をUnicode正規化だけにするのは早い
10匹版を翻訳系の処理へ入れたあと、こんなふうに不自然な空白が混ざった表示が観察された。
ภาษาไทยꪔ̤̮ภาษาꪔ̤̥ไทยภ าษาꪔ̤̫ไทยꪔ̤̮กำลังꪔ̤̥เข ียนอยู่...
ここで「Unicodeだからバグった」は半分正しいが、「正規化が勝手に空白を入れた」まで言うと雑だ。
Unicode正規化は、同じ文字を表す複数の符号列を一定の形式へそろえる仕組みだ。結合記号の分解・合成・標準順序などを扱う。[5] 一方、翻訳サービスはその前後で、文の分割、単語境界推定、トークン化、未知文字処理、フォント選択、Web表示、コピー&ペーストなど多数の処理を通る。
タイ語やミャンマー語のように分割自体が言語依存になる文字列へ、別文字体系の基字+複数の結合記号を差し込む。これは文字処理パイプラインへ「ほら、考えてみろ」と渡す嫌なテストケースになる。
どの層が空白を入れたかは、同じ入力を保存・送信・翻訳・表示の各段階でコードポイント列として比較しない限り断定できない。
うさぎはかわいい。原因分析はかわいくない。
7. 「見た目の1文字」と「コンピューターの1文字」は違う
Unicodeで重要なのが grapheme cluster、つまり人が1文字だと感じる単位だ。[3]
たとえばアクセント付き文字は、「完成済みの1コードポイント」の場合もあれば、「基字+結合記号」の複数コードポイントの場合もある。画面では同じ1文字に見えることがある。
ꪔ̤̮ も同じ方向の罠を持つ。
- 見た目:うさぎ1匹
- コードポイント:3個
- 文字列長:APIや言語によって数え方が変わり得る
- カーソル移動:書記素クラスタ対応なら1単位として動くのが理想
- Backspace:実装によって全部消えるか、結合記号だけ剥がれるか差が出る
- 検索・比較:正規化方針で結果が変わり得る
- フォント:別スクリプトのフォールバックで形や位置が変わる場合がある
つまり「画面で1匹」は、内部でも1匹とは限らない。
野生動物管理台帳がコードポイント単位だった。
8. 実はUnicodeテスト文字列としてかなり優秀
ふざけて作ったが、ꪔ̤̮ はテスト素材として面白い。
確認できるものが多い。
- UTF-8で保存・復元できるか。
- 結合記号を落とさずコピーできるか。
- 文字数カウントがコードポイント依存で壊れないか。
- カーソルや削除が書記素クラスタを尊重するか。
- NFC/NFDなどの正規化前後で比較ロジックが破綻しないか。
- フォントフォールバックで結合記号が迷子にならないか。
- タイ語・ミャンマー語等の分割や改行へ混ぜてもUIが壊れないか。
- 翻訳API往復後に、元のコードポイント列が必要以上に改変されないか。
- 検索インデックスが「見える文字」と内部表現の差を扱えるか。
- モバイル・PC・ブラウザ差で巨大なズレが出ないか。
ただし本番記事へ無意味に混ぜるのはやめた方がいい。テストフィクスチャとして隔離し、期待するコードポイント列を明示した方が安全だ。
本番データに野生のうさぎを放流するな。
9. 多言語サイトでの実務対策
ネタを実務へ戻すと、やることは地味だ。
- 文字コードはUTF-8で統一する。
- 正規化方針を決める。WebコンテンツではNFCを基本にする設計が一般的だが、目的に応じて原文保持との境界を決める。[5]
- 「文字数」はUTF-16コードユニットや単純な配列長ではなく、用途に応じてコードポイント・書記素クラスタ・バイト数を区別する。
lang属性を正しく付け、言語ごとのフォントと行間を確認する。- Thai / Myanmar / Khmer など、単純な空白分割が通用しない文字を実機で確認する。[4]
- 翻訳前後でsource-authored textを勝手に正規化・置換しない。必要な変換は仕様として固定する。
- 文字化けだけでなく、結合記号の位置、行切れ、選択、コピー、検索、読み上げもQAする。
- おかしな出力が出たら「Unicodeのせい」で終わらせず、入力→保存→API送信→翻訳→受信→描画のどこで列が変わったか比較する。
Unicodeは悪くない。だいたいUnicodeを扱う側が『1文字って1個でしょ?』と油断した瞬間に森からうさぎが出てくる。
10. 結論:草むらにいたのは、うさぎではなく人間の顔認識とUnicodeの奥深さだった
発端はただのSimeji顔文字だった。
ꪔ̤̮
それをミャンマー語へ混ぜたら草むらに見えた。タイ語へ混ぜたら完全に生息した。10匹入れたら群れになった。翻訳へ通したら文字列まで怪しくなった。
しかし分解してみると、話はかなりまともなUnicode入門になる。
- 見た目の1文字は、複数コードポイントかもしれない。
- 結合記号は基字へ重なって描画される。
- 東南アジアの文字には単純な空白分割では扱えないものがある。
- 正規化、分割、改行、フォント、翻訳、描画は別レイヤーだ。
- 「変な表示が出た」だけでは犯人を断定できない。
そして何より、
ภาษาไทยꪔ̤̮ภาษาไทย
これを一度「草むらに隠れたうさぎ」だと思ってしまうと、もう戻れない。
Unicodeの勉強をしていたはずなのに、生息確認で終わった。
