Document home

Delphi

Delphi Localization and Internationalization

Delphi (Wikipedia) is a popular development tool for Windows, macOS, Linux, iOS, and Android. It is one of the best environments for localization. The reason is Delphi's structural user interface resource format (form file), very easy-to-use resource strings, excellent support for Unicode, and the possibility to compile everything into a single executable file. Soluling localization tool and service support Delphi.

The following chapters describe the Delphi localization step by step.

The end of the document contains links to get the full source code of the samples.

Delphi frameworks

Delphi has two frameworks. They are VCL and FireMonkey. Both frameworks have two main resource formats: forms and strings.

Framework Description

VCL VCL

VCL is the traditional Delphi framework that works only on Windows. You can compile the same project into 32-bit and 64-bit. Originally VCL was 16-bit (Delphi 1) but soon evolved into 32-bit (Delphi 2). Starting at Delphi XE2, VCL supports both 32-bit and 64-bit. The primary localization method is the VCL localization. In addition, FireMonkey localization can be used.

Read the tutorial

FireMonkey FireMonkey

FireMonkey is a new cross-platform Delphi framework. It supports Windows, Linux, macOS, iOS, and Android. The components are similar to VCL, but some component names are different, and many properties are also different. FireMonkey does not support resource DLLs on mobile platforms (Android and iOS). Use FireMonkey localization with FireMonkey applications.

Read the tutorial

Localization Process

Delphi applications are written in Delphi language (Wikipedia) and compiled to a native executable file or files. Depending on the framework, the primary localization method is different. The available methods are:

VCL VCL localization (binary localization)

When localizing Delphi VCL applications you don't need source codes but only the compiled application file(s).

After you have internationalized your application, create a new Soluling project, and select your original application (.exe) or library (.dll) file. Soluling adds the file into the project and extracts resources (e.g., forms and strings) from the file. If your application uses packages files (.bpl) that have forms or string, add them to the project too. Translate the project and build the output files. Here you have two choices: localized application or library files and resource DLL files.

Read more from How to localize Delphi VCL applications.

Localized application or library files

A localized application or library file is the default build output. For each language in the project, Soluling creates one localized application file that is identical to the original application, except the resources are in a different language. So if you have an EXE file, you will get a set of EXE files. For example, the German EXE file will be in de subdirectory (e.g., de\Project1.exe), and the French EXE will be in fr subdirectory (e.g., fr\Project1.exe). If your project has several output platforms such as WIN32 and WIN64 add only one of those into your Soluling project. Then add the other files as platform files into the added file. Use Platform files frame of source dialog to add the platform files.

Resource DLL files

Soluling can also create resource libraries. VCL has built-in support for resource libraries. A resource library is a dynamic library (e.g., DLL) that contains only resources, no code. The file extension of the resource library is the language code, and the pathname is the same as the original file. For example, if you have Project1.exe, then the German resource library is Project1.de, and the French resource library is Project1.fr.

You can make Soluling create localized application files or localized resource libraries or both. Read more about resource files.

Project group files

If your Delphi project has several platforms or configurations, you will have multiple output files. If you add a platform or configuration, you might have to add the new output files into the Soluling project manually. All this might be frustrating and easy to forget. Fortunately, there is a simpler way. You don't add any application or library file into your Soluling project, but you add a Delphi project group file (.groupproj). Soluling will then automatically locate the output files of the project and adds the output files into the project. If you modify your Delphi project such a way that there will be new output files, Soluling will automatically add those (unless you explicitly exclude them).

FireMonkey FireMonkey localization (project file localization)

Mobile platforms of FireMonkey do not support resource DLLs. In addition, FireMonkey applications are mostly mobile application where one package has to support multiple languages. This makes it impossible to use binary localization. Instead, Soluling uses a special multilingual resource that is linked into your application. The resource contains localized forms, strings, and other resources such as images and audio data.

After you have internationalized your application, create a new Soluling project file, and select Delphi project file (.dproj). Soluling adds the file into the project, finds all source code and form files of the project and extract resources (e.g., forms and strings) from the files. Translate the project and build the output file. The output file is a single multilingual resource file (NtLangRes.ntres) that contains the form, strings, and other resources in the languages you have in the Soluling project file. Add this resource file as custom RCDATA resource into your Delphi project. Choose Project | Resource and Images from Delphi.

Make sure the resource identifier is NtLangRes. Add the NtResource unit, call _T function for each form, and compile your project. Now it is multilingual and enabled for the runtime language change. FireMonkey localization can also be used in VCL projects. You also have a choice to deploy the resource file as a standalone file.

Read more from How to localize Delphi FireMonkey applications.

Form files localization

Soluling contains a third option to localize Delphi applications or part of the application: form files. You can add any form file (.dfm or .fmx) into a Soluling project. When scanning, the form data is scanned in the same way as the form would be part of a binary or project file. However, this is not the recommended way to localize a Delphi application. Use it only if you need to have localized form files. When localizing the complete applications, use either the VCL localization or FireMonkey localization.

Why do we support several localization methods

It may seem confusing that Soluling supports two different localization methods for Delphi. We would like to have only one method, binary, but with mobile FireMonkey it is not possible. This is because mobile FireMonkey platforms do not have a resource library concept. All application code and resource must be in the single application file. The fact is that most customers need a multilingual deployment for their mobile applications. This is why we developed our resource format to store localized forms, strings, and images and to embed that resource file as a custom resource inside the application file.

Internationalization

Internationalization is a process where you enable your application for localization.

Remove hard coded strings

The most important part is to remove hardcoded strings.

VCL VCL localization

Delphi has a built-in feature for this in Delphi language: resourcestring. You can use it if you use the VCL localization.

str := 'Hello world';

becomes to

resourcestring
  SHelloWorldMessage = 'Hello world';
...
str := SHelloWorldMessage;

