{**********************************************}
{   Automatic Translation Unit  -Internal-     }
{   Copyright (c) 2001-2020 by Steema Software }
{**********************************************}
unit TeeTranslate;
{$I TeeDefs.inc}

interface

uses {$IFDEF FMX}
     FMX.Types, FMX.Forms, FMX.Controls, FMX.ExtCtrls, FMX.ListBox,
     {$IFDEF D18}
     FMX.StdCtrls,
     {$ENDIF}
     FMX.TabControl, FMX.Menus, FMXTee.Procs,
     {$ELSE}
     Graphics, Forms, Controls, StdCtrls, Buttons, ExtCtrls, ComCtrls, Menus,
     TeeProcs,
     {$ENDIF}

     SysUtils, Classes;

{.$DEFINE TEEFINDMISSING}  { <-- internal use }

{$IFDEF TEEFINDMISSING}
{ internal use }
Procedure StartMissing;
Procedure EndMissing(Const Path,FileName:String);

var Missing:TStringList=nil;
    MissingHelp:TStringList=nil;
    HelpIDList:TStringList=nil;
    MaxHelpContext:Integer=0;

var TeeMissingAddDuplicates:Boolean=False;
    TeeMissingAddUnused:Boolean=False;
    TeeMissingCleanExtract:Boolean=False;

Procedure StartExtract;
Procedure EndExtract(const Path:String);

var Extract:TStringList=nil;
{$ENDIF}

Procedure TeeTranslateAControl(const AControl:TTranslatable;
                               const ExcludedChilds:Array of TTranslatable);
Function TeeCanTranslate(Var S,HotKeyList:String):Boolean;

// Returns the TeeChart Language stored at the IniFile or Registry
Function TeeLanguageRegistry:Integer;

// Stores "Index" TeeChart Language into IniFile or Registry
Procedure TeeLanguageSaveRegistry(LanguageNum:Integer);

{$IFDEF FPC}
{$DEFINE TEEGETHOTKEY}
{$ENDIF}

{$IFDEF TEEGETHOTKEY}
function StripHotkey(const Text: string): string;
{$ENDIF}

implementation

uses
  Math,
  {$IFDEF FMX}
  FMXTee.Canvas, FMXTee.Constants
  {$ELSE}
  TeCanvas, TeeConst
  {$ENDIF}
  ;

{$IFDEF TEEFINDMISSING}
{ internal use }
Procedure StartMissing;
const ContextEditorFile='..\Docs\helpv7\Context_Editor.txt';

var t: Integer;
begin
  Missing:=TStringList.Create;
  MissingHelp:=TStringList.Create;
  HelpIDList:=TStringList.Create;
  if FileExists(ContextEditorFile) then
     HelpIDList.LoadFromFile(ContextEditorFile);
  MaxHelpContext:=0;

  if Assigned(TeeLanguage) then
  for t:=0 to TeeLanguage.Count-1 do
      TeeLanguage.Objects[t]:=Pointer(0);
end;

