Browsershot 改变浏览器语言:setEnvironmentOptions 的用法与底层原理
Browsershot 改变浏览器语言:setEnvironmentOptions 的用法与底层原理
导读
本文围绕 changing-the-language-of-the-browser.md 展开,讲解如何通过 Browsershot 的 setEnvironmentOptions() 方法为无头 Chromium 注入 LANG 等环境变量,从而让页面按指定语言(locale)渲染。你将掌握该方法的完整调用方式、它在 src/Browsershot.php 与 bin/browser.cjs 中的传递链路,以及与 Accept-Language 请求头、setNodeEnv 等相近方案的区别,以便在实际项目中按需选用。
为什么需要改变浏览器的语言
无头浏览器抓取页面时,站点往往依据浏览器进程的语言/区域设置来决定返回内容的语言。例如访问 https://example.com,默认环境下可能返回英文界面,而业务上需要以 en-GB(英式英语)或 de-DE(德语)等特定语言加载页面。Browsershot 官方文档给出的方案,就是通过 setEnvironmentOptions 向浏览器进程注入 LANG 环境变量,让 Chromium 以指定语言环境启动。
该能力属于 Miscellaneous Options 系列,与同目录下 passing-environment-variables-to-the-browser.md 是同一套 API:前者侧重改变浏览器语言,后者用于注入任意自定义环境变量(如 TZ 时区),二者底层实现完全一致。
基本用法:setEnvironmentOptions 注入 LANG
在文件顶部引入命名空间后(参见 docs/usage/introduction.md),即可链式调用:
use Spatie\Browsershot\Browsershot;
Browsershot::url('https://example.com')
->setEnvironmentOptions([
'LANG' => 'en-GB',
])
->save('example.pdf'); // 也可以换成 ->save('example.png') 等
要点说明:
setEnvironmentOptions()接收一个键值对数组,键为环境变量名,值为其取值;- 该方法返回
self,可继续链式调用其它方法(截图、PDF、HTML 等),示例中的...即代表后续任意操作; LANG是 POSIX 系统的标准语言环境变量,取值建议使用完整的 locale 标识(如en-GB、de-DE.UTF-8),让 Chromium 与系统本地化机制都能正确识别。
底层原理:从 PHP 选项到 Chromium 进程
1. 选项写入命令 JSON
setEnvironmentOptions 的实现非常简洁,它把传入数组整体写入 env 这个选项键:
// src/Browsershot.php
public function setEnvironmentOptions(array $options = []): self
{
return $this->setOption('env', $options);
}
setOption()(src/Browsershot.php)会通过 arraySet() 把键值写入内部 additionalOptions 数组,支持点分隔的嵌套键名;随后在 createCommand()(src/Browsershot.php)中通过 array_merge_recursive 合并进最终命令,随 Node 脚本一起执行。
2. Node 端注入 puppeteer 启动参数
PHP 端会把命令 JSON 传给 bin/browser.cjs(该脚本本质上是 puppeteer 的封装)。在浏览器启动处可以看到:
// bin/browser.cjs
browser = await puppet.launch({
headless: options.newHeadless ? true : 'shell',
executablePath: options.executablePath,
args: options.args || [],
pipe: options.pipe || false,
env: {
...(options.env || {}),
...process.env
},
protocolTimeout: options.protocolTimeout ?? 30000,
});
即 env 选项被直接传给 puppeteer 的 launch(),成为 Chromium 子进程的环境变量集合。这印证了文档中的说法:LANG 会作用于整个浏览器进程,而不仅仅是单次页面请求。
3. 同名变量覆盖顺序
从上面 spread 的书写顺序可以推断:options.env(即你通过 setEnvironmentOptions 传入的变量)在前,process.env(Node 进程自身环境)在后,因此 若自定义变量与 Node 运行环境中已存在的变量同名,后者会覆盖前者。例如当服务器本身已设置了 LANG 时,注入同名的 LANG 未必生效——这是从 bin/browser.cjs 源码结构可推断出的行为,在依赖系统默认语言环境的场景下需特别留意。
4. 测试用例佐证
仓库测试 tests/BrowsershotTest.php 明确验证了该方法的输出结构:
it('will allow passing environment variables', function () {
$instance = Browsershot::url('https://example.com')
->setEnvironmentOptions([
'TZ' => 'Pacific/Auckland',
]);
$this->assertEquals([
'url' => 'https://example.com',
'action' => 'screenshot',
'options' => [
// ...
'env' => [
'TZ' => 'Pacific/Auckland',
],
],
], $instance->createScreenshotCommand('screenshot.png'));
});
可见 env 会以顶层选项的形式出现在最终命令中,与 setEnvironmentOptions(['LANG' => 'en-GB']) 的用法完全对应。
同时注入多个环境变量
setEnvironmentOptions 接受任意数量的键值对,例如同时指定语言与时区:
Browsershot::url('https://example.com')
->setEnvironmentOptions([
'LANG' => 'de-DE',
'TZ' => 'Europe/Berlin',
])
->save('example.pdf');
也可以多次调用,最终通过 array_merge_recursive 合并生效。
与相近方案的区分
setNodeEnv:作用于 Node 进程,而非浏览器
注意区分 src/Browsershot.php 中的 setNodeEnv():它设置的是 Node 进程本身 的环境变量,由 buildEnvVarsCommand()(src/Browsershot.php)拼进命令行前缀;而 setEnvironmentOptions() 注入的是 Chromium 浏览器进程 的环境变量。前者影响 puppeteer/Node 侧,后者影响页面渲染侧,按需选择。
Accept-Language 请求头:控制 HTTP 层语言协商
如果目标站点是根据 HTTP 请求头协商语言,而非依赖浏览器进程环境,更直接的做法是设置 Accept-Language 头,参见 adding-extra-headers-to-the-navigational-request.md:
Browsershot::url('https://example.com')
->setExtraNavigationHttpHeaders(['Accept-Language' => 'en-GB'])
->save('example.pdf');
两者侧重点不同:LANG 环境变量影响浏览器进程的语言环境(如默认字体、日期/数字本地化规则),Accept-Language 影响页面发起的导航请求的语言协商。实际排障时可以两个维度结合排查。
注意事项与适用前提
- 平台差异:在 Windows 上,getWindowsEnv() 只透传
LOCALAPPDATA、Path、SystemRoot、USERPROFILE等系统关键变量,注入的LANG等 POSIX 风格变量在 Windows 环境下意义有限;该方案主要面向 Linux/macOS 类 POSIX 环境。 - 覆盖问题:如前所述,
process.env中的同名变量会覆盖自定义值,使用前可用setNodeEnv或直接核对运行环境,避免静默失效。 - 站点语言机制差异:部分站点通过 cookie、地理位置或登录态决定语言,仅设置
LANG不一定能改变页面内容,此时应结合 using-cookies.md 等方案综合处理。 - 调试建议:若设置了
LANG后页面语言未变化,可先确认浏览器进程实际继承的环境变量(如临时打印process.env),再决定改用Accept-Language头或 Chromium 的--lang启动参数。
小结
setEnvironmentOptions(['LANG' => 'en-GB']) 是 Browsershot 官方推荐的改变浏览器语言方式,其本质是把 env 选项透传给 puppeteer 的 launch(),最终成为 Chromium 子进程的环境变量。理解 src/Browsershot.php 到 bin/browser.cjs 的传递链路,以及它与 setNodeEnv、Accept-Language 头的边界,即可在不同语言协商场景下准确选型,并通过 tests/BrowsershotTest.php 的用例结构验证最终命令是否正确。