Autor Beitrag
AXMD
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 4006
Erhaltene Danke: 7

Windows 10 64 bit
C# (Visual Studio 2019 Express)
BeitragVerfasst: Sa 19.03.05 20:04 
Moin :)!

Im Zuge der Entwicklung des [url=www.dustsigns.de.vu]DSTP2[/url] wurde eine Unit zum Ändern der Sprache während der Laufzeit des Programms fällig - und zwar am besten eine, die minimalen Aufwand seitens der Wartung benötigt.

Hier einmal der Quelltext der Unit:

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
{************************************************}
{*                                              *}
{* Dust Signs Language Manager                  *}
{* [Filename: dslangmgr, Type: Unit]            *}
{*                                              *}
{* Version: 1.00.000 State: Pre-Alpha           *}
{*                                              *}
{* Main programmer: Dust Signs                  *}
{* Programmers: Dust Signs                      *}
{*                                              *}
{* File created: 21.02.2005 19:25               *}
{* Last modified: 22.02.2005 20:57              *}
{*                                              *}
{* Changelog:                                   *}
{* ----------                                   *}
{* 21.02.2005-22.02.2005 [Dust Signs]: Start    *}
{*                                              *}
{* Used as DSTP Terminplaner Core Unit #009     *}
{*                                              *}
{************************************************}
{* 2do                                          *}
{*     None                                     *}
{************************************************}

Unit dslangmgr;

Interface

Uses
    SysUtils,
    Classes,
    Forms,
    TypInfo; //To proof existence of properties

//Internal structures for parsed file entry
Type
    TFileEntryComponent = Record
        ComponentName: String;
        Caption: String;
    End;

    TFileEntryComponent2 = Record
        Index: Integer;
        Caption: String;
    End;

Function LoadLanguageFile(AFilename: String; AForm: TForm): Integer; overload;
Function LoadLanguageFile(AFilename: StringVar AnArray: Array Of String): Integer; overload;

Implementation

//* AssignCaption
//************************************************
//* Assigns a caption to a component
//* Parameters:
//    - Form where component is placed
//    - Componentname
//    - Caption to be assigned
//* Returns: true if successful, false if an error occured

Function AssignCaption(AForm: TForm; ComponentName, ACaption: String): Boolean;
Var
    p: PPropInfo;
Begin
    Result := false;
    If AForm.FindComponent(ComponentName) = Nil Then //Does component exist?
        exit;
    Try
        //Property 'Caption' exists?
        p := GetPropInfo(AForm.FindComponent(ComponentName).ClassInfo, 'Caption');
        If (p <> NilAnd (p^.SetProc <> NilThen //Set caption
            SetStrProp(AForm.FindComponent(ComponentName), 'Caption', ACaption)
        Else
        Begin //Set 'Text' property
            p := GetPropInfo(AForm.FindComponent(ComponentName).ClassInfo, 'Text');
            If (p <> NilAnd (p^.SetProc <> NilThen //Set caption
                SetStrProp(AForm.FindComponent(ComponentName), 'Text', ACaption)
            Else
            Begin
                Result := false; //Neither 'Caption' nor 'Text' property exists
                exit;
            End;
        End;
        Result := true;
    Except
        Result := false;
    End;
End;

//* ParseFileEntry
//************************************************
//* Parses one entry of a language file
//* Parameters:
//    - Entry to parse
//    - var: parsed result
//* Returns: true if successful, false if an error occured

Function ParseFileEntry(FileEntry: StringVar Components: TFileEntryComponent): Boolean;
//Example:
//  MyTestLabel1:Hello world
//  Name:Caption
Begin
    Try
        Components.ComponentName := Copy(FileEntry, 1, Pos(':', FileEntry) - 1);
        Delete(FileEntry, 1, Pos(':', FileEntry));
        Components.Caption := FileEntry;
        Result := true;
    Except
        Result := false;
    End;
End;

//* ParseFileEntry2
//************************************************
//* Parses one entry of a language file
//* Parameters:
//    - Entry to parse
//    - var: parsed result
//* Returns: true if successful, false if an error occured

Function ParseFileEntry2(FileEntry: StringVar Components: TFileEntryComponent2): Boolean;
//Example:
//  1:Hello world
//  Index:Caption
Begin
    Try
        Components.Index := StrToInt(Copy(FileEntry, 1, Pos(':', FileEntry) - 1));
        Delete(FileEntry, 1, Pos(':', FileEntry));
        Components.Caption := FileEntry;
        Result := true;
    Except
        Result := false;
    End;
End;

//* LoadLanguageFile
//************************************************
//* Loads a language file into a form's components
//* Parameters:
//    - Path of the language file
//    - Form where components are
//* Returns: 0 if successful, -1 if unexpected error, else: error count

Function LoadLanguageFile(AFilename: String; AForm: TForm): Integer; Overload;
Var
    list: TStringList; //Temporary list
    i: Integer; //Loop dummy
    components: TFileEntryComponent; //Temp: parsed components of current entry
Begin
    Result := 0;
    Try
        Try
            list := TStringList.Create;
            list.LoadFromFile(AFilename);
            For i := 0 To list.Count - 1 Do
            Begin
                If list[i] = '' Then
                    continue; //Skip empty lines
                If Copy(list[i], 11) = '!' Then
                    continue; //Skip comment lines (which start with "!")
                If Not (ParseFileEntry(list[i], components)) Then
                Begin
                    Inc(Result); //One more error
                    continue;
                End;
                If Not (AssignCaption(AForm, components.ComponentName, components.Caption)) Then
                Begin
                    Inc(Result); //One more error
                    continue;
                End;
            End;
        Except
            Result := -1;
        End;
    Finally
        list.Free;
    End;
End;

//* LoadLanguageFile
//************************************************
//* Loads a language file into an array
//* Parameters:
//    - Path of the language file
//    - Destination array
//* Returns: 0 if successful, -1 if unexpected error, else: error count

Function LoadLanguageFile(AFilename: StringVar AnArray: Array Of String): Integer; Overload;
Var
    list: TStringList; //Temporary list
    i: Integer; //Loop dummy
    components: TFileEntryComponent2; //Temp: parsed components of current entry
Begin
    Result := 0;
    Try
        Try
            list := TStringList.Create;
            list.LoadFromFile(AFilename);
            For i := 0 To list.Count - 1 Do
            Begin
                If list[i] = '' Then
                    continue; //Skip empty lines
                If Copy(list[i], 11) = '!' Then
                    continue; //Skip comment lines (which start with "!")
                If Not (ParseFileEntry2(list[i], components)) Then
                Begin
                    Inc(Result); //One more error
                    continue;
                End;
                AnArray[components.Index] := components.Caption;
            End;
        Except
            Result := -1;
        End;
    Finally
        list.Free;
    End;
End;

End.


Zur Verwendung hier zwei Beispiele:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
//* LoadLanguageFiles
//************************************************
//* Loads mainform language files
//* Parameters:
//    - Language to load (p.e. 'eng' for english)

Procedure LoadLanguageFiles(Language: String);
Var
    TempErr: Integer; //Error variable (temp);
Begin
    With MainForm Do
    Begin
        TempErr := LoadLanguageFile('lang\mainform.' + Language + '.lang', MainForm); //Form
        If TempErr <> 0 Then
            ShowMessage('An error occured while loading language files (Code: ' + IntToStr(TempErr) + ')');
        TempErr := LoadLanguageFile('lang\messages.' + Language + '.lang', MessageArray); //Messages
        If TempErr <> 0 Then
            ShowMessage('An error occured while loading language files (Code: ' + IntToStr(TempErr) + ')');
    End;
End;


wobei MessageArray wiefolgt deklariert ist: MessageArray: Array[0..M_COUNT - 1Of String//Messages (language-specific)

Natürlich müsst ihr dazu Languagefiles erstellen. Hier zwei Beispiele:

ausblenden Quelltext
1:
2:
3:
4:
!Hallo, ich bin ein Kommentar ;)

Label1:Hello world
Label2:Ich bin Label 2


Hier z.B. zwei Label; das hinter dem Doppelpunkt ist der Text, der zugewiesen wird.

ausblenden Quelltext
1:
2:
3:
4:
!Beispiel für ein Array

0:Ich bin Item #0
1:Hallo Welt :)


Hier das selbe, nur, dass die Zahlen den jeweiligen Indizes im Array entsprechen.

Die Unit ist Open Source, der Header muss allerdings bleiben wie er ist; eine Erwähnung in irgendeiner Form wäre nett, wenn jemand die Unit verwendet :)

AXMD

//EDIT: Beispiele zwecks Verständlichkeit verbessert ;)
//EDIT2: Immer diese Tippfehler ^^


Zuletzt bearbeitet von AXMD am Sa 19.03.05 21:18, insgesamt 2-mal bearbeitet
Christian S.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 20451
Erhaltene Danke: 2264

Win 10
C# (VS 2019)
BeitragVerfasst: Sa 19.03.05 20:25 
Irgendwie stehe ich gerade ein wenig auf dem Schlauch, wie ich die Unit denn nun benutze, will heißen, wie bekomme ich die Texte in die Komponenten? :gruebel:

_________________
Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".
Karlson
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 2088



BeitragVerfasst: Sa 19.03.05 20:50 
Mit der Languagefile.
Ich hab kurz rübergeschaut, und as far as i see (gibts die abkürzung AFAIS schon? :lol: ) liest er einfach alle captions der componenten aus, überprüft ob sie in der Languagefileliste sind und tauscht dann ggf. die Caption aus.
AXMD Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 4006
Erhaltene Danke: 7

Windows 10 64 bit
C# (Visual Studio 2019 Express)
BeitragVerfasst: Sa 19.03.05 20:51 
Das macht die Unit für dich. Beispiel: du hast Label2 auf deiner Form, das den Text "Englisch" bekommen soll.

Hier mal die Datei (ich nenn sie mal bla.lang) dafür:

ausblenden Quelltext
1:
Label2:Englisch					


Und dann einfach laden (z.B.):

ausblenden Delphi-Quelltext
1:
TempErr := LoadLanguageFile('bla.lang', Form1 {deine Form});					


So einfach geht's ;)

AXMD
AXMD Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 4006
Erhaltene Danke: 7

Windows 10 64 bit
C# (Visual Studio 2019 Express)
BeitragVerfasst: Sa 19.03.05 20:59 
user profile iconKarlson hat folgendes geschrieben:
Mit der Languagefile.
Ich hab kurz rübergeschaut, und as far as i see (gibts die abkürzung AFAIS schon? :lol: ) liest er einfach alle captions der componenten aus, überprüft ob sie in der Languagefileliste sind und tauscht dann ggf. die Caption aus.


So änhlich ;): ich gehe die Datei zeilenweise durch, überprüfe, ob die Komponente existiert und eine der Eigenschaften Caption oder Text hat; ist das der Fall, wird der jeweiligen Eigenschaft der Text hinter dem Doppelpunkt zugeweisen ;)

AXMD
retnyg
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2754

SNES, GB, GBA, CPC, A500, 486/66, P4/3.0HT: NintendOS, AmigaOS, DoS
Delphi 5, Delphi 7
BeitragVerfasst: Sa 19.03.05 21:07 
aha, und wie ginge das nun wenn ich ein file für englisch, deutsch, französisch und italienisch habe ?
dann wirds wohl nicht mehr funktionieren. wie wäre es wenn du über den komponentennamen (edit1) vorgehst ?

_________________
es gibt leute, die sind genetisch nicht zum programmieren geschaffen.
in der regel haben diese leute die regel...
AXMD Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 4006
Erhaltene Danke: 7

Windows 10 64 bit
C# (Visual Studio 2019 Express)
BeitragVerfasst: Sa 19.03.05 21:08 
user profile iconretnyg hat folgendes geschrieben:
aha, und wie ginge das nun wenn ich ein file für englisch, deutsch, französisch und italienisch habe ?

Einfach je nach Einstellung ein anderes File laden ;) (siehe erster Beitrag). Nenn die Files z.B. englisch.lang und deutsch.lang und mach if Programmsprache = 'Deutsch' then LoadLanguageFile('\deutsch.lang', Form1);
user profile iconretnyg hat folgendes geschrieben:
wie wäre es wenn du über den komponentennamen (edit1) vorgehst ?

