絵文字が文字化けする?バリアントセレクタが原因の不具合と対策

こんにちは。Webエンジニアの奥村一貴(YOUTRUST / X)です✨
7月でYOUTRUSTに入社して1年になりました!

今回は絵文字のUnicode、バリアントセレクタを理解しておらずQAで不具合が見つかった話を紹介します。

不具合の内容

ユーザー登録後に運営から送信されるチャットメッセージにて、チェックマークの絵文字が一部崩れて表示されていました。
※ 実装背景としては、プロフィール入力率を上げるためのオンボーディング施策の一貫として実装していました。

特徴的だったのは以下の点です
- iOS端末では正常に表示される
- WindowsとAndroidの端末のみで発生


不具合の原因

実際にRubyでバイト列を調べてみると、"不可視文字"が混入していることがわかりました。

具体的には「バリアントセレクタ(Variant Selector, VS16)」と呼ばれるもので、例えば "✅︎" と "✅" は見た目は同じですが、バイト列は異なります。

a = "✅︎"
b = ""

puts a.bytes.inspect # => [226, 156, 133, 239, 184, 142]
puts b.bytes.inspect # => [226, 156, 133]


※ 修正PRの差分をdiffで確認しても、ぱっと見では違いがわかりません。

バリアントセレクタとは

絵文字や記号には、「絵文字として表示するか」「テキストとして表示するか」を指定するための補助文字が存在します。

これが「バリアントセレクタ(Variant Selector)」と呼ばれるもので、代表的なものに以下の2つがあります。

  • U+FE0E: テキストスタイル(モノクロ、細い線)
  • U+FE0F: 絵文字スタイル(カラフルで装飾的)

この U+FE0E や U+FE0F は見た目には現れませんが、表示のスタイルに大きな影響を与えます。

参考

www.codejam.info

unicode.org


環境による表示の違い

なぜiOSでは正常に表示され、Windows/Androidでは表示が崩れるのでしょうか?

iOSではこのあたりの互換処理が優れており、バリアントセレクタが付与されていても自動的に調整され、見た目が一貫するように表示されます。


しかし、WindowsやAndroidではバリアントセレクタを厳密に解釈するため、同じ文字列でも「別のスタイルで表示される」といった差異が発生します。

そのため実装上は同じでも、UIの「見た目だけ変わる」という問題が発生することになります。


解決方法

バリアントセレクタが付いていない絵文字をコピーして置き換えるだけで解決できます。


まとめ

  • 絵文字にはバリアントセレクタという視認できない文字が付くことがある
  • iOS/Mac以外の環境では表示崩れの原因になる
  • 開発時はクロスプラットフォームでのテストが重要
  • AIツール使用時も最終的な人間のチェックが必要


おわりに

今回は、Devin(AIエージェント)に実装を依頼した際、テキストの絵文字にバリアントセレクタが混入してしまい不具合の原因になってしまいました。

特に見た目に違和感がなく、自身のMacやiPhoneでは再現しなかったため、QAエンジニアの指摘で初めて気づきました。
今回の件を通して、Unicodeの知識の重要性を実感しました。そして端末・OSの違いまで丁寧に見てくれるQAのありがたさを再認識しました。