profit-planet-frontend/src/app/components/LanguageSwitcher.tsx
DeathKaioken 7559466c27 i18
Co-authored-by: Copilot <copilot@github.com>
2026-05-02 21:00:08 +02:00

67 lines
3.2 KiB
TypeScript

'use client';
import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react';
import { ChevronDownIcon } from '@heroicons/react/20/solid';
import { useTranslation } from '../i18n/useTranslation';
// Built-in language info (code → name + flag emoji)
const BUILTIN_LANG_INFO: Record<string, { name: string; flag: string }> = {
en: { name: 'English', flag: '🇬🇧' },
de: { name: 'Deutsch', flag: '🇩🇪' },
};
interface LangEntry { code: string; name: string; flag: string }
interface LanguageSwitcherProps {
variant?: 'light' | 'dark';
}
export default function LanguageSwitcher({ variant = 'light' }: LanguageSwitcherProps) {
const { language, setLanguage, customI18n } = useTranslation();
// Combine built-in + custom languages (deduplicated by code)
const allLangs: LangEntry[] = [
...Object.entries(BUILTIN_LANG_INFO).map(([code, info]) => ({ code, ...info })),
...customI18n.languages
.filter((l) => !BUILTIN_LANG_INFO[l.code])
.map((l) => ({ code: l.code, name: l.name, flag: l.flag ?? '🏳️' })),
];
const activeLang: LangEntry =
allLangs.find((l) => l.code === language) ?? { code: language, name: language, flag: '🏳️' };
const buttonCls =
variant === 'dark'
? 'inline-flex items-center gap-x-1.5 rounded-md bg-white/10 px-3 py-2 text-sm font-semibold text-white hover:bg-white/20 transition-colors'
: 'inline-flex items-center gap-x-1.5 rounded-md bg-gray-100 px-3 py-2 text-sm font-semibold text-gray-900 ring-1 ring-gray-300 hover:bg-gray-200 transition-colors';
const menuCls =
variant === 'dark'
? 'absolute right-0 z-50 mt-2 w-52 origin-top-right rounded-xl bg-gray-800/95 backdrop-blur-sm border border-white/10 shadow-2xl py-1 transition data-closed:scale-95 data-closed:opacity-0 data-enter:duration-100 data-enter:ease-out data-leave:duration-75 data-leave:ease-in'
: 'absolute right-0 z-50 mt-2 w-52 origin-top-right rounded-xl bg-white border border-gray-200 shadow-xl py-1 transition data-closed:scale-95 data-closed:opacity-0 data-enter:duration-100 data-enter:ease-out data-leave:duration-75 data-leave:ease-in';
const itemCls = (isActive: boolean) =>
variant === 'dark'
? `flex w-full items-center gap-3 px-4 py-2.5 text-sm transition-colors ${isActive ? 'bg-[#8D6B1D] text-white' : 'text-gray-200 hover:bg-white/10 hover:text-white'}`
: `flex w-full items-center gap-3 px-4 py-2.5 text-sm transition-colors ${isActive ? 'bg-[#8D6B1D] text-white' : 'text-gray-700 hover:bg-gray-50 hover:text-gray-900'}`;
return (
<Menu as="div" className="relative inline-block">
<MenuButton className={buttonCls}>
<span>{activeLang.name}</span>
<ChevronDownIcon aria-hidden="true" className="size-4 opacity-60" />
</MenuButton>
<MenuItems transition className={menuCls}>
{allLangs.map((lang) => (
<MenuItem key={lang.code}>
<button onClick={() => setLanguage(lang.code)} className={itemCls(language === lang.code)}>
<span className="flex-1 text-left">{lang.name}</span>
{language === lang.code && <span className="text-xs font-bold"></span>}
</button>
</MenuItem>
))}
</MenuItems>
</Menu>
);
}