Plugin localization: private RU/EN domain
Editor language, domain inheritance, and translation cleanup.
Addon translations need a private domain. This separates identical keys from game strings and other plugins. Editor and running-project locales can differ: do not change global project locale for a tool panel.
Complete RU/EN example
Create asset://addons/docs_localized/, save LocalizedDockPlugin.cs and plugin.cfg, build, and enable:
using Reckfell;
[Tool]
public partial class LocalizedDockPlugin : EditorPlugin
{
public const string DomainName = "addons/docs_localized";
private TranslationDomain? _domain;
private Translation? _en, _ru;
private EditorDock? _dock;
public override void _EnterTree()
{
_domain = TranslationServer.GetOrAddDomain(DomainName);
_domain.SetLocaleOverride(EditorInterface.Singleton.GetEditorLanguage());
_en = new Translation { Locale = "en" };
_en.AddMessage("Localized plugin", "Localized plugin");
_en.AddMessage("Inspect settings", "Inspect settings");
_ru = new Translation { Locale = "ru" };
_ru.AddMessage("Localized plugin", "Локализованный плагин");
_ru.AddMessage("Inspect settings", "Проверить настройки");
_domain.AddTranslation(_en); _domain.AddTranslation(_ru);
_dock = new EditorDock { Name = "DocsLocalizedDock", DefaultSlot = EditorDock.DockSlot.LeftUl };
_dock.SetTranslationDomain(DomainName);
_dock.Title = _dock.Tr("Localized plugin");
_dock.AddChild(new Button { Name = "Action", Text = "Inspect settings" });
AddDock(_dock);
}
public override void _ExitTree()
{
if (_dock is not null) { RemoveDock(_dock); _dock.QueueFree(); _dock = null; }
TranslationServer.RemoveDomain(DomainName);
_domain?.Dispose(); _domain = null;
_en?.Dispose(); _ru?.Dispose(); _en = null; _ru = null;
}
}[plugin]
name="Docs localized dock"
description="Private translation domain with RU and EN messages"
author="Reckfell documentation"
version="1.0.0"
script="LocalizedDockPlugin.cs"Two small dictionaries are created in code, avoiding hidden translation-file dependencies. For a larger addon, load your Translation resources into the same domain; this suite did not test PO/CSV import.
What provides isolation
GetOrAddDomain("addons/docs_localized") creates or returns the uniquely named domain. This code owns it; do not use the main domain's empty name or remove a domain shared with another addon.
SetLocaleOverride(GetEditorLanguage()) selects editor UI language on activation rather than game language. Assign SetTranslationDomain() to the panel before adding children; the button inherits it. Its checked GetTranslationDomain() matched the addon name without changing global project locale.
Button.Text retains the source key. Comparing it with a Russian string alone does not test localization: translation is used during display. Direct Tr() returned Inspect settings for en and Проверить настройки for ru. A missing key returned source text.
The example sets EditorDock.Title to an already translated string at panel creation. It does not promise live updates of all strings when editor language changes: disable/enable afterward or explicitly refresh your own UI.
Inspector fields and other objects
Domain inheritance follows the Entity hierarchy. EditorInspectorPlugin is a separate ref-counted object, and its EditorProperty enters the Inspector tree. Do not assume they inherit an EditorPlugin domain automatically. Assign domains explicitly when adapting them and test captions separately; see the Inspector example.
Deactivation
Unregister and remove the owned panel first, then remove the plugin-owned domain and release dictionaries. If modules share a domain, use one owner with usage tracking rather than unconditional RemoveDomain by each participant.
Checks covered child inheritance, editor-language override, RU/EN lookup, source-key fallback, unchanged project locale, and cleanup across reenablement. Visible translated pixels, PO/CSV import, plural forms, numeric formatting, Inspector-field translation, and live language switching were not tested.