程序实现多国语言的动态切换解决方案(转)

时间:2022-12-27 03:01:43

以下部分均是参考自前辈写的示例程序的说明文档:

程序实现多国语言的动态切换解决方案

实现思想:传统的做法是把所有的资源都放到动态库中,一种语言一个动态库,程序运行的时候通过加载不同的动态库来实现多语言功能。这样做的缺点是不能动态切换语言,如果更换语言后必须重新启动软件。当然,没有人会需要经常的切换语言玩儿,但是采用动态库的方法,如果程序需要修改资源的话,就要更新所有的动态库,这是一个非常枯燥而且容易出现疏漏的工作。

我的方法是把所有用到的字符串都放到文件中,一种语言一个文件,根据选择的语言到对应的文件中去加载字符串。这样不但可以动态切换语言,而且用户可以根据需要自己添加新的语言。

具体实现:
	1、程序启动时检查所选择的语言,确定该语言文件,保存该文件路径

	2、提供一个全局函数,如 g_LoadString(CString szID),根据提供的字符串ID返回
	其内容,具体是哪个语言的在函数中判断,这样在程序中只要提供一个字符串ID就可以自动
	加载不同语言的文字了。		

	3、如何在程序中使用:
	原来代码:
	CString str;
	str = "语言";


	改动后:
	CString str;
	str = g_LoadString("IDS_LANGUAGE");

	4、对话框中如何实现
	如果像在程序中使用一样,每一个字符串都要去加载一次的话,如果对话框比较多,工作量可就太大了。
	所以我提供了一个函数g_SetDialogStrings(CDialog *pDlg,UINT uDlgID),每个对话框在初始化的时候调	用该函数,传递对话框的指针,我在函数中循环枚举所有的子控件,逐个设置文字。这样就可以省去很多工作
	
	注意:由于静态文本(CStatic)默认的ID是IDC_STATIC,值都是65535,无法区分,所以在需要改变其文字的CStatic的ID要改一改,不能用默认的

	5、语言文件样例:
		中文版:
			[Setting]
			Language=Chinese

			[String]
			IDS_MENU_FILE=文件
			IDS_MENU_FILE_NEW=新建(&N)
			IDS_MENU_FILE_OPEN=打开(&O)
			IDS_MENU_FILE_CLOSE=关闭(&C)
			IDS_MENU_FILE_EXIT=退出(&E)
		英文版:
			[Setting]
			Language=English


			[String]
			IDS_MENU_FILE=File
			IDS_MENU_FILE_NEW=&New
			IDS_MENU_FILE_OPEN=&Open
			IDS_MENU_FILE_CLOSE=&Close
			IDS_MENU_FILE_EXIT=&Exit
经过仔细阅读源码后发现实际上就是使用了两个函数

/*********************************************************************
* 函数名称:CPIDDlg::LoadLanguage
* 说明:	装入或者设置语言 如果 szLangSel为空则装入语言,否则设置语言
* 作者: Geng 
*********************************************************************/
void CPIDDlg::LoadLanguage(CString szLangSel)
{
	//查找语言
	CString szSection = "Setting";
	CString szKey = "Language",szLang;
	DWORD dwSize = 1000;
	
	if(!szLangSel.IsEmpty())	//保存语言
	{
		szLang = szLangSel;
	}
	else	//读取语言设置
	{
		//获取设置的语言
		GetPrivateProfileString(szSection,szKey,"English",szLang.GetBuffer(dwSize),dwSize,g_szSettingPath);
		szLang.ReleaseBuffer();
	}
	
	//查找是否存在
	CFileFind find;
	bool bRet = find.FindFile(g_szCurPath + "PIDLanguage\\*.ini");
	bool bFound = false;
	CString szEng = "";
	while(bRet)
	{
		bRet = find.FindNextFile();
		if(find.IsDots() || find.IsDirectory()) continue;
		
		CString szValue;
		CString szFilePath = find.GetFilePath();
		if(GetPrivateProfileString(szSection,szKey,"",szValue.GetBuffer(dwSize),
			dwSize,szFilePath) != 0)
		{
			szValue.ReleaseBuffer();
			if(szValue == szLang)
			{
				g_szLanguagePath = szFilePath;
				bFound = true;
				break;
			}
		}
	}
	find.Close();
	
	/*
	//未找到设定的语言
	if(!bFound)
	{
		int nLangID[2] = {IDR_LANG_ENG,IDR_LANG_CH};
		CString szFileName[2] = {"\\Multi_eng.ini","\\Multi_ch.ini"};
		CString szPath = g_szCurPath + "Language";
		CreateDirectory(szPath,NULL);
		for(int i = 0;i < 2;i++)
		{
			MakeResource(nLangID[i],szPath + szFileName[i]);
		}
		//默认使用英文
		g_szLanguagePath = szPath + szFileName[0];
		szLang = "English";
	}
	*/
	
	
	//保存语言设置
	WritePrivateProfileString(szSection,szKey,szLang,g_szSettingPath);
}


