【Unity】多言語対応をスプレッドシートで管理するシステム

多言語対応の問題点

UnityではTextMeshProなどを用いて文字を表示できます。
しかし多言語に対応する場合、例えば↓のようなスクリプトを書かなくてはいけません。

var lang = Application.systemLanguage;
if (lang == SystemLanguage.Japanese)
{
    text.text = "ヘンゼルのパンくず";
}
else if (lang == SystemLanguage.English)
{
    text.text = "English";
}

でもこれは大変面倒ですし、今後言語追加された場合など変更に弱いです。
さらに、文字列を企画担当の人が変更できないというのも問題になります。

対応方針

そこで今回は↓のように言語別の文字をにして管理し、

ラベル 日本語 英語
Title ヘンゼルのパンくず breadcrumbs
Menu メニュー Menu
Start ゲームスタート Start Game

スクリプトからは↓のように言語に寄って書き換えが必要ないシステムを作成します。

text.text = LanguageUtil.GetText(LangTextLabel.Title, Application.systemLanguage);

3分クッキング

ご自身で使う際は、コードをコピペしてご利用ください。
まずは、対応する言語リストを定義します。

using UnityEngine;

public static class AvailableLanguageList
{
    public static SystemLanguage[] AvailableLanguages = new SystemLanguage[]
    {
        SystemLanguage.Japanese,
        SystemLanguage.English,
    };
}

続いて、スプレッドシートに入力された言語ラベルをEnumに変換するスクリプトを作成します。
このファイルはEditorという名前のフォルダに入れると良いです。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using System;

public class LangTextEditorExtention : MonoBehaviour
{
    [MenuItem("多言語対応/言語シート変換")]
    public static void CreateLangEnumFile()
    {
        var resultStr = "";
        resultStr += "public enum LangTextLabel\n{\n";

        //プログレスバー表示
        float progress = 0;
        EditorUtility.DisplayProgressBar("変換中...", "チョットマッテネ", progress);

        var langTextAsset = Resources.Load<TextAsset>("LanguageText");
        var reader = new StringReader(langTextAsset.text);
        var lineCount = 0;
        while (reader.Peek() != -1)
        {
            var line = reader.ReadLine().Split(',');
            if (lineCount == 0)
            {
                // ヘッダーは読まない
                lineCount++;
                continue;
            }

            var label = line[0];
            var labelSp = label.Split('.');
            for (int i = 0; i < labelSp.Length; i++)
            {
                // 頭文字を大文字に
                var chars = labelSp[i].ToCharArray();
                var initial = char.ToUpper(chars[0]);
                chars[0] = initial;
                labelSp[i] = new string(chars);
            }
            var enumKey = string.Join("", labelSp);
            resultStr += $"    {enumKey},\n";

            lineCount++;
        }

        resultStr += "}";

        progress = 0.5f;
        EditorUtility.DisplayProgressBar("変換中...", "チョットマッテネ", progress);

        var stream = new StreamWriter($"{Application.dataPath}/Script/AutoGenerate/LangTextLabel.cs");
        stream.WriteLine(resultStr);
        stream.Flush();
        stream.Close();

        AssetDatabase.Refresh();

        LanguageUtil.Init();

        progress = 0.8f;
        EditorUtility.DisplayProgressBar("変換中...", "チョットマッテネ", progress);

        // チェック
        foreach (SystemLanguage lang in AvailableLanguageList.AvailableLanguages)
        {
            foreach (LangTextLabel label in Enum.GetValues(typeof(LangTextLabel)))
            {
                Debug.Log($"{lang} {label} : {LanguageUtil.GetText(label, lang)}");
            }
        }

        progress = 1f;
        EditorUtility.DisplayProgressBar("変換中...", "チョットマッテネ", progress);
        EditorUtility.ClearProgressBar();
    }
}

最後に、変換したEnumをもとに文字列を取得するためのユーティリティを作成します。

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;

public static class LanguageUtil
{
    private static bool hasInit = false;
    private static Dictionary<SystemLanguage, Dictionary<string, string>> langDict = null;
    private static SystemLanguage[] availableLanguages = new SystemLanguage[]
    {
        SystemLanguage.Japanese,
        SystemLanguage.English,
    };

    public static void Init()
    {
        langDict = new Dictionary<SystemLanguage, Dictionary<string, string>>();
        foreach (SystemLanguage lang in availableLanguages)
        {
            langDict.Add(lang, new Dictionary<string, string>());
        }

        var langTextAsset = Resources.Load<TextAsset>("LanguageText");
        var reader = new StringReader(langTextAsset.text);
        var lineCount = 0;
        while (reader.Peek() != -1)
        {
            var line = reader.ReadLine().Split(',');
            if (lineCount == 0)
            {
                // ヘッダーは読まない
                lineCount++;
                continue;
            }

            var label = line[0];
            var labelSp = label.Split('.');
            for (int i = 0; i < labelSp.Length; i++)
            {
                // 頭文字を大文字に
                var chars = labelSp[i].ToCharArray();
                var initial = char.ToUpper(chars[0]);
                chars[0] = initial;
                labelSp[i] = new string(chars);
            }
            var langTextEnumKey = string.Join("", labelSp);

            for (int i = 0; i < availableLanguages.Length; i++)
            {
                var text = line[i + 1];
                langDict[availableLanguages[i]].Add(langTextEnumKey, text);
            }

            lineCount++;
        }

        hasInit = true;
    }

    public static string GetText(LangTextLabel label, SystemLanguage language)
    {
        if (!hasInit)
            Init();

        return langDict[language][label.ToString()];
    }
}

以上で準備完了です。

使い方

ステップ1 スプレッドシートを作成

f:id:tmls:20220306134604p:plain

こんな感じのスプレッドシートもしくはエクセルファイルを作成します。

1列目(ラベル):Enumに変換される言語ラベルです。.を使って文字を区切ることもできます。
2列目以降:各言語に対応する文字列です。言語の順番は`AvalableLanguageListで定義した順にしてください。
備考:対応言語以降の行は自由に使ってください。

ステップ2 スプレッドシートをUnityにインポート

f:id:tmls:20220306135135p:plain

  1. スプレッドシート左上から、ファイル > ダウンロード > カンマ区切り形式(.csv)を選択してください。
  2. ダウンロードされたファイルをAssets/Resourcesフォルダに入れてください。
  3. ファイル名をLanguageTextに変更して下さい。

ステップ3 言語シート変換

Unityの上部のバーから、多言語対応 > 言語シート変換を選択してください。

f:id:tmls:20220306133829p:plain

ステップ4

スクリプトから下記の要領で言語別のテキストを取得できます。
EnumキーLangTextLabelはステップ3で自動生成されたものです。

var text = LanguageUtil.GetText(LangTextLabel.MenuStageTitle, Application.systemLanguage);

最後に

なにかあればコメントでお知らせください。