Multilingual Templating

This article collects all the resources you need to take advantage of Prismic's multi-language and localization feature when building a Next.js website.


Add languages to your repository

The first step is to go to your Dashboard, select your Prismic repository's Settings > Translations and locales, and add all the languages you need in your repo's Settings. You can choose from the library of language codes, or you can create a custom locale.

Create dynamic page routes

In Next.js, you can create dynamic routes by wrapping pages in brackets, for example, [category].js.

For example, we have two languages in our repository, English, and French; and a repeatable Custom Type called 'Page'. Then, in our project code, we'll create a 〜/pages/[lang] folder and a [uid].js file inside of it. This [lang]/[uid] route structure will allow us to build the following routes using the lang code and the document's UID:

  • English: /en-us/about-us
  • French: /fr-fr/a-propos-de-nous

Query by Language

Once you have set up your dynamic routes and created all of your documents and translations in your repository, you'll need to query the documents by language.

The configuration will vary depending on whether your project uses Slice Machine or not. Below, select the case that fits your project.

With @prismicio/client

Next.js' data fetching functions help you to build your queries and dynamic page routes. getStaticPaths() determines all of the URL routes to be created for a given page, and getStaticProps() gets the data for those pages to build queries.

Example: In a 〜/pages/[lang]/[uid].js file, we make a query by Type in getStaticPaths to get the type 'Page' documents in all languages '*'. Afterward, we build the paths using each document's lang and uid. Then, we query by UID using the params from getStaticProps to get our Page documents. Then, we return the doc to the PageTemplate to render the data.

Copy
import React from 'react'
import Prismic from '@prismicio/client'

// Update the path for your SliceZone file.
import SliceZone from '../path-to-your-slicezone-component'

// Update your-repo-name with the name of your repository.
const apiEndpoint = 'https://your-repo-name.cdn.prismic.io/api/v2'
const client = Prismic.client(apiEndpoint, { req })

const PageTemplate = ({ doc }) => {
  return (
    <SliceZone sliceZone={doc.data.body} />
  );
};

export async function getStaticProps({ params }) {
  const doc = await Client().getByUID('page', params.uid, { lang: params.lang });
  return {
    props: {
      doc,
    },
  };
}

export async function getStaticPaths() {
  const docs = await Client().query(
    Prismic.Predicates.at('document.type', 'page'),
    { lang: '*' }
  );
  return {
    paths: docs.results.map((doc) => {
      return { params: { uid: doc.uid, lang: doc.lang }};
    }),
    fallback: false,
  };
}

export default PageTemplate;

If you'd like to build a query for your homepage using a single type document, read Query Singleton Types

With next-slicezone

Slice Machine's Lifecycle hooks, an extended functionality version of getStaticProps() and getStaticPaths(), allow you to query data from Prismic and build your URL routes.

Example: In a 〜/pages/[lang]/[uid].js file, we pass the lang and uid to the apiParams on useGetStaticProps to query the pages, and the same to the formatPath of useGetStaticPaths to build the params routes. Then, we return the doc to the PageTemplate to render the data.

next-slicezone version

This example uses the latest version of the next-slicezone package (0.1.0-alpha.0), which changes how values from the parameters are called. Learn more about next-slicezone's Lifecycle hooks.

To install this run npm install next-slicezone@0.1.0-alpha.0

Copy
import Prismic from '@prismicio/client'
import SliceZone from 'next-slicezone'
import { useGetStaticProps, useGetStaticPaths } from 'next-slicezone/hooks'

// Update the paths for your functions and component files.
import resolver from '../path-to-your-resolver-config'
import Layout from '../path-to-your-layout-component'

// Update your-repo-name with the name of your repository.
const apiEndpoint = 'https://your-repo-name.cdn.prismic.io/api/v2'
const Client = Prismic.client(apiEndpoint, { req })

const PageTemplate = ({ slices }) => {
  return (
    <SliceZone slices={slices} resolver={resolver} />
  );
};

