メインコンテンツまでスキップ

ローカライゼーション

OpenMon には、Google Sheets をバックエンドとしたカスタムローカライゼーションシステムが搭載されています。翻訳は TSV ファイルとしてダウンロードされ、ScriptableLanguage アセットにパースされ、ランタイムで Localizer クラスを通じて提供されます。システムは OpenMon.Foundation.Localization にあり、ゲーム固有の後処理プログラムは OpenMon.MCE.Localization.PostProcessors にあります。

アーキテクチャ概要​

クラス目的
Localizerコアランタイムサービス。言語パックのロード、キーの解決、後処理プログラムの適用を行う
ILocalizerローカライザーのインターフェース。Zenject を介して注入
ScriptableLanguage1つの言語の全キーと値のペアを保持する ScriptableObject
LocalizerSettings開発者設定:言語パックディレクトリ、Google Sheet URL、後処理プログラム
LocalizerConfigurationプレイヤー設定:選択された言語を永続化(デフォルト:"English")
GoogleSheetLoaderシートをダウンロードして ScriptableLanguage アセットを生成するエディタツール
LocalizedTextMeshPro言語変更時に TMP_Text を自動更新する UI コンポーネント
LocalizationFontSwitcher言語ごとに TMP フォントを切り替える(CJK 対応用)
LanguageFontMap言語名を TMP_FontAsset のオーバーライドにマッピングする ScriptableObject
LocalizedTextPostProcessor解決済みテキストを後処理するための抽象基底クラス
ReplaceSubstring組み込み後処理プログラム:単純な検索と置換
InjectPlayerNameMCE 後処理プログラム:{PlayerName} をプレイヤーキャラクターの名前に置換
InjectDexValuesMCE 後処理プログラム:{Dex.CaughtSpecies} を捕獲数に置換

Google Sheets 連携​

翻訳は Google Sheets で以下のレイアウトで作成されます:

KeyEnglishSpanishJapanese
Menu/PlayPlayJugar...
Menu/OptionsOptionsOpciones...
Monsters/001/NameSproutylBrotino...

キー列以降の各列が言語を表します。列ヘッダーが ScriptableLanguage アセット名になります。

エディタインポート​

GoogleSheetLoader エディタユーティリティは、LocalizerSettings.GoogleSheetsDownloadUrls から各シート URL をダウンロードし、TSV エクスポート形式に変換し、行をパースして Assets/Resources/{LanguagePackDirectory}/ に ScriptableLanguage アセットを保存します。

フロー:

  1. 各 URL について、Google Sheets の /edit URL を /export?format=tsv に変換
  2. TSV ファイルを Temp/ にダウンロード
  3. ヘッダーをパースして言語名を取得
  4. 言語ごとに ScriptableLanguage アセットを作成または更新
  5. 各行について Language[key] = value を設定
  6. アセットを保存し、AssetDatabase をリフレッシュ

複数のシートをサポートします。同じキーが複数のシートで定義されている場合、後方の URL が優先されます(リストの下方の URL が上方のものを上書きします)。

ランタイム再ダウンロード​

Localizer は、ReDownloadLanguagesFromGoogleSheet() を使用してランタイムに翻訳を再ダウンロードできます。これにより、一時的な ScriptableLanguage インスタンスがメモリ内に作成され(ディスクには保存されません)、言語システムがリロードされます。エディタで再インポートせずに翻訳をテストするのに便利です。

ScriptableLanguage​

各言語は、すべてのキーと値のペアを含む SerializableDictionary<string, string> を持つ ScriptableLanguage ScriptableObject です。アセットは Resources/Languages/ に保存されます(LocalizerSettings.LanguagePackDirectory で設定可能)。

// ScriptableLanguage はキーを解決し、見つからない場合は元のキーを返す
string value = englishPack["Menu/Play"]; // "Play"
string missing = englishPack["Nonexistent"]; // "Nonexistent" (フォールバックとして元のキー)

キーが見つからない場合、アセットはキー自体を返します。これにより、欠落した翻訳がゲーム内で生のキー文字列として即座に確認できます。

Localizer​

Localizer はメインのランタイムサービスであり、Zenject を介して ILocalizer としてバインドされます。初期化時に Resources ディレクトリからすべての ScriptableLanguage アセットをロードします。

テキストの解決​

// インデクサ構文(最も一般的)
string text = localizer["Menu/Play"]; // "Play"

// 置換パラメータ付き
// 値が "You caught {0}!" で "Monsters/025/Name" が "Thundermane" に解決される場合
string text = localizer["Messages/Caught", true, "Monsters/025/Name"];
// 結果:"You caught Thundermane!"

// パラメータをリテラル文字列として(ローカライゼーションキーではない)
string text = localizer.GetText("Messages/Caught", false, "Thundermane");

翻訳値内の {0}、{1} などのプレースホルダーは、対応する修飾文字列に置き換えられます。modifiersAreLocalizableKeys が true(デフォルト)の場合、各修飾子は置換前にローカライザーを通じて解決されます。

言語の切り替え​

// 利用可能な言語を取得
List<string> languages = localizer.GetAllLanguageIds(); // ["English", "Spanish", "Japanese"]

// 現在の言語を取得
string current = localizer.GetCurrentLanguage(); // "English"

// 言語を切り替え(自動的に設定に永続化)
localizer.SetLanguage("Spanish");

// インデックスで切り替え
localizer.SetLanguage(1);

