mcpField
L
localization

localization

github.com/unity-technologies開発60460 回インストール今日更新
リポジトリ

localization について

localization は、Unity プロジェクトに多言語対応を加える開発者向けの Unity Localization 設定・利用ガイドです。ロケール、文字列・アセットテーブル、実行時の言語切り替え、CJK フォントの扱いを説明します。

主な機能

  1. 01Localization パッケージの確認と導入
  2. 02ロケールと Localization Settings の設定
  3. 03文字列・アセットテーブルの作成と更新
  4. 04イベントによるローカライズ文字列のバインド
  5. 05CJK 向けフォントアセットの設定

活用シーン

  1. 01プロジェクトに複数言語を設定
  2. 02ローカライズ文字列やアセットを更新
  3. 03実行時のロケール切り替えを実装
  4. 04中国語・日本語・韓国語のフォントを設定

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/unity-technologies/unity-agent-plugin --skill localization

インストール後、Claude Code で/localizationを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
api-notes.md
resources/
L10nBatchProcessor.cs
LocalizedFontAsset.cs

詳細

このガイドでは、ロケール、文字列テーブルとアセットテーブル、Addressables との統合、アセットテーブルを介した CJK フォントのサポートなど、Unity Localization のセットアップと設定について説明します。

0. パッケージのインストール確認

ほかの作業を始める前に、Localization パッケージがインストールされていることを確認してください。このスキルの多くの API は、パッケージが存在しないと、何も通知せずに失敗したり、わかりにくいエラーをスローしたりします。

  1. Package Manager に問い合わせるのではなく、プロジェクトを読み取って確認してください。 Packages/packages-lock.json で com.unity.localization を探します。このファイルには Unity が実際に解決した内容が記録されています。通常の JSON ファイルなので、Editor も非同期呼び出しも必要ありません。 (Packages/manifest.json に記録されるのは要求されたパッケージだけなので、ロックファイルを確認してください。)
  2. 見つからない場合はインストールします: UnityEditor.PackageManager.Client.Add("com.unity.localization")。
  3. 適切に待機してください。 Client.Add と Client.List は非同期です。呼び出しから戻った時点では、返されたリクエストはまだ InProgress なので、同じ文で結果を読み取っても何もわかりません。また、IsCompleted をビジーループで待つのも避けてください。実行中のメインスレッドをブロックしてしまいます。代わりに、インストールを開始したらいったん戻り、後続の呼び出しで packages-lock.json をポーリングし、ID が現れるまで確認してください。インストールによってドメインのリロードも発生するため、最初の 1〜2 回のポーリングは失敗することがあります。通常、新規インストールは数秒で解決します。
  4. アセンブリの準備が整う前にロックファイルが書き込まれることがあるため、使用する前に型が実際に読み込まれていることを確認してください:
    var t = System.Type.GetType(
        "UnityEngine.Localization.Settings.LocalizationSettings, Unity.Localization");
    return t != null ? "ready" : "not loaded yet";
    
    結果が ready になってから先に進んでください。

1. ローカライゼーション設定とロケール

LocalizationEditorSettings.ActiveLocalizationSettings が null の場合は、設定を検索するか作成する必要があります。

  1. 検索: AssetDatabase.FindAssets("t:LocalizationSettings", new[] { "Assets" }) を使用します。見つかった場合は、最初のアセットを読み込み、LocalizationEditorSettings.ActiveLocalizationSettings に割り当てます。
    • 検索フォルダーは必ず指定してください。 スコープを指定しない FindAssets は、読み取り専用のパッケージを含むプロジェクト全体を検索するため、パッケージ内のアセットが返され、編集できないものをプロジェクトが参照してしまう可能性があります。これは、このスキル内のすべての FindAssets 呼び出しに当てはまります。
  2. 作成: 見つからない場合は、新しいインスタンスを作成し、Assets/Localization/LocalizationSettings.asset に保存します。ScriptableObject.CreateInstance<LocalizationSettings>() に続けて AssetDatabase.CreateAsset() を使用します。
  3. 有効化: LocalizationEditorSettings.ActiveLocalizationSettings = settings を設定します。
  4. ロケール: en、fr、de などのロケールが存在することを確認します。不足している場合は作成し、LocalizationEditorSettings.AddLocale(locale) を使用して設定に追加します。

2. ローカライゼーションテーブルの変更

