此隨筆中主要內容來自http://blog.csdn.net/china_hxx/article/details/10066655,原出處不詳。
以下內容基于VC 6.0。要實現界面多語言化,必須要先配置項目使其支持Unicode編碼,文章《VC下的Unicode編程》 對此有詳細介紹。
首先創建一個基于MFC的工程,在選擇語言時選擇 中文[簡體,中國]。
項目框架選擇對話框、單文檔和多文檔都可以,這里就選個基于對話框的吧,然后立即修該項目屬性使其支持Unicode。
MFC已經為我們加入了一些資源,包括2個對話框、1個String Table等。為了節省空間修改了對話框的大小。
現在項目中的資源情況如下:
然后再新建一個DLL項目,為了方便管理可以將其添加到當前工作空間中。
立即修改此DLL項目使其支持Unicode,然后設置項目依賴性(Project->Dependencies…) ,資源DLL要在exe項目之前編譯:
為了將生成的資源DLL自動拷貝到exe項目執行目錄下,還需要修改DLL項目的Post-build Step:
然后將主項目目錄下的res文件夾、resource.h、TestMultiLang.rc文件拷貝到ResENG項目目錄下,res文件夾和resource.h直接替換,將ResENG.rc刪除后再將TestMultiLang.rc重命名為ResENG.rc。
切換到資源視圖就會發現這兩個項目的資源內容是一樣的了,將ResENG項目的資源更改為英文如下:
為了讓程序在啟動的時候加載英文語言資源,需要在CTestMultiLangApp::InitInstance()函數中Dlg創建之前添加如下語句:
HINSTANCE hLanguageDll = AfxLoadLibrary(_T("ResENG")); if (hLanguageDll) AfxSetResourceHandle(hLanguageDll);
最后重新編譯TestMultiLang項目,運行就會發現對話框已經是英文界面了 :)
動態實現語言切換
以上程序僅為示例,為了能使程序自動選擇合適的語言,還需要做許多工作。比如要在程序中添加程序語言切換菜單或語言選擇下拉列表框,并將用戶喜好保存在ini配置文件中,然后在程序啟動時自動讀取此ini文件加載相應的資源DLL;如果用戶未設置語言,則默認根據操作系統語言加載合適資源,如果不存在針對此語言的資源DLL,就使用最國際化的語言——英語。
在stdafx.h中做如下定義:
#define CHINESE 0 #define ENGLISH 1
在CTestMultiLangApp類中添加private變量 int m_Lang, 用來保存當前語言類型是CHINESE,還是ENGLISH。
在CTestMultiLangApp類中添加public方法GetLang用于返回當前語言類型。
int CTestMultiLangApp::GetLang(void) { return m_Lang; }
根據配置加載相應資源的DLL包裝成一個函數如下:
void CTestMultiLangApp::LoadLanguage(void) { CString strDLL; HINSTANCE hLanguageDll, hLanguageNow; //保存本身的資源句柄 static HINSTANCE hOriginalHandle = ::AfxGetResourceHandle(); //讀取ini配置文件 CString strFileName = _T("Language.ini"); if (PathFileExists(strFileName)) { int lang = 0; CFile file; file.Open(strFileName, CFile::modeRead | CFile::typeBinary); file.Read(&lang, sizeof(lang)); file.Close(); m_Lang = lang; } //根據用戶喜好來設置 if (m_Lang == ENGLISH)//英文 hLanguageDll = ::AfxLoadLibrary(_T("ResENG")); else if (m_Lang == CHINESE)//中文 hLanguageDll = hOriginalHandle; else { //用戶未指定,則根據系統選擇合適語言,默認為英文 WORD wLangPID = PRIMARYLANGID(GetSystemDefaultLangID()); if (wLangPID == LANG_CHINESE) hLanguageDll = hOriginalHandle; else if (wLangPID == LANG_ENGLISH) hLanguageDll = ::AfxLoadLibrary(_T("ResENG")); else hLanguageDll = ::AfxLoadLibrary(_T("ResENG")); } //保存已加載的資源DLL句柄 hLanguageNow = ::AfxGetResourceHandle(); //加載新的資源DLL if(hLanguageDll) ::AfxSetResourceHandle(hLanguageDll); //釋放之前加載的資源DLL if (hLanguageNow != hOriginalHandle) FreeLibrary(hLanguageNow); }
因為在切換界面語言的過程中需要頻繁地銷毀和創建對話框,所以我們也把創建對話框的代碼包裝成一個函數:
void CTestMultiLangApp::OpenWindow(void) { CTestMultiLangDlg dlg; m_pMainWnd = &dlg; dlg.DoModal(); }
現在在InitInstance()函數里簡單得調用一下這兩個函數就可以了:
BOOL CTestMultiLangApp::InitInstance() { AfxEnableControlContainer(); #ifdef _AFXDLL Enable3dControls(); // Call this when using MFC in a shared DLL #else Enable3dControlsStatic(); // Call this when linking to MFC statically #endif LoadLanguage(); if (!OpenWindow()) return FALSE; return FALSE; }
然后要實現用戶界面的語言選擇消息響應函數。該函數要將用戶的選擇保存到ini文件里,銷毀當前窗口,然后再重新加載資源并創建新的窗口:
void CTestMultiLangDlg::OnBnClickedButton1() { // TODO: 在此添加控件通知處理程序代碼 CTestMultiLangApp*pApp = (CTestMultiLangApp*)AfxGetApp(); int old_Lang = pApp->GetLang(); int new_Lang = 0; if(old_Lang == ENGLISH) new_Lang = CHINESE; else new_Lang = ENGLISH; CFile file; file.Open(_T("Language.ini"), CFile::modeWrite | CFile::modeCreate | CFile::typeBinary); file.Write(&new_Lang, sizeof(new_Lang)); file.Close(); //銷毀當前窗口 pApp->m_pMainWnd = NULL; this->DestroyWindow(); //創建新的窗口 pApp->LoadLanguage(); pApp->OpenWindow(); }
上面這段代碼中pApp->m_pMainWnd = NULL這一句是關鍵,它切斷了銷毀當前程序消息向上的路由,因此進程不會被銷毀,才有了機會重新創建新的窗口。如果項目是基于單文檔/多文檔的程序的話,還要加一句pApp->m_pDocManager = NULL才可以。
重新編譯項目,現在語言就可以動態切換了。
繼續添加其他語言
如果項目不僅要支持中英文這兩種語言,比如還需要支持德語,這里介紹兩種方法來實現。
第一種方法顯而易見————新建一個資源DLL項目。理論上這是可行的,但是有個棘手的問題:如果需要支持的語言很多,那么對程序資源的任何更改都需要同步更新到其他所有資源項目!
第二種方法是借用工具,由一個資源DLL制作出其他各語言版本的資源DLL。這個工具軟件會把資源DLL中的字符串、對話框、菜單項等資源提取出來并保存到一個po文件里,之后使用poedit就可以進行翻譯了。翻譯完成后再根據此po文件和原資源DLL生成新的資源DLL。
工具軟件截圖如下:
![]() |
不含病毒。www.avast.com |