我正在努力为我的 NextJS 项目中的语言选择重新路由。目前我有两种语言 EN(默认)和 PT。
开发环境的当前行为是:
localhost:3000
是EN语言PT
,重新路由到 localhost:3000/pt
,但下拉列表中的值保持不变,与 EN
EN
或 PT
,它将重新路由到 localhost:3000/pt/pt
或 /pt/en,从而生成 404 页面。/pt
,它会自动重新加载 localhost:3000/pt
localhost:3000
我希望 EN
成为默认值并显示在语言选择器中PT
时,我想转发到 localhost:3000/pt
并且语言选择器应显示 PT
EN
,那么我应该重新路由到localhost:3000
,或者如果单击已选择的PT
语言,则不会发生任何事情/en/about/something
到 /pt/about/something
下面是我的
middleware
文件和 LanguageSelect
组件。我正在使用 next-intl
包。不确定是否需要更多信息。
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, { useEffect, useState } from "react";
import { ChevronDown } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { useRouter, usePathname } from "@/i18n/routing";
import { routing } from "@/i18n/routing";
export interface Language {
code: string;
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 router = useRouter();
const pathname = usePathname();
const [currentLanguage, setCurrentLanguage] = useState<Language>(
languages[0]
);
useEffect(() => {
const detectedLang =
languages.find(
(lang) =>
pathname === `/${lang.code}` ||
(lang.code === routing.defaultLocale && pathname === "/")
) ||
languages.find((lang) => lang.code === routing.defaultLocale) ||
languages[0];
setCurrentLanguage(detectedLang);
}, [pathname]);
const handleLanguageChange = (lang: Language) => {
if (currentLanguage.code === lang.code) return;
let newPathname: string;
if (lang.code === routing.defaultLocale) {
newPathname = "/";
} else {
newPathname = `/${lang.code}`;
}
console.log("Changing language:", currentLanguage.code, "->", lang.code);
console.log("Current pathname:", pathname);
console.log("New pathname:", newPathname);
setCurrentLanguage(lang);
router.push(newPathname);
};
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}
className="hover:bg-gray-700 focus:bg-gray-700 text-gray-200 hover:text-gray-200 focus:text-gray-200"
onClick={() => handleLanguageChange(lang)}
>
{lang.name}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
);
}
我在我的项目中做了类似的基于语言的路由,以下是我的方法。尝试按如下方式更新中间件。
//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
}