I2 Localizationで複数言語に対応!

Unityでゲームを作って世界中の人に遊んでもらえれば、日本だけでは得られなかった嬉しい反応をもらえることがあったりしますよね。
でも、日本語と英語くらいならスクリプト内でも言語の切り替えくらい簡単にできますが、4ヶ国語目くらいから翻訳を追加する作業が段々キツくなってきます。。
そんな煩雑になりそうな多言語対応を楽にしてくれるassetが「I2 Localization」です。

お盆休みが終わりましたね。今年はオリンピックが無事に閉会できたものの、去年にも増して新型コロナが猛威を振るってるので外に出られず夏の思い出が全くありません。。。

この記事は「Unity アセット真夏のアドベントカレンダー2021 Summer!」の23日目の記事です。今回、私は3回目のアドカレ参加です。
22日目の記事は、Jaol-Industryさんの「コスパ最高! 格安特盛エフェクトアセット!【Epic Toon FX】」でした。

ちなみに前回のアドカレには、UModelerというUnityで簡単なモデリングができるassetの紹介で参加しました。今回は、多言語対応ということでUIに近い部分に関するassetの紹介になります。

<I2 Localizationとは>

I2 Localizationは、Unityで作ったゲームを実行時にデバイスまたは指定した言語環境に応じて、表示するテキスト・画像などを変更してくれるassetです。
Googleスプレッドシートを読み書きする機能が付いてるので、言語の数が多くても管理がしやすいのが特徴です。

■I2 Localizationのサポート

しっかりしたマニュアルのサイトがあり、Youtubeの動画もあります。また、質問を投げても即答は得られませんでしたが、作者さんのフォーラムもあります。

<I2 Localizationを導入する前の下準備>

I2Localizationをプロジェクトにimportするには、AssetStoreで購入したらAssetStoreなりPackageManagerなりで行います。このassetは大した容量じゃないので、丸ごと全部importしても問題ないです。
ちょっと面倒ですが、導入にあたって下準備をしておきます。
①I2 Localizationを試すためのUnityシーン
②GoogleのWebServiceのインストール(Googleアカウントが必要です。スプレッドシートを使ったり、asset上でGoogle翻訳を使うのに必要です)

■UIのTextフィールド

I2 Localizationを一番分かりやすく試すには、Unityの適当なシーンにUIからTextのGameObjectを選んでテキストフィールドを用意します。
別にテキストフィールドだけでもI2 Localizationの機能を確認することはできるのですが、UIっぽくするためにimageオブジェクトを敷いてSource ImageにUISpriteを選びました。何というか、ゲームのキャラクターの吹き出しみたいな雰囲気です。やる気を出すには雰囲気は重要です。w

■I2 Localizationを設定する場所

Unityのメニューから、Tool>I2 Localization>Open I2 Language.assetを選びます。(この設定画面のリソースは、Assets>Resorcesフォルダの中にあるI2Languagesというファイルです。でもToolメニューから開く方法の方が覚えやすいと思います)
すると、InspectorにI2 Localizationの設定画面が表示されます。Spreadsheets、Terms、Languages、Tools、Assetsと大きなタブがあります。

■Google Web Serviceの設定

先ほど開いたI2 Localizationの設定画面で、Spreadsheetsタブを開き、Googleと書かれた子タブを開きます。
その中にInstallと書かれたボタンがあるので押します。

すると、Google Apps Scriptの画面が開きます。ちょっと新しいエディタでのやり方が分からないので、「閉じる」ボタンを押して古いエディタを選びます。(後で新しいGASのエディタに切り替えたり、古いエディタに戻したりできるので心配ないです)

ファイルメニューから「公開」>「ウェブアプリケーションとして導入」を選びます。

Deploy as web appとダイアログが出るのですが、このまま暫く放置します。

しばらく放置しておくと、勝手にこんなウィンドウに変わります。(人によっては日本語で書かれてるかもしれません)
そのまま「更新」ボタンを押します。

その上にダイアログが重なって表示されるので、「許可を確認」ボタンを押します。

別ウィンドウが開いてGoogleアカウントの選択を要求されるので、自分のGoogleアカウントの中で使いたいアカウントを選びます。(例では1つしか無いので1択ですね)