Tu ich doch :gruebel:

AXMD
retnyg
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2754

SNES, GB, GBA, CPC, A500, 486/66, P4/3.0HT: NintendOS, AmigaOS, DoS
Delphi 5, Delphi 7
BeitragVerfasst: Sa 19.03.05 21:12 
bei deinem programm ist also z.b. __Start der name eines controls ?

_________________
es gibt leute, die sind genetisch nicht zum programmieren geschaffen.
in der regel haben diese leute die regel...
AXMD Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 4006
Erhaltene Danke: 7

Windows 10 64 bit
C# (Visual Studio 2019 Express)
BeitragVerfasst: Sa 19.03.05 21:14 
user profile iconretnyg hat folgendes geschrieben:
bei deinem programm ist also z.b. __Start der name eines controls ?


So ist es; Benennung ist bei mir nach Menütiefe; zwei Unterstriche bedeuten also zweite Ebene, sprich kein Hauptmenüpunkt, sondern eins weiter unten:

ausblenden Quelltext
1:
2:
Datei
  Neu <- Das wäre z.B. __Neu


Alles klar ;)

AXMD

//EDIT: ersten Beitrag bearbeitet um Verständlichkeit zu erhöhen ;) Hab statt meinen MenuItems Labels genommen ;)
bayou
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 88


