I'm currently figuring out how the Next.js localisation and internationalisation work.
beside the root page.jsx (home page), I have two folders: test and tester -sorry for the confusion- and two locales I want to support which they are: "ar"
and "en"
before starting the part of the middleware.js
I could view all of the pages under any locale, but now after completing the logic of the middleware.js
, the root page and /ar/test
and /ar/tester
become not found, I check the source page to check if there is a lang in <html>
tag but there was none.
this is not the case for the /en
, all the routes working and the locale was detected inside the tag.
I thought may be caused by something related to the browser's default language, so I changed it from Arabic to English but the same thing happened.
and here is the next.config file:
/** @type {import('next').NextConfig} */
const nextConfig = {
i18n: {
// locales: ['default',"ar", "ar-SA", "en", "en-GB"],
locales: ['ar', "en-GB"],
defaultLocale: 'ar',
localeDetection: false
},
trailingSlash: true,
};
export default nextConfig;
sample of the requests from the console where you can see that for some mysterious reason the /ar
is missing from the pathname:
GET /en/ 200 in 1797ms
GET / 404 in 284ms
GET / 404 in 280ms
GET /en/tester/ 200 in 401ms
GET /tester/ 404 in 284ms
the middleware.js
:
import { NextResponse } from "next/server";
import { getLocale } from "./helpers/getLocale"
const localesRegex = /^\/((?!ar\/|en\/|api|_next\/static|_next\/image|auth|favicon.ico|images).*)$/;
export const middleware = (request) => {
console.log("middleware.js is running");
const { pathname: url } = request.nextUrl;
// is the request to the root page and doesn't have any locale attached?
if(localesRegex.test(url)) {
// attach the locale based on the user's preferences:
const locale = getLocale(request);
// rebuild the new request's pathname (url):
// request.nextUrl.url = `/${locale}${url}`;
// return NextResponse.redirect(request.nextUrl);
return NextResponse.redirect(new URL(`/${locale}${url}`, request.nextUrl.origin).href);
}
return NextResponse.next();
}
export const config = {
matcher: ["/^\/((?!ar\/|en\/|api|_next\/static|_next\/image|auth|favicon.ico|images).*)$/"],
};
getLocale
function:
const supportedLocales = ["ar", "en"];
const matchedLocales = [];
// const matchPattern = ["/^(?!\/(ar|en)($|\/)).*/"];
// to get the locale from the request => request.nextUrl.locale
export function getLocale(request){
// 1) is there any locale ? is it supported ? return it
if(request.nextUrl.locale && supportedLocales.includes(request.nextUrl.locale)) {
console.log("there is", request.nextUrl.locale);
return request.nextUrl.locale;
}
// 2) build an array of the preferred locales:
const userLocals = request.headers.get("accept-language")?.split(",") || [];
// 3) empty userLocales ? return the matchers and the newUrl attached with /ar (the default)
if(!userLocals) return "ar";
// 4) extract the matches, the split(/[-;]/)[0] is used for locales like en-GB-oxendict;q=0.8 or en;q=0.7
userLocals.map(locale => {
locale = locale.split(/[-;]/)[0];
if(supportedLocales.includes(locale) && !matchedLocales.includes(locale)) matchedLocales.push(locale);
});
// 5) empty matchedLocales ? return the matchers and the newUrl attached with /ar (the default)
if(!matchedLocales) return "ar";
// 6) return the first match:
return matchedLocales[0];
}
I've solved the problem !! I don't know the explanation for it so please if anyone comes across this post in the future and can help us to understand what's happened, It would be a delighted to read your thoughts.
here is what I changed to make it work:
1- the next.config
file was:
locales: ['ar', "en-GB"],
defaultLocale: 'ar',
I had a problem with the Arabic language but not with the English because I didn't specify any region so it's now:
locales: ['ar-SA', "en-GB"],
defaultLocale: 'ar-SA',
the second thing to change is the middleware's matcher:
export const config = {
matcher: ["/","/((?!_next|api|favicon.ico).*)"],
};
and that's it! I hope this post will be some kind of help.