読者です 読者をやめる 読者になる 読者になる

kengo700のダイアリー

誰の役にも立たないと思う情報を発信するブログ

2Dアクションゲーム『Hollow Knight』の日本語化のための試行錯誤メモ

ゲーム 翻訳

2Dアクションゲーム『Hollow Knight』の日本語化のために試行錯誤した内容をメモしておく記事です。 まだ日本語化はできていません。

はじめに

先日から『Hollow Knight』という2Dアクションゲームの有志翻訳に挑戦しています。 どんなゲームかは下記ページなどをご覧ください。

現在は翻訳に取り組む前に日本語化の方法を模索している段階です。 具体的には、ビットマップのフォントデータを作成できれば日本語化できそうなので、フォントデータのフォーマットを解析している途中です。

とりあえず調べたことなどを今後見直すために記録しておきます。

3/4追記:新たに分かったことは別記事に書こうと思っていましたが、分かりにくく面倒くさくなりそうなので、この記事にどんどん追記していこうと思います。

試行錯誤

ゲームデータの確認

Steamのゲームのデータは、デフォルトでは下記の場所にインストールされる。

  • C:\Program Files (x86)\Steam\steamapps\common

Steamクライアントでゲームのプロパティを開き、「ローカルファイルを閲覧…」をクリックすると、ゲームデータの場所をエクスプローラで簡単に開くことができる。

f:id:kengo700:20170304015228p:plain

ちなみにプロパティの「ゲームキャッシュの整合性を確認…」をクリックすると、改変したゲームデータがデフォルトに戻るので、日本語化したものを元に戻せる。

『Hollow Knight』の場合は下図のように「*.assets」というファイルがあるので「Unity」で作られたゲームだということが分かる。

f:id:kengo700:20170304015804p:plain

UnityEXについて

Unityで作成されたゲームのデータは「UnityEX」というソフトでアンパック(展開)することができる(ここら辺は以前『Diluvion』の日本語化方法を調べた時に分かったこと)。

UnityEX - Zone of Games Forum

ダウンロードサイトはロシア語なので、Chromeの翻訳機能を使って内容を確認してダウンロード。

ちなみに「UnityEX」を紹介しているサイトにはどこも「念のためウィルスチェックをしたほうが良い」と書いてあるので、念のためウィルスチェックしておく。

UnityEXはGUI形式でもコマンドラインでも操作できる。

GUI形式の場合の操作は下記の通り。

  • アンパック
    • 「Open archive Unity」で「*.assets」を開く
    • ファイルを右クリックし「Export selected」で特定のファイルをアンパック
    • 「Export all files」で全てのファイルをアンパック
  • リパック
    • 上記手法でファイルをアンパック
    • ファイルを修正または別のファイルで上書き
    • 「Import all files」でリパック

f:id:kengo700:20170304050106p:plain

コマンドラインの場合は下記の通り

  • アンパック(txt形式のみ)
    • UnityEX.exe export resources.assets -t txt
  • リパック(txt形式のみ)
    • UnityEX.exe import resources.assets -t txt

コマンドラインの操作なので、下記のようなバッチファイルを作成してダブルクリックでもOK。

f:id:kengo700:20170304021452p:plain

「resources.assets」だけでなく、フォルダ内の「*.assets」全てからアンパックしたい場合は下記のバッチファイル。

f:id:kengo700:20170304021545p:plain

ファイル形式を限定しない場合は「-t txt」を削除する。

f:id:kengo700:20170304021620p:plain

UnityEXでテキストデータをアンパック

とりあえずテキストデータが欲しいのでtxt形式のファイルだけアンパックしたところ、下記のファイルが取り出せた。

  • _Backer Messages.txt
  • _Elderbug.txt
  • _Quirrel.txt
  • DE_*.txt
  • EN_*.txt
  • ES_*.txt
  • FR_*.txt
  • LineBreaking Following Characters.txt
  • LineBreaking Leading Characters.txt
  • PlayMakerAssemblies.txt
  • Price_Prices.txt
  • Type_Prices.txt
  • Notes_*.txt
  • Word Count_*.txt

どう見ても「DE」「EN」「ES」「FR」から始まるファイルが怪しいので、TeraPadで「EN_MainMenu.txt」を開くとこんな感じ。

f:id:kengo700:20170304022307p:plain

というわけで全部で43個ある「EN_*.txt」にゲームのテキストがまとめられていて、これを取り出すことに成功。

テキストデータがtxt形式で、わかりやすいフォーマットで、種類ごとに別ファイルまとめられているという、Unity製のゲームの中ではかなり多言語対応を考慮して作られているゲームっぽい。

UnityEXで翻訳データをリパック

試しに「EN_MainMenu.txt」と「EN_UI.txt」の一部を翻訳し、リパックしてゲームを起動してみる。ファイルはTeraPadで編集し、「UTF-8N」形式で保存。

f:id:kengo700:20170304022903p:plain

f:id:kengo700:20170304022910p:plain

メインメニューについては下図の通り日本語化できた。

f:id:kengo700:20170304023531p:plain

しかし会話文やアイテムの説明は「□□□□…」と文字化けしてしまう。

f:id:kengo700:20170304023604p:plain

いろいろ調べたところ、文字化けしている部分は「ビットマップフォント」が使われているらしい

フォントの種類について

フォントには「ダイナミックフォント」と「ビットマップフォント」の2種類がある。画像でいう「ベクター形式」と「ラスター形式」の違い。 詳しくは下記など。

ダイナミックフォントのファイルはttf形式かotf形式のファイル。

ビットマップフォントのファイルはfnt&dds形式など。 ビットマップフォントの場合は、画像のファイルと、各文字の座標情報のファイルの2つがセットになっている。

Unityでは、ダイナミックフォントの場合は、日本語フォントが見つからない場合はOSのフォントを探し出して使用してくれるらしい。 なので『Hollow Knight』のメニュ部分は日本語フォントを入れなくても日本語にできたっぽい。

しかし『Hollow Knight』のファイルの中にfnt形式やdds形式のファイルは見当たらず、どれがフォントデータか分からない。 なので中文化MODを探してみる。

中文化MODのデータの確認

下記ページで『Hollow Knight』を検索すると、すでに中文化MODが作成されていた。

f:id:kengo700:20170304024750p:plain

このMODのページをChromeで開こうとすると警告がでるので注意。たぶん下手な部分をクリックすると危険。

f:id:kengo700:20170304024936p:plain

ダウンロードしたファイルを解凍すると当然の如くウィルスチェックに引っかかるので、念のため「VMware」の仮想PC上で作業する。

下記ページを参考に、解凍したフォルダに「hollow_knight.exe」をコピーし「Hollow.Knight.CHS.PATCH.V2.0-ALI213.exe」を実行。

f:id:kengo700:20170304025222p:plain

左下のチェックはマルウェアらしいのでチェックを外し、右上のボタンをクリック。

f:id:kengo700:20170304025325p:plain

すると4種類のファイルが生成される。

f:id:kengo700:20170304025515p:plain

「logo.ress」ファイルは得体が知れないので放置して、残りの「resources.assets」「sharedassets0.assets」「sharedassets1.assets」を仮想PCから現PCへコピー。念のためウィルスチェックをしてからゲームファイルに上書き。

ゲームを起動すると中文化されている。タイトルロゴを改変しているこだわり具合。

f:id:kengo700:20170304025846p:plain

会話文やアイテムの文字も問題なく中文化されている。すげぇ…

f:id:kengo700:20170304025932p:plain

アンパックしたデータの比較

中文化時に改変されたファイルのうち、「sharedassets1.assets」はタイトルロゴのデータらしい(これだけ元に戻したところ、タイトルロゴが元に戻った)。

なので中文化に関わっているのは「resources.assets」「sharedassets0.assets」の2つのファイル。

比較するために、前述の「export_all.bat」で全てのファイルをアンパック。途中で謎のエラーが出るものの、「OK」を押しまくって進める。

中文化版とオリジナル版の内容を「FdateCompare」というソフトで比較。

翻訳データである「EN_*.txt」を除くと、変更されていたデータは下記の通り。

  • resources\ARIAL SDF Atlas.tex
  • resources\Perpetua-SDF Atlas.tex
  • resources\perpetua_tmpro Atlas.tex
  • resources\resources_00001.-2
  • resources\resources_00001.-20
  • resources\resources_00001.-5
  • resources\resources_00002.-5
  • resources\resources_00003.-5
  • resources\resources_00004.-5
  • resources\Textures\Perpetua-SDF Atlas.tex.dds
  • resources\Textures\perpetua_tmpro Atlas.tex.dds
  • resources\Textures\trajan_bold_tmpro Atlas.tex.dds
  • resources\trajan_bold_tmpro Atlas.tex
  • sharedassets0\sharedassets0_00001.-1
  • sharedassets0\Textures\TrajanPro-Bold SDF Atlas.tex.dds
  • sharedassets0\TrajanPro-Bold SDF Atlas.tex

どうやら「Trajan」「Perpetua」「Arial」というフォントが使われているもよう。

ddsは画像データで、UnityEXがtexから変換したもの?

texは「テクスチャ」だと思うが、ググってもあまり情報が出てこない。 残りのファイルがビットマップフォントの座標データだと思われる。

試しに「TrajanPro-Bold SDF Atlas.tex.dds」を「Windows Texture Viewer」で表示してみる。

オリジナル版の「TrajanPro-Bold SDF Atlas.tex.dds」。

f:id:kengo700:20170304034326p:plain

中文化後の「TrajanPro-Bold SDF Atlas.tex.dds」。確かに中国語の文字が並んでいる。

f:id:kengo700:20170304034357p:plain

ビットマップフォントデータのフォーマット確認

そもそもビットマップフォントのフォーマットについてよく知らないので調べてみる。

下記ページによると「Bitmap font generator」というソフトを使うと自分で作れるらしい。

どうやら特定の文字のフォントだけ出力できるっぽい。 ゲームで使っている文字のデータだけフォントにすることで、容量を削減したりするため?

特定の文字をテキストデータから入力できるらしい。しかし右のチェックを入れると特定の文字セットを全部選択できるみたいなので、とりあえず平仮名のデータを出してみる。

f:id:kengo700:20170304034744p:plain

f:id:kengo700:20170304034950p:plain

「Export Options」は「Text」と「Binary」の2種類を試す。

