Unityでローカライズしたい。Textの、多言語対応ってどうやるのかな。


検索しても実装してるスクリプト見当たらないなって思ったけど
もう少し検索していると、

Unityのチュートリアルの中に
「ライブセッション：ローカライゼーションツール」というのがあったので
ここのスクリプトを使ってみる（動画はみてません）

[Localized Text Component - Unity](https://unity3d.com/jp/learn/tutorials/topics/scripting/localized-text-component?playlist=17117)

* LocalizationData.cs
* LocalizationManager.cs
* StartupManager.cs
* LocalizedText.cs
* LocalizedTextEditor.cs


## 処理を調整
###  (localizedText_en.json, localizedText_de.json) があったのでとりあえず、localizedText_ja.json も追加する。


```json:Assets/StreamingAssets/localizedText_ja.json
{"items":[{"key":"test1","value":"テスト"},{"key":"test2","value":"てすと。"}]}
```

###  StartupManager.cs の内容を、自分のプロジェクトの初期化のシーンで呼ぶようにする。


```csharp
        IEnumerator Start()
        {
            LocalizationManager.instance.LoadLocalizedText("localizedText_ja.json");
            yield return new WaitUntil(LocalizationManager.instance.GetIsReady);
            
            // 遷移とか
        }
```

###  LocalizedText.cs に、RequireComponentをつけておく。


```diff:LocalizedText.cs
+    [RequireComponent(typeof(Text))]
     public class LocalizedText : MonoBehaviour
```


TextMeshProUGUI だとこう。

```diff:LocalizedText.cs
+    [RequireComponent(typeof(TextMeshProUGUI))]
     public class LocalizedText : MonoBehaviour
```


多分そんな感じで使っていけば、良さそうです。
おわり。



以降は、なんかやりたいことをやっている・・なにかです（？）


##  (playしてない時)エディタ上でUIレアウト確認用に、日本語を入れておきたい。


OnValidate() で、キーが変更されるたびに、テキストの値も置き換えるようにする。
キーが間違ってると、"Localized text not found"
になるからわかりやすくてお気に入り。（っていうか、enum使えって話かな。）

![txt.gif](https://qiita-image-store.s3.amazonaws.com/0/365269/d519e37e-3d45-655a-8915-a3bdf4246e5b.gif)

<small>本当は、テキストの部分(TextMeshProUGUI.text)はインスペクタ上でさわれないようにしたい
[CustomEditor(typeof(TextMeshProUGUI))] すればいいのかな・・。</small>


<details><summary>（clickで開く LocalizedText.cs）
</summary><div>

```sharp:LocalizedText.cs
using TMPro;
using UnityEngine;

namespace Localization
{
    /// <summary>
    ///  キー設定用
    /// </summary>
    [RequireComponent(typeof(TextMeshProUGUI))]
    public class LocalizedText : MonoBehaviour
    {
        [SerializeField] string key;
        public string GetKey => key;

        private TextMeshProUGUI _ui;
        private TextMeshProUGUI UI => _ui ?? GetComponent<TextMeshProUGUI>();
        private string value;

#if UNITY_EDITOR
        public void OnValidate()
        {
            if (UI != null) UI.text = LocalizationManager.GetLocalizedValue(key);
        }
#endif

        public void Awake()
        {
            if (string.IsNullOrEmpty(value))
            {

                var txt = LocalizationManager.GetInitializeValue(key);
                UI.text = txt;
            }
            else UI.text = value;
        }

        /// <summary>
        /// キー変更
        /// </summary>
        public void TextWithKey(string key, params object[] args)
        {
            this.key = key;
            Text(args);
        }

        /// <summary>
        /// テキストを設定する
        /// </summary>
        public void Text(params object[] args)
        {
            string format = LocalizationManager.GetLocalizedFormat(key);
            value = string.Format(format, args);
            UI.text = value;
        }
    }
}
```

</div></details>


###  (string format)と(bool 初期値として使うかどうか)を追加する


```diff:LocalizationData.cs

+        /// <summary>
+        /// The key.
+        /// </summary>
        public string key;
+        /// <summary>
+        /// The format.
+        /// </summary>
+        public string format;
+        /// <summary>
+        /// The value.
+        /// </summary>
        public string value;
+        /// <summary>
+        /// true:実行時にはvalue入れないで空にする。UIレアウト確認用。false:実行時もそのまま使う
+        /// </summary>
+        public bool value_is_test;
```


```diff:LocalizationManager.cs
     public static class LocalizationManager
     {
-        private static Dictionary<string, string> localizedText;
+        private static Dictionary<string, LocalizationItem> localizedText;
         private static readonly string missingTextString = "Localized text not found";
+        private static readonly string missingFormatString = "{0}";

```

全体としてはこう。
StreamingAsset やめて Resources.Load にして楽してる。
のちのち、ロードは変更すると思う。
<details><summary>（clickで開く LocalizationManager.cs）
</summary><div>

```csharp:LocalizationManager.cs
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace Localization
{
    /// <summary>
    /// Localization manager.
    /// </summary>
    public static class LocalizationManager
    {
        private static Dictionary<string, LocalizationItem> localizedText;
        private static readonly string missingTextString = "Localized text not found";
        private static readonly string missingFormatString = "{0}";

        /// <summary>
        /// 準備状態を取得する
        /// </summary>
        public static bool IsReady { get; private set; } = false;

        /// <summary>
        /// 読み込み開始
        /// </summary>
        public static void LoadLocalizedText()
        {
            if (IsReady) return;
            LoadAsset();
        }

        /// <summary>
        /// 読み込み
        /// </summary>
        public static void LoadAsset()
        {
            string filePath = Path.Combine("Localization", "common_ja");
            string dataAsJson = Resources.Load<TextAsset>(filePath).text;
            CreateDictionary(dataAsJson);
        }

        /// <summary>
        /// 設定する
        /// </summary>
        private static void CreateDictionary(string dataAsJson)
        {
            localizedText = new Dictionary<string, LocalizationItem>();
 
            LocalizationData loadedData = JsonUtility.FromJson<LocalizationData>(dataAsJson);
            for (int i = 0; i < loadedData.items.Length; i++)
            {
                 localizedText.Add(loadedData.items[i].key, loadedData.items[i]);
            }

            Debug.Log("Data loaded, dictionary contains: " + localizedText.Count + " entries");

            IsReady = true;
        }

        /// <summary>
        /// ローカライズされた値を取得する
        /// </summary>
        public static string GetLocalizedValue(string key)
        {
#if UNITY_EDITOR
            if (string.IsNullOrEmpty(key)) return missingTextString;
            if (!UnityEditor.EditorApplication.isPlaying
            && localizedText == null) LoadAsset();
#endif
            if (!localizedText.ContainsKey(key)) return missingTextString;
            localizedText.TryGetValue(key, out LocalizationItem result);
            return result.value;
        }

        /// <summary>
        /// ゲーム中の初期値を取得する
        /// </summary>
        public static string GetInitializeValue(string key)
        {
            if (!localizedText.ContainsKey(key)) return missingTextString;
            localizedText.TryGetValue(key, out LocalizationItem result);
            if (result.value_is_test) return null;
            return result.value;
        }

        /// <summary>
        /// ローカライズされたテキストフォーマットを取得する
        /// </summary>
        public static string GetLocalizedFormat(string key)
        {
            localizedText.TryGetValue(key, out LocalizationItem result);

            if (string.IsNullOrEmpty(result?.format)) return missingFormatString;
            return result.format;
        }

    }
}

```
</div></details>


##   1ファイルはしんどいから、ファイル分割したい。

あとは、

とっても大きなファイルになりそうなので
ファイル分けが出来ると、幸せになれると思う。


```diff:LocalizationManager.cs
         private static void LoadAsset()
         {
-            string filePath = Path.Combine("Localization", "common_ja");
-            string dataAsJson = Resources.Load<TextAsset>(filePath).text;
-            CreateDictionary(dataAsJson);
+            var txt = Resources.LoadAll<TextAsset>("Localization/ja");
+            CreateDictionary(txt);
         }
         
         /// <summary>
         /// 設定する
         /// </summary>
-        private static void CreateDictionary(string dataAsJson)
+        private static void CreateDictionary(TextAsset[] txt)
         {
             localizedText = new Dictionary<string, LocalizationItem>();
+            var objs = txt.Select(o => JsonUtility.FromJson<LocalizationData>(o.text));
 
-            LocalizationData loadedData = JsonUtility.FromJson<LocalizationData>(dataAsJson);
-            for (int i = 0; i < loadedData.items.Length; i++)
-            {
-                localizedText.Add(loadedData.items[i].key, loadedData.items[i]);
-            }
+            foreach (var o in objs)
+                foreach (var item in o.items)
+                    localizedText.Add($"{o.prefix}-{item.key}", item);
```


```diff:LocalizationData.cs
    public class LocalizationData
    {
+        /// <summary>
+        /// The prefix.
+        /// </summary>
+        public string prefix;
        /// <summary>
        /// The items.
        /// </summary>
        public LocalizationItem[] items;

```

いい感じ。