从 GRIT 到 WebUI:Chromium 内置资源加载与前端展示的完整链路解析
一、前言
在 Chromium 内核开发过程中,国际化字符串的加载与 WebUI 内置页面的展示,是两个经常被提及但又容易被忽视的核心环节。
一方面,GRIT(Google Resource and Internationalization Tool)承担了整个字符串资源编译、打包和加载的任务,它决定了开发者在 C++ 代码或者 WebUI 前端中看到的
IDS_XXX
字符串是如何被替换成实际文字的。另一方面,WebUI 内置页(如
chrome://settings/
、chrome://newtab/
)的展示机制,涉及到 C++ 后端和前端 HTML/JS 的对接,GRIT 中的资源如何最终呈现在用户眼前,也是一个完整的链路。
本篇文章将深入剖析 GRIT 的编译链路、运行时加载机制、多语言切换原理、资源 ID 分配,再结合 WebUI 内置页的对接机制,展示一个完整的前端展示流程。
阅读完这篇文章,你将能够回答:
.grd
文件和.xtb
文件有什么区别?.grd
如何编译成.pak
文件,运行时又如何被加载?多语言切换的机制是怎样的?
WebUI 内置页如何与这些资源对接?
一个完整的字符串从 定义 → 编译 → 加载 → 前端展示 的链路是什么?
二、GRIT 资源体系介绍
1. 什么是 GRIT
GRIT,全称 Google Resource and Internationalization Tool,是 Chromium 用来管理字符串、图片、图标等 UI 资源的工具。它的主要职责是:
资源定义:通过
.grd
文件声明一组资源(通常是字符串)。国际化支持:通过
.xtb
文件为不同语言提供翻译。资源编译:在构建过程中生成
.h
头文件和.pak
打包文件。运行时加载:通过
ui::ResourceBundle
和l10n_util
API 在代码中加载资源。
2. .grd
文件
.grd
文件是资源的“清单文件”,开发者在其中定义需要本地化的字符串,例如:
<message name="IDS_BACKGROUND_APP_INSTALLED_BALLOON_BODY" desc="Balloon content"> <ph name="APP_NAME">$1<ex>Background App</ex></ph> will launch at system startup and continue to run in the background even once you've closed all other <ph name="PRODUCT_NAME">$2<ex>Google Chrome</ex></ph> windows. </message>
特点:
name
定义了字符串 ID(如IDS_BACKGROUND_APP_INSTALLED_BALLOON_BODY
)。desc
用于描述字符串用途,方便翻译。<ph>
表示参数占位符。
3. .xtb
文件
.xtb
文件是 翻译文件,用于为 .grd
中的字符串提供多语言版本。
例如 en-US.xtb
、zh-CN.xtb
,它们内部存放着 <translation>
节点,对应不同语言的翻译。
4. .pak
文件
在编译阶段,GRIT 工具会把 .grd
+ .xtb
合并,最终生成 .pak
文件。
.pak
文件是一个二进制资源包,存放了 ID → 文本 的映射关系。
例如:
IDS_BACKGROUND_APP_INSTALLED_BALLOON_BODY (id=12345)
→"某某应用将在系统启动时运行"
三、GRIT 的编译流程
我们来看字符串是如何从 .grd
最终进入到 .pak
的。
1. .grd
→ .h
在 GN 构建过程中,.grd
文件会通过 grit.py
工具转换成 C++ 头文件,生成的 .h
文件包含一系列 #define
宏:
#define IDS_BACKGROUND_APP_INSTALLED_BALLOON_BODY 12345
这样,C++ 代码中使用 IDS_BACKGROUND_APP_INSTALLED_BALLOON_BODY
时,实际上就是在使用 整型 ID。
2. .grd
+ .xtb
→ .pak
构建时,GRIT 工具会:
读取
.grd
中定义的 ID。根据语言选择,加载对应
.xtb
文件翻译。生成
.pak
文件,将 ID 和对应的翻译文本打包进去。
例如:
pak 文件 ├── 12345 → "应用将在系统启动时运行" (中文) ├── 12345 → "The app will launch at system startup" (英文)
3. .pak
文件的安装位置
不同平台下,Chromium 会把 .pak
文件放在:
Windows:
chrome.pak
/resources.pak
Linux:
out/Default/locales/zh-CN.pak
macOS:
Chromium.app/Contents/Resources/zh-CN.pak
四、运行时加载机制
1. ui::ResourceBundle
运行时,Chromium 通过 ui::ResourceBundle
加载 .pak
文件。
ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); rb.AddDataPackFromPath(locale_path, ui::SCALE_FACTOR_NONE);
AddDataPackFromPath
会把.pak
文件加载到内存。内部使用一个
ResourceMap
存放 ID → 文本的映射。
2. l10n_util
API
开发者在 C++ 代码中使用 l10n_util::GetStringUTF16(id)
来获取字符串:
base::string16 text = l10n_util::GetStringUTF16( IDS_BACKGROUND_APP_INSTALLED_BALLOON_BODY);
流程是:
GetStringUTF16
→ 查询ui::ResourceBundle
→ 找到 ID 对应的文本。返回
std::u16string
,供 UI 展示。
3. 多语言切换
Chromium 的多语言切换依赖 Locale,即系统语言或者浏览器启动参数:--lang=zh-CN
启动时,
ResourceBundle::InitSharedInstanceWithLocale
会选择对应语言的.pak
文件。当用户在设置里切换语言时,浏览器需要重启,重新加载另一套
.pak
。
4. 资源 ID 的编译期分配
值得注意的是,ID 是在编译期固定的整数。
例如:
#define IDS_HELLO_WORLD 6001
无论语言怎么切换,IDS_HELLO_WORLD
始终是 6001,只有 .pak
文件里绑定的翻译会变化。
五、WebUI 内置页的前端对接
字符串加载只是第一步,最终还需要在 WebUI 页里展示。
1. 内置页 WebUI 概念
内置页的访问方式是
chrome://xxx/
。底层由
WebUIController
负责加载页面。前端通常是 HTML + JS + CSS,存放在
resources/
目录。
2. 字符串注入
C++ 后端会把 .pak
中的字符串传递给前端。常见方式是 LoadTimeData
。
webui::LocalizedString localized_strings[] = { {"appInstalledBalloonBody", IDS_BACKGROUND_APP_INSTALLED_BALLOON_BODY}, }; AddLocalizedStrings(source, localized_strings);
这样,前端 HTML 就能通过:
const text = loadTimeData.getString('appInstalledBalloonBody');
拿到对应的字符串。
3. 资源绑定流程
整体链路如下:
.grd
定义字符串。编译生成
.pak
。运行时
ResourceBundle
加载.pak
。WebUIDataSource
→AddLocalizedStrings
注入到前端。前端
loadTimeData.getString()
使用。
4. 实际案例
比如 新标签页(New Tab Page, NTP):
C++ 端:
source->AddLocalizedString("title", IDS_NEW_TAB_TITLE);
前端端:
<h1 id="title"></h1> <script> document.getElementById("title").textContent = loadTimeData.getString('title'); </script>
这样,最终 <h1>
会显示 "新标签页"
或 "New Tab"
。
六、GRIT + WebUI 的完整链路
结合上面的分析,我们得到一个完整链路:
开发者定义
.grd
文件中声明字符串 ID。
构建编译
.grd
生成.h
,定义整数 ID。.xtb
翻译合并,生成.pak
。
运行时加载
ResourceBundle
加载.pak
。l10n_util
根据 ID 获取字符串。
WebUI 注入
C++ 后端使用
AddLocalizedStrings
注入前端。前端用
loadTimeData
获取并展示。
七、对比总结
阶段 | 输入 | 输出 | 工具/接口 |
---|---|---|---|
编译前 | .grd / .xtb | .h / .pak | grit.py |
编译后 | .pak | ID → 文本 | ResourceBundle |
运行时 | ID | 文本 | l10n_util::GetStringUTF16 |
WebUI | 文本 | 页面展示 | loadTimeData.getString |
八、结语
GRIT 与 WebUI 的结合,是 Chromium 国际化 + 内置页面展示 的关键路径。
.grd
文件解决了资源声明与 ID 分配;.xtb
文件提供了多语言翻译支持;.pak
文件把不同语言打包成可分发资源;ui::ResourceBundle
在运行时统一加载;WebUI 内置页通过
AddLocalizedStrings
与loadTimeData
实现最终展示。
掌握这条链路后,你不仅能理解 字符串是如何出现在页面上的,还能在调试 WebUI 内置页时快速定位问题(如字符串丢失、多语言未生效等)。