f:id:kengo700:20170304035002p:plain

「Options」の「Save bitmap font as…」から適当な名前でビットマップフォントを作成。 フォントの画像データと座標データのファイルが生成された。

f:id:kengo700:20170304035240p:plain

f:id:kengo700:20170304035336p:plain

f:id:kengo700:20170304035356p:plain

バイナリ形式の座標データを「Stirling」で開いてみる。

するとこんな感じ。

f:id:kengo700:20170304035656p:plain

このフォーマットの詳細は下記ページに書かれている。

バイナリ形式では「info」「common」「pages」「chars」「kerning pairs」5つのブロックに分かれていて、それぞれにデータが書き込まれているらしい。 これを上のデータに当てはめると、こんな感じなはず。

f:id:kengo700:20170304041322p:plain

各ブロックの先頭のバイトがブロックの種類(01~05)、その次の4バイトがブロックの長さを表す。

バイトのデータは下の桁から書かれているらしい。なので「44 07 00 00」は「00000744」。これを10進数に直すと1860になる。

作成したフォントはひらがな93個。1文字に20バイトらしいので、93x20=1860。

文字部分を詳しく見てみるとこんな感じ。

f:id:kengo700:20170304042902p:plain

なのでバイナリ形式で「42 30 00 00 00 00 2B 00 12 00 13 00 02 00 08 00 16 00 00 0F」は、テキスト形式では「char id=12354 x=0 y=43 width=18 height=19 xoffset=2 yoffset=8 xadvance=22 page=0 chnl=15」

「char id」は識別番号のこと。「char id=12354」は「あ」を表す。

フォントデータの解析1

ビットマップフォントの座標データっぽい「sharedassets0_00001.-1」を見てみる。 左がオリジナル版、右が中文化版。

f:id:kengo700:20170304043826p:plain

どちらも「TrafanPro-Bold」という文字があり、「TrajanPro-Bold SDF Atlas」用のデータっぽい。

比較すると00005Eの位置までは一致している。ここまではフォントに関係ないデータ?

これがフォントの座標データだとすると、前節で見たように各文字のデータが繰り返されているはず。

「設定」の「拡張子別設定」から「00」の色を変えてみてたりして眺めると、マークを付けた部分が連番になっていることに気付く。

f:id:kengo700:20170304044418p:plain

「拡張子別設定」から一行を36バイトにして表示してみると、とてもそれっぽい。上がオリジナル版、下が中文化版。

f:id:kengo700:20170304044638p:plain

「0x0020」「0x0021」「0x0022」はそれぞれ「 」「!」「"」を表している。「Basic Latin」の記号部分。

この座標データは前節の「Bitmap font generator」で作成したものとは違っている。フォーマット情報を探しているものの見つかっていない。 もしゲームオリジナルのフォーマットの場合は、地道に各バイトの意味を解析しないといかん。

「Bitmap font generator」のフォーマットに照らし合わせて考えると、id、x、y、width、height、xoffset、yoffset、xadvanceにそれぞれ4バイト、page、chnlにそれぞれ2バイトで合計36バイトではないかと思われる(そうすると値が大きくなりすぎてしまうのが気になるが…)。 ただし最後の4バイトについてはオリジナル版でも中文化版でも「00 00 80 3F」なので、ここは考える必要は無い?

となると、あと分からないのは00005E~0000B3の部分。

フォントデータの解析2

まずは0000B0~0000B3の部分。オリジナル版では「D2 00 00 00」(10進数では210)、中文化版では「3F 09 00 00」(10進数では2367)。 オリジナル版の文字部分は0000B4~001E3Cまでなので180~7740バイトの部分。1文字に36バイト使っているので、(7740-180)/36=210。 中文化版の文字部分は0000B4~014D90までなので180~85392バイトの部分。1文字に36バイト使っているので、(85392-180)/36=2367。 これらのことから、0000B0~0000B3の部分は文字数を表しているのではないかと考えられる。

000064~000065の部分も0000B0~0000B3と同じ値になっている(他のファイルでも確認)。なのでここも文字数を表している? 文字数の情報が2回出てくるのはよく分からんけど、とりあえず先に進める。

次に、00009C~00009Fと0000A0~0000A3の部分。ここは連続して同じ値になっている。オリジナル版では「00 00 00 44」(10進数では1140850688)、中文化版では「00 00 80 45」(10進数では1166016512)。各フォントのddsファイルをWindows Texture Viewerで見ると縦横のサイズが同じで正方形の画像になっている。他に連続して同じ値なっている部分が見当たらないので、00009C~00009Fと0000A0~0000A3の部分は画像の縦横のサイズを表していると思われる。しかし10進数にするとサイズがおかしい。

そこでfloat型だと考えて変換すると、「00 00 00 44」は「512」、「00 00 80 45」は「4096」になる。オリジナル版の画像は512x512、中文化版では4096x4096なので、00009C~00009Fと0000A0~0000A3の部分は画像のサイズを表していると考えられる。

f:id:kengo700:20170304191651p:plain

f:id:kengo700:20170304191715p:plain

00005E~0000B3の部分のうち、分かった部分をまとめると下記の通り。オリジナル版と中文化版で変わっていない部分は灰色にしている。あと分からないのは5箇所。

f:id:kengo700:20170304192220p:plain

Bitmap Font Generatorのフォーマットを参考にすると、他にありそうなデータは「size」「lineHeight」「base」くらいか。

sizeはフォントの大きさ、lineHeightは複数行に渡るときの行の高さ?、Baseは行の上端から文字のbaseまでの距離?。

フォントデータの解析3

次は00005C~00005Fの部分。値は下記の通り。

  • 中文化版(全ファイル共通)
    • 00 00 F0 41(float、30)
  • オリジナル版(sharedassets0_00001.-1、TrajanPro-Bold SDF)
    • 00 00 0C 42(float、35)
  • オリジナル版(resources_00001.-5、Perpetua-SDF)
    • 00 00 40 42(float、48)
  • オリジナル版(resources_00002.-5、perpetua_tmpro)
    • 00 00 A6 42(float、83)
  • オリジナル版(resources_00003.-5、trajan_bold_tmpro)
    • 00 00 78 42(float、62)
  • オリジナル版(resources_00004.-5、Arial)
    • 00 00 AA 42(float、85)

これは「size」っぽい?

他の4箇所の値もまとめてチェックする。数では左側がオリジナル版、右側が中文化版。

f:id:kengo700:20170305150913p:plain

sharedassets0_00001.-1の00005C~0000B3の部分には分からない数値が5箇所ある。この00005Cを基準の1バイト目とすると、分からない部分は1~4、13~16、21~24、29~32、37~40バイト目の部分(他にも意味が分かっていない部分があるが、オリジナル版と中文化版で値が変わっていないので、とりあえず無視して進める)。

一覧にすると下記の通り。21~24と37~40が一致していることぐらいしか分からない。

1~4 13~16 21~24 29~32 37~40
中文化版 00 00 F0 41 00 00 F0 41 00 00 F0 41 00 00 00 00 00 00 F0 41
オリ版、1.-1 00 00 0C 42 00 00 28 42 00 00 D2 41 00 00 0C C1 00 00 D2 41
オリ版、1.-5 00 00 40 42 00 00 5C 42 00 80 1D 42 00 00 7A C1 00 80 1D 42
オリ版、2.-5 00 00 A6 42 00 40 BE 42 00 20 88 42 00 80 D8 C1 00 20 88 42
オリ版、3.-5 00 00 78 42 00 C0 94 42 00 00 3A 42 00 00 78 C1 00 00 3A 42
オリ版、4.-5 00 00 AA 42 00 80 C3 42 00 E0 99 42 00 00 90 C1 00 E0 99 42

これをfloat型で変換すると下記の通り。

1~4 13~16 21~24 29~32 37~40
中文化版 30 30 30 0 30
オリ版、1.-1 35 42 26.25 -8.75 26.25
オリ版、1.-5 48 55 39.375 -15.625 39.375
オリ版、2.-5 83 95.125 68.0625 -27.0625 68.063
オリ版、3.-5 62 74.375 46.5 -15.5 46.5
オリ版、4.-5 85 97.75 76.9375 -18 76.938

これは合ってるかどうか微妙な値だ… 1~4がsize、13~16がlineHeight、21~24と37~40がbaseではないかと思われるが、よく分からない。まあ中文化版ではシンプルな値になっているので、これを参考に作成して、問題が出たら修正すればいいようにも思う。

フォントデータの解析4

上記のフォントデータを改変してゲームにどう反映されるかを確認してみる。

改変前のスクショは下記。

f:id:kengo700:20170305154526p:plain

まずはresources_00002.-5の000054~000057(sizeだと思われる部分)を00 00 A6 42(float、83)から00 00 20 42(float、40)に変更してリパックしてゲームを起動してみる。

f:id:kengo700:20170305154540p:plain

文字が大きくなった。83から40に小さくしたことで文字が逆に大きくなる理屈は考えるのがめんどいのでスルーして、とりあえずこの部分がsizeっぽいのは分かった。

次に上記の変更は元に戻して、resources_00002.-5の000060~000063(lineHeightだと思われる部分)を00 40 BE 42(float、95.125)から00 00 16 43(float、150)に変更してリパックしてゲームを起動してみる。

f:id:kengo700:20170305155226p:plain

行間が広まったので、やっぱりこの部分がlineHeightっぽい。

次に上記の変更は元に戻して、resources_00002.-5の000068~00006Bと000078~00007B(baseだと思われる部分)を00 20 88 42(float、68.0625)から00 00 F0 42(float、120)に変更してリパックしてゲームを起動してみる(とりあえず二箇所両方変える)。

f:id:kengo700:20170305160652p:plain

違いが分かりにくいが全体が下に移動している。やっぱりこの部分がbaseっぽい。二箇所の違いはとりあえず後回しで。

次に上記の変更は元に戻して、resources_00002.-5の000070~000073(謎の部分)を00 80 D8 C1(float、-27.0625)から00 00 F0 C2(float、-120)に変更してリパックしてゲームを起動してみる

f:id:kengo700:20170305161234p:plain

変化は見られない。とりあえずここはスルーして先に進める。

フォントデータの解析5

文字の座標部分について確認してみる。

