Unreal Engine ゲームを翻訳する方法
Unreal のゲームは、テキストを .pak や IoStore コンテナーの中のコンパイル済み .locres テーブルに収めて出荷します。その読み方と、なぜ出力がゲームを丸ごと作り直したものではなく小さなオーバーライド pak になるのかを解説します。
Unreal Engine のゲームは、ドライブの中で最も翻訳が難しそうに見えます。data フォルダもなく、スクリプトファイルもなく、開ける .json もありません — あるのは実行ファイルと、1 つか 2 つの巨大な .pak ファイルが入った Content/Paks ディレクトリ、それに .utoc / .ucas のコンテナーが一組あるかどうか、という程度です。良い知らせは、本当に欲しいテキストは設計上そこに隠されているわけではない、ということです。Unreal には一級のローカライズシステムがあり、出荷されるゲームの多くはそれを使っています。やるべきことは、そのテーブルを見つけ、文字列を差し替え、元のものより優先される小さなファイルをエンジンに渡すことです。
その最後の部分こそ、仕組みが分かってしまえば Unreal が扱いやすいエンジンである理由です。60 GB のゲームを作り直す必要はありません。キロバイト単位のオーバーライドアーカイブを作って、元のファイルの隣に置くだけです。
手順に入る前に 1 つ:RuneTranslate の Unreal 対応は ベストエフォート で、RPG Maker や Ren’Py といったエンジンより新しいものです。実在の UE4 タイトルでプレイ可能なことは確認済みですが、フォーマットの面は非常に広く、スタジオごとにパッケージ化の仕方が違います。長い翻訳作業に取りかかる前に、エクスポートしたビルドをゲーム内で検証してください。
Unreal のゲームが実際に出荷しているもの
Unreal はプレイヤーに見えるテキストを `.locres` ファイル — FTextLocalizationResource のコンパイル済み形式 — に保持します。カルチャごとに 1 つあり、<Game>/Content/Localization/Game/ja/Game.locres のようなパスに置かれ、en や zh-Hans、そのスタジオが出荷したものが兄弟として並びます。翻訳をまったく持たないゲームでも、通常は制作時の言語のカルチャ用に 1 つは持っています。
.locres はテキストファイルではなく、単純なキー / 値のリストでもありません。ネームスペース として構成されたバイナリのテーブルで、各ネームスペースが キー を持ち、各キーが文字列を指しています。現代のバージョンでは、ネームスペースとキーは読める名前ではなく ハッシュ として保存され(バージョン 2 では CRC32、バージョン 3 では UTF-16 に対する CityHash64)、文字列自体は参照カウント付きで重複排除された共有ルックアップテーブルに入っています。ランタイムが文字列を英語のテキストで引くことはありません — ビルド時にゲームの Blueprint と C++ にコンパイルされた、そのネームスペース / キーの組で引きます。
この構造が、.locres を安全に編集する唯一の方法を決めています。RuneTranslate はすべてのネームスペースハッシュ、キーハッシュ、ソース文字列ハッシュを 1 バイトも変えずに 保持し、値だけを変更したうえで、重複排除された文字列テーブルとその参照カウントを再構築します。ハッシュを再計算する処理はどこにもないので、ゲームが見つけられないキーが生まれることもありません。翻訳が元より長くても短くても構いません。
この .locres ファイルは、.pak アーカイブ — ファイル末尾のフッターにあるマジック値 0x5A6F12E1 で識別されます — に、あるいは現代の UE5 タイトルでは IoStore の .utoc / .ucas コンテナーにパックされます。
1 時間を費やす前に対応範囲を確認する
.locres がカバーするのは UI、メニュー、システムメッセージ、チュートリアルテキスト、アイテム名とスキル名、字幕 — 一般的なゲームで目に見えるテキストのおおよそ 90% です。RuneTranslate が読み書きするのはこの部分です。
カバーしないのは、DataTable にコンパイルされたテキストや、クック済みの `.uasset` パッケージに焼き込まれたテキストです。一部のスタジオ — 特に小規模なプロジェクトや Blueprint 中心のプロジェクト — は、ローカライズシステムではなくそちらにセリフを置きます。それらの文字列は、読めるディレクトリインデックスを持たないクック済みパッケージの中でハッシュによって参照されており、正しく読むには、出荷されたゲームには含まれていない .usmap マッピングファイルが必要です。ゲームがスクリプトをそこに置いている場合、抽出結果はメニューとボタンだけになり、セリフは 1 つも出てきません。RuneTranslate はそれを、成功したふりをするのではなく、明示的に範囲外という結果として報告します。
早めに知っておくとよいこと:最も速い確認方法は、プロジェクト作成のステップを実行して、何が出てきたかを見ることです。Continue、Options、Are you sure? のような短い文字列が数百個だけなら、UI はローカライズされていてスクリプトはされていません。完全な文を含む数千の文字列が出てくれば、見込みは良好です。
AES キーと、それが壁ではない理由
出荷される pak の相当な割合が AES-256 で暗号化 されています — 具体的には IV なしの ECB モードで、アーカイブのインデックスに、そして多くの場合は個々のエントリーにも適用されます。キーがなければアーカイブは不透明で、中に何が入っているかを一覧することすらできません。
キーが復元できるのには、はっきりした理由があります。ゲームは実行時に、ネットワークもライセンスサーバーもないマシンで自分のアーカイブを読まなければならないので、キーは通常のデータとして出荷バイナリの中に存在します。RuneTranslate は実行ファイルとその DLL を 32 バイトの窓で走査し、エントロピーのゲートで候補を絞り込み(本物の AES キーは 32 バイト中に少なくとも 25 の異なるバイトを持ちます)、インデックスの先頭ブロックを復号して先頭の値がもっともらしい文字列長として読めるかどうかで安価な健全性チェックを行い、そのうえで残った候補をきちんと 検証 します:インデックス全体を復号し、その SHA-1 を pak 自身のフッターが記録しているハッシュと比較するのです。この最後の手順が、世に出回っているエントロピー順位付け型のキー探索ツールとの違いです — 候補はアーカイブ自身が記録したハッシュを再現するか、拒否されるかのどちらかです。誤ったキーが受け入れられることはありえません。
キーを生のバイト列ではなくテキストとして保持するゲームもあれば、バイナリ内の別々の命令に分割して持つゲームもあります。どちらのケースも扱えます。それでもうまくいかない場合、新規プロジェクトのダイアログは 16 進(0x… または 64 文字の 16 進)か base64 で貼り付けたキーを受け取ります。一度復元したキーはプロジェクトごとにキャッシュされるので、エクスポートのたびに 85 MB の実行ファイルを走査し直すことはありません。
UE5 の IoStore は別物で、抽出は絞り込まれる
UE5 タイトルは、素の pak ではなく IoStore コンテナーを出荷することが増えています:.utoc のディレクトリと .ucas のデータブロブの組で、Oodle 圧縮と独自の暗号化を使います。ここで素朴に全体を展開するというのは、105 GB のゲームではそもそも方法として成立しません。
RuneTranslate は retoc(MIT ライセンス)を同梱し、コンテナーをレガシーの pak に変換するよう指示します。ただし ローカライズファイルだけに絞り込んだ 形で — to-legacy -f locres --no-shaders --no-script-objects に相当します。105 GB の IoStore タイトルが数キロバイトの .locres にまで絞り込まれ、しかも数時間ではなく数秒で終わります。retoc はコンテナー自身の AES と Oodle も扱うので、ルーズな pak が引っかかりうる Oodle の問題を IoStore のゲームは回避できます。
ここで重要な半分は、IoStore が複雑にするのは 読み取り だけだということです。書き込みは共通で、それが次のセクションです。
出力は作り直したゲームではなくオーバーライド pak
ここは競合するどのガイドもきちんと説明していない部分で、Unreal の翻訳がいったん動き出せば安上がりである理由でもあります。
Unreal は pak ファイルを決まった順序でマウントし、名前が `_P` で終わるアーカイブは 最後に マウントされます。後からマウントされたほうが勝ちます。つまり、修正した .locres だけを、ベースゲームと同じ仮想パスに入れた小さなアーカイブが、元のものを単純に覆い隠します — ローカライズマネージャーはあなたのものを読み込み、出荷されたほうを見ることはありません。
- 再署名は不要。 pak の署名はまったく別の RSA の仕組みで、消費者向けの出荷タイトルで強制されることはまれです。オーバーライドはそこに触りません。
- 出力に AES キーは不要。 オーバーライドは暗号化なし、圧縮なしで書き出されます。キーが必要だったのは、元のファイルを 読む ときだけです。
- ゲームの再パックは不要。 ベースのアーカイブは手つかずです。60 GB のインストールは 60 GB のまま、あなたの翻訳はキロバイト単位のファイルです。
- IoStore のゲームでも機能します。
.locresは Zen パッケージローダーではなく pak ファイルシステム経由で読まれるので、アセットがすべて.utoc/.ucasにある UE5 のゲームでも、ルーズに置いた_P.pakが拾われます。
インストールは、生成された _P.pak を <Game>/Content/Paks/ — 元のファイルがあるのと同じフォルダ — にコピーするだけです。~mods/ サブフォルダをマウントするゲームもあり、そちらでも同じように機能します。翻訳を外すのはそのファイルを 1 つ削除するだけなので、元に戻すのは簡単で、早い段階から安全に試せます。
手順
- ダウンロードページから RuneTranslate をインストールしてサインインします。初回起動時に無料の Patreon サインインが必要です。無料ティアはすべてのエンジンとすべてのプロバイダーを解放していて、スループットをスロットルし、同時に保持できるプロジェクトを 1 つに制限します。
- 新規プロジェクトを作り、ゲームのルートフォルダ — 実行ファイルと
<Game>ディレクトリがあるフォルダ — に向けます。検出はContent/Paksと pak のフッターマジック、あるいは.utoc/.ucasを探します。 - アーカイブが暗号化されている場合は、キーの復元を実行させます。出荷バイナリを走査し、各候補を pak 自身のインデックスハッシュと照合して検証します。何も見つからなければ、ダイアログにキーを貼り付けてください。
- 抽出が終わるのを待ちます。ソースカルチャの
.locresエントリーが、ローカライズターゲットごとにグループ化されて、レビューできる状態で並びます。 - プロバイダーを選んで翻訳します。短い UI 文字列と長いセリフを別のプロバイダーに振り分ければ、お金をかける場所を絞れます。
- 返ってきたものをレビューします。機械翻訳が最も弱いのはメニューの文字列です — 裸の
SaveやLoadには文脈がなく、2 語のボタンが文になって返ってくればウィジェットからはみ出します。 - エクスポートします。RuneTranslate は、修正した
.locresを正しいエンジン相対パスに収めた、暗号化なしの_P.pakを 1 つ書き出します。 - そのファイルを
<Game>/Content/Paks/にコピーして、ゲームを起動します。
ゲーム全体をプロバイダーに通す前に、いくつかの文字列だけを翻訳した状態で手順 8 まで試してください。オーバーライドが無視される状況 — フォルダが違う、カルチャが違う、マウント構成が特殊 — なら、9,000 文字列分を支払った後ではなく 5 分で気づきたいはずです。
プレースホルダーとリッチテキスト
Unreal の FText の書式指定は、位置引数に {0}、{1}、名前付き引数に {PlayerName}、{Count} を使い、加えてインラインの装飾に <RichText> マークアップと </> の閉じタグを使います。翻訳プロバイダーがそれらを落としたり壊したりすると、ゲームに出るのは少し変な文ではなく、壊れた書式文字列か、引数が黙って行から消えた文です。
RuneTranslate は、プロバイダーが文字列を見る前にそれらすべてを中立的なプレースホルダーにマスクし、後で復元します。プロバイダーはマーカーを通り抜けるのではなくその周りを翻訳するので、波括弧が打ち間違いに見えたと判断したモデルによって引数が削除されることはありません。さらに踏み込んで特定の文字列を翻訳対象から完全に外したい場合、正規表現の除外フィルターはどのエンジンでも使えますし、用語集はゲーム全体で固有名詞の表記を安定させます。
アクセント記号の罠 — フォントではなく文字コードのバグ
これは症状が正反対の方向を指すので、単独で書き残しておく価値があります。Unreal は文字列を長さのプレフィックス付きでシリアライズします:正の長さは 1 文字 1 バイト、負の長さは UTF-16 を意味します。エンジンが狭いほうの形式を選ぶのは すべての文字が 7 ビット ASCII のとき だけで、そのチェックは 0x7F を超えるものを弾きます。ローダーも同じ作りなので、上位バイトを含む狭い形式の文字列を渡されると、その 1 バイトごとにリテラルの ? に置き換えます。
実際に何が起きるか:狭い形式で書かれた poción mágica を含む翻訳が、プレイヤーには poci?n m?gica として届きます。影響を受ける帯域は U+0080〜U+00FF で、これはちょうどスペイン語、フランス語、ドイツ語、ポルトガル語、イタリア語、北欧諸語のアクセント付き文字です。ポーランド語、トルコ語、チェコ語、ギリシャ語、キリル文字、日本語、中国語はすべて U+00FF より上にあるので自動的に広いほうの分岐に入り、影響を受けることはありません。
つまり見分け方は スペイン語ではアクセントが壊れるのにロシア語は問題ない です。これが見えたら、そのファイルを書いた側の文字コードのバグであって、ゲームのフォントにグリフがないわけではありません — グリフが欠けているときは四角や空白になり、疑問符にはなりません。RuneTranslate にもまさにこのバグがあり、0.49.4 で修正しました。エンコーダーは 1 か所にまとまり、ASCII の外側は広い形式で書き出します。この分野の他のツールは今もこれを間違えているので、知っておく価値があります。
プロバイダーの選び方
9 つのプロバイダーのうち 3 つは API キーがまったく不要で — Google、無料の DeepL、そして DeepL の Classic / Next-gen モデルです — 無料ティアでそのすべてが使えます。UI が大半を占めるゲームなら、無料のプロバイダーで実際に十分です。セリフの多いタイトルでは、LLM のプロバイダーのほうが文脈をはるかにうまく扱います。OpenAI、Anthropic、DeepSeek、あらゆる OpenAI 互換エンドポイント、そして Ollama / LM Studio 経由のローカルモデルが利用できます。プロバイダー比較では、それぞれの得意と不得意を実際のコストの数字とともに解説しています。
トラブルシューティング
ゲームは動くが何も翻訳されていない
ほぼ必ず次の 3 つのどれかです。ファイルが <Game>/Content/Paks/ に入っていない — 実行ファイルの隣に置いても何も起きません。ファイル名から _P のサフィックスが落ちている — その場合は通常のアルファベット順でマウントされ、ベースの pak が勝つことがあります。あるいは、翻訳していないカルチャでゲームが動いている場合です:ゲームは起動時に解決したカルチャの .locres を読み込むので、en を強制するゲームは ja 向けにだけ書かれたオーバーライドを無視します。まずはゲームの言語設定を確認してください。
Oodle 圧縮のルーズな pak で抽出が失敗する
Oodle で圧縮されたルーズな .pak には、同梱できない伸長処理が必要です。そのゲームが IoStore コンテナーも出荷しているなら、retoc の経路が Oodle を扱うので問題ありません。ルーズな pak だけの場合、これは現時点で行き止まりで、黙って空の結果を返すのではなく明示的に報告されます。
ゲームのどこにも .locres がない
その場合、テキストはクック済みアセットにコンパイルされていて、この経路では届きません。最も分かりやすい例は、IoStore のインデックスがファイル名を一切持たないコンテンツハッシュにまで削られた、大規模なライブサービス型の UE5 タイトルです — 数十万のチャンクがあって .locres はゼロです。RuneTranslate はコンテナー全体を延々と処理する代わりに、そう報告します。
検出がまったく反応しない
Content/Paks 自体でもなく、他のゲームが詰まった親フォルダでもなく、実行ファイルがあるフォルダに向けてください。それでも検出しない場合はパッケージ化が特殊なので、報告する価値があります — pak のフォーマットはバージョンごとにフッターの構成が違い、まだ誰も見ていない方言はまさに追加されていくたぐいのものです。
率直な制限
Unreal 対応はベストエフォートです。ルーズな pak と暗号化された pak の中、そして UE5 の IoStore の中の .locres が実証済みの経路で、UE4 タイトルのスペイン語でゲーム内のプレイ可能性を確認しています。DataTable とクック済み .uasset のテキストは対象外です。IoStore の対応物を持たない Oodle 圧縮のルーズな pak には対応していません。署名が強制されている pak はオーバーライドできませんが、実際にはまれです。そしてフォーマットはエンジンバージョンとスタジオによって大きく変わるので、最初のエクスポートは成果物ではなくテストとして扱ってください。
最終的に手に入るのは、自分のマシンに置いておけるプレイ可能な翻訳済みビルドです:元のゲームは手つかずのまま、削除すればすべてを元に戻せる小さなファイルが 1 つ増えるだけです。エンジンを比較したい、あるいは自分のゲームがそもそも対象なのかを確認したい場合は、Unreal エンジンページに技術的な詳細が、エンジン一覧に Unity や RPG Maker を含む対応 17 フォーマットが載っています。この作業自体が初めてなら、汎用の翻訳ガイドから始めて、Unreal 固有の部分についてここに戻ってきてください。
RuneTranslate を試してみませんか?
無料プランですべてのエンジンとすべての翻訳プロバイダーが使えます。Supporter(月額 $3)ならフルスピードで翻訳できます。
Windows 版をダウンロード