すると、その別ウィンドウがドキッとするような警告に変わるのですが、焦らず左下の「詳細」というテキストリンクを押します。ちなみにデベロッパーは、自分で選んだ自分のアカウントになっているはずです。

そのウィンドウの中で表示が上図のように変わるので、「〜に移動」と書かれたテキストリンクを押します。

最後に、その別ウィンドウが上図のように変わるので、別ウィンドウ内を下にスクロールして「許可」ボタンを押します。

別ウィンドウが閉じられると、元のGASのウィンドウに出ていたDeploy as web appダイアログが上図の様に変わるので、Current web app URL:下のURLをコピーしておきます。(次の工程でUnityに設定する前に失くすと面倒なので、メモ帳などにペーストしておくと良いです)

最初にInstallボタンを押したI2 LocalizationのSpreadsheets>GoogleタブのWeb Service URL:のフィールドに、コピーしたCurrent web app URLをペーストして今度はVerifyボタンを押します。
長かったですが、これでGoogle Webserviceの設定は終わりです。
ちなみに、前の作業で開いてたGoogle App Scriptの画面は、もう設定済みなのでOKボタンを押して閉じちゃって大丈夫です。余程のことが無ければ、もうI2 Localizationの作業で開くことは無いでしょう。

試しにGoogleスプレッドシートを開いてみましょう。中段のOpenボタンで開きます。Password欄は適宜入力しておきましょう。
画面右下のAuto Update FrequencyやIn-Editor Check Frequencyは、Google Driveとの接続の頻度を設定するところです。Neverにしておけば、自分がI2 LocalizationからOpen、Import、Exportなどで繋いだ時だけ接続になります。
Update Synchronizationは、私は勝手にやられるのが苦手なのでManualにしてます。

開いたGoogleスプレッドシートは、こんな感じになります。まだ何も内容を登録してないので空っぽですね。
スプレッドシートのファイル名は、Unityで保存したプロジェクト名がベースになるようです。

<I2 Localizationの導入>

下準備に時間がかかりましたが、ここからI2 Localizationの内容の方の設定です。

1) 対応する言語の登録

まず最初にLanguagesタブを開いて、対応する言語を追加します。デフォルトはEnglishしかありません。
言語を追加するには、中断にあるテキストフィールドに適当な言語名を途中まで入力すれば、下のプルダウンメニューで候補を選んでくれます。目的の言語が出てきたら、その右端にあるAddボタン(プルダウンメニューの右端のもの)を押せば、上のリストに言語が追加されます。
リストに並んだ言語の順番は、言語の右隣にある▲▼ボタンで上下に移動できます。この順番で重要になるのは、最初の言語です。これは、一番下にあるDefault Language:でデフォルトの言語を選ぶ基準をDevice Language(端末の言語)にするか、First in List(リストの最初の言語)にするかを選べます
例えば、世界中の全言語に対応するなんてやりたくないので、対応したい国以外は英語を表示させたいなんて要望があると思います。そんな時は、Englishを一番上にしておいてFirst in Listを選べば、言語対応してない国のデバイスでは英語が表示されるというワケです。
あと、後述するGoogleスプレッドシートでの列の順番が、ここで設定した順番になります。

2) 翻訳する内容

Languageタブで言語を追加したら、次はTermsタブを開いて実際に翻訳する内容(文章や言葉)を追加していきます。
画面の左にある+ボタンを押して、キー作成の枠を表示させます。

この作業の後にテキストオブジェクトへ表示用のコンポーネントを取り付けるのですが、キーはそこから呼び出す内容(ここではテキスト翻訳)のセットです。
直接、キーの名前を書いても良いのですが、「/(スラッシュ)」で区切って入力することでカテゴリに分けることができます。例えば、Googleスプレッドシートでは左側の大カテゴリはシートで分けてくれます。また、Termsタブには追加された全内容が列挙されるのですが、カテゴリで分けておけば必要なカテゴリだけに絞ってフィルタ表示することもできます。必要に応じて区切ってみてください。
ここでは、例として「Dialog(セリフ)/Character(キャラ)/Owner_WeaponShop(武器屋の主人)」みたいに区切ってみました。武器屋の主人のセリフですね。主人のセリフが複数あるなら、/Owner_WeaponShop/001などと番号で分ければ良いです。
入力したらCreate Keyボタンでキーを加えます。(失敗してもDeleteやRenameで消したり直したりできます)

