LCL: factored LCLTranslator unit out of DefaultTranslator. LCLTranslator does not call SetDefaultLang in its initialization section, allowing user to do it manually. DefaultTranslator still does it as before, thus keeping previous behaviour for compatibility.

Bug 26631.

git-svn-id: trunk@47310 -
This commit is contained in:
maxim 2015-01-05 16:54:28 +00:00
parent 239f26096f
commit 8664b260d9
3 changed files with 501 additions and 463 deletions

1
.gitattributes vendored
View File

@ -7138,6 +7138,7 @@ lcl/lclmessageglue.pas svneol=native#text/pascal
lcl/lclproc.pas svneol=native#text/pascal
lcl/lclrescache.pas svneol=native#text/pascal
lcl/lclstrconsts.pas svneol=native#text/pascal
lcl/lcltranslator.pas svneol=native#text/pascal
lcl/lcltype.pp svneol=native#text/pascal
lcl/lclunicodedata.pas svneol=native#text/pascal
lcl/lclversion.pas svneol=native#text/pascal

View File

@ -1,6 +1,6 @@
unit DefaultTranslator;
{ Copyright (C) 2004-2010 V.I.Volchenko and Lazarus Developers Team
{ Copyright (C) 2015 Lazarus Developers Team
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
@ -16,481 +16,25 @@ unit DefaultTranslator;
along with this library; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
}
{This unit is needed for using translated form strings made by Lazarus IDE.
It searches for translated .po/.mo files in some common places. If you need
to have .po/.mo files anywhere else, don't use this unit but initialize
LRSMoFile variable from LResources in your project by yourself.
{
If you need standard translation, just use this unit in your project and enable
i18n in project options.
i18n in project options. It will translate your project automatically.
Another reason for including this unit may be using translated LCL messages.
This unit localizes LCL too, if it finds lclstrconsts.xx.po/lclstrconsts.xx.mo
in directory where your program translation files are placed.
If you want to set translation language yourself, use LCLTranslator unit instead
and call SetDefaultLang in your program manually.
}
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, LResources, GetText, Controls, typinfo, FileUtil, LCLProc,
Translations, Forms;
type
{ TUpdateTranslator }
TUpdateTranslator = class(TAbstractTranslator)
private
FStackPath: string;
procedure IntUpdateTranslation(AnInstance: TPersistent);
public
procedure UpdateTranslation(AnInstance: TPersistent);
end;
TDefaultTranslator = class(TUpdateTranslator)
private
FMOFile: TMOFile;
public
constructor Create(MOFileName: string);
destructor Destroy; override;
procedure TranslateStringProperty(Sender: TObject; const Instance: TPersistent;
PropInfo: PPropInfo; var Content: string); override;
end;
{ TPOTranslator }
TPOTranslator = class(TUpdateTranslator)
private
FPOFile: TPOFile;
public
constructor Create(POFileName: string);
destructor Destroy; override;
procedure TranslateStringProperty(Sender: TObject; const Instance: TPersistent;
PropInfo: PPropInfo; var Content: string); override;
end;
procedure SetDefaultLang(Lang: string);
LCLTranslator;
implementation
uses
Menus;
type
TPersistentAccess = class(TPersistent);
function FindLocaleFileName(LCExt: string; Lang: string): string;
var
T: string;
i: integer;
function GetLocaleFileName(const LangID, LCExt: string): string;
var
LangShortID: string;
AppDir,LCFileName,FullLCFileName: String;
begin
if LangID <> '' then
begin
AppDir := ExtractFilePath(ParamStrUTF8(0));
LCFileName := ChangeFileExt(ExtractFileName(ParamStrUTF8(0)), LCExt);
FullLCFileName := ChangeFileExt(ExtractFileName(ParamStrUTF8(0)), '.' + LangID) + LCExt;
//ParamStrUTF8(0) is said not to work properly in linux, but I've tested it
Result := AppDir + LangID + DirectorySeparator + LCFileName;
if FileExistsUTF8(Result) then
exit;
Result := AppDir + 'languages' + DirectorySeparator + LangID +
DirectorySeparator + LCFileName;
if FileExistsUTF8(Result) then
exit;
Result := AppDir + 'locale' + DirectorySeparator + LangID +
DirectorySeparator + LCFileName;
if FileExistsUTF8(Result) then
exit;
Result := AppDir + 'locale' + DirectorySeparator + LangID +
DirectorySeparator + 'LC_MESSAGES' + DirectorySeparator + LCFileName;
if FileExistsUTF8(Result) then
exit;
{$IFDEF UNIX}
//In unix-like systems we can try to search for global locale
Result := '/usr/share/locale/' + LangID + '/LC_MESSAGES/' + LCFileName;
if FileExistsUTF8(Result) then
exit;
{$ENDIF}
//Let us search for reducted files
LangShortID := copy(LangID, 1, 2);
//At first, check all was checked
Result := AppDir + LangShortID + DirectorySeparator + LCFileName;
if FileExistsUTF8(Result) then
exit;
Result := AppDir + 'languages' + DirectorySeparator +
LangShortID + DirectorySeparator + LCFileName;
if FileExistsUTF8(Result) then
exit;
Result := AppDir + 'locale' + DirectorySeparator
+ LangShortID + DirectorySeparator + LCFileName;
if FileExistsUTF8(Result) then
exit;
Result := AppDir + 'locale' + DirectorySeparator + LangShortID +
DirectorySeparator + 'LC_MESSAGES' + DirectorySeparator + LCFileName;
if FileExistsUTF8(Result) then
exit;
//Full language in file name - this will be default for the project
//We need more careful handling, as it MAY result in incorrect filename
try
Result := AppDir + FullLCFileName;
if FileExistsUTF8(Result) then
exit;
//Common location (like in Lazarus)
Result := AppDir + 'locale' + DirectorySeparator + FullLCFileName;
if FileExistsUTF8(Result) then
exit;
Result := AppDir + 'languages' + DirectorySeparator + FullLCFileName;
if FileExistsUTF8(Result) then
exit;
except
Result := '';//Or do something else (useless)
end;
{$IFDEF UNIX}
Result := '/usr/share/locale/' + LangShortID + '/LC_MESSAGES/' +
LCFileName;
if FileExistsUTF8(Result) then
exit;
{$ENDIF}
FullLCFileName := ChangeFileExt(ExtractFileName(ParamStrUTF8(0)), '.' + LangShortID) + LCExt;
Result := AppDir + FullLCFileName;
if FileExistsUTF8(Result) then
exit;
Result := AppDir + 'locale' + DirectorySeparator + FullLCFileName;
if FileExistsUTF8(Result) then
exit;
Result := AppDir + 'languages' + DirectorySeparator + FullLCFileName;
if FileExistsUTF8(Result) then
exit;
end;
Result := '';
end;
begin
Result := '';
if Lang = '' then
for i := 1 to Paramcount - 1 do
if (ParamStrUTF8(i) = '--LANG') or (ParamStrUTF8(i) = '-l') or
(ParamStrUTF8(i) = '--lang') then
Lang := ParamStrUTF8(i + 1);
//Win32 user may decide to override locale with LANG variable.
if Lang = '' then
Lang := GetEnvironmentVariableUTF8('LANG');
if Lang = '' then
LCLGetLanguageIDs(Lang, T);
Result := GetLocaleFileName(Lang, LCExt);
if Result <> '' then
exit;
Result := ChangeFileExt(ParamStrUTF8(0), LCExt);
if FileExistsUTF8(Result) then
exit;
Result := '';
end;
function GetIdentifierPath(Sender: TObject;
const Instance: TPersistent;
PropInfo: PPropInfo): string;
var
Tmp: TPersistent;
Component: TComponent;
Reader: TReader;
begin
Result := '';
if (PropInfo = nil) or
(SysUtils.CompareText(PropInfo^.PropType^.Name, 'TTRANSLATESTRING') <> 0) then
exit;
// do not translate at design time
// get the component
Tmp := Instance;
while Assigned(Tmp) and not (Tmp is TComponent) do
Tmp := TPersistentAccess(Tmp).GetOwner;
if not Assigned(Tmp) then
exit;
Component := Tmp as TComponent;
if (csDesigning in Component.ComponentState) then
exit;
if (Sender is TReader) then
begin
Reader := TReader(Sender);
if Reader.Driver is TLRSObjectReader then
Result := TLRSObjectReader(Reader.Driver).GetStackPath
else
Result := Instance.ClassName + '.' + PropInfo^.Name;
end else if (Sender is TUpdateTranslator) then
Result := TUpdateTranslator(Sender).FStackPath + '.' + PropInfo^.Name;
Result := UpperCase(Result);
end;
var
lcfn: string;
{ TUpdateTranslator }
procedure TUpdateTranslator.IntUpdateTranslation(AnInstance: TPersistent);
var
i,j: integer;
APropCount: integer;
APropList: PPropList;
APropInfo: PPropInfo;
TmpStr: string;
APersistentProp: TPersistent;
StoreStackPath: string;
begin
APropCount := GetPropList(AnInstance.ClassInfo, APropList);
try
for i := 0 to APropCount-1 do
begin
APropInfo:=APropList^[i];
if Assigned(PPropInfo(APropInfo)^.GetProc) and
assigned(APropInfo^.PropType) and
IsStoredProp(AnInstance, APropInfo) then
case APropInfo^.PropType^.Kind of
tkSString,
tkLString,
tkAString: begin
TmpStr := '';
LRSTranslator.TranslateStringProperty(self,aninstance,APropInfo,TmpStr);
if TmpStr <>'' then
SetStrProp(AnInstance, APropInfo, TmpStr);
end;
tkclass: begin
APersistentProp := TPersistent(GetObjectProp(AnInstance, APropInfo, TPersistent));
if Assigned(APersistentProp) then
begin
if APersistentProp is TCollection then
begin
for j := 0 to TCollection(APersistentProp).Count-1 do
begin
StoreStackPath:=FStackPath;
FStackPath:=FStackPath+'.'+APropInfo^.Name+'['+inttostr(j)+']';
IntUpdateTranslation(TCollection(APersistentProp).Items[j]);
FStackPath:=StoreStackPath;
end;
end
else
begin
if APersistentProp is TComponent then
begin
if (csSubComponent in TComponent(APersistentProp).ComponentStyle) then
begin
StoreStackPath:=FStackPath;
FStackPath:=FStackPath+'.'+TComponent(APersistentProp).Name;
IntUpdateTranslation(APersistentProp);
FStackPath:=StoreStackPath;
end
end
else
begin
StoreStackPath:=FStackPath;
FStackPath:=FStackPath+'.'+APropInfo^.Name;
IntUpdateTranslation(APersistentProp);
FStackPath:=StoreStackPath;
end;
end;
end;
end;
end;
end;
finally
freemem(APropList);
end;
if (AnInstance is TComponent) then
for i := 0 to TComponent(AnInstance).ComponentCount-1 do
begin
StoreStackPath:=FStackPath;
if TComponent(AnInstance).Components[i] is TCustomFrame then
UpdateTranslation(TComponent(AnInstance).Components[i]);
FStackPath:=StoreStackPath+'.'+TComponent(AnInstance).Components[i].Name;
IntUpdateTranslation(TComponent(AnInstance).Components[i]);
FStackPath:=StoreStackPath;
end;
end;
procedure TUpdateTranslator.UpdateTranslation(AnInstance: TPersistent);
begin
FStackPath:=AnInstance.ClassName;
IntUpdateTranslation(AnInstance);
end;
{ TDefaultTranslator }
constructor TDefaultTranslator.Create(MOFileName: string);
begin
inherited Create;
FMOFile := TMOFile.Create(UTF8ToSys(MOFileName));
end;
destructor TDefaultTranslator.Destroy;
begin
FMOFile.Free;
//If someone will use this class incorrectly, it can be destroyed
//before Reader destroying. It is a very bad thing, but in THIS situation
//in this case is impossible. Maybe, in future we can overcome this difficulty
inherited Destroy;
end;
procedure TDefaultTranslator.TranslateStringProperty(Sender: TObject;
const Instance: TPersistent; PropInfo: PPropInfo; var Content: string);
var
s: string;
begin
if Assigned(FMOFile) then
begin
s := GetIdentifierPath(Sender, Instance, PropInfo);
if s <> '' then
begin
s := FMoFile.Translate(s + #4 + Content);
if s = '' then
s := FMOFile.Translate(Content);
if s <> '' then
Content := s;
end;
end;
end;
{ TPOTranslator }
constructor TPOTranslator.Create(POFileName: string);
begin
inherited Create;
// TPOFile expects AFileName in UTF-8 encoding, no conversion required
FPOFile := TPOFile.Create(POFileName);
end;
destructor TPOTranslator.Destroy;
begin
FPOFile.Free;
//If someone will use this class incorrectly, it can be destroyed
//before Reader destroying. It is a very bad thing, but in THIS situation
//in this case is impossible. May be, in future we can overcome this difficulty
inherited Destroy;
end;
procedure TPOTranslator.TranslateStringProperty(Sender: TObject;
const Instance: TPersistent; PropInfo: PPropInfo; var Content: string);
var
s: string;
begin
if Assigned(FPOFile) then
begin
s := GetIdentifierPath(Sender, Instance, PropInfo);
if s <> '' then
begin
s := FPOFile.Translate(s, Content);
if s <> '' then
Content := s;
end;
end;
end;
procedure SetDefaultLang(Lang: string);
var
Dot1: integer;
LCLPath: string;
LocalTranslator: TUpdateTranslator;
i: integer;
begin
LocalTranslator := nil;
// search first po translation resources
try
lcfn := FindLocaleFileName('.po', Lang);
if lcfn <> '' then
begin
Translations.TranslateResourceStrings(lcfn);
LCLPath := ExtractFileName(lcfn);
Dot1 := pos('.', LCLPath);
if Dot1 > 1 then
begin
Delete(LCLPath, 1, Dot1 - 1);
LCLPath := ExtractFilePath(lcfn) + 'lclstrconsts' + LCLPath;
Translations.TranslateUnitResourceStrings('LCLStrConsts', LCLPath);
end;
LocalTranslator := TPOTranslator.Create(lcfn);
end;
except
lcfn := '';
end;
if lcfn='' then
begin
// try now with MO traslation resources
try
lcfn := FindLocaleFileName('.mo', Lang);
if lcfn <> '' then
begin
GetText.TranslateResourceStrings(UTF8ToSys(lcfn));
LCLPath := ExtractFileName(lcfn);
Dot1 := pos('.', LCLPath);
if Dot1 > 1 then
begin
Delete(LCLPath, 1, Dot1 - 1);
LCLPath := ExtractFilePath(lcfn) + 'lclstrconsts' + LCLPath;
if FileExistsUTF8(LCLPath) then
GetText.TranslateResourceStrings(UTF8ToSys(LCLPath));
end;
LocalTranslator := TDefaultTranslator.Create(lcfn);
end;
except
lcfn := '';
end;
end;
if LocalTranslator<>nil then
begin
if Assigned(LRSTranslator) then
LRSTranslator.Free;
LRSTranslator := LocalTranslator;
// Do not update the translations when this function is called from within
// the unit initialization.
if (Lang<>'') then
begin
for i := 0 to Screen.CustomFormCount-1 do
LocalTranslator.UpdateTranslation(Screen.CustomForms[i]);
end;
end;
end;
initialization
//It is safe to place code here as no form is initialized before unit
//initialization made
//initialization is made
SetDefaultLang('');
finalization
LRSTranslator.Free;
end.

493
lcl/lcltranslator.pas Normal file
View File

@ -0,0 +1,493 @@
unit LCLTranslator;
{ Copyright (C) 2004-2015 V.I.Volchenko and Lazarus Developers Team
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
for more details.
You should have received a copy of the GNU Library General Public License
along with this library; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
}
{
This unit is needed for using translated form strings made by Lazarus IDE.
It searches for translated .po/.mo files in some common places. If you need
to have .po/.mo files anywhere else, don't use this unit but initialize
LRSMoFile variable from LResources in your project by yourself.
If you need standard translation, just use this unit in your project and enable
i18n in project options. Note that you will have to call SetDefaultLang manually.
If you want it to be called automatically, use DefaultTranslator unit instead.
Another reason for including this unit may be using translated LCL messages.
This unit localizes LCL too, if it finds lclstrconsts.xx.po/lclstrconsts.xx.mo
in directory where your program translation files are placed.
}
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, LResources, GetText, Controls, typinfo, FileUtil, LCLProc,
Translations, Forms;
type
{ TUpdateTranslator }
TUpdateTranslator = class(TAbstractTranslator)
private
FStackPath: string;
procedure IntUpdateTranslation(AnInstance: TPersistent);
public
procedure UpdateTranslation(AnInstance: TPersistent);
end;
TDefaultTranslator = class(TUpdateTranslator)
private
FMOFile: TMOFile;
public
constructor Create(MOFileName: string);
destructor Destroy; override;
procedure TranslateStringProperty(Sender: TObject; const Instance: TPersistent;
PropInfo: PPropInfo; var Content: string); override;
end;
{ TPOTranslator }
TPOTranslator = class(TUpdateTranslator)
private
FPOFile: TPOFile;
public
constructor Create(POFileName: string);
destructor Destroy; override;
procedure TranslateStringProperty(Sender: TObject; const Instance: TPersistent;
PropInfo: PPropInfo; var Content: string); override;
end;
procedure SetDefaultLang(Lang: string);
implementation
uses
Menus;
type
TPersistentAccess = class(TPersistent);
function FindLocaleFileName(LCExt: string; Lang: string): string;
var
T: string;
i: integer;
function GetLocaleFileName(const LangID, LCExt: string): string;
var
LangShortID: string;
AppDir,LCFileName,FullLCFileName: String;
begin
if LangID <> '' then
begin
AppDir := ExtractFilePath(ParamStrUTF8(0));
LCFileName := ChangeFileExt(ExtractFileName(ParamStrUTF8(0)), LCExt);
FullLCFileName := ChangeFileExt(ExtractFileName(ParamStrUTF8(0)), '.' + LangID) + LCExt;
//ParamStrUTF8(0) is said not to work properly in linux, but I've tested it
Result := AppDir + LangID + DirectorySeparator + LCFileName;
if FileExistsUTF8(Result) then
exit;
Result := AppDir + 'languages' + DirectorySeparator + LangID +
DirectorySeparator + LCFileName;
if FileExistsUTF8(Result) then
exit;
Result := AppDir + 'locale' + DirectorySeparator + LangID +
DirectorySeparator + LCFileName;
if FileExistsUTF8(Result) then
exit;
Result := AppDir + 'locale' + DirectorySeparator + LangID +
DirectorySeparator + 'LC_MESSAGES' + DirectorySeparator + LCFileName;
if FileExistsUTF8(Result) then
exit;
{$IFDEF UNIX}
//In unix-like systems we can try to search for global locale
Result := '/usr/share/locale/' + LangID + '/LC_MESSAGES/' + LCFileName;
if FileExistsUTF8(Result) then
exit;
{$ENDIF}
//Let us search for reducted files
LangShortID := copy(LangID, 1, 2);
//At first, check all was checked
Result := AppDir + LangShortID + DirectorySeparator + LCFileName;
if FileExistsUTF8(Result) then
exit;
Result := AppDir + 'languages' + DirectorySeparator +
LangShortID + DirectorySeparator + LCFileName;
if FileExistsUTF8(Result) then
exit;
Result := AppDir + 'locale' + DirectorySeparator
+ LangShortID + DirectorySeparator + LCFileName;
if FileExistsUTF8(Result) then
exit;
Result := AppDir + 'locale' + DirectorySeparator + LangShortID +
DirectorySeparator + 'LC_MESSAGES' + DirectorySeparator + LCFileName;
if FileExistsUTF8(Result) then
exit;
//Full language in file name - this will be default for the project
//We need more careful handling, as it MAY result in incorrect filename
try
Result := AppDir + FullLCFileName;
if FileExistsUTF8(Result) then
exit;
//Common location (like in Lazarus)
Result := AppDir + 'locale' + DirectorySeparator + FullLCFileName;
if FileExistsUTF8(Result) then
exit;
Result := AppDir + 'languages' + DirectorySeparator + FullLCFileName;
if FileExistsUTF8(Result) then
exit;
except
Result := '';//Or do something else (useless)
end;
{$IFDEF UNIX}
Result := '/usr/share/locale/' + LangShortID + '/LC_MESSAGES/' +
LCFileName;
if FileExistsUTF8(Result) then
exit;
{$ENDIF}
FullLCFileName := ChangeFileExt(ExtractFileName(ParamStrUTF8(0)), '.' + LangShortID) + LCExt;
Result := AppDir + FullLCFileName;
if FileExistsUTF8(Result) then
exit;
Result := AppDir + 'locale' + DirectorySeparator + FullLCFileName;
if FileExistsUTF8(Result) then
exit;
Result := AppDir + 'languages' + DirectorySeparator + FullLCFileName;
if FileExistsUTF8(Result) then
exit;
end;
Result := '';
end;
begin
Result := '';
if Lang = '' then
for i := 1 to Paramcount - 1 do
if (ParamStrUTF8(i) = '--LANG') or (ParamStrUTF8(i) = '-l') or
(ParamStrUTF8(i) = '--lang') then
Lang := ParamStrUTF8(i + 1);
//Win32 user may decide to override locale with LANG variable.
if Lang = '' then
Lang := GetEnvironmentVariableUTF8('LANG');
if Lang = '' then
LCLGetLanguageIDs(Lang, T);
Result := GetLocaleFileName(Lang, LCExt);
if Result <> '' then
exit;
Result := ChangeFileExt(ParamStrUTF8(0), LCExt);
if FileExistsUTF8(Result) then
exit;
Result := '';
end;
function GetIdentifierPath(Sender: TObject;
const Instance: TPersistent;
PropInfo: PPropInfo): string;
var
Tmp: TPersistent;
Component: TComponent;
Reader: TReader;
begin
Result := '';
if (PropInfo = nil) or
(SysUtils.CompareText(PropInfo^.PropType^.Name, 'TTRANSLATESTRING') <> 0) then
exit;
// do not translate at design time
// get the component
Tmp := Instance;
while Assigned(Tmp) and not (Tmp is TComponent) do
Tmp := TPersistentAccess(Tmp).GetOwner;
if not Assigned(Tmp) then
exit;
Component := Tmp as TComponent;
if (csDesigning in Component.ComponentState) then
exit;
if (Sender is TReader) then
begin
Reader := TReader(Sender);
if Reader.Driver is TLRSObjectReader then
Result := TLRSObjectReader(Reader.Driver).GetStackPath
else
Result := Instance.ClassName + '.' + PropInfo^.Name;
end else if (Sender is TUpdateTranslator) then
Result := TUpdateTranslator(Sender).FStackPath + '.' + PropInfo^.Name;
Result := UpperCase(Result);
end;
var
lcfn: string;
{ TUpdateTranslator }
procedure TUpdateTranslator.IntUpdateTranslation(AnInstance: TPersistent);
var
i,j: integer;
APropCount: integer;
APropList: PPropList;
APropInfo: PPropInfo;
TmpStr: string;
APersistentProp: TPersistent;
StoreStackPath: string;
begin
APropCount := GetPropList(AnInstance.ClassInfo, APropList);
try
for i := 0 to APropCount-1 do
begin
APropInfo:=APropList^[i];
if Assigned(PPropInfo(APropInfo)^.GetProc) and
assigned(APropInfo^.PropType) and
IsStoredProp(AnInstance, APropInfo) then
case APropInfo^.PropType^.Kind of
tkSString,
tkLString,
tkAString: begin
TmpStr := '';
LRSTranslator.TranslateStringProperty(self,aninstance,APropInfo,TmpStr);
if TmpStr <>'' then
SetStrProp(AnInstance, APropInfo, TmpStr);
end;
tkclass: begin
APersistentProp := TPersistent(GetObjectProp(AnInstance, APropInfo, TPersistent));
if Assigned(APersistentProp) then
begin
if APersistentProp is TCollection then
begin
for j := 0 to TCollection(APersistentProp).Count-1 do
begin
StoreStackPath:=FStackPath;
FStackPath:=FStackPath+'.'+APropInfo^.Name+'['+inttostr(j)+']';
IntUpdateTranslation(TCollection(APersistentProp).Items[j]);
FStackPath:=StoreStackPath;
end;
end
else
begin
if APersistentProp is TComponent then
begin
if (csSubComponent in TComponent(APersistentProp).ComponentStyle) then
begin
StoreStackPath:=FStackPath;
FStackPath:=FStackPath+'.'+TComponent(APersistentProp).Name;
IntUpdateTranslation(APersistentProp);
FStackPath:=StoreStackPath;
end
end
else
begin
StoreStackPath:=FStackPath;
FStackPath:=FStackPath+'.'+APropInfo^.Name;
IntUpdateTranslation(APersistentProp);
FStackPath:=StoreStackPath;
end;
end;
end;
end;
end;
end;
finally
freemem(APropList);
end;
if (AnInstance is TComponent) then
for i := 0 to TComponent(AnInstance).ComponentCount-1 do
begin
StoreStackPath:=FStackPath;
if TComponent(AnInstance).Components[i] is TCustomFrame then
UpdateTranslation(TComponent(AnInstance).Components[i]);
FStackPath:=StoreStackPath+'.'+TComponent(AnInstance).Components[i].Name;
IntUpdateTranslation(TComponent(AnInstance).Components[i]);
FStackPath:=StoreStackPath;
end;
end;
procedure TUpdateTranslator.UpdateTranslation(AnInstance: TPersistent);
begin
FStackPath:=AnInstance.ClassName;
IntUpdateTranslation(AnInstance);
end;
{ TDefaultTranslator }
constructor TDefaultTranslator.Create(MOFileName: string);
begin
inherited Create;
FMOFile := TMOFile.Create(UTF8ToSys(MOFileName));
end;
destructor TDefaultTranslator.Destroy;
begin
FMOFile.Free;
//If someone will use this class incorrectly, it can be destroyed
//before Reader destroying. It is a very bad thing, but in THIS situation
//in this case is impossible. Maybe, in future we can overcome this difficulty
inherited Destroy;
end;
procedure TDefaultTranslator.TranslateStringProperty(Sender: TObject;
const Instance: TPersistent; PropInfo: PPropInfo; var Content: string);
var
s: string;
begin
if Assigned(FMOFile) then
begin
s := GetIdentifierPath(Sender, Instance, PropInfo);
if s <> '' then
begin
s := FMoFile.Translate(s + #4 + Content);
if s = '' then
s := FMOFile.Translate(Content);
if s <> '' then
Content := s;
end;
end;
end;
{ TPOTranslator }
constructor TPOTranslator.Create(POFileName: string);
begin
inherited Create;
// TPOFile expects AFileName in UTF-8 encoding, no conversion required
FPOFile := TPOFile.Create(POFileName);
end;
destructor TPOTranslator.Destroy;
begin
FPOFile.Free;
//If someone will use this class incorrectly, it can be destroyed
//before Reader destroying. It is a very bad thing, but in THIS situation
//in this case is impossible. May be, in future we can overcome this difficulty
inherited Destroy;
end;
procedure TPOTranslator.TranslateStringProperty(Sender: TObject;
const Instance: TPersistent; PropInfo: PPropInfo; var Content: string);
var
s: string;
begin
if Assigned(FPOFile) then
begin
s := GetIdentifierPath(Sender, Instance, PropInfo);
if s <> '' then
begin
s := FPOFile.Translate(s, Content);
if s <> '' then
Content := s;
end;
end;
end;
procedure SetDefaultLang(Lang: string);
var
Dot1: integer;
LCLPath: string;
LocalTranslator: TUpdateTranslator;
i: integer;
begin
LocalTranslator := nil;
// search first po translation resources
try
lcfn := FindLocaleFileName('.po', Lang);
if lcfn <> '' then
begin
Translations.TranslateResourceStrings(lcfn);
LCLPath := ExtractFileName(lcfn);
Dot1 := pos('.', LCLPath);
if Dot1 > 1 then
begin
Delete(LCLPath, 1, Dot1 - 1);
LCLPath := ExtractFilePath(lcfn) + 'lclstrconsts' + LCLPath;
Translations.TranslateUnitResourceStrings('LCLStrConsts', LCLPath);
end;
LocalTranslator := TPOTranslator.Create(lcfn);
end;
except
lcfn := '';
end;
if lcfn='' then
begin
// try now with MO traslation resources
try
lcfn := FindLocaleFileName('.mo', Lang);
if lcfn <> '' then
begin
GetText.TranslateResourceStrings(UTF8ToSys(lcfn));
LCLPath := ExtractFileName(lcfn);
Dot1 := pos('.', LCLPath);
if Dot1 > 1 then
begin
Delete(LCLPath, 1, Dot1 - 1);
LCLPath := ExtractFilePath(lcfn) + 'lclstrconsts' + LCLPath;
if FileExistsUTF8(LCLPath) then
GetText.TranslateResourceStrings(UTF8ToSys(LCLPath));
end;
LocalTranslator := TDefaultTranslator.Create(lcfn);
end;
except
lcfn := '';
end;
end;
if LocalTranslator<>nil then
begin
if Assigned(LRSTranslator) then
LRSTranslator.Free;
LRSTranslator := LocalTranslator;
// Do not update the translations when this function is called from within
// the unit initialization.
if (Lang<>'') then
begin
for i := 0 to Screen.CustomFormCount-1 do
LocalTranslator.UpdateTranslation(Screen.CustomForms[i]);
end;
end;
end;
finalization
LRSTranslator.Free;
end.