「perpetua_tmpro Atlas.tex.dds」(UnityEXでtexをddsに変換したもの)をWindows Texture Viewerで開き、形が分かりやすい「I」の左上と右下のだいたいの座標をチェックすると下図の通り。

f:id:kengo700:20170305175532p:plain

このフォントの座標データを「resources_00002.-5」から探すと、恐らく下図の部分。

f:id:kengo700:20170305175727p:plain

この数値を抜き出し、float型で変換すると下記の通り。

  • 元データ:49 00 00 00, 00 00 C1 43, 00 00 28 42, 00 80 88 41, 00 40 3E 42, 00 00 10 40, 00 40 3E 42, 00 80 AC 41, 00 00, 80 3F
  • float型:73, 388, 42, 17.0625, 47.5625, 2.25, 47.5625, 21.5625, 0, 1

73は識別記号で「LATIN CAPITAL LETTER I」を表す。

それ以降の数字は、id、x、y、width、height、xoffset、yoffset、xadvance、page、chnlと考えるとそれっぽい。まあ正直xoffsetとかxadvanceとかの意味はよく分かってないので、とりあえずそうだとしてフォントファイルを作成してみて、問題があればまた考えよう。

日本語フォントの作成テスト

とりあえず「あ」と「い」の2文字だけのフォントを作ってみる。

f:id:kengo700:20170305192738p:plain

f:id:kengo700:20170305192808p:plain

出力オプションのBit depthを8にしたら表示されなかったので32にしている。

f:id:kengo700:20170305192833p:plain

出力すると2つのファイルが生成される。

f:id:kengo700:20170305192917p:plain

ddsの方は「perpetua_tmpro Atlas.tex.dds」にリネームしておく。fntの方を開くと下図の通り。

f:id:kengo700:20170305193049p:plain

この情報を元に手動で座標データファイルを作成する。「resources_00002.-5」を開き、前節までの解析結果をもとに下図のように編集してみた。

f:id:kengo700:20170305193338p:plain

そして「EN_Elderbug.txt」のセリフを下記のように編集。

f:id:kengo700:20170305193435p:plain

用意したファイルを下記のようにフォルダに入れる。

  • resources.assets(オリジナルのファイル)
  • sharedassets0.assets(オリジナルのファイル)
  • UnityEX.exe
  • Unity_Assets_Files
    • resources
      • resources_00001.-2(中文化版から抽出したファイル)
      • resources_00001.-5(中文化版から抽出したファイル)
      • resources_00002.-5(上記の手動で作成したやつ)
      • resources_00003.-5(中文化版から抽出したファイル)
      • resources_00004.-5(中文化版から抽出したファイル)
      • resources_00001.-20(中文化版から抽出したファイル)
      • EN_*.txt(オリジナルのファイル)
      • EN_Elderbug.txt(上記のセリフを一部変えたやつ)
      • Textures
        • perpetua_tmpro Atlas.tex.dds(上記の作成したやつ)
        • Perpetua-SDF Atlas.tex.dds(中文化版から抽出したファイル)
        • trajan_bold_tmpro Atlas.tex.dds(中文化版から抽出したファイル)
        • res_logo
          • ARIAL SDF Atlas.tex.dds(中文化版から抽出したファイル)
    • sharedassets0
      • sharedassets0_00001.-1
      • Textures
        • TrajanPro-Bold SDF Atlas.tex.dds(中文化版から抽出したファイル)

UnityEXを起動し、「resources.assets」と「sharedassets0.assets」をそれぞれ開いて「Import all files」を実行。 リパックされた「resources.assets」と「sharedassets0.assets」をゲームのフォルダにコピー。ゲームを起動。

f:id:kengo700:20170305194320p:plain

なんかずれとる…

文字を変えてみると、いろいろと設定があっていないっぽい。「Padding」や「Spacing」とかの部分かな?

f:id:kengo700:20170305195325p:plain

フォントデータの解析6

今のところ分かっている情報は下記の通り。

f:id:kengo700:20170305201449p:plain

この灰色に塗ってある、オリジナル版と中文化版で変化が無いので無視していた部分が、作成した日本語フォントの設定と合っていない可能性がある。

一部抜き出すと、下記の通り。

  • 00 00 80 3F, ~, 00 00 D7 41, ~
  • 00 00 60 C0, 00 00 00 3F, 00 00 60 C0, 00 00 E0 3F, 00 00 D2 42, 00 00 A0 40

float型に直すと、下記の通り。

  • 1, 26.875
  • -3.5, 0.5, -3.5, 1.75, 105, 5

…よく分からん。

日本語フォントの作成テスト2

「Padding」や「Spacing」はよく分からなかったので、等間隔のフォント画像を作ることにする。 「Equialize the cell heights」にチェック。加えてSpacgingを10にして文字を離してみる。

f:id:kengo700:20170305202940p:plain

f:id:kengo700:20170305203040p:plain

生成したfntファイルは下記。

f:id:kengo700:20170305203115p:plain

これを元に座標データを修正。

f:id:kengo700:20170305203210p:plain

リパックしてゲームを起動。

f:id:kengo700:20170305202837p:plain

ちゃんと表示された。

英語部分とずれている理由は不明。そこらへんはlineHeightやbaseの値をいじれば調整できるかな?

そもそも「あ」と「い」だけのフォントなのにアルファベットも表示されているのは謎。あるビットマップフォントで見つからない文字は、他のビットマップフォントから持ってくるような機能がUnityにあるのかもしれない。

フォントの座標データ変換プログラムの作成

前節まで手動で行っていた座標データの修正を自動的に行う。

とりあえずバイナリ関連のプログラミングを調べる。

float型の数字を出力してみる。

using System;
using System.IO;

namespace FontConverterForHollowKnight
{
    class Program
    {
        static void Main(string[] args)
        {

            float x = 25;
            byte[] data = BitConverter.GetBytes(x);
            File.WriteAllBytes("testnew.bin", data);
            
        }
    }
}

f:id:kengo700:20170305214938p:plain

複数の情報を出力する。

            float x = 10;
            float y = 11;
            float width = 12;
            float height = 13;

            FileStream fs = null;

            // ファイルを新規作成(既に存在する場合は上書き)
            fs = new FileStream(@"testnew.bin", FileMode.Create);

            // バイナリデータの書き込み
            byte[] data_x = BitConverter.GetBytes(x);
            fs.Write(data_x, 0, data_x.Length);

            byte[] data_y = BitConverter.GetBytes(y);
            fs.Write(data_y, 0, data_y.Length);

            // バイナリデータの書き込み
            byte[] data_width = BitConverter.GetBytes(width);
            fs.Write(data_width, 0, data_width.Length);

            byte[] data_height = BitConverter.GetBytes(height);
            fs.Write(data_height, 0, data_height.Length);

            fs.Close();

f:id:kengo700:20170305221507p:plain

XML形式のファイルの読み込みを復習。

検証のためにひらがなだけのビットマップフォントを作成した。fntファイルの内容は下記の通り。

f:id:kengo700:20170305224929p:plain

とりあえず座標データ部分だけを読み込んでみる。

            XmlDocument xmlDocument = new XmlDocument();
            xmlDocument.Load("hiragana.fnt");
            
            XmlNodeList nodeList = xmlDocument.SelectNodes(@"/font/chars");

            foreach (XmlNode node in nodeList)
            {

                XmlElement element = (XmlElement)node;
                XmlAttribute attr_count = element.GetAttributeNode("count");
                Console.WriteLine(attr_count.InnerText);

                for (int i = 0; i < node.ChildNodes.Count; i++)
                {
                    XmlElement element_char = (XmlElement)node.ChildNodes[i];

                    XmlAttribute attr_id = element_char.GetAttributeNode("id");
                    XmlAttribute attr_x = element_char.GetAttributeNode("x");
                    XmlAttribute attr_y = element_char.GetAttributeNode("y");
                    XmlAttribute attr_width = element_char.GetAttributeNode("width");
                    XmlAttribute attr_height = element_char.GetAttributeNode("height");
                    XmlAttribute attr_xoffset = element_char.GetAttributeNode("xoffset");
                    XmlAttribute attr_yoffset = element_char.GetAttributeNode("yoffset");
                    XmlAttribute attr_xadvance = element_char.GetAttributeNode("xadvance");

                    Console.WriteLine("{0},{1},{2},{3},{4},{5},{6},{7}",
                        attr_id.InnerText, attr_x.InnerText, attr_y.InnerText, attr_width.InnerText, attr_height.InnerText,
                        attr_xoffset.InnerText, attr_yoffset.InnerText, attr_xadvance.InnerText);

                }       

            }

ちゃんと読み込めた。

f:id:kengo700:20170305225109p:plain

fntファイルの座標データ部分を読み込んで、バイナリ形式で保存する。

using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;

