# 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