そういうのがいいブログ

アプリ個人開発 まるブログ

アプリ開発覚え書き

【Unity】多言語化パッケージ Localization 言語毎にフォントアセットを切り替える方法

環境

Unity 2022.3.15f1 (シリコン)
Localization 1.4.5
UniTask 2.5.0

はじめに

言語化パッケージ Localizationを使用して言語を切り替えることができますが
フォントが翻訳した言語に対応していないと表示ができません。

対応言語が日本語と英語だけなら特に問題ありませんが
アラビア語などに対応しようするとフォントアセットも
言語に応じて切り替える必要があります。

最適解ではないですが対応方法をメモしておきます。

テキストはTextMeshProを使用する前提で書きます。

注)メモが目的なので雑に書いてしまっています。

方法

次の3ステップです。
1.フォントアセット作成
2.スクリプト作成
3.スクリプト割当て

1.フォントアセット作成

対応する言語に応じたフォントアセットを用意します。

marumaro7.hatenablog.com


2.スクリプト作成

スクリプトは2つ作成します。(UniTaskを使用します。(コルーチンで代用可))
・言語が切り替わったらフォントアセットを切り替えるスクリプト
・フォントアセットを切り替えたいTextMeshProを登録するスクリプト

・言語が切り替わったらフォントアセットを切り替えるスクリプト
FontManagerという名前で作成しました。

using System.Collections.Generic;
using UnityEngine;
using TMPro;
using UnityEngine.Localization.Settings;
using UnityEngine.Localization;
using System;
using UnityEngine.SceneManagement;
using Cysharp.Threading.Tasks;

public enum SupportedLanguage
{
    ar, // アラビア語
    bn, // ベンガル語(バングラ語)
    zh_Hans, // 簡体字中国語
    zh_Hant, // 繁体字中国語
    en, // 英語
    fr, // フランス語
    de, // ドイツ語
    hi, // ヒンディー語
    id, // インドネシア語
    it, // イタリア語
    ja, // 日本語
    ko, // 韓国語
    mr_IN, // マラーティー語
    pt, // ポルトガル語
    ru, // ロシア語
    es, // スペイン語
    ta, // タミル語
    te, // テルグ語
    tr, // トルコ語
    ur // ウルドゥー語
}


// シリアライズ可能なクラスで、各言語と対応するTMP_FontAssetをマッピングします。
[System.Serializable]
public class FontAssetMapping
{
    public SupportedLanguage language; // SupportedLanguage 列挙型
    public TMP_FontAsset fontAsset;    // その言語のフォントアセット
}

// フォントを管理するためのマネージャークラス
public class FontManager : MonoBehaviour
{
    // シングルトンインスタンス
    public static FontManager Instance { get; private set; }

    // 各言語とフォントアセットのマッピング配列
    public FontAssetMapping[] fontMappings;

    // 登録されたTextMeshProUGUIコンポーネントのリスト
    [SerializeField] private List<TextMeshProUGUI> registeredTexts = new List<TextMeshProUGUI>();

    // 起動時の処理
    private void Awake()
    {
        // 既にインスタンスが存在していたら、重複を防ぐために破棄
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }

        // シングルトンインスタンスを設定
        Instance = this;

        // シーンを跨いでも破棄されないように設定
        DontDestroyOnLoad(gameObject);


        // ロケール変更イベントの購読
        LocalizationSettings.SelectedLocaleChanged += OnLocaleChanged;

        // 現在のロケールに基づいてフォントを設定
        SetFontForCurrentLocale();

