1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
|
{ ------------------------------------------------------------------------------
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(GetDefaultLang); }
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
SetDefaultLang(ALang);
// 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;
{ 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.
|