Internationalization with dynamic language routes and base routes with no param/suffix
Unanswered
Rufous-winged Sparrow posted this in #help-forum
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
This is all necessary because Google will not index pages that have redirects, so without
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
in url it looks like: www.example.com
in sitemap it looks like: www.example.com/en
hope i understand your problem.
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))
}
}