I18n for 404 not found page

Hello! I'm wondering if there's any way I can implement a multi-language feature for the 404 not found page?

More context about the project:

  • I've developed my website with Next.js App Router with 2 locales, "en-us" and "zh-tw", following the guide in this blog
  • I made a single-page type not_found_page for the Not found page in the slice machine, and I would like to use this for the 404 not-found page
  • I placed my not-found code file in app/[lang]/not-found.tsx.

The not-found.tsx look like the following:

import { Metadata } from "next";
import { SliceZone } from "@prismicio/react";

import { createClient } from "@/prismicio";
import { components } from "@/slices";
import Header from "@/components/Header";
import { getLocales } from "@/utils/getLocales";
type Params = { lang: string };

export default async function Page({ params }: { params: Params }) {
  const client = createClient();
  const page = await client.getSingle("not_found_page", {
    lang: params.lang,
  });
 
  const locales = await getLocales(page, client);
  
  const settings = await client.getSingle("settings");

  //   return <SliceZone slices={page.data.slices} components={components} />;
  return (
    <>
      <Header locales={locales} settings={settings} />
      <SliceZone
        slices={page.data.slices}
        components={components}
        context={page}
      />
    </>
  );
}

export async function generateMetadata({
  params,
}: {
  params: Params;
}): Promise<Metadata> {
  const client = createClient();
  const page = await client.getSingle("not_found_page", { lang: params.lang });

  return {
    title: page.data.meta_title,
    description: page.data.meta_description,
  };
}

The problem is when I tested going to a page that doesn't exist, it ran into an error because the lang property from the params is undefined. I suspect that it might be related to how I set my routing in prismicio.ts,

const routes: prismic.ClientConfig["routes"] = [
  { type: "page", path: "/:lang?", uid: "home" },
  { type: "page", path: "/:lang?/:uid" },
  // ...
];

I tried adding an entry like so:

{ type: "not_found_page", path: "/:lang?"}

But the error still persists, I suspect it's because the routing for a page that doesn't exist is being handled by the dynamic page routing that I set in { type: "page", path: "/:lang?/:uid" }

Any advice on how to solve this issue? Thank you!

Hi @epochdevelectronics, Next.js does not provide page parameters to the not-found.tsx page, unfortunately. This means you will not have access to the correct locale out of the box.

We recommend trying this solution: How to create a multilingual not-found page using route groups and nested layouts · Issue #53243 · vercel/next.js · GitHub

It uses a combination of middleware and headers to pass the locale to all pages. The [lang] page parameter should be used for all non-not-found.tsx pages, and the middleware for the not-found.tsx page.

With that solution, you should be able to use this not-found.tsx (untested):

import { SliceZone } from "@prismicio/react";
import { headers } from "next/headers";
import { cache } from "react";

import { createClient } from "@/prismicio";
import { components } from "@/slices";
import Header from "@/components/Header";
import { getLocales } from "@/utils/getLocales";

// Update with your default language.
const fallbackLanguage = "en-us";

const getLocale = () =>
  cache(() => {
    const preference = headers().get("X-Language-Preference");
    return preference ?? fallbackLanguage;
  });

export default async function NotFoundPage() {
  const client = createClient();

  const lang = getLocale();
  const page = await client.getSingle("not_found_page", { lang });

  const locales = await getLocales(page, client);

  const settings = await client.getSingle("settings");

  return (
    <>
      <Header locales={locales} settings={settings} />
      <SliceZone
        slices={page.data.slices}
        components={components}
        context={page}
      />
    </>
  );
}