Probie
asked on
Loading the base language in a multilanguage application
I am writing a class that makes it possible to select language dependent resource files (language strings) in runtime looking on the example shipped with Delphi in the REInit.pas file under "DELPHI5\Demos\RichEdit".
I have no problem with selecting/using a different language then the one used with Windows (Delphi always selects the resource file that is compatible with your current windows language) but what if the user wants to turn back to the base language during runtime?
The base language is stored in the resource data in the EXE file and can not be loaded the same way that REInit.pas does with external resource files.
This becomes a problem if the application is made for English and runs on a (for example) German windows version. Then Delphi loads the german resource file upon start, and if the user still wants English on the German system there is no easy way to go back.
I know that there is a registry key that can be set to force a multilanguage application to use a certain language, but I think that is a poor solution and there must be a better way???
So my questions are:
1. Can the automatically language selection that delphi does on startup be disabled in some way?
2. How can I load back the base language resource data?
Can anyone supply a code example on how that could fit in into the REInit.pas routines like a function:
function LoadBaseLanguageResource(E XEName : string) : boolean;
---[REInit.pas]----------- ---------- ---------- ---------- --
type TAsInheritedReader = class(TReader)
public
procedure ReadPrefix(var Flags: TFilerFlags; var AChildPos: Integer); override;
end;
procedure TAsInheritedReader.ReadPre fix(var Flags: TFilerFlags; var AChildPos: Integer);
begin
inherited ReadPrefix(Flags, AChildPos);
Include(Flags, ffInherited);
end;
function SetResourceHInstance(NewIn stance: Longint): Longint;
var CurModule: PLibModule;
begin
CurModule := LibModuleList;
Result := 0;
while CurModule <> nil do begin
if CurModule.Instance = HInstance then begin
if CurModule.ResInstance <> CurModule.Instance then FreeLibrary(CurModule.ResI nstance);
CurModule.ResInstance := NewInstance;
Result := NewInstance;
Exit;
end;
CurModule := CurModule.Next;
end;
end;
function LoadNewResourceModule(Loca le: LCID): Longint;
var
FileName: array [0..260] of char;
P: PChar;
LocaleName: array[0..4] of Char;
NewInst: Longint;
begin
GetModuleFileName(HInstanc e, FileName, SizeOf(FileName));
GetLocaleInfo(Locale, LOCALE_SABBREVLANGNAME, LocaleName, SizeOf(LocaleName));
P := PChar(@FileName) + lstrlen(FileName);
while (P^ <> '.') and (P <> @FileName) do Dec(P);
NewInst := 0;
Result := 0;
if P <> @FileName then begin
Inc(P);
if LocaleName[0] <> #0 then begin
// Then look for a potential language/country translation
lstrcpy(P, LocaleName);
NewInst := LoadLibraryEx(FileName, 0, LOAD_LIBRARY_AS_DATAFILE);
if NewInst = 0 then begin
// Finally look for a language only translation
LocaleName[2] := #0;
lstrcpy(P, LocaleName);
NewInst := LoadLibraryEx(FileName, 0, LOAD_LIBRARY_AS_DATAFILE);
end;
end;
end;
if NewInst <> 0 then Result := SetResourceHInstance(NewIn st)
end;
function InternalReloadComponentRes (const ResName: string; HInst: THandle; var Instance: TComponent): Boolean;
var HRsrc: THandle;
ResStream: TResourceStream;
AsInheritedReader: TAsInheritedReader;
begin { avoid possible EResNotFound exception }
if HInst = 0 then HInst := HInstance;
HRsrc := FindResource(HInst, PChar(ResName), RT_RCDATA);
Result := HRsrc <> 0;
if not Result then Exit;
ResStream := TResourceStream.Create(HIn st, ResName, RT_RCDATA);
try
AsInheritedReader := TAsInheritedReader.Create( ResStream, 4096);
try
Instance := AsInheritedReader.ReadRoot Component( Instance);
finally
AsInheritedReader.Free;
end;
finally
ResStream.Free;
end;
Result := True;
end;
function ReloadInheritedComponent(I nstance: TComponent; RootAncestor: TClass): Boolean;
function InitComponent(ClassType: TClass): Boolean;
begin
Result := False;
if (ClassType = TComponent) or (ClassType = RootAncestor) then Exit;
Result := InitComponent(ClassType.Cl assParent) ;
Result := InternalReloadComponentRes (ClassType .ClassName , FindResourceHInstance(
FindClassHInstance(ClassTy pe)), Instance) or Result;
end;
begin
Result := InitComponent(Instance.Cla ssType);
end;
procedure ReinitializeForms;
var
Count: Integer;
I: Integer;
Form: TForm;
begin
Count := Screen.FormCount;
for I := 0 to Count - 1 do begin
Form := Screen.Forms[I];
ReloadInheritedComponent(F orm, TForm);
end;
end;
I have no problem with selecting/using a different language then the one used with Windows (Delphi always selects the resource file that is compatible with your current windows language) but what if the user wants to turn back to the base language during runtime?
The base language is stored in the resource data in the EXE file and can not be loaded the same way that REInit.pas does with external resource files.
This becomes a problem if the application is made for English and runs on a (for example) German windows version. Then Delphi loads the german resource file upon start, and if the user still wants English on the German system there is no easy way to go back.
I know that there is a registry key that can be set to force a multilanguage application to use a certain language, but I think that is a poor solution and there must be a better way???
So my questions are:
1. Can the automatically language selection that delphi does on startup be disabled in some way?
2. How can I load back the base language resource data?
Can anyone supply a code example on how that could fit in into the REInit.pas routines like a function:
function LoadBaseLanguageResource(E
---[REInit.pas]-----------
type TAsInheritedReader = class(TReader)
public
procedure ReadPrefix(var Flags: TFilerFlags; var AChildPos: Integer); override;
end;
procedure TAsInheritedReader.ReadPre
begin
inherited ReadPrefix(Flags, AChildPos);
Include(Flags, ffInherited);
end;
function SetResourceHInstance(NewIn
var CurModule: PLibModule;
begin
CurModule := LibModuleList;
Result := 0;
while CurModule <> nil do begin
if CurModule.Instance = HInstance then begin
if CurModule.ResInstance <> CurModule.Instance then FreeLibrary(CurModule.ResI
CurModule.ResInstance := NewInstance;
Result := NewInstance;
Exit;
end;
CurModule := CurModule.Next;
end;
end;
function LoadNewResourceModule(Loca
var
FileName: array [0..260] of char;
P: PChar;
LocaleName: array[0..4] of Char;
NewInst: Longint;
begin
GetModuleFileName(HInstanc
GetLocaleInfo(Locale, LOCALE_SABBREVLANGNAME, LocaleName, SizeOf(LocaleName));
P := PChar(@FileName) + lstrlen(FileName);
while (P^ <> '.') and (P <> @FileName) do Dec(P);
NewInst := 0;
Result := 0;
if P <> @FileName then begin
Inc(P);
if LocaleName[0] <> #0 then begin
// Then look for a potential language/country translation
lstrcpy(P, LocaleName);
NewInst := LoadLibraryEx(FileName, 0, LOAD_LIBRARY_AS_DATAFILE);
if NewInst = 0 then begin
// Finally look for a language only translation
LocaleName[2] := #0;
lstrcpy(P, LocaleName);
NewInst := LoadLibraryEx(FileName, 0, LOAD_LIBRARY_AS_DATAFILE);
end;
end;
end;
if NewInst <> 0 then Result := SetResourceHInstance(NewIn
end;
function InternalReloadComponentRes
var HRsrc: THandle;
ResStream: TResourceStream;
AsInheritedReader: TAsInheritedReader;
begin { avoid possible EResNotFound exception }
if HInst = 0 then HInst := HInstance;
HRsrc := FindResource(HInst, PChar(ResName), RT_RCDATA);
Result := HRsrc <> 0;
if not Result then Exit;
ResStream := TResourceStream.Create(HIn
try
AsInheritedReader := TAsInheritedReader.Create(
try
Instance := AsInheritedReader.ReadRoot
finally
AsInheritedReader.Free;
end;
finally
ResStream.Free;
end;
Result := True;
end;
function ReloadInheritedComponent(I
function InitComponent(ClassType: TClass): Boolean;
begin
Result := False;
if (ClassType = TComponent) or (ClassType = RootAncestor) then Exit;
Result := InitComponent(ClassType.Cl
Result := InternalReloadComponentRes
FindClassHInstance(ClassTy
end;
begin
Result := InitComponent(Instance.Cla
end;
procedure ReinitializeForms;
var
Count: Integer;
I: Integer;
Form: TForm;
begin
Count := Screen.FormCount;
for I := 0 to Count - 1 do begin
Form := Screen.Forms[I];
ReloadInheritedComponent(F
end;
end;
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
I think you are right, Alexey. I tested LoadNewResourceModule(0) and it worked.
To be more specific, the default language can be specified by a constant as defined in the API help..
----------
The following three combinations of usPrimaryLanguage and usSubLanguage have special meaning:
Primary language ID Sublanguage ID Meaning
LANG_NEUTRAL SUBLANG_NEUTRAL Language neutral
LANG_NEUTRAL SUBLANG_DEFAULT User default language
LANG_NEUTRAL SUBLANG_SYS_DEFAULT System default language
----------
So,
const
DEFAULT = (SUBLANG_DEFAULT shl 10) or LANG_NEUTRAL;
will work. I tested it.
----------
The following three combinations of usPrimaryLanguage and usSubLanguage have special meaning:
Primary language ID Sublanguage ID Meaning
LANG_NEUTRAL SUBLANG_NEUTRAL Language neutral
LANG_NEUTRAL SUBLANG_DEFAULT User default language
LANG_NEUTRAL SUBLANG_SYS_DEFAULT System default language
----------
So,
const
DEFAULT = (SUBLANG_DEFAULT shl 10) or LANG_NEUTRAL;
will work. I tested it.
ASKER
But I guess that it needs more code then LoadNewResourceModule(0) to load the base langauge???
Does anyone know how to disable the autoselection of resource files in a delphi application???
Does anyone know how to disable the autoselection of resource files in a delphi application???
Hello, Probie.
> But I guess that it needs more code then LoadNewResourceModule(0) to load the base langauge???
No, not more code is needed to load base language.
>Does anyone know how to disable the autoselection of resource files in a delphi application???
Here is a form for selecting interface language from one of my projects. Here there is three buttons for choosing between English, Spanish and Catalan languages.
unit ufrmLogo;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Buttons, ExtCtrls, uReinit;
type
TfrmLogo = class(TForm)
Image1: TImage;
btnEnglish: TButton;
btnCastellano: TButton;
btnCatalan: TButton;
procedure FormShow(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure btnLangClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure HideButtons(Value: Boolean = False);
end;
implementation
{$R *.DFM}
const
ENGLISH = (SUBLANG_ENGLISH_US shl 10) or LANG_ENGLISH;
FRENCH = (SUBLANG_FRENCH shl 10) or LANG_FRENCH;
GERMAN = (SUBLANG_GERMAN shl 10) or LANG_GERMAN;
CASTILIAN = (SUBLANG_SPANISH shl 10) or LANG_SPANISH;
CATALAN = LANG_CATALAN;
procedure TfrmLogo.FormShow(Sender: TObject);
begin
btnCastellano.Visible := fsModal in FormState;
btnCatalan.Visible := fsModal in FormState;
btnEnglish.Visible := fsModal in FormState;
end;
procedure TfrmLogo.FormCreate(Sender : TObject);
begin
btnEnglish.Tag := ENGLISH;
btnCastellano.Tag := CASTILIAN;
btnCatalan.Tag := CATALAN;
end;
(* OnClick event handler for language buttons *)
procedure TfrmLogo.btnLangClick(Send er: TObject);
begin
LoadNewResourceModule(TCom ponent(Sen der).Tag);
ModalResult := 1;
end;
procedure TfrmLogo.HideButtons(Value : Boolean);
begin
btnCastellano.Visible := Value;
btnCatalan.Visible := Value;
btnEnglish.Visible := Value;
end;
end.
> But I guess that it needs more code then LoadNewResourceModule(0) to load the base langauge???
No, not more code is needed to load base language.
>Does anyone know how to disable the autoselection of resource files in a delphi application???
Here is a form for selecting interface language from one of my projects. Here there is three buttons for choosing between English, Spanish and Catalan languages.
unit ufrmLogo;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Buttons, ExtCtrls, uReinit;
type
TfrmLogo = class(TForm)
Image1: TImage;
btnEnglish: TButton;
btnCastellano: TButton;
btnCatalan: TButton;
procedure FormShow(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure btnLangClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure HideButtons(Value: Boolean = False);
end;
implementation
{$R *.DFM}
const
ENGLISH = (SUBLANG_ENGLISH_US shl 10) or LANG_ENGLISH;
FRENCH = (SUBLANG_FRENCH shl 10) or LANG_FRENCH;
GERMAN = (SUBLANG_GERMAN shl 10) or LANG_GERMAN;
CASTILIAN = (SUBLANG_SPANISH shl 10) or LANG_SPANISH;
CATALAN = LANG_CATALAN;
procedure TfrmLogo.FormShow(Sender: TObject);
begin
btnCastellano.Visible := fsModal in FormState;
btnCatalan.Visible := fsModal in FormState;
btnEnglish.Visible := fsModal in FormState;
end;
procedure TfrmLogo.FormCreate(Sender
begin
btnEnglish.Tag := ENGLISH;
btnCastellano.Tag := CASTILIAN;
btnCatalan.Tag := CATALAN;
end;
(* OnClick event handler for language buttons *)
procedure TfrmLogo.btnLangClick(Send
begin
LoadNewResourceModule(TCom
ModalResult := 1;
end;
procedure TfrmLogo.HideButtons(Value
begin
btnCastellano.Visible := Value;
btnCatalan.Visible := Value;
btnEnglish.Visible := Value;
end;
end.
Hi
>But I guess that it needs more code then LoadNewResourceModule(0) to load the base langauge???
Well ,you don't. I tested it. (From Alexey, adapted)
>Does anyone know how to disable the autoselection of resource files in a delphi application???
I didn't get the question..
Hi Alexey,
>procedure TfrmLogo.btnLangClick(Send er: TObject);
>begin
> LoadNewResourceModule(TCom ponent(Sen der).Tag);
> ModalResult := 1;
>end;
I thought you need ReInitializeForms;
By the way have you customized ReInit or something to use uReInit?
>But I guess that it needs more code then LoadNewResourceModule(0) to load the base langauge???
Well ,you don't. I tested it. (From Alexey, adapted)
>Does anyone know how to disable the autoselection of resource files in a delphi application???
I didn't get the question..
Hi Alexey,
>procedure TfrmLogo.btnLangClick(Send
>begin
> LoadNewResourceModule(TCom
> ModalResult := 1;
>end;
I thought you need ReInitializeForms;
By the way have you customized ReInit or something to use uReInit?
> The base language is stored in the resource data in the EXE file and can not be loaded the same way
that REInit.pas does with external resource files.
But it can, you simply use SetResourceHInstance(HInst ance), what's the problem?
that REInit.pas does with external resource files.
But it can, you simply use SetResourceHInstance(HInst
ASKER
Ok, LoadNewResourceModule(0) or LoadNewResourceModule(HIns tance); does the trick. So Alzy gets the points since he was first with that.
The question about how to disable Delphis automaticaly selection/load of resource files still remains.
Thanks to you all!
/Probie
The question about how to disable Delphis automaticaly selection/load of resource files still remains.
Thanks to you all!
/Probie
> how to disable Delphis automaticaly selection/load of resource files
The resource DLLs are loaded based on the DLLs' file name and extension.
Perhaps you could use different DLL file names or extensions for the resource DLLs so they are not found at app's startup. Then you can load them dynamically at runtime as needed.
The resource DLLs are loaded based on the DLLs' file name and extension.
Perhaps you could use different DLL file names or extensions for the resource DLLs so they are not found at app's startup. Then you can load them dynamically at runtime as needed.
What do you mean by autoselection? Choice of the language at startup of the program? Or is it a question of memory, loading all DLLs at once?
ASKER
Nah, if you create an application with multiple languages (one external resourcefile for each language), Delphi puts code so that the application automaticaly selectes a external resource file depending on your locale settings in windows upon application startup. I want to avoid this.
One way to avoid this is to set a registry value in /Software/Borland/Locales, forcing the language I want, but that is a poor solution...
What part in the code reads this registry value and how can I avoid that this happpens?
One way to avoid this is to set a registry value in /Software/Borland/Locales,
What part in the code reads this registry value and how can I avoid that this happpens?
const
ENGLISH = (SUBLANG_ENGLISH_US shl 10) or LANG_ENGLISH;
ARABIC = (SUBLANG_ARABIC_SAUDI_ARAB
procedure TForm1.English1Click(Sende
begin
(Sender as TMenuItem).Checked:=true;
if Sender = Arabic1 then
begin
if LoadNewResourceModule(ARAB
ReinitializeForms;
end
else if Sender = English1 then
begin
if LoadNewResourceModule(ENGL
ReinitializeForms;
end;
end;
// The default language is Arabic in my machine..