Skip to main content
Table of Contents

Editor language

The Localization Editor is localized itself. Its tabs, buttons, grid headers, status lines, and messages ship translated into English, German, French and Japanese, so the people who translate your application can work in a language they are comfortable with. This page explains how the editor picks its language, at design time and at run time. It also covers what to do when your translators need a language that is not built in. Choose between the two approaches like this. Use the built-in locales when one of the four languages is good enough, because it needs no setup at all. Use custom translation when you want the editor in another language or with your own terminology. In that case you translate the editor's UI with the same workflow you use for your own forms.

At design time: follows the IDE

When you open the editor from the IDE, it uses the language of the IDE. A German RAD Studio shows a German editor, and a Japanese RAD Studio shows a Japanese one, without any setting on your part. The editor reads the IDE's UI language from the same registry entry RAD Studio uses (Software\Embarcadero\Locales). When no language is configured, or the IDE runs in a language the editor does not ship, it falls back to English.

At run time: choose a built-in locale

At run time there is no IDE to follow, so the editor opens in English. To pick one of the built-in languages, call the Execute overload that takes a locale as its first parameter. Pass 'en', 'de', 'fr' or 'ja'. The value is case-insensitive:

Open the editor in a built-in languagePascalC++
procedure TFormMain.btnTranslateGermanClick(Sender: TObject);
begin
  { Opens the editor UI in German. 'en', 'de', 'fr' and 'ja' are built in;
    any other value is ignored and the default language is used. }
  LocalizationEditor.Execute('de');
end;

procedure TFormMain.btnTranslateFrenchClick(Sender: TObject);
begin
  { The close callback works the same as with the plain Execute overload. }
  LocalizationEditor.Execute('fr', EditorClosed);
end;

procedure TFormMain.EditorClosed(Sender: TObject);
begin
  { The translations are already saved; refresh text your code builds. }
  UpdateStatus;
end;
void __fastcall TFormMain::btnTranslateGermanClick(TObject *Sender)
{
  // Opens the editor UI in German. "en", "de", "fr" and "ja" are built in;
  // any other value is ignored and the default language is used.
  LocalizationEditor->Execute("de");
}

void __fastcall TFormMain::btnTranslateFrenchClick(TObject *Sender)
{
  // The close callback works the same as with the plain Execute overload.
  LocalizationEditor->Execute("fr", EditorClosed);
}

void __fastcall TFormMain::EditorClosed(TObject *Sender)
{
  // The translations are already saved; refresh text your code builds.
  UpdateStatus();
}

The overload behaves exactly like the plain Execute, including the optional close callback. It is non-blocking at run time, so put "after close" work in the callback. Some details are worth knowing:

  • The choice is per call. The locale applies to the editor you are opening and is reset when it closes. The next plain Execute uses the default again.
  • Unknown locales are ignored, not rejected. Any value other than the four built-in ones is ignored, and the editor opens in its default language. No exception is raised, so a value taken straight from your application's active language is safe to pass.
  • It does not change your application's language. The editor's own translations are kept apart from your application's translation file and from the localizer's active language.
  • Desktop only. The built-in locale selection applies to VCL and FMX applications.

Custom translation: translate the editor yourself

When your translators need the editor in a language that is not built in, such as Dutch, Polish, or Italian, or you want to adjust its wording, set the public property AllowCustomTranslation to True before calling Execute. The editor then stops using its built-in translations and treats its own UI like any other part of your application:

  • The editor window is no longer excluded from collection, so its controls are gathered under Forms like your own forms.
  • Its messages and status texts are registered as snippets under the Editor. prefix, for example Editor.StatusReady.
  • Collection rules are added so that the editor's list columns and its AI settings component are picked up as well.

You translate these entries with the editor, in the same way as the rest of your application, and they are saved to your translations.json. From then on, the editor follows your application's active language, as set by the localizer or a TTMSFNCLocalizationComboBox, instead of a built-in locale:

Translate the editor with your own translationsPascalC++
procedure TFormMain.btnTranslateClick(Sender: TObject);
begin
  { Skip the built-in editor translations: the editor's controls and its
    Editor.* message snippets are collected into the application's
    translation file and translated like any other string. }
  LocalizationEditor.AllowCustomTranslation := True;

  { The editor now follows the application's active language, for example
    Dutch, once its entries are translated and approved. }
  LocalizationEditor.Execute;
end;
void __fastcall TFormMain::btnTranslateClick(TObject *Sender)
{
  // Skip the built-in editor translations: the editor's controls and its
  // Editor.* message snippets are collected into the application's
  // translation file and translated like any other string.
  LocalizationEditor->AllowCustomTranslation = true;

  // The editor now follows the application's active language, for example
  // Dutch, once its entries are translated and approved.
  LocalizationEditor->Execute();
}

AllowCustomTranslation is public, not published, so set it in code. It defaults to False. While it is True, the locale parameter of Execute has no effect, because the editor's language comes from your own translations.

Putting it together: a translator tool

An in-house translation tool often needs both approaches. Translators who are happy with a built-in language get it directly. Everyone else gets the editor in the application's own language. The handler below opens the editor in the application's active language when that is one of the built-in four. Otherwise it switches to custom translation, so the strings you translated for the editor are used:

Built-in locale or custom translationPascalC++
procedure TFormMain.SwitchLanguage(const ALocale: string);
begin
  { Track the active application language where you switch it. }
  if DM.Localizer.TrySetLocale(ALocale) then
    FLocale := LowerCase(ALocale);
end;

procedure TFormMain.btnTranslateClick(Sender: TObject);
const
  BuiltInLocales: array[0..3] of string = ('en', 'de', 'fr', 'ja');
var
  vBuiltIn: Boolean;
  vLocale: string;
begin
  vBuiltIn := False;
  for vLocale in BuiltInLocales do
    if vLocale = FLocale then
      vBuiltIn := True;

  { Built-in language available: no setup needed. Otherwise use the
    translations made for the editor in the application's own file. }
  LocalizationEditor.AllowCustomTranslation := not vBuiltIn;
  if vBuiltIn then
    LocalizationEditor.Execute(FLocale)
  else
    LocalizationEditor.Execute;
end;
void __fastcall TFormMain::SwitchLanguage(const System::UnicodeString ALocale)
{
  // Track the active application language where you switch it.
  if (DM->Localizer->TrySetLocale(ALocale))
    FLocale = LowerCase(ALocale);
}

void __fastcall TFormMain::btnTranslateClick(TObject *Sender)
{
  const System::UnicodeString BuiltInLocales[] = {"en", "de", "fr", "ja"};
  bool vBuiltIn = false;
  for (const System::UnicodeString &vLocale : BuiltInLocales)
    if (vLocale == FLocale)
      vBuiltIn = true;

  // Built-in language available: no setup needed. Otherwise use the
  // translations made for the editor in the application's own file.
  LocalizationEditor->AllowCustomTranslation = !vBuiltIn;
  if (vBuiltIn)
    LocalizationEditor->Execute(FLocale);
  else
    LocalizationEditor->Execute();
}

Common pitfalls

  • Set AllowCustomTranslation before Execute. It is read when the editor opens. Changing it while the editor is open has no effect until the next Execute.
  • Custom translation adds the editor's strings to your translation file. The editor form and the Editor.* snippets become part of translations.json and ship with it. Enable custom translation only in builds that actually open the editor, such as a translator edition, if you want to keep the shipped file lean.
  • The first run shows the editor untranslated. With custom translation on, the editor's UI is collected the first time it opens, so it appears in the source language until you translate and approve its entries.
  • Approve the editor's translations too. Like every other string, an editor translation that is still Proposed is not applied.
  • Only four locales are built in. Execute('nl') silently opens the default language. Use custom translation for any other language.

See also