# 5. Language Menu, Saving and Runtime Scripting Part of [[Localization]]. Previous: [[4. Asset Tables Fonts Icons and Voice]]. Next: [[6. Localizing Game Data Dialogue and Pseudo-Localization]]. > [!note] Verification status > `LocalizationSettings.InitializationOperation`, `SelectedLocale`, `SelectedLocaleChanged`, `AvailableLocales`, `StringDatabase.GetLocalizedStringAsync(TableReference, TableEntryReference, ...)`, `Locale.LocaleName`, `PlayerPrefLocaleSelector` (stores the code with `PlayerPrefs.SetString`) from the 1.5 API/manual pages (2026-11-10). `LocaleIdentifier.CultureInfo` and `DropdownField.choices/index` are from memory of the APIs. **Code not compiled in Unity.** ## 5.1 Initialization The system loads locales and preloaded tables asynchronously. Anything that reads `SelectedLocale` or strings at startup should wait for `LocalizationSettings.InitializationOperation`. Bindings (part 2) and `StringChanged` handle this for you; code that calls `GetLocalizedString()` synchronously doesn't (and `WaitForCompletion` isn't supported on WebGL). ## 5.2 Persisting the player's choice Order the **Startup Locale Selectors**: Command Line -> **Player Pref** -> System -> Specific. The Player Pref selector stores the chosen locale code with `PlayerPrefs.SetString`, so a player who picked German stays in German even if their OS is English. Put **System** after it so first launch follows the device language. (Command line first lets QA force a language: `-language=ja`.) If your save system already owns settings ([[High Scores, Saves and Player Stats]], [[PlayerPrefs Advanced]]), store the locale code there too and drop the Player Pref selector - one source of truth. ## 5.3 Script: language dropdown (UI Toolkit) UXML: a `DropdownField` named `language` in your settings screen. ```csharp // Assets/Scripts/Localization/LanguageMenu.cs using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Localization; using UnityEngine.Localization.Settings; using UnityEngine.UIElements; [RequireComponent(typeof(UIDocument))] public class LanguageMenu : MonoBehaviour { public string dropdownName = "language"; DropdownField dropdown; List<Locale> locales; bool suppress; IEnumerator Start() { yield return LocalizationSettings.InitializationOperation; dropdown = GetComponent<UIDocument>().rootVisualElement.Q<DropdownField>(dropdownName); if (dropdown == null) { Debug.LogWarning("Language dropdown not found"); yield break; } locales = LocalizationSettings.AvailableLocales.Locales; var names = new List<string>(locales.Count); foreach (Locale l in locales) names.Add(NativeName(l)); dropdown.choices = names; Sync(LocalizationSettings.SelectedLocale); dropdown.RegisterValueChangedCallback(OnDropdownChanged); LocalizationSettings.SelectedLocaleChanged += Sync; } void OnDestroy() => LocalizationSettings.SelectedLocaleChanged -= Sync; // Show each language in its own language: "Deutsch", "日本語" - players find their language // even when the UI is in one they can't read. Needs a font that covers every script (part 4). static string NativeName(Locale l) { var culture = l.Identifier.CultureInfo; return culture != null ? culture.NativeName : l.LocaleName; } void OnDropdownChanged(ChangeEvent<string> evt) { if (suppress) return; int i = dropdown.index; if (i >= 0 && i < locales.Count) LocalizationSettings.SelectedLocale = locales[i]; } void Sync(Locale selected) { if (dropdown == null || locales == null) return; suppress = true; dropdown.index = locales.IndexOf(selected); suppress = false; } } ``` **Explanation** - `yield return` on the initialization handle is safe in a coroutine and avoids blocking the main thread. - The `suppress` flag prevents a feedback loop: changing the locale raises `SelectedLocaleChanged`, which sets the dropdown, which would otherwise raise a change event. - Since 1.5.12 the package can also localize **dropdown choices** themselves (bind UI Toolkit dropdown choices to a `LocalizedStringList`/`LocalizedStringGroup`) - useful for option menus like "Low/Medium/High". The language list should *not* be localized; native names are the convention. ## 5.4 Script: strings from code For logs, notifications, or values you compose at runtime: ```csharp // Assets/Scripts/Localization/Loc.cs using System; using System.Collections; using UnityEngine; using UnityEngine.Localization.Settings; using UnityEngine.ResourceManagement.AsyncOperations; /// Tiny facade over LocalizationSettings (a Service Locator in practice). public static class Loc { /// Async: preferred. Calls back on completion. public static IEnumerator Get(string table, string entry, Action<string> done, params object[] args) { AsyncOperationHandle<string> op = LocalizationSettings.StringDatabase.GetLocalizedStringAsync(table, entry, args); yield return op; done?.Invoke(op.Status == AsyncOperationStatus.Succeeded ? op.Result : quot;#{table}/{entry}#"); } /// Sync: blocks until loaded (not supported on WebGL). OK after initialization with preloaded tables. public static string GetNow(string table, string entry, params object[] args) => LocalizationSettings.StringDatabase.GetLocalizedString(table, entry, args); } ``` Usage: `StartCoroutine(Loc.Get("UI", "toast.saved", s => toast.Show(s)));` Returning `#table/entry#` on failure makes missing strings obvious in playtests instead of silently blank. ## 5.5 Exercise Add the language dropdown to the settings screen, pick German, quit, relaunch: still German? Then launch with `-language=ja` from a shortcut: Japanese wins (command line selector first). ## Sources - `LocalizationSettings` API: https://docs.unity3d.com/Packages/[email protected]/api/UnityEngine.Localization.Settings.LocalizationSettings.html - Startup Locale Selectors (Player Pref, Command Line, System): https://docs.unity3d.com/Packages/[email protected]/manual/LocaleSelector.html - `PlayerPrefLocaleSelector` API: https://docs.unity3d.com/Packages/[email protected]/api/UnityEngine.Localization.Settings.PlayerPrefLocaleSelector.html - `Locale` API: https://docs.unity3d.com/Packages/[email protected]/api/UnityEngine.Localization.Locale.html - Scripting overview: https://docs.unity3d.com/Packages/[email protected]/manual/Scripting.html - `LocalizedStringDatabase` API: https://docs.unity3d.com/Packages/[email protected]/api/UnityEngine.Localization.Settings.LocalizedStringDatabase.html