namespace FontConverterForHollowKnight
{
    class Program
    {
        static void Main(string[] args)
        {

            int count = 0;

            List<int> id = new List<int>();
            List<float> x = new List<float>();
            List<float> y = new List<float>();
            List<float> width = new List<float>();
            List<float> height = new List<float>();
            List<float> xoffset = new List<float>();
            List<float> yoffset = new List<float>();
            List<float> xadvance = new List<float>();

            XmlDocument xmlDocument = new XmlDocument();
            xmlDocument.Load("hiragana.fnt");
            
            XmlNodeList nodeList = xmlDocument.SelectNodes(@"/font/chars");

            foreach (XmlNode node in nodeList)
            {

                XmlElement element = (XmlElement)node;
                XmlAttribute attr_count = element.GetAttributeNode("count");
                Console.WriteLine(attr_count.InnerText);

                count = int.Parse(attr_count.InnerText);

                for (int i = 0; i < node.ChildNodes.Count; i++)
                {
                    XmlElement element_char = (XmlElement)node.ChildNodes[i];

                    XmlAttribute attr_id = element_char.GetAttributeNode("id");
                    XmlAttribute attr_x = element_char.GetAttributeNode("x");
                    XmlAttribute attr_y = element_char.GetAttributeNode("y");
                    XmlAttribute attr_width = element_char.GetAttributeNode("width");
                    XmlAttribute attr_height = element_char.GetAttributeNode("height");
                    XmlAttribute attr_xoffset = element_char.GetAttributeNode("xoffset");
                    XmlAttribute attr_yoffset = element_char.GetAttributeNode("yoffset");
                    XmlAttribute attr_xadvance = element_char.GetAttributeNode("xadvance");

                    Console.WriteLine("{0},{1},{2},{3},{4},{5},{6},{7}",
                        attr_id.InnerText, attr_x.InnerText, attr_y.InnerText, attr_width.InnerText, attr_height.InnerText,
                        attr_xoffset.InnerText, attr_yoffset.InnerText, attr_xadvance.InnerText);

                    id.Add(int.Parse(attr_id.InnerText));
                    x.Add(float.Parse(attr_x.InnerText));
                    y.Add(float.Parse(attr_y.InnerText));
                    width.Add(float.Parse(attr_width.InnerText));
                    height.Add(float.Parse(attr_height.InnerText));
                    xoffset.Add(float.Parse(attr_xoffset.InnerText));
                    yoffset.Add(float.Parse(attr_yoffset.InnerText));
                    xadvance.Add(float.Parse(attr_xadvance.InnerText));

                }       

            }

            float pagechnl = 1;

            FileStream fs = null;

            // ファイルを新規作成(既に存在する場合は上書き)
            fs = new FileStream(@"testnew.bin", FileMode.Create);

            byte[] data_count = BitConverter.GetBytes(count);
            fs.Write(data_count, 0, data_count.Length);

            for(int i=0; i< count; i++)
            {
                Console.WriteLine("{0}", i);

                byte[] data_id = BitConverter.GetBytes(id[i]);
                fs.Write(data_id, 0, data_id.Length);

                byte[] data_x = BitConverter.GetBytes(x[i]);
                fs.Write(data_x, 0, data_x.Length);

                byte[] data_y = BitConverter.GetBytes(y[i]);
                fs.Write(data_y, 0, data_y.Length);

                byte[] data_width = BitConverter.GetBytes(width[i]);
                fs.Write(data_width, 0, data_width.Length);

                byte[] data_height = BitConverter.GetBytes(height[i]);
                fs.Write(data_height, 0, data_height.Length);

                byte[] data_xoffset = BitConverter.GetBytes(xoffset[i]);
                fs.Write(data_xoffset, 0, data_xoffset.Length);

                byte[] data_yoffset = BitConverter.GetBytes(yoffset[i]);
                fs.Write(data_yoffset, 0, data_yoffset.Length);

                byte[] data_xadvance = BitConverter.GetBytes(xadvance[i]);
                fs.Write(data_xadvance, 0, data_xadvance.Length);

                byte[] data_pagechnl = BitConverter.GetBytes(pagechnl);
                fs.Write(data_pagechnl, 0, data_pagechnl.Length);

            }

            fs.Close();

        }
    }
    
}

できてるっぽい

f:id:kengo700:20170305231623p:plain

ひらがな、カタカナ、アルファベットのビットマップフォントを作ってみる。

f:id:kengo700:20170305234725p:plain

作成したプログラムで座標データをバイナリ形式で作成。「resources_00002.-5」にコピペ。画像サイズとかの情報部分は手動で書き換え。

f:id:kengo700:20170305234425p:plain

リパックしてゲームを起動。

f:id:kengo700:20170305234545p:plain

縦方向のずれはあるものの、何とかなりそう。細かい修正・調整は後からすることにして、そろそろ翻訳にも取り掛かろう。

テキストデータの抽出プログラムの作成

3/6~

フォントデータを扱うのに疲れたので、テキストデータの処理をしてみる。

テキストデータが複数の「EN_*.txt」に分散しているので、日本語化作業所(スプレッドシート)を立てるために情報を抽出してcsvまたはtsvファイルにまとめるプログラムを作成する。

まずは一つのファイルの内容を読み込んで表示してみる。

using System;
using System.Collections.Generic;
using System.Xml;

namespace ExtractTextForHollowKnight
{
    class Program
    {
        static void Main(string[] args)
        {


            string inputFile = @"C:\Temp\hollowknight\EN_MainMenu.txt";

            XmlDocument xmlDocument = new XmlDocument();
            xmlDocument.Load(inputFile);

            XmlNodeList nodeList = xmlDocument.SelectNodes(@"/entries/entry");

            foreach (XmlNode node in nodeList)
            {
                
                XmlElement element = (XmlElement)node;
                XmlAttribute attr_count = element.GetAttributeNode("name");

                Console.WriteLine(attr_count.InnerText);
                Console.WriteLine(element.InnerText);

            }

        }
    }
}

「EN_MainMenu.txt」の内容は下記の通りにXML形式っぽいので、フォント座標の変換プログラムを改変して流用。

f:id:kengo700:20170306190756p:plain

プログラムを実行すると下記の通り。ちゃんと読み込めている。

f:id:kengo700:20170306190905p:plain

読み込んだデータをtsv形式で保存する。tsv形式はデータをタブで区切った形式で、データをカンマで区切るcsv形式より少しだけ扱いやすいと思う(テキスト中のカンマの扱いが)。まあ大差ないので好きな方で大丈夫。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;

namespace ExtractTextForHollowKnight
{
    class Program
    {
        static void Main(string[] args)
        {

            string inputFile = @"C:\Temp\hollowknight\EN_MainMenu.txt";
            string outputFile = @"C:\Temp\hollowknight\hollowknight_en.tsv";

            // 読み込みデータの保存用変数
            List<string> input_id = new List<string>();
            List<string> input_text = new List<string>();

            // データファイルを開く
            XmlDocument xmlDocument = new XmlDocument();
            xmlDocument.Load(inputFile);

            XmlNodeList nodeList = xmlDocument.SelectNodes(@"/entries/entry");

            foreach (XmlNode node in nodeList)
            {
                
                XmlElement element = (XmlElement)node;
                XmlAttribute attr_count = element.GetAttributeNode("name");

                Console.WriteLine(Path.GetFileName(inputFile));
                Console.WriteLine(attr_count.InnerText);
                Console.WriteLine(element.InnerText);

                input_id.Add(attr_count.InnerText);
                input_text.Add(element.InnerText);

            }


            // 出力用ファイルを開く               
            using (StreamWriter writer = new StreamWriter(outputFile, false, Encoding.UTF8))
            {

                // 見出し行を出力
                writer.WriteLine("File\tID\tText");

                // データを書き込み
                for(int i=0; i<input_id.Count(); i++)
                {
                    writer.WriteLine("{0}\t{1}\t{2}", Path.GetFileName(inputFile), input_id[i], input_text[i]);
                }

            }

        }
    }
}

出力されたファイルは下記の通り。

f:id:kengo700:20170306192230p:plain

あとはフォルダ内のテキストファイルに同じ処理をするだけ。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;

namespace ExtractTextForHollowKnight
{
    class Program
    {
        static void Main(string[] args)
        {

            string inputFolder = @"C:\Temp\hollowknight";
            string outputFile = @"C:\Temp\hollowknight\hollowknight_en.tsv";

            List<string> inputFiles = new List<string>();

            // ファイル名の取得(フルパス)
            inputFiles.AddRange(Directory.EnumerateFiles(inputFolder, "*.txt", SearchOption.AllDirectories).ToList());


            // 出力用ファイルを開く               
            using (StreamWriter writer = new StreamWriter(outputFile, false, Encoding.UTF8))
            {
                // 見出し行を出力
                writer.WriteLine("File\tID\tText");


                // データファイルを一つずつ処理
                foreach (string inputFile in inputFiles)
                {
                    // 読み込みデータの保存用変数
                    List<string> input_id = new List<string>();
                    List<string> input_text = new List<string>();

                    // データファイルを開く
                    XmlDocument xmlDocument = new XmlDocument();
                    xmlDocument.Load(inputFile);

                    XmlNodeList nodeList = xmlDocument.SelectNodes(@"/entries/entry");

                    foreach (XmlNode node in nodeList)
                    {

                        XmlElement element = (XmlElement)node;
                        XmlAttribute attr_count = element.GetAttributeNode("name");

                        Console.WriteLine("{0}, {1}, {2}", Path.GetFileName(inputFile), attr_count.InnerText, element.InnerText);

                        input_id.Add(attr_count.InnerText);
                        input_text.Add(element.InnerText);

                    }

                    // データを書き込み
                    for (int i = 0; i < input_id.Count(); i++)
                    {
                        writer.WriteLine("{0}\t{1}\t{2}", Path.GetFileName(inputFile), input_id[i], input_text[i]);
                    }

                }

            }


        }
    }
}

f:id:kengo700:20170306193311p:plain

大丈夫っぽい。ほとんど昔作ったプログラムの流用で行けたので楽だった。

日本語化作業所を立てる

tsv形式のファイルができたので、もう作業所を作ってしまおう。tsv形式のファイルから「EN_*.txt」を書き換えるプログラムはどうとでもなるだろう。きっと…

Googleアカウントにログインした状態でスプレッドシートのページを開き、右下のボタンから新しいシートを作成。

f:id:kengo700:20170306193723p:plain

「ファイル」->「開く」->「アップロード」に作成したtsvファイルをドラッグ

f:id:kengo700:20170306193620p:plain

問題なく読み込めた。

f:id:kengo700:20170306193840p:plain

ざっと見てみると、一部のデータがちゃんとtsvファイルに読み込めてないように見える(IDの部分にテキストが入ってしまっている?)。

f:id:kengo700:20170306194401p:plain

しかし元のテキストファイルを見ると、元からそうなっている。中文化版もそのままになっているので、ここは訳さなくてもいいのかもしれない。日本語化フォントが完成したら試してみよう。

f:id:kengo700:20170306194537p:plain

f:id:kengo700:20170306194711p:plain

分かりやすいようにスプレッドシートの体裁を整える。

  • 幅をいい感じに
  • 一行目の見出し行を日本語に。色を付ける。
  • テキストの折り返し設定
    • 原文列は「折り返し」ID列は「切り詰め」
  • 進捗列を追加
    • 「データ」->「データの入力規則」で「リストを直接指定」。「✕,△,〇」で「保存」。他の行にもコピペ。
  • 未訳部分を色付け
    • 「表示形式」->「条件付き書式」で「カスタム数式」。「=($E2=“✕”)」と「=($E2=“△”)」をそれぞれ黄色と緑色にする。
  • 見出し行を固定
    • 「表示」->「固定」->「1行」
  • ファイル名、ID、原文を保護
    • 選択肢て右クリックの「範囲を保護」
    • ※誤って編集してしまうの防ぐために保護設定をしてみているが、保護すると幅を変更できなくなるので、保護しなくてもいいかもしれない。
  • シートの名前を変更
    • 左下のシートのタブを右クリックして「名前を変更」
  • 進捗情報を自動計算して表示
    • 原文の見出し行を下記に
