Docs

Set the page language

Set the page language

The lang attribute on the <html> tag says which language the page is written in: <html lang="en">. Screen readers use it to pick the right voice and pronunciation; without it, a Slovenian page read with an English voice is barely understandable. Browsers use it to decide whether to offer a translation, and search engines take it as one of the signals for which audience the page is for.

It is one attribute, usually set in one place for the whole site. The site checklist warns when the home page has none.

What the value should be

The value is a language code (BCP 47): a two-letter language, optionally followed by a region.

Value Meaning
en English
en-GB, en-US English, with a region when the difference matters
sl Slovenian
pl Polish
de-AT German as used in Austria

Common mistakes:

  • sl_SI, en_US with an underscore are server locale names, not HTML language codes. Use a hyphen: sl-SI, or simply sl.
  • si is Sinhala, not Slovenian. Slovenian is sl.
  • One value for every language. On a multilingual site the Polish pages need pl and the English ones en; a site-wide lang="en" on every page is wrong on most of them.

The checklist only checks that the attribute is there. Whether the value matches the content is for you to confirm.

Check it yourself

Replace example.com with your site:

curl -s https://example.com | grep -io '<html[^>]*>'

The output should contain lang="…" with the right code, for example <html lang="en-GB">. On a multilingual site, run it for one page in each language.

How to fix

Pick how your site is built. On most platforms the language comes from one setting; on custom code it is one line in the main layout.

WordPress

WordPress writes the attribute itself, from Settings → General → Site Language, through the language_attributes() function in the theme. If the attribute is missing, check two things.

1. The site language. Open Settings → General and pick the right Site Language, then Save Changes.

2. The theme. Block themes (Twenty Twenty-Two and newer) always print it. A classic theme has to call language_attributes() in header.php, and some older or custom themes write a bare <html> instead. Open header.php (in a child theme, so an update does not overwrite your change) and make the line look like this:

<!DOCTYPE html>
<html <?php language_attributes(); ?>>

Multilingual sites. WPML and Polylang switch the language per page through the same function, so with a theme that calls language_attributes() each translation gets its own code with no extra setup. If every language shows the same code, the theme has the value hard-coded: fix header.php as above.

Then clear the page cache (WP Rocket, LiteSpeed Cache, W3 Total Cache, your host's cache).

Node.js and Next.js

Next.js, App Router. The root layout writes the <html> tag. In app/layout.tsx (or .js):

export default function RootLayout({ children }) {
    return (
        <html lang="en">
            <body>{children}</body>
        </html>
    );
}

With one route per language (app/[lang]/layout.tsx), take the value from the route. In Next.js 15 and newer params is a promise:

export default async function RootLayout({ children, params }) {
    const { lang } = await params;

    return (
        <html lang={lang}>
            <body>{children}</body>
        </html>
    );
}

Next.js, Pages Router. With i18n in next.config.js, Next.js sets lang for you. Without it, set it in pages/_document.js:

import { Html, Head, Main, NextScript } from 'next/document';

export default function Document() {
    return (
        <Html lang="en">
            <Head />
            <body>
                <Main />
                <NextScript />
            </body>
        </Html>
    );
}

Other Node.js apps (Express with a template engine, Nuxt, Astro, SvelteKit): set it where the <html> tag is written. In SvelteKit that is src/app.html, in Astro the main layout, in Nuxt app.head.htmlAttrs in nuxt.config.

Laravel

New Laravel projects already have this in their layouts; projects that started from a custom template often do not. In the main layout (for example resources/views/layouts/app.blade.php):

<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">

app()->getLocale() is the application locale from config/app.php (APP_LOCALE in .env), or whatever your language middleware sets per request. The str_replace turns a locale like sl_SI into the valid sl-SI.

Static HTML

Every HTML file starts with the same tag. Add the attribute to it on every page:

<!DOCTYPE html>
<html lang="en">

If the site uses a generator (Hugo, Jekyll, Eleventy), change it once in the base layout instead. In Hugo, set languageCode in the site configuration and use <html lang="{{ site.Language.LanguageCode }}"> in the base template.

Shopify

Shopify themes write the attribute from the shop language: Dawn and other current themes use <html lang="{{ request.locale.iso_code }}">. The languages are set in Settings → Languages.

If it is missing, the theme was customised or is old. Open Online Store → Themes → … → Edit code, find layout/theme.liquid and change the <html> tag to:

<html lang="{{ request.locale.iso_code }}">

Wix

Wix writes the attribute from the site language, and with Wix Multilingual it switches per language. You cannot edit the <html> tag. If the checklist shows the wrong language, change the site language in the site's language settings in the dashboard; if it shows none, contact Wix support.

Webflow

Webflow takes the value from the site's primary locale: Site settings → Localization, set the primary locale, then publish the site again. With Webflow Localization, each locale gets its own code on its own pages.

Squarespace

Squarespace writes the attribute from the site language in Settings → Language & Region. Change it there if it is wrong. You cannot edit the <html> tag directly.

Confirm the fix

Run the curl command from Check it yourself again, then open the target in PulseKeeper and click Check now on the Checklist tab. The item turns OK within a few minutes instead of at the next daily run.