Procedure EndMissing(Const Path,FileName:String);
var t: Integer;
begin
  if TeeMissingAddUnused then
  begin
    Missing.Add('');
    Missing.Add('Unused:');
    Missing.Add('-------');

    if Assigned(TeeLanguage) then
    for t:=0 to TeeLanguage.Count-1 do
        if Integer(TeeLanguage.Objects[t])=0 then
           Missing.Add(TeeLanguage[t]);

    Missing.Add('-------');
  end;

  ForceDirectories(Path);

  Missing.SaveToFile(Path+'\'+FileName);

  MissingHelp.Add('');
  MissingHelp.Add('Maximum helpcontext = '+IntToStr(MaxHelpContext));
  MissingHelp.SaveToFile(Path+'\teemissinghelp.txt');

  FreeAndNil(Missing);
  FreeAndNil(MissingHelp);
  FreeAndNil(HelpIDList);
end;

Procedure StartExtract;
begin
  Extract:=TStringList.Create;
end;

Procedure EndExtract(const Path:String);
begin
  Extract.SaveToFile(Path+'\teeextract.txt');
  FreeAndNil(Extract);
end;

Function IsNumber(Const S:String):Boolean;
{$IFDEF D6}
var Value : Double;
{$ENDIF}
begin
  {$IFDEF D6}
  result:=TryStrToFloat(S,Value);
  {$ELSE}
  result:=False;
  {$ENDIF}
end;
{$ENDIF}

{$IFDEF TEEGETHOTKEY}
const cHotKeyPrefix = '&';

function StripHotkey(const Text: string): string;
var
  I: Integer;
begin
  Result := Text;
  I := 1;

  while I <= Length(Result) do
  begin
    {$IFNDEF LCL}
    if IsLeadChar(Result[I]) then
       Inc(I)
    else
    {$ENDIF}

    if Result[I] = cHotkeyPrefix then
      if SysLocale.FarEast and
        ((I > 1) and (Length(Result)-I >= 2) and
         (Result[I-1] = '(') and (Result[I+2] = ')')) then
        Delete(Result, I-1, 4)
      else
        Delete(Result, I, 1);
        
    Inc(I);
  end;
end;

{$ELSE}
{$IFDEF FMX}
const cHotKeyPrefix = '&';

function StripHotkey(const Text: string): string;
var i : Integer;
begin
  result:=Text;

  repeat
    i:=Pos(cHotKeyPrefix, result);
    if i>0 then Delete(result, i, 1);
  until i=0;
end;
{$ENDIF}

{$ENDIF}

Function TeeCanTranslate(Var S,HotKeyList:String):Boolean;

  {$IFDEF TEEGETHOTKEY}
  // From Delphi sources. Delphi 4 / Lazarus LCL does not has GetHotKey function.
  function GetHotkey(const Text: string): string;
  var
    I, L: Integer;
  begin
    Result := '';
    I := 1;
    L := Length(Text);
    while I <= L do
    begin
      if Text[I] in LeadBytes then
        Inc(I)
      else if (Text[I] = cHotkeyPrefix) and
              (L - I >= 1) then
      begin
        Inc(I);
        if Text[I] <> cHotkeyPrefix then
          Result := Text[I]; // keep going there may be another one
      end;
      Inc(I);
    end;
  end;

  {$ELSE}
  {$IFDEF FMX}
  function GetHotkey(const Text: string): string;
  var i : Integer;
  begin
    i:=Pos('&',Text);
    if i=0 then result:=''
    else
       result:=Copy(Text,i+1,1);
  end;
  {$ENDIF}
  {$ENDIF}
  
  // Optimized version of TStringList.Values[].
  Function GetTranslatedString(Const St:String):String;
  var t    : Integer;
      i    : Integer;
      tmpS : string;
  begin
    if Assigned(TeeLanguage) then
    with TeeLanguage do
    for t:=0 to Count-1 do
    begin
      tmpS:=Strings[t];
      i:=Pos('=',tmpS);
      if (i>0) and (Copy(tmpS,1,i-1)=St) then
      begin
        result:=Copy(tmpS,i+1,MaxInt);
        {$IFDEF TEEFINDMISSING}
        Objects[t]:=Pointer(TeeNativeInt(Objects[t])+1);
        {$ENDIF}

        {$IFDEF LCL}
        result:=AnsiToUTF8(result);
        {$ENDIF}

        exit;
      end;
    end;
    result:='';
  end;

  {$IFDEF TEEFINDMISSING}
  Function FoundInLanguage(const s:String):Boolean;
  var t:Integer;
  begin
    with TeeLanguage do
    for t:=0 to Count-1 do
    if Pos('='+s,UpperCase(Strings[t]))>0 then
    begin
      result:=true;
      exit;
    end;
    result:=False;
  end;

  function InTheLanguage(const s:String):Boolean;
  begin
    result:=False;
  end;
  {$ENDIF}

var s2         : String;
    s3         : String;
    s4         : String;
    tmpHot     : String;
    HasColon   : Boolean;
    HasHotKey  : Boolean;
    HasEllipse : Boolean;
    t          : Integer;
begin
  result:=False;

  if (s<>'') and {$IFDEF TEEFINDMISSING}Assigned(TeeLanguage) and{$ENDIF}
     (s<>'?') and (s<>'%') and (s<>'0') then
  begin
    s:=Trim(s);

    if s<>'' then
    begin
      HasEllipse:=False;
      HasColon:=False;

      { remove hot-key if exists... }
      s4:=StripHotkey(s);
      HasHotKey:=s4<>s;
      if TeeLanguageHotKeyAtEnd and HasHotKey then tmpHot:=GetHotkey(s);

      { ':' }
      s2:=UpperCase(s4);
      if Copy(s2,Length(s2),1)=':' then
      begin
        HasColon:=True;
        s2:=Copy(s2,1,Length(s2)-1);
      end;

      { ... }
      if Copy(s2,Length(s2)-2,3)='...' then
      begin
        HasEllipse:=True;
        s2:=Copy(s2,1,Length(s2)-3);
      end;

      { find new translated string }
      // Optimization of: s3:=TeeLanguage.Values[s2];
      s3:=GetTranslatedString(s2);

      if s3<>'' then
      begin
        s:=s3;

        if HasHotKey then { add ampersand (hotkey) to string }
        begin
          if TeeLanguageHotKeyAtEnd then
          begin
            s:=s+'('+cHotKeyPrefix+UpperCase(tmpHot)+')';
          end
          else { default }
          for t:=1 to Length(s) do

          {$IFDEF IOS}
          {$WARN SYMBOL_DEPRECATED OFF}
          {$ENDIF}

          {$IFDEF ANDROID}
          {$WARN SYMBOL_DEPRECATED OFF}
          {$ENDIF}

          if {$IFDEF D12}CharInSet(s[t], ['a'..'z','A'..'Z','0'..'9'])

          {$IFDEF IOS}
          {$WARN SYMBOL_DEPRECATED DEFAULT}
          {$ENDIF}

          {$IFDEF ANDROID}
          {$WARN SYMBOL_DEPRECATED DEFAULT}
          {$ENDIF}

            {$ELSE}s[t] in ['a'..'z','A'..'Z','0'..'9'] {$ENDIF}then
             if Pos(s[t],HotKeyList)=0 then { HotKey char not yet in list }
             begin
               HotKeyList:=HotKeyList+s[t];
               s:=Copy(s,1,t-1)+cHotKeyPrefix+Copy(s,t,Length(s)); { add hotkey }
               break;
             end;
        end;

        { after HotKey, check Ellipse and Colon }
        if HasEllipse then s:=s+'...';
        if HasColon then s:=s+':';
        result:=True;
      end
      else
      begin
       {$IFDEF TEEFINDMISSING}
       // string not found in translation, add to "missing" list...
       if Assigned(Missing) then
          if (s2<>'+') and (s2<>'-') and (s2<>'') and (not IsNumber(s2)) then
             if not FoundInLanguage(s2) then
             begin
               // Tricks:
               if (s2<>UpperCase(TeeMsg_Copyright)) and
                  (s2<>'SERIES 2') and
                  (s2<>'#.#') and
                  (s2<>'0.0%') and
                  (s2<>'DDD° MM'' SS".ZZZ') and
                  (s2<>'DDD MM'' SS".ZZZ') and
                  (s2<>'DDD DEG MM'' SS".ZZZ') and
                  (s2<>'DDD MM SS.ZZZ') and
                  (s2<>'DDD MM SS') and
                  (s2<>'DDD MM') and
                  (s2<>'DDD°') and
                  (not InTheLanguage(s2)) then
               begin
                 s2:=''''+s2+'=''#13+';
                 if (Missing.IndexOf(s2)=-1) then
                 begin
                   Missing.Add(s2);
                 end;
               end;
             end;
       {$ENDIF}
      end;
    end;
  end;
end;

type
  TControlAccess=class(TControl);
  TCustomListBoxAccess=class(TCustomListBox);

Procedure TeeTranslateAControl(const AControl:TTranslatable;
                               const ExcludedChilds:Array of TTranslatable);

 {$IFDEF TEEFINDMISSING}
 Procedure CheckExtract(const AComp:TComponent; Const S:String);
 var tmp : String;
 begin
   if Assigned(Extract) and (Trim(S)<>'') and (not IsNumber(S)) then
   begin
     if TeeMissingCleanExtract then
     begin
       if Pos(#13,S)=0 then // Do not store Memo (>1 lines) text
       begin
         tmp:=Trim(S);
         tmp:=ReplaceChar(tmp,'&',#0);
         tmp:=ReplaceChar(tmp,':',#0);
         tmp:=StringReplace(tmp,'...','',[rfReplaceAll]);

         if tmp<>'' then
            if Extract.IndexOf(tmp)=-1 then
               Extract.Add(tmp);
       end;
     end
     else
     begin
       if AComp.Owner=nil then tmp:='nil'
                          else tmp:=AComp.Owner.ClassName;

       Extract.Add(tmp+#9+AComp.ClassName+#9+AComp.Name+#9+S);
     end;
   end;
 end;

 Procedure CheckDuplicates;
 var Num,i,t,tt:Integer;
     s,s2:String;
 begin
   if Assigned(Missing) and (Missing.IndexOf('Duplicates:')=-1)
      and Assigned(TeeLanguage) then
   begin
     Missing.Add('');
     Missing.Add('Duplicates:');
     Missing.Add('---------------');
     for t:=0 to TeeLanguage.Count-1 do
     begin
       Num:=0;
       s:=TeeLanguage.Strings[t];
       i:=Pos('=',s);
       if i>0 then s:=Copy(s,1,i-1);
       for tt:=0 to TeeLanguage.Count-1 do
       begin
         s2:=TeeLanguage.Strings[tt];
         i:=Pos('=',s2);
         if i>0 then s2:=Copy(s2,1,i-1);
         if s=s2 then Inc(Num);
       end;

       if Num>1 then Missing.Add(s+'   ('+IntToStr(Num)+')');
     end;
     Missing.Add('---------------');
   end;
 end;

 Procedure CheckHelpContext(const AControl:TWinControl);

   Function GetTextToOutput(const C:TControl):String;
   var tmp : TControl;
   begin
     tmp:=C.Parent;
     if not (tmp is TCustomForm) then
        while Assigned(tmp.Parent) do
        begin
          tmp:=tmp.Parent;
          if tmp is TCustomForm then break;
        end;

     result:=C.Name+#9+C.ClassName+#9+#9+tmp.ClassName;
   end;

   Procedure CheckHelpInList(const C:TControl; Num:Integer);

     {$IFNDEF D6}
     function ValueFromIndex(const S:TStringList; Index: Integer): string;
     begin
       if Index>=0 then
          result:=Copy(S[Index], Length(S.Names[Index]) + 2, MaxInt)
       else
          result:= '';
     end;
     {$ENDIF}

   var s:String;
       tmp:String;
       t:Integer;
   begin
     tmp:=IntToStr(Num);
     for t:=0 to HelpIDList.Count-1 do
       {$IFNDEF D6}
         if Trim(ValueFromIndex(HelpIDList,t))=tmp then
       {$ELSE}
         if Trim(HelpIDList.ValueFromIndex[t])=tmp then
       {$ENDIF}
         exit;

     S:='BAD: '+GetTextToOutput(C);
     if MissingHelp.IndexOf(S)=-1 then
        MissingHelp.Add(S);
   end;

 var t : Integer;
     S : String;
     tmpHelp : Integer;
 begin
   with AControl do
   for t:=0 to ControlCount-1 do
      if Controls[t] is TWinControl then
      begin
         if TWinControl(Controls[t]).HelpContext=0 then { Missing Help Context number }
         begin
           if TWinControl(Controls[t]).Visible then
           if not (Controls[t] is TPanel) then
               if (Controls[t] is TButton) and (TButton(Controls[t]).Caption='OK') then
               else
               if (Controls[t] is TBitBtn) and (TBitBtn(Controls[t]).Caption='OK') then
               else
               if (Controls[t] is TButton) and (TButton(Controls[t]).Caption='Cancel') then
               else
               if (Controls[t] is TBitBtn) and (TBitBtn(Controls[t]).Caption='Cancel') then
               else
               if (Controls[t] is TRadioGroup) then
               else
               if (Controls[t] is TTabSheet) then
               else
               if (Controls[t] is TGroupBox) then
               else
               if (Controls[t].ClassName='TGroupButton') then
               else
               if (Controls[t].ClassName='TGalleryChart') then
               else
               if (Controls[t] is TPageControl) then
               else
               if (Controls[t] is TCustomForm) then
               else
               begin
                 S:=GetTextToOutput(Controls[t]);
                 if MissingHelp.IndexOf(S)=-1 then
                    MissingHelp.Add(S);
               end;
         end
         else
         begin
           tmpHelp:=TWinControl(Controls[t]).HelpContext;
           CheckHelpInList(Controls[t],tmpHelp);
           MaxHelpContext:=Math.Max(MaxHelpContext,tmpHelp);
         end;

         CheckHelpContext(Controls[t] as TWinControl);
      end
      {$IFDEF D6}
      else
      if Controls[t] is TLabel then
         if TLabel(Controls[t]).HelpContext<>0 then
              MissingHelp.Add('Wrong TLabel helpcontext (Delphi 4) '+GetTextToOutput(Controls[t]));
      {$ENDIF}
  end;
  {$ENDIF}

  function IsExcludedChild(const AComponent:TComponent):Boolean;
  var t : Integer;
  begin
    for t:=0 to Length(ExcludedChilds)-1 do
    if AComponent=ExcludedChilds[t] then
    begin
      result:=True;
      Exit;
    end;

    result:=False;
  end;

var t          : Integer;
    tt         : Integer;
    tmp        : Integer;
    s          : String;
    tmpSt      : String;
    HotKeyList : String;

    {$IFDEF LCL}
    tmpDeltaWidth : Integer;  // Fix for TLabel Alignment taRightJustify
    {$ENDIF}
begin
  {$IFDEF TEEFINDMISSING}
  { internal use }
  if TeeMissingAddDuplicates then
     CheckDuplicates;

  if Assigned(Missing) and (AControl is TWinControl) then
     CheckHelpContext(AControl as TWinControl);
  {$ENDIF}

  if Assigned(TeeLanguage)
     {$IFDEF TEEFINDMISSING}or Assigned(Extract){$ENDIF} then
  begin

    {$IFDEF FMX}
    if AControl is TTextControl then
    with TTextControl(AControl) do
    begin
      {$IFDEF TEEFINDMISSING}
      CheckExtract(AControl,Text);
      {$ENDIF}

      s:=Text;

      if TeeCanTranslate(s,HotKeyList) then
         Text:=s;
    end;

    {$ELSE}
    with TControlAccess(AControl) do
    begin
      {$IFDEF TEEFINDMISSING}
      CheckExtract(AControl,Caption);
      {$ENDIF}

      s:=Caption;

      if TeeCanTranslate(s,HotKeyList) then
         Caption:=s;
    end;
    {$ENDIF}

    with AControl do
    for t:=0 to ComponentCount-1 do
    if not IsExcludedChild(Components[t]) then
    begin
      { traverse all Components, not only Controls }

      if Components[t] is TLabel then
      with TLabel(Components[t]) do
      begin
        {$IFDEF TEEFINDMISSING}
        CheckExtract(AControl.Components[t],Caption);
        {$ENDIF}

        s:={$IFDEF FMX}Text{$ELSE}Caption{$ENDIF};

        if TeeCanTranslate(s,HotKeyList) then
        begin
          {$IFDEF LCL}
          // Bug on taRightJustify labels:
          if Alignment=taRightJustify then
          begin
            tmpDeltaWidth:=Round((Length(s)-Length(Caption))*(Width/Length(Caption)));
            Left:=Left-tmpDeltaWidth;
          end;
          {$ENDIF}

          {$IFDEF FMX}Text{$ELSE}Caption{$ENDIF}:=s;
          AutoSize:=True;
        end;
      end
      else
      if Components[t] is TComboBox then
      with TComboBox(Components[t]) do
      begin
        {$IFNDEF FMX}
        if Sorted then
        begin
          tmp:=-1;
          if ItemIndex=-1 then tmpSt:=''
                          else tmpSt:=Items[ItemIndex]
        end
        else
        {$ENDIF}
        begin
          tmp:=ItemIndex;
          tmpSt:='';
        end;

        for tt:=0 to Items.Count-1 do
        begin
          {$IFDEF TEEFINDMISSING}
          CheckExtract(AControl.Components[t],Items[tt]);
          {$ENDIF}

          s:=Items[tt];
          if TeeCanTranslate(s,HotKeyList) then Items[tt]:=s;
        end;

        {$IFNDEF FMX}
        if Sorted then ItemIndex:=Items.IndexOf(tmpSt)
                  else
        {$ENDIF}
                  ItemIndex:=tmp;
      end
      else
      if Components[t] is TCustomListBox then
      with TCustomListBoxAccess(Components[t]) do
      begin
        if Sorted then
        begin
          tmp:=-1;

          // Fix: Multiselect sorted listbox can return ItemIndex=0 when
          // Count is also zero.

          if (ItemIndex=-1) or (ItemIndex>=Items.Count) then
             tmpSt:=''
          else
             tmpSt:=Items[ItemIndex]
        end
        else
        begin
          tmp:=ItemIndex;
          tmpSt:='';
        end;

        for tt:=0 to Items.Count-1 do
        begin
          {$IFDEF TEEFINDMISSING}
          CheckExtract(AControl.Components[t],Items[tt]);
          {$ENDIF}
          s:=Items[tt];

          if TeeCanTranslate(s,HotKeyList) then
             Items[tt]:=s;
        end;

        if Sorted then ItemIndex:=Items.IndexOf(tmpSt)
                  else ItemIndex:=tmp;
      end
      else

      {$IFDEF FMX}
      if Components[t] is TRadioButton then
      with TRadioButton(Components[t]) do
      begin
        {$IFDEF TEEFINDMISSING}
        CheckExtract(AControl.Components[t],Caption);
        {$ENDIF}

        s:=Text;

        if TeeCanTranslate(s,HotKeyList) then
           Text:=s;
      end
      {$ELSE}

      if Components[t] is TRadioGroup then
      with TRadioGroup(Components[t]) do
      begin
        {$IFDEF TEEFINDMISSING}
        CheckExtract(AControl.Components[t],Caption);
        {$ENDIF}

        s:=Caption;

        if TeeCanTranslate(s,HotKeyList) then
           Caption:=s;

        for tt:=0 to Items.Count-1 do
        begin
          {$IFDEF TEEFINDMISSING}
          CheckExtract(AControl.Components[t],Items[tt]);
          {$ENDIF}
          s:=Items[tt];
          if TeeCanTranslate(s,HotKeyList) then Items[tt]:=s;
        end;
      end
      {$ENDIF}

      else

      {$IFNDEF FMX}
      if Components[t] is TPageControl then
      with TPageControl(Components[t]) do
      begin
        for tt:=0 to PageCount-1 do
        begin
          {$IFDEF TEEFINDMISSING}
          CheckExtract(AControl.Components[t],TTabSheet(Pages[tt]).Caption);
          {$ENDIF}

          s:=TTabSheet(Pages[tt]).Caption;

          if TeeCanTranslate(s,HotKeyList) then
             TTabSheet(Pages[tt]).Caption:=s;
        end;
      end
      else
      {$ENDIF}

      if Components[t] is TTabControl then
      with TTabControl(Components[t]) do
      begin
        for tt:=0 to {$IFDEF FMX}TabCount{$ELSE}Tabs.Count{$ENDIF}-1 do
        begin
          {$IFDEF TEEFINDMISSING}
          CheckExtract(AControl.Components[t],Tabs[tt]);
          {$ENDIF}

          s:=Tabs[tt]{$IFDEF FMX}.Text{$ENDIF};

          if TeeCanTranslate(s,HotKeyList) then
             Tabs[tt]{$IFDEF FMX}.Text{$ENDIF}:=s;
        end;
      end
      else
      if Components[t] is TMenuItem then
      with TMenuItem(Components[t]) do
      begin
        {$IFDEF TEEFINDMISSING}
        CheckExtract(AControl.Components[t],Caption);
        {$ENDIF}

        s:={$IFDEF FMX}Text{$ELSE}Caption{$ENDIF};
        if TeeCanTranslate(s,HotKeyList) then
           {$IFDEF FMX}Text{$ELSE}Caption{$ENDIF}:=s;
      end
      else
      if Components[t] is TPopupMenu then
      with TPopupMenu(Components[t]) do
      begin
        {$IFDEF D17}
        for tt:=0 to {$IFDEF FMX}ItemsCount{$ELSE}Items.Count{$ENDIF}-1 do
        begin
          {$IFDEF TEEFINDMISSING}
          CheckExtract(AControl.Components[t],Items[tt].Caption);
          {$ENDIF}

          s:=Items[tt].{$IFDEF FMX}Text{$ELSE}Caption{$ENDIF};

          if TeeCanTranslate(s,HotKeyList) then
             Items[tt].{$IFDEF FMX}Text{$ELSE}Caption{$ENDIF}:=s;
        end;
        {$ELSE}

        (*  XE2 PopupMenu pending
        for tt:=0 to {$IFDEF FMX}ChildrenCount{$ELSE}Items.Count{$ENDIF}-1 do
        begin
          {$IFDEF TEEFINDMISSING}
          CheckExtract(AControl.Components[t],Children[tt].Caption);
          {$ENDIF}

          s:={$IFDEF FMX}Children[t].Text{$ELSE}Items[tt].Caption{$ENDIF};

          if TeeCanTranslate(s,HotKeyList) then
             {$IFDEF FMX}Children[tt].Text{$ELSE}Items[tt].Caption{$ENDIF}:=s;
        end;
        *)
        {$ENDIF}
      end

      {$IFDEF FMX}
      else
      if Components[t] is TTextControl then
      with TTextControl(Components[t]) do
      begin
        {$IFDEF TEEFINDMISSING}
        CheckExtract(AControl.Components[t],Text);
        {$ENDIF}

        s:=Text;

        if TeeCanTranslate(s,HotKeyList) then
           Text:=s;
      end;
      {$ELSE}
      else
      if Components[t] is TTreeView then
      begin
        for tt:=0 to TTreeView(Components[t]).Items.Count-1 do
        begin
          s:=TTreeView(Components[t]).Items[tt].Text;

          {$IFDEF TEEFINDMISSING}
          CheckExtract(AControl.Components[t],s);
          {$ENDIF}

          if TeeCanTranslate(s,HotKeyList) then
             TTreeView(Components[t]).Items[tt].Text:=s;
        end;
      end
      else
      { last option: TControl }

      if Components[t] is TControl then
      with TControlAccess(Components[t]) do
      begin
        {$IFDEF TEEFINDMISSING}
        CheckExtract(AControl.Components[t],Caption);
        {$ENDIF}

        s:=Caption;
        if TeeCanTranslate(s,HotKeyList) then
           Caption:=s;
      end;

      { Hints... }
      if AControl.Components[t] is TControl then
      with TControl(AControl.Components[t]) do
      begin
        {$IFDEF TEEFINDMISSING}
        if Hint<>'' then CheckExtract(AControl.Components[t],Hint);
        {$ENDIF}
        s:=Hint;
        if (s<>'') and TeeCanTranslate(s,HotKeyList) then Hint:=s;
      end;
      {$ENDIF}

    end;
  end;
end;

Const
  TeeRegistry_Language='Language'; { <-- do not translate }

Function TeeLanguageRegistry:Integer;
begin
  result:=TeeReadIntegerOption(TeeRegistry_Language,-1);
end;

Procedure TeeLanguageSaveRegistry(LanguageNum:Integer);
begin
  TeeSaveIntegerOption(TeeRegistry_Language,LanguageNum);
end;

function TeeTranslateString(const Text:String):String;
var HotKeyList : String;
begin
  result:=Text;

  HotKeyList:='';

  if not TeeCanTranslate(result,HotKeyList) then
     result:=Text;
end;

initialization
  {$IFDEF FMX}FMXTee.Procs
  {$ELSE}TeeProcs
  {$ENDIF}
  .TeeTranslateHook:={$IFDEF D16}
                     TeeTranslateAControl
                     {$ELSE}
                     TeeTranslate.TeeTranslateAControl
                     {$ENDIF}
                     ;

  {$IFDEF FMX}FMXTee.Procs
  {$ELSE}TeeProcs
  {$ENDIF}
  .TeeTranslateStringHook:={$IFDEF D16}
                           TeeTranslateString
                           {$ELSE}
                           TeeTranslate.TeeTranslateString
                           {$ENDIF}
                           ;

finalization
  {$IFDEF FMX}FMXTee.Procs
  {$ELSE}TeeProcs
  {$ENDIF}
  .TeeTranslateHook:=nil;

  {$IFDEF FMX}FMXTee.Procs
  {$ELSE}TeeProcs
  {$ENDIF}
  .TeeTranslateStringHook:=nil;
end.
