Support forum of the software localization tool Sisulizer


Localization Tool for VB, Delphi, .NET, C#, VB.NET, XML, Online Help, HTML ... Home

Get in contact with the makers of Sisulizer.
Our forum is open for all questions around Sisulizer from customers and prospects.
Don't hesitate to register and ask. The Sisulizer team will answer ASAP.

Search     Help Home Sisulizer Website Download
Search by username
Not logged in - Login | Register 

 Moderated by: Renate.Reinartz, Markus.Kreisel, Jaakko.Salmenius, Ilkka.Salmenius
New Topic Reply Printer Friendly
Run time language change for Delphi 7 + .Net 2 project - Usage - Three simple steps to localize - Technical Support (You need to be registered at the forum to write) - Localization Tool for VB, Delphi, .NET, C#, VB.NET, XML, Online Help, HTML ...
AuthorPost
 Posted: Tue Apr 1st, 2008 12:10 am
PM Quote Reply
bikemike
Member
 

Joined: Tue Nov 20th, 2007
Location: New Zealand
Posts: 105
Status: 
Offline
Hi folks

My primary code is Delphi 7 localised with Sisu 1.6.22.
I have run-time language change working with this using this code:

  fromLang := GetLanguageDescriptor(false);
  if compatible then
    begin
      // [doCompatible] only LIST languages compatible with loaded code page
      if SelectResourceLocale('EN',[doCompatible]) then
        langChanged := true
    end
  else
    begin
      // [doUseCurrentLanguage] use native language to NAME languages, some may be incompatible.
      if SelectResourceLocale('EN',[doUseCurrentLanguage]) then
        langChanged := true;
    end;

  if langChanged then
  begin
    toLang := GetLanguageDescriptor(false);
    ShowLangChangeMsg(fromLang, toLang, false);
    SetCurrentDefaultLocaleReg; // Save the selected language to the system registry
    SisuPostTranslate;          // Initialize the main form again because it was reloaded
  end;


