Localization with Next.js

Localizing your Next.js App

This walkthrough starts from the next-js-simple starter. Add the i18n config to your next.config.js file. We’ll use sub-path routing in this example.

// next.config.js
module.exports = {
  i18n: {
    locales: ['en', 'fr', 'de'],
    defaultLocale: 'en',
	}
}

// With this configuration if you have a pages/blog.js it will 
// route to /blog, /fr/blog, and /de/blog

Now you should be able to access locale in the context of getStaticProps and pass it to options in your builder.get() call. This allows Builder’s API to automatically resolve the locale on custom components with localized inputs.

We also want to return the locale from getStaticProps so we can pass it to the locale prop of the <BuilderComponent/> in the page component. Similarly, this will allow the preview in the Visual Editor to auto resolve content client-side to the proper locale. Your [[...page]].tsx should look something like this:

// [[...page]].tsx

export async function getStaticProps({
  params,
  locale,
}: GetStaticPropsContext<{ page: string[] }>) {
  const page =
    (await builder
      .get('page', {
        userAttributes: {
          urlPath: '/' + (params?.page?.join('/') || ''),
        },
        options: { 
          locale 
        }
      })
      .toPromise()) || null

  return {
    props: {
      page,
      locale,
    },
    revalidate: 5,
  }
}

export async function getStaticPaths() {
  const pages = await builder.getAll('page', {
    options: { noTargeting: true },
    omit: 'data.blocks',
  })

  return {
    paths: pages.map((page) => `${page.data?.url}`),
    fallback: true,
  }
}

export default function Page({
  page,
  locale,
}: InferGetStaticPropsType<typeof getStaticProps>) {
  const router = useRouter()
  const isPreviewingInBuilder = useIsPreviewing()
  const show404 = !page && !isPreviewingInBuilder

  if (router.isFallback) {
    return <h1>Loading...</h1>
  }

  return (
    <>
      <Head>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        {!page && <meta name="robots" content="noindex" />}
      </Head>
      {show404 ? (
        <DefaultErrorPage statusCode={404} />
      ) : (
        <BuilderComponent 
          model="page" 
          content={page} 
          locale={locale}
        />
      )}
    </>
  )
}

Localizing Custom Components

You can enable localized input fields in your custom components by setting localized: true. Localized inputs will resolve to the correct locale, returning a single value with a type of the input type. Try it out with string, richText, or file input types and more!

Here is an example of a Heading component with a localized text field:

// Heading.tsx

export const Heading = (props: { title: string; }) => {

  return(
    <div style={{'width': '50vw'}}>
      <h1>{props.title}</h1>
    </div>
  )
}

Builder.registerComponent(Heading, {
  name: "Heading",
  inputs: [
    {
      localized: true,
      name: "title",
      type: "text", 
      defaultValue: 'I am a heading!'
    },
  ],
});

Setting Up Locales in Builder

In your Account Settings select Custom Targeting Attributes and enter a new attribute called locale of type string and enum. Add values for each of your locales. For more information see here.

At this point you’re all set up for several localization methods including localized components, localized text blocks, and localizing multiple blocks. For more information on localizing whole pages see here.

Demo
Starter: builder/examples/next-js-localized at main · BuilderIO/builder · GitHub
Video Walkthrough: Loom | Free Screen & Video Recording Software | Loom

1 Like

Hi @ancheetah, thanks for this walkthrough. Am I correct in saying that Builder only supports localisation if you’re prepared to drop $450/month? Or am I missing something? It seems like a pretty fundamental requirement for many scenarios where a business can’t justify that amount for their CMS.

Thanks,

Simon

Hi @Simon, thank you for your interest in our localization workflows, we appreciate your enthusiasm for this feature! You are correct that it is currently only available to our Growth level customers and above.

We understand that this feature is fundamental for many customers across all of our subscription levels, and hope you can appreciate the level of development and support that such a feature rich functionality requires. Based on our feedback and discussions with customers, we have often found that the value provided by this feature, especially for our multi-locale customers, often exceeds the cost, but we appreciate your feedback!

Rest assured that we are constantly working to improve our products and services, and we will certainly consider your feedback as we continue to develop and refine our offerings.

Thanks, @ancheetah, for the response. I don’t want to hijack what is a technical post, is there somewhere else I can provide further feedback? Don’t even start me on environments!!

Hi @Simon feel free to post in our general channel or feature requests channel. For feature requests we also have an ideas board here where the community can upvote each others ideas. We appreciate any feedback you can provide!

Why must I create a new targeting attribute called locale when the Localization attribute already exists there? This tutorial seems inconsistent with the introduction to Localization with Builder I found here: Localization Intro - Builder.io.


Hi @zbigniew.stepniewski you are correct! The original post here is about a year old, and we are constantly updating our feature set and functionality. Localization is one of those features that we are constantly expanding and the doc you linked to and the images you posted are the latest version. As you called out, we have since negated the need to create a locale attribute and instead have moved localization to be its own standalone targeting section.

The forum is a great resource for us to establish niche or experimental features, but in general once functionality is established and officially mapped out in our docs, that will usually be the source of truth for the best and most established paths.

That being said, most of the other technical aspects of this post such as how to use and pass the locale prop in your code is still accurate.

Hope this helps!

Thanks, now it’s clear. I was looking for how to combine the Builder Localization with NextJs.