NextLocalization
ScriptableObject-Based Localization for Unity. Lightweight, code-friendly, and zero-dependency.
Before you start: Delete the Assets/NextLocalization/Example/ folder and all
its contents once you've reviewed the examples. It is for demonstration only and should not ship with your
game.
Overview
NextLocalization is a lightweight, code-friendly localization system built entirely on ScriptableObjects. It ships with a full-featured custom editor window, CSV import/export, automatic key-rename refactoring across your entire project, and an extensible mod system so you can add custom inspector fields without ever touching package code.
Key Features
All data lives in .asset files — VCS-friendly, no JSON parsing at
runtime.
Languages · Table Sheet · Inspector, all in one unified view.
Saves player's language choice to disk automatically.
Serializable field with an in-editor searchable key picker.
Export to CSV, edit in Excel/Sheets, import back seamlessly.
Rename a key → all LocalizationKey refs update automatically.
Add custom inspector UI panels via LocalizationEntryInspectorMod.
No external packages required. TMP needed only for optional UI helpers.
Installation
-
1Import the package via the Unity Package Manager or drag the
NextLocalizationfolder into yourAssets/directory. -
2TextMeshPro is required for
TextConverter,DropdownConverter, andLanguageDropdown. Install it via Window → Package Manager → TextMeshPro if not already present. -
3Open the Localization Editor from the Unity menu bar:
NextHaven Studio → Localization Editor
-
4The editor will automatically create the
LocalizationTableasset at:
Assets/Resources/Localization/LocalizationTable.asset
The Resources/Localization/ path is required for runtime loading via
Resources.Load. Do not move LocalizationTable.asset out of a
Resources folder.
Quick Start (5 Minutes)
Step 1 — Add Languages
-
1Open NextHaven Studio → Localization Editor.
-
2In the Languages panel (left), click + to add a new entry (e.g.
English). -
3Click Create Asset in the Inspector panel to generate the language's
.assetfile. -
4Repeat for each language (e.g.
Turkish,German).
Step 2 — Add Keys
-
1In the Table Sheet panel (center), click + → Add Section (e.g.
menu). -
2Right-click the section → Add Key (e.g.
play,quit). -
3Keys are addressed as
section.keyat runtime — e.g."menu.play".
Step 3 — Enter Translations
-
1Select a Language in the Languages panel.
-
2The Inspector (right) shows all sections and keys with editable text fields.
-
3Type the translation for each key.
Step 4 — Save
Click Save Asset in the toolbar.
Step 5 — Initialize at Runtime
Add LocalizationInitializer to a root GameObject in your first scene:
Add Component → LocalizationInitializer
Set Default Language to English.
Step 6 — Use in Code
// Simple key lookup
string label = Loc.Get("menu.play"); // → "Play"
// Formatted lookup ({0} placeholder)
string round = Loc.Get("game.round", 3); // → "Round 3"
// Change language
Loc.SetLanguage("Turkish");
Localization Editor Window
Open via NextHaven Studio → Localization Editor.
Languages Panel
| Action | How |
|---|---|
| Add language | Click + |
| Delete language | Select → click − |
| Rename | Double-click the name |
| Assign existing asset | Drag a LocalizationLanguage asset into the Asset field |
Table Sheet Panel
The center panel shows the shared structure — sections (folders) and keys — across all languages.
| Action | How |
|---|---|
| Add section | + → Add Section |
| Add key | Right-click section → Add Key |
| Rename | Double-click the item |
| Delete | Select → Delete key, or right-click → Delete |
| Reorder | Drag and drop |
| Move key | Drag key onto the target section |
Toolbar
| Button | Description |
|---|---|
| Review | Diff window showing all unsaved changes |
| Export Template | CSV with keys only — for translators |
| Export CSV | Full CSV with all translations |
| Import CSV | Additive import from CSV |
| Save Asset | Commits all changes to disk. Turns green when dirty |
Runtime API — Loc
Loc is a static class — no instance or singleton reference needed.
Initialization
Loc.Init(); // Load saved language or first available
Loc.Init("Turkish"); // Load a specific language
LocalizationInitializer calls this automatically. Manual calls are rarely needed.
Getting Translations
// Simple
string text = Loc.Get("menu.play");
// → "Play" (key found)
// → "[menu.play]" (key missing — never throws)
// Formatted (localization value: "Round {0} of {1}")
string text = Loc.Get("game.round_of", 3, 10);
// → "Round 3 of 10"
Changing Language
Loc.SetLanguage("German");
// Loads the German asset, fires OnLanguageChanged, saves preference to disk
Properties
string lang = Loc.CurrentLanguage; // "English" / "Turkish" / ...
string[] langs = Loc.AvailableLanguages; // all languages in the table
bool ready = Loc.IsInitialized;
bool saved = Loc.HasSavedLanguage;
Language Changed Event
// Subscribe in OnEnable
Loc.OnLanguageChanged += RefreshMyUI;
// Always unsubscribe in OnDisable — prevents NullReferenceException
Loc.OnLanguageChanged -= RefreshMyUI;
Warning: Always pair += in OnEnable with -= in
OnDisable.
Scene Setup — LocalizationInitializer
| Property | Description |
|---|---|
| Default Language | Language loaded on first run (if no saved preference exists) |
- → Acts as a singleton — duplicates across multiple scenes self-destruct.
-
→
Calls
DontDestroyOnLoad— persists across scene transitions. - → If the player already has a saved language, that takes priority over Default Language.
UI Components
7.1 TextConverter
Keeps a TMP_Text in sync with the current language automatically.
Setup: Add TextConverter to a GameObject with a TMP_Text
component. Set Title Key in the Inspector.
7.2 DropdownConverter
Localizes the option labels of a TMP_Dropdown.
Setup: Add DropdownConverter to a GameObject with a
TMP_Dropdown. Add one Option Key per dropdown option (top-to-bottom order).
7.3 LanguageDropdown
A self-configuring TMP_Dropdown that lists all available languages and switches the active
language on selection.
Setup: Add LanguageDropdown to a GameObject with a TMP_Dropdown.
No additional configuration needed.
LocalizationKey Field
A serializable field type that stores a key string and provides a searchable picker in the Inspector.
public class MyUI : MonoBehaviour
{
[SerializeField] private LocalizationKey myKey;
private void Start()
{
// LocalizationKey implicitly converts to string
string value = Loc.Get(myKey);
}
}
In the Inspector:
- • Text field — shows the current key value.
- • 🔍 button — opens a searchable dropdown listing all sections and keys.
Tip: When you rename a key in the editor and save, all LocalizationKey
references across prefabs, ScriptableObjects, and scenes are updated automatically.
CSV Import & Export
Exported Format
Key,English,Turkish,German
menu.play,Play,Oyna,Spielen
menu.quit,Quit,Çık,Beenden
game.round,Round {0},Tur {0},Runde {0}
Import Rules
- ✓ Import is additive — existing values are preserved unless the CSV cell contains a new value.
- ✓ Empty CSV cells are skipped; they do not overwrite existing translations.
- ✓ After importing, click Save Asset to commit.
Tip: Use Export Template to give translators a file with only the keys and empty columns — they fill in their language column and send it back.
Rename Refactoring
When you rename a key or section in the editor and click Save Asset:
-
1Changes are detected by comparing internal IDs (not names), so renames are never missed.
-
2A confirmation dialog lists every detected rename.
-
3All
.asset,.prefab, and.unityfiles are scanned. -
4Every
LocalizationKeyreferencing the old name is updated in place. -
5Modified assets are saved automatically.
Important: The refactor opens closed scenes additively to update them. Make sure you have no unsaved scene work before clicking Save Asset when keys have been renamed.
Mod System
Extend the Localization Editor without modifying any package files.
Creating a Mod
using UnityEditor;
[InitializeOnLoad]
public class MyMod : LocalizationEntryInspectorMod
{
public override string ModId => "my.mod";
static MyMod() => Register(new MyMod());
// Return true to draw this panel for the given key
public override bool ShouldDraw(string sectionName, string fullKey)
=> sectionName == "dialogue";
// Draw your IMGUI UI here
public override void DrawFields(string sectionName, string fullKey)
{
EditorGUILayout.LabelField("Voice Clip", EditorStyles.boldLabel);
// ...
}
}
Save Hook
[InitializeOnLoad]
public static class MySaveHook
{
static MySaveHook()
{
LocalizationEntryInspectorMods.OnCommitSave += () =>
{
// Save your own data alongside the localization asset
};
}
}
Rename Hook
LocalizationEntryInspectorMods.OnCommitRename += renames =>
{
foreach (var (oldKey, newKey) in renames)
{
// Update your own data structures
}
};
Marking the Save Button Dirty
// Call this whenever your mod has unsaved changes
// The Save Asset button will turn green to signal pending changes
LocalizationEntryInspectorMods.MarkModDirty();
File & Folder Structure
FAQ
Loc API, LocalizationKey, and the editor window
have no TMP dependency. Only TextConverter, DropdownConverter, and
LanguageDropdown require TMP.
Loc.Get("missing.key") returns "[missing.key]". It never
throws an exception.Editor/ folder from builds. Only the
runtime scripts and your Resources/Localization/ assets are included.save.loc) in
Application.persistentDataPath. It contains only the language name string (e.g.
English).
Loc.Get() before Loc.Init()?
▾
Loc.Get() auto-initializes using the saved or first available
language if called before initialization.ShouldDraw().