Internationalization

This article shows you how to set up internationalization support in a Next.js app with Prismic. You will be using Next.js’ built-in internationalization features to add fully-featured support for multiple locales in your app.


Configure Route Resolver

The Route Resolver is an API option that resolves the URL for a page. In this example of an internationalized website, the Route Resolver will localize the content using a locale code in the URL. If the URL does not contain a locale code, the default locale is used.

Here's how that might look:

  • /hello-world (English, the default locale)
  • /fr-fr/bonjour-le-monde (French)

You can include locale codes in your app’s Route Resolver paths using the :lang? symbol.

prismicio.js
Copy
const routes = [
  {
    type: 'homepage',
    path: '/:lang?',
  },
  {
    type: 'page',
    path: '/lang?/:uid',
  },
]

The ? in lang? configures the Route Resolver to exclude the locale if it is the Prismic repository’s default locale.

Configure Next.js

To enable Next.js’ built-in internationalization support, add an i18n property to your project’s next.config.js file pointing to a list of your Prismic repository’s locale codes.

You can query your Prismic repository using @prismicio/client to automatically retrieve a list of your content’s locale codes. Note that the configuration object is wrapped in an async function.

next.config.js
Copy
const prismic = require("@prismicio/client");
const sm = require("./slicemachine.config.json");

/** @returns {Promise<import('next').NextConfig>} */
module.exports = async () => {
  const client = prismic.createClient(sm.repositoryName);

  const repository = await client.getRepository();
  const locales = repository.languages.map((lang) => lang.id);

  return {
    i18n: {
      locales,
      // This is the default locale. It will not be included in URLs.
      defaultLocale: locales[0],
    },
  };
};

Your app now supports routes prefixed with locale codes, like /en-us/hello and /fr-fr/bonjour.

Query by locale

You’ll need to query content by the route’s locale. Next.js provides the route’s locale code to getStaticProps() through the locale parameter.

Pass the locale parameter to your query’s lang parameter.

src/pages/[uid].js
Copy
export async function getStaticProps({ params, locale, previewData }) {
  const client = createClient({ previewData });

  const page = await client.getByUID("page", params.uid, { lang: locale });

  return {
    props: {
      page,
    },
  };
}

Define paths for all locales

If you have a dynamic route with getStaticPaths(), such as in a src/pages/[uid].js file, update your Prismic query in getStaticPaths() to fetch documents from all languages.

Add a lang: '*' parameter to your query.

src/pages/[uid].js
Copy
export async function getStaticPaths() {
  const client = createClient()

  const pages = await client.getAllByType('page', { lang: '*' })

  return {
    paths: pages.map((page) => page.url),
    fallback: false,
  }
}

Create a locale switcher

Now that routes are configured to support locales, your project needs a way to switch between locales.

In this example, you’ll see how to render a simple list of links linking to a page’s available locales. Feel free to customize it to fit your app.

Add the getLocales() helper

You’ll need a helper function to fetch a list of all the locales available for a specific document. This function will be used in your pages’ getStaticProps() function.

Create a file at src/lib/getLocales.js (you may need to create a lib directory) with the following contents.

src/lib/getLocales.js
Copy
/**
 * Returns an array of document metadata containing each locale a document has
 * been translated into.
 *
 * A `lang_name` property is included in each document containing the document's
 * locale name as it is configured in the Prismic repository.
 *
 * @param {import("@prismicio/types").PrismicDocument} doc
 * @param {import("@prismicio/client").Client} client
 *
 * @returns {Promise<(import("@prismicio/types").PrismicDocument & { lang_name: string })[]>}
 */
export async function getLocales(doc, client) {
  const [repository, altDocs] = await Promise.all([
    client.getRepository(),
    doc.alternate_languages.length > 0
      ? client.getAllByIDs(
          doc.alternate_languages.map((altLang) => altLang.id),
          {
            lang: '*',
            // Exclude all fields to speed up the query.
            fetch: `${doc.type}.__nonexistent-field__`,
          }
        )
      : Promise.resolve([]),
  ])

  return [doc, ...altDocs].map((doc) => {
    return {
      ...doc,
      lang_name: repository.languages.find(
        (lang) => lang.id === doc.lang
      ).name,
    }
  })
}

Query a document’s available locales

In your pages’ getStaticProps() function, use the getLocales() helper to fetch a list of all the available locales for the page.

Return the result as a prop called locales.

src/pages/[uid].js
Copy
 import { getLocales } from "@/lib/getLocales"

  export async function getStaticProps({ params, locale, previewData }) {
    const client = createClient({ previewData })
  
    const page = await client.getByUID('page', params.uid, { lang: locale })
  
   const locales = await getLocales(page, client)
  
    return {
      props: {
        page,
       locales,
      },
    }
  }

Display a list of locale links

The locales prop is an array of metadata for each of the page’s available languages.

You can turn that array into a list of links like this:

Copy
import { PrismicNextLink } from '@prismicio/next'

const Page = ({ page, locales }) => {
  return (
    <ul>
      {locales.map((locale) => (
        <li key={locale.id}>
          <PrismicNextLink href={locale.url}>{locale.lang_name}</PrismicNextLink>
        </li>
      ))}
    </ul>
  )
}

We recommend adding the locale switcher somewhere in your app’s header or footer in an easily discoverable location.


Related articles


Was this article helpful?
Not really
Yes, Thanks

Can't find what you're looking for? Spot an error in the documentation? Get in touch with us on our Community Forum or using the feedback form above.