Json driven localization for Unity. You add a text key like
ui.settings.video.resolution from an editor window, and MiLocalization rewrites
the strongly typed LocalizationData class and adds the key to every language
file in the project. At runtime a drop in prefab loads the selected locale and
keeps every text component in sync.
- One editor window for adding fields, adding languages and typing translations
- Collapsible tree view with a debounced, segment aware path search
- The
LocalizationDataclass is generated for you, always matching the json - Adding a field expands the default locale first, then every other language gets it blank
- Order preserving json writer, so hand edits and untouched keys are never lost
LanguageText(uGUI) andLanguageTextTMP(TextMeshPro) components- Manager prefab that survives scene loads and remembers the player's language
- Unity 6.3 (
6000.3) or newer com.unity.ugui(pulled in automatically, it also provides TextMeshPro)
In Unity, open Window > Package Manager, click + > Add package from git URL, and paste:
https://github.com/innocentmiau/MiLocalization.git
To pin a specific release, append the tag:
https://github.com/innocentmiau/MiLocalization.git#v1.0.0
Alternatively, add it directly to your project's Packages/manifest.json:
{
"dependencies": {
"com.andreleandrodev.milocalization": "https://github.com/innocentmiau/MiLocalization.git#v1.0.0"
}
}- Open Tools > MiLocalization and press Set Up MiLocalization. This creates:
Assets/Resources/MiLocalization/MiLocalizationConfig.assetAssets/Resources/MiLocalization/Locales/en-US.jsonAssets/MiLocalization/LocalizationData.cs
- Drag
MiLocalizationManager.prefab(in the package underRuntime/Prefabs) into your first scene. It marks itselfDontDestroyOnLoadand stays alive for the rest of the session.
In the Fields tab, type the dotted path and press Add Field:
ui.settings.video.resolution
MiLocalization then:
- Adds the key to the default locale json, which defines the field order.
- Adds the same key, blank, to every other locale json, reordered to match.
- Regenerates
LocalizationData.csso the nested classes match the json.
Keys have to be plain ascii, because each segment becomes a C# field name. If you
type ui.settings.video.resolução it is rejected with the ascii version offered
next to the box, so Use This turns it into ui.settings.video.resolucao in one
click. Accents belong in the translated text, not in the key.
The same tab shows the fields as a collapsible tree. Groups start collapsed and show how many texts are inside, so a large file opens as a short list of top level sections instead of hundreds of rows. Each group has its own arrow, all the way down. Expand All and Collapse All are there for the times you want everything at once.
Rows alternate between the normal background and a slightly tinted one, so a long list stays readable across the width of the window.
Every leaf has a text box for the locale picked in Editing Locale, so you can
type the translations without leaving the window. Press Save when done, the
button shows a * while there are unsaved changes.
Closing the window with unsaved changes asks whether to save them first. Unsaved edits also survive a script recompile, which would otherwise wipe them without a word.
The Search box narrows the tree, and matching groups are opened automatically so results are never hidden behind an arrow. It matches on path segments:
| Query | Shows |
|---|---|
ui |
everything under ui |
ui.settings.gameplay |
just that group's fields |
ui.settings.gameplay.title |
that one field |
ui.settings.vid |
the video branch, while you are still typing |
resolution |
any field called resolution, wherever it lives |
Because matching is segment aware, searching ui will not drag in a field named
guiScale. The search runs 100ms after the last keystroke rather than on every
repaint, so typing a long path does not refilter once per character.
Show Untranslated narrows the tree to fields with no text yet in the locale you are editing, and the button shows the count. A field only stops counting as untranslated once the file is saved, so it stays on screen while you type into it instead of vanishing after the first character. It combines with the search box.
The R button on a row renames that field across every language, keeping each language's text and the field's position in the file. The new name goes through the same ascii and identifier rules as a new field, and nothing is written unless every locale can take it, so a name clash cannot leave half the files renamed.
This replaces the old remove-and-re-add workaround, which lost the text in every language.
The CSV tab exports one row per key and one column per language:
key,en-US,pt-PT
ui.menuPlay,Play,
ui.notice,"Press {BUTTON}, then wait ""a bit""",
Pick which languages to include, export, and the translator fills in the blank columns in any spreadsheet. Import CSV matches columns by locale code and rows by key, then shows a summary of exactly what will change before writing anything.
Import only fills in fields that already exist, it never creates them, so a spreadsheet cannot inject a key that would become invalid C#. Unknown keys and unknown locale columns are reported and skipped. The file is written as UTF-8 with a byte order mark, which is what stops spreadsheets opening it in the local codepage and mangling accented text.
In the Languages tab, enter a locale code (the file name, for example
pt-PT) and the display name shown to players (for example Portugues). The new
file is created with the full field structure, every text blank.
Each language has four buttons:
- Make Default switches which locale drives the generated class and gets expanded first.
- Edit changes the locale code and the display name. Renaming the code renames the json file, updates the code stored inside it, and moves the default locale setting with it if that language was the default.
- Ping shows the json file in the Project window.
- Delete removes the language and its translations, after a confirmation. If you delete the default locale another language is promoted to default, and deleting the last remaining language is refused.
Renaming a locale code changes the id used by LoadLocale, so a player whose saved
choice was the old code falls back to the default on their next launch.
Put LanguageText on a GameObject with a Text, or LanguageTextTMP on one
with a TMP_Text. They register with the manager and refresh themselves whenever
the language changes.
Text Key is a picker, not a text box. It lists the keys that actually exist, so you cannot typo one, and every row shows the field name with its full path dimmed next to it:
title ui.settings.gameplay.title
title ui.settings.video.title
The search box uses the same segment aware matching as the Fields tab, so ui
lists everything under ui, ui.settings.gameplay narrows to that group, and a
bare title finds every field with that name across the file. Arrow keys move,
Enter picks, Escape closes.
If a key is set that no longer exists, for example after a rename in another branch, the inspector shows a warning under the field. Hovering shows the default locale's text for that key.
You can put the same dropdown on your own components:
[LocalizationKey]
[SerializeField] private string textKey;For text built in code:
string label = LocalizationManager.Text("ui.settings.video.resolution");LocalizationManager.Instance.LoadLocale("pt-PT");
LocalizationManager.Instance.LoadLocaleByLanguageName("Português");Either call saves the choice to PlayerPrefs and refreshes every registered
text. To build a language dropdown, read
LocalizationManager.Instance.FindAllLocales() and use each entry's
LanguageName and Locale.
LocalizationManager.LanguageChanged fires after every switch, for anything that
is not a plain text component.
Packages installed from a git url are read only, so the generated class is
written into your project instead, at Assets/MiLocalization/LocalizationData.cs
by default. It implements ILocalizationData from the package, which is how the
manager can deserialize and read it without referencing your assembly.
If you move the generated class into a folder with its own assembly definition, add a reference to Andreleandrodev.MiLocalization on that asmdef.
Do not edit the generated file by hand, it is overwritten on every change. Edit the json instead and press Regenerate LocalizationData in the Setup tab.
MIT (c) Andre Leandro