Difference between revisions of "Internationalisation programmation"

From Gestinux Wiki
Jump to navigation Jump to search
 
 
(30 intermediate revisions by one other user not shown)
Line 1: Line 1:
 
== Why ==
 
== Why ==
  
The common method using resource strings is a little tedious to implement.
+
The common I18N method using resource strings requires a development environment and programming skills to make or improve translations.
It requires development environment and programming skills to make translations available.
 
  
That's why I have used another system, with some success on a large application.
+
For Gestinux, we have used another system.
  
Translation are read by the software from mere inifiles and applied when forms are loaded. This is quick enough.
+
Translation are read by the software from mere text files (inifile like) and read when forms are loaded. This is quick enough.
  
This way, it is possible to define or modify translations directly when running the application, without developing tools and skills.
+
This way, it is possible to define or modify translations directly when running the application, without developing tools and skills. There is no need to recompile anything to add a new language, or to modify an existing language.
 +
 
 +
GTranslator is an utility integrated in the application, to make and check translations easily.
 +
 
 +
It is also possible to toggle between 2 languages at runtime, to check a new translation.
  
 
== How it works ? ==
 
== How it works ? ==
Line 14: Line 17:
 
=== Language file ===
 
=== Language file ===
  
One inifile by language must be provided.
+
Translation are stored in text files, referred as '''language files'''.
 
 
Currently it is stored in the ''languages'' subdirectory of the directory containg the main executable :
 
