多语言示例
本地化与国际化
先解释两个名词,本地化 和国际化
- 本地化 在软件工程中一般将外文界面的软件翻译为本土语言称为本地化,在中国也叫汉化。
- 国际化 与本地化相反,一般将本土语言翻译成外文称为国际化
其实二者没什么本质区别,在软件工程上都是实现软件的多语言支持。
多语言要求
对c++程序,实现多语言的基础是资源化,所有需要实现多语言的字段,都应该存在于资源中而不应该以明文的方式写在代码中。
例如:
acutPrintf(_T("测试资源")); // 错误 // 正确的示范: CString str; str.LoadString(IDS_STRING1); acutPrintf(str);
一. 资源副本
实现多语言的方案有很多,我们先说一种最简单的实现方案,这种方案的实现方式是分别编译不同语言版本的程序。这种方案实现简单,我们就以一个简单的mfc程序为例,代码在 ML_Demo_MultiVersion
里。
-
创建一个简单的MFC 对话框程序
-
创建不同的语言配置
-
为资源创建不同语言的副本
-
在资源ID上右键属性,为不同副本设置条件
注意,它们ID相同,但语言不同,条件也不相同(语言相同其实也没有关系)
原有语言也要设置条件,
Condition
-
修改不同语言对应的资源内容
-
在项目上右键属性,修改资源配置
注意默认配置也要修改
-
现在分别运行两个配置可以看到效果
Debug_enu:
Debug:
-
总结:
这种方式实现的多语言实现简单,只需要新增加配置,创建已有资源的副本就可以。但维护困难,适合已经完成的项目。
这种方式多语言的资源存在于同一个
rc
文件中,如果要对资源进行修改,必须同时修改多处,很容易遗漏,也没有太好的办法使用各种本地化工具进行维护。
二、翻译多个资源文件
资源副本存在于同一个rc
文件中,不方便管理,我们的第二个方案,是使用不同的rc
文件来管理资源。本示例代码在 MLDemo_MultiRc
文件夹中。我们还是以mfc对话框应用程序为例。
-
重复方案一中的步骤 1和2,创建
debug_enu
配置。 -
复制资源文件
在工程文件夹下,找到
MLDemoMultiRc.rc
,复制到同名文件夹下的MLDemoMultiRc_en.rc
。注意,在同文件夹中复制,它可以和原rc文件共用同一个
resource.h
。 -
将新的资源文件加入工程
在资源文件夹上右键添加现有项,添加新的资源文件。
-
修改资源
在资源界面下选择英文资源文件,修改其中的资源使用的字体
-
为不同配置选择资源
此时,编译项目会提示资源重复:
我们需要为不同的配置配置字体,在解决方案资源管理器
视图分别选择两个rc文件,点击属性,为不同的配置排除不正确的字体:
注意这里是排除而不是选择,所以要为中文配置排除其它语言的资源,为英文配置排除中文资源。
到目前为止,所有操作都是手动完成,与方案一并无区别。也存在当资源更改好维护不便的问题。但是由于不同语言的资源存在于不同的文件,我们可以借助工具。
三、 使用工具翻译多个资源文件
多语言工具有很多,我们在这里使用经典的 Lingobit Localizer 。在方案2的基础上优化:
- 打开
Localizer
软件,新建项目
- 选择本地化的原始语言,我们是中文,目标语言是英文:
- 选择要本地化的文件
MLDemoMultiRc.rc
:
- 完成后可以看到资源文件中的所有条目:
- 我们忽略代码条目,将中文条目翻译成英文:
- 配置翻译文件目标路径:
- 生成目标文件:
点击创建或运行,选择要输出的语言。
此时可以看到已经生成了MLDemoMultiRc_en.rc
文件
- 重新编译运行
- 当资源发生改变时,比如添加一个字符串
在Localizer中点击扫描:
可以看到新添加的资源已经被导入进来了。
-
总结
使用工具可以方便的管理翻译资源,避免重复劳动,也避免了难维护的缺点。这种方式可以为不同的语言管理不同的资源。一般已经可以满足我们的需求。
四、资源dll
以上三个方案,都是直接将代码编译为不同的语言版本,需要分别向客户提供分发包,或者将程序分为多个语言目录,无法实现同一个程序加载、切换多个不同的语言版本。
要实现在同一个编译版本中实现多语言,就需要使用资源dll。核心思想是将资源编译为单独的dll。
仍然以mfc对话框工程类型为例,资源dll的示例代码存放在 MLDemo_ResDll
中。
-
创建资源dll项目
在解决方案中添加新建项目:
选择动态链接库项目,项目名称与语言一致,这里是
zh-TW
和en
并删除所有代码文件
-
使用Localizer翻译资源
翻译后,资源文件刚好生成在两个对应的工程目录中
将其添加到对应工程中
此时编译工程会报错
这是因为
resource.h
在主工程目录中,我们在包含目录中添加该目录注意,是资源选项
还要设置dll入口点为无,否则会报错
2>LINK : error LNK2001: 无法解析的外部符号 __DllMainCRTStartup@12 2>W:\Work\gits\localizationdemo\MLDemo_ResDll\Debug\en.dll : fatal error LNK1120: 1 个无法解析的外部命令
-
配置项目生成路径,最终生成目录如下所示:
-- MLDemo_ResDll.exe -- zh-TW ----- ML_Demo_ResDll.dll -- en ----- ML_Demo_ResDll.dll
到现在为止,资源dll已经创建,下面应该修改代码来为不同的语言配置不同的资源
- 加载资源dll
HINSTANCE _hResInstance = nullptr; BOOL LoadResDll(LPCTSTR szLan) { if (nullptr != _hResInstance) { ::FreeLibrary(_hResInstance); _hResInstance = nullptr; } CString strExePath; AfxGetModuleFileName(AfxGetInstanceHandle(), strExePath); TCHAR szDrive[_MAX_DRIVE]; TCHAR szDir[_MAX_DIR]; TCHAR szFName[_MAX_FNAME]; TCHAR szExt[_MAX_EXT]; _tsplitpath_s(strExePath, szDrive, szDir, szFName, szExt); CString strDir; strDir = szDrive; strDir += szDir; strDir += szLan; CString strResDll = strDir + _T("\\") + szFName + _T(".dll"); _hResInstance = ::LoadLibraryEx(strResDll, nullptr, LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE); }
为了测试,我们简单的依次弹出不同语言的对话框:
LoadResDll(_T("chs")); // 其实没有这种语言,会加载失败 if (nullptr != _hResInstance) { AfxSetResourceHandle(_hResInstance); } CMLDemoResDllDlg dlg; INT_PTR nResponse = dlg.DoModal(); LoadResDll(_T("en")); // 其实没有这种语言,会加载失败 if (nullptr != _hResInstance) { AfxSetResourceHandle(_hResInstance); } nResponse = dlg.DoModal(); LoadResDll(_T("zh-TW")); if (nullptr != _hResInstance) { AfxSetResourceHandle(_hResInstance); } nResponse = dlg.DoModal();
运行可以看到,三个语言版本的界面会依次弹出。
我们可以通过配置、注册表等方式在程序运行时,用于控制当前语言版本。
来源:https://www.cnblogs.com/xzh1993/p/12424727.html