Localizing Nexus Addons

Updated 21 October, 2026

Localizing Nexus Addons

Guild Wars 2 is played in many languages, and Nexus lets users choose theirs. The localization API lets your addon join in: you provide texts for each language under an identifier, and Nexus returns the right one for the user's current language, with sensible fallbacks. This guide covers translating texts, providing translations, and the details that keep a translated ImGui interface working.

How translation works

Every translatable text has an identifier, for example MYADDON_SETTINGS_TITLE. Localization_Translate looks it up:

  1. in the language the user selected in Nexus,
  2. then in English,
  3. and if it is not found there either, it returns the identifier itself.

Nexus currently ships English (en), German (de), French (fr), Spanish (es), Brazilian Portuguese (br), Czech (cz), Italian (it), Polish (pl), Russian (ru) and Chinese (cn). Users can add more languages, see Customizing Nexus.

Providing translations

Register your texts with Localization_Set(identifier, language, text), usually in your load function. Texts are UTF-8:

void RegisterTexts()
{
	APIDefs->Localization_Set("MYADDON_TITLE", "en", "Boss Timers");
	APIDefs->Localization_Set("MYADDON_TITLE", "de", "Boss-Timer");
	APIDefs->Localization_Set("MYADDON_TITLE", "fr", "Minuteurs de boss");

	APIDefs->Localization_Set("KB_MYADDON_TOGGLE", "en", "Toggle Boss Timers");
	APIDefs->Localization_Set("KB_MYADDON_TOGGLE", "de", "Boss-Timer ein-/ausblenden");
}
  • Always provide English. It is the fallback for every language you do not cover.
  • New texts become available from the next frame on.
  • Keybind identifiers can be translated too, which gives them a readable name on the Keybinds page.
  • Prefix your identifiers with your addon's name, since all addons and Nexus share the same table.

Keeping translations out of your code

For more than a handful of texts, keep translations in JSON files, one per language, so translators can work on them without touching code. Embed the files as resources in your DLL, then parse them in your load function and pass each entry to Localization_Set. Using the same format as Nexus's own locale files makes them familiar to community translators:

{
	"Identifier": "de",
	"DisplayName": "Deutsch",
	"Texts": {
		"MYADDON_TITLE": "Boss-Timer",
		"KB_MYADDON_TOGGLE": "Boss-Timer ein-/ausblenden"
	}
}

Embedding keeps your addon a single DLL, which is what Nexus's updater expects. You may write your own files into addons\Nexus\Locales, that folder belongs to Nexus and its users.

Translating texts

ImGui::Text(APIDefs->Localization_Translate("MYADDON_TITLE"));
  • Translate where you draw, every frame. The lookup is fast, and it means your interface switches language as soon as the user changes it.
  • Do not keep the returned pointer. It belongs to Nexus and becomes invalid when translations are reloaded, for example after a language change. Copy the string if you need it for longer than the current frame.
  • Strings passed to ImGui::Text are format strings. If a translation can contain a %, draw it with ImGui::TextUnformatted instead.

Localization_TranslateTo(identifier, language) returns the text in a specific language, with the same fallbacks. This is useful when the output is not for the user's interface, for example for chat messages that should always be in English.

Translated labels and ImGui IDs

ImGui identifies widgets and windows by their label. If the label changes with the language, ImGui treats the widget as a different one: windows forget their position and size, and open tree nodes collapse. Keep the visible text and the ID apart with ###, which tells ImGui to use only the part after it as ID:

std::string title = std::string(APIDefs->Localization_Translate("MYADDON_TITLE")) + "###MyAddonMainWindow";
if (ImGui::Begin(title.c_str(), &s_IsVisible))
{
	std::string label = std::string(APIDefs->Localization_Translate("MYADDON_ONLY_IN_COMBAT")) + "##OnlyInCombat";
	ImGui::Checkbox(label.c_str(), &Settings::OnlyInCombat);
}
ImGui::End();

With ##, the part after it is added to the ID, but the visible label still counts. With ###, only the part after it counts, which is what you want for windows. If you register a window to close with Escape, pass the full title including the ### part, since that is the window's name.

Characters and fonts

Nexus adds every character that appears in a loaded translation to the font atlas, so translated texts display correctly in every font, including fonts added by addons. Texts that do not come from the translation table, such as names from the game or the official API, may contain characters that are only available in the user's main interface font. See Working with Fonts in Nexus.

Tips for translatable interfaces

  • Leave room. German and French texts are often a third longer than English. Avoid fixed widths for buttons and labels.
  • Translate whole sentences, not fragments you assemble in code, since word order differs between languages.
  • Keep identifiers stable, and add new identifiers instead of changing the meaning of existing ones, so translations do not silently become wrong.
  • Invite your community. Players are often happy to translate an addon they use.