双语或多语成品软件的编制
吕维·CPCW
只用一套源代码就可以方便地支持多种文字和多个地域,那么这个软件就可以方便地被翻译成本地版本,这个过程叫做地域化(Localization)。怎样才能不修改任何源代码就使之能动态地转换到不同的地域资源上呢?那就是使用Windows的程序特性之一?-资源。把在软件中用到的可见资源维系在一个资源DLL(Dynamic Link Library动态链接库)中,就能使地域化很容易地被实现,因为它把具体的文字组件单独提取放在一个文件中,所以,一个可执行文件就可以装载几种不同的语言文字,并且选择用子程序来装载适合的文字DLL。创建一个CString对象的实例,并用该字串的资源标识符(string ID)调用LoadString,即可避免繁琐的字串编码工作。
在大多数情况下,资源包含在应用程序的单元中,如果调用AfxSetResource Handle,就可以指向另一个不同的单元,示例StateDemo程序就是这样处理的。调用AfxSetResource Handle,从DLL资源中采集软件所需资源,通过替换掉不同语种的DLL资源,程序便可以使用一套完全不同的资源(如String字串、Dialogue对话框、Bmp位图、Menu菜单等)。
初始情况下,作为主执行体的StateDemo不含任何资源。首先,创建一个包含所有英文资源的DLL,在使用这个资源DLL时,应用程序就会以英文形式出现,即软件为英文版;然后,创建中文DLL,再次运行StateDemo程序,程序就以中文形式出现(即使应用程序未被重新编译)。
一.应用程序的创建与编译
1.创建StateDemo.EXE文件(1)创建StateDemo工作区
在Microsoft Visual C++ 5.0开发环境下,单击File | New选项,在创建类型选择框New中选择MFC AppWizard(exe)、Projectname:StateDemo,创建新项目StateDemo工作区,应用程序类型选择Multiple Documents,单击Finish完成工作区的建立。
(2)添加字串资源
这步实际上是在为英文资源DLL添加字串资源,因为后面将把英文资源StateDemo.rc从项目中移走,并把它拷贝到英文资源的项目中,而不是刚建立的StateDemo.EXE文件项目,执行体exe文件会引用这个资源,而字串就在资源DLL中。选择StateDemo项目资源中的String Table,在StringTable中单击右键选择New String,添加如下代码,然后选择菜单中File | Save All选项即完成。
ID:IDS_MINE=300
//主框架IDS_MINE定义为300,即ID的值为300
Caption(标题):Hi! it's my demo vc5 program of English version.
//这是英文版软件中字串的演示
运行结果如图1所示。
图1.运行结果
(3)添加CStateDemo类的数据成员
本程序要动态地装入包含所有资源的DLL,所以必须保存DLL的Handle(句柄),在后面会释放Handle,并在CStateDemoApp类中添加私有数据成员(代码如下)。
private:
HINSTANCE m_hDemoDLL;//资源DLL handle句柄
可以在MS-VC5的DevelopStudio位于左方的Workspace窗口中选择ClassView标签,单击CstateDemoApp类,单击鼠标右键选择Add Member Variable,然后输入Type与Declarition,并选择私有类型。
(4)修改CStateDemoApp::InitInstance函数
应用程序需装入包含所有资源的DLL,在本例程序中,英文资源DLL都命名为Resource.dll。把下面的代码加入初始化函数InitInstance的顶部。
BOOL CStateDemoApp::InitInstance()
{
AfxEnableControlContainer();
//Standard initialization
//If you are not using these features and wish to reduce the size
//of your final executable, you should remove from the following
//the specific initialization routines you do not need.
//调入StateDemo资源DLL.
m_hDemoDLL=AfxLoadLibrary (_T("Resource.dll"));
if(!m_hDemoDLL)
//如果Resource.dll文件Handle不存在
{
AfxMessageBox(_T("I can't load the resource DLL !"));
//没有找到Resource.dll文件时,弹出消息框发出警告:I can't load the resource DLL !;
//警告消息框如图2所示
return FALSE;
}
//告诉应用程序设置资源句柄m_hDemoDLL;
AfxSetResourceHandle(m_hDemoDLL);
}
图2. 警告消息框
(5)使用WizardBar处理CStateDemoApp的ExitInstance函数
在此需要释放前面用AfxLoadLibrary装入的DLL,将下面的代码添加到函数ExitInstance中就可达到此目的。选择菜单View | Class Wizard,在Message Maps的Classname Edit窗口中选择CstateDemoApp,然后在Messages下找到ExitInstance,选择Edit Code并加入以下代码。
int CStateDemoApp::ExitInstance()
{
//TODO: Add your specialized code here and/or call the base class
if(m_hDemoDLL)
AfxFreeLibrary(m_hDemoDLL);//释放DLL资源;
retrun CWinApp::ExitInstance();
}
(6)修改CStateDemoView::OnDraw
在Multiple-Document类型中显示不同语种字串和icon等各种资源,在函数中加入下列代码。
void CStateDemoView::OnDraw(CDC*pDC)
{
CStateDemoDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
pDC->DrawIcon(10,10,AfxGetApp()->LoadIcon(IDR_MAINFRAME));
//从资源DLL中调入图标(ID为IDR_MAINFRAME)并在窗口10,10位置绘制;
CString strMessage;//定义strMessage为Cstring字串类型;
//从resource DLL中显示字串;
strMessage.LoadString(IDS_DEMO);
pDC->TextOut(60,65,strMessage);
}
(7)从StateDemo Project中移走StateDemo.rc
因需要程序中不含有任何资源,只在Resource.dll中含有资源,所以,要在此项目中移走所有资源,并把它们放进Resource.dll中,但不是删除,这就是使用MFC的灵活性。具体操作如下:选择Workspace下的FileView,单击StateDemo.rc,按键盘上Delete键删除,此时所有资源就不在项目中了。
(8)编译StateDemo项目
开始编译项目,单击菜单Build | Execute StateDemo.exe选项,由于此时项目中没有资源,因此会弹出报错消息框"I can't load the resource DLL !",如图2所示,单击OK,不予理会。
现在,StateDemo项目的执行体StateDemo.exe就建成了,它不包含任何资源,而是依靠动态链接库DLL来提供所有资源。
下面来创建中、英文资源DLL,在这个DLL建立之后,把资源DLL拷回到本项目的Debug或Release目录中,就可运行该应用程序了。
2.创建英文资源Resource.dll
(1)创建英文新项目区
在创建类型选择框New中选择MFC AppWizard(dll),创建一个英文新项目区,在Location下为工作区选择目录...\StateDemo\即...\StateDemo\English作为该项目区目录,单击OK,在Step1中选择Regular DLL using MFC DLL,单击OK即可。
(2)删除项目中所有文件
刚才建立的DLL已包含了一些资源,但它的各种文件都不需要,所以在Workspace FileView中选中所有文件,按Delete删除所有文件。
(3)从StateDemo项目中复制英文资源文件
现在要用刚才从StateDemo中移出的英文资源,前面曾把一个资源字串加入到StateDemo.rc文件中,在此,资源字串就起作用了。将...\StateDemo目录下的StateDemo.rc和Resource.h拷贝到...\Statedemo\English目录下。目的是把...\StateDemo\res下的所有资源转移到...\StateDemo\English\res目录下。下面列出需从项目StateDemo中拷贝到项目英文中的所有文件及其注释。
需要拷贝的文件 注释
StateDemo.rc 资源文件
resource.h 资源头文件
res\StateDemo.ico 主程序的图标
res\StateDemo.rc2 用户资源描述文件
res\StateDoc.ico 文挡图标
res\Toolbar.bmp 工具条位图
表1.
(4)将资源文件添加到英文项目中去,使图标变为英文资源式样
把StateDemo.rc加入到英文项目中去,你可以在VC5 Develop Studio的Workspace窗口的File View选项卡中选择"English"。单击右键选择Add project to workspace选项,把文件加入项目中。在此,你可以从Resource View选项卡中看到,在最开始的StateDemo项目中加入的字串IDS_DEMO("It's my demo...")现在已被移到了英文项目中,并将成为Resource.dll的一员。
既然这个英文项目是英文资源DLL,并命名为Resource.dll(与后面建立的中文Resource.dll同名,这样才能使EXE执行程序存取双语或多语种DLL),所以要修改图标来演示StateDemo.exe被调入时是英文版的。把IDR_MAINFRAME图标修改为英文文化特征的,或在Resource View选项卡的图标一栏单击右键选择Import来导入你想要的图标,它可以是16*16,也可以是32*32,本演示程序是使用英国国旗作为IDR_MAINFRAME,以示是英文版的。你可以按自己的需要和风格任意修改在Resource View中的其他资源。
(5)修改链接目标程序中的设置
建立英文资源DLL的任务已经基本完成,还需修改DLL程序在Link前的设置。选择Workspace选项卡,选择Project | Settings选项,在窗口中选择Link标签,改变一些Link设置,将输出的文件名改为Resource.dll,也就是中英文资源同名的那个英文资源DLL,在链接命令Project Options中加入\NOENTRY,它的功能是告诉链接器这是一个含有唯一资源的DLL,不包括入口点。图3显示了修改过的Link选项卡对话框,注意必须把/NOENTRY选项加进公用项编译控件中。
图3. Link选项卡对话框
至此,英文资源DLL就创建成功了。运行它时,按热键F7链接(此时有窗口弹出,让用户选择所要相关的exe文件,可省去),Resource.DLL就在...\StateDemo\English\Debug目录下了。如需运行英文版演示程序,则把它拷贝到StateDemo\Debug目录下,然后运行StateDemo.exe文件,现在就是英文资源起作用了,它显示英文和英国国旗(如图4所示)。
图4. 英文版演示程序
3.创建中文资源Resource.dll
(1)创建中文新项目区并删除中文项目中所有文件
在创建类型选择框New中选择MFC AppWizard(dll),创建一个中文新项目区,在Location下为工作区选择目录...\StateDemo\即...\StateDemo\Chinese作为该项目区目录,单击OK。其他选项如上面创建英文项目区所述。
刚建立的DLL已包含了一些资源,但它的各种文件在此都不需要。所以,在Workspace FileView中选中所有文件,按Delete删除所有文件。
(2)拷贝资源文件
仍然使用前面从StateDemo中移出的英文资源,同样将目录...StateDemo\下StateDemo.rc和Resource.h拷贝到刚建立的中文资源项目目录Chinese下,将...\StateDemo目录下的StateDemo.rc和Resource.h拷贝到...\Statedemo\Chinese目录下。目的是把...\StateDemo\res下的所有资源转移到...\StateDemo\Chinese\res目录下,需从项目StateDemo拷贝到项目Chinese的所有文件和注释也如上述表1所示。
(3)把资源文件添加到中文项目中使图标变为中文资源式样
把StateDemo.rc加入到Chinese项目中去,你可以在VC5 Develop Studio的Workspace窗口的File View选项卡中选择"Chinese",单击右键选择Add project to workspace选项把文件加入到项目中。在此,StateDemo项目中的字串IDS_DEMO("it's my demo...")已经移到中文项目中,并将成为中文Resource.dll的一员。现在的Resource.dll是中文资源DLL(与前面建立的英文Resource.dll同名),把IDS_DEMO改为中文字串,代码如下:IDS_DEMO=300,
Caption(标题):"你好!这是MS VC5中文演示程序!"
运行结果如图5所示。
图5. 中文版演示程序
字串修改完成后,需要对菜单进行地域化处理,即把菜单项改为中文。打开Resource View标签,双击Menu项,在右边工作区所显示的MFC标准菜单中把英文翻译成中文;也可以添加菜单项,按软件所需作完全个性化的设计。本演示程序使用中国国旗做IDR_MAINFRAME,以表示是中文版。你还可以按自己的需要和风格任意修改Resource View中的其他资源。
(4)修改中文DLL链接目标程序和资源组件中的设置
中文资源DLL也需要修改DLL程序在Link前的设置,其方法与英文的相同:依次选择Workspace选项卡、Project | Settings菜单、Link选项卡,改变一些Link设置,将输出的文件名改为Resource.dll,在链接命令Project Options中加入\NOENTRY(参见图3)。修改资源组件中的设置,在Project | Settings菜单中的Resource选项卡中,将缺省的文字改为中文(如果是中文Windows,则此处就已经是中文了),接下来删除预处理定义_AFXDLL,最后在Directory中输入Visual C++ 5.0安装目录1.chs的路径,这个目录下装的是中文特定资源。还有其他资源如\1.fra法语、\1.ita意大利语、\1.kor韩文等,都可以在需要时选择使用。
删除预处理定义_AFXDLL并输入资源的路径的目的是要包含通用的资源,如果没有这样做,则应用程序会从系统安装的MFC中收集通用的资源。应用程序特定资源会被地域化,通用的MFC资源(诸如光标、打印预览和通用属性页)则不需要。所修改的设置如图6所示。
图6. 资源设置的修改
(5)链接并运行中文资源DLL
按热键F7进行链接,中文资源Resource.DLL就在...\StateDemo\Chinese\Debug目录下了。要看中文,就把它拷贝到StateDemo\Debug目录下,然后再运行StateDemo.exe文件,这时显示的就是中文版了。所有资源都中文化了,如中国国旗、中文菜单等,起作用的就是刚建立的中文资源Resource.dll,运行结果如图7所示。要注意的是,由于中文资源DLL包含了所有通用的MFC资源,所以它比英文要大一些。
图7. 中文版演示程序
至此,两种资源的DLL都建立好了。在本演示程序中,资源全部被从可执行文件中移走并被放入一个DLL中。在MFC类引用任何资源时(比如CString::LoadString),都可以使用当前的资源句柄。通常情况下,当前资源句柄和应用程序的资源句柄是一样的,但本演示程序通过AfxSetResourceHandle改变了这种情况,该函数通过传给AfxSetResourceHandle的句柄使MFC提取所有的资源。当英文的DLL被中文的DLL所取代时,资源ID就访问定位在中文DLL的资源。由于DLL是从初始的DLL拷贝翻译过来的,所以,所有的资源应是完整的,只不过是被翻译成中文罢了。Windows应用程序的资源一般维系在执行程序上,当需要时才被装入。当你的应用程序需要某种资源时,需要给Windows提供两件东西:应用程序的实例句柄和资源的标识符。
二.应用程序的多语种安装
标准软件安装程序制作工具Install Shield 5是在Microsoft Visual C++ 5.0 Enterprise Edition中提供的。在所有制作安装软件的工具中,它是最专业、功能最强大的。用Install Shield 5制作一般的实用安装程序很简单,只需在生成图标等很少几处加入代码。这里只讨论在安装软件前识别Windows版本和语言,并按照所基于的操作系统自动选择你的双语种或多语种软件的语言进行安装。
在Windows操作系统下,微软提供的Win32资源有很完善的功能。为了获取当前操作系统的语言版本,可以调用Win32函数GetOEMCP(),它的作用是获取当前操作系统的OEM(Original Equipment Manufacturer源设备供应商) 代码页,其函数定义如下:
UNIT GetOEMCP(VOID)//GetOEMCP(VOID)不带任何参数;
如果函数调用成功,则返回当前系统OEM代码页定义的值;如果当前系统没有OEM代码页,则返回用户定义的默认值。下面列举了几个定义的值所代表的意思。
定义的值 代表的意思
936 Chinese (P.R.C , Singapore) 中华人民共和国,新加坡
950 Chinese (Taiwan,HK) 中国台湾、香港
437 United States 美国
949 Korean 韩国
932 Japan 日本
855 Russian 俄罗斯
安装在Windows 95或Windows NT平台下的软件之前,先要根据当前系统的语言版本来选择与该语言版本所对应的动态链接库DLL,从而生成符合该操作系统版本的软件。例如,你可以用下面的代码来判断当前是否是简体中文:
if(GetOEMCP()==936)
{
.....
}
此外,还有一种方法就是采用AutoRun技术。如果应用软件是以光盘为载体,则可以在文件AutoRun上下功夫。AutoRun是一个放在光盘目录下的文件,当光盘放入光驱的时候,Windows 95会自动运行它。根据不同语言版本软件所需的不同资源DLL文件,用Install Sheld 5制作两套安装程序,分别放在各自的目录下,而且安装时的SPLASH版权封面图片setup.bmp也可以根据地域的不同而不同,例如把中文大陆国标码安装程序放在\GBSetup目录下,中文BIG5码安装程序放在\BIG5Setup目录下。你可以像上面一样,在AutoRun.exe中加入如下代码。
if(GetOEMCP()==936)
{
::WinExec(_T("GBSetup\setup.exe"),SW_SHOW);
//调用SDK函数WinExec执行命令行程序setup.exe
}
if(GetOEMCP()==950)
{
::WinExec(_T("BIG5Setup\setup.exe"),SW_SHOW);
}
现在就可以自动识别Windows操作系统语言版本而安装各自相应语言版本的软件了。在某种情况下,用户需要更大的自由度,你可以在安装前弹出窗口,询问要安装哪种语言版本。在AutoRun.exe可执行文件中,根据用户的选择来调用各自目录下的安装文件进行安装。
至此,你就可以用本文所讲述的内容编制自己的双语或多语种软件了。在软件测试完毕并且完成了帮助文件等必需的工作后,再制作一个具有个性的安装文件,这样,你的双语或多语种成品软件就诞生了。