UEFI HII本地化:EDK II中多语言支持实现方法
【免费下载链接】edk2 EDK II 项目地址: https://gitcode.com/gh_mirrors/ed/edk2
引言
你是否在开发UEFI(Unified Extensible Firmware Interface,统一可扩展固件接口)应用时,为多语言支持而烦恼?是否希望你的BIOS设置界面能够无缝切换中英文、法语等多种语言?本文将深入解析EDK II(EFI Development Kit II)中HII(Human Interface Infrastructure,人机交互基础设施)本地化技术,通过实例演示如何实现UEFI应用的多语言支持。读完本文,你将掌握从配置文件编写到代码集成的完整流程,轻松构建支持全球用户的UEFI应用。
HII本地化核心概念
HII概述
HII是UEFI规范定义的人机交互框架,提供了表单(Form)、字符串(String)、字体(Font)等用户界面元素的标准化管理方式。其核心优势在于将界面逻辑与具体实现分离,支持通过配置文件而非硬编码实现多语言切换。
本地化关键组件
EDK II中实现HII本地化需要三个关键文件类型:
- .uni文件:存储多语言字符串资源,采用键值对结构,支持按语言标签区分不同翻译版本
- .vfr文件:定义表单布局和用户界面元素,通过字符串令牌(Token)引用.uni文件中的多语言文本
- C源代码文件:实现HII配置协议(EFI_HII_CONFIG_ACCESS_PROTOCOL),处理用户交互和配置数据存储
三者关系如下:
多语言支持实现步骤
步骤1:创建.uni字符串文件
.uni文件采用特殊格式定义多语言字符串,基本语法如下:
#string <字符串ID> #language <语言代码> "<翻译文本>"
以VfrStrings.uni为例,定义支持英文和法语的字符串:
#langdef en-US "English"
#langdef fr-FR "Francais"
#string STR_FORM_SET_TITLE #language en-US "Browser Testcase Engine"
#language fr-FR "Browser Testcase Engine"
#string STR_ONE_OF_PROMPT_X_UEFI #language en-US "x-UEFI HII Option"
#language fr-FR "x-UEFI HII Option"
#string STR_ONE_OF_PROMPT_NON_X_UEFI #language en-US "Non x-UEFI HII Option"
#language fr-FR "Non x-UEFI HII Option"
最佳实践:为每个功能模块创建独立的.uni文件(如
DriverSample.uni、NetworkConfig.uni),避免单个文件过大难以维护
步骤2:编写.vfr表单定义文件
.vfr文件通过字符串令牌引用.uni文件中的多语言文本,关键语法如下:
formset
guid = DRIVER_SAMPLE_FORMSET_GUID,
title = STRING_TOKEN(STR_FORM_SET_TITLE),
help = STRING_TOKEN(STR_FORM_SET_TITLE_HELP),
classguid = EFI_HII_PLATFORM_SETUP_FORMSET_GUID,
varstore DRIVER_SAMPLE_CONFIGURATION,
varid = CONFIGURATION_VARSTORE_ID,
name = MyIfrNVData,
guid = DRIVER_SAMPLE_FORMSET_GUID;
form formid = 1, title = STRING_TOKEN(STR_FORM1_TITLE);
oneof varid = MyIfrNVData.QuestionXUefiKeywordRestStyle,
prompt = STRING_TOKEN(STR_ONE_OF_PROMPT_X_UEFI),
help = STRING_TOKEN(STR_ONE_OF_PROMPT_X_UEFI_HELP),
flags = RESET_REQUIRED | REST_STYLE,
option text = STRING_TOKEN(STR_ONE_OF_TEXT1), value = 0, flags = 0;
option text = STRING_TOKEN(STR_ONE_OF_TEXT2), value = 1, flags = DEFAULT;
endoneof;
endform;
endformset;
注意:所有字符串引用必须使用
STRING_TOKEN()宏包装,EDK II构建系统会自动处理令牌解析和字符串表生成
步骤3:实现HII配置协议
在C源代码中,需要实现HII配置协议以连接表单定义和实际功能:
EFI_STATUS
EFIAPI
DriverSampleConfigAccessCallback (
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
IN EFI_HII_CONFIGURATION_ACTION Action,
IN EFI_STRING ConfigRequest,
IN EFI_STRING ConfigResponse,
IN UINTN Progress,
OUT EFI_STRING Results
)
{
// 处理配置数据加载和保存
switch (Action) {
case EFI_HII_CONFIGURATION_ACTION_EXTRACT:
// 从NVRAM加载配置数据
break;
case EFI_HII_CONFIGURATION_ACTION_APPLY:
// 将配置数据保存到NVRAM
break;
}
return EFI_SUCCESS;
}
// 协议实例
EFI_HII_CONFIG_ACCESS_PROTOCOL mDriverSampleConfigAccess = {
DriverSampleExtractConfig,
DriverSampleRouteConfig,
DriverSampleConfigAccessCallback
};
步骤4:注册HII数据包
在驱动入口函数中,需要将表单和字符串资源注册到HII数据库:
EFI_STATUS
EFIAPI
DriverSampleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS status;
EFI_HII_HANDLE hiiHandle;
// 加载VFR表单和UNI字符串
hiiHandle = HiiAddPackages (
&gDriverSampleFormSetGuid,
ImageHandle,
DriverSampleVfrBin, // 编译后的VFR二进制数据
DriverSampleStrings, // 编译后的UNI字符串数据
NULL
);
if (hiiHandle == NULL) {
return EFI_OUT_OF_RESOURCES;
}
// 安装HII配置访问协议
status = gBS->InstallProtocolInterface (
&ImageHandle,
&gEfiHiiConfigAccessProtocolGuid,
EFI_NATIVE_INTERFACE,
&mDriverSampleConfigAccess
);
return status;
}
高级应用:动态语言切换
运行时语言检测
EDK II提供HiiGetSupportedLanguages()函数获取系统支持的语言列表,示例代码:
EFI_STATUS
GetSupportedLanguages (
OUT CHAR8 **LanguagesBuffer,
OUT UINTN *LanguagesSize
)
{
EFI_HII_PROTOCOL *HiiProtocol;
EFI_STATUS status;
// 获取HII协议
status = gBS->LocateProtocol (
&gEfiHiiProtocolGuid,
NULL,
(VOID **)&HiiProtocol
);
if (EFI_ERROR(status)) return status;
// 获取支持的语言列表
return HiiProtocol->GetSupportedLanguages (
HiiProtocol,
LanguagesBuffer,
LanguagesSize
);
}
字符串动态加载
通过HiiGetString()函数可根据语言代码动态获取对应翻译:
EFI_STATUS
GetLocalizedString (
IN EFI_HII_HANDLE HiiHandle,
IN EFI_STRING_ID StringId,
IN CHAR8 *Language,
OUT EFI_STRING *String
)
{
EFI_HII_STRING_PROTOCOL *HiiString;
EFI_STATUS status;
status = gBS->LocateProtocol (
&gEfiHiiStringProtocolGuid,
NULL,
(VOID **)&HiiString
);
if (EFI_ERROR(status)) return status;
return HiiString->GetString (
HiiString,
Language,
StringId,
HiiHandle,
String,
NULL
);
}
语言切换流程
常见问题与解决方案
问题1:字符串令牌解析失败
症状:编译时提示STRING_TOKEN(XXX) not found
原因:.vfr文件中引用的字符串ID在.uni文件中未定义或拼写错误
解决方案:
- 检查.uni文件确保包含对应的
#string定义 - 验证字符串ID大小写是否一致(区分大小写)
- 清理编译产物后重新构建(
build clean)
问题2:部分语言显示乱码
症状:中文、日文等非ASCII语言显示为乱码
原因:.uni文件编码不是UTF-8或未包含BOM头
解决方案:
- 使用支持UTF-8的编辑器(如VS Code)保存.uni文件
- 添加UTF-8 BOM头(Windows环境)
- 确保EDK II工具链支持宽字符(设置
INTEL_ASM_ENABLE_WIDE_CHARACTER_SUPPORT=TRUE)
问题3:HII数据库注册失败
症状:HiiAddPackages()返回NULL
原因:
- 资源文件路径错误
- 内存不足
- GUID冲突
解决方案:
- 使用
DEBUG宏输出详细日志 - 验证VFR和UNI文件是否正确编译为二进制资源
- 使用
guidgen工具生成新的唯一GUID
性能优化策略
字符串压缩
对于包含大量字符串的应用,可启用EDK II的字符串压缩功能:
在.dsc文件中添加:
[Defines]
SUPPORT_STRING_COMPRESSION = TRUE
按需加载
实现语言包的按需加载,减少内存占用:
// 只加载当前语言所需的字符串
EFI_STATUS
LoadLanguagePack (
IN CHAR8 *LanguageCode
)
{
EFI_STATUS status;
EFI_HANDLE packageHandle;
// 加载指定语言的HII包
status = gBS->LoadImage (
FALSE,
gImageHandle,
LanguageCode, // 语言包路径
NULL,
0,
&packageHandle,
NULL
);
if (!EFI_ERROR(status)) {
status = gBS->StartImage (packageHandle, NULL, NULL);
}
return status;
}
总结与展望
HII本地化技术是构建全球化UEFI应用的关键,通过.uni和.vfr文件的配合,能够以最小的开发成本实现多语言支持。随着UEFI 2.10及后续版本的发布,HII将支持更多富文本格式和动态界面元素,进一步提升用户体验。
本文介绍的方法已在EDK II的DriverSampleDxe等示例中得到验证,读者可参考以下路径的源代码深入学习:
MdeModulePkg/Universal/DriverSampleDxe:HII示例驱动MdeModulePkg/Include/Protocol/HiiConfigAccess.h:HII配置协议定义MdeModulePkg/Library/UefiHiiLib:HII操作工具函数库
掌握HII本地化技术,将使你的UEFI应用能够轻松走向国际市场,满足不同地区用户的语言需求。立即动手改造你的第一个多语言UEFI应用吧!
扩展学习资源
- EDK II官方文档:《UEFI HII Specification》
- EDK II示例代码:
MdeModulePkg/Universal/HiiResourcesSampleDxe - UEFI论坛:HII Working Group
- 书籍:《Beyond BIOS: Developing with the Unified Extensible Firmware Interface》
如果你觉得本文有帮助,请点赞、收藏并关注作者,获取更多UEFI开发技术分享! 下期预告:UEFI表单动态验证与错误处理
【免费下载链接】edk2 EDK II 项目地址: https://gitcode.com/gh_mirrors/ed/edk2
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



