Multilanguage Delphi Programm
Sprache:
Delphi XE6 VCL
Inhalt:
In dieser einfachen Unit "SpracheUnit.pas" ist eine Klasse erstellt worden, mit der man sein Programm auf Multilanguage umbauen kann. Die Unit ist selbst geschrieben und darf natürlich nach Belieben verändert/angepasst werden. Sie basiert auf UNICODE, es sind also auch Sprachen mit anderen Schriftsätzen möglich.
Die Funktionsweise habe ich in einem kleinen Beispielprogramm mit dargelegt.
Die Klasse ist sehr einfach aufgebaut und sofort benutzbar. Lediglich die verschiedenen Komponenten müssen noch eingepflegt werden. Jeder, der ein bisschen programmieren kann findet leicht die 2 Stellen, an denen alle Componenten eingebaut werden können.
Quellcode:
// SpracheUnit
unit SpracheUnit;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.StrUtils, Vcl.StdCtrls, Vcl.Menus;
type
TSprache = class
private
{ Private-Deklarationen }
LoadedLanguage : string;
LoadedLanguageKennung2 : string;
EintragListe : array of record
Name:string;
Wert:string;
end;
function GetEintragIndex(Name:string):integer;
Procedure SaveComponentenToStringlist(wohin:tstringlist;Quelle:TComponent);
Procedure SucheUndSetzeComponente(FPfad,wert:string;Quelle:TComponent);
public
{ Public-Deklarationen }
constructor Create(AOwner: TComponent); overload;
function RegisterIndividualText(Name:string;Wert:string):boolean;
function LoadedLanguageNameLong:string;
function LoadLanguageFromFile(Dateiname:string):boolean;
function LoadLanguageFromRessource(const ResName: String):boolean;
function LoadLanguageFromString(Quelle: String):boolean;
function Load_Name_And_Name2_FormFile(Dateiname: String;var NameK:string;var NameL:string):boolean;
procedure SaveLanguageToFile(Dateiname:string;NameK:string='';NameL:string='');
procedure SaveLanguageToString(var Ziel:string;NameK:string='';NameL:string='');
function GetText(Name:string):string;
function GetTextF(Name:string; const Args: array of const):string;
end;
var Sprache:TSprache;
implementation
constructor TSprache.Create(AOwner: TComponent);
begin
inherited create;
setlength(EintragListe,0);
LoadedLanguage:='Standard';
LoadedLanguageKennung2:='ST';
end;
function TSprache.LoadedLanguageNameLong:string;
begin
result:=LoadedLanguage+' ('+LoadedLanguageKennung2+')';
end;
function TSprache.GetEintragIndex(Name:string):integer;
var i:integer;
begin
result:=-1;
for i:=0 to length(EintragListe)-1 do
if EintragListe[i].Name='individuell.'+Name then
begin
result:=i;
exit;
end;
end;
function TSprache.RegisterIndividualText(Name:string;Wert:string):boolean;
var i:integer;
begin
Name:='individuell.'+Name;
result:=false;
i:=GetEintragIndex(Name);
if (i=-1) and (pos('=',Name)=0) and (pos('[',Name)=0) then
begin
result:=true;
setlength(EintragListe,length(EintragListe)+1);
EintragListe[length(EintragListe)-1].Name:=Name;
EintragListe[length(EintragListe)-1].Wert:=Wert;
end else showmessage('Sprache: doppelter/falscher Eintrag "'+Name+'"');
end;
function ReplaceStr_own(source,vonStr,zuStr:string):string;
var found,sPos,vonStr_len,zuStr_len:cardinal;
begin
result:=source;
sPos:=1;
vonStr_len:=length(vonStr);
zuStr_len:=length(zuStr);
repeat
found:=posEx(vonStr,result,sPos);
if found=0 then break;
spos:=found+zuStr_len;
Delete(result,found,vonStr_len);
Insert(zuStr,result,found);
until found=0;
end;
const EscapeChar = '?';
function hextostr(was:string):string;
var zahler: integer;
hexstr:string;
begin
result:='';
try
hexstr:='';
if length(was) mod 4 = 0 then
for zahler:=low(was) to high(was) do
begin
hexstr:=hexstr+was[zahler];
if length(hexstr)=4 then
begin
result:=result+Char(strtoint('$'+hexstr));
hexstr:='';
end;
end;
except
result:='';
end;
end;
function strtohex(was:string):string;
var zahler:integer;
begin
result:='';
for zahler:=low(was) to high(was) do
result:=result+inttohex(ord(was[zahler]),4);
end;
Function EscapeBIN(Text:string):string;
var c:Char;
z:byte;
begin
Result:=Text;
c:=EscapeChar; Result:=ReplaceStr_own(Result,c,EscapeChar+strtohex(c));
for z:=0 to 31 do begin c:=chr(z); Result:=ReplaceStr_own(Result,c,EscapeChar+strtohex(c)); end;
c:='\'; Result:=ReplaceStr_own(Result,c,EscapeChar+strtohex(c));
c:='"'; Result:=ReplaceStr_own(Result,c,EscapeChar+strtohex(c));
c:=#39; Result:=ReplaceStr_own(Result,c,EscapeChar+strtohex(c));
end;
Function unEscapeBIN(Text:string):string;
var c:Char;
z:byte;
begin
Result:=Text;
c:='\'; Result:=ReplaceStr_own(Result,EscapeChar+strtohex(c),c);
c:='"'; Result:=ReplaceStr_own(Result,EscapeChar+strtohex(c),c);
c:=#39; Result:=ReplaceStr_own(Result,EscapeChar+strtohex(c),c);
for z:=0 to 31 do begin c:=chr(z); Result:=ReplaceStr_own(Result,EscapeChar+strtohex(c),c); end;
c:=EscapeChar; Result:=ReplaceStr_own(Result,EscapeChar+strtohex(c),c);
end;
Procedure TSprache.SaveComponentenToStringlist(wohin:tstringlist;Quelle:TComponent);
Procedure AddComponent(Quelle:TComponent;Pfad:string);
var str:string;
begin
if Quelle.Name='' then exit;
str:='';
if (Quelle is TApplication) then
begin
str:='.Title='+EscapeBIN((Quelle as TApplication).Title);
end
else if (Quelle is TMenuItem) then
begin
if (Quelle as TMenuItem).Caption='-' then str:='' // Trennstrich abfangen
else str:='.Caption='+EscapeBIN((Quelle as TMenuItem).Caption);
end
else if (Quelle is TSaveDialog) then
begin
str:='.Filter='+EscapeBIN((Quelle as TSaveDialog).Filter);
end
else if (Quelle is TOpenDialog) then
begin
str:='.Filter='+EscapeBIN((Quelle as TOpenDialog).Filter);
end
else if (Quelle is TCheckbox) then
begin
str:='.Caption='+EscapeBIN((Quelle as TCheckbox).Caption);
end
else if (Quelle is TForm) then
begin
str:='.Caption='+EscapeBIN((Quelle as TForm).Caption);
end
else if (Quelle is TGroupbox) then
begin
str:='.Caption='+EscapeBIN((Quelle as TGroupbox).Caption);
end
else if (Quelle is TLabel) then
begin
str:='.Caption='+EscapeBIN((Quelle as TLabel).Caption);
end
else if (Quelle is TButton) then
begin
str:='.Caption='+EscapeBIN((Quelle as TButton).Caption);
end;
if str<>'' then wohin.Add(Pfad+str);
end;
Procedure GetComponenten(Quelle:TComponent;Pfad:string);
var i:integer;
begin
for i:=0 to Quelle.ComponentCount-1 do
if Quelle.Components[i].ComponentCount>0 then
begin
GetComponenten(Quelle.Components[i],Pfad+'.'+Quelle.Components[i].Name);
end else AddComponent(Quelle.Components[i],Pfad+'.'+Quelle.Components[i].Name);
AddComponent(Quelle,Pfad);
end;
begin
GetComponenten(Quelle,'');
end;
var NichtWeiter:boolean;
Procedure TSprache.SucheUndSetzeComponente(FPfad,wert:string;Quelle:TComponent);
Procedure CheckComponent(Quelle:TComponent;Pfad:string);
begin
if NichtWeiter=true then exit;
if Quelle.Name='' then exit;
if pos(Pfad,FPfad)<>1 then exit;
Pfad:=copy(FPfad,length(Pfad)+2,99999);
if pos('.',Pfad)>0 then exit;
NichtWeiter:=true;
if (Quelle is TApplication) then
begin
if Pfad='Title' then (Quelle as TApplication).Title:=Wert
end
else if (Quelle is TSaveDialog) then
begin
if Pfad='Filter' then (Quelle as TSaveDialog).Filter:=Wert
end
else if (Quelle is TOpenDialog) then
begin
if Pfad='Filter' then (Quelle as TOpenDialog).Filter:=Wert
end
else if (Quelle is TMenuItem) then
begin
if Pfad='Caption' then (Quelle as TMenuItem).Caption:=Wert
end
else if (Quelle is TCheckbox) then
begin
if Pfad='Caption' then (Quelle as TCheckbox).Caption:=Wert
end
else if (Quelle is TForm) then
begin
if Pfad='Caption' then (Quelle as TForm).Caption:=Wert
end
else if (Quelle is TGroupbox) then
begin
if Pfad='Caption' then (Quelle as TGroupbox).Caption:=Wert
end
else if (Quelle is TLabel) then
begin
if Pfad='Caption' then (Quelle as TLabel).Caption:=Wert
end
else if (Quelle is TButton) then
begin
if Pfad='Caption' then (Quelle as TButton).Caption:=Wert
end;
end;
Procedure GetComponenten(Quelle:TComponent;Pfad:string);
var i:integer;
begin
if NichtWeiter=true then exit;
for i:=0 to Quelle.ComponentCount-1 do
begin
if NichtWeiter=true then exit;
if Quelle.Components[i].ComponentCount>0 then
begin
GetComponenten(Quelle.Components[i],Pfad+'.'+Quelle.Components[i].Name);
end else CheckComponent(Quelle.Components[i],Pfad+'.'+Quelle.Components[i].Name);
end;
CheckComponent(Quelle,Pfad);
end;
begin
NichtWeiter:=false;
GetComponenten(Quelle,'');
end;
function TSprache.LoadLanguageFromFile(Dateiname:string):boolean;
var Datei:TStringlist;
i:integer;
str:string;
name,wert:string;
begin
result:=false;
if fileexists(Dateiname)=false then exit;
try
datei:=TStringlist.Create;
datei.LoadFromFile(Dateiname,TEncoding.Unicode);
LoadedLanguage:='';
LoadedLanguageKennung2:='';
for i:=0 to datei.Count-1 do
begin
if pos('[Language]=',datei[i])=1 then LoadedLanguage:=copy(datei[i],pos('=',datei[i])+1,999999);
if pos('[LanguageKennung]=',datei[i])=1 then LoadedLanguageKennung2:=copy(datei[i],pos('=',datei[i])+1,999999);
if pos('[Start]',datei[i])=1 then break;
end;
if (datei.Count>4) and (LoadedLanguage<>'') and (length(LoadedLanguageKennung2)=2) then result:=true;
if result=true then
begin
setlength(EintragListe,0);
for i:=i to datei.Count-1 do
begin
if pos('[End]',datei[i])=1 then break;
str:=datei[i];
if (trim(str)<>'') and (pos('=',str)>0) then
begin
name:=copy(str,1,pos('=',str)-1);
wert:=unEscapeBIN(copy(str,pos('=',str)+1,999999999));
if pos('individuell.',name)=1 then
begin
setlength(EintragListe,length(EintragListe)+1);
EintragListe[length(EintragListe)-1].Name:=Name;
EintragListe[length(EintragListe)-1].Wert:=Wert;
end else SucheUndSetzeComponente(name,wert,application);
end;
end;
end;
finally
datei.Free;
end;
end;
function TSprache.LoadLanguageFromRessource(const ResName: String):boolean;
var Datei:TStringlist;
i:integer;
str:string;
name,wert:string;
RS:TResourceStream;
begin
result:=false;
try
try
RS:=TResourceStream.Create(HInstance,ResName,RT_RCDATA);
except
RS.Free;
exit;
end;
if RS.Size<10 then exit;
datei:=TStringlist.Create;
datei.LoadFromStream(RS);
LoadedLanguage:='';
LoadedLanguageKennung2:='';
for i:=0 to datei.Count-1 do
begin
if pos('[Language]=',datei[i])=1 then LoadedLanguage:=copy(datei[i],pos('=',datei[i])+1,999999);
if pos('[LanguageKennung]=',datei[i])=1 then LoadedLanguageKennung2:=copy(datei[i],pos('=',datei[i])+1,999999);
if pos('[Start]',datei[i])=1 then break;
end;
if (datei.Count>4) and (LoadedLanguage<>'') and (length(LoadedLanguageKennung2)=2) then result:=true;
if result=true then
begin
setlength(EintragListe,0);
for i:=i to datei.Count-1 do
begin
if pos('[End]',datei[i])=1 then break;
str:=datei[i];
if (trim(str)<>'') and (pos('=',str)>0) then
begin
name:=copy(str,1,pos('=',str)-1);
wert:=unEscapeBIN(copy(str,pos('=',str)+1,999999999));
if pos('individuell.',name)=1 then
begin
setlength(EintragListe,length(EintragListe)+1);
EintragListe[length(EintragListe)-1].Name:=Name;
EintragListe[length(EintragListe)-1].Wert:=Wert;
end else SucheUndSetzeComponente(name,wert,application);
end;
end;
end;
finally
RS.Free;
datei.Free;
end;
end;
function TSprache.LoadLanguageFromString(Quelle: String):boolean;
var Datei:TStringlist;
i:integer;
str:string;
name,wert:string;
begin
result:=false;
try
if length(Quelle)<10 then exit;
datei:=TStringlist.Create;
datei.Text:=Quelle;
LoadedLanguage:='';
LoadedLanguageKennung2:='';
for i:=0 to datei.Count-1 do
begin
if pos('[Language]=',datei[i])=1 then LoadedLanguage:=copy(datei[i],pos('=',datei[i])+1,999999);
if pos('[LanguageKennung]=',datei[i])=1 then LoadedLanguageKennung2:=copy(datei[i],pos('=',datei[i])+1,999999);
if pos('[Start]',datei[i])=1 then break;
end;
if (datei.Count>4) and (LoadedLanguage<>'') and (length(LoadedLanguageKennung2)=2) then result:=true;
if result=true then
begin
setlength(EintragListe,0);
for i:=i to datei.Count-1 do
begin
if pos('[End]',datei[i])=1 then break;
str:=datei[i];
if (trim(str)<>'') and (pos('=',str)>0) then
begin
name:=copy(str,1,pos('=',str)-1);
wert:=unEscapeBIN(copy(str,pos('=',str)+1,999999999));
if pos('individuell.',name)=1 then
begin
setlength(EintragListe,length(EintragListe)+1);
EintragListe[length(EintragListe)-1].Name:=Name;
EintragListe[length(EintragListe)-1].Wert:=Wert;
end else SucheUndSetzeComponente(name,wert,application);
end;
end;
end;
finally
datei.Free;
end;
end;
procedure TSprache.SaveLanguageToString(var Ziel:string;NameK:string='';NameL:string='');
var Datei:TStringlist;
i:integer;
begin
try
Ziel:='';
if (NameK='') or (NameL='') then
begin
NameK:=LoadedLanguageKennung2;
NameL:=LoadedLanguage;
end;
datei:=TStringlist.Create;
datei.Clear;
datei.Add('[Language]='+NameL);
datei.Add('[LanguageKennung]='+NameK);
datei.Add('[Start]');
for i:=0 to length(EintragListe)-1 do datei.Add(EintragListe[i].Name+'='+EscapeBIN(EintragListe[i].Wert));
SaveComponentenToStringlist(datei,application);
datei.Add('[End]');
try Ziel:=datei.Text; except end;
finally
datei.Free;
end;
end;
procedure TSprache.SaveLanguageToFile(Dateiname:string;NameK:string='';NameL:string='');
var Datei:TStringlist;
i:integer;
begin
try
if (NameK='') or (NameL='') then
begin
NameK:=LoadedLanguageKennung2;
NameL:=LoadedLanguage;
end;
datei:=TStringlist.Create;
datei.Clear;
datei.Add('[Language]='+NameL);
datei.Add('[LanguageKennung]='+NameK);
datei.Add('[Start]');
for i:=0 to length(EintragListe)-1 do datei.Add(EintragListe[i].Name+'='+EscapeBIN(EintragListe[i].Wert));
SaveComponentenToStringlist(datei,application);
datei.Add('[End]');
try datei.SaveToFile(Dateiname,TEncoding.Unicode); except end;
finally
datei.Free;
end;
end;
function TSprache.Load_Name_And_Name2_FormFile(Dateiname: String;var NameK:string;var NameL:string):boolean;
var Datei:TStringlist;
i:integer;
str:string;
name,wert:string;
begin
result:=false;
NameK:='';
NameL:='';
if fileexists(Dateiname)=false then exit;
try
datei:=TStringlist.Create;
datei.LoadFromFile(Dateiname);
if length(datei.Text)<10 then exit;
for i:=0 to datei.Count-1 do
begin
if pos('[Language]=',datei[i])=1 then NameL:=copy(datei[i],pos('=',datei[i])+1,999999);
if pos('[LanguageKennung]=',datei[i])=1 then NameK:=copy(datei[i],pos('=',datei[i])+1,999999);
if pos('[Start]',datei[i])=1 then break;
end;
finally
if (NameK<>'') and (NameL<>'') then result:=true;
datei.Free;
end;
end;
function TSprache.GetText(Name:string):string;
var i:integer;
begin
result:='no text';
i:=GetEintragIndex(Name);
if i<>-1 then result:=EintragListe[i].Wert;
end;
function TSprache.GetTextF(Name:string; const Args: array of const):string;
var i:integer;
begin
result:='no text';
i:=GetEintragIndex(Name);
if i<>-1 then result:=format(EintragListe[i].Wert,Args);
end;
initialization
Sprache:=TSprache.Create(application);
Sprache.RegisterIndividualText('unit1.time','the current time ist %s.');
finalization
Sprache.free;
end.
// Beispielprogramm
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, SpracheUnit;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Label1: TLabel;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
showmessage(Sprache.GetTextF('unit1.time',[datetimetostr(now)]));
// GetTextF ist wie format
// GetText ist reiner Text
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
Sprache.LoadLanguageFromFile('Deutsch.lang');
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
Sprache.SaveLanguageToFile('Sprache1.lang');
end;
end.