{ ------------------------------------------------------------------------------
  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.