This application uses several .Net support dlls.
I have localized one to start with (it has some C# forms) and added the line to switch culture:
    public MyForm(string inputXml)
            {
               
                Thread.CurrentThread.CurrentUICulture = CultureInfo.CurrentCulture;
                InitializeComponent();
                etc.....
 


The localized output is something like blah\fr\MyProject.resources.dll next to blah\MyProject.dll.

I modified the native C# form to see that when used from the Delphi app I am using the correct version of the dll, and I am.

I do a run-time language change into French from the main Delphi app, and this all changes to French. 
When I then take action that invokes the C# code, the native dll is used, not the fr\MyProject.resources.dll.

What have I missed?
Thanks

Back To Top PM Quote Reply

 Posted: Tue Apr 1st, 2008 06:46 am
PM Quote Reply
Jaakko.Salmenius
Administrator


Joined: Sat Apr 8th, 2006
Location: Tokyo, Japan
Posts: 1641
Status: 
Offline
    public MyForm(string inputXml)
            {
               
                Thread.CurrentThread.CurrentUICulture = CultureInfo.CurrentCulture;
                InitializeComponent();
                etc.....
 

The above code makes the DLL to use whatever is the default language set in the Control Panel's Regional and Language Options.

Is your goal to make managed DLL to use the same language as the main Delphi application?

Jaakko



____________________
http://www.sisulizer.com - Three simple steps to localize
Back To Top PM Quote Reply

 Posted: Tue Apr 1st, 2008 06:56 pm
PM Quote Reply
bikemike
Member
 

Joined: Tue Nov 20th, 2007
Location: New Zealand
Posts: 105
Status: 
Offline
Yes, I have gathered as much now...!

Yes, my aim is to have all components in the 'application' running the same language, changed via SelectResourceLocale from the Delphi code.

Having poked around in the code, I am now exploring the idea of getting the .NET components to read the software\borland\locales\delphiapplication.exe key and use this to set the CurrentUICulture.

What do you make of this idea?  And, if it has been done, any gotchas :-)??

cheers

Back To Top PM Quote Reply

 Posted: Tue Apr 1st, 2008 10:56 pm
PM Quote Reply
bikemike
Member
 

Joined: Tue Nov 20th, 2007
Location: New Zealand
Posts: 105
Status: 
Offline
I've had a go but it is not quite working yet...

Here's the code:
     void AlignUILanguage ()
            {
                string keyName = "HKEY_CURRENT_USER" + "" + "SoftwareBorlandLocales";
                string hostAppKey = Application.ExecutablePath.ToString();

                string langCode = (string)Microsoft.Win32.Registry.GetValue(keyName, hostAppKey, "NOKEY");
                if (langCode == "") {
                    Thread.CurrentThread.CurrentUICulture = CultureInfo.CurrentCulture;
                    return;
                }

                if (langCode == "NOKEY") return;

                foreach (CultureInfo ci in CultureInfo.GetCultures(CultureTypes.NeutralCultures)) {
                    if ((ci.Name).ToUpper() == langCode.ToUpper()) {
                        Thread.CurrentThread.CurrentUICulture = ci;
                        return;
                    }
                }

                foreach (CultureInfo ci in CultureInfo.GetCultures(CultureTypes.SpecificCultures)) {
                    if ((ci.Name).ToUpper() == langCode.ToUpper()) {
                        Thread.CurrentThread.CurrentUICulture = ci;
                        return;
                    }
                }
                MessageBox.Show("Unable to set language to " + langCode);
            }

The problem is switching back to the default language... The way it is just now, the host application default language is effectively US English.  My System is Brittish English.  When I detect that there is nothing set in the registry key, I set the CurrentUICutlure to the CurrentCulture - so I get British not American.

How do I reset the .NET application culture to the default of the host application?

EDIT 1
Actually this is not going to work is it!
For example, neutral french gets the language code (and dll suffix) FR from delphi/sisu.  In .NET there is a neutral culture for french where the name is fr.  That works.  So do many other neutral languages:
CS   czech        cs
DE   German       de
FI   Finnish      fi
FR   French       fr
HU   Hungarian    hu
IT   Italian      it
KO   Korean       ko
NL   Dutch        nl
PL   Polish       pl
SV   Swedish      sv
TR   Turkish      tr

Some do not.   Keeping with neutral languages, take the Simplified and Traditional Chinese for example:
The delphi resources are suffixed CH and CHT respectively.  In .NET the neutral cultures for these have the names zh-CHS and zh-CHT respectively:
CH     Chinese, Simplified    zh-CHS 
CHT   Chinese, Traditional   zh-CHT

With Specific Cultures, there are problems too.

Brazillian Portugues is OK:
Delphi Resource is suffixed PTB for Brazillian Portuguese
.NET Specific Culture with the Three Letter Windows Code PTB is pt-BR (Portuguese (Brazil))

Spanish in Spain is NOT:
Delphi Resource dll is suffixed ESP for Spain specific Spanish.
.NET Specific Culture of es-ES (Spanish (Spain))  has the Three Letter Windows code ESN.

So, I think I will need a common utility that is used by the .NET satellites to align themselves with the host Delphi application, such that the     Thread.CurrentThread.CurrentUICulture is set according to a big switch statement....

Looks like the answer regarding setting .NET to the default language is to specify what I know to be the correct name for the Cutlure:

Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-US");


Thoughts?  Any way without a big Switch?

EDIT 2
Actually, If I use the above code in most cases, but drop through to the exceptions when there is not match, then this means there might be more chance I can distribute a new language without delivering a new application update/install.  As such, this might be preferable to a single switch statement that must be updated and that does not come into effect until the customer gets a new build...

Last edited on Wed Apr 2nd, 2008 01:48 am by bikemike

Back To Top PM Quote Reply

 Posted: Wed Apr 2nd, 2008 06:14 am
PM Quote Reply
bikemike
Member
 

Joined: Tue Nov 20th, 2007
Location: New Zealand
Posts: 105
Status: 
Offline
Well, I seem to be getting along with the code as I now have it - attached
I call it before any .NET form running in a dll loaded by the host Delphi app like this:

                ....
                AlignLanguage.DotNetUIToDelphiWin32HostAppUI(true);
                InitializeComponent();
                ....


If I need to call it for some code that might be already running whilst the host delphi app is translated, I can call with false, to prevent problem messages being displayed on every timer, loop, cycle or whatever....  blunt for now.  In those case I guess some things might not be translated until such forms are reloaded...

The only thing I have left to tidy is the assumptions about and setting of the default language.  I have some confusion between British and US english regarding defaults.

I also noticed at the last minute that when I set the default language, this actually clears the registry Key I am watching (ClearCurrentDefaultLocaleReg), but can handle that.

Hey, I hope someone finds this useful.
Suggestions welcome:-)




Attachment: Language.zip (Downloaded 4 times)

Back To Top PM Quote Reply

 Posted: Thu Apr 3rd, 2008 02:41 am
PM Quote Reply
Jaakko.Salmenius
Administrator


Joined: Sat Apr 8th, 2006
Location: Tokyo, Japan
Posts: 1641
Status: 
Offline
Thank you for the sample. I am now working with this.

Jaakko



____________________
http://www.sisulizer.com - Three simple steps to localize
Back To Top PM Quote Reply

 Posted: Mon Apr 7th, 2008 01:23 am
PM Quote Reply
bikemike
Member
 

Joined: Tue Nov 20th, 2007
Location: New Zealand
Posts: 105
Status: 
Offline
I tried to find a way to listen to the Registry value change, but I am not up to that yet.  Can it be done?

Only change I made so far was to move the const strings into a new Resources file for the Common.Language namespace and then changed references to CMISSINGRESOURCEACTION to be Resources.CMISSINGRESOURCEACTION for example.

That way some localised message might be able to help the user solve the problem!

Back To Top PM Quote Reply

 Posted: Mon Apr 7th, 2008 07:20 am
PM Quote Reply
Jaakko.Salmenius
Administrator


Joined: Sat Apr 8th, 2006
Location: Tokyo, Japan
Posts: 1641
Status: 
Offline
I have little bit hard time to test this. How do you call a managed DLL from unmanaged code. As far as I know the only way to do call managed DLLs from unmanaged code is to use COM. So the managed DLL has to be a COM object and you us it as any other COM object.

If you have found a way to call managed DLL just like they would look as unmanged DLL (e.g. standard WIN32 DLLs) tell me.

Jaakko



____________________
http://www.sisulizer.com - Three simple steps to localize
Back To Top PM Quote Reply

 Posted: Mon Apr 7th, 2008 08:17 pm
PM Quote Reply
bikemike
Member
 

Joined: Tue Nov 20th, 2007
Location: New Zealand
Posts: 105
Status: 
Offline
It's the other way around; the Delphi app is the primary app and the .NET dlls are accessed throuh COM interop.

Back To Top PM Quote Reply

 Posted: Mon Apr 7th, 2008 10:28 pm
PM Quote Reply
Jaakko.Salmenius
Administrator


Joined: Sat Apr 8th, 2006
Location: Tokyo, Japan
Posts: 1641
Status: 
Offline
Thanks. My sample is almost ready now. It is a Delphi applications that call two DLLs. First is written in Delphi and another in C#. Delphi DLL is called as any other DLL using standard C calling convention and C# DLL is called through COM interop. Both DLLs contains forms. The application has runtime language switch and it also make DLLs to change language.

I try to get it done today.

Jaakko



____________________
http://www.sisulizer.com - Three simple steps to localize
Back To Top PM Quote Reply

 Posted: Wed Apr 9th, 2008 12:45 am
PM Quote Reply
Jaakko.Salmenius
Administrator


Joined: Sat Apr 8th, 2006
Location: Tokyo, Japan
Posts: 1641
Status: 
Offline
Here is a sample. It contains a Delphi application, Project1.exe, that call two DLLs

1) VclModule.dll. This is written in Delphi and contains one form.

