草叢裡有兔子:把 Simeji 顏文字塞進泰文與緬甸文後,Unicode 突然變成野生動物園

它看起來像一隻小兔子正在盯著你,但 Unicode 並沒有把它編碼成「兔子顏文字」。

分享這篇文章
廣告
廣告

1. 5秒結論:看起來是一隻兔子,內部其實是三個碼位疊起來

主角是這個:

ꪔ̤̮

它看起來像一隻小兔子正在盯著你,但 Unicode 並沒有把它編碼成「兔子顏文字」。底層是 U+AA94 TAI VIET LETTER LOW TO,後面接著 U+0324 COMBINING DIAERESIS BELOW 與 U+032E COMBINING BREVE BELOW。[1][2]

也就是說,畫面上是「小動物」,資料裡卻是「Tai Viet 字母 + 下方組合符號 + 下方組合符號」。

長得很可愛,戶籍資料非常麻煩。

2. 30秒概要:丟進「草」裡之後,它真的開始棲息了

最開始只是一個裝飾顏文字:

- ̗̀ ꪔ̤̥ꪔ̤̮ꪔ̤̫ ̖́-

放進緬甸文字裡:

မြန်မာမြန်မာမြန်မာ ꪔ̤̮ မြန်မာမြန်မာ

對不識這種文字的人來說,圓潤又密集的字形可能像一片草叢,兔子臉就像從裡面探頭。

再放進泰文:

ภาษาไทยꪔ̤̮ภาษาไทย

第一反應就會變成:「等一下,裡面是不是有兔子?」

於是當然要放十隻。

ภาษาไทยꪔ̤̮ภาษาꪔ̤̥ไทยภาษาꪔ̤̫ไทยꪔ̤̮กำลังꪔ̤̥เขียนอยู่ꪔ̤̫ภาษาไทยꪔ̤̮ภาษาꪔ̤̥ไทยภาษาꪔ̤̫ไทยꪔ̤̮

到這裡已經不是閱讀,而是野生動物數量調查。

3. 「兔子」到底是什麼

外觀 碼位
ꪔ̤̥ U+AA94 + U+0324 + U+0325
ꪔ̤̮ U+AA94 + U+0324 + U+032E
ꪔ̤̫ U+AA94 + U+0324 + U+032B

U+AA94 是真實存在的 Tai Viet 文字系統中的子音字母。[1] 下方疊加的則是各自獨立的 Unicode 組合附加符號。[2]

不是 Unicode 官方偷偷畫了兔子,而是人的視覺系統從合法的字元組合裡認出了一張臉。

顏文字文化立刻把它抓來用了。

4. 為什麼泰文看起來特別像高強度偽裝

這不是說泰文字「奇怪」。這是對不會讀泰文的人而言的視覺笑話。

泰文有不少符號會出現在基字的上方或下方,而且一般泰文不像英文那樣把每個單字都用空格隔開。Unicode 的斷行規範也把泰文列入需要語言相關脈絡分析才能找出合適斷行位置的複雜文字之一。[4]

這時再塞進一個自己就把兩個組合符號掛在基字下方的 ꪔ̤̮。

會泰文的人自然能看出混入了外來字元;不會的人卻容易把整行先看成連續曲線與上下符號形成的「紋理」,於是兔子臉得到意外的迷彩效果。

ภาษาไทยꪔ̤̮ภาษาไทย

視覺翻譯就是:草、草、草……嗯?剛才是不是跟什麼東西對到眼?

5. 緬甸文、高棉文、馬拉雅拉姆文、僧伽羅文也能參賽

緬甸文:

မြန်မာမြန်မာ ꪔ̤̮ မြန်မာမြန်မာ

高棉文:

ខ្មែរខ្មែរអក្សរ ꪔ̤̮ ខ្មែរខ្មែរ

馬拉雅拉姆文:

മലയാളം മലയാളം ꪔ̤̮ മലയാളം

僧伽羅文:

සිංහල භාෂාව ꪔ̤̮ සිංහල

泰文:

ภาษาไทยกำลังเขียนอยู่ ꪔ̤̮ ภาษาไทย

所謂「哪個最強」完全是主觀玩笑。這些都是正常、完整、真實使用的文字系統,不是背景花紋。笑點只在於,不識某種文字的人可能先把陌生字形視為紋理,然後那隻兔子剛好神奇地混了進去。

泰文只是這項毫無必要的比賽裡非常強的選手。

6. 經過翻譯後真的出現異常空格,但不能直接把鍋丟給 Unicode 正規化

十隻兔子版本經過某種翻譯流程後,曾觀察到類似以下的異常顯示:

ภาษาไทยꪔ̤̮ภาษาꪔ̤̥ไทยภ าษาꪔ̤̫ไทยꪔ̤̮กำลังꪔ̤̥เข ียนอยู่...

說「這是 Unicode 的邊界案例」沒問題,但說「正規化自己插入了空格」就太武斷。

