What Apostate changes
Thelocale option sets the browser’s UI locale, navigator.languages and the Accept-Language header. The timezone option sets the timezone that Intl and Date use. The browser takes both at launch, so the request headers, the page and its workers agree. A Windows persona shows only Windows fonts, and only the ones installed on the host. With the Windows fonts installed, a screenshot uses the fonts a Windows visitor has. Locale and timezone covers both options.
Set up
check_locales.py opens a page for each region, reads what the page sees and saves a full-page screenshot. With no argument, it serves a local test page that picks its language from Accept-Language and shows dates, numbers, a price, a relative time and a list formatted by Intl.
The script
Each region has a locale, a timezone and the name of an environment variable that may hold a proxy in that region:Accept-Language:
Run it
htmlLangis thelangattribute of the page the server returned. On your site, it shows which translation each locale received.dateis the same instant, noon UTC on 15 January 2026, in each region’s timezone and format.ja-JPgives a language list that starts withja, andar-EGone that starts withar, because Chrome’s UI locale for these languages carries no region. One tag or a list shows how to pass an exact list instead.
out/, one per locale. Each is 1920 pixels wide, the width of the window seed 42 opens as a Windows persona.
Check your own site
Pass the page’s URL:date and number come from the browser, so they are the same for any page. htmlLang and the screenshots show what your site served.
Regions and proxies
A site that chooses the language, currency or content from the visitor’s IP address needs a visit from that region. Put a proxy in the region’s variable, and that region’s launch goes through it:de-DE Europe/Berlin through APOSTATE_PROXY_DE. The script still passes the region’s locale and timezone, so the package skips its GeoIP lookup. Use a proxy whose exit is in that region. Proxies covers proxy URLs and credentials.
Points for this job
- Seed and persona. Every region uses seed 42 as Windows, so only the locale and timezone change between screenshots. To compare macOS rendering, change
fingerprint_platformincheck(). - Fonts. The host’s font engine draws the glyphs, so glyph edges in a screenshot can differ slightly from a Windows machine’s (Known gaps). Compare screenshots taken on one host with each other.
- Voices and keyboard. A Windows persona lists English speech voices, and every persona reports a US keyboard layout, whatever the locale. For a site that uses either, Windows voices are English and The keyboard layout is US say what to do.
- Timezone names. Pass IANA names such as
Asia/Tokyo. From 0.4.4 both packages refuse any other name before launch. Python raisesConfigurationErrorwithtimezone 'Asia/Tokio' is not an IANA zone name, such as Europe/Berlin, and Node throwsRangeErrorwith the same text. In 0.4.3 the package writes an unknown name intoTZ, and a page’sIntltimeZone readsundefined. Locale and timezone covers whatIntlshows. - Headless. The script takes the screenshots headless, the default. A persona presents the same screen and window headless as headed (Screen and window).