MiguelSlv
MiguelSlv

Reputation: 15113

How to set HTML lang attribute dynamically on NextJs Document?

I have a multi language site and need to set up the HTML lang attribute according the language for the each page.

I try to pass the value in context, but does not update when page changes.

Here the current code:

import Document, { Html, Head, Main, NextScript } from 'next/document'
import GlobalContext , {eLanguage }from '../components/GlobalContext' //my global context 

export default class MyDocument extends Document {

static async getInitialProps(ctx) {

  const initialProps = await Document.getInitialProps(ctx)
  return { ...initialProps }
}
static contextType = GlobalContext;
render() {

  console.debug('Started')
  console.debug('language:'+ this.context.language) 

  return (
    <Html lang={eLanguage[this.context.language]}> //if the first page loaded as lang 'en' it sets 'en' and apply to all other pages.
      <Head>
      </Head>
      <body>       
        <Main />
        <NextScript />
      </body>
    </Html>
  )
}

}

Update: The language of each page can be inferred from the page route

Upvotes: 44

Views: 52318

Answers (11)

Kolade Chris
Kolade Chris

Reputation: 195

You can add the lang attribute without creating _document.js

All you need to do is add this code to your next.config.js:

i18n: {
    locales: ['en'],
    defaultLocale: 'en',
  },

Upvotes: 2

Juraj Bublinec
Juraj Bublinec

Reputation: 490

Most answers assume that the lang attribute is on the html tag is an alternative to link with a hreflang attribute. However, this is not the case!

You should provide both, as they are complementary.

This is how I implemented my hreflang links in NEXT.JS:

export const LayoutController = () => {
  const router = useRouter();

  return (
    <Layout>
      <Head>
         {router.locales.map((locale) => (
          <link
            key={locale}
            rel="alternate"
            href={`${process.env.NEXT_PUBLIC_BASE_URL}${
              locale === DEFAULT_LOCALE ? '' : `/${locale}`
            }${router.asPath}`}
            hrefLang={locale}
          />
        ))}
      </Head>
      {children}
    </Layout>
  );
};

These links were generated in my html page:

enter image description here

Upvotes: 2

Pavel Sheremet
Pavel Sheremet

Reputation: 81

Create _document.js in `pages`` folder and use this:

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

class MyDocument extends Document {
  static async getInitialProps(context) {
    const initialProps = await Document.getInitialProps(context);
    return { ...initialProps };
  }

  render() {
    return (
      <Html lang={this.props.locale}>
        <Head />
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

export default MyDocument;

for localization use next.config.js

{
 i18n: {
    locales: ['en', 'fr', 'de',],
    defaultLocale: 'en',
  },
}

Upvotes: 8

Osama Malik
Osama Malik

Reputation: 355

Nextjs versions after 10 provides default localization support, You don't have to configure much.

It automatically adds lang attribute to html, but still there is no support until now with v12 for dir attribute to include that we can use this small trick inside _document file.

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

function Document(props: any) {

  return (
    <Html dir={props.__NEXT_DATA__.locale === "en" ? "ltr" : "rtl"}>
      <Head></Head>
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

export default Document;

The end result would be enter image description here

Upvotes: 4

FabioRosado
FabioRosado

Reputation: 503

If you use next/head you can set the language to the html tag. Anything that you pass to the Head component will be either placed in the <head> or <html>.

Next Head works similar to React Helmet, so for your case you could do something along these lines:

  • Create a component and import Head from "next/head"
  • Inside the Head tag you add the <html lang={lan} /> to the component.

Then you can pass the desired language to that component, then import the component on the desired pages.

import React from "react"
import Head from "next/head"

const Language = ({title, lang}) => (
  <Head>
    <html lang={lang} />
    <title>{title}</title>
  </Head>
)

export default Language

That html bit will be injected inside the <html> tag.

Note that even if we inject it like this the console will log the following error: TypeError: n is null.

Upvotes: 25

Victor
Victor

Reputation: 25

To overide default nextjs settings create the file ./pages/_document.js and extend the Document class as shown below:

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

class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const initialProps = await Document.getInitialProps(ctx)
    return { ...initialProps }
  }

  render() {
    return (
      <Html>
        <Head />
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    )
  }
}

export default MyDocument

Custom attributes are allowed as props, like lang:

<Html lang="en">

You can read more about custom document here: https://nextjs.org/docs/advanced-features/custom-document

Upvotes: -2

Richie Nabuk
Richie Nabuk

Reputation: 157

I implemented this by adding this to next.config.js file:

i18n: {
// These are all the locales you want to support in
// your application
locales: ['en-US'],
// This is the default locale you want to be used when visiting
// a non-locale prefixed path e.g. `/hello`
defaultLocale: 'en-US' }

I didn't have the need to create a custom _document.js

Upvotes: 6

stef
stef

Reputation: 14268

You can use a React useEffect hook to set the document's language without having to change the way that Next.js generates the HTML tag itself.

Within your page component or another appropriate component, include the useEffect hook:

import {useEffect} from "react";

And then add the hook:

const MyPage = () => {
  useEffect(() => {
    document.documentElement.lang = "en-us";
  });
  // The rest of your component
}

This passes Lighthouse's check for "hreflang", and if your site has multiple languages, you can use this to set the page language per page.

Upvotes: 1

Joel Varty
Joel Varty

Reputation: 625

I believe the best solution here is to use a custom ./pages/_document.js file and override the document itself.

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

class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const initialProps = await Document.getInitialProps(ctx)
    return { ...initialProps }
  }

  render() {
    return (
      <Html lang="en">
        <Head />
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    )
  }
}

export default MyDocument

More explanation can be found here: https://nextjs.org/docs/advanced-features/custom-document

Upvotes: 31

vhs
vhs

Reputation: 10061

Next 10 supports Internationalized Routing and will add lang dynamically leaving you with:

<Html>
  <Head />
  <body>       
    <Main />
    <NextScript />
  </body>
</Html>

Upvotes: 17

MiguelSlv
MiguelSlv

Reputation: 15113

Using document object the lang attribute can be set like this:

var language= ...
switch (language) {
    case en:  document.documentElement.lang = 'en-us'; break;
    ...
}

This lang attribute will not be set on the initial html, before page is hydrated, but will still pass chrome "hreflang" audit check.

Upvotes: -2

Related Questions