gettext .mo 目录文件检查器
.mo 文件是 msgfmt 编译 .po 之后留下的产物,也是真正随程序发布到 locale/<lang>/LC_MESSAGES/ 的那个文件。一旦 .po 丢失,它就成了没人能读的二进制,尽管结构本身很小:一个魔数、一个修订号、两张字符串表的条目数与偏移,外加一张读取器可以忽略的可选哈希表。本工具把这些全部走一遍,显示里面究竟有什么。魔数是 0x950412de,同时也是它的字节交换值 0xde120495,这正是文件声明其余各个字的字节序的唯一方式,所以为大端机器编译的目录文件在这里读起来和小端的完全一样。.mo 里藏着的两样东西都是内嵌的 NUL 字节,在文本编辑器里看不见,也最容易弄错:带上下文的条目把 msgctxt、一个 0x04 字节和 msgid 拼成一个字符串;复数条目在原文一侧用 NUL 分隔 msgid 与 msgid_plural,在译文一侧用 NUL 连接各个形式。本工具把两者都拆开,上下文单独成列,复数形式编号列出。msgid 为空的元数据条目会被解析成 Project-Id-Version、Language、Content-Type、Plural-Forms 和 POT-Creation-Date,文件中其余字符串一律按该文件头声明的编码解码,而不是靠猜。当文件头缺失或撒谎时,页面会明说,而不是默默产出乱码。复数表达式会被解析并对 n = 0 到 20 求值,于是你能看到每个数量选中哪一种形式;条目携带的形式数量还会与 nplurals 核对,两者不符是真实存在的缺陷,只在某些数量上返回错误的字符串。此外还会指出未翻译条目、与 msgid 完全相同的译文、译文中丢失的 %s 或 {}、未排序的原文表(C 语言读取器的二分查找依赖该顺序),以及槽位少于条目数的哈希表。它不会把目录文件还原成 .po,也无法判断翻译好坏,只回答文件结构是否可靠、自身是否一致。
使用方法
- 把 .mo 文件拖到框里:可以是 locale/<lang>/LC_MESSAGES/ 里的编译产物,也可以是 msgfmt 生成的任何文件。
- 先看文件头一行:字节序、修订号、条目数,以及两张字符串表和哈希表在文件中的实际位置。
- 在元数据条目中确认字符编码。下面所有字符串都用它解码,因此 Content-Type 有误应当最先修正。
- 查看 n = 0 到 20 的复数规则表,看每个数量选中哪种形式,并把复数条目的形式数量与 nplurals 对照。
- 浏览发现的问题列表,再用过滤框在条目表中定位某个 msgid、译文或上下文。
常见问题
- 为什么魔数会出现两个不同的值?
- 魔数只有一个,就是 0x950412de。但 .mo 文件把每个 32 位字都按编译它的机器的字节序存放,魔数也一样。读取器把前四个字节按两种方式各读一遍:若按小端读得到 0x950412de,整个文件就是小端;若按大端读得到该值,整个文件就是大端。这就是全部的字节序声明,别处没有任何标志位,所以在大端机器上编译的目录文件在小端机器上照样能用。本页面把原始的四个字节和推断出的字节序并排显示,你可以自己核对,而不必只是相信结论。
- 有些 msgid 里的 0x04 字节是什么?
- 那是 msgctxt 的存放方式。gettext 没有单独的上下文字段,于是 msgfmt 把上下文、一个 0x04 字节(ASCII 的 EOT)和 msgid 拼成一个字符串,pgettext 查找的正是这个拼接后的字符串。所以两个条目可以共用 msgid “Open” 而仍然是不同条目:一个是 "menu\x04Open",另一个是 "state\x04Open"。在编辑器或简单的转储里分隔符是看不见的,两者看起来就像重复项。本工具在该字节处切分,并让上下文单独成列,共用同一 msgid 的条目就会并排出现。
- 复数形式如何存放?nplurals 又要与什么一致?
- 原文一侧是 msgid、一个 NUL 字节,然后是 msgid_plural;译文一侧是各个形式用 NUL 连接起来。应该有几种形式由文件头决定:Plural-Forms 里有 nplurals,以及一个关于 n 的表达式,返回要使用的形式下标。如果某条目的形式数少于 nplurals 所承诺的,选中缺失下标的那些数量将取不到任何内容,gettext 会退回未翻译的 msgid 或 msgid_plural,而且只在部分数字上发生,这正是该缺陷能通过测试的原因。本页面统计每个复数条目的形式数量,并标出与文件头不符的条目。
- Content-Type 文件头缺失或有误会怎样?
- 文件内部除了这个头之外没有任何东西标明自身编码,读取器只能相信它。Python 的 gettext 会直接拒绝该目录文件:未填写的 charset=CHARSET 触发未知编码错误,按声明编码无法解释的字节触发解码错误。本页面刻意更宽容:退回 UTF-8,尽可能解码,并告诉你发生的是哪一种,因为你打开这个文件往往就是为了查清应用为何显示乱码。完全没有元数据条目的目录文件则既无编码,也无语言和复数规则;gettext 这时把字符串当作 ASCII,并套用日耳曼语系的 n != 1 规则。
- 条目顺序重要吗?
- 重要,而且这是手写 .mo 生成器最常忽略的要求。gettext 规范要求原文字符串按字节顺序排序,因为读取器可以在原文表上用二分查找定位条目。C 实现在没有可用哈希表时正是这么做的,于是未排序的目录文件会悄悄找不到某些条目;而把整个文件读成字典的 Python 却能完全正常读取。这是一类很讨厌的缺陷:在你的测试脚本里正常,在应用里却不行。本页面会检查排序,并报告有多少对条目次序颠倒。
- 哈希表有什么用,可以忽略吗?
- 它是可选的查找加速结构:msgfmt 写入一张 hashpjw 表,槽位比条目数多约三分之一,采用开放定址,这样读取器不用二分查找也能找到 msgid。读取器完全可以忽略它,Python 就是这么做的,大小为 0 的目录文件也完全合法。不合法的是槽位不多于条目数的表,因为开放定址至少要留一个空位;出现这种组合说明写文件的程序并不理解该格式,因此这里会报告出来。页面显示的大小与偏移直接取自文件头中的字。
相关工具
Java 类文件分析器
在浏览器中解析编译好的 .class 文件:类文件版本与对应的 Java 版本、访问标志、常量池、字段、方法和引用的类。
字体文件检查器
打开 .ttf、.otf 或 .woff,读取真实字族名、字形与字符数量、版本、许可文本以及嵌入权限。
WAV 文件检视器
拖入 .wav 文件即可查看格式、声道、采样率、位深、时长、所有 RIFF 块以及 LIST/INFO 元数据 — 完全在浏览器内运行。
STL 3D 模型检查器(三角形、体积、包围盒、打印床适配)
拖入 .stl 文件(二进制或 ASCII),立即获得三角形数量、包围盒、X×Y×Z 尺寸、体积、表面积、水密性检测、三视图正交预览,以及是否能放进常见 3D 打印机床面(Bambu、Prusa、Voron、Ender)。
Mbox 归档查看器
在浏览器里切分 .mbox 归档:邮件边界、解码后的头部、MIME 结构、会话线索与重复 Message-ID,全程不上传。
ZIP 内容查看器
拖入 ZIP 即可查看内部文件——大小、内容、单文件下载,无需在本地解压。