|
Here is another approach:
Multiple language support for MFC applications with extension DLL[^]
This is DLL approach. Translater using only *.rc and res\*.* file to make resource-only DLL file for a new language.
Only problem with that approach, you cannot change language on-the-fly, you have to exit your application and launch it again.
I think, personally, for web applicaiton, text file, incluing xml file may be better. For Windows application, language resource-only DLL is a better direction.
---
Herbert Yu
Are you sure the speed of computer industry is proper? Are you sure the software you released is a bug free one?
|
|
|
|
|
i'm quite interested in the XML way
|
|
|
|
|
Several problems here:
> None of the articles I found provided this."
What about this:
http://www.microsoft.com/globaldev/getwr/steps/wrg_mui.mspx
http://www.microsoft.com/globaldev/handson/dev/muiapp.mspx
And, in general, http://www.microsoft.com/globaldev is a very good starting point
> Some people are still hard-coding the strings of their application in their code.
and a bit below
> const CString LNG_DEFAULT = "english";
This is just another form of hard-coding.
Better ways would be to:
- allow the user to configure this.
- pop a dialog with the list of available languages if the desired one is missing
> if (!pLanguage->LoadLanguage(GetSystemDefaultLangID()))
Quote from the MSDN documentation: "The GetSystemDefaultLangID function retrieves the language identifier of the system locale"
What you want is "GetUserDefaultUILanguage"
See here why: http://www.microsoft.com/globaldev/reference/localetable.mspx
The whole idea of the article is not very useful. Instead of carrying around the handle to the resource DLL, so that you can use it with GetString, we now have to carry a CLanguage pointer:
> pLanguage->GetString(IDS_MYSTATICTEXT)
Since you use MFC, you have two main options:
- use AfxSetResourceHandle, and you need no other changes to your code.
- do nothing
Right, "do nothing" is a real option if you use MFC7x
Because deep in the MFC code there is something that gets (correctly) the UI language of the OS, then searches for a resource dll named <appname>.<3charLangCode>
So, if you application is named HelloWorld.exe, drop in the same folder localized DLLs named DEU (German), FRA (France), JPN (Japanese) and so on. On a French system is going to load the resources from the French DLL and so on.
The 3 characters abbreviation is the value returned by GetLocaleInfo with the LOCALE_SABBREVLANGNAME flag.
Try it. It just works, no need for code changes. You might even localize 3rd party applications (if they where developed with MFC7x).
> Language DLL ID Code Language description
> czech.dll 0x0405 CSY Czech
And
> you can edit these settings yourself in Language.cpp
This is more hard-coded stuff.
And the approach restricts you to one module only.
If you have several localizable modules, you are out of luck, you can only have one dll per language.
MS approach for Windows and Office MUI (Multilingual User Interface):
MyApp.exe
Module.dll
PlugIn.dll
<MUI>
<0409>
MyApp.dll
Module.dll
PlugIn.dll
<040c>
MyApp.dll
Module.dll
PlugIn.dll
Now, don't get me wrong, the article tries to solve a real problem, and it does it almost ok.
As a student project is not bad. But the bad part is recommending this in a place like CodeProject, to be used "for real".
The problem was solved before, and better. Just spending a bit more time on the Internet searching for previous ideas always helps.
|
|
|
|
|
> Some people are still hard-coding the strings of their application in their code.
and a bit below
> const CString LNG_DEFAULT = "english";
This is just another form of hard-coding.
>> Better ways would be to:
>> - allow the user to configure this.
>> - pop a dialog with the list of available languages if the desired one is missing
Yes, but this is to configure the class for your application. Of course this is hard-coded.
You are always able to change this to your needs as a software engineer. You can list
the language themselves if LoadLanguage returns false! It's not my task to obligate the
programmer to use my dialogs.
>> Quote from the MSDN documentation: "The GetSystemDefaultLangID function retrieves the language identifier of the system locale"
>> What you want is "GetUserDefaultUILanguage"
Because this is just an example, this is really not important. It is just to show the
class can work with language id's. You aren't going to use this code because it is not
part of the class itself.
>> Since you use MFC, you have two main options:
>> - use AfxSetResourceHandle, and you need no other changes to your code.
>> - do nothing
But there is also a non-MFC class supported. And the AfxSetResourceHandle needs all
the resources to be in the new resource file.
BTW, thanks for all the tips, I will try to search for other articles to write...
BTW2, it's a pitty you have written the message anonymous. I would like to see your articles and maybe they can inspire me to write other/better articles.
Geert
Want to spread the newest version of your software automatically? Use Updater!
Visit my website: http://geert.yoki.org
|
|
|
|
|
In MFC usage of AfxSetResourceHandle is wrong way.
If you have the application with some resource DLL, u can load needed resource DLL and set resource app handle in app' InitInstance to it.
But if you also have another DLL which has own resource DLLs and trying to use AfxSetResourceHandle in dll, you set main app resorce handle instead dll resource handle.
In this case using MFC AfxFindResourceHandle is right way.
It find resource in ALL DLL loaded to main app and to app' dll (resource, system and your own statically and dynamically linked). You also can using resource of one DLL or main app in another DLL without loading it again.
Also you can use my implementation FindResourceHandleLang (class CLanguageTools) - it's simply code copy of MFC AfxFindResourceHandle, that support find resource by language.
ps - sorry for my english
|
|
|
|
|
Hi,
Me again (the "Anonymous").
I am sorry, it was not my intent to do that. In fact, I am quite sure I have typed a name (but not an address). And I know I was not logged-in (could not find the password
I will start with some minor comments, then and then some blah-blah
==============
>> Yes, but this is to configure the class for your application.
>> Of course this is hard-coded.
Well, it is not of-course. The flexibility to change should be at the user level, not programmer level.
Scenario: Belgium. You try Dutch, not found, defaults to French (configurable as fallback by the sys-admin)
The real "hard-fallback" is usualy done by having the fallback language in the main executable.
==============
>> Because this is just an example, this is really not important.
...
>>You aren't going to use this code because it is not part of the class itself.
This is the theory. The practice is that if you provide a nice example, other
will copy-paste and start from there.
The only excuse of not doing it right is if it does hide the point you are trying to make. Example: complete error checking in code trying to show something else. But even then I would add a big comment in the code!
Sure, this is not mandatory, but is based on experience
==============
>> But there is also a non-MFC class supported.
>> And the AfxSetResourceHandle needs all the resources to be in the new resource file.
Correct.
==============
>> BTW2, it's a pitty you have written the message anonymous. I would like to
>> see your articles and maybe they can inspire me to write other/better
>> articles.
My name is Mihai Nita (just in case CodeProject displays only the nick), worked in localization / internationalization for the last 8 years.
MS Windows SDK MVP, in fact, Internationalization MVP. There is no such title, they squized me where in the closes drawer , but you can see it here:
http://blogs.msdn.com/michkap/archive/2005/06/01/424067.aspx
You can find me almost every day (night, California time) answering i18n questions, mostly on microsoft.public.win32.programmer.international
You can probably find most of my older answers with Google (search Groups for author:mihai author:n)
I am kind of lazzy, so I cannot point you to a web site (at least not for now But I am working on it.
======= Now, the blah-blah =======
Now, enough about me.
I have read again my post, it seems nastier than intended. Sorry.
English is not my language and I don't like reading twice befor posting.
Overall you did quite nice. I have seen way worse in production applications.
The lack of i18n awareness is amazing sometimes.
Keep up the good work, and (maybe) keep in touch,
Mihai
|
|
|
|
|
well, I think you are right about the constants. I changed them to member variables which can be changed using an Init function. I will upload the new version today (but CP will take a few days to review the article).
I am going to try and expand this class so it can load multiple dll's, toolsbars and dialogs.
I also removed the library dependency by using the file as Blake Miller suggested.
And I know not all articles can be used in real-life. But most programmers here also want to develop applications in their free time. For them, this is an easy solution to support multiple languages.
Because most things here can only be used for non-commercial apps, I think this site is not only for professional developers.
Geert
Want to spread the newest version of your software automatically? Use Updater!
Visit my website: http://geert.yoki.org
|
|
|
|
|
Right, "do nothing" is a real option if you use MFC7x
Because deep in the MFC code there is something that gets (correctly) the UI language of the OS, then searches for a resource dll named .<3charLangCode>
I could have sworn that MFC with Visual Studio 6.0 did this as well, but I could not find the code. I am almost certain that the older verisons of MFC DID do this, then MS must have removed it for some reason, and now it is back in again. Very strange.
|
|
|
|
|
Well Said Mihai...
Nat pear
|
|
|
|
|
|
Using your method you need to create numerous DLL for different language support. Another approach is to load resource from ONE dll or program, i.e. from one instance contain different language.
The problem of this - is loading of resource by language (as default FindResource and LoadResource english language is prefered while finding or loading).
My multilanguage support is modification of article (in russian)-
http://www.rsdn.ru/article/ui/multilang.xml[^]. But this dont work in DLL because FindResourceEx look resource only in main app instance.
MFC source has modification of FindResource - AfxFindResource - that allow to round problem with DLL. It simply find resource in all instance of app.
Using article feature and MFC source i create class CLanguageTools, that allows to manipulate with resource.
Next code is example of use -
<----------------------------
CString str;
CLanguageTools tools;
tools.LoadString(str, IDS_YOUR_STRING, CGlobalData::GetInstance()->GetCurrentLangID());
there CGlobalData - is global data (patern Singleton), specified current language and another program option, that has unique instance for all DLL and app.
----------------------------->
Class CLanguageTools provide loading string, menu and toolbar resource and can be easily extended for other resorce type.
For dialog add this to DoModal() -
-------------------------------------
CLanguageTools tools;
HGLOBAL hrRes = tools.LoadResourceLang(RT_DIALOG, nDialogID, CGlobalData::GetInstance()->GetCurrentLangID());
m_lpszTemplateName = NULL;
m_lpDialogTemplate = (LPDLGTEMPLATE)hrRes;
return CDialog::DoModal();
-------------------------------------
For property page add this to constructor -
-----------------------------------------------------------
CLanguageTools tools;
HGLOBAL hrRes = tools.LoadResourceLang(RT_DIALOG, nID, CXYGraphGlobalData::GetInstance()->GetCurrentLangID());
if (hrRes)
{
m_psp.dwFlags |= PSP_DLGINDIRECT;
m_psp.pResource = (LPCDLGTEMPLATE)LockResource(hrRes);
}
-----------------------------------------------------------
Header file of CLanguageTools -
<------------------------------------------------------
// LanguageTools.h: interface for the CLanguageTools class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_LANGUAGETOOLS_H__INCLUDED_)
#define AFX_LANGUAGETOOLS_H__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
//////////////////////////////////////////////////////////////////////////////////////////
// Êëàññ CLanguageTools äëÿ ðàáîòû ñ ðàçëè÷íûìè ÿçûêàìè -
class CLanguageTools
{
public:
CLanguageTools();
virtual ~CLanguageTools();
HINSTANCE FindResourceHandleLang(LPCTSTR lpszName, LPCTSTR lpszType, WORD wLanguage);
HGLOBAL LoadResourceLang(LPCTSTR resType, DWORD resID, WORD wLanguage);
BOOL LoadString(CString& str, UINT nID, WORD wLanguage);
BOOL LoadMenu(CMenu* pMenu, UINT nID, WORD wLanguage);
BOOL LoadToolBar(CToolBar* pToolbar, UINT nID, WORD wLanguage);
private:
int LoadStringLang(UINT nID, LPTSTR lpszBuf, UINT nMaxBuf, WORD wLanguage);
};
#endif // !defined(AFX_LANGUAGETOOLS_H__INCLUDED_)
--------------------------------------------------------------------------->
and .cpp -
<--------------------------------------
// LanguageTools.cpp: implementation of the CLanguageTools class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "LanguageTools.h"
#include "C:\Program Files\Microsoft Visual Studio\VC98\MFC\SRC\afximpl.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
#ifdef _UNICODE
#define CHAR_FUDGE 1 // one TCHAR unused is good enough
#else
#define CHAR_FUDGE 2 // two BYTES unused for case of DBC last char
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CLanguageTools::CLanguageTools()
{
}
CLanguageTools::~CLanguageTools()
{
}
HINSTANCE CLanguageTools::FindResourceHandleLang(LPCTSTR lpszName, LPCTSTR lpszType, WORD wLanguage)
{
ASSERT(lpszName != NULL);
ASSERT(lpszType != NULL);
HINSTANCE hInst;
// first check the main module state
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
if (!pModuleState->m_bSystem)
{
hInst = AfxGetResourceHandle();
if (::FindResourceEx(hInst, lpszType, lpszName, wLanguage) != NULL)
return hInst;
}
// check for non-system DLLs in proper order
AfxLockGlobals(CRIT_DYNLINKLIST);
for (CDynLinkLibrary* pDLL = pModuleState->m_libraryList; pDLL != NULL;
pDLL = pDLL->m_pNextDLL)
{
if (!pDLL->m_bSystem && pDLL->m_hResource != NULL &&
::FindResourceEx(pDLL->m_hResource, lpszType, lpszName, wLanguage) != NULL)
{
// found it in a DLL
AfxUnlockGlobals(CRIT_DYNLINKLIST);
return pDLL->m_hResource;
}
}
AfxUnlockGlobals(CRIT_DYNLINKLIST);
// check language specific resource next
hInst = pModuleState->m_appLangDLL;
if (hInst != NULL && ::FindResourceEx(hInst, lpszType, lpszName, wLanguage) != NULL)
return hInst;
// check the main system module state
if (pModuleState->m_bSystem)
{
hInst = AfxGetResourceHandle();
if (::FindResourceEx(hInst, lpszType, lpszName, wLanguage) != NULL)
return hInst;
}
// check for system DLLs in proper order
AfxLockGlobals(CRIT_DYNLINKLIST);
for (pDLL = pModuleState->m_libraryList; pDLL != NULL; pDLL = pDLL->m_pNextDLL)
{
if (pDLL->m_bSystem && pDLL->m_hResource != NULL &&
::FindResourceEx(pDLL->m_hResource, lpszType, lpszName, wLanguage) != NULL)
{
// found it in a DLL
AfxUnlockGlobals(CRIT_DYNLINKLIST);
return pDLL->m_hResource;
}
}
AfxUnlockGlobals(CRIT_DYNLINKLIST);
// if failed to find resource, return application resource
return AfxGetResourceHandle();
}
HGLOBAL CLanguageTools::LoadResourceLang(LPCTSTR resType, DWORD resID, WORD wLanguage)
{
HINSTANCE hInstance = FindResourceHandleLang(MAKEINTRESOURCE(resID), resType, wLanguage);
HRSRC hrRes = FindResourceEx(hInstance, resType, MAKEINTRESOURCE(resID), wLanguage);
if (!hrRes)
hrRes = FindResourceEx(hInstance, resType, MAKEINTRESOURCE(resID), MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US));
return LoadResource(hInstance, hrRes);
}
int CLanguageTools::LoadStringLang(UINT nID, LPTSTR lpszBuf, UINT nMaxBuf, WORD wLanguage)
{
LPCWSTR str = (LPCWSTR)LoadResourceLang(RT_STRING, (nID >> 4) + 1, wLanguage);
if (!str)
return 0;
for (WORD strPos = 0; strPos < (nID & 0x000F); strPos++)
str += *str + 1;
if (!nMaxBuf)
return *str;
if (!lpszBuf)
return 0;
#ifdef _UNICODE
lstrcpyn(lpszBuf, str + 1, min(nMaxBuf, *str + 1));
#else
WideCharToMultiByte(CP_ACP, 0, str + 1, *str + 1, lpszBuf, nMaxBuf, NULL, NULL);
#endif
lpszBuf[min(nMaxBuf - 1, *str)] = TEXT('\0');
return min(nMaxBuf, wcslen(str));
// return min(nMaxBuf, *str + 1);
}
BOOL CLanguageTools::LoadString(CString& str, UINT nID, WORD wLanguage)
{
// try fixed buffer first (to avoid wasting space in the heap)
TCHAR szTemp[256];
int nLen = LoadStringLang(nID, szTemp, _countof(szTemp), wLanguage);
if (_countof(szTemp) - nLen > CHAR_FUDGE)
{
str = szTemp;
return nLen > 0;
}
// try buffer size of 512, then larger size until entire string is retrieved
int nSize = 256;
do
{
nSize += 256;
nLen = LoadStringLang(nID, str.GetBuffer(nSize - 1), nSize, wLanguage);
}
while (nSize - nLen <= CHAR_FUDGE);
str.ReleaseBuffer();
return nLen > 0;
}
BOOL CLanguageTools::LoadMenu(CMenu* pMenu, UINT nID, WORD wLanguage)
{
ASSERT(pMenu);
if (!pMenu)
return FALSE;
return pMenu->LoadMenuIndirect(LoadResourceLang(RT_MENU, nID, wLanguage));
}
struct CToolBarData
{
WORD wVersion;
WORD wWidth;
WORD wHeight;
WORD wItemCount;
//WORD aItems[wItemCount]
WORD* items()
{ return (WORD*)(this+1); }
};
BOOL CLanguageTools::LoadToolBar(CToolBar* pToolbar, UINT nID, WORD wLanguage)
{
ASSERT(pToolbar);
LPCTSTR lpszResourceName = MAKEINTRESOURCE(nID);
// determine location of the bitmap in resource fork
HINSTANCE hInst = FindResourceHandleLang(lpszResourceName, RT_TOOLBAR, wLanguage);
HRSRC hRsrc = ::FindResourceEx(hInst, RT_TOOLBAR, lpszResourceName, wLanguage);
if (hRsrc == NULL)
return FALSE;
HGLOBAL hGlobal = LoadResource(hInst, hRsrc);
if (hGlobal == NULL)
return FALSE;
CToolBarData* pData = (CToolBarData*)LockResource(hGlobal);
if (pData == NULL)
return FALSE;
ASSERT(pData->wVersion == 1);
UINT* pItems = new UINT[pData->wItemCount];
for (int i = 0; i < pData->wItemCount; i++)
pItems[i] = pData->items()[i];
BOOL bResult = pToolbar->SetButtons(pItems, pData->wItemCount);
delete[] pItems;
if (bResult)
{
// set new sizes of the buttons
CSize sizeImage(pData->wWidth, pData->wHeight);
CSize sizeButton(pData->wWidth + 7, pData->wHeight + 7);
pToolbar->SetSizes(sizeButton, sizeImage);
// load bitmap now that sizes are known by the toolbar control
bResult = pToolbar->LoadBitmap(lpszResourceName);
}
UnlockResource(hGlobal);
FreeResource(hGlobal);
return bResult;
}
|
|
|
|
|
We typically don't have Cyrillic support and Japanese support on our English language systems. It is not in our favor to try to put all our languages into a single DLL, since then we can't even edit our own language's resources. The only advantage to not loading the proper DLL and just using AfxSetResourceHAndle is if you DO want to put all languages into a single resource DLL. Otherwise, MFC will always try to find the resource using the handle set by AfxSetResourceHandle and you will pull up the correct language on the first try. All that without needing to code special classes into your application every time you want to pull in a string or other resource. To me, the advantage of having all languages in a single resource DLL far outweighs the effort to use special classes every time a resource is required.
What might be a better approach, and I leave it as an exercise to another, is to see if you can link in your own AfxFindResourceHandle ahead of the 'real' one. Since your 'override' will be called instead, then you can return language resources from whatever DLL you want, instead of needing to have special resource accessor functions spread all over your code. It might also be necesary to 'hijack' the AfxFindResourceHandle within the MFC DLL in your appclication, so the base classes within the MFC DLL will call your function instead of the one in the DLL.
|
|
|
|
|
Idea to put all resorce into one DLL was follow.
I developed engineering soft.
I have main app using for preprocessor and postprocessor. All dialog, data structures and classes i put into _interface_ DLL, because many dialogs i use in another programs. Also i have a several library of calculational core, and several library of Graph plotters, dxf inport-export and more other.
Next step after creation of this sortware was translation it to english.
(It's my own opinion that engineering language is english). Thus my program must contain only 2 languages at max.
Main app, interface DLL and graph plotter DLL is 3 independent projects because this DLLs are also used in other projects. Thus i became in fact that i must have 6 language resorce DLL (2 for app, 2 for interface DLL and 2 for graph DLL). Instead of this i only create resource DLL for main app and interface DLL, and for graph it was experiment to put all resources in one DLL and use patern singleton to identify current program language and other program state.
So i think then u have a several language (2 - 4), it's wright way to put all in one "box". Besides u can use programs such RC-wintrans to add new languages.
>>>We typically don't have Cyrillic support and Japanese support on our English language systems .........
U can set system language to this and work with resources or convert your .RC file to UNICODE - after it will not opened in MSVC, but all will be OK.
Using AfxSetResourceHAndle is needless because AfxFindResorceHandle (and my modification of it) find resource in ALL DLL (system and your own) loaded to app.
About modification MFC - i think it's simply to use my class instead of modication. Apropos about MFC own resource - i also put it all into my resource DLL and AfxFindResorceHandle find my copy first.
Also it's very laborious work to add new resorce or modifiing then they are in separate resource DLL. U must look that resource ID was identifical (almost thrusty is to copy resource.h). When you add new dialog as example in one resource DLL, you must copy its template to all other. And there are a lot of other troubles. Whereas when u put it all in one DLL, to add new languages or to make modifications u only need to insert copy of resource in another languages.
|
|
|
|
|
Hello,
I found a little error in your headerfile. If you include it in multiple translation units, you get the error that the following symbols are defined multiple times:
const string LNG_PATH = "lng";
const string LNG_DEFAULT = "english";
You can fix this by placing the those lines in your .cpp file and change them in the header file to:
extern const string LNG_PATH;
extern const string LNG_DEFAULT;
Besides that, the article is great!
Behind every greak black man...
... is the police. - Conspiracy brother
Blog[^]
|
|
|
|
|
Hi,
Thanks for the tip! I will update the source code in the next version!
Geert
Want to spread the newest version of your software automatically? Use Updater!
Visit my website: http://geert.yoki.org
|
|
|
|
|
Such as toolbars and icons.
BTW
It seems haven't shlwapi.lib in my computer so I describe the necessary function in class.
BOOL CLanguage::PathFileExists(CString S)
{ CFileFind FF;
return FF.FindFile(S);
}
Dim.
|
|
|
|
|
Hi, maybe I will implement this in a next version.
Today, I have implemented an example application, a class using MFC and one without MFC. Tomorrow, I will upload this new version.
dim13 wrote:
BTW
It seems haven't shlwapi.lib in my computer so I describe the necessary function in class.
BOOL CLanguage::PathFileExists(CString S)
{ CFileFind FF;
return FF.FindFile(S);
}
Maybe it's a good idea to implement such a function in the class. I will take a look at it (because CFileFind is an MFC class, and I also have a non-MFC class).
Geert
Want to spread the newest version of your software automatically? Use Updater!
Visit my website: http://geert.yoki.org
|
|
|
|
|
You can also detect if a file exists using pure WIN32 API by this test:
if( (DWORD)-1 == GetFileAttributes(szFilePath) ){<br />
} else {<br />
}
I never use the CFileFind class.
|
|
|
|
|
This is a really good function! Now it will not be needed anymore to link to special libraries.
Thanks!
Geert
Want to spread the newest version of your software automatically? Use Updater!
Visit my website: http://geert.yoki.org
|
|
|
|
|
Great works, But can you do it without MFC?
|
|
|
|
|
I will release a version as soon as possible which will work exactly as this class, but without MFC (so win32 users can use it too!).
Geert
Want to spread the newest version of your software automatically? Use Updater!
Visit my website: http://geert.yoki.org
|
|
|
|
|
|
How to make it to support Chinese? Is there a Chinese.dll
|
|
|
|
|
You can do this yourself by just loading your own dll. The dll's in the list are the dll's that can be automatically loaded.
Geert
Want to spread the newest version of your software automatically? Use Updater!
Visit my website: http://geert.yoki.org
|
|
|
|
|
It only displayed '?????' for chinese.
I created a chinese.dll. Only modified the IDS value with 5 Chinese word.
Here is what I did in your example code:
in the InitLanguagesAvailable():
I added:
m_arrLanguages[m_iLanguageCount].arrLanguages[m_arrLanguages[m_iLanguageCount].iLanguageCount++] = 0x0804; // Chinese
m_arrLanguages[m_iLanguageCount++].sDll = "chinese";
In OnBtnLoadLanguage():
sDLLPath += "\\lng\\chinese.dll";
m_pLanguage->LoadLanguage(sDLLPath, "chinese");
I also changed my computer Regional and Language to Chinese.
Many thanks.
mn_guy
|
|
|
|
|