C++ Builder 参考手册 ➙ 本地化 (多语言) 功能
本文介绍 C++ Builder 自带的本地化 (多语言) 功能
- C++ Builder 自带的多语言功能简介
1.1. 实现多语言的方式
1.2. 发现的问题 - 创建一个 VCL 项目,添加多语言功能
2.1. 创建一个 VCL 项目
2.2. 添加语言 (英语)
2.3. 翻译语言 (中文到英语)
2.4. 英语/中文界面测试
2.5. 程序实现切换语言功能
2.6. 在隐藏的控件里面做可以切换语言的字符串
2.7. 在 .rc 文件里面做可以切换语言的字符串
1. C++ Builder 自带的多语言功能简介
这是 C++ Builder 自带的,也是 RAD Studio (包括 Delphi 和 C++ Builder) 自身的多语言实现的方式。
1.1. 实现多语言的方式
- 通过加载 dll 的方式切换语言,每种语言一个 dll,后缀名为语言的简称,例如 CHS, CHT, ENU 等
- 通过 Resource DLL Wizard 添加和更新语言;
Form 上的控件自动生成表格,只需要把标题或文字翻译成对应的语言;
资源文件 (.rc 文件) 里面的字符串可以生成对应的翻译文字表格; - 通过修改注册表 HKEY_CURRENT_USER\Software\Embarcadero\Locales 里面的注册表项更换显示语言的 dll,注册表项为应用程序完整的路径和文件名 (注册表项为 Application->ExeName),注册表内容为语言简称,即与需要显示语言的 dll 后缀名相同,
例如注册表项 "D:\Test\App.exe" 的值为 "ENU",那么运行 D:\Test\App.exe 时会加载 App.ENU 这个 dll 文件。
1.2. 发现的问题
- 主要是编译 .rc 文件的问题,不使用 .rc 文件不会出现 (Delphi 可以不使用 .rc 文件在资源里面添加字符串,所以问题主要出在 C++ Builder):
由于 C++ Builder 编译 .rc 文件仍然采用古老的 Borland 编译器,即使是最新 11.0 版本,所以 .rc 文件只支持 ANSI 编码,只能用当前操作系统支持的语言的 ANSI 字符集,如果遇到外国字符或其他 UNICODE 符号就会无法编译,.rc 保存为 UTF-8 编码也会无法编译,对于多语言来说是个严重的障碍;
如果把 .rc 文件的编译器切换到 Windows SDK Resource Compiler,虽然可以编译 UTF-8 编码的 .rc 文件了,支持 UNICODE 了,但是 Resource DLL Wizard 多语言向导会工作异常,无法正确处理 .rc 文件里面的字符串。
所以建议在 Form 上放置一个隐藏的 Panel,把要翻译的字符串通过 Label 控件放在隐藏的 Panel 里面,因为 Form 里面控件的文字始终能够正常翻译,也支持 UNICODE。 - 切换语言必须通过修改注册表,而且必须是固定的 Embarcadero\Locales 注册表位置,修改完注册表需要退出重新运行才能生效,没发现立即生效的方法;
- 顺便说一下 Delphi,文档说的是使用 resourcestring 关键字,把字符串放在资源里面,而在 C++ 里面可以用 System::LoadResourceString 读取 Delphi 资源 resourcestring 关键字里面的字符串。
2. 创建一个 VCL 项目,添加多语言功能
2.1. 创建一个 VCL 项目
选择菜单 File -- New -- Windows VCL Application - C++ Builder 创建一个新的 VCL 项目,然后添加几个控件,用于测试多语言功能:
把这个项目保存到新创建的项目文件夹里面,项目名称为 TestLocalization:
2.2. 添加语言 (英语)
- 要求在添加语言之前,必须先把程序编译通过,那么先编译这个新创建的应用程序,无论是 Alt-F9 编译,还是直接 F9 运行,还是其他方法,只要生成了 exe 就说明编译通过了。
- 选择菜单 Project -- Languages -- Add...
也可以选择菜单 File -- New -- Other,左面点选 C++ Builder 大类,然后右面选择 Resource DLL Wizard
选择添加语言的项目,默认就是当前的项目,基础语言 (Base Language) 为当前操作系统默认的语言 0804 就是简体中文,这是应用程序自身的语言 (可以点击选择更换基础语言),直接点击 "Next >" 按钮进行下一步:
选择多语言支持的语言,可以多选,每个语言将生成一个对应的 dll 项目,可以编译为 dll 语言 (资源) 文件,Local ID 为语言的 LCID,Extension 为语言文件的后缀,美国英语对应的是 .ENU 后缀名。选择语言之后,点击 "Next >" 按钮进行下一步:
这是项目文件存放位置,可以点击 Path 栏里面的路径更改保存位置,默认就在要添加语言项目的文件夹里面,创建语言后缀名相同的名称的文件夹里面,点击 "Next >" 按钮继续:
这是语言的状态,"英语 (美国)" 的状态为 "Create New"
点击 "Next >" 继续:
- Skip DRC files that are not found: 跳过找不到的 DRC (Delphi Resource String File) 文件
- Show statistics when done: 结束时显示总结。
点击 "Finish" 结束添加语言。
提示 Resource DLL Wizard 需要重新编译项目,选择 "Yes"
添加语言结束:
可以看到新增了一个 TestLocalization.ENU 项目,这个项目可以编译生成 TestLocalization.ENU 文件,在 exe 相同文件夹里面,是美国英语的资源文件。
这时候,项目组 ProjectGroup1 也需要保存,如下截图:
选择 File - Save All 保存所有文件,或者点击工具栏上的 "Save All" 按钮:
将提示把项目组文件存盘,例如保存为 Localization.groupproj
以后再打开这个项目组文件,就会同事打开项目文件和所有的语言翻译 dll 项目
2.3. 翻译语言 (中文到英语)
双击英文翻译项目 TestLocalization.ENU 里面的 Unit1.dfm
打开的不是 Form 画面设计,而是这个 Form 的翻译,这里只需要翻译一些控件的标题或文字,如上截图的画圈部分:
可以看到,在 "英语 (美国)" 栏里面翻译了的项目,Status 栏自动变成了 Translated,表示 "已经翻译",而其他没有翻译的,是 Untranslated 即 "没翻译",那些项目也不需要翻译。
点击翻译表格左上角的保存按钮,把翻译内容存盘,如上截图画圈的按钮位置。
编译语言项目:
在 TestLocalization.ENU 项目点击右键,选择 Make,或者选择菜单 Project - Make all projects 编译所有项目。
会在 TestLocalization.exe 相同文件夹里面生成 TestLocalization.ENU 文件,即为英语资源文件。
2.4. 英语/中文界面测试
直接运行项目,和添加语言之前没有区别,是设计时的中文界面:
选择菜单:Project -- Languages -- Set Active...
选择 "英语 (美国)",然后点击 "Finish" 按钮。
再运行程序,就变成英文界面了:
这里 "中文" 按钮在语言翻译表格里面没有翻译为英文,所以切换到英文界面也是 "中文",这有助于切换到一个陌生的语言再切换回去。
如果要切换到中文界面,再选择一次菜单:Project -- Languages -- Set Active... ,选择 "中文 (简体,中国)" 就可以了。
2.5. 程序实现切换语言功能
前面内容演示了如何测试多语言界面,但是程序发布的时候,要在程序里面切换语言,而不是在编译器里面切换。
这个演示程序里面:Button1 是 "English",Button2 是 "中文",现在实现这两个按钮的功能。
由于需要修改注册表,所以加入了头文件 #include <System.Win.Registry.hpp>
#include <System.Win.Registry.hpp>
注册表位置:HKEY_CURRENT_USER\Software\Embarcadero\Locales
注册表项:应用程序的完整路径和文件名,即 Application->ExeName
如果注册表项的值为 "ENU",程序运行时会加载与 exe 文件同名的后缀为 .ENU 的资源 dll 文件;把注册表项的值改为 CHS,即简体中文,应用程序运行时找不到后缀为 .CHS 的资源文件,就不加载资源文件了,使用设计界面时的默认文字,即简体中文。
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TRegistry *reg = new TRegistry;
reg->RootKey = HKEY_CURRENT_USER;
reg->OpenKey(L"Software\\Embarcadero\\Locales", true);
reg->WriteString(Application->ExeName, L"ENU");
reg->CloseKey();
delete reg;
ShowMessage(L"语言已经更改,关闭程序重新运行将使用更改之后的语言。");
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
TRegistry *reg = new TRegistry;
reg->RootKey = HKEY_CURRENT_USER;
reg->OpenKey(L"Software\\Embarcadero\\Locales", true);
reg->WriteString(Application->ExeName, L"CHS");
reg->CloseKey();
delete reg;
ShowMessage(L"语言已经更改,关闭程序重新运行将使用更改之后的语言。");
}
更改语言,即修改注册表项之后,需要退出重新运行,才能生效。
由于修改了程序,所以要更新语言翻译的项目,才能在切换到非默认的语言里面生效:选择菜单 project -- Languages -- Update Localized Projects 更新本地化项目:
选择之后,即没有成功的提示,也没有失败的提示,就是没有问题了,然后要重新编译所有的项目:选择菜单 Project -- Make All Projects
这样修改程序在所有的语言都生效了。
例如运行在英文界面,点击 "中文" 按钮,有如下提示:
退出、重新运行:
2.6. 在隐藏的控件里面做可以切换语言的字符串
由于刚才的例子,在 C++ 代码里面有字符串:
ShowMessage(L"语言已经更改,关闭程序重新运行将使用更改之后的语言。");
这样的字符串不能被语言翻译向导收录并自动切换语言,而需要想其他办法,用隐藏的控件的文字来翻译 (例如 Label 的 Caption 属性) 就是一个办法。如果要翻译的文字很多,可以把所有这些 Label 放在一个 Panel 里面,Panel 隐藏 (Visible 属性设为 false)。
以上截图:Panel1 的 Visible 属性设为 false 专门用来放翻译用的字符串的,里面的 Label3 的 Caption 设为 ShowMessage 的提示信息。
把 ShowMessage(L"语言已经更改,关闭程序重新运行将使用更改之后的语言。");
改为 ShowMessage(Label3->Caption);
就可以应用翻译了。
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TRegistry *reg = new TRegistry;
reg->RootKey = HKEY_CURRENT_USER;
reg->OpenKey(L"Software\\Embarcadero\\Locales", true);
reg->WriteString(Application->ExeName, L"ENU");
reg->CloseKey();
delete reg;
ShowMessage(Label3->Caption); // L"语言已经更改,关闭程序重新运行将使用更改之后的语言。"
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
TRegistry *reg = new TRegistry;
reg->RootKey = HKEY_CURRENT_USER;
reg->OpenKey(L"Software\\Embarcadero\\Locales", true);
reg->WriteString(Application->ExeName, L"CHS");
reg->CloseKey();
delete reg;
ShowMessage(Label3->Caption); // L"语言已经更改,关闭程序重新运行将使用更改之后的语言。"
}
由于更改了程序,仍然需要
- 选择菜单 project -- Languages -- Update Localized Projects 更新本地化项目,
- 然后双击英文翻译项目 TestLocalization.ENU 里面的 Unit1.dfm 打开翻译表格,
- 找到表格里面的 Label3 的 Caption 属性,翻译为英语:
- 要重新编译所有的项目:选择菜单 Project -- Make All Projects
这样修改程序在所有的语言都生效了。
2.7. 在 .rc 文件里面做可以切换语言的字符串
在项目里面添加一个 TestLocalization_res.rc 文件,内容如下:
STRINGTABLE
BEGIN
101 "语言已经更改,关闭程序重新运行将使用更改之后的语言。"
END
这是 ID 为 101 的字符串内容
由于更改了程序,仍然需要
选择菜单 project -- Languages -- Update Localized Projects 更新本地化项目
更新之后,在语言项目里面也会增加一个 TestLocalization_res.rc 文件,双击这个文件,出现的是翻译表格。
仍然是把 "英语 (美国)" 栏的内容翻译之后,点击表格左上角的存盘按钮保存。
把 ShowMessage(L"语言已经更改,关闭程序重新运行将使用更改之后的语言。");
改为 ShowMessage(LoadStr(101));
即使用 ID 为 101 的字符串
要重新编译所有的项目:选择菜单 Project -- Make All Projects
这样修改程序在所有的语言都生效了。
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TRegistry *reg = new TRegistry;
reg->RootKey = HKEY_CURRENT_USER;
reg->OpenKey(L"Software\\Embarcadero\\Locales", true);
reg->WriteString(Application->ExeName, L"ENU");
reg->CloseKey();
delete reg;
ShowMessage(LoadStr(101)); // Label3->Caption // L"语言已经更改,关闭程序重新运行将使用更改之后的语言。"
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
TRegistry *reg = new TRegistry;
reg->RootKey = HKEY_CURRENT_USER;
reg->OpenKey(L"Software\\Embarcadero\\Locales", true);
reg->WriteString(Application->ExeName, L"CHS");
reg->CloseKey();
delete reg;
ShowMessage(LoadStr(101)); // Label3->Caption // L"语言已经更改,关闭程序重新运行将使用更改之后的语言。"
}
相关:
- C++ Builder 的字符串类型、字符类型、字符编码
- System::Sysutils::TEncoding
- C++ Builder 的反射 (三) - 通用 Reflection Factory
- C++ Builder 的反射 (二) - Reflection Factory
- C++ Builder 的反射 (一) - Reflection 简单实现
- 枚举控件所有的属性、事件和方法
- 枚举窗口内所有的控件
- C++ Builder 的枚举类型
- C / C++ 可变参数的函数
- C / C++ 可变参数的宏,__VA_ARGS__,...
- C++ 可变参数的模板
- C++ Builder 的 PME 架构
C++ Builder 参考手册 ➙ 本地化 (多语言) 功能