=CONCATENATE("訳文(全体:",countif(E2:E,"?*"),"、翻訳済:",(countif(E2:E,"〇")+countif(E2:E,"△")),"、進捗:",fixed((countif(E2:E,"〇")+countif(E2:E,"△"))/countif(E2:E,"?*")*100,2),"%)")」

f:id:kengo700:20170306201033p:plain

シートを追加し、翻訳方法などの説明情報を書き込む。

f:id:kengo700:20170306202517p:plain

こんな感じでいいかな。とりあえず日本語化の方法が確立するまでは「作業所(仮)」ということにしておこう。

注意事項とかをまとめるページをどうするかは考え中。Wikiを立ち上げると管理がめんどいし、Steamのガイドかブログの記事でいいか。

念のために自動バックアップだけ設定しておこう。まずはGoogleドライブに適当なフォルダを作る。作業所のシートもそこに移動しておく。

f:id:kengo700:20170306202941p:plain

「ツール」->「スクリプトエディタ」に下記のスクリプトを書き込む。英数字の部分はシートとバックアップ用のフォルダを開いたときの英数字部分。

//ファイルのバックアップ
function backup() {  
  var file = DriveApp.getFileById('「シートのURLのランダムな英数字」');//バックアップしたいファイル
  var folder = DriveApp.getFolderById('「フォルダのURLのランダムな英数字」');  // ここで指定したフォルダにファイルが入る
  file.makeCopy(file.getName()+'-'+Utilities.formatDate(new Date(), 'JST', 'yyyy-MM-dd-HH'),folder);   
}

▶ボタンで実行し、承認の確認が出たら許可する。ちゃんとバックアップが作成されたことを確認。

f:id:kengo700:20170306203723p:plain

時計マークのボタンでトリガーの設定を開き、毎日実行されるように設定して保存する。

f:id:kengo700:20170306203817p:plain

最後に作業所をWebに公開する。「ファイル」->「共有」の右下の「詳細設定」。

f:id:kengo700:20170306204003p:plain

非公開となっている部分右の「変更」をクリックし、「オン - リンクを知っている全員」にチェックを入れ、アクセスを「編集者」にして「保存」。

f:id:kengo700:20170307135043p:plain

※「オン - ウェブ上で一般公開」でもいいと思うが、日本語化についての注意点をまとめたページを見てから作業所を開いてほしいので「オン - リンクを知っている全員」にしてみている。どっちでもいいと思う。

あとは「共有するリンク」のURLをどこかに公開する。

注意点をまとめたページはそのうち作りますが、上記作業所で翻訳作業を開始していただいてもOKです。

ただしこの記事に書いてある通り、まだフォントの作成も完了しておらず、日本語化の手法が確立していない状態ということをご了承ください。 今後何かしらの問題が発生して日本語化パッチの作成が不可能となった場合、翻訳作業も無駄になってしまうかもしれません。

翻訳データの挿入プログラムの作成

3/7~

日本語化作業所(スプレッドシート)から出力した翻訳データ(csvまたはtsv形式)を元に、「EN*.txt」を書き換えるプログラムを作成する。 翻訳データのみから「EN.txt」を生成することもできるが、バージョンアップで「EN_.txt」が変更されることを考えて、書き換える方式にする。

