前回は、YAMPTを使ったリバースエンジニアリングと、辞書(Glossary)の作成、そしてClaude Haikuを使ったベース翻訳の作成までをやった。
今回は、いよいよ本丸であるINFO(会話テキスト)の翻訳における「コンテキスト問題」と、その技術的解決について。
まず3,000行くらい翻訳してみて、プロンプトの改善、辞書マスターによる表記ゆれ対応、翻訳精度の向上を確認できたので、本翻訳に移ることにした。
その前に、このINFO.tsvにおける最大の問題点を解決しないといけなかった。

誰が喋っているのかが一切わからない
翻訳自体は進んだが、INFO(会話データ)には致命的な欠点があった。
現状だと、keyによって何が喋ってるか(ガードなのか、ネレヴァリンなのか)は非常にざっくりわかってはいたが、
- 誰が(NPC名)
- どこで(CELL)
- どういう状況で(シチュエーションやクエスト進行度)
喋っているのかがテキストだけでは一切わからない。
そこで、PythonでESMバイナリを直接パースし、コンテキスト情報を抽出することにした。
ESMバイナリ解析
既存のツール(tes3convなど)では必要な情報が欠落するため、Pythonの struct モジュールと construct ライブラリを使ってバイナリを直接解析した。
INFOレコードには DATA や ONAM(話者)といったサブレコードが含まれている。
これらを解析することで、以下の情報を抽出した。
これで「特定NPCのセリフだけ見て翻訳」「ガード汎用だけ口調を統一」みたいな実務ができるようになった。
| 抽出した情報 | 具体例 |
|---|---|
| 話者 (ONAM) | 衛兵 (guard_hlaalu) |
| 場所 (CELL) | バルモラ |
| クエストID | A1_1_FindSpymaster |
- これで「衛兵(男性)がバルモラで喋っている」という情報が取れるようになった。
- これをプロンプトに注入することで、「噂を聞いたか?」を「最近の噂を聞いたか?(衛兵口調)」に訳し分けることに成功した。

性別問題の落とし穴
あとになって気づいた落とし穴があった。
基本的にNPCは、トピックに基づいて話すセリフは固定なのに、男性だったり女性だったりする。
ところが翻訳側で「male」と固定してしまっていて、そのせいで女性のNPCが急に「俺は~」と言い出したりする場面が出てしまった。

ESP生成スクリプトの開発
とりあえず、なんとか翻訳データは揃った!
あとはこれをESP(パッチファイル)として書き出すだけだったが、ここからが本当の地獄だった。
v1: 最初の試み — 完全な失敗
最初はシンプルに、中華版と同じ構造のパッチESPを生成しようとした。
結果: バイナリレコードヘッダーの逆スキャンロジックが壊れており、生成されたESPが構造的エラーを含み、ゲームが読み込めない。
v2: 2バイトの罠
根本的に書き直し、2パス処理に変更した。しかし、またしてもESPが壊れる。
ESPをバイナリダンプして確認すると、恐ろしいことが起きていた。
INAM size=21 のはずが、データが \x00\x003064...原因は、TES3フォーマットのサブレコードサイズを4バイト(Integer)ではなく2バイト(Short)だと誤解していたことだった。
# 間違い
struct.pack('<I', size) # 4バイトで書き込んでいた
# 正解
struct.pack('<H', size) # 2バイトだった!たった2バイトのズレ。しかし、これがファイル全体のアドレスを狂わせていた。
「レコード構造なんて大体一緒だろw」という思い込みが招いた恥ずかしいミスだ。
v3: 辞書キーの不一致という大問題
v2の問題を直し、ようやくESPが生成できた。
しかし、ゲーム内でINFO(対話テキスト)が全部英語のままだった。
なぜだ?

調べてみると、翻訳辞書のキー(INAM値)が中華版ESPのINAMと一致していなかった。
翻訳ツールが使った英語ESMと、実際にゲームで使う中華版ESPでは、同じ対話でもINAM値が異なっていたのだ。影響範囲は約12,543個。

これを解決するために、以下の3段階のマッピングを実装した。
- 英語ESM全体からINAMマッピングを構築
- 中華版ESPとの相互参照で正確な対応関係を復元
- 日本語翻訳辞書を正しいキーで再構築
これでようやく、ゲーム内で日本語テキストが表示された。
「最初のクエストが進まない」
ニューゲームで始めると、最初のシーンでジウブ(NPC)が話しかけてこない。
原因を探ると、スクリプト内のテキストが中国語(GBKエンコード)のままだった。
OpenMWをUTF-8モードで動かしているため、GBKのテキストを読み込んだ瞬間にパースエラーを起こし、スクリプトが停止していた。
スクリプトに含まれる Say や MessageBox コマンド内のテキスト("You're finally awake..."など)を検出し、日本語に翻訳してUTF-8で書き直すことで、ようやくジウブが喋りだした。
おかえり、ジウブ。

コメント