Reputation: 1710
I am struggling to get the re-routing right for language selection in my NextJS project. At the moment I have two languages EN (default) and PT.
The current behavior for the dev environment is:
localhost:3000
I want EN
to be the default and to be displayed in the language selectorPT
I want to be forwarded to localhost:3000/pt
and the language selector should display PT
EN
then I should be re-routed to localhost:3000
or if clicking on the already selected PT
language, nothing should happen/en/about/something
to /pt/about/something
Below is my middleware
file and the LanguageSelect
component. I am using next-intl
package. Not sure if more information is required.
src/middleware.ts
import createMiddleware from "next-intl/middleware";
import { routing } from "./i18n/routing";
export default createMiddleware({
locales: routing.locales,
defaultLocale: routing.defaultLocale,
localePrefix: "as-needed",
});
export const config = {
matcher: ["/((?!api|_next|_vercel|.*\\..*).*)", "/(pt|en)/:path*"],
};
src/components/LanguageSelect.tsx
"use client";
import React, { useState, useEffect } from "react";
import { ChevronDown } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { usePathname, useRouter } from "@/i18n/routing";
import { routing } from "@/i18n/routing";
export interface Language {
code: (typeof routing.locales)[number];
name: string;
}
export const languages: Language[] = routing.locales.map((locale) => ({
code: locale,
name: locale.toUpperCase(),
}));
interface LanguageSelectProps {
isMobile: boolean;
}
export function LanguageSelect({ isMobile }: LanguageSelectProps) {
const pathname = usePathname();
const router = useRouter();
const [currentLanguage, setCurrentLanguage] = useState<Language>(
languages[0]
);
useEffect(() => {
const detectedLang =
languages.find((lang) => pathname.startsWith(`/${lang.code}`)) ||
languages.find((lang) => lang.code === routing.defaultLocale) ||
languages[0];
setCurrentLanguage(detectedLang);
}, [pathname]);
const handleLanguageChange = (lang: Language) => {
setCurrentLanguage(lang);
console.log(currentLanguage);
router.push(pathname, { locale: lang.code });
};
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
className={`flex items-center space-x-1 bg-gray-800 hover:bg-gray-700 focus:ring-0 text-gray-200 ${
isMobile ? "border border-gray-600" : ""
}`}
>
<span>{currentLanguage.name}</span>
<ChevronDown className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="bg-gray-800 border-gray-700">
{languages.map((lang) => (
<DropdownMenuItem
key={lang.code}
onClick={() => handleLanguageChange(lang)}
className="hover:bg-gray-700 focus:bg-gray-700 text-gray-200 hover:text-gray-200 focus:text-gray-200"
>
{lang.name}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
);
}
Upvotes: 0
Views: 218
Reputation: 1520
I did similar language based routing in my project and following was my approach. Try updating middleware as follows.
//middleware.tsx
const i18n = {
locales: ["pt", "en"],
};
export default async function middleware(
req: NextRequest,
event: NextFetchEvent
) {
const pathname = req.nextUrl.pathname;
const localeInPathName = i18n.locales.find((locale) =>
pathname.startsWith(`/${locale}`)
);
let locale = localeInPathName ?? "en";
//for client routes only adding locale
if (
!localeInPathName &&
!(
pathname.startsWith("/_next") ||
pathname.includes("/api/") ||
PUBLIC_FILE.test(pathname)
)
) {
return NextResponse.redirect(
new URL(
`/${locale}${pathname.startsWith("/") ? "" : "/"}${pathname}`,
req.url
)
);
}
//Other scenarios
}
Upvotes: 0