时区、locale 与语言:浏览器 API 中的区别
区分时区、locale、语言协商和网站偏好,逐字段配置并排查区域显示不一致问题。
BotBrowser Team
浏览器可能用一种 locale 格式化日期、报告另一种首选语言、向服务器发送不同的语言偏好,并使用与代理无关的时区。BotBrowser 提供时区、locale 和语言的配置项,但这些设置不会让所有网站选择相同内容,也不能证明实际地理位置。实用做法是在各字段所属的层级进行配置,再检查工作流关心的 JavaScript 值和 HTTP 响应。
关键在于区分默认值与显式选择。不带参数调用 Intl.DateTimeFormat() 时,会使用环境的默认 locale 和时区。为某次格式化调用传入显式 locale 或 timeZone,只会请求该格式化器返回不同结果;它不会重写 navigator.language、请求中的 Accept-Language 头、网站 cookie 或浏览器配置的时区。因此,区域设置检查应明确指出测量的是哪项信号,而不是把「locale」当成完整诊断。
TL;DR
- 时区控制如何将时刻显示为本地民用时间;IANA 时区不等于固定 UTC 偏移。
- locale 控制小数分隔符、月份名称等格式惯例;它本身不会选择网站语言。
navigator.language和navigator.languages描述浏览器语言偏好。Accept-Language向服务器传递偏好,服务器可以据此协商响应,也可以忽略它。- 网站可以把语言选择保存在 cookie 或账户设置中。这种记忆的选择不同于浏览器偏好和 HTTP 协商。
- 对于 BotBrowser 的每个字段,显式 CLI 值优先于所选配置文件的
configs;没有具体值时,auto根据代理路由派生设置。--bot-location是独立的地理位置控制项。
目录
- 观看视频
- 修改前先识别信号
- 默认值与显式 Intl 选项
- 时区不是偏移量
- 语言偏好与 HTTP 协商
- Cookie 与应用语言
- BotBrowser 的逐字段优先级
- 配置决策表
- 可复制的诊断查询
- 按观测层排查问题
- 限制与安全解读
- 实践结论
观看视频
这段英文视频检查设置区域后,时区、区域设置、语言列表和 Accept-Language 请求头是否仍然一致。视频在 Ubuntu 和 Windows 两台运行主机上,用原版 Chrome 加载一个受控的本地页面,数值由测试声明,因此只说明各处之间的关系,不代表任何真实用户或地区。测试不涉及 BotBrowser 配置文件、代理或地理位置。音轨为英文。
修改前先识别信号
「区域身份」是便于概括的说法,并不是某一项浏览器属性。测试页面、网络请求、服务器渲染的页面和登录后的站点,都可能体现不同的选择。两项观测不一致时,先记录它们分别来自哪里:JavaScript 运行时、请求头、响应头、应用存储,还是受权限控制的 API。这样可以避免常见的排查误区:为了修复 cookie 选择的内容,反而修改 locale flag。
以下术语的范围有意限定:
- 时区是一套把时刻解释为本地日期和时钟字段的规则。JavaScript 可通过
Intl.DateTimeFormat().resolvedOptions().timeZone等方法以及日期格式化默认值暴露它。 - Locale 是国际化 API 用于格式化或比较的 BCP 47 语言与地区偏好,例如
en-GB和de-DE;它不是城市或坐标。 - 语言偏好是
navigator.languages暴露的有序列表,navigator.language通常代表主要偏好。它们可用于内容协商,但不能强制服务器采用某种翻译。 - 地理位置是页面请求 Geolocation API 且浏览器上下文允许后返回的位置结果。它不是从
Intl格式化推导出的,也不能根据时区字符串推断。 - 网站语言是应用为自己的界面或内容选择的语言。它可能取决于请求、URL、cookie、账户偏好或服务器策略。
例如,页面可以调用 new Intl.NumberFormat('fr-FR'),而浏览器的主要语言仍是 en-US。服务器可能收到英语 Accept-Language 偏好,却因 URL 是 /fr/ 而返回法语内容。之后网站还可能把法语保存在 cookie 中,并在后续请求中继续返回法语。这些观测本身都不能证明其他层配置错误。
这种区分也有助于限定地理方面的判断。许多地点共享同一时区,一个国家也可能有多个时区。locale 不包含精确位置。语言偏好可能反映用户的工作语言,而不是代理所在国家。把这些值当作严格的国家对应链条会造成误报,并诱发不必要的覆盖配置。
默认值与显式 Intl 选项
ECMAScript 国际化 API(Intl)提供按 locale 格式化的能力。不传 locale 选项调用格式化器,相当于让运行时使用默认值。传入显式 locale,则要求该格式化器使用给定标签。代码能直接体现这一区别,诊断时也应区分两种情况:
const defaultDate = new Intl.DateTimeFormat().format(new Date());
const britishDate = new Intl.DateTimeFormat('en-GB').format(new Date());
const defaultNumber = new Intl.NumberFormat().format(1234567.89);
const frenchNumber = new Intl.NumberFormat('fr-FR').format(1234567.89);
这段示例有意不打印固定结果。格式化字符串可能取决于运行时附带的 locale 数据、当前时刻和格式化器选项。要对应用进行可复现的检查,应固定输入时刻,并检查解析出的选项和可见字符串。即便如此,检查描述的也只是该运行时和输入,并不代表测量了所有浏览器进程或 worker。
resolvedOptions() 有用,因为它会报告该格式化器实例选用的设置。对于默认格式化器,locale 和 timeZone 描述该格式化器解析出的运行时默认值。对于使用 'en-GB' 构造的格式化器,locale 结果描述该请求经解析后的结果。它不会报告 navigator.language、配置文件设置或后续 HTTP 请求发送的语言。
const defaultFormatter = new Intl.DateTimeFormat();
const explicitFormatter = new Intl.DateTimeFormat('en-GB', {
timeZone: 'Europe/London',
dateStyle: 'medium',
});
console.log(defaultFormatter.resolvedOptions());
console.log(explicitFormatter.resolvedOptions());
这里只展示示意形式;该片段没有在此运行,具体属性和格式化值应从目标运行时读取。关键不在于输出是否符合预期截图,而在于应用使用默认格式化器,还是显式传入了 locales 和选项。如果应用自行传入 locale,修改浏览器默认值可能不会影响这次调用。
应用代码中的日期格式化也遵循同一规则。库可能不传 locale 参数调用 Intl.DateTimeFormat()、传入固定 locale,或在创建格式化器前读取应用状态中的偏好。修改浏览器配置前,先搜索应用代码或检查调用边界。日期时间格式化指南讨论了应用层时区策略和可移植回退方案与浏览器默认值之间的区别。
Locale 匹配不等于命令浏览器翻译任意文本。Intl 会格式化受支持的数据、比较字符串,或从请求列表与可用数据中选择 locale。它不会翻译页面、重写 URL,也不会覆盖网站特定的产品语言策略。网站显示英文文本,同时按德国惯例格式化数字,可能完全符合开发者的配置。
时区不是偏移量
时刻是 UTC 时间线上的一个点。时区是一套有名称的规则,用于把时刻映射为本地民用时间字段和偏移量。America/New_York 根据适用的时区数据,在不同日期可能使用不同偏移。UTC-5 这样的固定字符串表达偏移量,不包含同一组历史与未来规则。因此,应用要求涉及某地区的时间规则时,应比较 IANA 名称;只有要求涉及某个具体时刻时,才比较偏移量。
Date.prototype.getTimezoneOffset() 返回所表示日期的数值偏移,单位为分钟,并采用 JavaScript 的符号约定。它不是稳定的地区标识。两个具名时区可能在某个时刻偏移相同,在另一个时刻却不同。不要把一次偏移读数当成特定时区的证明,也不要据此认定季节性转换一定一致。
const instant = new Date('2026-01-15T12:00:00Z');
const zone = new Intl.DateTimeFormat().resolvedOptions().timeZone;
const offsetMinutes = instant.getTimezoneOffset();
console.log({ zone, offsetMinutes });
这是示意查询,不是 BotBrowser 的记录结果。偏移量按运行时默认时区针对所选日期计算。比较两个日期时,应在相关转换前后分别构造日期并检查;没有查询目标地区当前规则前,不要假定具体的 DST 转换日期或结果。时区数据库和法律规则可能独立于应用代码发生变化。
Locale 和时区可以分别配置。用 America/New_York 时间配 en-GB 格式,可能是有意的产品选择:偏好伦敦语言环境的用户可能在查看以纽约办公室为基准的日程。反过来,网站可能需要按活动所在时区显示日期,同时让周围文本使用浏览器语言。两者应如何关联,取决于应用的含义,不存在普遍适用的「同一国家」要求。
纯日期值需要特别谨慎。2026-10-18 这样的字符串,在不同库和 API 中可能与 2026-10-18T09:00:00Z 这样的时刻有不同解释。把只含日期的业务值转换为时刻,可能导致它在另一个时区显示为不同的日历日期。如果需求是「会议在这个时刻开始」,就保留时刻,并在选定时区中格式化。如果需求是「办公室在这个本地日期休息」,就保留日历日期,避免增加会改变其含义的时区转换。
语言偏好与 HTTP 协商
navigator.language 和 navigator.languages 是描述首选语言的运行时属性。前者是主要语言;后者在可用时提供有序偏好列表。浏览器也可能发送 Accept-Language 请求头,通常带有质量权重。该请求头是服务器协商的输入,并不保证响应使用第一种语言,甚至不保证使用列表中的任何一种语言。
HTTP 服务器可以用 Accept-Language 选择响应表示,但结果也可能取决于 URL 结构、应用默认值、支持的翻译、身份验证状态或明确的产品规则。响应中的 Content-Language 头可提供表示内容的有用证据,但未必能描述复杂页面上的每段文本。应检查正在调查的路由所对应的实际请求和响应。
GET /account/ HTTP/1.1
Accept-Language: fr-CA,fr;q=0.9,en;q=0.8
这是示意请求;本文没有发出网络请求。如果服务器支持法语,该偏好列表可能帮助它选择法语表示。服务器也可能重定向、使用 URL locale、要求用户选择,或采用其他策略。MDN 的 Accept-Language 参考介绍该请求头及其协商作用。
不要把浏览器语言、locale 格式和 HTTP 语言当作同义词。de-DE locale 可以按德国惯例格式化小数和日期,同时语言列表却是 en-US,en。请求可以携带英语偏好,而页面显式创建 Intl.NumberFormat('de-DE')。每个值回答的问题不同:格式化器如何格式化、浏览器偏好什么,以及请求向服务器提供什么选项。
服务器对不同资源的选择也可能不同。文档路由可能协商 HTML,而 API 端点返回与语言无关的 JSON。脚本或图片请求也未必使用与页面相同的语言选择逻辑。归因于浏览器语言列表前,应比较同类内容:方法、URL、重定向、请求头、响应头和身份验证状态都应匹配。
代理与地理位置文档介绍代理派生的区域值和逐项设置覆盖。一项有用的 Web 平台检查会沿着语言偏好,从浏览器属性追踪到发出的请求,再与服务器选择的表示进行比较。网络路由可能影响自动设置,但不会消除服务器独立做出的协商决定。
Cookie 与应用语言
网站可能把语言选择保存在 cookie、本地存储、账户资料或 URL 中。存在这类状态后,即使浏览器发送不同的 Accept-Language 值,页面仍可能继续使用保存的选择。语言选择器通常表示应用的显式偏好,它合理地可能优先于自动协商。清除浏览器偏好或修改 --bot-languages,并不能可靠地重置服务器端账户设置。
只有在工作流允许时,才对比干净会话和现有会话。记录路由是否包含 /fr/ 这样的语言前缀、响应是否重定向、是否存在语言 cookie,以及账户是否保存 locale。避免删除无关 cookie 或存储:这可能抹去识别选择层所需的证据,也可能改变身份验证或同意状态。
页面一直停留在「错误」语言时,可以按以下顺序检查:
- 确认请求 URL 和其中的 locale 前缀。
- 检查请求的
Accept-Language值。 - 检查重定向,以及最终响应中与语言相关的头。
- 检查网站语言选择器、cookie 和账户偏好。
- 在允许的情况下,将干净上下文与原上下文比较,每次只改变一项输入。
这套顺序是一种诊断方法,不保证每个网站都会暴露其语言决策。应用可能混用多种语言、缺少 Content-Language,或把偏好保存在服务器端。没有证据时,应报告该层未知,而不是根据一个可见标签推断。
BotBrowser 的逐字段优先级
BotBrowser 的时区、locale 和语言控制是彼此独立的字段。相关命令行 flag 为 --bot-timezone、--bot-locale 和 --bot-languages。对于每个字段的具体值,文档规定的顺序是 CLI flag、所选配置文件的 configs,然后是自动选择。设置一个字段的值,并不要求覆盖其他字段。
| 字段 | CLI flag | 配置文件 configs 字段 | 自动选择 | 建议先观察 |
|---|---|---|---|---|
| 时区 | --bot-timezone | timezone | auto | Intl.DateTimeFormat().resolvedOptions().timeZone |
| Locale | --bot-locale | locale | auto | Intl.NumberFormat().resolvedOptions().locale |
| 语言 | --bot-languages | languages | auto | navigator.language、navigator.languages、请求头 |
按字段分别评估优先级。例如,显式的 --bot-locale=en-GB 可以与配置文件中的时区及自动语言选择同时使用。这种组合可能是刻意安排的,但要记录固定 locale 的原因,避免后续配置文件或路由变化后,看起来像是设置没有生效。相反,如果某字段应跟随路由,就应同时从启动参数和配置文件层移除过期的显式值,而不是再增加一层覆盖。
auto 表示「将此字段交由自动选择」;它不是 locale、时区或语言标签。文档说明具体 CLI 值优先于配置文件设置,并将 auto 描述为自动选择。文档没有定义 CLI 显式传入 auto,而配置文件提供具体值时如何冲突。在确认适用版本的行为前,应把这种组合视为未确定;不要仅根据 flag 的写法推断哪个值胜出。
地理位置是相关但不同的控制项。--bot-location 为请求地理位置 API 的页面配置位置结果,但仍受适用上下文的权限限制。坐标不会自动从时区推导,时区也不能证明坐标。代理与地理位置文档讨论代理 IP 输入和查询边界;除非来源针对具体配置明确说明,否则不要声称结果具有城市级或坐标精度。本文不声称某组坐标一定与出口 IP 相符。
代理一致性指南中的区域表说明如何共同检查代理路由和面向网络的身份信号。这里应把这种关系作为背景,而不是严格相等的测试:代理路由可能影响 auto,显式逐字段设置和应用策略也可能产生合理例外。
配置决策表
选择能够满足应用需求的最少显式值。下表用于辅助决策;其中的值是示例,不是适用于所有部署的建议。
| 需求 | 时区 | Locale | 语言 | 后续检查 |
|---|---|---|---|---|
| 让路由决定区域默认值 | 保持自动 | 保持自动 | 保持自动 | 读取所选值以及分配给此上下文的路由 |
| 让数字格式在不同路由间保持稳定 | 除非需要,否则保持自动 | 设置 --bot-locale 或配置文件 locale | 除非内容语言也要固定,否则保持自动 | 检查显式和默认的 Intl.NumberFormat 调用 |
| 路由变化时保持界面语言偏好 | 除非需要,否则保持自动 | 仅在格式也必须固定时选择 | 设置 --bot-languages 或配置文件 languages | 分别检查运行时偏好、请求头和网站选择 |
| 按活动场地的民用时间显示活动 | 在所属层设置活动的 IANA 时区 | 独立选择周边内容的格式 | 独立选择内容协商语言 | 使用显式 timeZone 选项格式化同一时刻 |
| 调查持续存在的网站语言选择 | 无证据时不要修改 | 不要假定 locale 会选择页面文本 | 将浏览器列表与请求进行比较 | 检查 URL、重定向、cookie 和账户偏好 |
如果区域发生变化,但应用只要求固定语言,同时设置时区、locale、语言和坐标会形成四条需要维护的覆盖路径。只有一个字段是需求时,优先只显式设置该字段。如果浏览器应跟随代理决定三个受支持的设置,就保持它们为自动,并把路由分配作为受控输入。如果不同上下文有意使用不同路由,则应分别验证每个上下文。
为配置保留精简记录,每个字段占一行:所选值、来源层(CLI、配置文件或自动)、负责人和原因。不要在记录中保存凭据。这样审查时能看清优先级,也有助于区分「该值来自配置文件」和「该值由路由选出」。覆盖项不再需要时应移除,而不是留着重复值以防万一。
可复制的诊断查询
下面的浏览器端查询会收集诊断相关值的精简快照。它分别读取默认值和显式格式化选项,不会抓取页面,也不会触发地理位置权限提示。只能在获准检查的上下文中运行。返回结果的结构仅供示意,并标记为 not_run;它不是记录所得的观测结果。
function inspectRegionalDefaults() {
const defaultDate = new Intl.DateTimeFormat();
const explicitDate = new Intl.DateTimeFormat('en-GB', {
timeZone: 'Europe/London',
dateStyle: 'medium',
});
return {
navigatorLanguage: navigator.language,
navigatorLanguages: [...navigator.languages],
defaultDateOptions: defaultDate.resolvedOptions(),
explicitDateOptions: explicitDate.resolvedOptions(),
defaultNumberLocale: new Intl.NumberFormat().resolvedOptions().locale,
explicitNumberLocale: new Intl.NumberFormat('de-DE').resolvedOptions().locale,
note: 'illustrative query; not_run',
};
}
console.log(inspectRegionalDefaults());
显式格式化器的值有意与默认值不同。这种差异体现的是单次调用选项,不是浏览器配置错误。若要检查 Accept-Language,应在获准的测试应用中捕获请求,或使用该应用的网络工具;JavaScript 没有可读取浏览器传出请求头的通用 API。不要把代理凭据或账户隐私数据放进公开诊断材料。
比较服务器行为时,只捕获最低限度的相关元数据:请求 URL、重定向链、Accept-Language、响应状态和语言相关响应头。Cookie 值应脱敏。如果网站提供语言切换功能,记录所执行的操作,以及新上下文中该偏好是否仍然保留。这些细节比包含整张已登录页面的截图更有用。
按观测层排查问题
| 观测结果 | 优先检查的层 | 下一项区分性检查 |
|---|---|---|
| 默认日期格式不同,但显式格式化器结果相同 | 运行时默认值或应用格式化器选择 | 将 resolvedOptions() 与格式化器的显式选项比较 |
resolvedOptions().timeZone 与目标时区不同 | CLI、配置文件或自动时区选择 | 依次检查 --bot-timezone、配置文件 timezone 和路由分配 |
navigator.languages 符合预期,但服务器页面不符 | HTTP 协商或网站状态 | 检查请求头、重定向、URL locale、cookie 和账户偏好 |
| 数字分隔符不同,但文本语言正确 | Locale 格式,而非语言协商 | 检查 Intl.NumberFormat 构造器的具体参数 |
| 一个上下文与另一个不同 | 每个上下文的配置文件、flags、路由或已有应用状态 | 逐字段比较两个上下文,并在更改后新建上下文 |
| 坐标与根据时区推断的城市不符 | 地理位置配置或应用权限 | 检查是否请求了地理位置,以及适用的 --bot-location 值 |
每次只更改一个责任层。如果存在显式 CLI 字段,修改配置文件不能证明配置文件才是生效来源。如果字段为自动,修改语言 cookie 也不能证明代理路由已经变化。更改前记录每个字段当前来源,然后在新上下文中重复相同的应用操作,避免页面旧状态掩盖结果。
不要把某个测试页面当作通用判定标准。极简页面可以显示运行时默认值,但无法显示所有请求实际发送的精确头信息。网络捕获可以显示某个请求,却不能说明应用随后保存了什么。渲染页面能揭示用户可见的结果,但不能说明是哪项来源选出了结果。只组合足以回答工作流问题的观测,不要把无关信号带入报告。
限制与安全解读
BotBrowser 公开文档描述了配置优先级和浏览器侧控制项。目标网站仍会自行作出决定。服务器可能忽略语言偏好,JavaScript 应用可能传入显式 Intl 选项,网站也可能记住用户选择。一次页面读取成功,不能证明所有路由、worker、frame 或未来请求都会使用相同值。
时区行为取决于具名时区的数据和被格式化的日期。不能根据一次针对当前日期的检查,就承诺所有夏令时转换和所有操作系统数据版本下的结果都正确。需要地区规则时使用 IANA 时区,针对实际应用测试有代表性的日期,并说明检查所用的运行时和日期。本文没有声称执行过 BotBrowser;以上描述的是文档所载的配置边界,不是实测兼容性。
自动地理位置取决于已配置的输入和可用的查询行为;仅凭时区无法确定纬度或经度。没有任何保证能获得城市级坐标,也不能保证结果与 IP 数据库匹配;网站可能不会请求或采用地理位置。权限状态和应用处理方式是独立条件。
这些浏览器属性可用于获准的兼容性测试、隐私研究和应用调试,但不能证明某个人的身份或实际所在位置。使用获准的测试账户和路由,不要收集超过问题所需的数据;对未知行为,应如实报告为未知,不要把合理相关性写成事实。
常见问题
修改 Intl locale 会改变 navigator.language 吗?
不会。为某个 Intl 构造器提供 locale,会改变该格式化器的 locale 解析结果。单凭这项操作,不会重写浏览器语言偏好属性,也不会改写网络请求头。如果应用传入显式格式化选项,应先检查调用参数,再修改浏览器语言配置。
Accept-Language 会强制网站显示对应语言吗?
不会。它是服务器在内容协商中可以采用的请求偏好。应用可能优先使用 URL 中的 locale、保存的 cookie、账户设置或产品默认值。应分别检查请求和网站状态,不要把请求头当作最终语言决定。
时区和 locale 应该总是对应同一个国家吗?
不应该。它们用途不同,有意设为不同值也完全合理。用户可以为格式偏好一种 locale,同时查看按另一地区时区安排的日程。根据应用需求选择值,再检查最终显示和网络行为是否满足该需求。
CLI 与配置文件设置不同时,哪个优先?
对于每个受支持字段,具体 CLI 值优先于所选配置文件的 configs 值,之后才是自动选择。文档没有规定显式传入 CLI auto 时是否覆盖配置文件中的具体值。时区、locale 和语言要分别判断,不要假设一个 flag 会覆盖整组设置。
时区足以推断地理位置坐标吗?
不够。时区是一组多个地点共享的规则,不是坐标。地理位置是浏览器 API 的独立结果,取决于配置和权限流程。如果坐标属于需求,应使用文档说明的位置控制项,并检查获准应用中的结果。
来源
- BotBrowser:Timezone, Locale & Language
- BotBrowser:Proxy and Geolocation
- MDN:Intl
- MDN:Intl.DateTimeFormat
- MDN:Accept-Language
- MDN:Date
- ECMA-402:Internationalization API Specification
实践结论
应把时区、格式 locale、浏览器语言偏好、HTTP 协商和网站记住的语言视为彼此独立的输入。先找出哪项观测不正确,再追踪该字段的负责人:显式 CLI 值、配置文件设置、代理派生的自动选择、应用代码或网站状态。只保留工作流确实需要的覆盖项,在新上下文中重复相同的获准检查,并准确描述观测结果,不要将其泛化到所有 worker、日期转换、路由或坐标。
本文属于指南: 浏览器身份表面:一致性实用总览