引擎指南FAQPatreonDiscord下载
登录
RuneTranslate · 端到端翻译日语等 30+ 种语言的游戏
引擎指南对比图片文字存档编辑器作弊模式FAQ下载更新日志TrustpilotPatreonDiscordYouTube隐私政策服务条款联系我们
全部文章
gettext · po · mo · engine · tutorial

如何翻译游戏的 .po 和 .mo 文件(gettext)

2026年8月4日·7 分钟阅读

.po 是可编辑的源文件,.mo 才是游戏实际加载的已编译目录——这就是只改 .po 却毫无效果的原因。RuneTranslate 同时读取两者,保护 %s 和 {count} 占位符,按你的语言调整复数形式,并写回一个可用的 .mo。

如果你打开一个游戏文件夹,发现里面有个装满 .po 与 .mo 文件的 locale 目录,那么这个游戏是用 gettext 做的本地化 —— 而 RuneTranslate 现在可以把它从头到尾翻译完,从创建项目一直到导出可游玩的版本。

本文说明这两种文件到底是什么、为什么改动你能读懂的那一个通常在游戏里毫无变化,以及该如何正确地翻译它们。

.po 与 .mo —— 几乎人人都会踩的区别

它们是同一份语言包的两种形态:

  • `.po` —— Portable Object。可以用任意编辑器打开的纯文本文件。每条记录包含 msgid(原文)与 msgstr(译文)。译者实际工作的正是这个文件。
  • `.mo` —— Machine Object。同样内容的已编译二进制版本,其结构让游戏可以瞬间查到某条字符串。运行中的游戏读取的就是它。

最后这一点会让人白白搭进一个下午。你可以把 .po 的每一行都译完、保存、启动游戏 —— 却看不到任何变化,因为游戏根本不会打开 .po。它加载的是旁边那个 .mo,里面仍是旧文本。通常你需要用 GNU gettext 工具里的 msgfmt 把前者编译成后者。RuneTranslate 两个都会写出,因此没有额外的编译步骤。

gettext 游戏的文件夹长什么样

标准结构是一个按语言代码划分的 locale 目录:

  • MyGame/game.exe
  • MyGame/locale/en/LC_MESSAGES/mygame.mo
  • MyGame/locale/fr/LC_MESSAGES/mygame.mo
  • MyGame/locale/de/LC_MESSAGES/mygame.mo

LC_MESSAGES 文件夹是约定的一部分,并非笔误;mygame 则是文本域,也就是游戏加载字符串时所请求的名称。不少游戏改用更扁平的形式,例如 lang/fr.po,同样可以工作。

哪些游戏在用 gettext?

gettext 源自 Unix 世界,后来成为 C、Python 以及一切基于 GTK 的软件做本地化的默认方式。落到游戏上,就是:

  • Python 与 pygame 游戏
  • Solarus、SDL 及其他 C/C++ 引擎
  • 通过 Poedit、Weblate 或 Crowdin 做本地化的游戏 —— 它们都原生支持 PO
  • 部分 Godot 项目,其支持以 PO 作为翻译来源

它在欧美独立游戏中远比日式游戏常见 —— Kirikiri 和 RPG Maker 这类日系引擎都把文本存放在各自的格式里。

用 RuneTranslate 翻译 gettext 游戏

  1. 把 RuneTranslate 指向游戏文件夹。 也就是含有 locale/、lang/ 或 i18n/ 的那个。识别依据是文件的实际字节而不是扩展名,因此实际上是 3D 场景的 .mo 会被忽略,而不会生成一个坏掉的项目。
  2. 查看概要。 在你确认创建项目之前,会看到语言包数量、已编译 .mo 与源文件 .po 的比例、游戏已自带哪些语言,以及字符串总数。
  3. 选择语言与翻译服务。 免费的 Google 或 DeepL,或你自己的 DeepL、OpenAI、Claude、DeepSeek 密钥 —— 参见服务对比。
  4. 校对后导出。 你会得到一份译文已就位的游戏完整副本。

悄悄毁掉 gettext 翻译的两件事

占位符。 msgid 是格式字符串,而不是写好的成品文本。Hello, %s! 会在运行时把玩家名字代入。机器翻译常常会删掉 %s、给它加上空格,或者遇到 Python 的具名形式 %(player)s 时把里面的单词也译了。后果不是外观问题:少一个 %s 会让后面所有参数整体错位,多数引擎会直接崩溃。RuneTranslate 会在文本送往翻译服务之前,把它找到的每个占位符(%s、%d、%(player)s、{count}、{0})遮蔽起来,之后再还原。

复数形式。 英语有两种(“1 coin” / “5 coins”),日语和土耳其语只有一种,俄语和波兰语有三种。语言包会在文件头里声明自己用几种;一旦这个数字与语言对不上,gettext 就会越过列表末尾,然后悄无声息地退回未翻译的原文。RuneTranslate 会按你的目标语言重新适配这些形式,并相应改写文件头。

为什么导出会把语言包写两遍

导出既会生成一个规范的新语言 —— locale/<你的语言>/LC_MESSAGES/ —— 也会生成游戏原本就在加载的那个语言包的更新版本。

真正让译文显示出来的是后者。游戏决定加载哪种语言的方式,从文件夹里看不出来:它可能读取操作系统语言、某个配置文件、启动器参数,或者干脆写死在代码里。给一个只会请求英文的游戏加上中文语言包,你面对的将是一个看似毫无变化的版本。覆盖它确实会加载的那个语言包,就免去了这层猜测。两者都只发生在导出的副本中 —— 你原本的游戏文件夹不会被修改。

你尚未翻译的条目会保留游戏原有的译文,因此一个做到一半的项目绝不会清空开发者自己的本地化。

它做不到的事

只有已经存在于语言包中的文本才能被翻译。如果开发者把某条字符串直接写死在代码里,而没有经由 gettext,那么它既不在 .po 也不在 .mo 中,任何 PO 工具都无法从中找到它。这是格式的限制,而不是工具的限制。

RuneTranslate 还会把 msgid 视为原文,这正是 gettext 自身的约定。少数项目让 msgid 保持英文,而真正的源语言放在 msgstr 里;这类项目会从英文开始翻译。

最后,如果游戏运行在 RuneTranslate 直接支持的引擎上 —— Godot、Unity、Ren’Py —— 那就交由该引擎处理,这样游戏其余的文本也能被取到,而不仅仅是语言包。

分享之前

启动导出的版本,读上几屏。菜单和按钮是最快的检查点,因为语言包里的文本大多在那里。如果哪里不对 —— 游戏识别不到、导出的版本打不开,或文本仍是原语言 —— 请通过应用的帮助菜单或 Discord 反馈。下载 RuneTranslate,打开你的第一个语言包。

相关阅读
01

如何将 Godot 游戏翻译成英语

godotenginetutorial2026年6月17日 · 6 分钟
阅读 →
02

如何将 Ren'Py 游戏翻译成英语(完整教程)

renpytutorialengine2026年6月9日 · 6 分钟
阅读 →
03

如何将 Kirikiri / KAG 视觉小说翻译成英语

kirikiritutorialengine2026年6月9日 · 6 分钟
阅读 →

准备好试用 RuneTranslate 了吗?

免费版即可解锁全部引擎 + 全部翻译服务商。支持者($3.99/mo)解锁全速翻译。

下载 Windows 版