Skip to content

Internationalization

David Benson edited this page Oct 7, 2026 · 1 revision

How the draw.io user interface is translated: the resource files, their format, how the editor and the viewer choose and load a language, how to use strings in core code and in plugins, and how to contribute a translation fix. For developers who add UI text, write plugins, run their own deployment or fork the editor.

Overview

Piece Where Role
English strings resources/dia.txt Master list of keys (about 2,200) with English text
Translations resources/dia_<code>.txt One file per language, same keys
Key reference resources/dia_i18n.txt Maps every key to itself, used with ?lang=i18n
Lookup mxResources parse, get, add
Language list mxLanguageMap in js/diagramly/Init.js Codes and names shown in Extras > Language
Startup load App.main in js/diagramly/App.js Loads one language file before the UI is created
Runtime switch EditorUi.prototype.setAndPersistLanguage in js/diagramly/EditorUi.js Loads another file and fires languageChanged

All paths are relative to src/main/webapp/.

Resource files

The resources folder holds dia.txt, 58 dia_<code>.txt files, and a README.md and CONTRIBUTING.md with notes about the folder.

Of the 58 files:

  • 42 are languages offered by the editor. They are the entries of mxLanguageMap: id, ms, bs, bg, ca, cs, da, de, et, es, eu, fil, fr, gl, it, hu, kl, lt, lv, nl, no, pl, pt-br, pt, ro, fi, sv, vi, tr, el, ru, sr, uk, he, ar, fa, th, ta, ko, ja, zh, zh-tw. English (en) is also in the map and uses dia.txt.
  • 15 have no entry in mxLanguageMap: am, bn, eo, gu, hi, hr, kn, ml, mr, my, si, sk, sl, sw, te. The editor neither lists nor loads them as shipped; a lang parameter with one of these codes gives English.
  • dia_i18n.txt maps each key to its own name (about=about). The map has an i18n entry with an empty display name, so it is not in the menu, but ?lang=i18n loads it and the UI then shows resource keys instead of text. Use it to find the key behind a string.

File format

copyOf=Copy of {1}
pageWithNumber=Page-{1}
timeAgo={1} ago

mxResources.parse reads the files line by line:

  • The key is the text before the first =. The value is everything after it, up to the end of the line; a trailing carriage return is removed. There is no trimming, so do not put spaces around =.
  • Lines that start with # and lines without = are ignored.
  • Values are used verbatim. mxResources.resourcesEncoded is false, so \uXXXX and %XX sequences are not decoded. Files are UTF-8; write characters directly.
  • \n stays as two characters. A few consumers turn it into a line break (for example mxGraph.prototype.getTooltip replaces it with <br> for the collapse-expand tooltip), so keep it wherever the English text has it.
  • {1}, {2}, ... are placeholders filled from the params array of mxResources.get. Translations may reorder them (dia_de.txt has timeAgo=Vor {1}) but must keep every one.
  • A key with an empty value (key=) stores an empty string, and mxResources.get returns it as is. An empty value does not fall back to English.
  • A couple of values contain an HTML link. Keep the markup unchanged in translations.
  • Keys are camelCase, with two exceptions (collapse-expand, draw.io). The order of the lines does not matter.

How a language is chosen

js/diagramly/Init.js sets the global mxLanguage. The first rule that gives a value wins:

  1. window.mxLanguage, if it was already set before Init.js runs, for example in js/PreConfig.js. bootstrap.js loads PreConfig.js first in development mode, in the desktop app and on hosts other than *.draw.io and *.diagrams.net. A value set here overrides everything below, including the lang parameter.
  2. The lang URL parameter, for example ?lang=de.
  3. The language the user chose in Extras > Language, stored as language in the .drawio-config object in localStorage (mxSettings.setLanguage).
  4. In the desktop app, the system locale that the app passes as the appLang URL parameter, lower-cased and without the region.
  5. On a fixed list of official hosts in Init.js, such as app.diagrams.net, the browser language (navigator.language without the region), if it is supported.
  6. Otherwise mxLanguage stays null and the editor uses English.