言語が変更されると、選択内容は IConfigurationManager を介して永続化され、アプリの再起動後も保持されます。言語変更イベントの全サブスクライバーに通知されます。

言語変更イベント​

// 言語名でサブスクライブ
localizer.SubscribeToLanguageChange((string newLang) =>
{
Debug.Log("Language changed to: " + newLang);
});

// パラメータなしでサブスクライブ(通知のみ)
localizer.SubscribeToLanguageChange(() => RefreshUI());

// サブスクライブ解除
localizer.UnsubscribeFromLanguageChange(myCallback);

ローカライゼーションキーの規則​

キーは階層的なパス形式に従います:

ドメインパターン例
UI / メニューMenu/{Screen}/{Element}Menu/Battle/Run
モンスターMonsters/{DexNumber}/NameMonsters/001/Name
モンスターの説明Monsters/{DexNumber}/DescriptionMonsters/001/Description
技Moves/{MoveName}/NameMoves/Tackle/Name
アイテムItems/{ItemName}/NameItems/Potion/Name
クエストQuests/{QuestName}Quests/FindTheProfessor
クエスト目標Quests/{QuestName}/Objectives/{N}Quests/FindTheProfessor/Objectives/0
ダイアログDialog/{SceneName}/{NPC}/{Line}Dialog/Route1/OldMan/0

テキスト後処理プログラム​

キーが解決され、修飾子が置換された後、テキストは LocalizerSettings.TextPostProcessors で設定された LocalizedTextPostProcessor インスタンスのパイプラインを通過します。各プロセッサは文字列をその場で変更できます。

組み込み:ReplaceSubstring​

単純な検索と置換プロセッサです。OpenMon > Localization > TextPostProcessors > ReplaceSubstring から作成します。インスペクターで Original フィールドと Replacement フィールドを設定します。\n を実際の改行に置き換えるようなグローバル置換に便利です。

MCE:InjectPlayerName​

ローカライズされた文字列内の {PlayerName} をプレイヤーキャラクターの LocalizableName に置き換えます。MCE > Localization > TextPostProcessors > InjectPlayerName から作成します。

// 翻訳値:"Welcome, {PlayerName}! Ready to explore?"
// 後処理後:"Welcome, Red! Ready to explore?"

MCE:InjectDexValues​

{Dex.CaughtSpecies} をプレイヤーが捕獲した種の数に置き換えます。MCE > Localization > TextPostProcessors > InjectDexValues から作成します。

カスタム後処理プログラム​

LocalizedTextPostProcessor を拡張して独自のプロセッサを作成します:

[CreateAssetMenu(menuName = "MyGame/Localization/MyPostProcessor")]
public class MyPostProcessor : LocalizedTextPostProcessor
{
public override bool PostProcessText(ref string text, params object[] extraParams)
{
text = text.Replace("{MyToken}", GetMyValue());
return true;
}
}

アセットを LocalizerSettings.TextPostProcessors に追加すると、パイプラインに含まれます。

UI:LocalizedTextMeshPro​

LocalizedTextMeshPro コンポーネントは、TMP_Text をローカライゼーションキーにバインドします。言語が変更されると自動的に更新されます。

// コードから設定
localizedText.SetValue("Menu/Play");

// 修飾子付き
localizedText.SetValue("Messages/Caught", true, "Monsters/025/Name");

TMP_Text を持つ任意の GameObject にこのコンポーネントを追加します。インスペクターで LocalizationKey フィールドを設定します。SetOnEnable が true(デフォルト)の場合、有効化時にテキストが即座に解決されます。

CJK フォントサポート​

日本語のように異なるグリフが必要な言語では、LocalizationFontSwitcher コンポーネントを LanguageFontMap アセットとともに使用します。

セットアップ​

  1. OpenMon > Localization > Language Font Map から LanguageFontMap アセットを作成
  2. 言語名を TMP_FontAsset 参照にマッピングするエントリを追加:
    • "Japanese" -> NotoSansJP-Regular SDF
    • オプションで言語ごとに太字バリアントを設定
  3. ルート Canvas に LocalizationFontSwitcher を追加
  4. LanguageFontMap を FontMap フィールドに割り当て
  5. SearchScope を Children(デフォルト)または Scene に設定

言語が変更されると、スイッチャーは設定されたスコープ内のすべての TMP_Text コンポーネントを反復処理し、フォントを交換します。元のフォントをキャッシュするため、マッピングされていない言語に戻すとデフォルトに復元されます。

依存性注入のセットアップ​

LocalizationInstaller(OpenMon > Installers > Localization から作成)は以下をバインドします:

  • LocalizerSettings をシングルトンとして(Localizer に注入)
  • ILocalizer を Localizer にレイジーシングルトンとして
  • 設定されたすべての TextPostProcessors が注入用にキューイングされる
SceneContext
+-- LocalizationInstaller(LocalizerSettings アセットを参照)

新しい言語の追加​

  1. Google Sheet に言語名をヘッダーとする新しい列を追加(例:French)
  2. すべてのキーの翻訳を入力
  3. エディタで Google Sheet インポートを再実行
  4. Resources/Languages/ に新しい French.asset が表示される
  5. 言語に異なるフォントが必要な場合は、LanguageFontMap にエントリを追加

コードの変更は不要です。Localizer は起動時に設定されたディレクトリ内のすべての ScriptableLanguage アセットを自動検出します。