lazarus/examples/translation/main.pas

233 lines
8.4 KiB
ObjectPascal

{ ------------------------------------------------------------------------------
Steps to create a translated application
------------------------------------------------------------------------------
- In Project Options, activate i18n and specify a folder for translations
Make sure that this folder can be found at run-time. If you use a relative
filename it must be relative to the location of the exe.
Select the option to automatically update the po file.
- Add DefaultTranslator or LCLTranslator to uses clause of main form
(DefaultTranslator determines the default language automatically,
LCLTranslator does not).
- If the project contains several forms that need translation:
- Copy LocalizedForms.* (to be found in this project) to the folder of
the new project
- Inherit all forms to be translated from LocalizedForm
(defined in LocalizedForms.pas)
- For this purpose modify the class declaration of the forms to
"class(TLocalizedForm)" instead of "class(TForm)"
Open the lfm file ("view source (.lfm)") and change the first word to
"inherited". See main.lfm and unit2.lfm for examples.
- Create an empty unit to collect all resourcestrings of the project
(this simplifies cross-form usage of strings).
- Declare each string that needs to be translated as a resourcestring. This
is not absolutely necessary for component properties "Caption", "Text" or
"Hint" which are transparently handled by Default/LCLTranslator.
Explicitly declared resource strings are required for stringlist items,
such as those of comboboxes, radiogroups etc.
- To create resource strings from existing code: create a resourcestring section
at the end of the interface section of each unit, then <right click> on each
string and select "Refactoring" | "Make Resource String..." This will create
the resource strings and place the string into the declaration. Then copy all
resource strings to the resource strings unit and delete the resourcestring
sections. Or, enter the resource strings into the resource strings unit
directly.
- Using poedit (or a similar translation program) translate the strings in the
project's po file (to be found in the languages folder) to the languages that
you support. When saving insert language code before ".po", i.e.
"Project1.de.po" for German translation file of "Project1.po".)
- See "SelectLanguage()" for required procedures when changing language at
run-time.
}
unit Main;
{$mode objfpc}{$H+}
interface
uses
SysUtils, Dialogs, ExtCtrls, StdCtrls, LCLTranslator, LocalizedForms;
type
{ TMainForm }
// inherit from TLocalizedForm, .lfm file begins with "inherited" instead of "object"
TMainForm = class(TLocalizedForm)
Bevel1: TBevel;
Button1: TButton;
Button2: TButton;
CbLanguage: TComboBox;
Label1: TLabel;
LblCurrentSelection: TLabel;
RgDrinks: TRadioGroup;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure CbLanguageChange(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure RgDrinksClick(Sender: TObject);
private
FSelectionTime: TTime;
procedure SelectLanguage(ALang: String);
protected
procedure UpdateTranslation(ALang: String); override;
public
end;
var
MainForm: TMainForm;
implementation
{$R *.lfm}
uses
Unit2, StringsUnit;
procedure TMainForm.Button1Click(Sender: TObject);
begin
Form2.Show;
end;
{ This example demonstrates how a translated string can be composed of other
words in phrases. }
procedure TMainForm.Button2Click(Sender: TObject);
begin
if RgDrinks.ItemIndex = -1 then
MessageDlg(LblCurrentSelection.Caption, mtInformation, [mbClose], 0)
else
MessageDlg(Format(rsYouSelectedAt, [
RgDrinks.Items[RgDrinks.ItemIndex], TimeToStr(FSelectionTime)]),
mtInformation, [mbClose], 0);
{ The format mask rsYouSelectedAt ('You selected %0:s at %1:s.') contains
two format placeholders %0:s and %1:s. The former one is replaced by the
string with index 0 in the parameter list, the latter one by the string
with index 1. When using multiple placeholders always use the index
specifiers because the order of placeholders may change from language to
language. }
{ Another comment: The strings used in "MessageDlg" can be translated by
copying the files "lclstrconsts.*.po" to the languages folder.
LCL/DefaultTranslater then includes these strings as well. Please note that
we did not copy these files in this demo project to avoid duplication of
Lazarus files. }
end;
{ Event handler fired when a new language is selected in the language combobox.
We extract the language code from the selected combobox item, and call the
procedure "SelectLanguage". }
procedure TMainForm.CbLanguageChange(Sender: TObject);
var
lang: String;
p: Integer;
begin
if CbLanguage.ItemIndex > -1 then begin
lang := CbLanguage.Items[CbLanguage.ItemIndex];
p := pos(' ', lang);
if p = 0 then p := pos('-', lang);
if p = 0 then
raise Exception.Create('Language items are not properly formatted');
{ This string is essentially meant as a message to the programmer, it
will - hopefully - never make its way to the user. Therefore, there is
not need to use a resourcestring and activate if for translation. }
lang := copy(lang, 1, p-1);
SelectLanguage(lang);
end;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
{ Lets start the program with English translation by default. You could also
store language in a configuration file and apply that selection here. }
SelectLanguage('en');
{ OR: Start the program with system's default language:
SelectLanguage(''); }
end;
{ Another example how to combine translated strings, in this case for a
label caption. }
procedure TMainForm.RgDrinksClick(Sender: TObject);
begin
if RgDrinks.ItemIndex > -1 then
LblCurrentSelection.Caption := Format(rsYouSelected, [RgDrinks.Items[RgDrinks.ItemIndex]]);
FSelectionTime := time();
end;
{ This is the main procedure that has to be called when changing language:
- It replaces resourcestrings with the translated ones.
- It activates the format settings corresponding to the new language
- It tries to use the BiDi mode for the new language (not completely correct)
- It calls "UpdateTranslation" for itself and for each form of the project -
this way, the forms can do things that are not done automatically.
- It updates the language selector combobox }
procedure TMainForm.SelectLanguage(ALang: String);
var
i, p: Integer;
lang: String;
begin
// Switch language - this is in LCLTranslator
ALang := SetDefaultLang(ALang);
if ALang <> '' then
begin
// Switch default settings by calling the procedure provided in BasicLocalizedForm.pas.
UpdateFormatSettings(ALang);
// Adjust BiDiMode to new language
UpdateBiDiMode(ALang);
// Update items not automatically translated.
UpdateTranslation(ALang);
// Select the new language in the language combobox.
ALang := lowercase(ALang);
for i:=0 to CbLanguage.Items.Count-1 do begin
lang := CbLanguage.Items[i];
p := pos(' ', lang);
if p = 0 then p := pos('-', lang);
if p = 0 then
raise Exception.Create('Language items are not properly formatted.');
lang := lowercase(copy(lang, 1, p-1));
if lang = ALang then begin
CbLanguage.ItemIndex := i;
break;
end;
end;
{ Remember the new language. Forms may want to check in UpdateTranslation
whether the new language has a different BiDiMode. }
CurrentLang := ALang;
end;
end;
{ This method is inherited from LocalizedForm and manually inserts translated
strings in cases where LCL/DefaultTranslator cannot do this. }
procedure TMainForm.UpdateTranslation(ALang: String);
begin
inherited;
{ The items of the radiogroup are not automatically handled by
LCL/DefaultTranslator. Therefore, we have to assign the strings to the
translated versions explicitly. }
RgDrinks.Items[0] := rsBeer;
RgDrinks.Items[1] := rsWine;
RgDrinks.Items[2] := rsWater;
{ The label LblCurrentSelection is created by a Format statement. Since
LCL/DefaultTranslator does not execute code we have to update the translation
of the label here. It is sufficient to call RgDrinksClick here where the
caption is re-composed by means of the Format statement. }
RgDrinksClick(nil);
end;
end.