Init.js also builds mxLanguages, the supported codes: every key of mxLanguageMap except en. mxClient.js copies these into mxClient.languages and mxClient.language; when mxLanguage is null, mxClient.language is the raw navigator.language. Like mxLanguage, both mxLanguageMap and mxLanguages are only set by Init.js if no earlier script has defined them.

Use the codes exactly as they appear in mxLanguageMap: lower case, with pt-br and zh-tw as the only regional codes. App.main passes mxLanguage to mxResources unchanged, so ?lang=pt-BR or ?lang=de-CH is not a supported code and loads English.

To give your own deployment a default language without overriding lang and the user's choice, check both before you set mxLanguage in PreConfig.js, as shown in Configuration. The Docker image has a DRAWIO_LANG variable for this; see its README. The full list of URL parameters is on drawio.com.

How strings are loaded

The editor

Before it creates the UI, App.main sets mxResources.loadDefaultBundle = false and loads exactly one file:

mxResources.loadDefaultBundle = false;
doLoad(mxResources.getDefaultBundle(RESOURCE_BASE, mxLanguage) ||
	mxResources.getSpecialBundle(RESOURCE_BASE, mxLanguage));

For a supported code this is resources/dia_<code>.txt; for English, null or an unsupported code it is resources/dia.txt. doLoad fetches the file and passes its text to mxResources.parse. If the file cannot be loaded, the page shows an error with a link that retries in English.

English is not loaded underneath a translation. js/grapheditor/Graph.js overrides mxResources.get so that a missing key returns the default value passed by the caller or, if there is none, the key itself. A key missing from dia_de.txt therefore shows up as its key name in a German UI, which is why new keys go into the translation files as well as into dia.txt.

RESOURCES_PATH (default resources) and RESOURCE_BASE (default RESOURCES_PATH + '/dia') are globals that Init.js only sets if they are undefined, so a deployment can serve the files from elsewhere by setting them in PreConfig.js. The extension comes from mxResourceExtension, which defaults to .txt.

Switching at runtime

Extras > Language calls EditorUi.prototype.setAndPersistLanguage(code). It saves the setting, sets mxClient.language, and calls mxResources.add(RESOURCE_BASE, null, callback) with loadDefaultBundle still false, which parses the new file over the current strings. It then closes open dialogs (their text is static), fires a languageChanged event on the EditorUi, and reopens the start dialog if no file is open. UI parts that build their own text update through EditorUi.prototype.dependsOnLanguage(fn), which calls fn now and on every languageChanged.

Offline

Language files are not precached. When the service worker is active (see Self-hosting), each file is cached the first time it is used. While the browser is offline, Extras > Language disables languages that are not cached (EditorUi.prototype.isLanguageAvailableOffline). The defaultLanguages configuration key installs a list of languages for offline use at startup; see Configuration.

The viewer

The viewer bundles (viewer.min.js, viewer-static.min.js) compile dia.txt in: the app target of the Ant build generates Graph-Resources.js (a mxResources.parse call with the text of dia.txt) and includes it in the viewer. GraphViewer.loadLanguageResources then loads <base>_<code>.txt over it, so in the viewer a missing key falls back to English.

  • The code comes from GraphViewer.getLanguage: mxLanguage, else mxClient.language (the browser language), reduced to a supported code. English needs no request.
  • The base comes from GraphViewer.getResourceBase: RESOURCE_BASE, except that the viewer bundles, whose STYLE_PATH defaults to https://viewer.diagrams.net/styles, load from the resources/dia next to STYLE_PATH.
  • Load errors, for example a missing CORS header on your own host, are ignored and the viewer stays in English.

A page that embeds the viewer can force a language by setting window.mxLanguage before it loads the script. See Embedding.

Right-to-left languages

Arabic (ar), Persian (fa) and Hebrew (he) values are mostly wrapped in Unicode bidirectional controls: U+202B (right-to-left embedding) before the text and U+202C (pop directional formatting) after it. They make the browser lay out the value as right-to-left text, including any punctuation and Latin words in it, inside the left-to-right UI. Copy the markers when you edit such a value.

