724 lines
25 KiB
ObjectPascal
724 lines
25 KiB
ObjectPascal
{-----------------------------------------------------------------------------
|
|
The contents of this file are subject to the Mozilla Public License
|
|
Version 1.1 (the "License"); you may not use this file except in compliance
|
|
with the License. You may obtain a copy of the License at
|
|
http://www.mozilla.org/MPL/MPL-1.1.html
|
|
|
|
Software distributed under the License is distributed on an "AS IS" basis,
|
|
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
|
|
the specific language governing rights and limitations under the License.
|
|
|
|
The Original Code is: JvgLanguageLoader.PAS, released on 2003-01-15.
|
|
|
|
The Initial Developer of the Original Code is Andrey V. Chudin, [chudin att yandex dott ru]
|
|
Portions created by Andrey V. Chudin are Copyright (C) 2003 Andrey V. Chudin.
|
|
All Rights Reserved.
|
|
|
|
Contributor(s):
|
|
Michael Beck [mbeck att bigfoot dott com].
|
|
Burov Dmitry, translation of russian text.
|
|
|
|
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
|
|
located at http://jvcl.sourceforge.net
|
|
|
|
Description:
|
|
eng:
|
|
Load new string resources from file to components. Uses RTTI
|
|
|
|
Replaces the found lines(strings) from one language to another if found in dictionary
|
|
|
|
Dictionary is text like:
|
|
Langauge1_line=Language2_line
|
|
..
|
|
Language1_line=Language2_line
|
|
|
|
rus:
|
|
Çàìåíÿåò ïî ñëîâàðþ èç ôàéëà âñå íàéäåííûå ñòðîêè îäíîãî ÿçûêà íà äðóãîé
|
|
Ñëîâàðü â âèäå òåêñòà âèäà:
|
|
Ñòðîêà íà ÿçûêå 1=Ñòðîêà íà ÿçûêå 2
|
|
...
|
|
Ñòðîêà íà ÿçûêå 1=Ñòðîêà íà ÿçûêå 2
|
|
|
|
Known Issues:
|
|
-----------------------------------------------------------------------------}
|
|
// $Id: JvgLanguageLoader.pas 11043 2006-11-26 07:21:48Z marquardt $
|
|
|
|
unit JvgLanguageLoader;
|
|
|
|
{$I jvcl.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
{$IFDEF USEJVCL}
|
|
{$IFDEF UNITVERSIONING}
|
|
JclUnitVersioning,
|
|
{$ENDIF UNITVERSIONING}
|
|
{$ENDIF USEJVCL}
|
|
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
|
|
{$IFDEF USEJVCL}
|
|
ComCtrls,
|
|
JvComponentBase;
|
|
{$ELSE}
|
|
ComCtrls;
|
|
{$ENDIF USEJVCL}
|
|
|
|
type
|
|
TLanguageLoaderOptions = set of (lofTrimSpaces, lofConvertEscapes);
|
|
// lofConvertEscapes: if set (=default) converts escape sequences when reading
|
|
// language strings form language file, eg \n = #10 (Note: only used in
|
|
// FTranslationStrings, not in FTranslations
|
|
//{îïöèÿ óäàëåíèÿ íà÷àëüíûõ è çàâåðøàþùèõ ïðîáåëîâ}
|
|
{ Option to Trim first and last spaces [translated] }
|
|
|
|
{$IFDEF USEJVCL}
|
|
TJvgLanguageLoader = class(TJvComponent)
|
|
{$ELSE}
|
|
TJvgLanguageLoader = class(TComponent)
|
|
{$ENDIF USEJVCL}
|
|
private
|
|
// FTranslations: temporary stringlist with translations of strings on a form
|
|
// Note: is destroyed after the translation is complete!
|
|
FTranslations: TStringList;
|
|
|
|
// FOldStrings: temporary stringlist needed when changing translations
|
|
// Note: is destroyed after the translation is complete!
|
|
FOldStrings: TStringList;
|
|
|
|
// FTranslationChange: used internally. Set to true when changing translations
|
|
// This way we now we have to look up the original text in FOldStrings.
|
|
FTranslationChange: Boolean;
|
|
|
|
// FOptions: some translation options
|
|
FOptions: TLanguageLoaderOptions;
|
|
|
|
// FSaveEmpty doesn't work at the moment? (include in options?)
|
|
//FSaveEmpty: Boolean; //JGB: save empty strings or not (default=false)
|
|
|
|
// FDictionaryFileName: Name and path of the language dictionary file
|
|
FDictionaryFileName: string;
|
|
|
|
// FFormSection: section in languagefile where the translation of the
|
|
// current form can be found
|
|
// (empty=default=[<FormName>] (if Owner=nil: [Translation])
|
|
FFormSection: string;
|
|
|
|
// FStringsSection: section in languagefile where the translation of
|
|
// string consts can be found
|
|
// (empty=default=[<FormName>.Strings] (if Owner=nil: [Translation.Strings])
|
|
FStringsSection: string;
|
|
|
|
// FTranslationStrings: list of string translations!
|
|
// Read form FStringSection of language file
|
|
FTranslationStrings: TStringList;
|
|
|
|
// FIgnoreList: List of component names that should not be translated e.g. contents of a RichEdit control
|
|
FIgnoreList: TStringList;
|
|
|
|
// DEPRECATED
|
|
function TranslateString(AText: string): string;
|
|
|
|
procedure SetIgnoreList(const Value: TStringList);
|
|
procedure SetTranslationStrings(const Value: TStringList);
|
|
function GetFormSection: string;
|
|
function GetStringsSection: string;
|
|
protected
|
|
procedure UpdateComponent(Component: TPersistent); virtual;
|
|
procedure GetAllComponentStrings(Component: TPersistent); virtual;
|
|
|
|
// Changed to internal function
|
|
procedure LoadLanguage(Component: TComponent; const FileName: string);
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
|
|
// TranslateComponent: Translate the said component using the language file
|
|
// specified in FDictionaryFileName
|
|
procedure TranslateComponent(Component: TComponent; DoLoadStrings: Boolean);
|
|
|
|
// ChangeTranslation: As TranslateComponent but now change an already
|
|
// translated component to another language. DictionaryFileName should
|
|
// contain the new language filename and the old (current) language file
|
|
// should be in OldLanguageFilename
|
|
procedure ChangeTranslation(Component: TComponent; DoLoadStrings: Boolean;
|
|
OldLanguageFileName: string);
|
|
|
|
// LoadStrings: Loads strings form the FStringsSection of the languagefile
|
|
// into FTranslationStrings, after that it can be used in MessageBox and Translate
|
|
procedure LoadStrings;
|
|
|
|
// JGB - Save all strings on a form (equivalent to LoadLanguage
|
|
procedure SaveAllStrings(Component: TComponent; FileName: string);
|
|
|
|
// JGB - Translation wrapper for Application.Messagebox
|
|
// Call this instead of Application.Messagebox when translation is needed
|
|
function MessageBox(const Text, Caption: PChar; Flags: Longint = MB_OK): Integer;
|
|
// Maybe more like MessageBox needed?
|
|
|
|
// JGB - Translate: use this to translate a string loaded in ... stringlist (todo)
|
|
function Translate(const Text: string): string;
|
|
|
|
property TranslationStrings: TStringList read FTranslationStrings
|
|
write SetTranslationStrings;
|
|
published
|
|
property Options: TLanguageLoaderOptions read FOptions write FOptions
|
|
default [lofTrimSpaces, lofConvertEscapes];
|
|
property DictionaryFileName: string read FDictionaryFileName
|
|
write FDictionaryFileName;
|
|
property FormSection: string read GetFormSection write FFormSection;
|
|
property StringsSection: string read GetStringsSection write FStringsSection;
|
|
property IgnoreList: TStringList read FIgnoreList write SetIgnoreList;
|
|
end;
|
|
|
|
|
|
procedure LoadLanguage(Component: TComponent; FileName: string; Options:
|
|
TLanguageLoaderOptions);
|
|
|
|
{$IFDEF USEJVCL}
|
|
{$IFDEF UNITVERSIONING}
|
|
const
|
|
UnitVersioning: TUnitVersionInfo = (
|
|
RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/tags/JVCL3_32/run/JvgLanguageLoader.pas $';
|
|
Revision: '$Revision: 11043 $';
|
|
Date: '$Date: 2006-11-26 08:21:48 +0100 (dim., 26 nov. 2006) $';
|
|
LogPath: 'JVCL\run'
|
|
);
|
|
{$ENDIF UNITVERSIONING}
|
|
{$ENDIF USEJVCL}
|
|
|
|
implementation
|
|
|
|
{.$DEFINE DEBUG_LANG}
|
|
uses {$IFDEF DEBUG_LANG}DbugIntf,{$ENDIF}
|
|
TypInfo, IniFiles, JclStrings;
|
|
|
|
//{Ô-èÿ äëÿ çàãðóçêè ñëîâàðÿ áåç ïðåäâàðèòåëüíîãî ñîçäàíèÿ êîìïîíåíòà}
|
|
{ Function to load dictionary without previous creation of the component [translated] }
|
|
|
|
procedure LoadLanguage(Component: TComponent; FileName: string; Options:
|
|
TLanguageLoaderOptions);
|
|
var
|
|
LanguageLoader: TJvgLanguageLoader;
|
|
begin
|
|
LanguageLoader := TJvgLanguageLoader.Create(nil);
|
|
try
|
|
LanguageLoader.LoadLanguage(Component, FileName);
|
|
finally
|
|
LanguageLoader.Free;
|
|
end;
|
|
end;
|
|
|
|
{ TJvgLanguageLoader }
|
|
|
|
constructor TJvgLanguageLoader.Create(AOwner: TComponent);
|
|
begin
|
|
inherited;
|
|
Options := [lofTrimSpaces,lofConvertEscapes];
|
|
FIgnoreList := TStringList.Create;
|
|
FIgnoreList.Sorted := True;
|
|
{$IFDEF COMPILER6_UP}
|
|
FIgnoreList.CaseSensitive := True;
|
|
{$ENDIF COMPILER6_UP}
|
|
|
|
FTranslationStrings := TStringList.Create;
|
|
FTranslationStrings.Sorted := True;
|
|
{$IFDEF COMPILER6_UP}
|
|
FTranslationStrings.CaseSensitive := True;
|
|
{$ENDIF COMPILER6_UP}
|
|
end;
|
|
|
|
destructor TJvgLanguageLoader.Destroy;
|
|
begin
|
|
FreeAndNil(FIgnoreList);
|
|
FreeAndNil(FTranslationStrings);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TJvgLanguageLoader.SetIgnoreList(const Value: TStringList);
|
|
begin
|
|
FIgnoreList.Assign(Value);
|
|
end;
|
|
|
|
procedure TJvgLanguageLoader.SetTranslationStrings(const Value: TStringList);
|
|
begin
|
|
FTranslationStrings.Assign(Value);
|
|
end;
|
|
|
|
function TJvgLanguageLoader.GetFormSection: string;
|
|
begin
|
|
if FFormSection <> '' then
|
|
Result := FFormSection
|
|
else
|
|
if Owner <> nil then
|
|
Result := Owner.Name
|
|
else
|
|
Result := 'Translation';
|
|
end;
|
|
|
|
function TJvgLanguageLoader.GetStringsSection: string;
|
|
begin
|
|
if FStringsSection <> '' then
|
|
Result := FStringsSection
|
|
else
|
|
if Owner <> nil then
|
|
Result := Owner.Name
|
|
else
|
|
Result := 'Translation.Strings';
|
|
end;
|
|
|
|
//{ Çàãðóçêà ñëîâàðÿ, îáõîä óêàçàííîãî êîìïîíåíòà è }
|
|
//{ âñåõ åãî äî÷åðíèõ êîìïîíåíòîâ }
|
|
{ Loading dictionary, passing the given component and all his children components
|
|
[translated] }
|
|
|
|
procedure TJvgLanguageLoader.LoadLanguage(Component: TComponent; const FileName: string);
|
|
var
|
|
IniFile: TCustomIniFile;
|
|
|
|
procedure UpdateAllComponents(Component: TComponent);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
//{ îáðàáîòêà ñâîöñòâ êîìïîíåíòà }
|
|
{ Processing the component's properties [translated] }
|
|
UpdateComponent(Component);
|
|
for I := 0 to Component.ComponentCount - 1 do
|
|
UpdateAllComponents(Component.Components[I]);
|
|
end;
|
|
|
|
begin
|
|
FTranslations := TStringList.Create;
|
|
IniFile := TIniFile.Create(FileName);
|
|
try
|
|
//{ Çàãðóçêà ñëîâàðÿ èç çàäàííîãî ôàéëà }
|
|
{ Loading dictionary from given file }
|
|
FTranslations.Sorted := true;
|
|
{$IFDEF COMPILER6_UP}
|
|
FTranslations.CaseSensitive := True;
|
|
{$ENDIF COMPILER6_UP}
|
|
IniFile.ReadSectionValues(FormSection,FTranslations);
|
|
UpdateAllComponents(Component);
|
|
finally
|
|
FTranslations.Free;
|
|
FreeAndNil(IniFile);
|
|
end;
|
|
end;
|
|
|
|
// TranslateComponent: Translate the said component using the language file
|
|
// specified in FDictionaryFileName
|
|
// if DoLoadStrings = true then LoadStrings is called after translating the form
|
|
procedure TJvgLanguageLoader.TranslateComponent(Component: TComponent;
|
|
DoLoadStrings: Boolean);
|
|
begin
|
|
if DictionaryFileName <> '' then
|
|
begin
|
|
LoadLanguage(Component,DictionaryFileName);
|
|
if DoLoadStrings then
|
|
LoadStrings;
|
|
end;
|
|
end;
|
|
|
|
// ChangeTranslation: As TranslateComponent but now change an already
|
|
// translated component to another language. DictionaryFileName should
|
|
// contain the new language filename and the old (current) language file
|
|
// should be in OldLanguageFilename
|
|
procedure TJvgLanguageLoader.ChangeTranslation(Component: TComponent;
|
|
DoLoadStrings: Boolean; OldLanguageFileName: string);
|
|
var
|
|
OldIniFile: TCustomIniFile;
|
|
I: Integer;
|
|
EqualPos, Len: Integer;
|
|
TempStr: string;
|
|
begin
|
|
if (DictionaryFileName <> '') and (OldLanguageFileName <> '') then
|
|
begin
|
|
FOldStrings := TStringList.Create;
|
|
OldIniFile := TIniFile.Create(OldLanguageFileName);
|
|
try
|
|
{$IFDEF COMPILER6_UP}
|
|
FOldStrings.CaseSensitive := True;
|
|
{$ENDIF COMPILER6_UP}
|
|
FOldStrings.Sorted := False;
|
|
OldIniFile.ReadSectionValues(FormSection, FOldStrings);
|
|
FOldStrings.BeginUpdate;
|
|
for I := 0 to FOldStrings.Count-1 do
|
|
begin
|
|
TempStr := FOldStrings.Strings[I];
|
|
EqualPos := Pos('=', TempStr);
|
|
if EqualPos > 0 then // Found: exchange Name=Value
|
|
begin
|
|
Len := Length(TempStr);
|
|
FOldStrings.Strings[I] := Copy(TempStr, EqualPos + 1, Len - EqualPos) + '=' +
|
|
Copy(TempStr,1,EqualPos-1);
|
|
end;
|
|
end;
|
|
FOldStrings.EndUpdate;
|
|
FOldStrings.Sorted := True;
|
|
FTranslationChange := True;
|
|
LoadLanguage(Component, DictionaryFileName);
|
|
finally
|
|
FTranslationChange := False;
|
|
FreeAndNil(FOldStrings);
|
|
OldIniFile.Free;
|
|
end;
|
|
// *** Alas, Changing translation for the following is not possible!
|
|
if DoLoadStrings then
|
|
LoadStrings;
|
|
end;
|
|
end;
|
|
|
|
|
|
// LoadStrings: Loads strings form the FStringsSection of the languagefile
|
|
// into FTranslationStrings, after that it can be used in MessageBox and Translate
|
|
procedure TJvgLanguageLoader.LoadStrings;
|
|
var
|
|
I: Integer;
|
|
IniFile: TCustomIniFile;
|
|
begin
|
|
if DictionaryFileName <> '' then
|
|
begin
|
|
IniFile := TIniFile.Create(DictionaryFileName);
|
|
try
|
|
TranslationStrings.Clear; // Remove old translation strings
|
|
TranslationStrings.Sorted := False; // no sort during processing
|
|
IniFile.ReadSectionValues(StringsSection, TranslationStrings);
|
|
if lofConvertEscapes in Options then
|
|
begin
|
|
TranslationStrings.BeginUpdate;
|
|
for I := 0 to TranslationStrings.Count-1 do
|
|
TranslationStrings.Strings[I] :=
|
|
StrEscapedToString(TranslationStrings.Strings[i]);
|
|
TranslationStrings.EndUpdate;
|
|
end;
|
|
TranslationStrings.Sorted := True; // and now sort once
|
|
finally
|
|
FreeAndNil(IniFile);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
// JGB - Save all strings on a form (equivalent to LoadLanguage
|
|
procedure TJvgLanguageLoader.SaveAllStrings(Component: TComponent; FileName: string);
|
|
var
|
|
IniFile: TCustomIniFile;
|
|
|
|
procedure GetAllComponents(Component: TComponent);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
// Processing the component's properties [translated]
|
|
GetAllComponentStrings(Component);
|
|
for I := 0 to Component.ComponentCount - 1 do
|
|
GetAllComponents(Component.Components[I]);
|
|
end;
|
|
|
|
procedure WriteSectionValues;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for I := 0 to FTranslations.Count-1 do
|
|
IniFile.WriteString(FormSection, FTranslations.Names[i],FTranslations.Values[FTranslations.Names[i]]);
|
|
end;
|
|
|
|
begin
|
|
FTranslations := TStringList.Create;
|
|
IniFile := TIniFile.Create(FileName);
|
|
try
|
|
// Saving dictionary to given file
|
|
GetAllComponents(Component);
|
|
WriteSectionValues; // write to dictionary
|
|
finally
|
|
FTranslations.Free;
|
|
IniFile.Free;
|
|
end;
|
|
end;
|
|
|
|
//{ Ïðîõîä ïî âñåì ñâîéñòâàì êîìïîíåíòà }
|
|
//{ Äëÿ âñåõ ñòðîêîâûõ ñâîéñòâ - çàãðóçêà ïåðåâîäà èç ñîîâàðÿ }
|
|
{ Passing(iterating) all the component's properties
|
|
For all string properties - load the translation from dictionary [translated] }
|
|
|
|
procedure TJvgLanguageLoader.UpdateComponent(Component: TPersistent);
|
|
var
|
|
PropInfo: PPropInfo;
|
|
TypeInf, PropTypeInf: PTypeInfo;
|
|
TypeData: PTypeData;
|
|
I, J, Idx: integer;
|
|
AName, PropName, StringPropValue: string;
|
|
PropList: PPropList;
|
|
NumProps: word;
|
|
PropObject: TObject;
|
|
OldSort: Boolean;
|
|
TempTrans: string;
|
|
|
|
function NeedsTranslation(SourceStr: string; var DestStr: string): Boolean;
|
|
begin
|
|
DestStr := TranslateString(SourceStr);
|
|
Result := SourceStr <> DestStr;
|
|
end;
|
|
|
|
begin
|
|
// JGB: First we look if this component is in the ignore list
|
|
if IgnoreList.Find(Component.GetNamePath, Idx) then
|
|
Exit; // Found in the list of excluded components so don't translate
|
|
|
|
{ Playing with RTTI }
|
|
TypeInf := Component.ClassInfo;
|
|
AName := TypeInf^.Name;
|
|
TypeData := GetTypeData(TypeInf);
|
|
NumProps := TypeData^.PropCount;
|
|
|
|
GetMem(PropList, NumProps * sizeof(Pointer));
|
|
|
|
try
|
|
GetPropInfos(TypeInf, PropList);
|
|
|
|
for I := 0 to NumProps - 1 do
|
|
begin
|
|
PropName := PropList^[I]^.Name;
|
|
|
|
PropTypeInf := PropList^[I]^.PropType^;
|
|
PropInfo := PropList^[I];
|
|
|
|
case PropTypeInf^.Kind of
|
|
tkString, tkLString:
|
|
//{ Ïåðåâîäèòü ñâîéñòâî Name íå ñëåäóåò }
|
|
{ .Name is not to be translated [translated] }
|
|
if PropName <> 'Name' then
|
|
begin
|
|
//{ Ïîëó÷åíèå çíà÷åíèÿ ñâîéñòâà è ïîèñê ïåðåâîäà â ñëîâàðå }
|
|
{ Retrieving the property's value and searchin for translation in
|
|
dictionary [translated] }
|
|
StringPropValue := GetStrProp(Component, PropInfo);
|
|
if NeedsTranslation(StringPropValue, TempTrans) then
|
|
SetStrProp(Component, PropInfo, TempTrans);
|
|
end;
|
|
tkClass:
|
|
begin
|
|
PropObject := GetObjectProp(Component, PropInfo); {, TPersistent}
|
|
|
|
if Assigned(PropObject) then
|
|
begin
|
|
//{ Äëÿ äî÷åðíèõ ñâîéñòâ-êëàññîâ âûçîâ ïðîñìîòðà ñâîéñòâ }
|
|
{ For children properties-classes calling iterate again [translated] }
|
|
if PropObject is TPersistent then
|
|
UpdateComponent(PropObject as TPersistent);
|
|
|
|
//{ Èíäèâèäóàëüíûé ïîäõîä ê íåêîòîðûì êëàññàì }
|
|
{ Specific handling of some certain classes [translated] }
|
|
if PropObject is TStrings then
|
|
begin
|
|
TStrings(PropObject).BeginUpdate;
|
|
try
|
|
if PropObject is TStringList then
|
|
begin
|
|
// It's problematic when a stringlist is sorted:
|
|
// so reset it for now
|
|
OldSort := (PropObject as TStringList).Sorted;
|
|
(PropObject as TStringList).Sorted := False;
|
|
for J := 0 to (PropObject as TStrings).Count - 1 do
|
|
if NeedsTranslation(TStrings(PropObject)[J], TempTrans) then
|
|
TStrings(PropObject)[J] := TempTrans;
|
|
(PropObject as TStringList).Sorted := OldSort;
|
|
end
|
|
else
|
|
for J := 0 to (PropObject as TStrings).Count - 1 do
|
|
if NeedsTranslation(TStrings(PropObject)[J], TempTrans) then
|
|
TStrings(PropObject)[J] := TempTrans;
|
|
finally
|
|
TStrings(PropObject).EndUpdate;
|
|
end;
|
|
end;
|
|
if PropObject is TTreeNodes then
|
|
begin
|
|
TTreeNodes(PropObject).BeginUpdate;
|
|
try
|
|
for J := 0 to (PropObject as TTreeNodes).Count - 1 do
|
|
if NeedsTranslation(TTreeNodes(PropObject).Item[J].Text, TempTrans) then
|
|
TTreeNodes(PropObject).Item[J].Text := TempTrans;
|
|
finally
|
|
TTreeNodes(PropObject).EndUpdate;
|
|
end;
|
|
end;
|
|
if PropObject is TListItems then
|
|
begin
|
|
TListItems(PropObject).BeginUpdate;
|
|
try
|
|
for J := 0 to (PropObject as TListItems).Count - 1 do
|
|
if NeedsTranslation(TListItems(PropObject).Item[J].Caption, TempTrans) then
|
|
TListItems(PropObject).Item[J].Caption := TempTrans;
|
|
finally
|
|
TListItems(PropObject).EndUpdate;
|
|
end;
|
|
end;
|
|
//{ Çäåñü ìîæíî äîáàâèòü îáðàáîòêó îñòàëüíûõ êëàññîâ }
|
|
{ And here may be added more specific handlers for certain other
|
|
classes [translated] }
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
finally
|
|
FreeMem(PropList, NumProps * sizeof(Pointer));
|
|
end;
|
|
end;
|
|
|
|
{ Get all strings on this component that can be translated and put it in
|
|
the dictionary. }
|
|
|
|
procedure TJvgLanguageLoader.GetAllComponentStrings(Component: TPersistent);
|
|
var
|
|
PropInfo: PPropInfo;
|
|
TypeInf, PropTypeInf: PTypeInfo;
|
|
TypeData: PTypeData;
|
|
I, J, Idx: integer;
|
|
AName, PropName, StringPropValue: string;
|
|
PropList: PPropList;
|
|
NumProps: Word;
|
|
PropObject: TObject;
|
|
begin
|
|
// JGB: First we look if this component is in the ignore list
|
|
if IgnoreList.Find(Component.GetNamePath, Idx) then
|
|
Exit; // Found in the list of excluded components so don't translate
|
|
|
|
{ Playing with RTTI }
|
|
TypeInf := Component.ClassInfo;
|
|
AName := TypeInf^.Name;
|
|
TypeData := GetTypeData(TypeInf);
|
|
NumProps := TypeData^.PropCount;
|
|
|
|
GetMem(PropList, NumProps * sizeof(pointer));
|
|
|
|
try
|
|
GetPropInfos(TypeInf, PropList);
|
|
|
|
for I := 0 to NumProps - 1 do
|
|
begin
|
|
PropName := PropList^[I]^.Name;
|
|
|
|
PropTypeInf := PropList^[I]^.PropType^;
|
|
PropInfo := PropList^[I];
|
|
|
|
case PropTypeInf^.Kind of
|
|
tkString, tkLString:
|
|
{ .Name is not to be translated [translated] }
|
|
if PropName <> 'Name' then
|
|
begin
|
|
{ Retrieving the property's value and storing in dictionary }
|
|
StringPropValue := GetStrProp(Component, PropInfo);
|
|
if {FSaveEmpty or} (StringPropValue <> '') then
|
|
begin
|
|
//FTranslations.Add(PropName+'='+StringPropValue);
|
|
FTranslations.Add(Component.GetNamePath + '.' + PropName);
|
|
FTranslations.Values[Component.GetNamePath + '.' + PropName] := StringPropValue;
|
|
end;
|
|
end;
|
|
tkClass:
|
|
begin
|
|
PropObject := GetObjectProp(Component, PropInfo); //{, TPersistent}
|
|
|
|
if Assigned(PropObject) then
|
|
begin
|
|
{ For children properties-classes calling iterate again [translated] }
|
|
if (PropObject is TPersistent) then
|
|
UpdateComponent(PropObject as TPersistent);
|
|
|
|
{ Specific handling of some certain classes [translated] }
|
|
if (PropObject is TStrings) then
|
|
begin
|
|
for J := 0 to (PropObject as TStrings).Count - 1 do
|
|
if {FSaveEmpty or} (TStrings(PropObject)[J] <> '') then
|
|
begin
|
|
FTranslations.Add(Component.GetNamePath + '.' + PropName);
|
|
FTranslations.Values[Component.GetNamePath + '.' + PropName] :=
|
|
TStrings(PropObject)[J];
|
|
end;
|
|
end;
|
|
if (PropObject is TTreeNodes) then
|
|
begin
|
|
for J := 0 to (PropObject as TTreeNodes).Count - 1 do
|
|
if {FSaveEmpty or} (TTreeNodes(PropObject).Item[J].Text <> '') then
|
|
begin
|
|
FTranslations.Add(Component.GetNamePath + '.' + PropName);
|
|
FTranslations.Values[Component.GetNamePath + '.' + PropName] :=
|
|
TTreeNodes(PropObject).Item[J].Text;
|
|
end;
|
|
end;
|
|
if (PropObject is TListItems) then
|
|
begin
|
|
for J := 0 to (PropObject as TListItems).Count - 1 do
|
|
if {FSaveEmpty or} (TListItems(PropObject).Item[J].Caption <> '') then
|
|
begin
|
|
FTranslations.Add(Component.GetNamePath + '.' + PropName);
|
|
FTranslations.Values[Component.GetNamePath + '.' + PropName] :=
|
|
TListItems(PropObject).Item[J].Caption;
|
|
end;
|
|
end;
|
|
{ And here may be added more specific handlers for certain other
|
|
classes [translated] }
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
finally
|
|
FreeMem(PropList, NumProps * sizeof(pointer));
|
|
end;
|
|
end;
|
|
|
|
//{ Ïîèñê ïåðåâîäà äëÿ çàäàííîé ñòðîêè â ñëîâàðå }
|
|
{ Searching for translation of given line in dictionary [translated] }
|
|
|
|
function TJvgLanguageLoader.TranslateString(AText: string): string;
|
|
var
|
|
TempStr: string;
|
|
begin
|
|
if lofTrimSpaces in Options then
|
|
AText := trim(AText);
|
|
|
|
if FTranslationChange then
|
|
TempStr := FOldStrings.Values[AText]
|
|
else
|
|
TempStr := AText;
|
|
|
|
if FTranslations.IndexOfName(TempStr) <> -1 then
|
|
Result := FTranslations.Values[TempStr]
|
|
else
|
|
Result := AText;
|
|
end;
|
|
|
|
// JGB - Translation wrapper for Application.Messagebox
|
|
// Call this instead of Application.Messagebox when translation is needed
|
|
function TJvgLanguageLoader.MessageBox(const Text, Caption: PChar; Flags: Longint = MB_OK): Integer;
|
|
begin
|
|
Result := Application.MessageBox(PChar(Translate(string(Text))),PChar(Translate(string(Caption))),Flags);
|
|
end;
|
|
|
|
// JGB - Translate: use this to translate a string
|
|
function TJvgLanguageLoader.Translate(const Text: string): string;
|
|
begin
|
|
// should be removed: spaces are always trimmed when using ini file
|
|
if lofTrimSpaces in Options then
|
|
Result := Trim(Text)
|
|
else
|
|
Result := Text;
|
|
|
|
Result := FTranslationStrings.Values[Result];
|
|
if Result = '' then
|
|
Result := Text;
|
|
end;
|
|
|
|
|
|
{$IFDEF USEJVCL}
|
|
{$IFDEF UNITVERSIONING}
|
|
initialization
|
|
RegisterUnitVersion(HInstance, UnitVersioning);
|
|
|
|
finalization
|
|
UnregisterUnitVersion(HInstance);
|
|
{$ENDIF UNITVERSIONING}
|
|
{$ENDIF USEJVCL}
|
|
|
|
end.
|
|
|