feat: improve locale switching with dedicated LocaleSwitcher component
- Create new `LocaleSwitcher` component with dropdown menu for language selection - Modify `LocaleSelector` to remove `showLocaleName` prop - Update `Navbar` to use new `LocaleSwitcher` instead of `LocaleSelector` - Adjust `ThemeSwitcher` button styling for consistency - Enhance locale switching user experience with more intuitive UI
This commit is contained in:
parent
57166fe250
commit
c96d3952d0
@ -24,9 +24,7 @@ import { useEffect, useTransition } from 'react';
|
||||
*
|
||||
* https://next-intl.dev/docs/routing/navigation#userouter
|
||||
*/
|
||||
export default function LocaleSelector({
|
||||
showLocaleName = true,
|
||||
}: { showLocaleName?: boolean }) {
|
||||
export default function LocaleSelector() {
|
||||
const router = useLocaleRouter();
|
||||
const pathname = useLocalePathname();
|
||||
const params = useParams();
|
||||
@ -67,7 +65,7 @@ export default function LocaleSelector({
|
||||
{currentLocale && (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-lg">{LOCALE_LIST[currentLocale].flag}</span>
|
||||
{showLocaleName && <span>{LOCALE_LIST[currentLocale].name}</span>}
|
||||
<span>{LOCALE_LIST[currentLocale].name}</span>
|
||||
</div>
|
||||
)}
|
||||
</SelectValue>
|
||||
|
78
src/components/layout/locale-switcher.tsx
Normal file
78
src/components/layout/locale-switcher.tsx
Normal file
@ -0,0 +1,78 @@
|
||||
'use client';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { useLocalePathname, useLocaleRouter } from '@/i18n/navigation';
|
||||
import { DEFAULT_LOCALE, Locale, LOCALE_LIST, routing } from '@/i18n/routing';
|
||||
import { useLocaleStore } from '@/stores/locale-store';
|
||||
import { Languages } from 'lucide-react';
|
||||
import { useLocale } from 'next-intl';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { useEffect, useTransition } from 'react';
|
||||
|
||||
/**
|
||||
* LocaleSwitcher component
|
||||
*
|
||||
* Allows users to switch between available locales using a dropdown menu.
|
||||
*
|
||||
* Based on next-intl's useRouter and usePathname for locale navigation.
|
||||
* https://next-intl.dev/docs/routing/navigation#userouter
|
||||
*/
|
||||
export default function LocaleSwitcher() {
|
||||
const router = useLocaleRouter();
|
||||
const pathname = useLocalePathname();
|
||||
const params = useParams();
|
||||
const locale = useLocale();
|
||||
const { currentLocale, setCurrentLocale } = useLocaleStore();
|
||||
const [, startTransition] = useTransition();
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentLocale(locale);
|
||||
}, [locale, setCurrentLocale]);
|
||||
|
||||
function onSelectLocale(nextLocale: Locale) {
|
||||
setCurrentLocale(nextLocale);
|
||||
|
||||
startTransition(() => {
|
||||
router.replace(
|
||||
// @ts-expect-error -- TypeScript will validate that only known `params`
|
||||
// are used in combination with a given `pathname`. Since the two will
|
||||
// always match for the current route, we can skip runtime checks.
|
||||
{ pathname, params },
|
||||
{ locale: nextLocale }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-9 w-9 p-0.5 border border-border rounded-full"
|
||||
>
|
||||
<Languages className="size-3" />
|
||||
<span className="sr-only">Switch language</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
{routing.locales.map((localeOption) => (
|
||||
<DropdownMenuItem
|
||||
key={localeOption}
|
||||
onClick={() => onSelectLocale(localeOption)}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<span className="mr-2 text-md">{LOCALE_LIST[localeOption].flag}</span>
|
||||
<span className="text-sm">{LOCALE_LIST[localeOption].name}</span>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
@ -26,6 +26,7 @@ import { cn } from '@/lib/utils';
|
||||
import { Routes } from '@/routes';
|
||||
import { ArrowUpRightIcon } from 'lucide-react';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import LocaleSwitcher from './locale-switcher';
|
||||
|
||||
interface NavBarProps {
|
||||
scroll?: boolean;
|
||||
@ -227,7 +228,7 @@ export function Navbar({ scroll }: NavBarProps) {
|
||||
)}
|
||||
|
||||
<ThemeSwitcher />
|
||||
<LocaleSelector showLocaleName={false} />
|
||||
<LocaleSwitcher />
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
@ -21,8 +21,8 @@ export function ThemeSwitcher() {
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="p-2 border border-border rounded-full text-sm"
|
||||
size="sm"
|
||||
className="h-9 w-9 p-0.5 border border-border rounded-full"
|
||||
>
|
||||
<SunIcon className="rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
||||
<MoonIcon className="absolute rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
||||
|
Loading…
Reference in New Issue
Block a user