Try to give a meaningful name for the resource string name. By default, the translator can only see the string name and the value. So SHelloWorldString is a better name as SStr1. If you want to pass more information to the translator, you can add comments to resource strings.

resourcestring
  SHelloWorldMessage = 'Hello world'; // loc This is a comment

Learn more about resource string comments.

FireMonkey FireMonkey localization

When using the FireMonkey localization, you cannot use a resource string feature. Instead, you wrap hard code string around _T function (in NtResource.pas).

str := 'Hello world';

becomes to

str := _T('Hello world');

You can also use Translate instead of _T. They are the same.

str := Translate('Hello world');

You can give an id for the string by passing a second parameter to the function.

str := _T('Hello world', 'HelloWorldMessage');

This makes it possible to add more than one "Hello world" strings and even translate them differently.

As with resource strings, you can add resource string comments.

str := _T('Hello  world'); // loc This is a comment

Delphi form file

Form files are the main resource files in a Delphi application. Each form is stored in a separate file. It uses is a text file format or optionally a binary file formal. Form files are embedded in the application file as a binary resource. VCL uses .dfm file extension. FireMonkey uses .fmx file extension. Even the file extension is different, both files use a similar file format. When Soluling localizes a form, it makes a full copy of the original form and then translates the selected properties. The properties are selected based on the scan property rules.

Initial language of a Delphi application

Delphi's class library, VCL, has built-in support to load a resource DLL when starting. By default, the available resource DLLs must locate on the same directory as the application, and the file name part must be the same. However, with Soluling's API, you can make your application load resource DLLs from any directory. The file extension of each resource DLL is the ISO language tag or Windows language code of the language the resource DLL uses. If the application is Project1.exe, then the German resource DLL is Project1.de, and German (Germany) resource DLL is Project.de-DE.

Delphi attempts to load resources in the following order, stopping when it finds one:

  1. Resource DLL matching the locale override
  2. Resource DLL matching the default locale
  3. Origianal resources

In addition it is possible to disable resource DLLs.

Locale override

First, Delphi looks if the system registry contains information about what resource DLL to load. This information is called the locale override. Delphi looks from following registry keys in order, stopping when it finds one:

Order Key Notes
1. HKEY_CURRENT_USER\​Software\Embarcadero\​Locales Delphi XE and later
2. HKEY_LOCAL_MACHINE\​Software\Embarcadero\​Locales Delphi XE and later
3. HKEY_CURRENT_USER\​Software\CodeGear\​Locales Delphi 2009 and later
4. HKEY_LOCAL_MACHINE\​Software\CodeGear\​Locales Delphi 2009 and later
5. HKEY_CURRENT_USER\​Software\Borland\​Locales  
6. HKEY_CURRENT_USER\​Software\Borland\Delphi\​Locales  

If the application finds such an entry, it loads a resource DLL matching data of the entry. For example, if we have an application created by Delphi XE or later and the application file is C:\NT\Deploy\Samples\Delphi\Sport\Project1.exe, then default place, where the locale override is written, is shown in the following picture.

Locale registry

If our application is D:\NT\Deploy\Samples\Delphi\Sport\Project1.exe, then Delphi finds that de extension should be used. It is going to look for D:\NT\Deploy\Samples\Sport\Project1.de resource DLL file. If the resource DLL file is found, then Delphi uses resources of that file.

Default locale

If no entry is found from the registry or the file matching the entry value does not exist, then the application is going to look for file matching the default locale. How this is done depends on the Delphi version that was used to compile the application.

Delphi 2009 or older

The default locale is the default locale of the user. This setting is specified in Regional and Language sheet of Control Panel.

If the user default locale is German (Germany), the application is going to look for Project1.de-DE. If that is not found, then the application looks for a country neutral resource. In this case, if Project1.de is found, the application uses that.

Delphi 2010 or later

The default locale is the user interface language of the operating system. It can not be changed without installing a new operating system or installing a multilingual operating system. If you want that your application still works like an application compiled with Delphi 2009 or older, you have to add NtInitialLocale into your application such way that its initialization party is called before application loads any resources. The right place is to add it as the first unit in the uses section of the program file.

program LanguageSwitch;
uses
  NtInitialLocale,
  Forms,
  Unit1 in 'Unit1.pas' {Form1};
   
