|
Registriert seit: 11. Okt 2003 Ort: Elbflorenz 44.340 Beiträge Delphi 12 Athens |
#1
Im Zuge einer mehrsprachigen Unterstütung
und da ![]() hab ich mir, nach mehreren Anläufen, folgenden Code einfallen lassen. Die verschiedenen Sprachen liegen dabei alle im selben Ressourcenbereich. Man braucht dafür nur meine Funktion und die nötigen Resourcen in der Anwendung (über .hModule kann auch eine externe Resource angegeben, z.B. eine DLL). FindResourceLang sucht dann die vorhandenen Sprachen und bietet über Result den Index zur automatischen Vorauswahl. Im zurückgegebenem Array stehen dann alle gefundenen Sprachresourcen, wo man sich nach Lust und Laune oder über den Index(Result) bediehnen kann. Code 1:
Delphi-Quellcode:
wird z.B. so verwendet;
Type TResTableString = packed Record
Len: Word; Text: packed Array[0..0] of WideChar; End; PResTableString = ^TResTableString; TEnumResRec = packed Record hModule: THandle; ResType: PWideChar; ResName: PWideChar; LangIDs: packed Array of packed Record LangID: LANGID; Case Byte of 0: (P: Pointer; Len: Integer); 1: (Text: PResTableString); // RT_STRING End; End; PEnumResRec = ^TEnumResRec; Const DefaultLang = LANG_ENGLISH or (SUBLANG_ENGLISH_US shl 10); Var GUILang: LANGID = LANG_NEUTRAL or (SUBLANG_NEUTRAL shl 10); ResErrorStr: WideString; Function FindResourceLang(Var LangResList: TEnumResRec): Integer; Function EnumResLangProcW(hModule: THandle; lpszType, lpszName: PWideChar; wIDLanguage: LANGID; lParam: PEnumResRec): LongBool; StdCall; Var hRes: HRSRC; hResLoad: THandle; ResP: PResTableString; ResL, i, i2: Integer; Begin Result := True; If lpszType = PWideChar(RT_STRING) Then Begin hRes := FindResourceExW(lParam^.hModule, lParam^.ResType, MakeIntResourceW(Integer(lParam^.ResName) shr 4 + 1), wIDLanguage); hResLoad := LoadResource(lParam^.hModule, hRes); ResL := SizeOfResource(lParam^.hModule, hRes); ResP := LockResource(hResLoad); If ResP = nil Then Exit; i := ResL; For i2 := 1 to Integer(lParam^.ResName) and $0F do Begin Dec(i, (ResP^.Len + 1) * 2); Inc(PWideChar(ResP), ResP^.Len + 1); If i <= 0 Then Exit; End; If (ResP^.Len = 0) or ((ResP^.Len + 1) * 2 > i) Then Exit; ResL := ResP^.Len + 2; End Else Begin hRes := FindResourceExW(lParam^.hModule, lParam^.ResType, lParam^.ResName, wIDLanguage); hResLoad := LoadResource(lParam^.hModule, hRes); ResL := SizeOfResource(lParam^.hModule, hRes); ResP := LockResource(hResLoad); End; i := Length(lParam^.LangIDs); SetLength(lParam^.LangIDs, i + 1); lParam^.LangIDs[i].LangID := wIDLanguage; lParam^.LangIDs[i].P := ResP; lParam^.LangIDs[i].Len := ResL; End; Var ResName: PWideChar; LangX, MaskX: LANGID; i, i2: Integer; S, S2: WideString; Begin Result := -1; LangResList.LangIDs := nil; If (Cardinal(LangResList.ResName) <= $0000FFFF) and (LangResList.ResType = PWideChar(RT_STRING)) Then ResName := MakeIntResourceW(Integer(LangResList.ResName) shr 4 + 1) Else ResName := LangResList.ResName; EnumResourceLanguagesW(LangResList.hModule, LangResList.ResType, ResName, @EnumResLangProcW, Integer(@LangResList)); If LangResList.LangIDs = nil Then Begin If Cardinal(LangResList.ResName) < $0000FFFF Then S := IntToStrT(Integer(LangResList.ResName)) Else S := WideString('''') + LangResList.ResName + WideString(''''); Case Integer(LangResList.ResType) of Integer(RT_STRING): Begin Result := 0; ResErrorStr := #0'[Resource ' + S + ' (' + 'RT_STRING' + ') not found]'; ResErrorStr[1] := WideChar(Length(ResErrorStr) - 1); SetLength(LangResList.LangIDs, 1); LangResList.LangIDs[0].LangID := LANG_NEUTRAL or (SUBLANG_NEUTRAL shl 10); LangResList.LangIDs[0].Text := @ResErrorStr[1]; LangResList.LangIDs[0].Len := Length(ResErrorStr) - 1; Exit; End; Integer(nil): S2 := WideString('''') + WideString(''''); Integer(RT_CURSOR): S2 := 'RT_CURSOR'; Integer(RT_BITMAP): S2 := 'RT_BITMAP'; Integer(RT_ICON): S2 := 'RT_ICON'; Integer(RT_MENU): S2 := 'RT_MENU'; Integer(RT_DIALOG): S2 := 'RT_DIALOG'; Integer(RT_FONTDIR): S2 := 'RT_FONTDIR'; Integer(RT_FONT): S2 := 'RT_FONT'; Integer(RT_ACCELERATOR): S2 := 'RT_ACCELERATOR'; Integer(RT_RCDATA): S2 := 'RT_RCDATA'; Integer(RT_MESSAGETABLE): S2 := 'RT_MESSAGETABLE'; 12..$FFFF: S2 := IntToStrT(Integer(LangResList.ResType)); Else S2 := WideString('''') + LangResList.ResType + WideString(''''); End; Exception(888, ['Resource ' + S + ' (type ' + S2 + ') not found.']); End; MaskX := $FFFF; For i2 := 0 to 9 do Begin Case i2 of 0, 4: If GUILang = LANG_NEUTRAL or (SUBLANG_NEUTRAL shl 10) Then Continue Else LangX := GUILang; 1, 5: LangX := Word(GetThreadLocale); 2, 6: LangX := GetUserDefaultLangID; 3, 7: LangX := GetSystemDefaultLangID; 8: LangX := DefaultLang; Else LangX := LANG_NEUTRAL or (SUBLANG_NEUTRAL shl 10); End; If (i2 = 4) or (LangX and not $03FF = 0) Then MaskX := $03FF; For i := High(LangResList.LangIDs) downto 0 do If LangResList.LangIDs[i].LangID and MaskX = LangX and MaskX Then Result := i; If Result >= 0 Then Break; End; If Result < 0 Then Result := 0; End;
Delphi-Quellcode:
Var LangResList: TEnumResRec;
Language: Integer; StrBuffer: Array[0..1023] of WideChar; i: Integer; LangResList.hModule := HInstance; LangResList.ResType := PWideChar(RT_STRING); LangResList.ResName := {MakeIntResourceW(MsgID)}; Language := FindResourceLang(LangResList); i := Min(Integer(LangResList.LangIDs[Language].Text^.Len * 2), SizeOf(StrBuffer) - 2); CopyMemory(@StrBuffer, @LangResList.LangIDs[Language].Text^.Text, i); StrBuffer[i div 2] := #0;
Delphi-Quellcode:
Code 2 (ohne Exception):
Var LangResList: TEnumResRec;
Language: Integer; LangResList.hModule := HInstance; LangResList.ResType := PWideChar(RT_DIALOG); LangResList.ResName := {MakeIntResourceW(DlgID)}; Language := FindResourceLang(LangResList); Result := DialogBoxIndirectParamW(HInstance, PDlgTemplate(LangResList.LangIDs[Language].P)^, ...);
Delphi-Quellcode:
Type TResTableString = packed Record
Len: Word; Text: packed Array[0..0] of WideChar; End; PResTableString = ^TResTableString; TEnumResRec = packed Record hModule: THandle; ResType: PWideChar; ResName: PWideChar; LangIDs: packed Array of packed Record LangID: LANGID; Case Byte of 0: (P: Pointer; Len: Integer); 1: (Text: PResTableString); // RT_STRING End; End; PEnumResRec = ^TEnumResRec; Const DefaultLang = LANG_ENGLISH or (SUBLANG_ENGLISH_US shl 10); Var GUILang: LANGID = LANG_NEUTRAL or (SUBLANG_NEUTRAL shl 10); Function FindResourceLang(Var LangResList: TEnumResRec): Integer; Function EnumResLangProcW(hModule: THandle; lpszType, lpszName: PWideChar; wIDLanguage: LANGID; lParam: PEnumResRec): LongBool; StdCall; Var hRes: HRSRC; hResLoad: THandle; ResP: PResTableString; ResL, i, i2: Integer; Begin Result := True; If lpszType = PWideChar(RT_STRING) Then Begin hRes := FindResourceExW(lParam^.hModule, lParam^.ResType, MakeIntResourceW(Integer(lParam^.ResName) shr 4 + 1), wIDLanguage); hResLoad := LoadResource(lParam^.hModule, hRes); ResL := SizeOfResource(lParam^.hModule, hRes); ResP := LockResource(hResLoad); If ResP = nil Then Exit; i := ResL; For i2 := 1 to Integer(lParam^.ResName) and $0F do Begin Dec(i, (ResP^.Len + 1) * 2); Inc(PWideChar(ResP), ResP^.Len + 1); If i <= 0 Then Exit; End; If (ResP^.Len = 0) or ((ResP^.Len + 1) * 2 > i) Then Exit; ResL := ResP^.Len + 2; End Else Begin hRes := FindResourceExW(lParam^.hModule, lParam^.ResType, lParam^.ResName, wIDLanguage); hResLoad := LoadResource(lParam^.hModule, hRes); ResL := SizeOfResource(lParam^.hModule, hRes); ResP := LockResource(hResLoad); End; i := Length(lParam^.LangIDs); SetLength(lParam^.LangIDs, i + 1); lParam^.LangIDs[i].LangID := wIDLanguage; lParam^.LangIDs[i].P := ResP; lParam^.LangIDs[i].Len := ResL; End; Var ResName: PWideChar; LangX, MaskX: LANGID; i, i2: Integer; Begin Result := -1; LangResList.LangIDs := nil; If (Cardinal(LangResList.ResName) <= $0000FFFF) and (LangResList.ResType = PWideChar(RT_STRING)) Then ResName := MakeIntResourceW(Integer(LangResList.ResName) shr 4 + 1) Else ResName := LangResList.ResName; EnumResourceLanguagesW(LangResList.hModule, LangResList.ResType, ResName, @EnumResLangProcW, Integer(@LangResList)); If LangResList.LangIDs = nil Then Exit; MaskX := $FFFF; For i2 := 0 to 9 do Begin Case i2 of 0, 4: If GUILang = LANG_NEUTRAL or (SUBLANG_NEUTRAL shl 10) Then Continue Else LangX := GUILang; 1, 5: LangX := Word(GetThreadLocale); 2, 6: LangX := GetUserDefaultLangID; 3, 7: LangX := GetSystemDefaultLangID; 8: LangX := DefaultLang; Else LangX := LANG_NEUTRAL or (SUBLANG_NEUTRAL shl 10); End; If (i2 = 4) or (LangX and not $03FF = 0) Then MaskX := $03FF; For i := High(LangResList.LangIDs) downto 0 do If LangResList.LangIDs[i].LangID and MaskX = LangX and MaskX Then Result := i; If Result >= 0 Then Break; End; If Result < 0 Then Result := 0; End;
Delphi-Quellcode:
DefaultLang die Sprache in der eure Anwendung hauptsächlich perstellt wurde
Var LangResList: TEnumResRec;
Language: Integer; LangResList.hModule := ...; LangResList.ResType := ...; LangResList.ResName := ...; Language := FindResourceLang(LangResList); If Language < 0 Then {Error} ... also die wo möglichst alle Resourcen vorhanden sind Tipp: Englisch macht sich gut, damit erhöht sich die Wahrscheinlichkeit daß jemand, dessen Sprache nicht unterstützt wird, dennoch was entziffern kann (Englisch ist ja angeblich 'ne Weltsprache) GUILang kann vom Programmierer zur Auswahl der Sprache passend definiert werden. ResErrorStr wird intern zum Speichern des Fehlertextes benötigt. Nach einer passenden Sprache wird in dieser Reinfolge gesucht:
Code:
wurde keine Resource gefungen, so wird eine Exception ausgelößt und für RT_STRING ein Fehlertext zurückgegeben
GUILang
ThreadLangID UserDefaultLangID SystemDefaultLangID GUILang (ignore SUBLANG_*) ThreadLangID (ignore SUBLANG_*) UserDefaultLangID (ignore SUBLANG_*) SystemDefaultLangID (ignore SUBLANG_*) DefaultLang (ignore SUBLANG_*) LANG_NEUTRAL erste gefungene Sprache oder es kommt der Rückgabewert -1 (beim zweiten Code). in der Ressourcendatei (.RC) sähe es dann z.B. so aus:
Code:
STRINGTABLE
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US BEGIN 123 "an example text" END STRINGTABLE LANGUAGE LANG_GERMAN, SUBLANG_GERMAN BEGIN 123 "ein Beispieltext" END 100 DIALOGEX 0, 0, 193, 122 STYLE ... FONT ... LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US CAPTION "..." BEGIN ... END Zum auslesen vorhandener Sprachen kann die selbe Funktion mißbraucht werden:
Delphi-Quellcode:
diese ließe sich ganz gut über die Sprachnamen (welche man natürlich mit speichern müßte) machen:
Var LangResList: TEnumResRec;
LangResList.hModule := HInstance; LangResList.ResType := ...; // vorhandene Resource, LangResList.ResName := ...; // von welcher die Sprachen ausgelesenwerden FindResourceLang(LangResList); For i := 0 to High(LangResList.LangIDs) do {LangResList.LangIDs[i].LangID}
Delphi-Quellcode:
Man könnte zwar die LangID entziffern und darüber den Sprachnamen erhalten, aber dafür bräuchte man dann in der Anwendung eine Liste aller möglichen LANG_* und SUBLANG_* Kombinationen.
Var LangResList: TEnumResRec;
S: WideString; LangResList.hModule := HInstance; LangResList.ResType := PWideChar(RT_STRING); LangResList.ResName := MakeIntResourceW(100); FindResourceLang(LangResList); For i := 0 to High(LangResList.LangIDs) do Begin SetLength(S, LangResList.LangIDs[i].Text^.Len); CopyMemory(@S[1], @LangResList.LangIDs[i].Text^.Text, LangResList.LangIDs[i].Text^.Len * 2); List.Add(S, LangResList.LangIDs[i].LangID); End; // STRINGTABLE // LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // BEGIN // 100 "english" // ... // END // // STRINGTABLE // LANGUAGE LANG_GERMAN, SUBLANG_GERMAN // BEGIN // 100 "Deutsch" // ... // END // // STRINGTABLE // LANGUAGE LANG_GERMAN, SUBLANG_GERMAN_SWISS // BEGIN // 100 "schweizer Deutsch" // ... // END Außerdem finde ich es besser den Sprachnamen in der entsprechenden Sprache zu speichern. Windows hat da zwar auch irgendwo 'ne Liste, aber diese ist nur in der Systemsprache (von Windows) vorhanden. (oder wer von euch kann alle Sprachen, um dann rauszufinden was dann in 'ner Liste "deutsch" heißt?) PS: das Menü zur Sprachänderung sollte entweder Bildlich gekennzeichnet oder in englich (können ja wohl die Meißten) benannt sein. Im Anhang hab ich das Ganze mal in ein Demoprojekt ausgelagert und unter Anderem auch mal eine LoadStringLangW erstellt, sowie eine Unterstütung für mehrere Resourcendateien entwickelt. Das ganze funktioniert zwar (hab's "erfolgreich" in 'nem Projekt drin), aber für Verbesserungen bin ich dennoch dankbar. vorallem die Reinfolge der Sprachen der Autoauswahl beschäftigt mich immernoch sehr. So wäre es z.. auch möglich,
Code:
aber da gibt's Probleme mit den Untersprachen (SUBLANG_*)
GUILang
GUILang (ignore SUBLANG_*) ThreadLangID ThreadLangID (ignore SUBLANG_*) UserDefaultLangID UserDefaultLangID (ignore SUBLANG_*) SystemDefaultLangID SystemDefaultLangID (ignore SUBLANG_*) DefaultLang (ignore SUBLANG_*) LANG_NEUTRAL erste gefungene Sprache wenn z.B. Chinesisch-Simple ausgewählt wird und nur Chinesich-Traditional gefunden wird, aber der Benutzer das Trationale nicht lesen kann, dann wär's ja blöd ihm dieses zu präsentieren ![]() [edit=sakura] Anhang auf Wunsch entfernt, neuer weiter unten. Mfg, sakura[/edit]
Ein Therapeut entspricht 1024 Gigapeut.
|
![]() |
Ansicht |
![]() |
![]() |
![]() |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
![]() |
![]() |