Hiding Master Language for a Multilingual Website - Root Resolver Issue

Hi everyone! :wave:

I’m working on a multilingual website using Next.js (with the app directory) and Prismic, and I’ve run into an issue with my root resolver when trying to hide the master language (fr) in the URL.

Here’s my setup and the issue I’m facing:


Route Configuration (Root Resolver)

I’ve defined my Prismic routes as follows:

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

Locale Definition in next.config.ts

Here’s how I fetch and define the locales dynamically:

const nextConfig = async (): Promise<NextConfig> => {
  const client = createClient();
  const repository = await client.getRepository();
  const locales = repository.languages.map((lang) => lang.id);

  console.log("locales", locales); // Logs available locales

  return {
    output: "export",
    reactStrictMode: true,
    i18n: {
      locales: ["en", "fr"], // Supported locales
      defaultLocale: "fr",   // Master language
    },
    images: {
      remotePatterns: [
        {
          protocol: "https",
          hostname: "**.prismic.io",
        },
      ],
    },
    redirects: async () => {
      return [];
    },
  };
};

Folder Structure (app Directory)

I’ve structured my app folder as follows to handle both root and language-specific routes:

app/
├── [lang]/
│   ├── [...uids]/page.tsx  // For language-specific pages
│   ├── page.tsx           // For language-specific homepages
├── [...uids]/page.tsx      // For master language pages
├── page.tsx                // For master language homepage

Current Behavior

Here’s what works and what doesn’t:

  1. :white_check_mark: / → Shows the fr (default/master language) homepage.
  2. :white_check_mark: /fr → Shows the fr homepage.
  3. :white_check_mark: /en → Shows the English homepage.
  4. :white_check_mark: /fr/bonjour → Works as expected.
  5. :white_check_mark: /en/bonjour → Works as expected.
  6. :x: /bonjour → Fails with the following error:
[Server] Error: Some(bonjour) is not a valid language code

It seems like the root resolver doesn’t recognize bonjour as a valid page for the master language and instead tries to interpret it as a language code.


What I Need

I want to:

  1. Hide the default language (fr) in the URL for the master language, e.g., /bonjour instead of /fr/bonjour.
  2. Maintain full support for other languages, e.g., /en/bonjour.
  3. Ensure the site remains in SSG mode using app directory.

Question

What am I doing wrong with my current setup? Why is the root resolver not identifying bonjour as a valid page in the master language? Is there a better way to handle this in the app directory with the [...uids] structure?

Any help or suggestions would be greatly appreciated! :pray:

Hi @ali!

Thanks for all the details in your query! So I've had a look - I believe this indeed happens because Next.js tries to match /bonjour using the [lang] route first, assuming bonjour is a locale before it reaches the [...uids] route. You've got several options here:

1. Adjust Route Priority

The [lang]/[...uids] route currently has priority over the [...uids] route. Since fr is your default language, you should ensure that [...uids] is properly handling cases where the language is omitted.

Try modifying your app structure:

app/
├── [lang]/
│   ├── [...uids]/page.tsx  // Language-specific pages
│   ├── page.tsx           // Language-specific homepages
├── [...uids]/page.tsx      // Handles pages when no `lang` is specified (fr)
├── page.tsx                // Master language homepage (fr)

This ensures that:

  • /bonjour hits app/[...uids]/page.tsx instead of being processed as a locale.

2. Improve Route Resolution in Prismic

Modify your routes array to explicitly define handling for the default language:

const routes: prismic.ClientConfig["routes"] = [
  {
    type: "home-page",
    path: "/", // Default language home
  },
  {
    type: "home-page",
    path: "/:lang/", // Other languages
  },
  {
    type: "page",
    path: "/:uid", // Default language pages (fr)
  },
  {
    type: "page",
    path: "/:lang/:uid", // Other language pages
  },
];

This ensures:

  • / and /bonjour resolve correctly for fr.
  • /en/bonjour resolves correctly for en.

3. Override Next.js Language Detection

Modify your middleware to rewrite requests properly:

Create a middleware.ts file:

import { NextRequest, NextResponse } from "next/server";

export function middleware(req: NextRequest) {
  const { pathname } = req.nextUrl;

  // Skip paths that start with an explicit locale
  if (pathname.startsWith("/en") || pathname.startsWith("/fr")) {
    return NextResponse.next();
  }

  // Assume missing locale means the default one (fr)
  const newUrl = req.nextUrl.clone();
  newUrl.pathname = `/fr${pathname}`;

  return NextResponse.rewrite(newUrl);
}

This ensures:

  • /bonjour is internally rewritten to /fr/bonjour without changing the visible URL.
  • Other locales (/en) work normally.

Expected Outcome

  • / → Shows the fr homepage.
  • /bonjour → Shows the fr page for "bonjour" without errors.
  • /en/bonjour → Works normally.
  • /fr/bonjour → Works but is unnecessary (optional).

This should keep your site in SSG mode while hiding fr from URLs.

Let me know if any of that worked for you! :rocket:

Thanks for the help. I don't know middleware don't work as expected for me. I abandoned this idea. Hope nextJS will guive more flexibility for this case. The only thing I'm questioning about : Is there really a role for prismic resolver four rooting as nextjs app do all the Job ? Why use prismic resolver ?

The Prismic route resolver is how Prismic generates URLs for your web pages. We recommend using it as describes how Prismic should generate URLs for each document in your repository. These URLs are then included for each document and each internal link in your repository in the API response. The route resolver can use data from parent and grandparent documents, allowing you to build complex URLs.

If you're finding that it's not enough, or doesn't quite do the job, we also recommend trying the Link Resolver: Route Resolver - Documentation - Prismic. It helps when the route resolver doesn't cover specific cases for routing such as URL formatting or advanced conditions. You could always give that a go as well!