キーが生成されると、先ほど登録した言語の分のテキストフィールドを持ったキーの内容が開きます。
青いエリアの上部分には、先ほど区切ったキー名Owner_WeaponShopがタブで表示され、その右隣にはカテゴリ名Dialog/Characterがタブで表示されています。

ここで日本語(Japanese)のフィールドに武器屋の主人のセリフを適当に入力します。
その後、右下のTranslate Allボタンを押すと、残りの英語、タイ語、インドネシア語のフィールドにGoogleの翻訳の結果が入ります!
この機能は下準備のGoogle Web Serviceを設定しないと使えないので、苦労した甲斐があるってもんです。w
それぞれの言語のフィールドの右端にあるTボタンを使えば、言語ごと個別にGoogle翻訳を機能させることもできます。まぁ、Google翻訳なのでそのまま使えるかどうかは微妙ですが、翻訳のアタリを付けるには良いですね。

青く表示されたキーの内容は、キー名のタブを押すことで畳んだり開いたりすることができます。
例えば、他にもキーを登録してあって、先ほどの開いたキー内容を畳むと、こんな感じでリスト表示になります。もっとキーの数が多ければ分かりやすいですが、実は青いキーの内容表示はリスト欄の1つのキーの内容を広げた状態なのです。

あと、キーをカテゴリで区切っておけば、前述した通り右端にあるEverythingと書かれたプルダウンメニューで、必要なキーだけに絞ってフィルタ表示することもできます。このフィルタ機能は、Termsタブのキーが増えてリストの長さが膨大な数になった時に重宝します。

Termsタブでの翻訳セットの登録は、一旦完了です。

3)翻訳のセットをシーンのテキストコンポーネントに設定

いよいよ、下準備で作ったUnityのシーンの出番です。
テキストコンポーネントが入ったGameObjectを選択し、そのInspectorでAdd Componentを押して、I2 Localizeというコンポーネントを選んでアタッチします。
このコンポーネントに、先程まで設定したキーの内容(翻訳)を反映させます。
Term:のプルダウンをたどって、Owener_WeaponShopを選びます。

すると、このI2 LocalizeコンポーネントのTermがDialog/Character/Owner_WeaponShopに変更され、このTextコンポーネントにそのTermの内容が反映されます!
これで、このテキストフィールドは多言語対応したのです!!

って言われても、これだと日本語しか表示されないので有り難みが全く分からないですね。w

そこで、I2 Localizationのサンプルシーンに良い部品があるのでコピーしてきます。
AssetフォルダのI2>Localization>Examples>ScenesにI2Localization UnityUIというシーンがあるので開きます。
このシーンのCanvas>Localized Examples>Dropdown(下の方)を選んでコピーします。
(このシーン自体も再生して遊んでみると参考になります。テキストじゃなく、言語によって画像を変えるサンプルです)

自分で作った下準備のシーンに戻り、TextのGameObjectが入ったCanvasの中に適当にペーストします。
Unityを再生するとプルダウンメニューが機能するので、好きな言語を選んでみてください。
すると、セリフが設定したTermsの言語に変わります!!
これで、ちゃんとI2 Localizeが働いてることが分かりますね。実際のスマホやPCなどのデバイスでは、こんなプルダウンが無くても端末の言語に基づいて表示されます。逆に、このプルダウンメニューの仕組みを使えば、ユーザが意図的に言語を変更できるUIも作れるというワケです。

でも、勘のイイ方ならもうお気付きかと思いますが、「これじゃ1つのテキストフィールドに1つのTermしか割り当てられないから、セリフの数だけテキストフィールドを用意しなきゃならなくて使い物にならないじゃん!」と言いたくなりますよね?
もっと勘のイイ方なら「だったら、今までの面倒臭い設定なんてしないで、最初からI2 Localizeコンポーネントをアタッチして翻訳を入力すればイイじゃん」って思いますよね。
確かに、1つのテキストフィールドをPrefabにして、そのテキストフィールドをいろんなセリフで使い回さないと効率が悪すぎます。
大丈夫です、安心してください。物事には順番が必要です。そのための仕組みを次のステップで紹介します。

