Skyphobia

关于Hugo和Hexo生成日语浊音假名标题链接导致404的问题

由于平时常用博客发表一些涉及日文相关的文章,免不了在标题中使用到一些日语。然而在将生成 HTML 部署到服务器上后,这些带了日语的标题经常会出现 404 Not Found 的情况。

起初我以为是生成的 Permalink 有问题。因为在复制了服务器上对应的文件名后再手动输入链接地址,居然是能直接访问到目标网页的。于是我比对了一下生成的 Permalink 和实际能够访问到文章的 URL,发现了其中的玄机:

对比结果显示两个字符串不相等

表面上看到的是同一串字符串,浏览器对它们分别进行 URL encode 出来的结果却一长一短,只有部分相等。尝试复制两串字符串,在控制台中进行全等比较发现看上去相同的两个字符串实际上并不全等。剔除相等部分后可以看到两边不一样的地方。

字符串不一致的地方

这个时候就比较尴尬了,为什么同样的一串字符串,Hugo生成出来文件名会和原来的不一样呢?粗略比较了一下正常链接和导致 404 的链接,几乎可以大胆地猜测导致 404 的元凶是日语的浊音假名。

根据猜测,这里可以找到一个线索:长的字符串中有一个字重复了两次。单独把重复了两次的%E3%82%99进行一次 decode 会发现这个“神秘”的字符串其实正是代表浊音的两个点

再则,同样的问题在 Linux 系统(elementary OS 0.4 Loki)上无法复现(手边没有 Windows 系统未进行测试),URL encode 的结果和文件名 encode 的结果始终保持一致。

基本排查到这个地方,这个问题就有眉目了。Hugo 生成文件时依赖于文件系统,那就是我使用的 macOS 在文件系统的编码上有着异于常人的筋骨体肤了:

Mac OS X 操作系统使用正式分解万国码(canonically decomposed Unicode),在文件系统中使用 UTF-8 编码进行文件命名,这做法通常被称为 UTF-8-MAC。正式分解万国码中,预组合字符是被禁止使用的,必须以组合字符替换。
这种方法使分类变得非常简单,但是会搞混那些使用预组合字符为标准、组合字符用来显示特殊字符的软件。Mac 系统的这种 NFD 数据是万国码规范化(Unicode normalization)的一种格式。而其他系统,包括 Windows 和 Linux,使用万国码规范的 NFC 形式,也是 W3C 标准使用的形式。所以通常 NFD 数据必须转换成 NFC 才能被其他平台或者网络使用。

以上引自 UTF-8 - 维基百科,自由的百科全书

因此问题的根源就在于 macOS 的文件系统使用 NFD,会把浊音假名这类组合字拆开,而浏览器采用的是 W3C 标准的 NFC 形式,所以浏览器 encode 的结果就和 macOS 下 Hugo 生成出来的文件名 encode 的结果不一致了。

Google 了一下发现大多数人选择了退而求其次,用纯英数字的 slug 来代替标题作为链接,以此避免 NFD 的问题,但是这个做法对 SEO 貌似不是很友好……这里暂时想到两个除此之外的解决方案,仅供参考:

方案一:在服务端安装并运行 convmv,把 NFD 转回 NFC,有条件的可以写个监听,只要文件发生变动就自动跑一遍 convmv 的脚本

1
2
apt-get install convmv
convmv -f utf-8 -t utf-8 -r --notest --nfc [需要转码的文件夹]

方案二:如果使用 FileZilla 的话,把文章所在的文件夹单独上传至服务器,FileZilla 似乎会对上传的一级目录进行 NFD 2 NFC 的转换

PS:其实 macOS 下也有一个 convmv,但并没有什么太大的用处,因为无论在 macOS 环境下转码多少次,文件系统依旧会雷打不动地使用 NFD……