文字列テーブルまたはアセットテーブルをプログラムで変更した場合は、Editor に通知する必要があります。 プロジェクト内に既存のテーブルがない場合を除き、必要なアセットテーブルは必ず作成してください。

安全なデータ投入パターン

データセットからテーブルにデータを投入する場合は、Locale.Identifier.Code を明示的に照合してください。GetLocales() の順序が入力データ配列と一致する保証はありません。一致すると仮定すると、データの不整合が通知されないまま発生し、デバッグが非常に困難になります。 アセットテーブルでは、アセットの GUID を使用します:table.GetEntry(sharedId) ?? table.AddEntry(sharedId, guid);。

更新と通知

キーの追加や値の更新など、変更を行った後は、Editor が内部状態を更新できるよう通知してください。これを省略すると、次回の再インポートまで Editor に古いデータが表示されます。

  1. 変更した各 Table について、EditorUtility.SetDirty(collection) と EditorUtility.SetDirty(collection.SharedData) を呼び出します。
  2. Unity 6 以降の通知: LocalizationEditorSettings.EditorEvents.RaiseCollectionModified(sender, collection);
  3. 最後に必ず AssetDatabase.SaveAssets() を呼び出します。

3. UI のローカライゼーションとレイアウト

名前空間と競合
  • 常に名前を完全修飾します: UnityEngine.UI.Image、UnityEngine.UI.VerticalLayoutGroup、UnityEngine.UI.ScrollRect、UnityEngine.UI.Mask、UnityEngine.UI.CanvasScaler、UnityEngine.UI.GraphicRaycaster、UnityEngine.UI.ContentSizeFitter、UnityEngine.UI.LayoutRebuilder などを使用します。
  • UnityEngine.UI は名前空間であると同時にクラスのコンテナーでもあるため、名前を修飾せずに使うと CS0118(名前空間を型として使用)が発生します。完全修飾すれば、これを確実に回避できます。
  • インスタンスは1つに限定: 新しく作成する前に、必ず GameObject.Find("YourCanvasName") で確認し、既存のものを破棄します。
  • ロケールの切り替え:パッケージを使い、プレビューと実行時を分けます。 これは別々の仕組みです。混同すると、ロケールの切り替えを独自実装することになりがちです。
    • 作成中にロケールをプレビューするには、 Localization Scene Controls ウィンドウ(Window > Asset Management > Localization Scene Controls)を使用します。これはエディター専用です。実行時の機能ではないため、ゲーム自体に言語設定が必要な場合の解決策にはなりません。
    • 実行時にロケールを切り替えるには、 LocalizationSettings.SelectedLocale を設定します。これはサポートされているエントリーポイントであり、LocalizeStringEvent を通じてバインドされたすべての要素がこれに応じて更新されます。
    • ゲームの起動時に使用するロケールを固定するには、 Localization Settings アセットで起動時のロケールセレクターを設定します。選択したロケールを強制するのは SpecificLocaleSelector です。設定しない場合、デフォルトの選択チェーンによってシステム言語が使用されます。
    • ロケールの状態を独自実装することは絶対に避けてください。 ゲーム内の言語メニューを実装するのは問題なく、想定されている方法です。ただし、SelectedLocale を設定し、パッケージに変更を反映させてください。禁止されているのは、独自の「現在の言語」変数を追跡したり、文字列を独自に切り替えたり、パッケージを迂回したりするデバッグ用のドロップダウンやメニューです。これらを使うと、プロジェクト内のほかの要素に変更が反映されません。
ローカライズ文字列イベント(堅牢なバインド)
  • コンポーネントの種類を確認: 対象が TextMeshPro か、レガシーの UnityEngine.UI.Text かを特定します。

  • 正しくバインド: 公開コンポーネント UnityEngine.Localization.Components.LocalizeStringEvent を追加して、手動で接続します。StringReference にテーブルエントリーを設定し、OnUpdateString リスナーを追加して、値をテキストコンポーネントに代入します(TextMeshPro では TMP_Text.text、レガシーの Text では UnityEngine.UI.Text.text を使用します)。

    UnityEditor.Localization.Plugins.TMPro.LocalizeComponent_TMPro やその UGUI 版にリフレクションでアクセスしないでください。これらは internal です(Localization 1.5.12 で確認済み)。これらにアクセスすることは、アクセス制御を迂回して、Unity が安定性を保証していない API を利用することになります。パッケージのどのリリースでも、変更されたり削除されたりする可能性があります。LocalizeStringEvent は公開されており、接続を明示した形で同じ処理を行えます。

  • レイアウトの再構築: ローカライズ済みテキストを設定した後やリストにデータを追加した後は、UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(parentTransform) を呼び出して、寸法が更新されるようにします。

