Unity Localizationを実用化してみよう。③Smartのチェックを一括化する【hako 生活の休日】

# 前回まで
前回はGoogleSpreadSheetからいつでもすべてのシートの情報を同期できるようにしました。

https://qiita.com/clrfnd/items/595e288d108bd26be368

# 動的文字列を便利に扱える、"SmartString"機能
Unity Localizationには、 Smart Stringという強力な動的文字列の処理機能があります。

https://docs.unity3d.com/ja/Packages/com.unity.localization@1.4/manual/Smart/SmartStrings.html

変数を文字列中に入れたい時など、動的な書式を表示するための便利な機能です。
例えば、
```
こんにちは。{login}回目のログインです。
```
という文字列を用意して、以下のコードで出力します。
``` C#
LocalizedString lstr = new LocalizeString("テーブルの名前","エントリーのID");
var value = new IntVariable();
value.Value = v;
lstr.Add(key, value);
lstr.RefreshString();
//こんにちは。10回目のログインです。
```

# 切り替え機能

セレクター・フォーマッターといった機能が用意されています。

https://docs.unity3d.com/ja/Packages/com.unity.localization@1.4/manual/Smart/Plural-Formatter.html

複数形の表記なども簡単に扱うことができます。

# Smart機能のチェック大変すぎ問題

さて、大変便利なSmart機能ですが、StringTableCollectionの設定が必要です。
ただ、設定の仕方がちょっと問題ありまして…、


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/704830/1710429c-e529-871f-3cbf-1d1ffe6dbfad.png)


**Smart機能を使いたい全てのセルに個別にSmartのチェックをしなければならない**というわりと恐ろしいUIになっており、頻繁に動的文字列を使うプロジェクトでは、かなり大変な仕様となっています。言語ごとにチェックを入れる必要があるため、使用箇所と言語数に合わせて、設定する工数は２次元的に増えてしまいます。

※ショートカットまたは一括設定できるデフォルトの機能があれば、コメントで教えていただけると幸いです…。

# 一括設定機能を作る

セルの情報であるStringTableEntryさえ取得できれば、Smart機能のチェックは簡単です。
``` C#
StringTableEntry entry;
entry.IsSmart = true;
```
まずは、StringTableCollectionを一括設定する汎用のメソッドを作ります。
パフォーマンスを考慮して、Smart機能を使うセルのみチェックを入れるべきでしょう。Smart機能の書式に使用される"{}"の文字列を含む場合のみSmartチェックを入れるような判定を行います。
``` C# LocalizeUtil.cs
#if UNITY_EDITOR
using UnityEditor.Localization;
using UnityEngine.Localization;
using UnityEngine.Localization.Tables;

/// <summary>
/// Localizeに関する汎用機能
/// </summary>
public static class LocalizeUtil
{
    //forceにチェックをいれた場合は文法チェックをスルーして、直接Smart化します

    public static void SetSmart(StringTableCollection collection, bool all)
    {
        var tables = collection.Tables;
        foreach (var table in tables)
        {
            var stable = table.asset as StringTable;
            foreach (var t in stable)
            {
                var str = t.Value.ToString();
                t.Value.IsSmart = all || (str.Contains('{') && str.Contains('}'));
            }
        }
    }
}
#endif
```

# すべてのStringTableCollectionにSmartチェックを入れる

複数シートで構成しているプロジェクトで、すべてのシートで一括設定をできるようにします。

``` C# StringTableCollectionList.cs
//StringTableCollectionを一括管理するScriptableObject
//このスクリプトは任意のEditorフォルダ以下に配置すること。
#if ODIN_INSPECTOR
// Odin Inspectorを使う前提ですが、なくても動きます。
using Sirenix.OdinInspector;
#endif
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Localization;
using UnityEngine;

[CreateAssetMenu(menuName = "Localization拡張/StringCollectionList")]
public class StringTableCollectionList : ScriptableObject
{
	public List<StringTableCollection> collections = new List<StringTableCollection>();

#if ODIN_INSPECTOR
	[Button]
#endif
    [ContextMenu("CheckSmartAll")]
	public void CheckSmartAll()
    {
		foreach (var collection in collections)
		{
			LocalizeUtil.SetSmart(collection, false);
		}
	}
}
```

作成したScriptableObjectのコンテキストメニューのCheckSmartAllから、スマートのチェックができるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/704830/a144b027-7935-a816-2ec4-dd02ef01d1a2.png)


# GoogleSpreadSheet連携機能との合体

前回の記事ではGoogle Spread Sheetの全取得を実装しました。

https://qiita.com/clrfnd/items/edf03f018e52063d57cd

こちらのStringTableCollectionのリストを編集して、スプレッドシートをプルした際に同時にSmart機能もチェックするように設定してみます。

``` C# StringTableCollectionList.cs
//StringTableCollectionを一括管理するScriptableObject
//このスクリプトは任意のEditorフォルダ以下に配置すること。
#if ODIN_INSPECTOR
// Odin Inspectorを使う前提ですが、なくても動きます。
using Sirenix.OdinInspector;
#endif
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Localization;
using UnityEditor.Localization.Plugins.Google;
using UnityEngine;

[CreateAssetMenu(menuName = "Localization拡張/StringCollectionList")]
public class StringTableCollectionList : ScriptableObject
{

	public string SpreadSheetID = "任意のSpreadSheetID";
	public SheetsServiceProvider ssp;
	public List<StringTableCollection> collections = new List<StringTableCollection>();

#if ODIN_INSPECTOR
	[Button]
#endif
    [ContextMenu("PullAll")]
//シートをPullするやつ
	public void PullAll()
    {
		var gss = new GoogleSheets(ssp);
		gss.SpreadSheetId = SpreadSheetID;
        //ループでそれぞれ取得（取得の是非の判定は割愛）
		foreach (var collection in collections)
		{
			var sheetsExtension = collection.Extensions.OfType<GoogleSheetsExtension>().FirstOrDefault();
			gss.PullIntoStringTableCollection(
				sheetsExtension.SheetId,
				collection,
				sheetsExtension.Columns,
				createUndo: true);

			//ここで同時にSmartの設定を行う
			LocalizeUtil.SetSmart(collection, false);
		}
	}
#if ODIN_INSPECTOR
	[Button]
#endif
    [ContextMenu("OpenSheet")]
//シートを開くやつ
	public void OpenSheet()
	{
		GoogleSheets.OpenSheetInBrowser(SpreadSheetID);
	}
}


#if ODIN_INSPECTOR
	[Button]
#endif
    [ContextMenu("CheckSmartAll")]
	public void CheckSmartAll()
    {
		foreach (var collection in collections)
		{
			LocalizeUtil.SetSmart(collection, false);
		}
	}
}
```
これで、SpreadSheetからPull Allした際に自動でSmartが設定されるようになりました。

# 次の記事
未定


# 前の記事

https://qiita.com/clrfnd/items/edf03f018e52063d57cd