Language and Locale with Puppeteer

A complete guide on how to configure browser language and locale with Puppeteer.


I have not found a decent and working example on how to set language and locale of a browser instance neither with Google, nor with ChatGPT, nor in the Puppeteer documentation.

However, it is one of the basic features, that should be available with one simple function call, but it is not.

Per browser instance

I will try to be short, but that’s a working example to configure the language and the locale of a browser instance with Puppeteer, globally:

import puppeteer from "puppeteer";

const browser = await puppeteer.launch({
    headless: true,
    args: ["--accept-lang=de-DE", "--locale=de-DE"],
});

const page = await browser.newPage();

// it is critical to call this before the page is loaded
// it has impact on date formatting, number formatting, and other locale-specific information
const cdpSession = await page.createCDPSession();
await cdpSession.send("Emulation.setLocaleOverride", {
    locale: "de_DE", // ICU style C locale
});

await page.goto("https://httpbin.org/headers");

const data = await page.evaluate(() => {
    return {
        navigator: {
            language: navigator.language,
            languages: navigator.languages,
        },
        locale: new Intl.NumberFormat().resolvedOptions().locale,
        acceptLanguage: JSON.parse(document.body.textContent).headers[
            "Accept-Language"
        ],
    };
});

console.log(data);
//   {
//     navigator: { language: 'de-DE', languages: [ 'de-DE' ] },
//     locale: 'de-DE',
//     acceptLanguage: 'de-DE'
//   }

await page.close();
await browser.close();

Calling Emulation.setLocaleOverride is critical to change the locale. It won’t work without it. And it has impact on date formatting, number formatting, and other locale-specific information.

Not that Emulation.setLocaleOverride should be called before the page is loaded and for every new page you create.

Also, you might want to set the timezone:

await page.setTimeZone("Europe/Berlin");

Per page

If you need to set the language and locale with Puppeteer only for a specific page, you can do it like this:

import puppeteer from "puppeteer";

const browser = await puppeteer.launch({
    headless: true,
});

const page = await browser.newPage();

await page.setExtraHTTPHeaders({
    "Accept-Language": "de-DE",
});

// it is critical to call this before the page is loaded
// it has impact on date formatting, number formatting, and other locale-specific information
const cdpSession = await page.createCDPSession();
await cdpSession.send("Emulation.setLocaleOverride", {
    locale: "de_DE", // ICU style C locale
});

// it is a hacky way to set the language and locale,
// but it works
await page.evaluateOnNewDocument(() => {
    Object.defineProperty(navigator, "language", {
        get: function () {
            return "de-DE";
        },
    });
    Object.defineProperty(navigator, "languages", {
        get: function () {
            return ["de-DE"];
        },
    });
});

await page.goto("https://httpbin.org/headers");

const data = await page.evaluate(() => {
    return {
        navigator: {
            language: navigator.language,
            languages: navigator.languages,
        },
        locale: new Intl.NumberFormat().resolvedOptions().locale,
        acceptLanguage: JSON.parse(document.body.textContent).headers[
            "Accept-Language"
        ],
    };
});

console.log(data);
//   {
//     navigator: { language: 'de-DE', languages: [ 'de-DE' ] },
//     locale: 'de-DE',
//     acceptLanguage: 'de-DE'
//   }

await page.close();
await browser.close();

Summary

It is pretty easy to set the language and locale of a browser instance with Puppeteer. However, there is a catch with the locale, and that even setting extra HTTP headers, does not work for the navigator.language and navigator.languages.