Unityで多言語対応...?
Unityでの多言語対応のベストプラクティスってあるんですかね?
Unity側で用意されている何かがあったり、こうするといいよ的な何か?
今でも結局自力で解決する問題なのですかね??
ってことで、
Unityで多言語対応をちゃっと考えてみた
です(笑)
別に自分で直近で必要に迫られているとかじゃなくて、なんとなく今ふと思ったので考えてみただけという。。
(Unityで遊ぶようになってここら全くやってなかったから...(笑))
軽くググッてみてもみんないろんな方法でやってますね〜。
僕もそのテーマで投下して、使用者がどれ選べばいいんや...って迷わせるのに貢献したいとおもいます!😎
環境
- Unity v2018.1.0f2
目指すもの
- なるべく簡単に設定できたらいいよね
- 設定自体は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上にでてこないですからね...(汗)
実際につくってみるとこういう感じ
上の奴らを使って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
をセットして...あとは実行するだけ!
わりと最初に立てた目標は達成できた感ある!
実際にやっていくときは...
ScriptableObject作ってテキスト書いてコンポーネント貼って作ったScriptableObjectを設定すると..
簡単!(数多いとめんどいけど簡単!)
Unityエディタだけでドンドン追加できる!
こういう感じで設定ファイルがドンドン増えていくけどね!
画像も同じよう要領でできるはずです〜
成果物
よいかんじ💪
いや使う予定ないんだけどね...(笑)
なんかあった!
みてたら、公式でもちゃんとありますね!
確認バージョンがv5.5ってあるから、もしかするとそのままだと動かないとこあるかもだけど、ちゃんとあるやーん!!
ま、そりゃあるよね...ふふふ...😇