# 2. Localizing UI Toolkit and uGUI Text Part of [[Localization]]. Previous: [[1. Localization Install Locales and String Tables]]. Next: [[3. Smart Strings Plurals and Variables]]. > [!note] Verification status > UI Toolkit bindings (UI Builder *Add binding*, UXML `<Bindings>` with `UnityEngine.Localization.LocalizedString property="text" table="..." entry="..."`, `label.SetBinding("text", localizedString)`, `label.GetBinding("text") as LocalizedString`, local variables like `IntVariable`) are copied in structure from the Localization 1.5 *UI Toolkit (Unity 6+)* page. `LocalizeStringEvent` members from its API page (2026-11-10). **Code not compiled in Unity.** ## 2.1 UI Toolkit: bind in UI Builder (no code) 1. Open your HUD UXML in UI Builder (e.g. the HUD from [[3.HUD and Inventory Example]]). 2. Select a Label > Inspector > right-click the **Text** field > **Add binding** > choose **LocalizedString**. 3. Pick table `UI` and entry `hud.health.label`. For strings with placeholders, add **Variables** (e.g. an Int named `value`). 4. Change locale in the Game view's locale menu (or part 5's dropdown) - the label updates by itself. The resulting UXML looks like this (namespaced short form): ```xml <ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:l="UnityEngine.Localization" xmlns:lv="UnityEngine.Localization.SmartFormat.PersistentVariables"> <ui:Label name="health-label" text="Health"> <Bindings> <l:LocalizedString property="text" table="UI" entry="hud.health.value"> <variables> <l:LocalVariable name="value"> <lv:IntVariable value="100" /> </l:LocalVariable> </variables> </l:LocalizedString> </Bindings> </ui:Label> </ui:UXML> ``` Table/entry can be referenced by name (as above) or by `GUID:...` / `Id(...)`; names are readable, GUID/ID survive renames. UI Builder writes GUID/ID by default (configurable in Preferences > Localization). ## 2.2 Script: HUD with live localized values String table entries (Smart, see part 3): | Key | en | de | |---|---|---| | `hud.health.value` | `HP {value}` | `LP {value}` | | `hud.score.value` | `Score: {value:N0}` | `Punkte: {value:N0}` | | `hud.ammo.value` | `{value:plural:1 round\|{} rounds}` | `{value:plural:1 Schuss\|{} Schuss}` | ```csharp // Assets/Scripts/UI/LocalizedHud.cs using UnityEngine; using UnityEngine.Localization; using UnityEngine.Localization.SmartFormat.PersistentVariables; using UnityEngine.UIElements; [RequireComponent(typeof(UIDocument))] public class LocalizedHud : MonoBehaviour { const string Table = "UI"; IntVariable health; IntVariable score; IntVariable ammo; void OnEnable() { VisualElement root = GetComponent<UIDocument>().rootVisualElement; health = Bind(root.Q<Label>("health-label"), "hud.health.value", 100); score = Bind(root.Q<Label>("score-label"), "hud.score.value", 0); ammo = Bind(root.Q<Label>("ammo-label"), "hud.ammo.value", 30); } static IntVariable Bind(Label label, string entry, int initial) { if (label == null) { Debug.LogWarning(quot;HUD label for '{entry}' not found"); return new IntVariable(); } var variable = new IntVariable { Value = initial }; var text = new LocalizedString(Table, entry); text.Add("value", variable); // name must match {value} in the table label.SetBinding("text", text); // updates on locale change and on variable change return variable; } // Gameplay calls these. Setting Value triggers the label to re-format. public void SetHealth(int v) => health.Value = v; public void AddScore(int v) => score.Value += v; public void SetAmmo(int v) => ammo.Value = v; } ``` **Explanation** - `IntVariable` implements value-changed notification; the bound `LocalizedString` re-formats when `Value` changes. No `Update()` polling, no string concatenation in C#. - Never build sentences in code (`"Score: " + n`): word order differs per language. Put the whole sentence in the table and pass values in. - Bindings on elements created in `OnEnable` are fine; if you rebuild the UI tree, re-run `Bind`. - HUD values change often. If you profile allocations, consider updating very hot values (a timer at 60 Hz) with a cached format string fetched once per locale change instead (`LocalizedString.StringChanged`). ## 2.3 uGUI and TextMeshPro: Localize String Event For uGUI/TMP text (still common in VR world-space canvases): 1. Select the TMP text > **Localize** from the component context menu (or add **Localize String Event**). 2. Set **String Reference** to table/entry. Add local variables if needed. 3. **Update String** event > target the TMP component's `text` setter (Unity usually wires this for you when you use the Localize context menu). From code, change the entry or force refresh: ```csharp // Assets/Scripts/UI/PromptText.cs using UnityEngine; using UnityEngine.Localization.Components; [RequireComponent(typeof(LocalizeStringEvent))] public class PromptText : MonoBehaviour { LocalizeStringEvent localize; void Awake() => localize = GetComponent<LocalizeStringEvent>(); /// e.g. ShowPrompt("prompt.open_door") / ShowPrompt("prompt.pick_up") public void ShowPrompt(string entryKey) { localize.SetEntry(entryKey); // triggers OnUpdateString with the new text } public void Refresh() => localize.RefreshString(); // after changing argument values } ``` ## 2.4 Script: minimal label for anything else When the target isn't UI Toolkit or uGUI (a `TextMesh`, a 3D sign, a log), subscribe to `StringChanged` - and always unsubscribe. ```csharp // Assets/Scripts/UI/LocalizedTextMesh.cs using UnityEngine; using UnityEngine.Localization; [RequireComponent(typeof(TextMesh))] public class LocalizedTextMesh : MonoBehaviour { public LocalizedString text = new LocalizedString("UI", "sign.welcome"); TextMesh mesh; void Awake() => mesh = GetComponent<TextMesh>(); void OnEnable() => text.StringChanged += OnChanged; // fires immediately once loaded void OnDisable() => text.StringChanged -= OnChanged; // avoid the lapsed listener void OnChanged(string value) => mesh.text = value; } ``` The first subscription starts loading and delivers the current value; later locale changes call `OnChanged` again. Forgetting `OnDisable` is the **lapsed listener** bug from the Observer chapter ([[Game Programming Patterns (Review)]]). ## 2.5 Exercise Localize the entire HUD from [[3.HUD and Inventory Example]] without a single `+` on a string. Then switch to German at runtime and look for clipped labels - fix them in USS with flexible widths, not by shortening the German. ## Sources - UI Toolkit (Unity 6+) bindings: https://docs.unity3d.com/Packages/[email protected]/manual/UIToolkit.html - `LocalizedString` API (examples incl. SetBinding and IntVariable): https://docs.unity3d.com/Packages/[email protected]/api/UnityEngine.Localization.LocalizedString.html - `LocalizeStringEvent` API: https://docs.unity3d.com/Packages/[email protected]/api/UnityEngine.Localization.Components.LocalizeStringEvent.html