【Unity】【Localization】スクリプトで操作する方法総まとめ

UnityのLocalizationパッケージの各処理をスクリプトで操作する方法についてまとめました。

Unity2020.3.15f2
Localization 1.2.1

はじめに

この記事ではUnityのLocalizationパッケージにおいて、スクリプトで色々な操作を行う方法についてまとめます。
実際に使いそうな操作のみをまとめています。後で追記するかもしれません。

なおLocalizationパッケージの基礎知識については以下の記事にまとめていますので、必要に応じて参照してください。

light11.hatenadiary.com

初期化する・初期化を待機する

Localizationパッケージでは初期化処理として、現在アクティブになっている言語に関するリソースのうちプリロード対象のものをダウンロードしたりロードしたりといったことが行われます。

この初期化処理はアプリを立ち上げた時や、選択中のLocaleが変わった時に自動的に開始されます。
しかしながらこの処理は非同期で行われるため、以下のように終了を待機する必要があります。

using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Localization.Settings;

public sealed class Example : MonoBehaviour
{
    private async Task WaitForInitialization()
    {
        await LocalizationSettings.InitializationOperation.Task;
    }
}

ちなみにProject Settings > Localization > Initialize Synchronouslyにチェックを入れると同期的に初期化することもできます。
ただしWebGLでは使えません。
内部的にはAddressablesの機能が使われているので、詳細は以下を参照してください。

light11.hatenadiary.com

初期化しなおす

アセットバンドルを更新するときなど、初期化をやり直したい時にはまずLocalizationSettings.Instance.ResetState()を呼びます。
このメソッドは初期化状態をリセットし、全てのテーブルをアンロードします。

その上でLocalizationSettings.InitializationOperationにアクセスすると自動的に初期化が始まり、そのまま完了を待機できます。

using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Localization.Settings;

public sealed class Example : MonoBehaviour
{
    private async Task Reinitialize()
    {            
        // Reset initialization state. (Unload all tables)
        LocalizationSettings.Instance.ResetState();
            
        // Initialize LocalizationSettings.
        await LocalizationSettings.InitializationOperation.Task;
    }
}

なおこの辺りのAPIは今後変わるかもしれません。
詳細は以下の記事を参照してください。

light11.hatenadiary.com

Localeを変更する

Localeは以下のようにして変更します。
変更後には上述の初期化処理が走るため、終了を待つ必要がある点に注意してください。

using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Localization;
using UnityEngine.Localization.Settings;

public sealed class Example : MonoBehaviour
{
    private async Task ChangeSelectedLocale()
    {
        // Set locale with locale code.
        LocalizationSettings.SelectedLocale = Locale.CreateLocale("en");

        // Wait initialization.
        await LocalizationSettings.InitializationOperation.Task;
    }
}

テーブルをプリロードする

Localizationパッケージにおいて、エディタからプリロード対象として設定したテーブルに関しては初期化時にプリロードされます。
プリロード対象としなかったテーブルに関しては、自身でプリロードを行うことができます。

String TableAsset Tableでインターフェースが異なるので注意してください。

using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Localization.Settings;

public sealed class Example : MonoBehaviour
{
    private async Task Preload()
    {
        // Preload string table.
        await LocalizationSettings.StringDatabase.PreloadTables("String Table Name").Task;

        // Preload asset table.
        await LocalizationSettings.AssetDatabase.PreloadTables("Asset Table Name").Task;
    }
}

プリロードの仕組みの詳細については以下の記事を参照してください。

light11.hatenadiary.com

テーブルを取得する

テーブルを取得するメソッドには同期メソッドと非同期メソッドの二つがあります。
事前にプリロードされているテーブルであれば同期メソッドで取得できます。
一応プリロードされていなくても同期的に取得はできますが、ダウンロードなど重い処理が走る可能性があるのと、例の如くWebGLだと使えないので注意が必要です。

using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Localization.Settings;

public sealed class Example : MonoBehaviour
{
    private async Task GetTable()
    {
        var targetTableName = "String Table Name";

        // Preload
        await LocalizationSettings.AssetDatabase.PreloadTables(targetTableName).Task;

        // Get
        var table = LocalizationSettings.StringDatabase.GetTable(targetTableName);

        var value = table.GetEntry("Foo Entry").Value;
    }
}

以下のように非同期的に待機することもできます。
プリロードや初期化が終わっていない状況でテーブルを取得する可能性があるのであればこうした方がいいかと思います。

using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Localization.Settings;

public sealed class Example : MonoBehaviour
{
    private async Task GetTable()
    {
        var targetTableName = "String Table Name";

        // Get asynchronously
        var table = await LocalizationSettings.StringDatabase.GetTableAsync(targetTableName).Task;

        var value = table.GetEntry("Foo Entry").Value;
    }
}

エントリを取得する

エントリを取得する方法にもいくつか種類があります。
取得済みのテーブルからエントリを取得する方法は以下の通りです。

using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Localization.Settings;

public sealed class Example : MonoBehaviour
{
    private async Task GetEntry()
    {
        var targetTableName = "String Table Name";
        var table = await LocalizationSettings.StringDatabase.GetTableAsync(targetTableName).Task;

        // Get Entry
        var value = table.GetEntry("Foo Entry").Value;
    }
}