Unicode 正規化主要定義等價字串如何分解、依標準順序排列與重新合成。[5] 翻譯產品在這之外還可能做句子切分、詞界推定、斷詞、未知字元處理、儲存轉換、字型後備、版面配置與複製貼上等工作。

泰文和緬甸文的分割本來就比「按空格切英文」更依賴語言脈絡。[4] 再混入「另一個文字系統的基字 + 多個組合附加符號」,非常適合把整條文字處理管線逼到極限。

要抓到真正的變化點,必須逐層比較輸入、儲存、API 請求、翻譯結果、回應解析與最終繪製時的確切碼位序列。

兔子很可愛,事故分析一點都不可愛。

7. 螢幕上的「一個字」不一定等於一個 Unicode 碼位

Unicode 文字處理有 grapheme cluster(字素叢集) 的概念,用來近似使用者認為的一個字元單位。[3]

一個帶重音的字母可能是單一預組碼位,也可能是基字加上一個或多個組合符號。螢幕上都像一個字,內部表示卻可能不同。

這隻兔子也是如此:

  • 視覺上:1隻兔子
  • 內部:3個碼位
  • 字串長度:依程式語言與 API 的計數模型而不同
  • 游標移動:理想上應以使用者感知單位處理
  • Backspace:不同實作可能有不同結果
  • 搜尋與比較:受到正規化策略影響
  • 字型:後備字型與組合符號定位可能改變外觀

原來野生動物登記是按碼位建檔。

8. 雖然是玩笑,卻是一個不錯的 Unicode 測試字串

ꪔ̤̮ 可以順手測試:

  1. UTF-8 往返保存
  2. 複製貼上是否保留組合符號
  3. 長度計算是否區分碼位與字素叢集
  4. 游標與刪除行為
  5. NFC/NFD 比較策略
  6. 字型後備與附加符號位置
  7. 與泰文、緬甸文、高棉文混排時的斷行
  8. 翻譯 API 往返
  9. 搜尋索引
  10. 不同瀏覽器與手機的繪製

但不要無緣無故把它撒進正式內容。這種極端字串應放在明確的測試資料中,並記錄預期碼位序列。

不要在正式資料庫裡放生野兔。

9. 多語網站的實務對策

  • 統一使用 UTF-8。
  • 明確定義正規化策略。Web 內容常用 NFC,但也要規範何時必須保持作者原文。[5]
  • 區分位元組、code unit、碼位與字素叢集。
  • 正確設定 lang,測試對應字型與行高。
  • 對泰文、緬甸文、高棉文等無法靠簡單空格切分的文字做實機測試。[4]
  • 翻譯匯入時不要靜默改寫作者原文。
  • QA 不只看亂碼,也要看附加符號位置、換行、選取、複製、搜尋與無障礙。
  • 發生異常時逐層比較碼位序列,不要一句「Unicode 有毒」就結案。

Unicode 多半只是照規格辦事。真正危險的是軟體偷偷假設「肉眼一個字 = 內部一個簡單單位」。

兔子就是從這個假設的縫隙裡鑽出來。

10. 結論:草叢裡真正藏著的是人類的模式辨識與 Unicode 的深度

故事從一個可愛的 Simeji 風格顏文字開始。

ꪔ̤̮

放進緬甸文,它像躲在草裡;放進泰文,它像原本就住在那裡;放十隻,變成族群;再送進翻譯管線,直接變成 Unicode 除錯課。

玩笑底下其實有幾個很正經的結論:

  • 視覺上的一個字元可能由多個碼位組成。
  • 組合附加符號會依附基字繪製。
  • 某些文字需要語言相關的分割處理。
  • 正規化、分割、斷行、字型、翻譯與繪製是不同層。
  • 一張奇怪截圖不能證明到底是哪一層出錯。

而且只要你曾經把這行看成「草裡有兔子」:

ภาษาไทยꪔ̤̮ภาษาไทย

就很難再恢復正常視角。

本來是來學 Unicode 的,最後做完了兔子棲地確認。


廣告

尋找其他文章

所有文章

Mendoi-chan

作者

Mendoi-chan

把工作與日常生活中的麻煩整理成清楚的結構與下一步行動。

關於本站
廣告

最新文章

  1. 1一天睡了18小時,是恢復性睡眠,還是需要留意的訊號?
  2. 2「沒讓父母抱到孫輩,對不起」真的有必要嗎?——成年子女回家陪父母吃頓飯,本身就可能已經很有價值
  3. 340歲VTuber變成「數位里民活動中心」的那一天:年齡不一定殺死需求,它可能只是改變需求的形狀
  4. 4大約一週,AI文章自動化變成「自治工廠」:Ultra一拳、Level 6,以及為什麼Level 7還不用急
  5. 5AI很強,但工廠常常停在「所以到底要做什麼?」——能點燃第一個想法的人,才會把能力變成生產力

推薦閱讀

廣告