语言选择器组件使用 next-intl 重新路由到 NextJS 中正确的语言页面

问题描述 投票:0回答:1

我正在努力为我的 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>
  );
}

reactjs next.js i18next next-intl
1个回答
0
投票

我在我的项目中做了类似的基于语言的路由,以下是我的方法。尝试按如下方式更新中间件。

//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

}

© www.soinside.com 2019 - 2024. All rights reserved.