4. アジア言語のフォント対応(CJK)

CJK ロケールでは TMP フォールバックフォントを避けてください。代わりに、ロケールごとに アセットテーブルでフォントを切り替える 方法を使用します。フォールバックは信頼性が低く、グリフが欠けた際のデバッグも困難です。

前提条件:TMP Essential Resources をインポートする必要があります

TMP API に触れる前に、必ず確認してください。 一度もインポートされていないプロジェクトでは、TMP_Settings.instance が null になり、TMP の呼び出しが有用な情報を何も示さない NullReferenceException で失敗します。TMP_FontAsset.CreateFontAsset もその一つであり、フォント作成は最初の行で停止するため、コードのバグのように見えるエラーが発生します。

// The check.
var ready = TMPro.TMP_Settings.instance != null;

準備ができていない場合は、非対話的にインポートします。

// Do NOT use EditorApplication.ExecuteMenuItem("Window/TextMeshPro/Import TMP Essential Resources").
// It returns true and then opens a dialog that waits for a human, so nothing gets imported and the
// run appears to hang. Import the package directly instead.
string package = null;
var cache = System.IO.Path.GetFullPath(System.IO.Path.Combine(
    UnityEngine.Application.dataPath, "..", "Library", "PackageCache"));
foreach (var dir in System.IO.Directory.GetDirectories(cache))
{
    // TMP ships inside com.unity.ugui in Unity 6, and the folder name carries a version hash,
    // so search for the file rather than hardcoding a path.
    var candidate = System.IO.Path.Combine(dir, "Package Resources", "TMP Essential Resources.unitypackage");
    if (System.IO.File.Exists(candidate)) { package = candidate; break; }
}
UnityEditor.AssetDatabase.ImportPackage(package, false);   // false = non-interactive

