本指南涵蓋設定和配置 Unity Localization,包括語言環境、字串和資源表、Addressables 整合,以及透過資源表支援 CJK 字型。
0. 套件安裝檢查
在執行任何操作之前,請先確認 Localization 套件是否已安裝。如果套件不存在,此技術中的許多 API 將會靜默失敗或拋出令人困惑的錯誤。
- 透過讀取專案文件來檢查,不要詢問 Package Manager。 在
Packages/packages-lock.json中尋找com.unity.localization。該文件記錄了 Unity 實際解析的結果,是純 JSON 格式,讀取它不需要 Editor 也不需要非同步呼叫。(Packages/manifest.json只記錄了請求的內容,所以請檢查鎖定文件。) - 如果缺少則安裝:
UnityEditor.PackageManager.Client.Add("com.unity.localization")。 - 正確等待。
Client.Add和Client.List是非同步的:它們在呼叫返回時傳回一個仍為InProgress的請求,因此在同一個陳述式中讀取結果是沒有意義的。也不要忙等待IsCompleted;這會阻塞您正在執行的主執行緒。相反地,在觸發安裝後就返回,然後在後續的呼叫中輪詢packages-lock.json,直到出現該 ID。安裝也會觸發領域重新載入,因此預期前一兩次輪詢會失敗;全新的安裝通常會在幾秒內解析完成。 - 在使用類型之前,確認它們已實際載入,因為鎖定文件可能在程式集準備好之前就被寫入:
只有在回傳var t = System.Type.GetType( "UnityEngine.Localization.Settings.LocalizationSettings, Unity.Localization"); return t != null ? "ready" : "not loaded yet";ready時才繼續進行。
1. Localization Settings 與 Locales
如果 LocalizationEditorSettings.ActiveLocalizationSettings 為 null,則必須找到或建立它:
- 尋找: 使用
AssetDatabase.FindAssets("t:LocalizationSettings", new[] { "Assets" })。如果找到,載入第一個並將其指派給LocalizationEditorSettings.ActiveLocalizationSettings。- 始終傳遞搜尋資料夾。 未限定範圍的
FindAssets會搜尋整個專案,包括唯讀套件,因此它可能會從某個套件中傳回一個資源,導致您將專案指向一個無法編輯的資源。這適用於此技術中的每個FindAssets呼叫。
- 始終傳遞搜尋資料夾。 未限定範圍的
- 建立: 如果未找到,建立一個新實例並將其儲存到
Assets/Localization/LocalizationSettings.asset。使用ScriptableObject.CreateInstance<LocalizationSettings>(),接著使用AssetDatabase.CreateAsset()。 - 啟用: 設定
LocalizationEditorSettings.ActiveLocalizationSettings = settings。 - 語言環境: 確保語言環境(en、fr、de 等)存在。如果缺少則建立它們,並使用
LocalizationEditorSettings.AddLocale(locale)將它們加入設定中。
2. 修改本地化資料表
對字串或資源表進行程式化修改需要通知 Editor。 除非專案中已有現有的資料表,否則始終建立所需的資源表。
安全填充模式
從資料集填充資料表時,請明確地透過 Locale.Identifier.Code 進行比對。GetLocales() 的順序無法保證與您的輸入資料陣列相符 — 假設它們相符將導致難以除錯的靜默資料不匹配。
對於資源表,請使用資源的 GUID:table.GetEntry(sharedId) ?? table.AddEntry(sharedId, guid);。
重新整理與通知
在進行任何修改(新增金鑰、更新值)之後,通知 Editor 以便它重新整理其內部狀態。跳過此步驟將使 Editor 顯示過時的資料,直到下次重新匯入為止。
- 對每個修改過的
Table,呼叫EditorUtility.SetDirty(collection)、EditorUtility.SetDirty(collection.SharedData)。 - Unity 6+ 通知:
LocalizationEditorSettings.EditorEvents.RaiseCollectionModified(sender, collection); - 最後始終呼叫
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(命名空間被當作類型使用)。使用完整名稱可以完全避免此問題。- 單一實例: 始終檢查
GameObject.Find("YourCanvasName"),並在建立新實例之前銷毀舊的。 - 語言切換:使用套件,並將預覽和執行時分開。 這是兩種不同的機制,將它們混為一談正是語言切換經常轉而採用手工編碼的原因。
- 要在創作時預覽某個語言環境,請使用 Localization Scene Controls 視窗(
Window > Asset Management > Localization Scene Controls)。這僅限 Editor。它不是一個執行時功能,因此當遊戲本身需要語言設定時,這不是答案。 - 要在執行時切換語言環境,請指派
LocalizationSettings.SelectedLocale。這是受支援的進入點,所有透過LocalizeStringEvent綁定的內容都會從這裡更新。 - 要固定遊戲啟動時的語言環境,請在 Localization Settings 資源上設定一個啟動語言環境選擇器。
SpecificLocaleSelector是強制選擇特定語言環境的選擇器;否則,默認鏈會選取系統語言。 - 絕不手工編碼語言狀態。 一個真正的遊戲內語言選單是完全可以且預期的,只要它設定
SelectedLocale並讓套件傳播更改。禁止的是那種追蹤自己的「目前語言」變數、自行交換字串或繞過套件的除錯下拉選單或選單,因為專案中的其他任何東西都不會跟隨它。
- 要在創作時預覽某個語言環境,請使用 Localization Scene Controls 視窗(
本地化字串事件(穩健綁定)
-
檢查元件類型: 確認目標是
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 Fallback Fonts。請改為為每個特定語言環境使用資源表字型交換 — 回退字型不可靠,且在字型缺失時難以除錯。
先決條件:必須匯入 TMP Essential Resources
在觸碰任何 TMP API 之前檢查此項。 在從未匯入這些資源的專案中,TMP_Settings.instance 為 null,且 TMP 呼叫會失敗並拋出一個未命名任何有用資訊的簡單 NullReferenceException。TMP_FontAsset.CreateFontAsset 就是其中之一,因此字型建立會在第一步就因錯誤而終止。