Reputation: 166
Background:
I have a NextJS application using version 13.5 and the app router. Using middleware, I have a subdomain of app
, as well as a neatly organised local file structure for the main website's homepage.
Desired outcome
I would like to introduce i18n in a way that a user can browse the English site without additional path params. So paths would be example.com
, example.com/features
etc. If the user changes their language, or the site automatically detects their language, the path routing would be example.com/es
, example.com/es/features
.
Problem: Introduce i18n by using a dynamic route while accommodating my existing middleware. I have followed the NextJS help docs, but since my middleware isn't the most straightforward, I can't seem to figure out the correct syntax to accommodate my current setup + the dynamic route.
Attempted solutions:
I've attempted to create [lang]
folder inside the first app
folder, which would wrap the existing file structure.
In doing this, I changed the NextResponse.rewrite
method inside my middleware.ts
file to add the dynamic route to the path:
**Before**
new URL(`/example.com${path === "/" ? "" : path}`, req.url)
**After**
new URL(`[lang]/example.com${path === "/" ? "" : path}`, req.url)
This change allowed the routing to continue working, localhost:3000
and app.localhost:3000
. However, it doesn't work when adding language codes to the URL path, i.e. localhost:3000/es
I've tried a multitude of solutions but can't seem to get anything working. I suspect I may need to use rewrites within the next.config.js
, but I'm not at all sure how I could get this working.
Any help would be deeply appreciated. Below is extracts of code for reference.
Project setup:
└── project-root
├── app
│ ├── app <-- subdomain; app.localhost:3000
│ │ ├── (auth)
│ │ │ ├── *sign-in and sign-out routes here*
│ │ │ └── layout.tsx
│ │ └── (dashboard)
│ │ ├── features
│ │ │ └── page.tsx
│ │ ├── page.tsx
│ │ └── layout.tsx
│ ├── example.com <-- home page; localhost:3000
│ │ ├── about
│ │ │ └── page.tsx
│ │ ├── features
│ │ │ └── page.tsx
│ │ ├── page.tsx
│ │ └── layout.tsx
│ ├── layout.tsx
│ └── providers.tsx
├── lib
│ └── middleware
│ └── app.ts
├── middleware.ts
└── next.config.js
middleware.ts
import { NextFetchEvent, NextRequest, NextResponse } from "next/server"
import { APP_HOSTNAMES, isHomeHostname } from "@/lib/constants"
import { AppMiddleware } from "@/lib/middleware"
import { parse } from "@/lib/middleware/utils"
export const config = {
matcher: [
"/((?!api/|_next/|_proxy/|_static|_vercel|[\\w-]+\\.\\w+|).*)",
],
}
export default async function middleware(req: NextRequest, ev: NextFetchEvent) {
const { domain, path, key } = parse(req)
// Rewrite root application to example.com folder
if (isHomeHostname(domain)) {
// the example.com below is referring to the local folder name and not the production domain url
return NextResponse.rewrite(
new URL(`/example.com${path === "/" ? "" : path}`, req.url)
)
}
// Rewrite for App subdomain. Contains Clerk auth middleware to protect
// all app.example.com || app.localhost:3000 routes
if (APP_HOSTNAMES.has(domain)) {
return AppMiddleware(req, ev)
}
}
next.config.js
const { withContentlayer } = require("next-contentlayer")
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
}
// export default nextConfig
module.exports = withContentlayer(nextConfig)
app/layout.tsx
/**
* This is the Root layout which affects all parts of the application.
* It is inherited by all pages within the app folder and example.com folder.
*/
import "@/styles/globals.css"
import { fontInter } from "@/lib/fonts"
import { Analytics } from "@vercel/analytics/react"
import Providers from "./providers"
interface RootLayoutProps {
children: React.ReactNode
}
export default function RootLayout({ children }: RootLayoutProps) {
return (
/**
* Hydration warning suppressed due to using next-theme in Providers
*/
<html
lang="en"
suppressHydrationWarning={true}
className={fontInter.className}
>
<body className="bg-background antialiased">
<Providers>{children}</Providers>
</body>
<Analytics />
</html>
)
}
Upvotes: 1
Views: 467