その後、Step 0 のパッケージ確認と同じ方法で、後続の呼び出しで TMP_Settings.instance != null になるまで確認し、null でなくなってから処理を続けます。Unity 6000.5.8f1 で確認済みです。非対話型のインポートは数秒で完了し、アセットは Assets/TextMesh Pro に格納されます。

  1. ロケールに対応したフォントを使用する: Arial や Liberation Sans などの欧文フォントには CJK グリフが含まれていないため、「豆腐」(四角いブロック)が表示されます。必ず対象言語向けに設計されたフォントを使用してください。
    • **簡体字中国語(zh-Hans)**の場合:msyh.ttc(Microsoft YaHei)または同等のフォントを使用してください。
    • **日本語(ja)**の場合:msgothic.ttc(MS Gothic)または同等のフォントを使用してください。
    • **韓国語(ko)**の場合:malgun.ttf(Malgun Gothic)または同等のフォントを使用してください。
    • システムフォントのコピーに失敗した場合は、処理を停止して報告してください。欧文フォントで代用しないでください。
  2. 堅牢なフォントの作成: インポートしたフォントから動的な TMP_FontAsset を作成します。
  3. マルチアトラスと動的生成: CJK の文字セットは大きすぎて静的アトラスには収まりません。単一のアトラスはすぐに容量不足になります。
    • fontAsset.atlasPopulationMode = AtlasPopulationMode.Dynamic;
    • fontAsset.isMultiAtlasTexturesEnabled = true;
  4. サブアセット: アトラステクスチャとマテリアルを追加します。テクスチャだけを追加するのがよくある間違いで、その場合マテリアルはファイルに保存されません。Unity 6000.5.8f1 で測定したところ、2 回目の呼び出しを行わずに保存したフォントアセットには、ディスク上に Material オブジェクトがゼロ個含まれていました。2 回目の呼び出しを行った場合は含まれていました。メモリ上にしか存在しないマテリアルはアセットの一部ではないため、アセットを新たに読み込むと、設定したマテリアルではなく TMP が再構築したものが使用され、マテリアルに適用した設定もすべて何も通知されず失われます。
    // 最初のものだけでなく、すべてのアトラステクスチャ。手順 3 でマルチアトラスを有効にしているため、複数ある場合があります。
    foreach (var atlas in fontAsset.atlasTextures)
    {
        UnityEditor.AssetDatabase.AddObjectToAsset(atlas, fontAsset);
    }
    // マテリアルも追加します。この行がないと、アセットに書き込まれません。
    UnityEditor.AssetDatabase.AddObjectToAsset(fontAsset.material, fontAsset);
    
    • マテリアルのテクスチャを明示的にリンクします:fontAsset.material.mainTexture = fontAsset.atlasTexture;。保存する前に、フォントアセット、マテリアル、およびテクスチャを変更済みとして設定してください。(atlasTexture は atlasTextures の最初の要素であり、プライマリマテリアルが参照するものなので、上記のとおりすべてのテクスチャを追加する方法と整合しています。)
    • 手元のオブジェクトではなく、ファイルを検証してください。 AssetDatabase.SaveAssets() の後に AssetDatabase.LoadAllAssetsAtPath(path) を呼び出し、返されたオブジェクトの中に Material が含まれていることを確認します。fontAsset.material != null だけで済ませないでください。マテリアルが保存されているかどうかにかかわらず、TMP はメモリ上のマテリアルを返すため、保存済みか未保存かを判別できません。
  5. Addressables: Asset Table で参照するすべてのアセットを Addressable としてマークする必要があります。
    • Asset Table から Resources/ フォルダー内のアセットを参照しないでください。これにより OperationException: Failed to load sub-asset エラーが発生します。アセットが Resources/ 内にある場合は、Addressable にする前に Assets/Fonts/ などへコピーしてください。
    • フォントアセットを削除して再作成した場合、新しい GUID を Asset Table 内で手動で更新し、Addressables に再追加する必要があります。
  6. 特殊な型: TextMesh Pro のフォント切り替えには、暗黙的な変換エラーを避けるため、LocalizedAsset<TMP_FontAsset> より LocalizedTmpFont を優先してください。
  7. ビルド要件: Asset Table または Addressable グループを更新した後、ビルドを実行します:AddressableAssetSettings.BuildPlayerContent();
検証手順

CJK ローカライズ作業を完了する前に、次の項目を確認してください。

  1. 豆腐チェック: エディターのロケールを zh-Hans、ja、ko に切り替えて、UI を確認します。文字が四角(豆腐)として表示される場合、フォントの設定に失敗しています。
  2. Asset Table の確認: CJK ロケールの AssetTable が、デフォルトの欧文フォントではなく、正しい CJK TMP_FontAsset を参照していることを確認します。
  3. マルチアトラスの確認: CJK フォントアセットで isMultiAtlasTexturesEnabled が true になっていることを確認します。

5. 自動レイアウト(UGUI)

  • 親: VerticalLayoutGroup の Child Control Height: True、Child Force Expand Height: False を設定します。
  • ラベル: 各ラベルに Vertical Fit: Preferred Size を設定した ContentSizeFitter を追加します。
  • TMP: Enable Word Wrapping: True、Overflow: Overflow を設定します。
