Application i18n and L10n
The unit expects all textual content (both resourcestring
and
RTTI derived captions) to be correct English text. A list of all used textual
elements will be retrieved then hashed into an unique numerical value. When a
specific locale is set for the application, the unit will search for a
.msg
text file in the executable folder matching the expected
locale definition. For instance, it will search for FR.msg
for
translation into French.
In order to translate all the user interface, a corresponding
.msg
file is to be supplied in the executable folder. Neither the
source code, nor the executable is to be rebuild to add a new language. And
since this file is indeed a plain textual file, even a non developer (e.g. an
end-user) is able to add a new language, starting from another
.msg
.
Creating the reference file
In order to begin a translation task, the SQlite3i18n.pas
unit
is able to extract all textual resource from the executable, and create a
reference text file, containing all English sentences and words to be
translated, associated with their numerical hash value.
It will in fact:
- Extract all
resourcestring
text; - Extract all captions generated from RTTI (e.g. from enumerations or class properties names);
- Extract all embedded
dfm
resources, and create per-form sections, allowing a custom translation of displayed captions or hints.
This creation step needs a compilation of the executable with the
EXTRACTALLRESOURCES
conditional defined, globally to the
whole application (a full rebuild is necessary after having added or
suppressed this conditional from the Project / Options /
Folders-Conditionals IDE field).
Then the ExtractAllResources
global procedure is to be called
somewhere in the code.
For instance, here is how this is implemented in !TMainForm.FormShow!LibMainDemoFileMain.pas, for the framework main demo:
procedure TMainForm.FormShow(Sender: TObject); begin {$ifdef EXTRACTALLRESOURCES} ExtractAllResources( // first, all enumerations to be translated [TypeInfo(TFileEvent),TypeInfo(TFileAction),TypeInfo(TPreviewAction)], // then some class instances (including the TSQLModel will handle all TSQLRecord) [Client.Model], // some custom classes or captions [],[]); Close; {$else} //i18nLanguageToRegistry(lngFrench); {$endif} Ribbon.ToolBar.ActivePageIndex := 1; end;
The TFileEvent
and TFileAction
enumerations RTTI
information is supplied, together with the current TSQLModel
instance. All TSQLRecord
classes (and therefore properties) will
be scanned, and all needed English caption text will be extracted.
The Close
method is then called, since we don't want to use the
application itself, but only extract all resources from the executable.
Running once the executable will create a SynFile.messages
text
file in the SynFile.exe
folder, containing all English text:
[TEditForm] Name.EditLabel.Caption=_2817614158 Name KeyWords.EditLabel.Caption=_3731019706 KeyWords
[TLoginForm] Label1.Caption=_1741937413 &User name: Label2.Caption=_4235002365 &Password:
[TMainForm] Caption=_16479868 Synopse SQLite3 Framework demo - SynFile
[Messages] 2784453965=Memo 2751226180=Data 744738530=Safe memo 895337940=Safe data 2817614158=Name 1741937413=&User name: 4235002365=&Password: 16479868= Synopse SQLite3 Framework demo - SynFile 940170664=Content 3153227598=None 3708724895=Page %d / %d 2767358349=Size: %s 4281038646=Content Type: %s 2584741026=This memo is password protected.|Please click on the "Edit" button to show its content. 3011148197=Please click on the "Extract" button to get its content. 388288630=Signed,By %s on %s (...)
The main section of this text file is named [Messages]
. In
fact, it contains all English extracted texts, as
NumericalKey=EnglishText
pairs. Note this will reflect the exact
content of resourcestring
or RTTI captions, including formating
characters (like %d
), and replacing line feeds (#13
)
by the special character (a line feed is not expected on a one-line-per-pair
file layout). Some other text lines are separated by a comma. This is usual for
instance for hint values, as expected by the code.
As requested, each application form has its own section (e.g.
[TEditForm]
, [TMainForm]
), proposing some default
translation, specified by a numerical key (for instance
Label1.Caption
will use the text identified by 1741937413 in the
[Messages]
section). The underline character before the numerical
key is used to refers to this value. Note that if no _NumericalKey
is specified, a plain text can be specified, in order to reflect a specific use
of the generic text on the screen.
Adding a new language
In order to translate the whole application into French, the following
SynFile.FR
file could be made available in the
SynFile.exe
folder:
[Messages] 2784453965=Texte 2751226180=Données 744738530=Texte sécurisé 895337940=Données sécurisées 2817614158=Nom 1741937413=&Nom utilisateur: 4235002365=&Mot de passe: 16479868= Synopse mORMot Framework demo - SynFile 940170664=Contenu 3153227598=Vide 3708724895=Page %d / %d 2767358349=Taille: %s 4281038646=Type de contenu: %s 2584741026=Le contenu de ce memo est protégé par un mot de passe.|Choisissez "Editer" pour le visualiser. 3011148197=Choisissez "Extraire" pour enregistrer le contenu. 388288630=Signé,Par %s le %s (....)
Since no form-level custom captions have been defined in this
SynFile.FR
file, the default numerical values will be used. In our
case, Name.EditLabel.Caption
will be displayed using the text
specified by 2817614158, i.e. 'Nom'
.
Note that the special characters %s %d ,
markup was preserved:
only the plain English text has been translated to the corresponding
French.
Language selection
User Interface language can be specified at execution.
By default, it will use the registry to set the language. It will need an
application restart, but it will also allow easier translation of all forms,
using a low-level hook of the TForm.Create
constructor.
For instance, if you set in FileMain.pas, for the framework main demo:
procedure TMainForm.FormShow(Sender: TObject);
begin
(...)
i18nLanguageToRegistry(lngFrench);
Ribbon.ToolBar.ActivePageIndex := 1;
end;
Above code will set the main application language as French. At next
startup, the content of a supplied SynFileFR.msg
file will be used
to translate all screen layout, including all RTTI-generated captions.
Of course, for a final application, you'll need to change the language by a
common setting. See i18nAddLanguageItems, i18nAddLanguageMenu
and
i18nAddLanguageCombo
functions and procedures to create your own
language selection dialog, using a menu or a combo box, for instance.
Localization
Take a look at the TLanguageFile
class. After the main language
has been set, you can use the global Language
instance in order to
localize your application layout.
The SQlite3i18n
unit will register itself to some methods of
SQlite3Commons.pas
, in order to translate the RTTI-level text into
the current selected language. See for instance i18nDateText
.
As usual, feedbacks and comments are welcome on our forum!.