<I2 Localizationを使いこなす>

■1つのテキストフィールドをキーの定数で切り替える

1) Termを定数化する

「1つのテキストフィールドを複数のセリフで使い回す」ってことを実現するためには、スクリプトの力を使うことになります。

まずは何種類かのセリフを用意します。例として、魔法屋(Owner_MagicShop)と宿屋(Owner_Hotel)の主人のセリフをTermsに追加しました。

次に、これらのセリフを定数化します。
I2 Localizationの「Tools」タブを開き、子タブの「Script」を開きます。Termsタブのようなキーのリストが表示されるので、左下のAllボタンを押して全てのキーを選択状態にし、画面下のBuild Script with Selected Termsボタンを押します。

すると、AssetフォルダのルートにScriptLocalization.csというC#ファイルが生成されます。
確認のために中を覗くと、3つのセリフを定数で呼び出せるようになり、それらの定数は端末の言語に応じた翻訳の文字列を返すようになっていることが分かります。

2) 1つのテキストフィールドをキーの定数で切り替える

まず、導入の時にテキストフィールドにアタッチしたI2 LocalizeコンポーネントのTermを<none>か<inferred from text>に変更します。
これでコンポーネントに設定した特定のセリフではなく、スクリプトから指定するキーの定数を受け入れられるようになります。

あとは、テキストフィールドを複数のキーで差し替えられるようなスクリプトを作ればOKです。
キーを定数化すると何が良いって、コードヒントで選択できるようになるのでコーディングがめっちゃ楽です。(キーは文字列で呼び出すこともできますが、打ち間違いなどによるバグが発見しにくくなりますね)
上図のように、「ScriptLocalization.」と打つと、候補として先ほど自分で設定した「Dialog_Character」などが挙がってくるので選択します。同様に続けて、どの店の主人かも候補として挙がってくるので選びます。
これで、TALD_IDで選んだ主人のセリフが、設定した言語に応じて翻訳された状態で、I2 Localizeコンポーネントをアタッチしたテキストフィールドに表示されるようになります!

C#ファイルの頭にusing I2.Loc;を入れないとScriptLocalizationって打ってもエラーが出るので注意しましょう。

やっつけで恐縮ですが、サンプルのコードはこんな感じです。
本当はボタンとかで切り替えるようなサンプルコードを用意したかったのですが、すみません疲れました。言語とセリフはStart()で自分で変えてからシーンを再生して試してください。w

もし、やる気があれば、ボタンからStart_Talk()の引数にセリフを言う人物のIDを入れるようにすればOKですね。
それから、LocalizationManager.CurrentLanguageCodeという変数に文字列のコードを入れて言語を切り替えるコードを追加してありますが、言語も変えられるってことを確認するためなので本来は不要なコードですね。(本来はデバイスで使われてる言語が自動的に選ばれるので)
でも、これを応用すれば、プルダウンメニューでユーザが意図的に言語を変更できるUIも簡単に作れるってことです。言語コードは、I2 Localization設定画面の「Languages」タブの各言語の右に表示された「Code:」欄のものが使えます。
また、言語コード以外にLocalizationManager.CurrentLanguageとすれば、”English”のように言語名で指定することもできます。

先ほどのコードを下準備のシーンで使うとしたら、MainCameraにでもAdd Componentして、I2 Localizeコンポーネントが入ったTextのGameObjectをTalk_TextのスロットにアタッチすればOK!
これで、やっとI2 Localizationでキャラクターのセリフ×翻訳を表示できるようになりました。