{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

NtInitialLocale is located on <data-dir>\Library\Delphi\NtInitialLocale.pas.

If you want to see in more detail how VCL choose the language look for <delphi-dir>\Source\Win32\rtl\sys\System.pas and look for LoadResourceModule function. It performs the resource DLL loading.

Original resources

If the application can not find any of the above resource DLLs, it will use the original resources of the application or library file. Our sample application will use the resources of Project1.exe.

Disable resource files

There is no direct way to disable Delphi's build-in resource loading. If there is a matching resource DLL, the application will load it. The easiest way to disable using them is to remove the resource DLL file(s). If you want to keep them but not use them, there is a way to unload the loaded resource DLLs. This makes it possible to disable resource DLLs. To disable them, call TNtResource.DisableResourceDlls function. The function must be called before the application creates any forms. A good place for that is the initialization part of the main unit.

initialization
  TNtResource.DisableResourceDlls;
end.

See <data-dir>\Samples\Delphi\IgnoreResourceDll sample.

Resource DLL location

By default Delphi's RTL expects resource DLL files to locate in the same directory as the application or library files. For example, if your application is in C:\Samples\Project1.exe, then the German resource file should be in C:\Samples\Project1.de. If you want to store the resource DLL on a different directory, you have to use TNtBase.SetResourceDllDir method. You need to call this function before any form gets created. A good place would be the .dpr file.

program CustomDir;
uses
  NtBase,
  Vcl.Forms,
  Unit1 in 'Unit1.pas' {Form1};
  
{$R *.res}

begin
  // By default resource DLL directory is the same as the application dir.
  // if you want to use some other directory set the resource DLL directory before calling Initialize or CreateForm.
  TNtBase.SetResourceDllDir('Output');
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

The above code sets the resource DLL directory to be the Output sub directory of the application directory. The function also loads the resource DLL matching the initial language.

See <data-dir>\Samples\Delphi\CustomDir sample.

Runtime language change in Delphi

Delphi has a built-in feature to use resource DLLs. This makes it possible to deploy the same application file (.exe or .dll) with different resource DLLs and still be able to run the application in the language needed. When a VCL application starts, VCL looks for available resource DLLs from the same directory where the application file is located. If VCL can find a matching file, it uses resources of the resource DLL instead of the original application file. For example, if your application is Project1.exe, it can have German and French resource DLLs: Project1.de and Project1.fr. If you start the application on German Windows VCL will use resource data of Project1.de instead of resource data of Project1.exe.

Soluling extends VCL's resource DLLs by providing applications to perform runtime language change. This makes it possible to start the application in one language and later change the language on run time as many times as possible. Soluling's runtime language change is very fast and flicker-free. It does not require you to add any components to the forms. You only have to use Soluling to create resource DLLs and include one or two Soluling units to your project.

Create resource files(s)

VCL VCL localization

Soluling can either created localized files or localized resource DLLs for your Delphi application or library. By default, it creates localized files. You need to change the output format. Right-click the source name on the project tree to open the source dialog and select the Output sheet. In Output files group check Language checkbox.

VCL resource DLL

By default, the localized resource DLLs contain all the same resources as the original application or library, even if the resources have not been localized. You can make your resource DLL files smaller by unchecking Include all resources in output file checkbox. This makes Soluling to add only those resource items that have been localized.

FireMonkey FireMonkey localization

When you use FireMonkey localization, the output file is always NtLangRes.ntres. It is a file that contains localized forms, strings, and images. In order to use it you need to add the file as a custom RT_RCDATA resource. Read how to do this.

Runtime language change

Soluling contains functions that let you change the language of VCL application on run time. <data-directory>\Library\Delphi contains the Delphi units. NtLocalization.pas, NtTranslator.pas, and NtLanguageDlg.pas are the main units. NtLocalization.pas contains the functions for accessing resource data NtTranslator.pas contains a class that translates forms, and NtLanguageDlg.pas contains a dialog that shows possible resource DLL language. Users can select the one he or she likes to switch on. Use TNtLanguageDialog.Select in NtLanguageDlg.pas to show a language dialog and switch on a new language. The functions show a dialog where the user can select the language, and after selection, it turns on the new language by loading the new resource DLL and reloading existing forms. The functions work with any Delphi 5 and later. In addition, there are several Nt*Extension.pas units that extend TNtTranslator, providing translations of complex and binary properties of certain components.

Adding runtime language change is very easy. At a minimum, it only requires that you make Soluling to create resource DLLs instead of localized files, and you add two lines of code into your application (shown in green typeface).

implementation

uses
  NtLanguageDlg;

procedure TForm1.Language1Click(Sender: TObject);
begin
  TNtLanguageDialog.Select;
end;

The first parameter (optional, default is 'en') in function specifies the original language of the application. The original language is the language that you used in the forms and menu when you wrote the application. When you run the application and choose File | Language menu, the following dialog will be shown.

Language dialog

Select a new language and press, OK. It is simple like that! If you want to use your own user interface to show available languages and way to choose a new one you can use the functions in NtBase.pas to get list of available languages and finally set a new language active.

Form translation requires little bit more explanation. TNtLanguage.Select calls TNtTranslator.SetNew function that performs the resource loading. It load the resource DLL of the selected language (e.g. Project1.de). After loading new resources it translates all existing forms. It is very important to understand what happens here. Soluling reads the form data from localized resource DLL and assigns all properties of every component on every form. This means that after the process all components and properties of all forms have the values that the localized resource DLL contains. If you application contains any dynamic property (e.g. value set on run time) you have to update them after language change. If you used OnCreate to initialize the dynamic properties call the event again. Note that now the resourcestring values contain value in different language so you can use them in the same way as in your original code. Let's have an example.

<data-dir>\Samples\Delphi\VCL/LangaugeSwitch contains a simple application using resource DLLs. FormShow event calls UpdateItems that initializes the dynamic property values.

procedure TForm1.FormShow(Sender: TObject);
begin
  UpdateItems;
end;

procedure TForm1.UpdateItems;
resourcestring
  SSample = 'This is another sample';
begin
  Label2.Caption := SSample;
end;

When the application has been started, the form no longer is in the design time state. The design time state is the state that exists in the DFM file (e.g., form data). The caption of the label has been changed on run time. When a language change occurs, Soluling automatically updates the static properties (e.g., those that exist on the form data) but does not change dynamic properties. This is why you have to set them again.

The following code changes the language and translates dynamic properties.

procedure TForm1.Button1Click(Sender: TObject);
begin
  if TNtLanguageDialog.Select then
    UpdateItems();
end;

TNtLanguageDialog.Select changes the language and UpdateItems resets the dynamic properties using the new language. This might seem a bit complicated, but in most cases, your language change routine is on the main menu or the main form. In a situation like that, you only have one form existing at that moment, so you only have to take care of resetting the dynamic properties of that form. All forms that you create after language change will automatically use the new language.

A final note. Delphi IDE will add form creation code in the initialization section of the application for each form that you add to the project. You better keep only the main form there and create all other forms yourself at the moment when they are first needed.

program Project1;
...
begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

<data-dir>\Library\Delphi contains the full source code of all Soluling functions, and <data-dir>\Samples\Delphi contains several sample applications showing how to make localized and multilingual Delphi applications.

Extensions

TNtTranslator can translate all basic properties such as string, integer, color, etc. However, it can not translate binary properties and some complex properties of certain components. To translate also those properties, TNtTranslator uses extensions. They are add-ons to TNtTranslator and provide translations of binary and complex properties. There are several module classes, one for each type of component. The following table contains the module components.

Extension Unit Description
TNtStrings​Extension NtTranslator Translates properties whose type is TStrings or derived. This extension is automatically included whenever you add either NtTranslator or NtLanguageDlg unit into your application.
TNtImage​Extension NtImage​Extension Translates images such as Picture property of TImage control.
TNtListView​Extension NtListView​Extension Translates list items of TListView control and derived.
TNtTreeView​Extension NtTreeView​Extension Translates tree nodes of TTreeView control and derived.

To take module in use, just add its unit to the uses clause somewhere in the application. The following piece of code implements runtime language switch and adds a module for TTreeView component.

implementation
	  
uses
  NtLanguageDlg,
  NtTreeViewExtension;
  
procedure TForm1.Button1Click(Sender: TObject);
begin
  TNtLanguageDialog.Select;
end;

end.

<data-dir>\Samples\Delphi\LanguageSwitch shows how to use extensions.

You can easily implement your extension if needed for your components or 3rd party components that you use. See the source code of Soluling's extensions to learn how to write them. If you create your module, you have to register it. Call NtExtensions.Register function in NtTranslator.pas. A good place for that is the initialization part of your extension unit.

type
  TMyExtension = class(TNtExtension)
  ...
initialization
  NtExtensions.Register(TMyExtension);
end.

Fine tuning runtime language change

There are several ways you can control what forms, components, and properties are translated.

Excluding resources and data types

This is the easiest way to control what to translate because it is done on the Soluling application and not Soluling VCL units. In most cases, you can exclude resource types and/or any data types from your project. In that case, Soluling does not localize those resources or data types when creating localized resource DLLs.

NtEnabledProperties

NtTranslator.pas unit contains NtEnabledProperties variable.

var
  NtEnabledProperties: TTypeKinds;

It stores types that TNtTranslator should translate. By default, it is an empty set that means that every property is translated no matter its type. Because this includes Left, Top, Width, Height, and Picture properties, it might be that during the runtime language change, you will see some flickering. In such a case, you can enable only string types. A good place for that is the initialization part of the main unit.

initialization
  NtEnabledProperties := [tkString, tkLString, tkWString];
end.

In addition to giving you a flicker-free language change, it speeds up translation time. However, if you disable translation of integer properties, TNtTranslator does not update Left, Top, Width, and Height properties. This will disable any layout change done by your translator (e.g., in the case where translation did not fit original space).

Events

NtTranslator unit contains the following events.

var
  NtBeforeTranslate: TNtBeforeTranslateEvent;
  NtAfterTranslate: TNtAfterTranslateEvent;
 

TNtTranslator calls NtBeforeTranslate event every time before assigning a value to a property. The event contains a cancel parameter. It the event set this to True TNtTranslator does not set the property value but remains as it was. The following lines contain a sample event that disables translations of the Label2 component.

procedure BeforeTranslate(
  host: TComponent;
  obj: TObject;
  propertyInfo: PPropInfo;
  const currentValue: Variant;
  var newValue: Variant;
  var cancel: Boolean);
begin
  cancel := obj = Form1.Label2;
end;

If you want to modify the value to be set, change the value in the newValue parameter. If you want to disable translation of the property, set cancel to True. You register the event my assigning it to the global NtBeforeTranslate variable. A good place for that is the initialization part of the main unit.

initialization
  NtBeforeTranslate := BeforeTranslate;
end.

TNtTranslator calls NtAfterTranslate event every time after it has assigned a value to a property. Because these events are called before and after every property, the events are called several even thousands of items. This is why it is important to keep the event functions as fast as possible to prevent runtime language change to slow down too much. Of course, if you do not use events at all (e.g., do not assign the event variables), there is no speed penalty.

Database field captions

<data-dir>\Samples\Delphi\Sport contains a multilingual database sample that shows multilingual data.

Short cut text of menu items

<data-dir>\Samples\Delphi\Menu contains a sample that shows how to perform runtime language switch of short-cut text of menu items.

Common dialogs and message boxes

VCL's common dialogs and message boxes are not real Delphi components but wrap around the common dialogs and message boxes of the operating system itself. This is why they can not be localized, but they always appear in the same language as the operating system itself.

API documentation

<data-dir>\Library\Delphi contains Vcl.chm and Fmx.chm API documentation file.

Patterns such as Plurals, Genders and Selects in Delphi

Standard Delphi code uses a Format function to create composite format strings. It is not plural, gender, or select enabled, but fortunately, Soluling contains a multi-pattern enabled Format function for Delphi. The function support plural, gender, and select patterns.

If you use standard Format function, you might have something like this

str := Format('%d files', [fileCount]);

Replace '%d files' with '{plural, {one {%d file} other {%d files}}' and make that string a resource string and give it SMessagePlural name. Replace Format with Soluling's TMultiPattern.Format function. The modified code is

resourcestring
  SMessagePlural = '{plural, {one {%d file} other {%d files}}';  //loc 0: Amount of files
...
str := TMultiPattern.Format(SMessagePlural, fileCount, [fileCount]);

TMultiPattern.Format function is found in <data-dir>\Library\Delphi\NtPattern.pas.

See following samples:

Technology Sample directory
VCL <data-dir>\Samples\Delphi\VCL\Patterns
FireMonkey <data-dir>\Samples\Delphi\FMX\Patterns

File dialog format filters in Delphi

Both VCL and FireMonkey contain file dialogs that you can use to select a file to be opened and specify the file to be saved. The dialog contains Filter property that specifies file typed shown in the file type combo box.

dialog := TOpenDialog.Create(nil);
dialog.Filter := 'All supported files (*.xml;*.ini)|*.xml;*.ini|XML files (*.xml)|*.xml|Ini files (*.ini)|*.ini';

The format of the string is quite complicated. It contains the description text separator with file mask(s). There can be several of these pairs. If you place the above string into a string resource, the translator will see it as it is

All supported files (*.xml;*.ini)|*.xml;*.ini|XML files (*.xml)|*.xml|Ini files (*.ini)|*.ini

It may be that the translator will miss one | character or enter one extra. Either one is fatal, and your application will crash if the invalid localized file string is used. This is why it is better to use Soluling's file dialog filter format class.

dialog := TOpenDialog.Create(nil);

filter := TNtDialogFilter.Create;
filter.Supported('All supported files');
filter.Add('XML files', 'xml');
filter.Add('Ini files', 'ini');
dialog.Filter := filter.Value;
filter.Free;

This will expose translator three plain strings: "All supported files," "XML files," and "Ini files." They are easy to translate, and a translator cannot enter invalid values. If you don't want to use or cannot use the file dialog filter format class, use the file dialog filter validation to check if any of the filter translations are invalid.

Soluling.DialogFilter class is found from <data-dir>\Library\Delphi\NtDialog.pas. See following samples:

Technology Sample directory
VCL <data-dir>\Samples\Delphi\VCL\FileDialog
FireMonkey <data-dir>\Samples\Delphi\FMX\FileDialog

DRC file

VCL This applies only if you use VCL localization.

Delphi resource string file (.drc) is used to get unique and immutable context values for resource strings. When the Delphi compiler compiles a resource string, it stores the string value as a standard Windows string resource and assigns an id for the resource string. If you add new resourcestrings into the application, modify exiting, or delete strings, the compiler will assign most resource strings new ids. This can cause loss of translations or even replacing existing translations with other translations. To prevent this, specify a DRC file name so Soluling can use it to assign immutable ids for resource strings. The resource string id will change only if the programmer intentionally changes the resource string variable name. The file extension of DRC files use is .drc (e.g. C:\Samples\Release\Win32\Project1.drc).

You can localize a Delphi binary file without specifying a DRC file, but it is not recommended. Including the DRC context method, there are two other methods:

Context method Description
Use DRC file Soluling uses the DRC file to get unique and immutable resource strings context values. This option is effective only if a DRC file has been specified. If not, resource ids are used.
String value Soluling uses string values as context values. This is safe, even if you modify and recompile your application. However, if the application has the same text in two ore more resource strings, this method brings only one row to the project file. So all instances of the same text will share the same row, and you can no longer translate them differently.
Resource id Soluling uses resource ids as context values. This is potentially dangerous because the Delphi compiler may change the resource strings ids next time you compile your project. Even Soluling makes everything to resynch changed ids correctly; this may cause to loss of existing translations, or even mixing two existing translations. Both errors are very hard to locate and fix.

Because both alternative context methods have their limitations and dangers, it is very much recommended to specify a DRC file. To create a DRC file, start Delphi, choose Project | Options | Linking, and check Output resource string .drc file checkbox. If you use Delphi XE or older, select Detailed in the Map file selection.

DRC file

If you use platform files, you must use DRC files.

A sample that shows different context methods

For example, let's look at Sample.pas that has the following resource string.

resourcestring
  SHello = 'Hello World';

The following table shows how the context is generated in each context method.

Context method Context String
Use DRC file Sample.SHello Hello World
String value Hello World Hello World
Resource id 4095.65512 Hello World

As you can see, the resource id context id does not give any meaningful information (4095.65512) for the translator. The string value method also does not give any additional information compared to the string value ("Hello World" vs. "Hello World"). Using the DRC file method gives some extra information (Sample.SHello) in addition to the string value.

Resource and source code string comments

Delphi has a very easy-to-use resource string mechanism. However, it does not let you enter any comments for the string. Most often, translators would need an extra comment to translate the string properly. Soluling has a solution for this. You can add comments into your source code inside a comment next to the resource string.

Binary localization

When Soluling scans your Delphi application is first reads the resource items from the compiled file (.exe or .dll) and then resource string comments you have added from the source code files (.pas).

The following sample shows how to add a comment.

resourcestring
  SHello = 'Hello World'; // loc This is a comment

The above lines of code specify, "This is a comment" comment for SHello resource string.

The following sample shows how to add a comment with multiple lines. Use \n to mark a new line.

resourcestring
  SHello = 'Hello %s'; // loc Hello text \n %s: name of the user

The following line has a resource string tag that contains a comment ("Clicking this button closes the form") and sets the maximum length to 200 pixels:

resourcestring
SMsg = 'Click to close the form'; //slz MaxPixels=100 Clicking this button closes the form

Using resource string comments is optional. If you use them, you have to make sure your Delphi source has the Delphi project file specifies, and you have turned on source code scanning. Use the Project file sheet to do this.

FireMonkey localization

The following sample shows how to add a comment.

str = _T('Hello World'); // loc This is a comment

The above lines of code specify "This is a comment" comment for the "Hello World" string.

Unlike with binary localization, you don't have to turn on comment scanning. It is always on.

See <data-dir>\Samples\Delphi\VCL\Comments sample to see how to use comments.

Scan Rules in Delphi

Soluling uses scan rules to specify what components and properties are localized and how they are localized. Form resources are the most important resource type of Delphi applications. You use the Delphi IDE to create and edit forms. Each form is stored in a standalone form file (.dfm or .fmx). By default, a form file is a text file that contains the components and their properties in a structural format. Here a sample form, Unit1.dfm, that contains a label, a button, and a query. Let's look a form in Delphi's form designer.

Form on a designer

If you open Unit1.dfm on a text editor, you will see that it contains several property values, one value on each line.

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form'
  ClientHeight = 172
  ClientWidth = 328
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object Label1: TLabel
    Left = 8
    Top = 8
    Width = 74
    Height = 13
    Caption = 'This is a sample'
  end
  object Button1: TButton
    Left = 136
    Top = 80
    Width = 75
    Height = 25
    Caption = '&Get'
    TabOrder = 0
  end
  object ADOQuery1: TADOQuery
Parameters = <>
SQL.Strings = (
'SELECT * FROM Values')
Left = 104
Top = 64
end
end

When Delphi compiles an application, it adds each form as a resource inside the application. The following picture shows the resources of a typical Delphi application.

PE resources

Form resources are stored in the RC Data resource type. Each resource has the same name as the form itself but in capital letters. So TForm1 (Unit1.dfm) becomes TFORM1. The resource data is not the same as in the DFM file but the more compact binary format. If you view the form resource data in a binary editor, you will see the following bytes.

Binary DFM

You can see that it contains the same properties as Unit1.dfm but in a binary format. Compared to the textual DFM file, the form resource takes less space and is faster to process. Soluling reads and writes this binary form resource.

The original form file, Unit1.dfm, contains all the properties of the form. However, most of them are not needed to be translated. In most cases, you only want to translate most of the strings and a few other properties. For example, in some cases, you also want to change the width, height, or position of the form. In that case, you could "translate," for example, ClientWidth or ClientHeight properties. Soluling uses scan rules to control what property is localized and what not.

String typed properties

The rules work such a way that by default, all string typed properties are localized, and all other properties are not localized. This method picks up the following string properties.

Property Value
Caption Form
Font.Name Tahoma
Label1.Caption This is a sample
Button1.Caption &Get
ADOQuery1.SQL.Strings SELECT * FROM Values

All three Caption properties should be translated. However, Font.Name should not unless on very few circumstances. SQL statement should not be localized. To make this possible, Delphi's scan property rules can contain exception rules. They specify those string type properties that are not localized (remember by default all string typed properties are localized). By default, the rules contain exceptions for all those VCL properties that should not be localized. For example.

Component name Property name
* *Font.Name
TADOQuery SQL.Strings

The first rule excludes from all components all property names that end with Font.Name. The second rule excludes SQL.Strings property of TADOQuery.

Note! The component name is the component type name. For example, VCL's label control's type name is TLabel. The property name, however, is the name that is used in the DFM file. Sometimes this differs from the name used in Object Inspector. For example, the property to store TADOQuery's SQL has just SQL name in Object Inspector but SQL.Strings in DFM. You must use the later (SQL.Strings).

Images, fonts and colors

To localize images, fonts or colors you have to either add a rule for each property or turn localization of each type on. Right-click the source in the project tree and choose Options. Select the Data types sheet. It contains three radio buttons for each data type. Check the Yes radio button for data types you want to localize and click OK. Next time you scan, the new resource types are scanned.

Note! Turning a data type localization on will localize all properties having that data type unless there is an exclude rule of that property. Use scan rules to turn on the localization of a specific property.

See following sample: <data-dir>\Samples\Delphi\VCL\Image

Size and position properties

These properties are scanned, but by default, they are not visible in the translation grid. You can turn them visible by checking the Coordinate data type in the grid filter. You can also use the visual editor to change the size and position properties.

Binary properties

Some components use a custom binary format to store property data. This is not recommended, but because the feature is there, some components still use it. The drawback of custom binary property is that it can contain data in any format. Unlike the standard DFM format, custom binary properties do not contain data in a standard format. Each component uses its property format. The format can only be localized if Soluling knows the format and contains a dedicated parser for that format. We have implemented a dedicated parser for most common binary properties, such as items of TTreeView and TListView. The full list of formats is shown here.

You can see what property uses a binary format by viewing the DFM file. A binary property contains hexadecimal data inside { and } characters. The following sample form contains a TTreeView component with Items.NodeData property that uses a custom binary format.

object TreeView1: TTreeView
  Left = 8
  Top = 24
  Width = 193
  Height = 129
  Indent = 19
  TabOrder = 0
  Items.NodeData = {
    0303000000240000000000000000000000FFFFFFFFFFFFFFFF00000000000000
    000100000001034F006E006500240000000000000000000000FFFFFFFFFFFFFF
    FF00000000000000000000000001034F006E0065002400000000000000000000
    00FFFFFFFFFFFFFFFF0000000000000000000000000103540077006F00280000
    000000000000000000FFFFFFFFFFFFFFFF000000000000000000000000010554
    006800720065006500}
 end

If you have a component that uses a proprietary binary property format that is not one in this list, you have two choices. The first one is not to set the property on design time but set it on run time using string values from resource strings. This works with any component. Another is to ask us to implement a binary parser for the property. If the component is popular and we can easily get the full source code of the component, it is more likely that we will implement a parser for the component.

Other properties

It is very rare that you need to localize another kinds of properties than shown above. If you do, Soluling can handle it. Other property types such as enumerations and numbers are not localized unless you add them to the scan rules.

Output files with various platform

Depending on your target operating system Delphi compiler creates different application files. The following table contains the file formats used by all supported operating systems:

Opera​ting system Applica​tion and library file format Applica​tion file exten​sion Library file exten​sion Notes
Windows 32-bit 32-bit PE file .exe .dll  
Windows 64-bit 64-bit PE file .exe .dll  
macOS 32-bit MachO file none .dylib  
macOS 64-bit MachO file none .dylib  
iOS 32-bit MachO file none - iOS can not contain library files but only application files.
No resource DLLs available.
iOS 64-bit MachO file none - iOS can not contain library files but only application files.
No resource DLLs available.
Android 32-bit ELF file - .so Delphi creates only library files even if your project is an application.
No resource DLLs available.
Android 64-bit ELF file - .so Delphi creates only library files even if your project is an application.
No resource DLLs available.

Even the application file formats differ from each other, the resource data inside all files is the same. All files contain a form, string, image, etc. resources. When scanning, Soluling reads the resource data and when building Soluling writes the resource data.

In addition to multiple platforms, a Delphi project can also contain multiple configurations. There are used to specify different compiler options (e.g., Debug and Release). If you have both multiple platforms and configurations, the total number of output files is the product of platform count and configuration count. For example, if you have Debug and Release configuration and Win32 and Win64 platforms, you end up to four output files.

Each output file has an individual DRC file (.drc). When you add a platform file into a source, Soluling will automatically detect the location of the DRC file. If that fails, you have to enter the DRC file manually. You can not localize a Delphi application having more than one output file without using DRC files.

3rd party components in Delphi

3rd party components are just like the standard Delphi components shipped with Delphi. All components are ultimately inherited from TComponent, and they contain published properties that are stored in the form file of the owner form. Soluling has been designed to localize any kind of forms and data modules. Applications use the scan rules to select what properties to scan. Even the built-in scanning mechanism can properly parse all form data, some 3rd party components might have some issues.

The first possible issue is that the component does not write the property data into a form. If it is not in the form, Soluling can not localize it. If a property value does not appear in your project, even you have a proper scan rule, check that your form contains the value. Then check that the scanning rules include the property and do not exclude it.

Another possible issue is that the property is correctly written, but it does not use the standard data type but uses a proprietary binary format. Soluling can correctly read many binary format but if the format is unknown to Soluling, it can not parse it but ignorers the property. You have two choices here. The first one is to contact us and provide a sample that we can use. If the component is widely used and we can have full source code of the component (e.g., the component vendor sends it to us), we can implement a parser for that specific format. Another solution is not to set the property value on design time but runtime. That way, you can store data into standard string resources and use that data to set the proprietary property. Soluling can localize the standard string resources.

Some 3rd party components have issues with different locales. They might work correctly when the locale is English but may fail in Japanese. This is why we want to test all major 3rd party components. The following list contains the 3rd party component libraries we have tested and found localizable.

Component library Notes
Raize/Konopka Signature VCL controls  
TMS Component Pack  

Even if the above list does not contain the component library, you can still use Soluling to localize your application.

Reports

Most reporting components for Delphi use forms or data modules to configure reports. This makes them compatible with forms, and Soluling's from parser scans and localizes them just like any other form or data module. ReportBuilder also uses external file format .rtm that uses the same format as .dfm. If you want to localize external reports file, just add each .rtm file into your project.

Form layout checking

In many cases, translations are longer than original strings. That may cause some controls in your forms to overlap and some text in the controls to truncate. It may be difficult to manually detect these, especially because some control may be created and/or moved on runtime. This is why you should use the TLayoutChecker class to check your forms once you run your application automatically. The checker writes check results with screenshots that contain problem areas highlighted in the Checker subdirectory.

All you have to do is to call the LayoutChecker.Check function for all your forms. A good place to call it is the OnActivate event.

procedure TForm1.FormActivate(Sender: TObject);
begin
  NtLayoutChecker.Check(Self);
end;

LayoutChecker object is found from <data-dir>\Library\Delphi\NtChecker.pas. You need to use Delphi XE2 or later to use TLayoutChecker. If your application uses a localized resource DLL, then the output is written into a language-specific subdirectory of TLayoutChecker.OuputDir. For example, if your application is C:\Files\Sample\Win32\Debug\Project1.exe, then the default output directory is C:\Files\Sample\Win32\Debug\Checker and the German output directory is C:\Files\Sample\Win32\Debug\Checker\de.

See following samples:

Technology Sample directory
VCL <data-dir>\Samples\Delphi\VCL\Checker
FireMonkey <data-dir>\Samples\Delphi\FMX\Checker

Soluling's API for Delphi

Soluling contains a rich internationalization API for Delphi. It contains classes for runtime language switch, layout checking, and for many other cases that help you write better-internationalized code. The full source code of API is found from <data-dir>\Library\Delphi directory or on GitHub. API documentation is found from <data-dir>\Library\Delphi\VCL.chm.

C++Builder

C++Builder is not officially supported, but because C++Builder uses the same resource format as Delphi, most C++Builder VCL applications can be localized using Soluling.

Samples

<data-dir>\Samples\Delphi contains Delphi samples in two directories: VCL and FMX.

VCL VCL

GitHub and <data-dir>\Samples\Delphi\VCL contains following Delphi VCL samples:

Directory Description Notes
Driving Shows how to localize a Delphi application.
Original Original English driving application that has not been internationalized.
Localized Internationalized and localized driving application.
Multilingual As above but with a runtime language switch.

Try this first!

     
Active​Language Shows how to use TNtLanguageDialog and runtime language switch. Runtime language switch
Audio Shows how to use localized sound and audio resources.  
Checker Shows how to add layout checker into your applications.  
Comments Shows how to add resource comments.  
CustomDir Shows how to use resource DLLs that do not locate on the same directory as the application file.  
Custom​Select Shows how to build your own user interface to show available languages and to select a new language. Runtime language switch
Dual​Language Shows how to implement an application where some part of the user interface uses different language than the rest. Runtime language switch
File​Dialog Shows how to internationalize filter of file dialog in a smarter way.  
Font Shows how to localize fonts.  
Forms Shows how to virutal form inheritance to make localization easier. Runtime language switch
Format Shows how to localize composite format strings that use Format function. Runtime language switch
Ignore​ResourceDll Shows how to disable resource DLLs.  
Image Shows how to localize images.  
Image​List Shows how to localize images in the image list.  
IME Shows how to use input method editors.  
Japanese Shows how to do if the original language is Japanese instead of English. Runtime language switch
Language​Param Shows how to set initial language using a command line parameter. Runtime language switch
Language​Switch Shows how to perform runtime language switch on an application using images, tree views, and list views. Runtime language switch
Layout Shows how to use layout containers.  
Menu Shows how to perform runtime language switch on application using menu shortcuts.  
MessageDlg Shows how to localize message dialogs.  
Multi Shows how to localize a RAD Studio project group.  
Original Shows how to use a custom original language. Runtime language switch
Package Shows how to localize packages, either your custom ones or VCL's standard packages such as vclXXX.bpl.  
Patterns Shows how to create grammatically correct multi patterns such as plural, gender and select enabled messages. Contains following projects:
Plural Shows how to use plurals.
PluralCustom Shows how to use custom plural patterns for zero and two.
PluralMulti Shows how to use two plural enabled variables.
PluralMixed Shows various way to solve plural forms such as to ignoring the whole problem, using a home brew solution, and finally using the proper Soluling solution.
Gender Shows how to use genders.
GenderFinnish Shows how to use genders when the original language used in the application does not use genders (Finnish).
Select Shows how to use select values.
Runtime language switch
Resize Shows how to use resize controls in the localized forms.  
Resources Shows how to localize custom XML, JSON, SVG, INI, etc resources.  
SingleFile​Deploy Shows how to create a single EXE application that contains resource DLLs inside the EXE in custom resources. Runtime language switch
Sport Shows how to localize user interface and data the application uses. Contains following projects:
DatabaseGrid Sport data is stored in a database and shown on a grid.
DatabaseRecord Sport data is stored in a database and shown record by a record.
StringTable Sport data is stored in a STRINGTABLE resource
IniResource Sport data is stored in an INI file that is linked into EXE as a custom resource.
JsonResource Sport data is stored in a JSON file that is linked into EXE as a custom resource.
TextResource Sport data is stored in a text file that is linked into EXE as a custom resource.
XmlResource Sport data is stored in an XML file that is linked into EXE as a custom resource.
Runtime language switch
StringArray Shows how to replace resource string arrays when using runtime language switch. Runtime language switch
Version Shows how to check that the version number of the application and resource DLLs match.  
3rdParty Shows how to localize and check application using some of the 3rd party controls. Contains following projects:
DevExpress Shows how to localize and check applications using DevExpress controls.
Raize Shows how to localize and check applications using Raize/Konopka Signature VCL Controls controls.
TMS Shows how to localize and check applications using TMS controls.
VirtualTree Shows how to perform runtime language switch on an application using Virtual String Tree control.

You need to have each 3rd party control installed.

Runtime language switch

All samples are written in Delphi 10.3 Rio and tested as 32-bit and 64-bit. If you use older Delphi, you might need to modify the application slightly. Samples contain pre-compiled EXE files. VCL root directory contains the RAD Studio group project file that contains most of the samples. Driving, Multi, Package, Patterns, and Sport directories contain several samples, and this is why they all contain their own group project files.

FireMonkey FMX

GitHub and <data-dir>\Samples\Delphi\FMX contains following Delphi FireMonkey samples:

Directory Description Notes
Driving Shows how to localize a Delphi application.
Original Original English driving application that has not been internationalized.
Localized Internationalized and localized driving application.
Multilingual As above but with a runtime language switch.
Try this first!
     
Active​Language Shows how to use TNtLanguageDialog and runtime language switch. Runtime language switch
Comments Shows how to add resource comments.  
Custom​Select Shows how to build your own user interface to show available languages and to select a new language. Runtime language switch
Language​Switch Shows how to perform runtime language switch on an application that contains images, tree views, and list views. Runtime language switch
Patterns Shows how to create grammatically correct multi patterns such as plural, gender and select enabled messages. Contains following projects:
Plural Shows how to use plurals. Runtime language switch
PluralMulti Shows how to use two plural enabled variables.  
PluralMixed Shows various way to solve plural forms such as to ignoring the whole problem, using a home brew solution, and finally using the proper Soluling solution.  
SkiAndBicycle    
 
TreeView Shows how to localize tree and list views.  

All samples are written in Delphi 10.3 Rio and tested in Windows, macOS, Linux, iOS, and Android. If you use older Delphi, you might need to modify the application slightly. Samples contain pre-compiled EXE files. FMX root directory contains the RAD Studio group project file that contains most of the samples. Driving and Patterns directories contain several samples, and this is why they all contain their own group project files.

Configuring Delphi localization

Soluling can localize Delphi application and library files (.exe, .dll, .bpl) when using the VCL localization, Delphi projects (.dproj) when using the FireMonkey localization, Delphi project group files (.groupproj), and Delphi form files (.dfm or .fmx). Read the process paragraph to figure out what is the right method for you.

Configuring Delphi Application or Library File Localization

You can configure how to localize your Delphi application or library by selecting the item in the project tree, right-clicking, and choosing the Options menu. A source dialog appears that lets you edit the options. This source uses the following option sheets.

Note! When you add a Delphi project file into your project, Soluling will use the VCL localization.

Configuring Delphi Project File Localization

You can configure how to localize your Delphi project file by selecting the item in the project tree, right-clicking, and choosing the Options menu. A source dialog appears that lets you edit the options. This source uses the following option sheets.

Note! When you add a Delphi project file into your project, Soluling will use the FireMonkey localization.

Configuring Delphi Project Group File Localization

You can configure how to localize your Delphi project group file by selecting the item in the project tree, right-clicking, and choosing the Options menu. A source dialog appears that lets you edit the options. This source uses the following option sheets.

When you add a Delphi project file into your project Soluling will automatically add a sub-node that is a Delphi application or library file source, and initializes the source using properties of the selected Delphi project file.

Configuring Delphi Form File Localization

You can configure how to localize your Delphi form file by selecting the item in the project tree, right-clicking, and choosing the Options menu. A source dialog appears that lets you edit the options. This source uses the following option sheets.

Note! Use form localization only if you need to create localized form files. If you want to localize a Delphi application, select a binary file or project file.