2) NetModudel.dll. This is written in C#/VS 2005 and contains one forms.

Application use DLL calling convention to call VclModule.dll and COM interop to call NetModule.dll

Application has runtime language switch. After language change applications call DLLs SetLanguage method. It performs the same language switch in the DLL. The language parameter is passed as VCL language DLL file extension. This is DE for German, FI for Finnish, etc. Because VclModule.dll is also VCL it can use the same code to pass that to SetNewResourceFile. However NetModule.dll has to change the value to culture info. My current implementation is not perfect. It just changes give values to lower case and pass that to CultureInfo

FI -> fi -> CultureInfo("fi");

I will add a logic that will use WIN32 NSL API to convert VCL's code (that is actually Windows code) to ISO code used by .NET. Unfortunately I am a bit busy at the moment so it might take few more days.

Delphi code is written in Delphi 2007 but does also works on Delphi 7.
C# code is writtein in Visual Studio 2005 but does also works on VS 2008.

I hope that this will help you.

Jaakko

Attachment: Sample.zip (Downloaded 16 times)



____________________
http://www.sisulizer.com - Three simple steps to localize
Back To Top PM Quote Reply

 Posted: Wed Apr 9th, 2008 01:34 am
PM Quote Reply
bikemike
Member
 

Joined: Tue Nov 20th, 2007
Location: New Zealand
Posts: 105
Status: 
Offline
That sounds like a much neater solution than mine.  I look forward to the conversion of the language codes, then I will look at changing our interfaces to do it this way.  Will look at the code later.