        // 新しいシーンが読み込まれたときのイベントに登録
        SceneManager.sceneLoaded += OnSceneLoaded;
    }


    public void SetFontForCurrentLocale()
    {
        // 現在のロケールを取得
        Locale currentLocale = LocalizationSettings.SelectedLocale;

        if (currentLocale != null)
        {
            // 現在のロケールに基づいてフォントを設定
            OnLocaleChanged(currentLocale);
        }
    }

    // TextMeshProUGUIコンポーネントを登録するためのメソッド
    public void RegisterTextMeshPro(TextMeshProUGUI tmp)
    {
        // 既にリストに含まれていなければ追加
        if (!registeredTexts.Contains(tmp))
        {
            registeredTexts.Add(tmp);
        }
    }

    // オブジェクトが破棄されるときの処理
    private void OnDestroy()
    {
        // イベントハンドラの登録解除
        LocalizationSettings.SelectedLocaleChanged -= OnLocaleChanged;
    }


    
    private async void OnSceneLoaded(Scene scene, LoadSceneMode mode)
    {
        // 0.2秒遅延 遅延させないと先に処理が実行されてしまってフォントアセットが切り替わらない。
        await UniTask.Delay(TimeSpan.FromSeconds(0.2f));

        // 遅延後の処理
        // 例: 現在のロケールに基づいてフォントを設定
        SetFontForCurrentLocale();

        // 必要に応じて他の処理をここに追加
    }
    

    private void OnLocaleChanged(Locale newLocale)
    {
        SupportedLanguage currentLanguage = (SupportedLanguage)Enum.Parse(typeof(SupportedLanguage), newLocale.Identifier.Code.Replace("-", "_"));
        TMP_FontAsset targetFontAsset = null;

        foreach (var mapping in fontMappings)
        {
            if (mapping.language == currentLanguage)
            {
                targetFontAsset = mapping.fontAsset;
                break;
            }
        }

        if (targetFontAsset != null)
        {
            foreach (var tmp in registeredTexts)
            {
                tmp.font = targetFontAsset;
            }
        }


    }

}


・フォントアセットを切り替えたいTextMeshProを登録するスクリプト
TMPFontRegistrarという名前で作成しました。

using UnityEngine;
using TMPro;

public class TMPFontRegistrar : MonoBehaviour
{
    private void Start()
    {
        // FontManagerが存在し、このオブジェクトにTextMeshProUGUIコンポーネントがある場合、
        // このTextMeshProUGUIをFontManagerに登録する
        var textMeshPro = GetComponent<TextMeshProUGUI>();
        if (FontManager.Instance != null && textMeshPro != null)
        {
            FontManager.Instance.RegisterTextMeshPro(textMeshPro);

            //途中でテキストがアクティブになった場合はフォントが適用されないため
            //フォント設定の関数を実行
            FontManager.Instance.SetFontForCurrentLocale();
        }
    }
}

3.スクリプト割当て

空のオブジェクトを作成しFontManagerのスクリプトを割り当て

FontMappingsの欄で
切り替えたい言語コード言語の選択と
言語コードに対応するフォントアセットを割り当てます。


あとは言語切り替えの対象としている
Localize String Eventを付けたオブジェクトに
TMPFontRegistrarのスクリプトを割り付けます。

※言語切り替えの設定方法は以下で解説しています。 marumaro7.hatenablog.com


確認

これで実行中に言語を切り替えると
フォントアセットが切り替わります。
シーン切り替えに対応していますので最初に読み込むシーンに
FontManagerを設置すると良いでしょう。

今回の方法は再生ボタンで実行しなければフォントアセットが切り替わりません。
そのため、テキスト表示の確認方法は次の2つです。
・実行してから言語を切り替える
・オブジェクトのフォントアセットを一時的に手動で切り替える
確認が少し面倒ですが今はこれが一番手間が少ないと考えているので
私はこの方法で進みます。


今回の方法までに失敗したこと

TextMeshProのインスペクターにて
フォントアセットの欄にて右クリック
→LocalizePropertyをクリック
→追加されるGameObjectLocalizer
こちらを利用することで言語に応じたフォントアセットを切り替えることができますが
切り替えタイミングが言語と同じタイミングで行われるようで
文字化けして使用できませんでした。