Lokalisierung: Unterschied zwischen den Versionen
(Erste Zwischenspeicherung, Unvollständig!) |
|||
Zeile 1: | Zeile 1: | ||
= Lokalisierung = | = Lokalisierung = | ||
== Allgemein == | == Allgemein == | ||
− | Als | + | Als Lokalisierung bezeichnet man den Vorgang eine Software in verschiedene Sprachen zu übersetzen. |
+ | |||
+ | Besonders schwer ist die Übersetzung in Sprachen mit komplett anderem Zeichensatz(Kyrillisch, Chinesisch, Japanisch etc.), insbesondere wenn diese Multibyte-Schriften benötigen. So weit wird jedoch kaum ein Hobbyentwickler gehen, der aus keinem dieser Länder stammt, weshalbt ich darauf nicht eingehen werde. | ||
== Anforderungen == | == Anforderungen == | ||
Zeile 7: | Zeile 9: | ||
* Software kann auch nach dem Übersetzen noch verändert werden | * Software kann auch nach dem Übersetzen noch verändert werden | ||
* Auch bei anderem Satzbau noch verwendbar | * Auch bei anderem Satzbau noch verwendbar | ||
+ | |||
== Ansätze == | == Ansätze == | ||
* Formularresourcen (Borland)<br>Nachteil: Es ist schwer das Formular nach dem Übersetzen noch anzupassen und es wird bei sonstigen Texten keine Unterstüzung geboten | * Formularresourcen (Borland)<br>Nachteil: Es ist schwer das Formular nach dem Übersetzen noch anzupassen und es wird bei sonstigen Texten keine Unterstüzung geboten | ||
* Programm das im Quelltext alle Strings suchen unt ersetzen kann<br>Nachteil: Es gibt viele nicht zu übersetzende Strings, Programm muss für jede Spreche kompiliert werden | * Programm das im Quelltext alle Strings suchen unt ersetzen kann<br>Nachteil: Es gibt viele nicht zu übersetzende Strings, Programm muss für jede Spreche kompiliert werden | ||
* Übersetzungsfunktion:<br>Vorteil: Kann überall im Quelltext eingesetzt werden<br>Nachteil: Jeder zu übersetzende String muss an diese Funktion übergeben werden | * Übersetzungsfunktion:<br>Vorteil: Kann überall im Quelltext eingesetzt werden<br>Nachteil: Jeder zu übersetzende String muss an diese Funktion übergeben werden | ||
+ | |||
== Einfaches Übersetzungssystem == | == Einfaches Übersetzungssystem == | ||
Hier werde ich den 3. Ansatz implementieren: eine Übersetzungsfunktion | Hier werde ich den 3. Ansatz implementieren: eine Übersetzungsfunktion | ||
+ | Ich verwende zur Übersetzung Stringlisten der Form | ||
+ | Bezeichner=Wert | ||
+ | sie sind also ähnlich wie Ini-Dateien aufgebaut, jedoch ohne Sektionen | ||
+ | Sie werden mit Hilfe der Funktion LoadLanguage geladen | ||
+ | <pascal> | ||
+ | procedure LoadLanguage(const Lang:String); | ||
+ | begin | ||
+ | CurLang:=lowercase(Lang); | ||
+ | langdata.loadfromfile(changefileext(paramstr(0),'.'+CurLang)); | ||
+ | end; | ||
+ | </pascal> | ||
+ | Diese Funktion macht nicht mehr als die momentane Sprache in einer Variable zu speichern und die Übersetzungsdatei zu laden. Der Dateiname wird bei mir über changefileext(paramstr(0),'.'+CurLang) festgelegt, das solltet ihr jedoch an euer Projekt anpassen. | ||
+ | |||
+ | Die eigentliche Übersetzung wird von der Funktion Translate durchgeführt. | ||
+ | (Ich bevorzuge aussagekräftige Funktionsnamen, besonders im C++ Bereich habe ich auch schon einen einfachen Unterstrich als Namen für so eine Funktion gesehen. Das ist auch in Delphi möglich, falls ihr so tippfaul seid) | ||
+ | <pascal> | ||
+ | Function Translate(const Name: string): string;overload; | ||
+ | var i:integer; | ||
+ | begin | ||
+ | langdata.find(Name+'=',i); | ||
+ | if (i<langdata.count)and(copy(langdata[i],1,length(Name)+1)=Name+'=') | ||
+ | then result:=Copy(langdata[i], Length(Name) + 2, MaxInt) | ||
+ | else raise exception.create('String not translated: "'+Name+'"'); | ||
+ | result:=stringreplace(result,'\r',#13,[rfReplaceAll]); | ||
+ | result:=stringreplace(result,'\n',#10,[rfReplaceAll]); | ||
+ | result:=stringreplace(result,'\\','\',[rfReplaceAll]); | ||
+ | end; | ||
+ | </pascal> | ||
+ | Hier noch der Gesamte Quelltext der Übersetzungsunit: | ||
<pascal> | <pascal> | ||
unit Translator; | unit Translator; |
Version vom 26. Juli 2006, 10:26 Uhr
Inhaltsverzeichnis
Lokalisierung
Allgemein
Als Lokalisierung bezeichnet man den Vorgang eine Software in verschiedene Sprachen zu übersetzen.
Besonders schwer ist die Übersetzung in Sprachen mit komplett anderem Zeichensatz(Kyrillisch, Chinesisch, Japanisch etc.), insbesondere wenn diese Multibyte-Schriften benötigen. So weit wird jedoch kaum ein Hobbyentwickler gehen, der aus keinem dieser Länder stammt, weshalbt ich darauf nicht eingehen werde.
Anforderungen
- Einfach einzusetzen
- Software kann auch nach dem Übersetzen noch verändert werden
- Auch bei anderem Satzbau noch verwendbar
Ansätze
- Formularresourcen (Borland)
Nachteil: Es ist schwer das Formular nach dem Übersetzen noch anzupassen und es wird bei sonstigen Texten keine Unterstüzung geboten - Programm das im Quelltext alle Strings suchen unt ersetzen kann
Nachteil: Es gibt viele nicht zu übersetzende Strings, Programm muss für jede Spreche kompiliert werden - Übersetzungsfunktion:
Vorteil: Kann überall im Quelltext eingesetzt werden
Nachteil: Jeder zu übersetzende String muss an diese Funktion übergeben werden
Einfaches Übersetzungssystem
Hier werde ich den 3. Ansatz implementieren: eine Übersetzungsfunktion Ich verwende zur Übersetzung Stringlisten der Form
Bezeichner=Wert
sie sind also ähnlich wie Ini-Dateien aufgebaut, jedoch ohne Sektionen Sie werden mit Hilfe der Funktion LoadLanguage geladen
procedure LoadLanguage(const Lang:String); begin CurLang:=lowercase(Lang); langdata.loadfromfile(changefileext(paramstr(0),'.'+CurLang)); end;
Diese Funktion macht nicht mehr als die momentane Sprache in einer Variable zu speichern und die Übersetzungsdatei zu laden. Der Dateiname wird bei mir über changefileext(paramstr(0),'.'+CurLang) festgelegt, das solltet ihr jedoch an euer Projekt anpassen.
Die eigentliche Übersetzung wird von der Funktion Translate durchgeführt. (Ich bevorzuge aussagekräftige Funktionsnamen, besonders im C++ Bereich habe ich auch schon einen einfachen Unterstrich als Namen für so eine Funktion gesehen. Das ist auch in Delphi möglich, falls ihr so tippfaul seid)
Function Translate(const Name: string): string;overload; var i:integer; begin langdata.find(Name+'=',i); if (i<langdata.count)and(copy(langdata[i],1,length(Name)+1)=Name+'=') then result:=Copy(langdata[i], Length(Name) + 2, MaxInt) else raise exception.create('String not translated: "'+Name+'"'); result:=stringreplace(result,'\r',#13,[rfReplaceAll]); result:=stringreplace(result,'\n',#10,[rfReplaceAll]); result:=stringreplace(result,'\\','\',[rfReplaceAll]); end;
Hier noch der Gesamte Quelltext der Übersetzungsunit:
unit Translator; interface Function Translate(const Name: string; const Args: array of const): string;overload; Function Translate(const Name: string): string;overload; procedure LoadLanguage(const Lang:String); function CurrentLanguage:String; implementation uses sysutils,classes; var langdata:TStringlist; CurLang:String; Function Translate(const Name: string; const Args: array of const): string;overload; begin result:=format(translate(Name),Args); end; Function Translate(const Name: string): string;overload; var i:integer; begin langdata.find(Name+'=',i); if (i<langdata.count)and(copy(langdata[i],1,length(Name)+1)=Name+'=') then result:=Copy(langdata[i], Length(Name) + 2, MaxInt) else raise exception.create('String not translated: "'+Name+'"'); result:=stringreplace(result,'\r',#13,[rfReplaceAll]); result:=stringreplace(result,'\n',#10,[rfReplaceAll]); result:=stringreplace(result,'\\','\',[rfReplaceAll]); end; procedure LoadLanguage(const Lang:String); begin CurLang:=lowercase(Lang); langdata.loadfromfile(changefileext(paramstr(0),'.'+CurLang)); end; function CurrentLanguage:String; begin result:=CurLang; end; initialization langdata:=TStringlist.create; langdata.sorted:=true; finalization langdata.free; end.
unit TranslatorVCL; interface uses classes,translator,controls,menus,actnlist,sysutils,comctrls,stdctrls; Procedure Translate(const Component:TComponent;Path:String='');overload; procedure CreateTranslationTable(const Component:TComponent;const Filename:String); implementation Type TMyControl=class(TControl); Procedure Translate(const Component:TComponent;Path:String='');overload; var i:integer; begin //Pfad anpassen if Path='' then Path:=Component.Name else Path:=Path+'.'+Component.Name; //Eigenschaften übersetzen if (Component is TControl)and(TMyControl(Component).Caption<>'')then TMyControl(Component).Caption:=Translate(Path+'.Caption'); if (Component is TControl)and(TControl(Component).Hint<>'')then TMyControl(Component).Hint:=Translate(Path+'.Hint'); if (Component is TMenuItem)and(TMenuItem(Component).Caption<>'')and(TMenuItem(Component).Action=nil)then TMenuItem(Component).Caption:=Translate(Path+'.Caption'); if (Component is TMenuItem)and(TMenuItem(Component).Hint<>'')and(TMenuItem(Component).Action=nil)then TMenuItem(Component).Hint:=Translate(Path+'.Hint'); if (Component is TCustomAction)and(TCustomAction(Component).Caption<>'')then TCustomAction(Component).Caption:=Translate(Path+'.Caption'); if (Component is TCustomAction)and(TCustomAction(Component).Hint<>'')then TCustomAction(Component).Hint:=Translate(Path+'.Hint'); if (Component is TTabControl)and(TTabControl(Component).Tabs.text<>'')then TTabControl(Component).Tabs.text:=Translate(Path+'.Tabs'); if (Component is TCustomComboBox)and(TCustomComboBox(Component).items.text<>'')then TCustomComboBox(Component).items.text:=Translate(Path+'.Items'); if (Component is TCustomListBox)and(TCustomListBox(Component).items.text<>'')then TCustomListBox(Component).items.text:=Translate(Path+'.Items'); //Unterkomponenten übersetzen for i:=0 to Component.ComponentCount-1do Translate(Component.Components[i],Path); end; procedure CreateTranslationTable(const Component:TComponent;const Filename:String); var List:TStringlist; function Escape(const S:String):String; begin result:=S; result:=stringreplace(result,'\','\\',[rfReplaceAll]); result:=stringreplace(result,#13,'\r',[rfReplaceAll]); result:=stringreplace(result,#10,'\n',[rfReplaceAll]); end; procedure AddComponent(const Component:TComponent;Path:String); var i:integer; begin //Pfad anpassen if Path='' then Path:=Component.Name else Path:=Path+'.'+Component.Name; //Eigenschaften speichern if (Component is TControl)and(TMyControl(Component).Caption<>'')then List.add(Path+'.Caption='+Escape(TMyControl(Component).Caption)); if (Component is TControl)and(TControl(Component).Hint<>'')then List.add(Path+'.Hint='+Escape(TMyControl(Component).Hint)); if (Component is TMenuItem)and(TMenuItem(Component).Caption<>'')and(TMenuItem(Component).Action=nil)then List.add(Path+'.Caption='+Escape(TMenuItem(Component).Caption)); if (Component is TMenuItem)and(TMenuItem(Component).Hint<>'')and(TMenuItem(Component).Action=nil)then List.add(Path+'.Hint='+Escape(TMenuItem(Component).Hint)); if (Component is TCustomAction)and(TCustomAction(Component).Caption<>'')then List.add(Path+'.Caption='+Escape(TCustomAction(Component).Caption)); if (Component is TCustomAction)and(TCustomAction(Component).Hint<>'')then List.add(Path+'.Hint='+Escape(TCustomAction(Component).Hint)); if (Component is TTabControl)and(TTabControl(Component).Tabs.text<>'')then List.add(Path+'.Tabs='+Escape(TTabControl(Component).Tabs.text)); if (Component is TCustomComboBox)and(TCustomComboBox(Component).Items.text<>'')then List.add(Path+'.Items='+Escape(TCustomComboBox(Component).Items.text)); if (Component is TCustomListBox)and(TCustomListBox(Component).Items.text<>'')then List.add(Path+'.Items='+Escape(TCustomListBox(Component).Items.text)); //Unterkomponenten speichern for i:=0 to Component.ComponentCount-1do AddComponent(Component.Components[i],Path); end; begin List:=TStringlist.create; try AddComponent(Component,''); List.SaveToFile(Filename); finally List.free; end; end; end.