export const getStaticProps = useGetStaticProps({
  client: Client(),
  apiParams({ params }) {
    return {
      lang: params.lang,
      uid: params.uid,
    }
  },
})

export const getStaticPaths = useGetStaticPaths({
  client: Client(),
  formatPath: (prismicDocument) => {
    return {
      params: {
        uid: prismicDocument.uid,
        lang: prismicDocument.lang,
      },
    }
  },
})

export default PageTemplate;

Learn more about the Lifecycle Hooks in the next-slicezone technical reference.


Create a language switcher

Now that your dynamic localized routes are built, you need to create a navigation button so users can select to switch between languages on your website. This can be anything from a <select> element if you have many languages, a button, or a boolean toggle switch if you only have two.

In this example, we'll show you how to build a component that renders a list of links with the flags of each language.

Get the alternate languages

The first step is to get the alternate_languages. We will get them from the response in our page query and then pass it as props to the lang switcher component (we'll create it in the next step).

Example: In a 〜/pages/[lang]/[uid].js file:

  • If you're using @prismicio/client: Return the alternate_languages from the query response in getStaticProps, and pass them as props in the PageTemplate to the LanguageSwitcher component.
  • If you're using next-slicezone: Retrieve the props in the PageTemplate and pass them to the LanguageSwitcher component.
  • With @prismicio/client
  • With next-slicezone
Copy
// truncated code

// Update the path for your LanguageSwitcher file.
import LanguageSwitcher from '../path-to-your-LanguageSwitcher-component'

const PageTemplate = ({ doc, altLangs }) => {
  return (
    <section>
       <LanguageSwitcher altLangs={altLangs} />
       <SliceZone sliceZone={doc.data.body} />
    </section>
  );
};

export async function getStaticProps({ params }) {
  const doc = await Client().getByUID('page', params.uid, { lang: params.lang });
  const altLangs = doc.alternate_languages;
  return {
    props: {
      doc,
      altLangs,
    },
  };
}
Copy
// truncated code

// Update the path for your LanguageSwitcher file.
import LanguageSwitcher from '../path-to-your-LanguageSwitcher-component'

const PageTemplate = ({ slices, alternate_languages }) => {
  return (
    <section>
      <LanguageSwitcher altLangs={props.alternate_languages}
      <SliceZone slices={slices} resolver={resolver} />;
    </section>
  );
};

Create a language switcher component

In the /components folder, create a LangSwitcher.js file and return the altLangs that we previously passed as props. We'll use next/link to create the links between routes with the help of the linkResolver.

Copy
// truncated code
import NextLink from 'next/link'

// Update the path for your linkResolver file.
import { linkResolver } from './path-to-link-resolver'

const AltLangs = ({ altLangs = [] }) =>
  altLangs.map((altLang) => {
    return (
      <li className="language-switcher" key={altLang.id}>
        <NextLink
          locale={altLang.lang}
          href={linkResolver(altLang)}
          passHref
        >
          <a className={`flag-icon-${altLang.lang.slice(-2)}`}>/</a>
        </NextLink>
      </li>
    );
  });

export default LanguageSwitcher = ({ altLangs }) => <AltLangs altLangs={altLangs} />;

Create a Link Resolver

Finally, once your routes are created, create a Link Resolver that redirects to the right routes. In this example, we create the link-resolver.js file and set one conditional statement for the 'Page' type. You can modify this as your project grows to handle any new routes you've made.

Copy
module.exports = {
  linkResolver: function (doc) {
    if (doc.type === 'page') {
      return `/${doc.uid}`
    }
    return '/'
  },
}

If you're interested in building more complex URLs read about the Route Resolver


Related articles

Read the full documentation on how to create simple routes with the help of the Link Resolver:


Was this article helpful?
Not really
Yes, Thanks

Can't find what you're looking for? Get in touch with us on our Community Forum.