How to translate a game’s .po and .mo files (gettext)
The .po is the editable source; the .mo is the compiled catalog the game actually loads — which is why editing the .po alone changes nothing. RuneTranslate reads both, protects %s and {count} placeholders, refits plural forms to your language, and writes a working .mo back.
ゲームのフォルダーを開いて .po と .mo ファイルが並んだ locale ディレクトリを見つけたなら、そのゲームは gettext でローカライズされています。RuneTranslate はそうしたゲームを、プロジェクト作成からプレイできるビルドの書き出しまで一通り翻訳できるようになりました。
この記事では、その 2 つのファイルが実際に何なのか、なぜ読める方のファイルを編集してもゲーム内では何も変わらないことが多いのか、そしてどう正しく翻訳するのかを説明します。
.po と .mo — 誰もがつまずく違い
この 2 つは同じカタログの 2 つの形です。
- `.po` — Portable Object。どのエディターでも開けるプレーンテキストです。各エントリーは
msgid(元の文字列)とmsgstr(その訳)を持ちます。翻訳者が作業するのはこのファイルです。 - `.mo` — Machine Object。同じ内容をゲームが即座に引けるように配置した、コンパイル済みのバイナリ版です。実行中のゲームが読むのはこちらです。
最後の点が、多くの人の午後を奪います。.po の全行を訳して保存し、ゲームを起動しても、何も変わらないことがあります。ゲームは .po を開かないからです。読み込むのは隣にある .mo で、そこには古い文字列が残っています。本来は GNU gettext の msgfmt で一方をもう一方にコンパイルします。RuneTranslate は両方を書き出すので、別途コンパイルする手順は要りません。
gettext のゲームフォルダーの見た目
標準的な構成は、言語コードをキーにした locale ディレクトリです。
MyGame/game.exeMyGame/locale/en/LC_MESSAGES/mygame.moMyGame/locale/fr/LC_MESSAGES/mygame.moMyGame/locale/de/LC_MESSAGES/mygame.mo
LC_MESSAGES フォルダーは間違いではなく約束事の一部で、mygame は テキストドメイン — ゲームが文字列を読み込むときに指定する名前です。多くのゲームは代わりに lang/fr.po のような、より平たい構成を使います。それでも動作します。
どんなゲームが gettext を使うのか
gettext は Unix の世界で生まれ、C や Python、GTK ベースのソフトウェアをローカライズする既定の方法になりました。ゲームでは次のようなものが該当します。
- Python と pygame のゲーム
- Solarus、SDL、その他の C/C++ 系エンジン
- いずれも PO を標準で扱う Poedit、Weblate、Crowdin を通してローカライズされたゲーム
- PO を翻訳ソースとして利用できる一部の Godot プロジェクト
日本のゲームより欧米のインディーゲームではるかに一般的です。Kirikiri や RPG Maker といった日本製エンジンは、テキストを独自の形式で保持しています。
RuneTranslate で gettext のゲームを翻訳する
- RuneTranslate にゲームフォルダーを指定します。
locale/、lang/、i18n/のいずれかを含むフォルダーです。検出は拡張子を信用せず実際のバイト列を読むため、中身が 3D シーンの.moは壊れたプロジェクトを作らずに無視されます。 - 要約を確認します。 プロジェクトを作成する前に、カタログ数、コンパイル済み
.moとソース.poの内訳、ゲームが既に同梱している言語、文字列の総数が表示されます。 - 言語と翻訳サービスを選びます。 無料の Google または DeepL、あるいは自分の DeepL・OpenAI・Claude・DeepSeek のキー。サービス比較もご覧ください。
- 確認してから書き出します。 翻訳が適用されたゲームの完全なコピーが得られます。
gettext の翻訳を静かに壊す 2 つのもの
プレースホルダー。 msgid は完成した文章ではなく書式文字列です。Hello, %s! では実行時にプレイヤー名が差し込まれます。機械翻訳は %s を削ったり、間に空白を入れたり、Python の名前付き形式 %(player)s では中の単語を訳してしまいがちです。影響は見た目だけではありません。%s が 1 つ欠けると以降の引数がすべてずれ、多くのエンジンはそのまま落ちます。RuneTranslate は見つけたプレースホルダー(%s、%d、%(player)s、{count}、{0})をサービスに送る前にマスクし、翻訳後に復元します。
複数形。 英語は 2 つ(「1 coin」「5 coins」)、日本語とトルコ語は 1 つ、ロシア語とポーランド語は 3 つです。カタログはヘッダーでいくつ使うかを宣言しており、その数が言語と合っていないと gettext はリストの末尾を越えて参照し、黙って未翻訳の原文に戻ります。RuneTranslate は対象言語に合わせて形を調整し、ヘッダーも書き換えます。
書き出しがカタログを 2 回書く理由
書き出しでは、正しい新しい言語(locale/<選んだ言語>/LC_MESSAGES/)と、ゲームがすでに読み込んでいたカタログの更新版の両方が作られます。
翻訳を実際に表示させるのは後者です。ゲームがどのロケールを読み込むかは、フォルダーの中身からは分からない方法で決まります。OS の言語を見ているかもしれませんし、設定ファイルやランチャーのフラグ、あるいは単に決め打ちかもしれません。英語しか要求しないゲームに日本語カタログを足しても、まったく変わっていないように見えるビルドを眺めることになります。実際に読み込まれているカタログを上書きすれば、その推測が要らなくなります。どちらも書き出したコピーの中だけで行われ、元のゲームフォルダーは変更されません。
未翻訳の文字列は、ゲームが元から持っていた訳文をそのまま保ちます。作業途中のプロジェクトが開発者のローカライズを消してしまうことはありません。
これができないこと
翻訳できるのは、すでにカタログにある文字列だけです。開発者が gettext を通さずに直接コードへ書いた文字列は .po にも .mo にも存在せず、どんな PO ツールでもそこから見つけることはできません。これはツールではなく形式の限界です。
RuneTranslate は msgid を原文として扱います。gettext 自身がそう定めているからです。一部のプロジェクトでは msgid を英語にし、本来の原語を msgstr に置いています。その場合は英語からの翻訳になります。
最後に、ゲームが RuneTranslate の直接対応するエンジン(Godot、Unity、Ren’Py)で動いている場合は、そのエンジンが処理します。カタログだけでなく、ゲームの残りのテキストにも手が届くようにするためです。
共有する前に
書き出したビルドを起動して、いくつかの画面を読んでみてください。カタログのテキストはほとんどが UI にあるので、メニューとボタンを見るのが一番早い確認になります。うまくいかないとき(検出されない、書き出したものが起動しない、テキストが元の言語のまま)は、アプリのヘルプメニューか Discord からご報告ください。RuneTranslate をダウンロードして、最初のカタログを開いてみましょう。
RuneTranslate を試してみませんか?
無料プランですべてのエンジンとすべての翻訳プロバイダーが使えます。Supporter(月額 $3)ならフルスピードで翻訳できます。
Windows 版をダウンロード