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. This article is based on our multi-language example project, feel free to download and use it.


Add languages to your repository

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

Internationalized Routing

To quote their website, "Next.js has built-in support for internationalized (i18n) routing since v10.0.0. You can provide a list of locales, the default locale, and domain-specific locales and Next.js will automatically handle the routing." Take a look at their full documentation on the subject to learn more.

The settings for i18n are defined in the next.config.js file at the root of your project. For our example we want this configuration to be populated from the Prismic API response, so when a content creator adds a new language in the Prismic repository this new language will be automatically added to the website application without any intervention from the developer.

The example below imports helper functions from the prismic.io file as defined and explained in the Next.js and Prismic set-up article. An instance of the client is created to query the Prismic repository for the languages being used in that repository, these languages are then mapped and passed to the locales list of i18n. The first result of the languages array is retrieved and set as the defaultLocale for the project. The first result of this array is always the main locale that is set on Prismic.

Copy
const prismic = require("@prismicio/client");

const sm = require("./sm.json");

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

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

  return {
    i18n: {
      // These are all the locales you want to support in
      // your application
      locales,
      // This is the default locale you want to be used when visiting
      // a non-locale prefixed path e.g. `/hello`
      defaultLocale: locales[0],
    },
    images: {
      loader: "imgix",
      path: "",
    },
  };
};

Query by language

Now the routing is configured, your project will need to query each page based on the language. This is done by passing the locale, which is used to construct the URL path, to the page query inside getStaticProps().

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

In this dynamic page example, UID is also taken from the URL path by accessing params. This same information is passed to getStaticPaths() to build the page routes, except we query all pages and languages at the same time with the same time getAllByType("page", { lang: "*" }).
Learn more about how Next.js allows you to create dynamic routes by wrapping page names in square brackets. You can see the full example below.

Copy
// ~/pages/[uid].js example file

import { SliceZone } from "@prismicio/react";
import { createClient } from "../prismicio";
import { Layout } from "../components/Layout";
import { components } from "../slices";

const Page = ({ doc, menu }) => {
  if (doc?.data) {
    return (
      <Layout altLangs={doc.alternate_languages} menu={menu}>
        <SliceZone slices={doc.data.body} components={components} />
      </Layout>
    );
  }
};

export async function getStaticProps({ params, locale }) {
  const client = createClient();

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

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

export async function getStaticPaths() {
  const client = createClient();

  const documents = await client.getAllByType("page", { lang: "*" });

  return {
    paths: documents.map((doc) => {
      return { params: { uid: doc.uid }, locale: doc.lang };
    }),
    fallback: false,
  };
}

export default Page;

If you'd like to build a query for a homepage, read how to Query Singleton Types.

Create a language switcher

Now the dynamic localized routes are built, the project needs a navigation button so users can switch between languages on the website. This can be anything from a <select> element if there are many languages, a button, or a boolean toggle switch if there only have two.

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

Get the alternate languages

The alternate_languages for each document are returned in the Prismic API response. In the page example above this information is passed to a Layout component as a prop called altLangs.

Copy
// 〜/pages/[uid].js

<Layout altLangs={doc.alternate_languages} menu={menu}>
  <SliceZone slices={doc.data.body} components={components} />
</Layout>

In the Layout component, this data is again passed to a header component as a prop, then finally to a LanguageSwitcher component. You can see this flow of information in the example tabs below.

  • Layout.js
  • Header.js
Layout.js
Copy
// 〜/components/Layout.js

import Head from "next/head";
import { Header } from "./Header";
import { Footer } from "./Footer";

export const Layout = ({ children, altLangs, menu }) => {
  return (
    <>
      <Head>
        <link rel="icon" href="/favicon.png" type="image/png" />
        <title>Multi-language site</title>
      </Head>
      <Header altLangs={altLangs} menu={menu} />
      <main>{children}</main>
      <Footer />
    </>
  );
};
Header.js
Copy
// ~/components/Header.js
import { PrismicLink } from "@prismicio/react";

import { Navigation } from "./Navigation";
import { LanguageSwitcher } from "./LanguageSwitcher";

export const Header = ({ menu, altLangs }) => {
  return (
    <header className="bg-white px-6 pt-8 md:py-16">
      <div className="mx-auto grid max-w-5xl justify-items-center gap-4 md:grid-cols-[1fr_auto] md:justify-items-start">
        <div>
          <PrismicLink href="/">
            <img className="" src="/images/logo.png" alt="Site logo" />
          </PrismicLink>
        </div>
        <div className="grid items-center justify-items-center gap-3 md:grid-cols-[1fr_auto] md:justify-items-end md:gap-6 md:justify-self-end">
          <Navigation menu={menu} />
          <LanguageSwitcher altLangs={altLangs} />
        </div>
      </div>
    </header>
  );
};

Create the language switcher

In the ~/components folder, create a LanguageSwitcher.js file. In this file the altLangs array, that was passed as props, is mapped and each alternate language is passed through the PrismicLink component and linkResolver() function which is defined in the prismicio.js file. This will create a link to each alternate language version of the current page.

Copy
<PrismicLink href={linkResolver(altLang)} locale={altLang.lang}>

One final bonus configuration here is to automatically add flag icons based on the locales of the Prismic repository. You can do this by installing the flag-icons package. Import this CSS file globally.

  • npm
  • Yarn
  • tailwind styles.css file
npm
Copy
npm install flag-icons
Yarn
Copy
yarn add flag-icons
tailwind styles.css file
Copy
@import "@fontsource/inter/400.css";
@import "@fontsource/inter/700.css";
@import "flag-icons/css/flag-icons.css";

@tailwind base;
@tailwind components;
@tailwind utilities;

.collapsible.bg-white.py-12 + .collapsible.bg-white.py-12 {
  @apply pt-0 md:pt-0;
}

The LangIcon component in the example below gets the 2 digit locale code from the alternate language, then this code is joined to the CSS class for the flag-icons library. With this configuration, anytime a content creator adds a new locale in Prismic a new locale will be added in the website project with a language switcher button for the locale without any work from the developer.

Copy
// ~/components/LanguageSwitcher.js

import { PrismicLink } from "@prismicio/react";
import { linkResolver } from "../prismicio";

const LangIcon = ({ lang }) => {
  const code = lang.substring(3).toLowerCase();
  return <span className={`fi fi-${code}`} />;
};

export const LanguageSwitcher = ({ altLangs = [] }) => {
  return (
    <ul className="-ml-4 -mt-4 flex flex-wrap">
      {altLangs.map((altLang) => {
        return (
          <li key={altLang.lang} className="" className="pl-4 pt-4">
            <PrismicLink href={linkResolver(altLang)} locale={altLang.lang}>
              <span className="sr-only">{altLang.lang}</span>
              <LangIcon lang={altLang.lang} />
            </PrismicLink>
          </li>
        );
      })}
    </ul>
  );
};

export default LanguageSwitcher;

That's everything you'll need to do to configure a Next.js project for a website in multiple languages and locales. You can see a live example of what that looks like here.


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.