[Unity]多言語対応...?

Unityでの多言語対応のベストプラクティスってあるんですかね?
Unity側で用意されている何かがあったり、こうするといいよ的な何か?
今でも結局自力で解決する問題なのですかね??
ってことで、

Unityで多言語対応をちゃっと考えてみた

です(笑)

Unity v2018.1.0f2

別に自分で直近で必要に迫られているとかじゃなくて、なんとなく今ふと思ったので考えてみただけという。。
(Unityで遊ぶようになってここら全くやってなかったから...(笑))

軽くググッてみてもみんないろんな方法でやってますね〜。
僕もそのテーマで投下して、使用者がどれ選べばいいんや...って迷わせるのに貢献したいとおもいます!😎

目指すもの

  • なるべく簡単に設定できたらいいよね
  • 設定自体はUnityエディタだけで完結させたいな(コード触りたくない)
  • 言語切り替えたらちゃっと変更反映されるといいよね

あたりでしょうか?
これらを実現するためのシステムを...!

🤔

なるべく簡単に...?

ってことなので、やっぱり該当のGameObjectに対してComponentを貼っ付ける感じでしょうか?
ひとつひとつやるんで、めんどいんだけど簡単ではあるよね...めんどいけど(笑)

Unityエディタだけで完結...?

Dictionary<String, String>とかでどっかに記述していく感じだと、
新たに追加するときにコードをいじっていくことになるから...これじゃない。
かといって、作ったコンポーネント毎に直接言語別のテキストをSerializeFieldで持つと、
同じ文言でも違うGameObjectだった場合にそれぞれで書かなきゃだしめんどい...
ScriptableObjectか...。こいつで多言語の設定を持てば、
同じ文言であれば同じScriptableObjectを参照させれば済むし、
あらたに追加するときなんかは、UnityエディタでScriptableObjectを作っていけばいけそうだ...

切り替えたら変更反映...?

UniRxとかSystem.Reactiveとか使えるならそれで変更通知するけど...
UniRxに依存しちゃうとこういうUtil系としてはアレな気もするし(自分のプロジェクトは全部入ってるからいいけど(笑))、System.Reactiveはつかえんし...
System.Actionでいいか...

📝

アプリの言語設定を司るやつ

現在の言語が何かを記録したり、変更があった時に通知をしたり...をするやつ。
記録はPlayerPrefsに...
端末情報から設定されている言語を引っ張ってくるでもいいんだけど、、
それが必ずしも一致するわけでないからなー...。一定数居ると思うんですよ..
端末の言語は英語だけどアプリは日本語で...みたいな人(笑)
なので、アプリはアプリで持ったほうが良いとおもうんですよね。
変更通知はAction経由で...

public enum Lang
{
    en = 0,
    ja = 10
}

public class LangSetting
{
    private static readonly string PREFS_KEY = "Xyz.AnzFactory.Util.Localization.LangSetting.Lang";

    public System.Action OnChangedCurrentLang;
    private static LangSetting instance = new LangSetting();
    private Lang currentLang;

    public static LangSetting Instance
    {
        get { return instance; }
    }
    
    public Lang CurrentLang
    {
        get { return instance.currentLang; }
        set {
            if (instance.currentLang == value) {
                return;
            }
            instance.currentLang = value;
            OnChangedCurrentLang?.Invoke();	// 変更通知
            UnityEngine.PlayerPrefs.SetInt(PREFS_KEY, (int)instance.currentLang);
            UnityEngine.PlayerPrefs.Save();
        }
    }

    private LangSetting()
    {
        var langValue = UnityEngine.PlayerPrefs.GetInt(PREFS_KEY, (int)Lang.en);
        currentLang = (Lang)System.Enum.ToObject(typeof(Lang), langValue);
    }
}

多言語情報

多言語の情報そのものの入れ物。ScriptableObjectで実現。
言語とそれに結びつくテキストですかね

[CreateAssetMenu(menuName="LocalizationText")]
public class LocalizationTextData: ScriptableObject
{
    public System.Collections.Generic.List<LocalizationTextItem> items;

    public string GetText(Lang lang)
    {
        for (int i = 0; i < items.Count; i++) {
            var item = items[i];
            if (item.Lang == lang) {
                return item.Text;
            }
        }

        Debug.LogError($"not found localization data...");

        return "";
    }
}

[System.Serializable]
public class LocalizationTextItem
{
    public Lang Lang;
    public string Text;
}

ホントはDictionary<Lang, String>ぐらいで出来たらいいんですけど、
DictionaryはUnityのInspector上にでてこないですからね...(汗)

実際につくってみるとこういう感じ

LocalizationTextData

上の奴らを使ってTextを変えていく

LangSettingから現在の言語をとってきて、それに結びつくテキストをLocalizationTextDataから引っ張ってきてテキスト更新する!
という流れですか...

[RequireComponent(typeof(UnityEngine.UI.Text))]
public class TextLocalizer: MonoBehaviour
{
    [SerializeField] private LocalizationTextData data;

    private void Awake()
    {
        // 変更通知を受け取る
        LangSetting.Instance.OnChangedCurrentLang += OnChangedCurrentLang;
    }

    private void Start()
    {
        UpdateText();
    }

    private void OnDestroy()
    {
        // 通知受取解除
        LangSetting.Instance.OnChangedCurrentLang -= OnChangedCurrentLang;
    }

    private void OnChangedCurrentLang()
    {
        UpdateText();
    }

    private void UpdateText()
    {
        var target = gameObject.GetComponent<UnityEngine.UI.Text>();
        target.text = data.GetText(LangSetting.Instance.CurrentLang);
    }

}

これをUnityEngine.UI.Textコンポーネントが貼られているGameObjectに対して貼っていくと...
そして、必要なLocalizationTextDataをセットして...あとは実行するだけ!

TextLocalizer

わりと最初に立てた目標は達成できた感ある!

実際にやっていくときは...
ScriptableObject作ってテキスト書いてコンポーネント貼って作ったScriptableObjectを設定すると..
簡単!(数多いとめんどいけど簡単!)
Unityエディタだけでドンドン追加できる!

こういう感じで設定ファイルがドンドン増えていくけどね!

LocalizationDataFolder

画像も同じよう要領でできるはずです〜

成果物

Localization

よいかんじ💪
いや使う予定ないんだけどね...(笑)

なんかあった!

みてたら、公式でもちゃんとありますね!

確認バージョンがv5.5ってあるから、もしかするとそのままだと動かないとこあるかもだけど、ちゃんとあるやーん!!
ま、そりゃあるよね...ふふふ...😇

参考

anzfactory/Unity-Utils | GitHub