既存プロジェクトを翻訳する際の注意事項
  • コードの変更は最小限に: ローカライズと無関係なコードは変更しないでください。既存のスクリプトに簡単に組み込めるよう、静的ヘルパークラス(例: L10n)を使って LocalizationSettings.StringDatabase.GetLocalizedString をラップします。
  • 堅牢なマッピング戦略: 既存の UI テキストをキーにマッピングする際は、キーを文字列の長さの降順で並べ、最も長い文字列から照合します。これにより、「NO」のような短い文字列が長い文の一部に一致するのを防げます。適切な場合は、大文字と小文字を区別しない照合を使用します。
  • コンポーネントのイベントリスナー: UnityEventTools.AddPersistentListener を使って LocalizeStringEvent.OnUpdateString を登録し、テキストコンポーネントの公開 text セッターを基にしたデリゲートを渡します。セッターには C# のメソッドグループ名がないため、名前を指定してデリゲートを作成します: (UnityAction<string>)Delegate.CreateDelegate(typeof(UnityAction<string>), text, "set_text")。 これは公開メンバーに対するリフレクションなので問題ありません。既存の永続リスナーを最初にすべて削除して、繰り返し実行しても重複が積み重ならないようにする処理を含む実装例については、resources/L10nBatchProcessor.cs を参照してください。
    • SerializedObject を通して永続呼び出しのフィールド(m_MethodName、m_Mode、m_PersistentCalls)を直接書き換えないでください。これらは互換性が保証されていない非公開のシリアライズ名であり、その必要もありません。上記のデリゲートを指定して AddPersistentListener を呼び出すと、同じシリアライズ済み呼び出し(ターゲット = テキストコンポーネント、メソッド = set_text、モード = EventDefined)が生成されます。

    • その後、必ず呼び出し状態を設定してください。設定しないと、ラベルはエディター上で更新されません。 AddPersistentListener は呼び出しを UnityEventCallState.RuntimeOnly のままにするため、バインディングは正しくても Play モード以外では無効です。エディターでロケールを切り替えても何も変わらず、保存して再読み込みした後もそのままです。公開 API の UnityEventBase.SetPersistentListenerState を使って修正します:

      UnityEventTools.AddPersistentListener(lse.OnUpdateString, setText);
      var index = lse.OnUpdateString.GetPersistentEventCount() - 1;
      lse.OnUpdateString.SetPersistentListenerState(
          index, UnityEngine.Events.UnityEventCallState.EditorAndRuntime);
      

      Unity 6000.5.8f1 で確認済みです。2 つ目の呼び出しを行わない場合、プレハブを保存して再読み込みした後も Edit モードではリスナーが実行されません。設定すると呼び出し状態が EditorAndRuntime になり、テキストがすぐに更新されます。

    • 永続リスナーは、必ず UnityEngine.Object のメソッドを参照する必要があります。ラムダでは動作しません。

    • その後、バインディングが有効であることを確認してください。決めつけてはいけません。 インスペクター上では正しく設定されているように見えるのに何も起きないのは、この手順でよくある失敗です。必要な読み戻し処理はすべてイベントの公開 API で行えるため、シリアライズ済みフィールドに触れる必要はありません:

      確認項目呼び出し期待される結果
      何らかの設定が行われているGetPersistentEventCount()> 0
      テキストコンポーネントを参照しているGetPersistentTarget(i)、GetPersistentMethodName(i)コンポーネント、set_text
      編集中に実行されるGetPersistentListenerState(i)EditorAndRuntime
      ラベルが実際に更新されるlEvent.RefreshString()テキストの値が変わる
      4 つすべてを実行してください。カウントがゼロより大きいことは、何かが配線されたことだけを示しており、RuntimeOnly の呼び出しはビルドにとってはまったく正しくても最後のチェックで失敗するため、状態を読むことが、休眠状態のバインディングと壊れたバインディングを見分ける鍵となります。追加および設定されたものの一度も発火しないコンポーネントは、ローカライズされていないラベルよりも厄介です。それは完了したように見えるからです。
  • 初期化と更新:
    • LocalizationEditorSettings.CreateStringTableCollection は完全なアセットパスではなく、ディレクトリパス(例: Assets/Localization)を想定しています。
    • LocalizedString 参照をプログラムで割り当てた後は、常に lEvent.RefreshString() を呼び出して UI を即座に更新してください。
    • キーはコレクションのすべてのテーブル(en、de、ja など)に空でない値で存在している必要があります。値が空のまま存在するキーがよくある抜けであり、それはサイレントではありません。パッケージは独自の「No translation found for …」というテキストをゲーム UI に出力するため、出荷された画面に開発者向けメッセージが表示されます。これを目視で確認しないでください。以下の完全性チェックを実行してください。
  • 名前空間と Linq: コレクションを検索するときは常に using System.Linq; を、ロケールやテーブルを扱うときは using UnityEngine.Localization; を含めてください。
  • 検証: テーブルや addressables を変更した後は、AddressableAssetSettings.BuildPlayerContent() を実行し、Editor のロケールを切り替えて変更を検証してください。アクティブ化後に LocalizationSettings.Instance のステータスを確認してください。
テーブル完全性チェック(作業完了を宣言する前にこれを実行してください)

テーブルを列挙することで、「すべてのキーがすべてのロケールで値を持っているか」に機械的に答えることができるため、誰かがゲームをプレイする前に抜けを見つけられます。これを実行して出力を報告してください。

var gaps = new System.Collections.Generic.List<string>();
var checkedCount = 0;

