Next.js Discord

Discord Forum

Internationalization with dynamic language routes and base routes with no param/suffix

Unanswered
Rufous-winged Sparrow posted this in #help-forum
Open in Discord
Rufous-winged SparrowOP
I'm using i18n translations in my Next app and I'm not sure the best practices for allowing for both dynamic routes using the app/[lang]/page folder structure but simultaneously allow for the root domain to be reachable without the language param (e.g. www.example.com not www.example.com/en). Is creating a duplicate folder structure at app which imports the page s from app/[lang]/ the only solution?

This is all necessary because Google will not index pages that have redirects, so without www.example.com, our site will only be indexed with /lng param.

5 Replies

Komondor
I too am interested in the solution to this.
Mossyrose gall wasp
i have this one for my project
import { match as matchLocale } from "@formatjs/intl-localematcher";
import Negotiator from "negotiator";

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

import { i18n } from "../i18n-config";

function getLocale(request: NextRequest): string | undefined {
  const negotiatorHeaders: Record<string, string> = {};
  request.headers.forEach((value, key) => (negotiatorHeaders[key] = value));
  const locales: string[] = i18n.locales;
  let languages = new Negotiator({ headers: negotiatorHeaders }).languages(
    locales
  );

  const locale = matchLocale(languages, locales, i18n.defaultLocale);

  return locale;
}

export function middleware(request: NextRequest) {
  const pathname = request.nextUrl.pathname;
  const locales = i18n.locales;
  if (
    [
      "/manifest.json",
      "/favicon.ico",
      "/sitemap.xml",
    ].includes(pathname)
  )
    return;
  if (
    pathname.startsWith(`/${i18n.defaultLocale}/`) ||
    pathname === `/${i18n.defaultLocale}`
  ) {
    const currUrl = pathname.replace(
      `/${i18n.defaultLocale}`,
      pathname === `/${i18n.defaultLocale}` ? "/" : ""
    );
    return NextResponse.redirect(new URL(currUrl, request.url));
  }
  const pathnameIsMissingLocale = locales.every(
    (locale) => !pathname.startsWith(`/${locale}/`) && pathname !== `/${locale}`
  );
  if (pathnameIsMissingLocale) {
    return NextResponse.rewrite(
      new URL(`/${i18n.defaultLocale}${pathname}`, request.url)
    );
  }
}
export const config = {
  matcher: ["/((?!api|_next/static|_next/image|favicon.ico|static).*)"],
};


in url it looks like: www.example.com
in sitemap it looks like: www.example.com/en
hope i understand your problem.
Komondor
thanks for posting your solution.
@Komondor thanks for posting your solution.
Mossyrose gall wasp
is this you looking for? its not perfect solution i should add cookie too
Rufous-winged SparrowOP
hey it helped me out for sure. I got it working by using some of your code. I haven't thoroughly tested it yet but seems to be good so far, not sure yet if Google will now start indexing the site now, but I can report back. Here's my solution:

import { NextResponse } from "next/server"
import acceptLanguage from "accept-language"
import { languages, LANGUAGE_COOKIE_NAME } from "./i18n/settings"

acceptLanguage.languages(languages)

export const config = {
  matcher: "/((?!api|static|.*\\..*|_next).*)",
}

export function middleware(req) {
  let lng
  const cookieSet = req.cookies.has(LANGUAGE_COOKIE_NAME)
  if (cookieSet) lng = acceptLanguage.get(req.cookies.get(LANGUAGE_COOKIE_NAME).value)
  const pathname = req.nextUrl.pathname
  if (["/manifest.json", "/favicon.ico", "/sitemap.xml"].includes(pathname)) return
  if (pathname.startsWith("/en/") || pathname === "/en") {
    const currUrl = pathname.replace("/en", pathname === "/en" ? "/" : "")
    return NextResponse.redirect(new URL(currUrl, req.url))
  }
  const pathnameIsMissingLocale = languages.every(
    (lang) => !pathname.startsWith(`/${lang}/`) && pathname !== `/${lang}`
  )
  if (pathnameIsMissingLocale) {
    return NextResponse.rewrite(new URL(`/${lng}${pathname}`, req.url))
  }
}