/*********************************************************************
* 函数名称:g_SetDialogStrings(CDialog *pDlg,UINT uDlgID)
* 说明:	当对话框运行时获取其所有可得到的字符串,并保存到语言文件中
每个控件用对话框ID和控件ID唯一标识

  * 入口参数:
  * CDialog *pDlg -- 对话框的指针
  *  UINT uDlgID -- 该对话框的ID
  * 作者: Geng 
*********************************************************************/
void CPIDDlg::g_SetDialogStrings(CDialog *pDlg, UINT uDlgID)
{
	CString szSection = "String";
	CString szKey,szText;
	bool bSetText = 1;	// 1:从文件读,设置对话框
	// 0:从对话框读,保存到文件
	
	if(bSetText)	// 1:从文件读,设置对话框
	{
		CString szDefault = "";
		DWORD dwSize = 1000;
		char* pData = (char*)malloc(dwSize);
		
		//读对话框标题
		szKey.Format("IDD%d_Title",uDlgID);
		if(GetPrivateProfileString(szSection,szKey,szDefault,
			pData,dwSize,g_szLanguagePath) != 0)
		{
			pDlg->SetWindowText(pData);
		}
		//写入各个子控件的标题文字
		CWnd* pWnd = pDlg->GetWindow(GW_CHILD);
		while(pWnd != NULL)
		{
			szKey.Format("IDD%d_%d",uDlgID,pWnd->GetDlgCtrlID());
			if(GetPrivateProfileString(szSection,szKey,szDefault,
				pData,dwSize,g_szLanguagePath) != 0)
			{
				pWnd->SetWindowText(pData);
			}
			
			pWnd = pWnd->GetWindow(GW_HWNDNEXT);
		}
		
		//释放内存
		free(pData);
	}
	else	//0:从对话框读,保存到文件
	{
		//写入对话框标题
		szKey.Format("IDD%d_Title",uDlgID);
		pDlg->GetWindowText(szText);
		WritePrivateProfileString(szSection,szKey,szText,g_szLanguagePath);
		
		//写入各个子控件的标题文字
		CWnd* pWnd = pDlg->GetWindow(GW_CHILD);
		while(pWnd != NULL)
		{
			szKey.Format("IDD%d_%d",uDlgID,pWnd->GetDlgCtrlID());
			pWnd->GetWindowText(szText);
			WritePrivateProfileString(szSection,szKey,szText,g_szLanguagePath);
			
			pWnd = pWnd->GetWindow(GW_HWNDNEXT);
		}
	}
}

void CPIDDlg::ChangeLanguage(CString language)
{
	//获取当前路径
	CString szCurPath("");
	GetModuleFileName(NULL,szCurPath.GetBuffer(MAX_PATH),MAX_PATH);	
	szCurPath.ReleaseBuffer();
	g_szCurPath = szCurPath.Left(szCurPath.ReverseFind('\\') + 1);
	
	LoadLanguage(language);
	g_SetDialogStrings(this, IDD_DLG_PID);
	g_SetDialogStrings(&m_DlgI, IDD_DLG_I);
	g_SetDialogStrings(&m_DlgII, IDD_DLG_II);
	g_SetDialogStrings(&m_DlgIII, IDD_DLG_III);
}	
   

 
 

CDlgIm_DlgI;
CDlgII m_DlgII;
CDlgIII m_DlgIII;
初始化对话框的时候就可以调用
ChangeLanguage(CString language)
LoadLanguage(language);实现字符串的加载
g_SetDialogStrings(CDialog *pDlg, UINT uDlgID) 实现替换


关键点:准备好配置文件
配置文件的对应举例如下
其中102是高速计数器功能向导对话框在resource.h中对应的数字,“IDD102_1002=生成”表示102对应的对话框中在resource.h对应数字为1002的控件
IDD102_Title = 高速计数器功能向导
IDD102_3=上一步
IDD102_4=下一步
IDD102_1002=生成
IDD102_2 = 取消

IDD129_Title = 
IDD129_1003=模式选择
IDD129_1004=简介:\n此向导
共享了一个小demo到pudn上:http://www.pudn.com/downloads523/sourcecode/windows/dialog/detail2168437.html