foreach (var col in UnityEditor.Localization.LocalizationEditorSettings.GetStringTableCollections())
{
    foreach (var key in col.SharedData.Entries)
    {
        foreach (var table in col.StringTables)
        {
            checkedCount++;
            var entry = table.GetEntry(key.Id);
            // A missing entry and a present-but-empty entry both show as untranslated in game.
            if (entry == null || string.IsNullOrWhiteSpace(entry.Value))
            {
                gaps.Add($"{col.TableCollectionName} / {table.LocaleIdentifier.Code} / {key.Key}");
            }
        }
    }
}

// Zero entries is not a pass. It means no collections or no locales were found, so the check
// examined nothing: report that distinctly instead of letting it read as success.
if (checkedCount == 0)
{
    return "INCONCLUSIVE: no table entries found. Either no String Table Collection exists yet, "
         + "or the collection has no locale tables. Fix that before trusting this check.";
}

return gaps.Count == 0
    ? $"COMPLETE: {checkedCount} entries checked, no gaps"
    : $"GAPS ({gaps.Count} of {checkedCount} checked):\n  " + string.Join("\n  ", gaps);

Unity 6000.5.8f1 で、ja の値を意図的に 1 つ空にしたテーブルに対して検証済みです。まさにそのエントリを名指しして GAPS 1 of 4 を報告し、値が埋まると COMPLETE (4 checked) となり、テーブルが存在しないプロジェクトでは INCONCLUSIVE となります。

ギャップリストは黙って解消するのではなく報告してください。 ギャップの中には間違いではなく判断であるものもあります。翻訳を依頼されていないロケールや、意図的に言語間で同一となっているキーなどです。それらを英語テキストで埋めると判断が覆い隠されてしまいます。それらをリストアップし、どれが意図的なものかをユーザーに判断してもらってください。

  • スマート文字列: 必要に応じてスマート文字列を設定してください。各文字列のコンテキストは、それが配置されている UI 全体、およびそれに影響を与えるスクリプトを考慮して調べてください。翻訳が意味を成すように、文字列テーブルにコンテキストを設定してください。

6. 推奨される翻訳戦略

既存のプロジェクトを効率的に翻訳するには、次の複数の手順からなるワークフローに従ってください。

  1. 抽出とコンポーネントの設定:

    • すべての該当箇所を見つけます。隠れている場所は2か所あり、片方だけを検索すると、もう片方を見落とします。
      • 作成されたテキストは、シーンやプレハブ内のコンポーネントにあります。レガシーな UnityEngine.UI.Text と TextMeshPro(TMP_Text。TextMeshProUGUI と TextMeshPro の基底クラス)の両方を調べます。実際のプロジェクトで計測したところ、FindObjectsByType<Text> で見つかったコンポーネントは1つでしたが、FindObjectsByType<TMP_Text> では13個でした。つまり、レガシーなものだけを検索すると、ほとんど何も処理していないのに完了と報告してしまいます。
      • コード内で組み立てられるテキストは、編集時にコンポーネント上に存在しないため、シーンを走査しても見つかりません。scoreLabel.text = $"EXP {value}" は、コンポーネントベースの検索では検出できず、「完了した」ローカライズ作業の後も残る文字列です。代わりに C# で探します。
        # 文字列リテラルを含む代入と SetText 呼び出し。
        grep -rnE '\.text\s*(=|\+=)\s*\$?"|SetText\(\s*\$?"' --include='*.cs' Assets/
        
        このパターンでは、通常の文字列、補間文字列、連結、+= 形式に加え、SetText も検出します。また、label.text = someVariable や label.text = Localize("KEY") には意図的に一致しません(前者は抽出対象がなく、後者はすでに適切な経路を通っています)。変数や const として別の場所に宣言されたリテラルは検出できません。プロジェクトの規模に対して検出数が少ない場合は、そのファイル内の文字列リテラルも grep してください。
    • 変換しなかった箇所も報告します。 組み立てられた文字列には通常、スマート文字列またはフォーマット引数が必要で、判断が必要になります。また、ローカライズする価値が本当にない文字列もあります。どちらを選ぶ場合も、検索で見つかったすべての箇所と、変換したかどうかを列挙し、変換しなかった場合はその理由も記載してください。9か所が未対応のまま「24個の文字列をローカライズしました」と報告するのは、この一覧で防ぐべき失敗例です。作業が完了したように見え、問題は別のロケールのスクリーンショットを確認したときに初めて明らかになります。
    • 共有テーブル: 基本言語と、翻訳者が参考にできる各キーの「Context」列を含む、中央の String Table(例:UIStrings)を作成します。
    • コンポーネントの追加: 見つかった各 UI 要素に、(テキスト用の)LocalizeStringEvent と、(フォント切り替え用の)LocalizedFont ヘルパーを追加します。
    • 検証: ロケールが変更されたときにエディターですぐ更新されるよう、これらのコンポーネントに永続リスナー(EditorAndRuntime)が設定されていることを確認します。
  2. 文脈に応じた翻訳:

    • 翻訳: テーブルにデータを入力したら、各ロケールの翻訳を用意します。
    • 文脈が重要です: 翻訳が意図した意味に合い、表示領域に収まるよう、「Context」列を必ず参照するか、UI レイアウトを確認します。
    • 文法とトーン: トーンをゲームのスタイルに合わせます。たとえば、ボタンには命令形を使い(ドイツ語:「Laufen」ではなく「Lauf!」)、ラベルの複数形も正しく使います(「Punkt」ではなく「Punkte」)。
  3. 品質保証(QA):

    • シーンの操作: Window > Asset Management > Localization Scene Controls を使うか、スクリプトで LocalizationSettings.SelectedLocale = LocalizationSettings.AvailableLocales.GetLocale("de"); を実行します。
    • 目視確認: 基本言語とすべての対象言語で、すべてのプレハブとシーンを順を追って確認します。
    • レイアウトへの適合: テキストのはみ出しや「豆腐」(グリフの欠落)がないか確認します。文字列が長すぎる場合は、フォントサイズを調整するか、ContentSizeFitter を使用します。