テーブル名とエントリ名を指定して直接エントリを取得することもできます。
以下は同期メソッドの例です。

using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Localization.Settings;

public sealed class Example : MonoBehaviour
{
    private void GetEntry()
    {
        var targetTableName = "String Table Name";

        // Get Entry
        var entry = LocalizationSettings.StringDatabase.GetTableEntry(targetTableName, "Foo Entry").Entry;

        var value = entry.Value;
    }
}

例の如く非同期メソッドも用意されています。

using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Localization.Settings;

public sealed class Example : MonoBehaviour
{
    private async Task GetEntry()
    {
        var targetTableName = "String Table Name";

        // Get Entry
        var entryResult =
            await LocalizationSettings.StringDatabase.GetTableEntryAsync(targetTableName, "Foo Entry").Task;

        var value = entryResult.Entry.Value;
    }
}

テーブルを強制的に解放する

さてLocaleを切り替えたときには、不要になったLocaleのTableは全て開放され、アンロードされます。
しかし自身でプリロードして厳密にメモリ管理をしたい場合、テーブルを解放する必要も出てくるでしょう。

テーブルの解放は以下のように行います。

using UnityEngine;
using UnityEngine.Localization.Settings;

public sealed class Example : MonoBehaviour
{
    private void ReleaseTable()
    {
        var targetTableName = "String Table Name";
        LocalizationSettings.StringDatabase.ReleaseTable(targetTableName);
    }
}

また上述の通り、LocalizationSettings.Instance.ResetState()を呼んだときにもテーブルは解放されます。

ただあくまで解放処理を行うだけなので、テーブルのエントリへの参照があった場合にはアンロードされないので注意してください。

ローカライズされた文字列をシリアライズする

以下のようにLocalizedStringクラスをシリアライズすると、ローカライズ文字列をInspectorから設定できます。
Localeや文字列が切り替わった際にはLocalizedString.StringChangedで変化した内容を受け取れます。

public sealed classExample: MonoBehaviour
{
    [SerializeField] private LocalizedString _example;

    private string _text;

    private voidOnEnable()
    {
        _example.StringChanged += UpdateString;
    }

    private voidOnDisable()
    {
        _example.StringChanged -= UpdateString;
    }

    private voidOnGUI()
    {
        EditorGUILayout.LabelField(_text);
    }

    private void UpdateString(string str)
    {
        _text = str;
    }
}

このスクリプトをアタッチすると、Inspectorには下図のように表示されます。

f:id:halya_11:20220310195932p:plain
Inspector

なおSmartStringを使っていて、なおかつその引数を動的に設定する場合には、以下のように引数を変更した際にLocalizedString.RefreshString()を呼ぶ必要があるのでご注意ください。

using UnityEditor;
using UnityEngine;
using UnityEngine.Localization;
using UnityEngine.SceneManagement;

public sealed class Example : MonoBehaviour
{
    [SerializeField] private LocalizedString _example;

    private string _text;

    private void OnEnable()
    {
        SetArgument();
        _example.StringChanged += UpdateString;
    }

    private void OnDisable()
    {
        _example.StringChanged -= UpdateString;
    }

    private void OnGUI()
    {
        EditorGUILayout.LabelField(_text);

        if (GUILayout.Button("Change Argument"))
            SetArgument();
    }

    private void SetArgument()
    {
        var rootGameObjects = SceneManager.GetActiveScene().GetRootGameObjects();
        var obj = rootGameObjects[Random.Range(0, rootGameObjects.Length)];
        // 引数を更新
        _example.Arguments = new object[] { obj };
        // 引数の更新を反映
        _example.RefreshString();
    }

    private void UpdateString(string str)
    {
        _text = str;
    }
}

ローカライズされたアセットをシリアライズする

ローカライズされたアセットも前節の文字列と同様の方法でシリアライズできます。
この場合には以下のように、LocalizedStringの代わりにLocalizedAssetクラスを使用します。

using UnityEditor;
using UnityEngine;
using UnityEngine.Localization;

public sealed class Example : MonoBehaviour
{
    [SerializeField] private LocalizedAsset<Sprite> _example;

    private string _text;

    private void OnEnable()
    {
        _example.AssetChanged += UpdateAsset;
    }

    private void OnDisable()
    {
        _example.AssetChanged -= UpdateAsset;
    }

    private void OnGUI()
    {
        EditorGUILayout.LabelField(_text);
    }

    private void UpdateAsset(Sprite texture)
    {
        _text = texture.name;
    }
}

なおマニュアルには具象クラスを定義するように書かれていますが、Unity2020.1以降はGenericな型をシリアライズできるので具象クラスを定義する必要はなさそうです。
マニュアルはおそらくUnity2019以前のバージョンに対応するための記述になっているものと思われます。

ローカライズしたアセットを動的に切り替える

ローカライズしたアセットを動的に切り替える方法については以下の記事にまとめています。

light11.hatenadiary.com

関連

light11.hatenadiary.com

light11.hatenadiary.com

light11.hatenadiary.com

参考

docs.unity3d.com