Skip to content

Commit 0f18c9a

Browse files
committed
keep using the middleware solution since the redirect one creates a redirect loop with some nested pages
1 parent 248aef9 commit 0f18c9a

File tree

3 files changed

+65
-24
lines changed

3 files changed

+65
-24
lines changed

next-i18next.config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ module.exports = {
1313
// url. Ref: https://nextjs.org/docs/pages/building-your-application/routing/internationalization#prefixing-the-default-locale
1414
defaultLocale: "default",
1515
// supported locales defined in `i18n.config.json`
16-
locales: [...locales, "default"],
16+
locales: ["default", ...locales],
17+
localeDetection: false,
1718
},
1819
// define custom location for intl files, otherwise default to public/locales (https://github.com/i18next/next-i18next#2-translation-content)
1920
localePath: "./src/intl",

next.config.js

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -38,29 +38,6 @@ module.exports = (phase, { defaultConfig }) => {
3838
images: {
3939
deviceSizes: [640, 750, 828, 1080, 1200, 1504, 1920],
4040
},
41-
async redirects() {
42-
/**
43-
* Redirect /default to /en.
44-
*
45-
* This allows us to always have a default locale prefix for all URLs.
46-
*
47-
* @see https://github.com/vercel/next.js/discussions/18419#discussioncomment-327128
48-
*/
49-
return [
50-
{
51-
source: "/default",
52-
destination: "/en",
53-
locale: false,
54-
permanent: false,
55-
},
56-
{
57-
source: "/default/:slug*",
58-
destination: "/en/:slug*",
59-
locale: false,
60-
permanent: false,
61-
},
62-
]
63-
},
6441
}
6542

6643
if (phase !== PHASE_DEVELOPMENT_SERVER) {

src/middleware.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { NextRequest, NextResponse } from "next/server"
2+
3+
import { DEFAULT_LOCALE, FAKE_LOCALE, LOCALES_CODES } from "./lib/constants"
4+
5+
const PUBLIC_FILE = /\.(.*)$/
6+
7+
function detectLocale(acceptLanguage: string | null) {
8+
if (!acceptLanguage) {
9+
return DEFAULT_LOCALE
10+
}
11+
12+
// it comes in the format of `en-US,en;q=0.9,de;q=0.8`
13+
const locales = acceptLanguage.split(",")
14+
15+
const locale = locales
16+
.map((localeWeight) => localeWeight.split(";")[0].trim())
17+
.find((locale) => {
18+
return LOCALES_CODES.includes(locale)
19+
})
20+
21+
return locale
22+
}
23+
24+
export const config = {
25+
matcher: [
26+
"/", // explicit matcher for root route
27+
/*
28+
* Match all request paths except for the ones starting with:
29+
* - _next/static (static files)
30+
*/
31+
"/((?!_next/static).*)",
32+
],
33+
}
34+
35+
// Middleware required to always display the locale prefix in the URL. It
36+
// redirects to the default locale if the locale is not present in the URL
37+
export async function middleware(req: NextRequest) {
38+
const { pathname, locale, search } = req.nextUrl
39+
40+
if (
41+
pathname.startsWith("/_next") ||
42+
pathname.includes("/api/") ||
43+
PUBLIC_FILE.test(pathname)
44+
) {
45+
return
46+
}
47+
48+
if (locale === FAKE_LOCALE) {
49+
// Apparently, the built-in `localeDetection`from Next does not work when
50+
// using the faked locale hack. So, we need to detect the locale manually
51+
const localeDetected = detectLocale(req.headers.get("accept-language"))
52+
const locale = localeDetected || DEFAULT_LOCALE
53+
54+
const redirectUrl = new URL(`/${locale}${pathname}${search}`, req.url)
55+
56+
// Add trailing slash if it's not present
57+
if (!redirectUrl.pathname.endsWith("/")) {
58+
redirectUrl.pathname = redirectUrl.pathname + "/"
59+
}
60+
61+
return NextResponse.redirect(redirectUrl, { status: 301 })
62+
}
63+
}

0 commit comments

Comments
 (0)