API リファレンス

API の詳しい使い方、よくある名前空間の競合、Addressables のパターン、フォントの修正手順については、references/api-notes.md を参照してください。

7. ローカライズの高速化ワークフロー

プロジェクト全体を効率よくローカライズするには、すべてのシーンを一度に処理するバッチ処理スクリプトを使用します。

実行前に確認してください: バッチ処理を実行する前に、必ずユーザーに確認します。

「この処理では、プロジェクト内のすべてのシーンを開き、LocalizeStringEvent コンポーネントを追加して、変更したすべてのシーンを保存します。この操作は自動的に元に戻せません。実行してもよろしいですか?」

ユーザーの確認を得てから実行してください。バッチプロセッサーのテンプレートは resources/L10nBatchProcessor.cs にあります。

LocalizeAll にも同じ確認処理が組み込まれているため、テンプレートを調整する際に、これらのチェックを削除したり回避したりしないでください。バッチモードでの実行を拒否し、未保存のシーン変更を保存するか破棄するかをユーザーに確認してから、書き換えるシーンの一覧を表示するブロッキング形式の Editor ダイアログを表示します。ユーザーに Editor を確認し、確認ボタンをクリックするよう伝えてください。キャンセルされた場合は OperationCanceledException がスローされ、何も書き込まれません。回避すべきエラーではなく、キャンセルとして報告してください。

Text と TMP_Text の両方を処理し、LocalizeAll は一致するものがなかったラベルを返します (scene :: object :: "text" の形式)。その一覧を出力してください。この戻り値があるのはそのためです。20 個のラベルを設定し、9 個を何も言わずに処理しなかった実行は、そうでない場合、すべて正常に完了した実行と見分けがつきません。この一覧には作成済みテキストのみが含まれるため、セクション 6 のコードスキャンと、セクション 5 のテーブルの完全性チェックを併せて実施してください。

高速化のための技術的なヒント
  • テーブル参照: GUID ではなく TableReference の名前(文字列)を使用してください。読みやすく、保守もしやすくなります。
  • バッチ更新: 変更後に LocalizationSettings.Instance.ForceRefresh() を使用し、エディター内の UI を強制的に更新してください。
  • フォント切り替えの自動化: GameAssets テーブルを一度作成し、スクリプトを使ってすべてのラベルに LocalizeFontEvent を一括で再割り当てしてください。
  • LocalizedFontAsset コンポーネント: テンプレートは resources/LocalizedFontAsset.cs にあります。

コメント