Back To Top PM Quote Reply

 Posted: Thu Nov 20th, 2008 12:05 am
PM Quote Reply
Peter Jaquiery
Member
 

Joined: Mon May 26th, 2008
Location: Dunedin, New Zealand
Posts: 7
Status: 
Offline
In fact calling between managed code and unmanaged code is possible and even fairly straight forward without COM. We have a large (> 1e6 lines) C++ application that we are starting to add managed code to to provide .Net UI elements.

You need a header file that is included by both the managed and unmanaged code that provides the "interface". Something like this:

// Struct to allow call back into unmanaged C++ code
struct ConfigurationCallback
{
// Configuration state
virtual wchar_t const *GetConfigName (int row) = 0;
virtual void SetConfigName (int row, wchar_t const *name) = 0;
virtual int GetConfigOnBits (int row) = 0;
virtual int GetConfigOffBits (int row) = 0;
};


typedef struct SelectHolder *SelectHandle;

// -- Select dialog management - managed C++ called by unmanaged C++
SelectHandle CreateSelect
   (
   int parent,
   ConfigurationCallback &config
   );
void ShowSelect (SelectHandle selectDlgH);
void CloseSelect (SelectHandle selectDlgH);


In the unmanaged code you can then:

class RowsCallback: public ConfigurationCallback
{
public:

RowsCallback (DOutDocument &host, DOutSettings &settings);

wchar_t const *GetConfigName (int row);
void SetConfigName (int row, wchar_t const *name);
int GetConfigOnBits (int row);
int GetConfigOffBits (int row);

private:

DOutDocument &mHost;
};

...
RowsCallback callback (*this);
SelectHandle handle = CreateSelect (int (hWnd), callback);
...
CloseSelect (handle );


and in the managed code you implement the dialog management by:

SelectHandle CreateSelect (int parent, ConfigurationCallback &config)
{
SelectHandle handle = new SelectHolder
   (
   parent,
   gcnew MyForm (config)
   );

MyForm  ^dlg = handle->mDlg;

dlg->SuspendLayout ();
dlg->Visible = false;
ShowSelect (handle);
dlg->ResumeLayout ();
dlg->Visible = true;

return handle;
}


void ShowSelect (SelectHandle handle)
{
MyForm ^dlg = handle->mDlg;

if (! dlg->Visible)
   dlg->Show (handle->mParentWnd);

dlg->BringToFront ();
}


void CloseSelect (SelectHandle handle)
{
DigiOut::DOutSelectForm ^dlg = handle->mDlg;

delete dlg;
delete handle;
}


and call back into the unmanaged C++ by:

String ^configName = gcnew String (config.GetConfigName (row));


and:

array<wchar_t> ^chars = configName->ToCharArray();
pin_ptr<wchar_t> pinx = &chars[0];

config.SetConfigName (index, pinx);


This of course hardly scratches the surface and there are many gotchas, but it should be enough to give the flavour of what is needed.

All the code is put in a .vcproj (C++) project. The project is set for "No Common Language Runtime support" in the General properties. The individual managed C++ files are set to "Common Language Runtime Support(/clr)" and the unmanaged C++ source files get the project default setting.

Back To Top PM Quote Reply

Current time is 03:04 am  
Localization Tool for VB, Delphi, .NET, C#, VB.NET, XML, Online Help, HTML ... > Technical Support (You need to be registered at the forum to write) > Usage - Three simple steps to localize > Run time language change for Delphi 7 + .Net 2 project



WowUltra 1.11 Copyright © 2007 by Jim Hale - Based on WowBB Copyright © 2003-2006 Aycan Gulez

Sisulizer software localization tool - Three simple steps to localize