The name of the file is made from the Posix code of the language (en_US for English American, fr_FR for french...)
 
  
The Parameter file must (at least) contains a parameter in the ''General'' Section for the name of the language (in it's language).
+
There are two language files for each language, and they are merged at startup in a single Inifile in memory.
  
[General]
+
To locate, or create or improve a translation, see [[Translating#The_code_for_your_language|this page]].
 
 
LanguageName=English
 
 
 
Each parameter files named language/%%_%%.ini, and having this valid parameter, will make a language available in the application.
 
 
 
Thus, there is no need to recompile anything to add a new language.
 
  
 
=== Simple controls ===
 
=== Simple controls ===
  
For Edit, Label, Buttons, MainMenu, PopupMenu  (or Gedit, GLabel, GButton, GMainMenu, GPopupMenu in gestinux 1.0)
+
For each control of [[Gestinux_util]], the translation is searched in the language file :
  
The sections is the Form name.
+
The section is the Form name (the form class name in version under development).
The parameter name is the component name, followed by a dot and the property name.
+
The parameter name is the component name, followed by a dot and the property name to translate.
 
The parameter value is the translation
 
The parameter value is the translation
  
E.g :
+
If the file or the parameter is not found, the design properties are used.
  
[FormConnection]
 
  
LabelUsername.Caption=Username
+
E.g :
 
 
EditUsername.Hint=Enter your username
 
 
 
BtnOk.caption=Connect
 
  
BtnOk.Hint=Click here to connect
+
[FormConnection]
 +
LabelUsername.Caption=Username
 +
EditUsername.Hint=Enter your username
 +
BtnOk.caption=Connect
 +
BtnOk.Hint=Click here to connect
 +
MenuItemQuit.Caption=Exit
  
MenuItemQuit.Caption=Exit
+
=== GGrid and GDbGrid ===
  
In gestinux 0.5, to load the translations, we call  '''TranslateForm(Self)''' in the OnCreate Form event.
+
For each column, indexed by its position, we can define the title.
If the file or the parameter is not found, the design properties are used.
+
The ''Columns'' property must be defined, and all columns must be added, to allow translations (and other features).
This procedure can be called also when the language is changed, to have a dynamic translation.
 
 
 
In gestinux 1.0, we should use specific visual components and forms and there is nothing to do. The translation is automatic.
 
 
 
=== CustomStringGrids, dBGrids ===
 
 
 
''Or for GGrid and GDbGrid in gestinux 1.0''
 
 
 
For each column, indexed by its position, we can define the title
 
  
 
E.g :
 
E.g :
  
Grid.Employees.0.Title=Name
+
GridEmployees.Name.Title=Name
 +
GridEmployees.Address.Title=Address
 +
GridEmployees.City.Title=City
  
Grid.Employees.1.Title=Address
+
=== Messages ===
 
+
Grid.Employees.2.Title=City
+
MessageDlg and ShowMessage procedures must not be used, since they don't use the same translation system.
 
 
The Columns property must be defined to allow translations (and other features).
 
  
----
+
Messages are identified by keywords not used for control's translations. They are stored in the form section, along with other control's translations.
  
=== Messages ===
+
Parameters are allowed.  
Messages are identified by keywords not used for control's translations.
 
  
In gestinux 0.5 Messages are stored in a special section [Messages].
+
Example :
In gestinux 1.0 they are stored in the form section, along with other control's translations.
 
  
Parameters are allowed. Example :
+
UsernameConnectError=%0:s in not a valid username, retry.
  
UsernameConnectError=%1:s in not a valid username, retry.
+
To display a translated message, use only one of these functions member of TGForm :
  
MessageDlg and ShowMessage procedures must not be used, since they don't use the same translation system.
+
AbortMessage ( 'UsernameConnectError', [EditUsername.Text] );
To display a translated message, we use special procedures :
+
ErrorMessage ( 'UsernameConnectError', [EditUsername.Text] );
 +
ConfirmationMessage ( 'UsernameConnectError', [EditUsername.Text] );
 +
InformationMessage ( 'UsernameConnectError', [EditUsername.Text] );
  
ErrorMessage ( 'UsernameConnectError', [EditUsername.Text] );
+
The message form displays an Help button, which opens by default the wiki page named after the HelpUrl property of the form.
 +
There is also a button to copy the message in the clipboard, to paste it somewhere else.
  
ConfirmationMessage ( 'UsernameConnectError', [EditUsername.Text] );
+
In some special cases, a more general procedure can be used :
  
InformationMessage ( 'UsernameConnectError', [EditUsername.Text] );
+
DisplayMessage ( ParentForm, Section, MessageId, [parameter-1, ..., parameter_n], Severity, WikiPage );
  
 
=== Other strings ===
 
=== Other strings ===
  
Other parameters can be present in the ini file, for special usages.
+
Other parameters can be present in the language file, for special usages.
 
Their translations can be queried at any time by :
 
Their translations can be queried at any time by :
  
TranslateText( Section, Key, Default );
+
TranslateText( Section, Key, Default );
 
 
=== Other controls ===
 
 
 
It is necessary to be able to set the appropriate properties, depending on the type.
 
 
 
Some types of controls are not translated today in the translation module, because I do not need a text currently.
 
For example, TSpeedButton.Hint is not translated, but it should be easy to do this, by adding a few lines in UnitTranslation.pas.
 
  
We should always refer to the highest possible ancestor, so there is no problem with descendants.
+
or
  
== Translation tools ==
+
TranslateMessage ( Section, Key, [parameter_1, ... parameter_n] );
  
There is a translation utility, available for Linux and for Windows, which can read a reference language file, and fill another language file, detecting missing translations.
+
When these procedures are used, and to enable dynamic translation (triggered by F4 key), a Translate method must generally be added in the form, overriding the ancestor, and calling the TranslateText or TranslateMessage for the new language.
  
Let us know if you want to translate in your mother language.
+
Example :
  
== Potential evolution ==
+
procedure TFormAccountingEntries.DisplayNotSelected;
 +
begin
 +
if NumberSelected = 0 then
 +
  LabelNotAllSelected.Caption := ''
 +
else
 +
  LabelNotAllSelected.Caption :=
 +
      Format(TranslateText(Name, 'NotAllRecordsSelected'), [MaxRecordsSelected]);
 +
end;
 +
 +
procedure TFormAccountingEntries.Translate;
 +
begin
 +
  inherited Translate;
 +
  DisplayNotSelected;
 +
end;
  
In gestinux 1.0 we use a package with descendants of all types of visual controls. They are translated after they are loaded, without a line of code in application programs.
+
== How to make a translation ==
  
Later, we plan to open a form when right-clicking over the component, to enter the translation, and store it in the language inifile.
+
Use the [[Translating|translation utility]], available for Linux and for Windows, which can read a reference language file, and fill another language file, detecting missing translations.

Latest revision as of 13:04, 18 November 2015

Why

The common I18N method using resource strings requires a development environment and programming skills to make or improve translations.

For Gestinux, we have used another system.

Translation are read by the software from mere text files (inifile like) and read when forms are loaded. This is quick enough.

This way, it is possible to define or modify translations directly when running the application, without developing tools and skills. There is no need to recompile anything to add a new language, or to modify an existing language.

GTranslator is an utility integrated in the application, to make and check translations easily.

It is also possible to toggle between 2 languages at runtime, to check a new translation.

How it works ?

Language file

Translation are stored in text files, referred as language files.

There are two language files for each language, and they are merged at startup in a single Inifile in memory.

To locate, or create or improve a translation, see this page.

Simple controls

For each control of Gestinux_util, the translation is searched in the language file :

The section is the Form name (the form class name in version under development). The parameter name is the component name, followed by a dot and the property name to translate. The parameter value is the translation

If the file or the parameter is not found, the design properties are used.


E.g :

[FormConnection]
LabelUsername.Caption=Username
EditUsername.Hint=Enter your username
BtnOk.caption=Connect
BtnOk.Hint=Click here to connect
MenuItemQuit.Caption=Exit

GGrid and GDbGrid

For each column, indexed by its position, we can define the title. The Columns property must be defined, and all columns must be added, to allow translations (and other features).

E.g :

GridEmployees.Name.Title=Name
GridEmployees.Address.Title=Address
GridEmployees.City.Title=City

Messages

MessageDlg and ShowMessage procedures must not be used, since they don't use the same translation system.

Messages are identified by keywords not used for control's translations. They are stored in the form section, along with other control's translations.

Parameters are allowed.

Example :

UsernameConnectError=%0:s in not a valid username, retry.

To display a translated message, use only one of these functions member of TGForm :

AbortMessage ( 'UsernameConnectError', [EditUsername.Text] );
ErrorMessage ( 'UsernameConnectError', [EditUsername.Text] );
ConfirmationMessage ( 'UsernameConnectError', [EditUsername.Text] );
InformationMessage ( 'UsernameConnectError', [EditUsername.Text] );

The message form displays an Help button, which opens by default the wiki page named after the HelpUrl property of the form. There is also a button to copy the message in the clipboard, to paste it somewhere else.

In some special cases, a more general procedure can be used :

DisplayMessage ( ParentForm, Section, MessageId, [parameter-1, ..., parameter_n], Severity, WikiPage );

Other strings

Other parameters can be present in the language file, for special usages. Their translations can be queried at any time by :

TranslateText( Section, Key, Default );

or

TranslateMessage ( Section, Key, [parameter_1, ... parameter_n] );

When these procedures are used, and to enable dynamic translation (triggered by F4 key), a Translate method must generally be added in the form, overriding the ancestor, and calling the TranslateText or TranslateMessage for the new language.

Example :

procedure TFormAccountingEntries.DisplayNotSelected;
begin
if NumberSelected = 0 then
  LabelNotAllSelected.Caption := 
else
  LabelNotAllSelected.Caption :=
     Format(TranslateText(Name, 'NotAllRecordsSelected'), [MaxRecordsSelected]);
end;

procedure TFormAccountingEntries.Translate;
begin
  inherited Translate;
  DisplayNotSelected;
end;

How to make a translation

Use the translation utility, available for Linux and for Windows, which can read a reference language file, and fill another language file, detecting missing translations.