问题

微信授权登录 app 后,昵称中含 emoji 符号无法显示

原因,微信实际是U+0001F61E,用了第3方授权控件,从微信取到后被截断为U+F61E

Unicode & utf-8

Unicode是抽象编码集,记着编码—字符对应关系,但不负责实际编码存储和传输

最流行的用于传输和存储的是 utf-8,8 位变长编码方法。向下兼容 ASCII 码

字符属于 ASCII 集时,utf-8 使用一个 byte,也就是 8 bit 编码,超出后,则使用两个 byte,再超出则使用 3 bytes,03 年 RFC3629 最长到 4 个字节

0xxxxxxx
110xxxxx 10xxxxxx
1110xxxx 10xxxxxx 10xxxxxx
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

Unicode 两个编码集,UCS-2(字节) 和 UCS-4(字节)

光汉字(GB18030)就超 7 万,UCS-2 不够

早期 Mysql 中,不支持 4 bytes 的 utf-8(utf8mb4),将截断成 3 bytes

微信编码

从微信取的数据属 PUA(Unicode Private Usage Area)

早期编码最流行版本是 Soft-bank,使用的是 Soft-bank PUA,最大用户群体是 Apple iOS 5 前

Soft-bank 版本和 unified 版本 emoji 有对应关系,问题是,如何将Soft-bank PUA emoji 正确显示出来

为什么微信不统一将旧编码替换成新编码

PUA 是私有码段,各组织自己内部使用来做些爱做的映射(苹果映射关系,微软映射关系等)

emoji 统一前,三大派系—Google、Soft-bank(Apple)、NTT DoCoMo 都用的是 PUA,一个编码,在 Google 那边的 emoji 是😇(天使),在 Soft-bank 那边可能就是😈

另一极端场景, iPhone 3GS 内置的Soft-bank emoji 编码,将其中一个作微信昵称

如果微信将这个编码替换成统一码,在该系统上没有对应的字符,昵称不能正确显示

对用户来说,自然是一个 bug

微信解决方案是,在客户端维护映射表,从客户端解决显示兼容问题

可以怎么做

从工程上考虑,客户端维护映射,成本上比统一处理要高

且正面效果随着时间的流逝会越来越弱,机器,软件在逐年更新换代,历史包袱越来越轻

app 用户群体鲜明特征就是使用旧设备的极少

大部分情况可以从服务端解决兼容问题

统一处理需要做的事情也是不少的

一次性批量替换数据库中的内容,还有新增内容的过滤,且新增内容的来源很多,这样过滤器就要支持得很好才行,维护成本也高

换个角度,从服务器输出内容处做过滤,则要简易得多。nginx lua script 做 response body sub filter 的解决方案

macOS 10.12 与 iOS 10 iPhone 、iPad 上对 PUA 的支持情况

维基百科 Emoji 页面上 PUA 的章节

  • macOS 10.12 Safari

1

  • iOS 10.0.1 iPhone 6s Plus

1

  • iOS 10.0.1 iPad Pro 9.7

1

虽同为 iOS 10.0.1,iPad Pro Emoji PUA 支持