また、セリフ以外にもボタン内の文字とかダイアログの文字とか、翻訳したい箇所はたくさんあると思います。
そんな時は、I2 Localizeコンポーネントをアタッチしたテキストフィールドをそれぞれの場所に作り、先ほどのサンプルコードのようにそのテキストフィールドを個別に読み込んで、そのテキストフィールドに適したScriptLocalization.[自分で設定した定数]を流し込めばセリフやボタンなど用途に応じて分けることができます。
1つのコントローラー(C#ファイル)にたくさんのテキストフィールドをまとめてしまうと、場所が増えた時に管理が大変なので、テキストフィールド毎にコントローラーを用意した方が良いかもしれませんね。

これにて、複数の翻訳(Termsのキー)を複数の場所(テキストフィールド)に適用できるようになりました!
そうなると翻訳も場所もどんどん増えるので、もっと翻訳の管理をしやすくするためにGoogleスプレッドシートを使うことについて、次でお話しします。

■Googleスプレッドシートでセリフを管理する

せっかくセリフを複数入力したので、Googleスプレッドシートに書き出してみましょう。(※この機能もTranslateと同じでGoogle Web Serviceの下準備をしていないと使えない機能です)

I2 Localization設定画面のSpreadsheets>Googleを開きます。
先程までの作業でセリフと翻訳はTerms内で入力してしまったので、Googleスプレッドシートを今Openしても空っぽのままです。Unityで入力した内容をGoogleスプレッドシートへ書き出すには、Export欄のボタンを押します。
今回はExport欄の「Replace」ボタンを押して、UnityのTermsのキーでGoogleスプレッドシート側を全て置き換えます。(もし、Googleスプレッドシート側にUnityではまだ入力していない文字がある場合は、全部上書きされて消えてしまうので注意してください)

すると、Termsタブで入力した3人の店主のセリフと各国語の翻訳が表の中に割り当てられています!
大カテゴリであるDialogは、Googleスプレッドシートのシートタブに割り当てられます。
中カテゴリとキーは、「_(アンダーバー)」で繋げられてKeys列に列挙されます。
B列以降は、Languagesタブで並べた順番に登録した言語が並びます。
セリフや翻訳を修正したい場合は、UnityのI2 Localization設定画面で修正するよりGoogleスプレッドシートの方が遥かにやりやすいですね。

もし、Googleスプレッドシート側で文字列を修正したら、それをUnity側へ反映します。
今度は、I2 Localization設定画面のSpredsheets>GoogleタブのImport欄の「Replace」ボタンを押します。これでスプレッドシートの内容で、Unity側のTermsのキーが全て上書きされます。
私は差分が分からなくなると嫌なので、どちらかだけで作業をしてImportもExportもReplaceを使っていますが、複数人数などの制作体制ではMergeやAdd Newボタンの方が都合が良いかもしれませんね。

これでI2 Localizationを文字列の翻訳でパーフェクトに使いこなせるようになりました!
最後に、TextmeshProをI2 Localizationで使う方法を説明して締めにしたいと思います。

<I2 LocalizationにTextmeshProを使う>

TextmeshProはアトラス画像に文字を焼くので解像度によっては美しい文字を表示することができ、使う文字を予め画像に焼くのでデバイスに対応するフォントを持ってなくてもUnityや検証端末上でフォントを表示させることができるのが特徴です。
昔、TextmeshProは$100以上もするassetでした。今ではUnityファミリーに入って誰でも無料で使えるので必要であれば使っちゃいましょう!(私はTextmeshProが無料化してから活用できるようになりました(涙))
I2 LocalizationはTextmeshProを使うこともできます。1つのフォントの範囲を超える文字、例えばタイ語やアラビア語のように特殊な文字を使う言語がある場合でも、I2 LocalizationでSDFを切り替えて使うように設定することができます。

TextmeshProの導入や使い方は別の話になってしまうので、既にUnityプロジェクトにTextmeshProをimport済みでTextmeshProは使ったことがあるという前提で話を進めます。

■TextmeshProを使う準備

まず、切り替え用のフォント(.ttfファイル)をプロジェクトに読み込んでおきます。今回は、タイ語以外をNotSansCJKjp-Medium.otfというフォント、タイ語をTHSarabunNew Bold.ttfというフォントをそれぞれ読み込みました。
NotoSansCJKjpは日本語だけでなく、韓国語や中国語の簡体字も行ける優秀なフォントです。

これらのフォントでTextmeshProのSDF(material形式のフォントアトラス)を作っておきます。
セリフは既にGoogleスプレッドシートで管理できるようになってるので、その文字をTextファイルにでもコピペしてUnityプロジェクトへimportすれば、TextmeshProのFont Asset CreatorでCharacters from Fileとして読み込むことができます。

次に、Termsタブでフォント用のキーを作ります。名前は分かりやすく「Font」とでもしておきます。
重要なのは、このキーのタイプです。「Type:」をプルダウンして「Text Mesh P Font」に変えます。
すると、今まで翻訳の文字を入れていたスロットがフォントを入れられるようになります。
今回はタイ語だけを別のSDFにするので、ThaiをTHSarabunNewBoldにしました。

もし、前作業のTermsタブでFontキーのType:をプルダウンしても「Text Mesh P Font」が現れない場合は、I2 Localizationがプロジェクトの中にTextmeshProの存在することを知らないので、Tool>I2 Localization>Enable Plugins>Force Detectionを選んで認知させましょう。
これで、Type:にText Mesh P Fontが現れるはずです。

■I2 LocalizeコンポーネントでTextmeshProを使う

先ほどまでTextを使ってたお試しシーンのTextオブジェクトを潔く削除し、今度はUI>Text -TextmeshProを選んでTextmeshProのテキストフィールドを作ります。

先ほどのサンプルコードを使うのであれば、TextmeshProを読み込むように修正します。

修正すると、Void Start()までの導入部分のコードはこうなります。

忘れがちですが、TextmeshProのテキストフィールドが入ったGameObjectで、修正したコンポーネントのTalk_Textスロットへアタッチし直します。

次に、TextmeshProの入ったGameObjectにI2 LocalizeをAdd Componetします。
今までと違うのは、Target:が「Prefab」ではなく「TextMeshPro UGUI」になっていると思います。(なっていなかったらプルダウンで選びます)

そして、今回はフォントを切り替えるので、中段の「Secondary」と書かれたタブをクリックします。
このTerm:をプルダウンして先ほどTermsで設定した「Font」に変えます。すると、各スロットが設定したSDFに振り分けれた状態で表示されます。

これでUnityを再生すると、Void Start()のLocalizationManager.CurrentLanguageCode = “th”にすれば、タイ語用に用意したSDFフォントTHSarabunNew Boldが表示され、それ以外の言語にするとNotoSansCJKjpのSDFフォントが表示されるようになりました!

<実際に使用した時のハマりポイントと回避>

すっごい便利でビルドしても何の問題も起きなかったI2 Localizationですが、XcodeからiPhoneへビルドした時に大問題が起きました。
動作確認をするべく、iOSの言語設定でタイ語やアラビア語に切り替えてアプリを起動すると強制終了したのです!I2 Localizationを入れていないバージョンでは強制終了するので、明らかにI2 Localizationをimportしたことが原因なのですが、困ったことにUnityのフォーラムや作者さんのフォーラムへ質問しても回答が得られず、、、。
こちらの解決策で強制終了を回避することができました!
同じようなことにハマった時は試してみてください。

<I2 Localizationを使用した自前のアプリ>

画像に alt 属性が指定されていません。ファイル名: 640_200_banner.jpg

さて、最後にI2 Localizationを使った自作アプリ「爆走!ドライビング スクール シミュレーター」の紹介です。
元々は、スクリプトに自前の言語切り替えを直接書いて対応していて、日本語・英語・中国(簡体字)・インドネシア語くらいまで何とかやりくりできていました。
しかし、タイやベトナム、台湾、マレーシアなどでも次々にアプリがダウンロードされるようになり始め、スクリプトに直接書いて対応言語を増やすのに限界を感じてI2 Localizationを導入しました。

I2 Localizationを導入した結果、タイとベトナム、台湾を伸ばすことができました!
特にタイは人口差4倍以上のインドネシアに近づくほど伸ばせました〜。
やっぱり、「少しでも伸びそう!」と思った時に素早く対応しないと、その国で英語が読めるユーザさん周辺くらいのブームで終わってチャンスを逃しちゃうんですよね。
もし、Unityで作ったアプリの多言語対応が面倒臭いなぁと思ったら、I2 Localizationの導入を検討してみてはいかがでしょうか?

以上で、「Unity アセット真夏のアドベントカレンダー2021 Summer!」の23日目の記事「I2 Localizationで複数言語に対応!」を終了します。
長文読んでいただき、ありがとうございました。

明日のアドベントカレンダーはシロフードさんの「PuppetMaster:キャラの頭を撫でて反応してもらう」です。
よろしくお願いします〜。