The editor layout itself is not mirrored for these languages: there is no dir="rtl" handling in the UI code. The shape search strips these control characters before matching translated terms (Sidebar.js). Text direction inside diagram labels is a separate cell style, textDirection (see Styles).

Using strings in code

To add UI text, add a key to dia.txt and look it up with mxResources.get(key, params, defaultValue):

// Plain lookup
mxUtils.write(hd, mxResources.get('properties'));

// {1}..{n} are filled from the array: "Copy of " + title in English
var name = mxResources.get('copyOf', [title]);

// Explicit default if the key may be missing
var text = mxResources.get('timeAgo', [str], '{1} ago');
  • Actions and menus. An action registered with ui.actions.addAction('myKey', fn) uses its key as its label. Action.prototype.getTitle looks the label up with mxResources.get and, if the label ends in ..., appends ... after the translated text. Add myKey=My Text to the resources.
  • Escaping. Values are plain text, and placeholders often carry user data such as file or page names. Insert text with mxUtils.write or textContent. If you build HTML, escape the value, as DrawioFile.js does with mxUtils.htmlEntities(mxResources.get('readOnly')). See Security.
  • Language changes. Text you put into long-lived DOM goes stale when the user switches language. Set it inside ui.dependsOnLanguage(function() { ... }) or listen for languageChanged. Menu items are created each time a menu opens, so the labels of actions in menus need nothing extra.
  • New keys in a fork. Because only one file is loaded, add a new key to dia.txt and to every dia_<code>.txt, with the English text where you have no translation. Otherwise users of other languages see the key name.

Strings in plugins

Plugins add their own keys with mxResources.parse, which accepts the same key=value text as the files. This is what the bundled plugins do, for example plugins/anonymize.js:

Draw.loadPlugin(function(editorUi)
{
	mxResources.parse('anonymizeCurrentPage=Anonymize Current Page');

	editorUi.actions.addAction('anonymizeCurrentPage', function()
	{
		// ...
	});

	var menu = editorUi.menus.get('extras');
	var oldFunct = menu.funct;

	menu.funct = function(menu, parent)
	{
		oldFunct.apply(this, arguments);
		editorUi.menus.addMenuItems(menu, ['-', 'anonymizeCurrentPage'], parent);
	};
});

Keys parsed by a plugin survive a language switch, because the language files do not contain them. To ship translations, choose the text by language code, as Editor.applyCustomResources does with mxClient.language, and parse it again on languageChanged. Prefix your keys so they cannot collide with core keys. See Plugins.

Without code, the resources key of the editor configuration overrides existing strings or adds new ones. Each value is a string, or an object with one entry per language code and main as the fallback:

{"resources": {"saveAs": {"main": "Save a Copy", "de": "Kopie speichern"}, "myKey": "My Text"}}

Editor.configure stores the object in Editor.customResources, and js/diagramly/Editor.js wraps mxResources.parse so that these values are applied again after every parsed file, including after a language switch. They take precedence over the built-in files. See Configuration and the configuration reference.

Translating diagram content

The UI language and the text inside a diagram are separate. For multilingual diagrams, Graph.js can read a label from a language-specific attribute of the cell's XML value:

  • With Graph.translateDiagram on (?translate-diagram=1, or Extras > Diagram Language > Language Code), a cell shows label_<code> instead of label if that attribute exists, for example label_de.
  • The code is Graph.diagramLanguage: the diagram-language URL parameter, else mxClient.language.
  • Tooltips (tooltip_<code>) and placeholder attributes (<name>_<code>) are resolved the same way.

The user-facing guide is Enable in-diagram text translation. See Graph model for user objects and attributes.

Contributing a translation

Translation fixes are the one kind of pull request the project accepts. Edit dia_<code>.txt in a fork and open a pull request against dev, following resources/CONTRIBUTING.md: change values only, keep placeholders and markup, and keep the English text where you have no translation (an empty value is shown as empty text). To find the key, search for the current text in dia_<code>.txt or open the editor with ?lang=i18n. A new language needs an issue first. Details are in Contributing.

See also

Clone this wiki locally