# 6. Localizing Game Data, Dialogue and Pseudo-Localization Part of [[Localization]]. Previous: [[5. Language Menu Saving and Runtime Scripting]]. Next: [[7. Localization Pitfalls and VR]]. > [!note] Verification status > `LocalizedString` in ScriptableObjects mirrors the `GameQuest` example on the `LocalizedString` API page. Editor APIs (`LocalizationEditorSettings.GetStringTableCollections()`, `TableCollectionName`, `SharedData.Entries`) appear in the 1.5 API pages; `StringTableCollection.StringTables`, `StringTable.GetEntry(long)`, `SharedTableEntry.Id/Key` and `LocalizationTable.LocaleIdentifier` are from memory of the 1.x API. Pseudo-Localization and CSV/Google Sheets pages exist in the 1.5 manual (2026-11-10). **Code not compiled in Unity.** ## 6.1 LocalizedString in ScriptableObjects (Type Object) Item and quest definitions from [[Inventory Systems]] and [[Quest Systems]] store display text as `string`. Swap those for `LocalizedString`: ```csharp // Assets/Scripts/Items/ItemDefinition.cs (localized version) using UnityEngine; using UnityEngine.Localization; [CreateAssetMenu(menuName = "Game/Item Definition")] public class ItemDefinition : ScriptableObject { public string id = "potion_small"; // stable, never localized public LocalizedString displayName = new LocalizedString("Items", "item.potion_small.name"); public LocalizedString description = new LocalizedString("Items", "item.potion_small.desc"); public Sprite icon; // icons rarely need localizing public int maxStack = 10; } ``` ```csharp // Assets/Scripts/Quests/QuestDefinition.cs (localized fields only) using UnityEngine; using UnityEngine.Localization; [CreateAssetMenu(menuName = "Game/Quest Definition")] public class QuestDefinition : ScriptableObject { public string id = "wolf_trouble"; public LocalizedString title = new LocalizedString("Quests", "quest.wolf_trouble.title"); // Smart: "Defeat {count} wolves ({progress}/{count})" public LocalizedString objectiveText = new LocalizedString("Quests", "quest.wolf_trouble.obj"); public int targetCount = 5; } ``` Bind them in UI with `SetBinding` (part 2): for the objective, add `IntVariable`s named `count` and `progress` to a *copy* of the `LocalizedString` per UI row - don't add variables to the asset's shared instance at runtime, or every row shares (and fights over) the same variables. ```csharp // Assets/Scripts/Quests/QuestTrackerRow.cs using UnityEngine.Localization; using UnityEngine.Localization.SmartFormat.PersistentVariables; using UnityEngine.UIElements; public class QuestTrackerRow { readonly IntVariable progress = new IntVariable(); readonly IntVariable count = new IntVariable(); public QuestTrackerRow(Label titleLabel, Label objectiveLabel, QuestDefinition quest) { titleLabel.SetBinding("text", quest.title); // Fresh LocalizedString per row, pointing at the same table entry. var objective = new LocalizedString(quest.objectiveText.TableReference, quest.objectiveText.TableEntryReference); count.Value = quest.targetCount; objective.Add("count", count); objective.Add("progress", progress); objectiveLabel.SetBinding("text", objective); } public void SetProgress(int value) => progress.Value = value; } ``` ## 6.2 Dialogue - One `LocalizedString` per line, keys like `dlg.blacksmith.greet.01`. Keep speaker names in their own entries (`npc.blacksmith.name`). - Use globals for the player's name (`{global.player-name}`, part 3) instead of string replace. - **Typewriter speed per language** ([[TypewriterEffect.cs]]): characters-per-second tuned for English is too slow for CJK (more information per character) and too fast for German. Store a speed multiplier as **Locale metadata** or a small lookup keyed by locale code. - Branching dialogue editors ([[7.Dialogue Visual Node Editor]]) should store **keys**, not text. Text lives in tables; the graph stays language-neutral. ## 6.3 Pseudo-localization: test before translating A **Pseudo-Locale** transforms your source strings automatically: accented characters (`Ŝéţţîñĝš`), expansion (padding), brackets around each string. Add one in the Locale Generator / Pseudo-Locale asset, switch to it, and play: - Text without brackets = **hard-coded string** someone forgot to localize. - Clipped brackets = **layout too tight** for expansion. - Boxes instead of accented letters = **font coverage** problem. Run it on every UI screen before sending strings to translators. ## 6.4 CSV and Google Sheets Translators don't use Unity. Export a String Table Collection to **CSV** (or connect **Google Sheets**) from the collection's Extensions; import back after translation. Keep the key and a comment column; lock keys from editing. The Google Sheets extension's OAuth flow got clearer error reporting in 1.5.12 (changelog). ## 6.5 Script: missing translations report ```csharp // Assets/Editor/MissingTranslationsReport.cs using UnityEditor; using UnityEditor.Localization; using UnityEngine; using UnityEngine.Localization.Tables; public static class MissingTranslationsReport { [MenuItem("Tools/Localization/Report Missing Translations")] static void Report() { int missing = 0; foreach (StringTableCollection collection in LocalizationEditorSettings.GetStringTableCollections()) { foreach (SharedTableData.SharedTableEntry shared in collection.SharedData.Entries) { foreach (StringTable table in collection.StringTables) { StringTableEntry entry = table.GetEntry(shared.Id); if (entry == null || string.IsNullOrWhiteSpace(entry.Value)) { missing++; Debug.LogWarning( quot;[{collection.TableCollectionName}] '{shared.Key}' missing in {table.LocaleIdentifier.Code}", table); } } } } Debug.Log(missing == 0 ? "No missing translations." : quot;{missing} missing translations."); } } ``` Run it before every build; wire it into CI later (see the Pipelines section of the index for build automation notes). ## 6.6 Exercise Convert the item and quest definitions in your project to `LocalizedString`, switch to the Pseudo-Locale, and screenshot every screen. Count bracket-less strings; each is a bug. ## Sources - `LocalizedString` API (ScriptableObject quest example): https://docs.unity3d.com/Packages/[email protected]/api/UnityEngine.Localization.LocalizedString.html - Pseudo-Localization: https://docs.unity3d.com/Packages/[email protected]/manual/Pseudo-Localization.html - CSV: https://docs.unity3d.com/Packages/[email protected]/manual/CSV.html - Google Sheets: https://docs.unity3d.com/Packages/[email protected]/manual/Google-Sheets.html - `LocalizationEditorSettings` API: https://docs.unity3d.com/Packages/[email protected]/api/UnityEditor.Localization.LocalizationEditorSettings.html