D6 PE
BeitragVerfasst: Mo 15.12.08 19:04 
Hi,
das Thema ist zwar schon etwas älter aber genau was ich gesucht habe.
Dein Programm funktioniert super, leider habe ich ein Problem.
Wie kann ich den Items von RadioGroup umbenenen?
Gibt es eine Möglichkeit dein Programm so zu erweitern das auch diese Umbenannt werden?

Wäre für eine Antwort echt super Dankbar.
Jürgen
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mo 15.12.08 19:13 
Im Grunde müsste (wenn ich das beim schnellen Überfliegen richtig gesehen habe) nur in AssignCaption neben den Properties auch geprüft werden, ob es eine RadioGroup ist.
Wenn ja, müsstest du aus dem Wert aus der Languagedatei (ACaption) irgendwie mehrere Items auslesen (z.B. getrennt durch Semikola) und zuweisen.
AXMD Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 4006
Erhaltene Danke: 7

Windows 10 64 bit
C# (Visual Studio 2019 Express)
BeitragVerfasst: Mo 15.12.08 19:14 
Hallo!

jaenicke hat eigentlich schon das vorweggenommen, was ich auch gerade sagen wollte ;)

AXMD
bayou
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 88


D6 PE
BeitragVerfasst: Mo 15.12.08 19:19 
könntet Ihr mir dabei helfen?
Bin immer noch Anfänger, komme zu selten dazu mit Delphi zu arbeiten.
Ist schon 1 Jahr her wo ich das letzte mal was gemacht habe.

