如何加速 msgmerge 合并翻译文件:gettext 模糊匹配与清理技巧详解

msgmerge 太慢怎么办?gettext 翻译流程优化与 fuzzy 用法全解析

文章目录

在构建多语言网站或应用时,使用 gettext 工具链处理 .po 文件和 .pot 模板是标准流程。但随着项目规模扩大,你可能会发现 msgmerge 处理大文件非常慢,且翻译条目行为有些“诡异”:明明写了翻译,实际却显示原文。本文从开发者角度深入拆解这些坑,并提供一整套高效、可靠的处理方案。

gettext 工作流简述

典型工作流:

  1. 从源码生成模板文件 .pot(通过 xgettext
  2. .pot 与已有 .po 合并(通过 msgmerge
  3. 使用翻译工具修改 .po
  4. 编译为 .mo 文件供程序使用(msgfmt

本文重点关注第 2 步:msgmerge 合并行为及其性能与正确性问题。

gettext 合并 po 文件太慢?本文教你提速翻译流程

问题一:msgmerge 合并太慢,怎么办?

.po 文件有成千上万个条目时,默认合并速度可能非常慢。原因在于:

  • 默认启用 fuzzy 匹配,gettext 会对每个 msgid 做模糊比对;
  • fuzzy 比对代价高(字符串距离、近似匹配、语义相似度);
  • 模糊结果还会被标记为 #, fuzzy,需要人工审核。

解决方案:关闭 fuzzy 匹配

使用 --no-fuzzy-matching 可大幅提升速度,跳过所有模糊匹配逻辑,只保留精确匹配项:

msgmerge --no-fuzzy-matching old.po template.pot -o merged.po

那 fuzzy 匹配有什么用?为什么默认启用?

fuzzy 是 gettext 系统的“翻译保守机制”:

  • 当模板中的句子发生轻微改动(如 "Save" 改为 "Save file"),会尝试找到旧翻译;
  • 系统自动标记为 #, fuzzy,表示 “可能对,但需要人类确认”
#, fuzzy
msgid "Save file"
msgstr "保存"

重点:fuzzy 条目不会在实际运行中生效,gettext 会忽略其 msgstr!

那我是不是可以干脆不开 fuzzy?

是的!特别适合以下场景:

  • 个人开发者 / 独立站点:效率优先,旧翻译价值低;
  • 翻译内容经常变动:旧句子参考意义小,保留反而污染;
  • 不依赖 Poedit 或专业翻译工具:人工清洗 fuzzy 成本高。

推荐做法:

msgmerge --no-fuzzy-matching old.po template.pot -o merged.po

搭配清理工具:

msgattrib --no-obsolete merged.po -o clean.po

fuzzy 条目的运行行为再总结一下

条目状态实际显示内容使用 msgstr 吗?
正常翻译翻译内容✅ 是
#, fuzzy 标记原文(msgid)❌ 否
空的 msgstr原文(msgid)❌ 否

误解警告:我看有些文章,包括 AI 回复都是不对的。--previous 不会提升性能,反而略微拖慢。

实际作用是:

把旧的 msgid 作为注释保留下来,供翻译者参考。

例子:

#| msgid "Save"
msgid "Save file"
msgstr "保存文件"

适合在关闭 fuzzy 后仍保留一点上下文提示,但它会:

  • 增加合并逻辑复杂度
  • 增加 .po 文件体积
  • 对自动化流程没帮助

仅建议用于人工翻译或 GUI 工具(如 Poedit)环境下。

那旧的 msgid 会不会残留在 .po 文件里?

是的,但是以“废弃条目”的形式存在:

#~ msgid "Save"
#~ msgstr "保存"

这些是 gettext 特有的 obsolete 条目,不会影响翻译,但会让 .po 文件臃肿。

可用 msgattrib 清理:

msgattrib --no-obsolete merged.po -o clean.po

是否可以直接覆盖同一个文件输出?

msgattrib --no-obsolete merged.po -o merged.po  # 是否可以?

不推荐!

虽然工具允许,但:

  • 会触发系统层“先清空再写入”风险;
  • 容易造成文件半写入、损坏或丢失;
  • 调试困难,流程不安全。

推荐更安全的做法:

msgattrib --no-obsolete merged.po -o tmp.po && mv tmp.po merged.po

或使用临时文件自动生成:

tmp=$(mktemp)
msgattrib --no-obsolete merged.po -o "$tmp" && mv "$tmp" merged.po

推荐合并 + 清理脚本流程(适合开发者)

#!/bin/bash
set -e

OLD_PO=$1
POT=$2
OUT_PO=${3:-merged.po}

TMP_MERGED=$(mktemp)
TMP_CLEANED=$(mktemp)

# 合并并禁用 fuzzy
msgmerge --no-fuzzy-matching "$OLD_PO" "$POT" -o "$TMP_MERGED"

# 清理废弃条目
msgattrib --no-obsolete "$TMP_MERGED" -o "$TMP_CLEANED"

# 移动到目标文件
mv "$TMP_CLEANED" "$OUT_PO"

总结

问题/目标推荐做法
合并过慢使用 --no-fuzzy-matching
想保留旧 msgid 提示信息使用 --previous
fuzzy 条目被显示为原文清除 fuzzy 或人工确认后移除 fuzzy 标记
旧 msgid 仍留在文件中使用 msgattrib --no-obsolete 清理
安全写入 .po 文件输出到临时文件后 mv 覆盖

适用判断表

场景是否关闭 fuzzy是否使用 previous是否清理 obsolete
个人项目 / 自动化✅ 是❌ 否✅ 是
需要上下文提示 / GUI 翻译工具❌ 否✅ 是可选
内容频繁变化 / 快速迭代项目✅ 是可选✅ 是
专业翻译团队 / 翻译一致性要求❌ 否✅ 是可选

如果你正在构建国际化支持的 CI/CD 流程、开发自动翻译工具链、或者维护一个翻译量大的项目,希望这篇文章能为你节省大量时间,并避免一些不易察觉的陷阱。


也可以看看