Files
ignum/components/languageSwitcher.tsx
nabijonovdavronbek619@gmail.com de2554a2e7 language Switcher added
2026-01-27 17:36:09 +05:00

121 lines
4.0 KiB
TypeScript

"use client";
import { useState, useTransition } from "react";
import { useRouter, usePathname } from "next/navigation";
import { Check, ChevronDown, Globe } from "lucide-react";
import { locales, localeFlags, localeNames, type Locale } from "@/i18n/config";
import { useLocale } from "next-intl";
export default function LanguageSelectRadix() {
const router = useRouter();
const pathname = usePathname();
const currentLocale = useLocale() as Locale;
const [isPending, startTransition] = useTransition();
const [isOpen, setIsOpen] = useState(false);
const changeLanguage = (newLocale: Locale) => {
if (newLocale === currentLocale) {
setIsOpen(false);
return;
}
startTransition(() => {
// ✅ 1. Set cookie (middleware will sync this)
document.cookie = `NEXT_LOCALE=${newLocale}; path=/; max-age=31536000; SameSite=Lax`;
// ✅ 2. Build new path
const segments = pathname.split("/").filter(Boolean);
// Remove current locale if exists
const pathWithoutLocale =
segments[0] && locales.includes(segments[0] as Locale)
? segments.slice(1)
: segments;
// Add new locale
const newPath = `/${newLocale}${
pathWithoutLocale.length ? "/" + pathWithoutLocale.join("/") : ""
}`;
// ✅ 3. Navigate (middleware will handle the rest)
router.push(newPath);
// ✅ 4. Force refresh after navigation completes
setTimeout(() => {
router.refresh();
}, 100); // Small delay ensures navigation completes
});
setIsOpen(false);
};
return (
<div className="relative">
{/* Trigger Button */}
<button
onClick={() => setIsOpen(!isOpen)}
disabled={isPending}
className="inline-flex items-center justify-between gap-2 px-2 py-1 border border-gray-300 hover:border-red-600
rounded-lg text-white text-sm font-medium shadow-sm hover:bg-red-600 disabled:opacity-50 disabled:cursor-not-allowed transition-all"
aria-label="Tilni tanlash"
aria-expanded={isOpen}
>
<div className="flex items-center gap-2">
{isPending ? (
<Globe className="w-4 h-4 animate-spin" />
) : (
<span className="text-lg">{localeFlags[currentLocale]}</span>
)}
<span className="hidden md:inline font-medium">
{localeNames[currentLocale]}
</span>
</div>
<ChevronDown
className={`w-4 h-4 transition-transform duration-200 ${
isOpen ? "rotate-180" : ""
}`}
/>
</button>
{/* Dropdown */}
{isOpen && (
<>
{/* Backdrop */}
<div
className="fixed inset-0 z-40"
onClick={() => setIsOpen(false)}
aria-hidden="true"
/>
{/* Menu */}
<div className="absolute right-0 mt-2 w-48 rounded-lg border border-gray-200 bg-white shadow-lg z-50 animate-in fade-in slide-in-from-top-2 duration-200">
<div className="p-1" role="menu">
{locales.map((lang) => (
<button
key={lang}
onClick={() => changeLanguage(lang)}
disabled={isPending}
role="menuitem"
className={`w-full flex items-center justify-between px-3 py-2 text-sm rounded-md transition-colors ${
currentLocale === lang
? "bg-blue-50 text-blue-700 font-medium"
: "hover:bg-gray-100 text-gray-700"
} disabled:opacity-50 disabled:cursor-not-allowed`}
>
<div className="flex items-center gap-2">
<span className="text-lg">{localeFlags[lang]}</span>
<span>{localeNames[lang]}</span>
</div>
{currentLocale === lang && (
<Check className="w-4 h-4 text-blue-700" />
)}
</button>
))}
</div>
</div>
</>
)}
</div>
);
}