- The
localeandtimezoneoptions, when you pass them. - A GeoIP lookup of the exit, when
geoipis on, which is the default. en-USand the host’s timezone.
--fingerprint-locale and --fingerprint-timezone. For the defaults they pass neither switch.
The GeoIP lookup
Before the browser starts, the package asks where the exit is. With a proxy, the exit is the proxy’s. Without one, it is the host’s own connection.- It asks
ip-api.com,ipinfo.io,ipwho.isandifconfig.co, in that order, over plain HTTP, through the proxy when there is one. - It tries each service twice, 5 seconds per try, and stops at the first answer that has both a country and a timezone.
- The whole lookup may take 20 seconds. Change the limit with
geoip_timeoutin Python orgeoipTimeoutMsin Node.
config/country-locales.json. The table covers 257 territories. It gives each the tag of its main official language in a form Chrome lists, and English where desktop installs are English, as in India and Pakistan. Some entries:
GeoIP always gives one tag, never a list.
Set them yourself
Pass both options to make a launch independent of the lookup. The Python package then skips it. Behind a proxy, the Node package still runs it to learn the exit IP for WebRTC, unlessargs has --fingerprint-webrtc-ip (WebRTC).
examples/python/locale_timezone.py and examples/node/locale-timezone.mjs run the same check.
One tag or a list
The first tag becomes the browser’s UI locale. What happens to the language list depends on the form you pass:- One tag, such as
de-DE, sets only the UI locale.navigator.languagesand theAccept-Languageheader are then Chrome’s own default list for that UI locale, as on a real machine set to that language. - A comma list, such as
de-DE,de, setsnavigator.languagesandAccept-Languageexactly.
locale:
Chrome’s UI locale for Japanese is
ja, so the tag ja-JP gives the same result as ja. Chrome’s default lists include English after the local language. A real user who removed English from Chrome’s language settings sends a list without it. Pass a list to match that.
What Intl shows
The UI locale isIntl’s default locale. It decides date, time and number formats, collation, and the default calendar and hour cycle. Intl.DateTimeFormat().resolvedOptions().locale reports the UI locale as Chrome names it, such as de for de-DE and for de-CH, and ja for ja-JP. With ja-JP and Asia/Tokyo, the same noon UTC prints as 2026/9/27 21:00:00 and the number as 1,234,567.891.
The timezone sets Intl.DateTimeFormat().resolvedOptions().timeZone, Date offsets and every local time a page formats. Pass an IANA name such as Europe/Berlin. From 0.4.4 the packages refuse any other name before the browser starts. Python raises ConfigurationError:
RangeError with the same wording.
The 0.4.3 packages do not check the name. They put an unknown name in TZ, and a page’s Intl.DateTimeFormat().resolvedOptions().timeZone then reads undefined.
Environment variables
For a persona, the packages also set the browser’s locale environment, so that the C library and every child process agree with the UI locale. They set:
The values come from the first tag, and
TZ from the timezone when there is one. Without a locale, the four locale variables get en-US and en_US.UTF-8, so your shell’s language never reaches a persona. Host mode sets none of them. An env you pass to the package is applied last, so your entries win.
Voices
speechSynthesis.getVoices() lists the voices of the persona’s platform, chosen by the first tag. The voice tables distinguish en-US and en-GB. Any other tag gets the en-US set.
—lang
Chromium’s--lang switch has no effect on a persona. The launch below still presents German:
locale option, or --fingerprint-locale when you run the browser without the packages.
Without GeoIP
geoip=False in Python or geoip: false in Node skips the lookup. A launch with no locale then presents en-US, and one with no timezone presents the host’s timezone.
timezone whenever you turn GeoIP off.
When the lookup fails
A lookup that fails or times out does not stop the launch. The package sends no locale or timezone switch, so the persona presentsen-US and the host’s timezone. It reports a warning:
- The Python package prints it to stderr, prefixed
apostate:. From 0.4.4 it also adds it tobrowser.apostate_diagnostics["warnings"](Python). - The Node package prints it with
console.warn, prefixed[Apostate], adds it tobrowser.apostateDiagnostics.warningsand setsapostateDiagnostics.geoiptounresolved(Node).
Your own lookup
To use your own GeoIP service, pass a function. It receives the proxy URL and returns a two-letter country code and an IANA timezone. The package maps the country to a locale as above.locale or languages to set the tag directly.
Without the packages
The browser reads--fingerprint-locale and --fingerprint-timezone directly. Without them, a persona presents en-US and the host’s timezone, never the host’s language. The browser logs an unknown timezone passed as a switch and keeps the host’s. Switches lists both, and Raw binary covers running the browser yourself.