Vielen Dank im vorraus
Jürgen


Zuletzt bearbeitet von bayou am Mo 15.12.08 21:53, insgesamt 1-mal bearbeitet
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mo 15.12.08 19:29 
Hmm, ganz schnell hingeschrieben (geht sicher auch besser):
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
Function AssignCaption(AForm: TForm; ComponentName, ACaption: String): Boolean;
Var
    p: PPropInfo;
    i: Integer;
    RadioGroup: TRadioGroup;
Begin
    Result := false;
    If AForm.FindComponent(ComponentName) = Nil Then //Does component exist?
        exit;
    if AForm.FindComponent(ComponentName) is TRadioGroup then
    begin
        RadioGroup := TRadioGroup(AForm.FindComponent(ComponentName));
        for i := 0 to RadioGroup.Items.Count - 1 do
        begin
            if Pos(';', ACaption) = 0 then
              RadioGroup.Items[i] := ACaption
            else
              RadioGroup.Items[i] := Copy(ACaption, 1, Pos(';', ACaption) - 1);
            Delete(ACaption, 1, Pos(';', ACaption));
        end;
        Result := True;
        Exit;
    end;
    ... usw.
Ungetestet und es ginge vermutlich auch performanter (z.B. könnte man in der gesamten Routine eigentlich das Ergebnis von FindComponent zwischenspeichern ;-)), aber es sollte seinen Zweck erfüllen. Die Items werden getrennt durch Semikola in der Datei erwartet.
bayou
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 88


D6 PE
BeitragVerfasst: Mo 15.12.08 22:30 
1000 Dank, es funktioniert super.
Ich werde jetzt erst mal ein wenig Testen um das ganze richtig zu verstehen.

nochmals Danke für Eure Hilfe
bayou
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 88


D6 PE
BeitragVerfasst: Di 16.12.08 01:14 
Könntet Ihr mir noch bitte einen Tip geben wie ich es mit den Caption der Form händeln muss?
Bin seit einiger Zeit dran und bekomme es nicht hin.

Vielen Dank
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Di 16.12.08 01:34 
Nun ja, AForm.Name ist ja der Name deiner Form, wenn der Parameter ComponentName damit übereinstimmt, dann weise den Wert AForm.Caption zu.
bayou
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 88


D6 PE
BeitragVerfasst: Di 16.12.08 08:09 
OK, danke, ich versuche es mal.
ssb-blume
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 375
Erhaltene Danke: 7

XP, W7, W8
Deutschland
BeitragVerfasst: Di 16.12.08 09:32 
Frage:

Was macht der Anwender mit dem Programm, wenn er die Sprache wechseln will und
der Programmierer diese Sprache (noch) nicht berücksichtigt hat?

_________________
Brain: an apparatus with which we think we think.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Di 16.12.08 09:38 
Das kommt ja darauf an wie du das dann programmierst. Du kannst ja alle Sprachdateien suchen, die vorhanden sind, so dass man auch eigene erstellen kann, du kannst die theoretisch aber natürlich auch fest einprogrammieren.

Aber das hat mit der eigentlichen Unit ja nix zu tun, das liegt ja an deiner Programmierung, wenn du die Unit verwendest. :nixweiss: