everspader
everspader

Reputation: 1710

Language Selector component to re-route to correct Language page in NextJS with next-intl

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:

Desired behavior:

Below is my middleware file and the LanguageSelect component. I am using next-intl package. Not sure if more information is required.

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*"],
};

"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

Answers (1)

Jouhar
Jouhar

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

Related Questions