Description

Localization is the practice of swapping every piece of UI text in your software for the language the person reading it actually speaks. A "Save" button becomes "Speichern" for a German user, a "Settings" header becomes "Paramètres" for a French one, and so on for every label, tooltip, dialog, and help box you ship.
Most teams reach for a localization layer the moment their game grows past a single language. The same is true of editor tools: custom inspectors, build pipelines, asset processors, and editor windows are all software with a UI, and that UI benefits from translation just like the game itself does. Hand-rolling a switch over a language enum is fine for ten strings; it stops scaling fast. A small i18n layer that loads translation files on demand, falls back to the key when an entry is missing, and persists the user's language choice across sessions saves a lot of repeated work.
LocaleForge
GuardingPearSoftware's LocaleForge is a small, dependency-free localization toolkit for the Unity Editor. It maps each language to a flat key/value translation map, loads them from plain .json or .csv, ships with country flags and a ready-to-use language dropdown, and remembers the active language across editor restarts.
LocaleForge is editor-only by design. Every script lives under Editor/ and an asmdef keeps it out of player builds. That makes it a good fit for any tooling you write inside Unity: build wizards, custom importers, scene utilities, and so on.
Integration
LocaleForge is plug and play. Drop the package into Assets/, drop your translation files into Resources/Localizations/, and add the languages you ship to Configuration.SupportedLanguages. From there you call LocaleForgeEditorGUILayout.LanguageDropdown(...) once per editor window and translate every label through Localizations.t("some.key").
Once done you do not have to load the translations yourself. The dropdown's internal EnsureLoaded calls it lazily the first time the user picks a language, so your tool stays a single line of LanguageDropdown(...) plus a bunch of t(...) calls.
Code Example
A typical translated editor window looks like this:
public sealed class MyToolWindow : EditorWindow
{
[MenuItem("Window/My Tool")]
public static void Open() => GetWindow<MyToolWindow>();
private void OnGUI()
{
LocaleForgeEditorGUILayout.LanguageDropdown(
new GUIContent("Language"),
GUILayout.MaxWidth(260f));
GUILayout.Label(Localizations.t("menu.title"));
if (GUILayout.Button(Localizations.t("actions.save")))
{
// do work
}
}
}
Switching the language in the dropdown re-runs OnGUI, so every translated label in the window updates immediately.
File Formats
LocaleForge reads two formats, JSON and CSV, and you mix and match them per language.
JSON is nested. The parser flattens nested objects into dot-notation keys, so this file:
{
"menu": { "title": "Main", "play": "Play" },
"stats": { "score": "Score" }
}
resolves to the keys menu.title, menu.play, and stats.score. You then call Localizations.t("menu.title") and get back "Main".
CSV is two columns separated by ;, with optional double-quote quoting for values that contain ;, ", or line breaks. Translator-spreadsheet output reads better in this form:
menu.title;Main
menu.play;Play
The semicolon plays nicely with comma-decimal locales (German or French Excel) and you can include an optional key;value header row that LocaleForge auto-detects and skips.
Flags
Country flag PNGs ship with the package under Resources/Flags/PNG/64. FlagHelper.GetFlag(language) resolves each ELanguage to a sensible country (for example ELanguage.German returns Germany.png, ELanguage.Portuguese returns Portugal.png). Constructed or extinct languages without a clear country (Avestan, Esperanto) return null, and the dropdown handles that by rendering the row without a flag.
To use your own flag set, replace the PNGs in the folder or repoint Configuration.FlagRoot to your own folder.
Persistence
When the user picks a language in the dropdown, LocaleForge writes the choice to EditorPrefs under the key GUPS.LocaleForge.CurrentLanguage. The next time the editor starts up (or the next domain reload happens), the same language is restored. There is no scene object, no ScriptableObject, and nothing for the user to save manually.
Setting Localizations.Current = null clears the persisted choice and falls back to the OS UI culture (when it appears in Configuration.SupportedLanguages) or to Configuration.Fallback otherwise.
For Asset Authors
LocaleForge is editor-only and self-contained, so it slots neatly inside any Unity Asset Store editor extension that wants a localized UI without rolling its own i18n layer. You ship LocaleForge alongside your tool and it works exactly like the demo does. The setup steps are covered in the How to start page, and the FAQ has a dedicated answer for shipping LocaleForge inside your own asset.
If you do ship it inside your own asset, a small mention of GuardingPearSoftware in your asset's README, store page, or credits is appreciated. It is not required, just a kind gesture that helps other tool developers find LocaleForge.