スプレッドシートのスクリプト(Google Apps Script)を使うとデータを整形したり処理したりして出力できるが、今回はめんどくさいのでそのまま出力(csvでデータを「"」で囲みたいときなどはスクリプトを使う)。

翻訳データのタブを開き、「ファイル」->「形式を指定してダウンロード」->「タブ区切りの値」をクリック。下図のようにデータをtsv形式でダウンロードできる。

f:id:kengo700:20170307190731p:plain

このデータをプログラムで読み込む。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace InsertTextForHollowKnight
{
    class Program
    {
        static void Main(string[] args)
        {

            string translatedFile = @"C:\Temp\hollowknight\jp\Hollow Knight 日本語化作業所(仮) - 翻訳データ.tsv";

            Dictionary<string, string> translated_text_jp = new Dictionary<string, string>();

            // 翻訳データファイル(tsv形式)をオープン
            if (File.Exists(translatedFile))
            {

                using (StreamReader reader_translated = new StreamReader(translatedFile, Encoding.UTF8))
                {
                    // 1行目(見出し行)はスキップ
                    reader_translated.ReadLine();


                    // ファイル末尾まで、1行ずつ読み込み
                    while (reader_translated.EndOfStream == false)
                    {
                        string line = reader_translated.ReadLine();

                        char[] delimiterChars = { '\t' };
                        string[] words = line.Split(delimiterChars);

                        string temp_file = "";
                        string temp_id = "";
                        string temp_text_en = "";
                        string temp_text_jp = "";

                        int count = 0;
                        foreach (string word in words)
                        {
                            switch (count)
                            {
                                case 0:
                                    temp_file = word;
                                    break;
                                case 1:
                                    temp_id = word;
                                    break;
                                case 2:
                                    temp_text_en = word;
                                    break;
                                case 3:
                                    temp_text_jp = word;
                                    break;
                            }
                            count++;
                        }

                        Console.WriteLine("{0},{1}",temp_file, temp_id);

                        // データを変数に追加
                        translated_text_jp[temp_file+temp_id] = temp_text_jp;

                    }
                }

            }
        }
    }
}

「EN_*.txt」内のIDデータは全体で一意ではないっぽい(あるファイルと別のファイルで同じIDがある)のでファイル名とIDを組み合わせてDictionaryのキーにしてみている。

プログラムを実行すると、読み込めてそう。

f:id:kengo700:20170307192632p:plain

オリジナルデータも読み込み、翻訳データに書き換えた「EN_*.txt」を生成する。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Xml;

namespace InsertTextForHollowKnight
{
    class Program
    {
        static void Main(string[] args)
        {

            string translatedFile = @"C:\Temp\hollowknight\jp\Hollow Knight 日本語化作業所(仮) - 翻訳データ.tsv";
            string originalFile = @"C:\Temp\hollowknight\en\EN_Achievements.txt";
            string outputFile = @"C:\Temp\hollowknight\jp\EN_Achievements.txt";

            Dictionary<string, string> translated_text_jp = new Dictionary<string, string>();

            // 翻訳データファイル(tsv形式)をオープン
            if (File.Exists(translatedFile))
            {

                using (StreamReader reader_translated = new StreamReader(translatedFile, Encoding.UTF8))
                {
                    // 1行目(見出し行)はスキップ
                    reader_translated.ReadLine();


                    // ファイル末尾まで、1行ずつ読み込み
                    while (reader_translated.EndOfStream == false)
                    {
                        string line = reader_translated.ReadLine();

                        char[] delimiterChars = { '\t' };
                        string[] words = line.Split(delimiterChars);

                        string temp_file = "";
                        string temp_id = "";
                        string temp_text_en = "";
                        string temp_text_jp = "";

                        int count = 0;
                        foreach (string word in words)
                        {
                            switch (count)
                            {
                                case 0:
                                    temp_file = word;
                                    break;
                                case 1:
                                    temp_id = word;
                                    break;
                                case 2:
                                    temp_text_en = word;
                                    break;
                                case 3:
                                    temp_text_jp = word;
                                    break;
                            }
                            count++;
                        }

                        Console.WriteLine("{0},{1}", temp_file, temp_id);

                        temp_text_jp = temp_text_jp.Replace("'", "&#39;");
                        temp_text_jp = temp_text_jp.Replace("<", "&lt;");
                        temp_text_jp = temp_text_jp.Replace(">", "&gt;");

                        // データを変数に追加
                        translated_text_jp[temp_file + temp_id] = temp_text_jp;

                    }


                    // 元データファイルを開く
                    XmlDocument xmlDocument = new XmlDocument();
                    xmlDocument.Load(originalFile);

                    XmlNodeList nodeList = xmlDocument.SelectNodes(@"/entries/entry");

                    // 元データの保存用変数
                    List<string> original_id = new List<string>();

                    foreach (XmlNode node in nodeList)
                    {

                        XmlElement element = (XmlElement)node;
                        XmlAttribute attr_count = element.GetAttributeNode("name");

                        original_id.Add(attr_count.InnerText);

                    }


                    // 出力用ファイルを開く
                    using (StreamWriter writer = new StreamWriter(outputFile, false))
                    {

                        writer.WriteLine("<entries>");

                        for (int i = 0; i < original_id.Count(); i++)
                        {
                            writer.WriteLine("<entry name=\"{0}\">{1}</entry>", original_id[i], translated_text_jp[Path.GetFileName(originalFile) + original_id[i]]);
                        }

                        writer.WriteLine("</entries>");

                    }

                }
            }
        }
    }
}

やっぱりオリジナルデータを使わずに直接「EN_*.txt」を生成したほうが良かった気がする…

出力したファイルは下記の通り。ここでは「'」「<」「>」の文字参照への変換を確かめるために、tsvファイルの一部を「征服者の試しを'クリアする」に書き換えている。

f:id:kengo700:20170307201858p:plain

全ての「EN_*.txt」に対して同じ処理を実行。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Xml;

namespace InsertTextForHollowKnight
{
    class Program
    {
        static void Main(string[] args)
        {

            string translatedFile = @"C:\Temp\hollowknight\jp\Hollow Knight 日本語化作業所(仮) - 翻訳データ.tsv";

            string originalFolder = @"C:\Temp\hollowknight\en";
            string outputFolder = @"C:\Temp\hollowknight\jp";

            Dictionary<string, string> translated_text_jp = new Dictionary<string, string>();

            // 翻訳データファイル(tsv形式)をオープン
            if (File.Exists(translatedFile))
            {

                using (StreamReader reader_translated = new StreamReader(translatedFile, Encoding.UTF8))
                {
                    // 1行目(見出し行)はスキップ
                    reader_translated.ReadLine();


                    // ファイル末尾まで、1行ずつ読み込み
                    while (reader_translated.EndOfStream == false)
                    {
                        string line = reader_translated.ReadLine();

                        char[] delimiterChars = { '\t' };
                        string[] words = line.Split(delimiterChars);

                        string temp_file = "";
                        string temp_id = "";
                        string temp_text_en = "";
                        string temp_text_jp = "";

                        int count = 0;
                        foreach (string word in words)
                        {
                            switch (count)
                            {
                                case 0:
                                    temp_file = word;
                                    break;
                                case 1:
                                    temp_id = word;
                                    break;
                                case 2:
                                    temp_text_en = word;
                                    break;
                                case 3:
                                    temp_text_jp = word;
                                    break;
                            }
                            count++;
                        }

                        Console.WriteLine("{0},{1}", temp_file, temp_id);

                        temp_text_jp = temp_text_jp.Replace("'", "&#39;");
                        temp_text_jp = temp_text_jp.Replace("<", "&lt;");
                        temp_text_jp = temp_text_jp.Replace(">", "&gt;");

                        // データを変数に追加
                        translated_text_jp[temp_file + temp_id] = temp_text_jp;

                    }


                    List<string> originalFiles = new List<string>();

                    // ファイル名の取得(フルパス)
                    originalFiles.AddRange(Directory.EnumerateFiles(originalFolder, "*.txt", SearchOption.AllDirectories).ToList());

                    foreach (string originalFile in originalFiles)
                    {

                        // 元データファイルを開く
                        XmlDocument xmlDocument = new XmlDocument();
                        xmlDocument.Load(originalFile);

                        XmlNodeList nodeList = xmlDocument.SelectNodes(@"/entries/entry");

                        // 元データの保存用変数
                        List<string> original_id = new List<string>();

                        foreach (XmlNode node in nodeList)
                        {

                            XmlElement element = (XmlElement)node;
                            XmlAttribute attr_count = element.GetAttributeNode("name");

                            original_id.Add(attr_count.InnerText);

                        }

                        string outputFile = outputFolder + "\\" +  Path.GetFileName(originalFile);

                        // 出力用ファイルを開く
                        using (StreamWriter writer = new StreamWriter(outputFile, false))
                        {

                            writer.WriteLine("<entries>");

                            for (int i = 0; i < original_id.Count(); i++)
                            {
                                writer.WriteLine("<entry name=\"{0}\">{1}</entry>", original_id[i], translated_text_jp[Path.GetFileName(originalFile) + original_id[i]]);
                            }

                            writer.WriteLine("</entries>");

                        }


                    }

                }
            }
        }
    }
}

かなり雑なプログラムだけど、問題なさそう。

f:id:kengo700:20170307203101p:plain

日本語フォントの作成テスト3

次は漢字も含めたフォントを作ってみる。

「Bitmap font generator」は出力する文字をテキストデータを使って選択することができる。やり方と、日本語用のテキストデータは下記ページで。 ただし、このテキストデータはディレクトリ名に日本語が含まれない場所に置く必要がある。

出力設定もいくつか変えてみる。

f:id:kengo700:20170307205011p:plain

f:id:kengo700:20170307205022p:plain

f:id:kengo700:20170307205001p:plain

座標データは下記のような感じ。sizeがマイナスになっている理由は不明。

f:id:kengo700:20170307211105p:plain

バイナリデータを修正。試しに中文化版と同じように、サイズとかの部分を「F0 41」に統一してみる。

f:id:kengo700:20170307211136p:plain

テキストは作業所で多少修正したものを利用。したらバグった。

元のテキストと翻訳データを差し込んだテキストを比べてみると、改行コードが違う。

f:id:kengo700:20170307212253p:plain

なので出力部分をちょっと変える。

                            writer.Write("<entries>\n");

                            for (int i = 0; i < original_id.Count(); i++)
                            {
                                writer.Write("<entry name=\"{0}\">{1}</entry>\n", original_id[i], translated_text_jp[Path.GetFileName(originalFile) + original_id[i]]);
                            }

                            writer.Write("</entries>\n");

翻訳データを入れた「EN_*.txt」を全て使うとバグる。「EN_Elderbug.txt」だけなら問題なく日本語化できた。1つずつ試してみて、原因を探る。

「EN_Credits List.txt」を入れるとバグる。元データで「&」となっている部分が「&」になっているからっぽい。

変換部分を追記する。

                        temp_text_jp = temp_text_jp.Replace("'", "&#39;");
                        temp_text_jp = temp_text_jp.Replace("<", "&lt;");
                        temp_text_jp = temp_text_jp.Replace(">", "&gt;");
                        temp_text_jp = temp_text_jp.Replace("&", "&amp;");

「"」の変換も加える。

                        temp_text_jp = temp_text_jp.Replace("\"", "&quot;");

ID部分にも文章っぽい文字が入っている場合があることを忘れていた。この部分も変換処理をする。

                            for (int i = 0; i < original_id.Count(); i++)
                            {

                                string temp_id = original_id[i].Replace("'", "&#39;");
                                temp_id = temp_id.Replace("<", "&lt;");
                                temp_id = temp_id.Replace(">", "&gt;");
                                temp_id = temp_id.Replace("&", "&amp;");
                                temp_id = temp_id.Replace("\"", "&quot;");

                                writer.Write("<entry name=\"{0}\">{1}</entry>\n", temp_id, translated_text_jp[Path.GetFileName(originalFile) + original_id[i]]);
                            }

f:id:kengo700:20170307232138p:plain

f:id:kengo700:20170307232220p:plain

おっけー。

「し」とかの文字がちょっとかすれて見えるのは、ビットマップフォント作成時のスムージングの影響っぽい。

「Font smoothing」のチェックを外すと、生成されるフォントが少し変わる。

f:id:kengo700:20170307235027p:plain

チェックを入れた場合

f:id:kengo700:20170307235246p:plain

チェックを外した場合

f:id:kengo700:20170307235232p:plain

まあ、細かい調整は最後にやろう。

日本語フォントの作成テスト4

文字の表示位置が下にずれているのを修正する。 会話文であれば違和感はないが、インベントリではずれてかぶってしまう。

f:id:kengo700:20170308001021p:plain

上の図で気付いたが、ビットマンとフォントの設定を間違って「Noto Sans CJK KR DemiLight」 を選んでしまっている。「Noto Sans CJK JP DemiLight」を選んだつもりだった…

これはテストのためだけのフォントで、後々もっとおしゃれなフォントを探すつもりだけど、気になるのでビットマップフォントを作り直し。

f:id:kengo700:20170308002800p:plain

とりあえず前に解析した結果から、座標データファイルの中のBaseの部分をゼロにしてみる。

f:id:kengo700:20170308003411p:plain

するとずれが解消された。

f:id:kengo700:20170308003457p:plain

根本的な原因が分からず、対処療法的でとてもすっきりしないが、とりあえずこれでいいとしておく。

日本語フォントの作成テスト5

これまでは「perpetua_tmpro Atlas.tex.dds」と「resources_00002.-5」しか日本語化していないので、他のフォントも日本語化する。とりあえず同じフォントデータを使いまわす。

f:id:kengo700:20170308012418p:plain

f:id:kengo700:20170308014437p:plain

いちいちUnityEXを起動するのがめんどいのでバッチファイルでリパックする。

for %%a in (*.assets) do UnityEX.exe import "%%a"
pause

これまで表示されなかったオブジェクトとのインタラクトの文字が表示されるようになったが、縦方向がずれてしまっている。やっぱり何かしらフォントの座標データファイルの解析が間違っているっぽい。

f:id:kengo700:20170308023757p:plain

baseの部分を41F0(float、30)にしたらさらに下がった。C1F0(float、-30)にしても上には上がらず。 やはり根本の原因を探らないと解決できないようだ。

フォントデータの解析7

座標データファイル内で、中文化版ではゼロになっていたのでゼロにしていた部分をマイナスにしてみる。C1F0(float、-30)にすると、少し上がった。

f:id:kengo700:20170308030659p:plain

f:id:kengo700:20170308030634p:plain

C220(float、-40)にするともう少し上がったが、C248(float、-50)にしてもこれ以上は上がらなかった。

f:id:kengo700:20170308030942p:plain

3/8~

そもそもそういえば、先日フォントを作成したときは「あ」と「い」の高さもずれていたので、「Equalize the cell heights」のオプションでフォントの高さを合わせていたのだった。このオプションを外してフォントを作成してみる。

f:id:kengo700:20170308194546p:plain

f:id:kengo700:20170308194846p:plain

ゲーム内では、やはりy座標の設定がおかしいようで、位置がバラバラになっている。

ためしに「の」の字の座標データを書き換えてみる。

f:id:kengo700:20170308195422p:plain

このyoffsetを13から100に書き換える。

f:id:kengo700:20170308195521p:plain

f:id:kengo700:20170308200147p:plain

結果、上方向に移動した。yoffsetはプラスで下だと思っていたけど逆のようだ。

座標データ変換ソフトでyoffsetをマイナスにしてみる。

            for (int i = 0; i < count; i++)
            {
                yoffset[i] = yoffset[i] * (float)-1.0;
            }

f:id:kengo700:20170308205335p:plain

ずれが解消された。

オブジェクトとのインタラクト関連のフォントのファイル「sharedassets0」に同じフォント画像と座標データを適用する。昨日マイナスにした部分はゼロに戻しておく。

f:id:kengo700:20170308210235p:plain

f:id:kengo700:20170308210456p:plain

やっぱりずれている。ゼロにした部分をC248(float、-50)に戻す。

f:id:kengo700:20170308211004p:plain

f:id:kengo700:20170308210939p:plain

模様と被らないようになった。 対処療法に対処療法を重ねた感じだけれど、これでいいとしてしまおう。 yoffsetがプラスマイナス逆だったのでxoffsetも怪しいけれど、そちらも問題が出てから調べる。

フォント探し

フリーで再配布も可能でゲームの雰囲気にあったフォントを探す。

オリジナルの英語のフォントは、前述の通り「Trajan」「Perpetua」「Arial」。

「Trajan」はメニューなどに使われているフォント。「Q」の字がカッコいい。

f:id:kengo700:20170308211422p:plain

「Perpetua」は会話文などに使われているフォント。読みやすさ重点。

f:id:kengo700:20170308211703p:plain

というわけでスタイリッシュなフォントと、細めで読みやすいフォントを探してみる。ついでに海底探索ゲーム『Diluvion』用のフォントも探す。

IPAフォントはビットマップフォント状態での配布はできないらしい。多くの明朝体のフリーフォントがIPAフォントの漢字を利用しているので、それらのフォントはビットマップフォントとしては使えない。

パワーポイントでいろいろ比べてみる。

  • 花園明朝A
  • 花園明朝B
  • 源真ゴシック Normal
  • 源真ゴシックP Light
  • 02うつくし明朝体
  • あおぞら明朝 Regular
  • 飴鞭ゴシック-01
  • 刻明朝 Regular
  • はんなり明朝
  • IPAex明朝
  • Noto Sans CJK JP Light

f:id:kengo700:20170308231756p:plain

「飴鞭ゴシック」など、あまり特徴的すぎると主張が激しくなりすぎる。ゴシックのフォントは読みやすいものの、ちょっとポップな感じが出てしまう。 試した中では、メニューなどはIPAex明朝、その他は花園明朝Aが良さげか。

メニューなどの部分はダイナミックフォントなので、フォントを作成しなくてもすぐ試せる。下記のようにファイルを配置し、リパックする。

  • resources.assets
  • UnityEX.exe
  • Unity_Assets_Files
    • resources
      • TrajanPro-Bold.ttf(←IPAex明朝のipaexm.ttfをリネーム)
      • TrajanPro-Regular.ttf(←IPAex明朝のipaexm.ttfをリネーム)
      • Perpetua.ttf(←花園明朝AのHanaMinA.ttfをリネーム)

f:id:kengo700:20170308232506p:plain

すごく良い…

f:id:kengo700:20170308233828p:plain

実績画面は、実績の名前がIPAex、実績の説明が花園明朝になっている。どちらも癖の少ない明朝体なのであまり違いは分からないが、とりあえず両方使う方針で。

ちなみに『Diluvion』の方は「刻明朝 Regular」と「Noto Sans CJK JP Bold」が良さげ。英語の「BirchStd Font」と似ていてしっくりくる。

f:id:kengo700:20170309015232p:plain

これを「IPAex明朝」にしてみると、間延びしてしまって違和感が大きい。悪くはないけど。

f:id:kengo700:20170309015455p:plain

日本語フォントの作成

作成したプログラムを使用して、日本語(花園明朝A)のビットマップフォントを作成する。

まずは「perpetua_tmpro Atlas.tex.dds」と「resources_00002.-5」だけ修正。sizeの部分は00004248に。

f:id:kengo700:20170309002614p:plain

f:id:kengo700:20170309002844p:plain

少しカッチリしすぎて浮いているような気がする。ふちがもう少しだけぼやけてほしい。

出力設定の「Render from True Type outline」のチェックを外してみる。

f:id:kengo700:20170309004035p:plain

ぼやけはしてくれなかったけど、前より少しだけカスレが改善されて見やすくなった。

「ClearType」のチェックを外してみる。

f:id:kengo700:20170309005201p:plain

少しだけ文字が太く、さらにカスレが改善した。

「Super sampling」をlevel4にしてみる。

f:id:kengo700:20170309010312p:plain

カスレが悪化してしまった。ひとつ前のものの方がよさそう。

というわけで下記の設定で行くことにする。

f:id:kengo700:20170309011018p:plain

f:id:kengo700:20170309011033p:plain

日本語フォントの作成2

3/9~

残りのファイルを修正する。

f:id:kengo700:20170309191603p:plain

リパック中、Steamクライアントを起動したら『Hollow Knight』がアップデートした。

テキストデータのアップデート箇所のチェック

念のためSteamクライアントから「ゲームキャッシュの整合性を確認」を実行。「resources.assets」と「sharedassets0.assets」を取り出して、「UnityEX.exe」と下記内容の「export_txt_all.bat」でテキストデータを抜き出す。

for %%a in (*.assets) do UnityEX.exe export "%%a" -t txt
pause

アンパックしたtxtファイルの内EN_*.txtを別フォルダにコピーし、自作した「ExtractTextForHollowKnight」プログラムでデータをtsv形式の一つのファイルにまとめる。

そのファイルと、前回作成したファイルを「WinMerge」に取り込み、比較する。

f:id:kengo700:20170309192619p:plain

「表示」メニューの「Diffコンテキスト」を0にすると変更部分のみが表示されるため、この状態で表示されているテキストを「TeraPad」などにコピペすれば変更行数が分かる。

f:id:kengo700:20170309192806p:plain

今回は32テキストのみの変更。これぐらいなら手動で作業所をメンテナンスできるものの、今後のことを考えて、アプデに対応してtsvファイルを更新するプログラムを作るべきかもしれない。

作業所の更新

今回は手動で更新する。

作業所を開き、「ツール」->「スプリプトエディタ」から先日作成したバックアップスクリプトを実行しておく。

あとは「WinMerge」の比較画面や元ファイルを確認しながら、一つずつ編集する。

終わったら作業所の履歴部分に追記しておく。

f:id:kengo700:20170309195546p:plain

ゲームプログラムのアップデート箇所のチェック

下記ファイルを、アプデ前とアプデ後と中文化版のものを比較する。場合によっては中文化MODの更新を待つ必要がでてくる。

  • resources\resources_00001.-2
  • resources\resources_00001.-20
  • resources\resources_00001.-5
  • resources\resources_00002.-5
  • resources\resources_00003.-5
  • resources\resources_00004.-5
  • sharedassets0\sharedassets0_00001.-1

まずは「resources_00001.-2」。Stirlingで開いて「検索・移動」->「比較」でアプデ前とアプデ後、アプデ前と中文化版をそれぞれ比較する。

f:id:kengo700:20170309200902p:plain (上からアプデ前、アプデ後、中文化版)

これは…無理かもしれぬ…。このファイルを修正するにはUnityの知識が必須っぽい。

次は「resources_00001.-20」

f:id:kengo700:20170309202427p:plain (上からアプデ前、アプデ後、中文化版)

アプデ前とアプデ後よりも、アプデ後と中文化版のほうが近いのはなぜだろうか…

思い付きで、中文化版の「resources_00001.-2」と「resources_00001.-20」を除いたビットマップフォントの画像と座標データと「EN_*.txt」だけを、v1.0.0.7の「resources」と「sharedassets0」にリパックしてゲームを起動してみる。

f:id:kengo700:20170309211539p:plain

f:id:kengo700:20170309211407p:plain

問題なく中文化できた。ゲームを進めたら問題が生じるかもしれないが、とりあえず「resources_00001.-2」と「resources_00001.-20」は使わずに日本語化MODを作成してみることにする。

次は「resources_00001.-5」を比較。座標データなので中文化版では変わっているはず。アプデ前とアプデ後だけを比較する。

f:id:kengo700:20170309212034p:plain

変更箇所は1箇所だけ。ここはオリジナル版と中文化版で変化していなかったので無視していた部分。今後の作業ではここだけアプデ後版の値に変えておこう。

次は「resources_00002.-5」。変更箇所は2箇所。

f:id:kengo700:20170309212400p:plain

f:id:kengo700:20170309212425p:plain

次は「resources_00003.-5」。変更箇所は1箇所。

f:id:kengo700:20170309212549p:plain

次は「resources_00004.-5」。変更箇所は1箇所。

f:id:kengo700:20170309212646p:plain

次は「sharedassets0_00001.-1」。変更箇所はなし。

日本語フォントの作成3

作成中だったファイルの5箇所を修正して、v1.0.0.7の「resources」と「sharedassets0」にリパック。前節で中文化版では修正しなくても動いていたので、修正しなくてもいいかもしれない。

f:id:kengo700:20170309213440p:plain

f:id:kengo700:20170309214218p:plain

f:id:kengo700:20170309214510p:plain

そういえば「EN_*.txt」はv1.0.0.7用に作り直し忘れていたけど、問題はなさげ。

フォントのサイズや行間などを微調整。

f:id:kengo700:20170309221956p:plain

f:id:kengo700:20170309222041p:plain

f:id:kengo700:20170309222057p:plain

f:id:kengo700:20170309222119p:plain

やっぱりちょっとだけ字がカッチリし過ぎていて浮いている気もするけれど、なかなかいい感じだと思う。 日本語フォント作成は、とりあえずこれでひと段落。

日本語Wikiの立ち上げ

翻訳作業の注意事項をまとめたり、翻訳内容について議論する場所を確保するため、日本語wikiを立ち上げることにする。

スプレッドシートは情報をまとめたり議論したりするには向いていないと思うので、作業所のほかに情報をまとめる何かしらのウェブページを作ったほうが良いと思う。

今回はSteamガイドかブログの記事を作ろうかと思っていたものの、Steamガイドは検索に引っかかりにくいこと、ブログの記事のコメント欄は議論に向かないことから、Wikiを立ち上げることにした。 それと、ブログは個人のものというイメージが強いので、ブログ記事で取りまとめられている有志翻訳には参加しにくいのではないかとも思った。

Wikiは自前で用意することもできるが、Wikiサービスを利用したほうが簡単。今回は「WIKIWIKI.jp」というサービスを利用することにする。

WIKIWIKIで新しいWikiページを立ち上げる方法は下記の通り。

  • 下記ページの「新規登録する」をクリック
  • 必要事項を記入し「利用規約に同意し新規登録する」をクリック
    • WIKIWIKI ID:Wikiのurlになる。後から変更不可
    • WIKIタイトル:Wikiのタイトル。後から変更できるかは忘れた
    • デザインテンプレート:Wikiの見た目。後から変更可 f:id:kengo700:20170309224655p:plain
  • メールが送られてくるので、記載されたurlを開く

以上でWikiページの作成が完了

f:id:kengo700:20170309224850p:plain

デフォルトではWikiの使い方などのリンクが書かれているので、ゴリゴリ書き換えていく。

日本語Wikiの編集

ページを編集するには、左上の「編集」をクリックし書き換えて「ページの更新」をクリックする。 凍結されているページを編集するには登録したパスワードが必要。

とりあえずトップページを、以前立ち上げた『Owlboy』のWikiを参考に編集。

#br

&size(20){''Hollow Knight JP Wiki へようこそ!''};

このWikiは2Dアクションゲーム「Hollow Knight」に関する情報サイトです。誰でも自由に編集できます。 

#br

&flash(http://www.youtube.com/v/UAO2urG23S4,682x408);

#br

* お知らせ [#f881edfa]

**2017/02/25 [#q560f468]

-Hollow Knightリリース!

サイドバーを編集するには、上の「一覧」アイコンをクリックし、「MenuBar」ページを開いて編集する。

トップの文字を画像に変えるには、フロントページの「添付」から画像をアップロードし、「新規作成」で「:Header」を作成して編集する。

CENTER:[[&attachref(FrontPage/logo_main.png,nolink, nolink,30%, トップ画像);:http://wikiwiki.jp/hollowknight/?FrontPage]]

結果、こんな感じに。

f:id:kengo700:20170309231832p:plain

ダークな感じにしたいので、デザインテンプレートを変更する。やり方は下記ページ。

今回は「default_black03_z」を選んだ。設定画面でフォントも変えられるみたいだが、試しに「明朝」にしてみたところ読みにくかったので「ゴシック」に戻した。明朝の方が雰囲気はいいんだけど…

f:id:kengo700:20170309232415p:plain

なかなかいい感じ。

日本語Wikiの編集2

Wikiを立ち上げた目的である有志翻訳に関するページを作る。

新しいページを作るには左上の「新規」をクリックするか、ページ編集で新しいページタイトルというリンクを書き込み、それをクリックする。

だいたいこんな感じか。

日本語化についてと掲示板のページも作った。

翻訳作業

3/10~

やっぱりプレイしてエリアやボスのビジュアルを確認しないと翻訳できないな…

もうちょっとだけ進めたら翻訳に戻ろう…次のベンチまで…

テキストデータのアップデート箇所のチェック2

3/14~

v1.0.1.4へのアップデートが来たので確認。

テキストについては修正10、追加3。

f:id:kengo700:20170314224232p:plain

作業所のデータを手動で修正。

ちなみにゲームの進捗はたぶん中盤ぐらい。 毎日ちまちま進めているところ。

生涯一度の初見プレイはじっくりと楽しまなければ。

それと連続してプレイしていると疲れてきて、ミスが増えて進めなくなる…(疲れていなければミスしないとは言っていない)。

3/17~

ゲームクリア!

翻訳は明日から本気出す。

ゲームプログラムのアップデート箇所のチェック2

3/18~

前回のアプデと同様に、下記ファイルのv1.0.0.7のものとv1.0.1.4のものと比較する。

  • resources\resources_00001.-5
  • resources\resources_00002.-5
  • resources\resources_00003.-5
  • resources\resources_00004.-5
  • sharedassets0\sharedassets0_00001.-1

上がv1.0.0.7、下がv1.0.1.4。sharedassets0_00001.-1については変更箇所なし。

f:id:kengo700:20170318183636p:plain

f:id:kengo700:20170318183716p:plain

f:id:kengo700:20170318183741p:plain

f:id:kengo700:20170318183819p:plain

f:id:kengo700:20170318183910p:plain

変更箇所は前回と全く同じ。前回と同様に、v1.0.0.7からv1.0.1.4への変更箇所部分だけ日本語フォントの座標データのファイルを手動で修正。v1.0.1.4のresources.assetsとsharedassets0.assetsにリパック。

f:id:kengo700:20170318185624p:plain

f:id:kengo700:20170318185705p:plain

問題なさげ。

現在の作業所のデータを使ってみる。tsvファイルにエクスポートし、自作のプログラムで「EN_*.txt」を作成。resources.assetsへリパック。

↓ちょっとネタバレ注意

[ジャーナルの一部(クリックで表示)]

f:id:kengo700:20170318190914p:plain

[インベントリ(クリックで表示)]

f:id:kengo700:20170318191237p:plain

[全体マップ(クリックで表示)]

f:id:kengo700:20170318191010p:plain

やっぱり一部文字がかすれてしまっているけど、いい感じ。

ある程度ゲームをプレイしてみたけど、問題は起きなかった。Hiveへの入り方が分からねえ…

日本語テストパッチの作成

翻訳としては正しいものでもゲーム画面に表示してみると違和感が出るものもあるので、私以外の人も作業所のデータを元に日本語化できるように準備する。

とりあえず下記のようなファイルを用意すればいいか。

  • README.txt(←説明)
  • リパック.bat(←tsvファイルからtxtの作成と、リパックの作業を一括で行うバッチファイル)
  • Hollow Knight 日本語化作業所(仮) - 翻訳データ.tsv(←翻訳データ。各自が作業所からエクスポートして上書きする)
  • Unity_Assets_Files
    • resources
      • resources_00001.-5(日本語フォントの座標データ)
      • resources_00002.-5(日本語フォントの座標データ)
      • resources_00003.-5(日本語フォントの座標データ)
      • resources_00004.-5(日本語フォントの座標データ)
      • Textures
        • perpetua_tmpro Atlas.tex.dds(日本語フォントの画像データ)
        • Perpetua-SDF Atlas.tex.dds(日本語フォントの画像データ)
        • trajan_bold_tmpro Atlas.tex.dds(日本語フォントの画像データ)
        • res_logo
          • ARIAL SDF Atlas.tex.dds(日本語フォントの画像データ)
    • sharedassets0
      • sharedassets0_00001.-1(日本語フォントの座標データ)
      • Textures
        • TrajanPro-Bold SDF Atlas.tex.dds(日本語フォントの画像データ)
  • program
    • InsertTextForHollowKnight.exe(←自作のプログラム)
  • originaltext_v1.0.1.4
    • EN_*.txt(←オリジナルのテキストファイル)
  • license(←配布するフォントのライセンス情報)
    • IPAex明朝
    • 花園明朝

resources.assets、sharedassets0.assetsとUnityEX.exeは各自で用意してもらい、「リパック.bat」を実行すると下記の処理が行われるようにする。

  1. InsertTextForHollowKnight.exeを実行し、オリジナルのEN.txtと翻訳データの.tsvから翻訳済みのEN*.txtを生成する
  2. UnityEX.exeを実行し、resources.assetsとsharedassets0.assetsにデータをリパックする

これを実現するために翻訳データの挿入プログラムを修正する。

プログラム内で決め打ちにしていたファイル名やフォルダ名の部分を引数で与えるようにする。

            //string translatedFile = @"C:\Temp\hollowknight\jp\Hollow Knight 日本語化作業所(仮) - 翻訳データ.tsv";
            //string originalFolder = @"C:\Temp\hollowknight\en_v1.0.1.4";
            //string outputFolder = @"C:\Temp\hollowknight\jp";

            if (args.Length >= 3)
            {

                string translatedFile = args[0];
                string originalFolder = args[1];
                string outputFolder = args[2];

                (中略)
            }

そして下記のバッチファイルを作成して実行すると、問題なくEN_*.txtが生成された。

InsertTextForHollowKnight.exe "C:\Temp\hollowknight\jp\Hollow Knight 日本語化作業所(仮) - 翻訳データ.tsv" "C:\Temp\hollowknight\en_v1.0.1.4" "C:\Temp\hollowknight\jp"
pause

ファイルが見つからなかった時のエラー表示とか全くしてないけど、まあいいか…

前述のフォルダを用意し、下記のバッチファイルを実行すると、問題なくEN*.txtが生成された。翻訳データ(tsvファイル)のファイル名が変わってもいいように、同じフォルダ内のtsvファイル全部に対して同じ処理をしている。ただし複数のtsvファイルをマージできるわけでなく、生成されるEN*.txtは最後に処理したtsvファイルのものになる(こんな変なことをしているのは、バッチファイルの書き方の知識が無くて、前に作ったバッチファイルを修正しているから。もっとスマートな書き方はあるはず…)。

for %%a in (*.tsv) do ".\program\InsertTextForHollowKnight.exe" "%%a" ".\original_text\en_v1.0.1.4" ".\Unity_Assets_Files\resources"
pause

リパック処理も加える。

for %%a in (*.tsv) do ".\program\InsertTextForHollowKnight.exe" "%%a" ".\original_text\en_v1.0.1.4" ".\Unity_Assets_Files\resources"
for %%a in (*.assets) do UnityEX.exe import "%%a"
pause

もろもろのファイルを準備。

f:id:kengo700:20170318212842p:plain

いったんゲームを英語版に戻し、準備したファイルを利用して日本語化できることを確認。

日本語テストパッチの配布

配布するために「Lhaplus」で圧縮し、Googleドライブにアップロード。

f:id:kengo700:20170318213121p:plain

ファイルを右クリックし、「共有」から作業所の共有と同じように公開設定にする。ただしアクセスは「編集者」ではなく「閲覧者」に。

日本語化作業所にリンクを記載

f:id:kengo700:20170318213933p:plain

Googleアカウントからログアウトしてダウンロードできることを確認。 READMEのフォントのところに、誤字というか表記揺れが…

これで問題がなければ、後はひたすら翻訳するだけだ…

文字がかすれる問題については、ビットマップフォントを作るときにボールドにすればいいかもしれないと思いついたのでメモ。 時間があるときにやってみよう。

表示がおかしい部分のチェック(途中)

日本語化すると表示がおかしい部分をチェックする。

ジャーナルの左の文字が下にずれている? 右上の表記で名前が長くて二行になると被ってしまう。 ここら辺はもう少し文字を小さくするか、文字間隔が狭いフォントを使いたいところ。

f:id:kengo700:20170319164635p:plain

キーボード操作だと、マップ画面の右下のキーが表示されない(コントローラだと表示される)。

f:id:kengo700:20170319164710p:plain

翻訳作業2(途中)

3/26~

ひたすら翻訳。

息抜きにこれまでの進捗をグラフにしてみる。 なぜが26日のバックアップが取れてなかった…

f:id:kengo700:20170326173545p:plain

皆さまお疲れ様です。

そろそろフォントの修正もしなければ…

3/27~

やっぱり最後までプレイしないと翻訳できないな…

なんだこのノコギリ…

各フォントの使用箇所の調査(未着手)

日本語フォントの改善(未着手)

差分パッチの作成方法の調査(未着手)

表記揺れの修正(未着手)

日本語化パッチの作成(未着手)

校正作業(未着手)

おわりに

いざ日本語化に取り組んでみると、中国人の中文化MODの作成スピードにビビります…

想像ですけれど、専門知識を持った方々が組織的に中文化を行っているのではないかと思います。 今回私が調べながら試行錯誤しているような内容も、ノウハウが共有されているのではないでしょうか。

うらやましいですね…