Next.js Discord

Discord Forum

Multi Tenant with NextIntl

Unanswered
Bull Arab posted this in #help-forum
Open in Discord
Bull ArabOP
Any guide integrate Next Intl into multi-tenant app with custom domain

20 Replies

What did you try, what didn't work?
Bull ArabOP
Currently I have setup NextIntl with NextAuth in my NextJs project that all pages is require to wrap in [locale], it works well.

https://vercel.com/guides/nextjs-multi-tenant-application

My question is how to setup Localizing into NextJs Multiple Tenant Application

I have no idea how to setup, do I have to wrap the [domain] under [locale] and how do I config the next auth matches on the Url ?
Asian black bear
You'll need to combine the middleware from your i18n package and implement the multi-tenant rewrites yourself. The Vercel platforms middleware is a good start: https://github.com/vercel/platforms/blob/main/middleware.ts

I'm using next-international but it should be somewhat similar: https://gist.github.com/kylekz/b698239da2ea94045fbe3d40b46f31e7
Bull ArabOP
Do you have experience share the sample code of it ?
@Bull Arab Do you have experience share the sample code of it ?
you'd rather want to give it a try, and then share more specific questions to us
Bull ArabOP
Alright.
Before I give it a try. May I know the [domain] is wrap under [locale] or [locale] wrap into [domain]?
you can encode both into the same param, if you want them to invisible
if you want the locale to be visible though, I would do /domain -> /locale
because there are chances that the local is visible in the url like "foo.com/fr/hello"
and you want to add the domain, invisible via a rewrite adding it as the first part of the path
"domain/fr"
and locales are also easy to identify if you want to tell if the path contains a locale already or not (they are always 2 chars long and known in advance)
Bull ArabOP
Folder Structure
- app
 - [locale]
  - [domain]
Bull ArabOP
Middleware.ts
const intlMiddleware = createIntlMiddleware({
  locales,
  localePrefix: "never",
  defaultLocale: "en",
});

const authMiddleware = withAuth(
  function onSuccess(req) {
    return intlMiddleware(req);
  },
  {
    callbacks: {
      authorized: ({ token }) => token != null,
    },
    pages: {
      signIn: "/login",
    },
  }
);

export default function middleware(req: NextRequest) {
  const url = req.nextUrl;

  const publicPathnameRegex = RegExp(
    `^(/(${locales.join("|")}))?(${publicPages
      .flatMap((p) => (p === "/" ? ["", "/"] : p))
      .join("|")})/?$`,
    "i"
  );

  const isPublicPage = publicPathnameRegex.test(req.nextUrl.pathname);

  const hostname = req.headers
    .get("host")!
    .replace(".localhost:3000", `.${process.env.NEXT_PUBLIC_HOSTNAME_URL}`);

  const searchParams = req.nextUrl.searchParams.toString();

  const path = `${url.pathname}${
    searchParams.length > 0 ? `?${searchParams}` : ""
  }`;

    if (
      hostname === "localhost:3000" ||
      hostname === process.env.NEXT_PUBLIC_ROOT_DOMAIN
    ) {
      return NextResponse.rewrite(
        new URL(`/home${path === "/" ? "" : path}`, req.url),
      );
    } 

  return NextResponse.rewrite(new URL(`/en/${hostname}${path}`, req.url));
}
This work multi tenant with subdomain. But useTranslations is not work with on pages
import { localesConfig } from "constants/locale";
import { withAuth } from "next-auth/middleware";
import createIntlMiddleware from "next-intl/middleware";
import { NextRequest, NextResponse } from "next/server";

const locales = localesConfig;
const publicPages = [
  "/",
  "/login",
  "/register",
  "/forgot-password",
  "/products/*",
  "/home/*",
];

const intlMiddleware = createIntlMiddleware({
  locales,
  localePrefix: "never",
  defaultLocale: "en",
});

const authMiddleware = withAuth(
  // Note that this callback is only invoked if
  // the `authorized` callback has returned `true`
  // and not for pages listed in `pages`.
  function onSuccess(req) {
    return intlMiddleware(req);
  },
  {
    callbacks: {
      authorized: ({ token }) => token != null,
    },
    pages: {
      signIn: "/login",
    },
  }
);

export default function middleware(req: NextRequest) {
  const url = req.nextUrl;

  const publicPathnameRegex = RegExp(
    `^(/(${locales.join("|")}))?(${publicPages
      .flatMap((p) => (p === "/" ? ["", "/"] : p))
      .join("|")})/?$`,
    "i"
  );

  // Test Public Page List
  const isPublicPage = publicPathnameRegex.test(req.nextUrl.pathname);

  // Get hostname of request (e.g. demo.vercel.pub, demo.localhost:3000)
  const hostname = req.headers
    .get("host")!
    .replace(".localhost:3000", `.${process.env.NEXT_PUBLIC_HOSTNAME_URL}`);

  const searchParams = req.nextUrl.searchParams.toString();

  // Get the pathname of the request (e.g. /, /about, /blog/first-post)
  const path = `${url.pathname}${
    searchParams.length > 0 ? `?${searchParams}` : ""
  }`;

  if (isPublicPage) {
     return intlMiddleware(req);
   } else {
     return (authMiddleware as any)(req);
   }
}


Require to use intlMiddleware to get useTranslations to get work.
Could you give me an advice how to make intlMiddleware work with your multi tenant NextResponse.rewrite ?
Bull ArabOP
This project i have implement NextAuth & Next-Intl with this multi tenant