Skip to content

Repository files navigation

MiLocalization

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 LocalizationData class 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) and LanguageTextTMP (TextMeshPro) components
  • Manager prefab that survives scene loads and remembers the player's language

Requirements

  • Unity 6.3 (6000.3) or newer
  • com.unity.ugui (pulled in automatically, it also provides TextMeshPro)

Installation (Git URL)

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"
  }
}

Setup

  1. Open Tools > MiLocalization and press Set Up MiLocalization. This creates:
    • Assets/Resources/MiLocalization/MiLocalizationConfig.asset
    • Assets/Resources/MiLocalization/Locales/en-US.json
    • Assets/MiLocalization/LocalizationData.cs
  2. Drag MiLocalizationManager.prefab (in the package under Runtime/Prefabs) into your first scene. It marks itself DontDestroyOnLoad and stays alive for the rest of the session.

Usage

Adding a text

In the Fields tab, type the dotted path and press Add Field:

ui.settings.video.resolution

MiLocalization then:

  1. Adds the key to the default locale json, which defines the field order.
  2. Adds the same key, blank, to every other locale json, reordered to match.
  3. Regenerates LocalizationData.cs so 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.

Browsing and editing texts

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.

Searching

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.

Finding what still needs translating

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.

Renaming a field

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.

Handing files to a translator

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.

Adding a language

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.

Showing text in the UI

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");

Changing language at runtime

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.

How the generated class works

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.

License

MIT (c) Andre Leandro

About

Json-driven localization for Unity: an editor window for keys and translations, a generated strongly-typed data class, and runtime components that stay in sync.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages