|
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
|
|
|
|
|
Did you also changed the resource dll language to chinese?
It is some time ago I wrote this, and I am afraid unicode is not supported. You can simply support unicode by using the _T() macro for each string.
Best regards,
|
|
|
|
|
Hi Geert,
if you use MFC, why don't you use AfxSetResourceHandle to load a different language. If you use CString, CDialog and so on, you haven't to change anything else in your code.
Steffen
|
|
|
|
|
That's a good point!
Does this even work when I have seperate dll's for each language? And will the resources not found in the language dll be loaded from the current application?
Geert
Want to spread the newest version of your software automatically? Use Updater!
Visit my website: http://geert.yoki.org
|
|
|
|
|
Wouldn't that require you to create a resource dll of all your resources? This doesn't scale very well if you want to change your program and modify a few dialogs..
I could be wrong, but MSDN says that all the resources will be taken from that source..
Behind every greak black man...
... is the police. - Conspiracy brother
Blog[^]
|
|
|
|
|
You can load a DLL containing only resources and use the AfxSetResourceHandle.
It is possible to redirect all existing resource editing to a resource file separate from the primary EXE resource file.
I am pretty sure, after using AfxSetResourceHandle, that if a resource is not found in the DLL, it will try to find it in the EXE and various other places - see MFC source for AfxFindResourceHandle.
I am not sure about the scaling issue you bring up. Of course, if you have many resource modules, and you change resources, there is work to do. So, what's the point?
|
|
|
|
|