feat(i18n): add LanguageSwitcher component and useLanguage hook

This commit is contained in:
ZhenYi 2026-04-27 18:19:32 +08:00
parent a93a343d2b
commit 77e0923f28
2 changed files with 65 additions and 0 deletions

View File

@ -0,0 +1,36 @@
import {useLanguage, SUPPORTED_LANGUAGES} from '@/hooks/useLanguage';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import {Button} from '@/components/ui/button';
import {Globe} from 'lucide-react';
export function LanguageSwitcher() {
const {currentLanguage, changeLanguage} = useLanguage();
return (
<DropdownMenu>
<DropdownMenuTrigger>
<Button variant="ghost" size="sm" className="gap-2">
<Globe className="h-4 w-4" />
<span>{currentLanguage.nativeName}</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
{SUPPORTED_LANGUAGES.map((lang) => (
<DropdownMenuItem
key={lang.code}
onClick={() => changeLanguage(lang.code)}
className={lang.code === currentLanguage.code ? 'bg-accent' : ''}
>
<span className="mr-2">{lang.nativeName}</span>
<span className="text-muted-foreground text-xs">{lang.name}</span>
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
);
}

29
src/hooks/useLanguage.ts Normal file
View File

@ -0,0 +1,29 @@
import {useTranslation} from 'react-i18next';
export const SUPPORTED_LANGUAGES = [
{code: 'en', name: 'English', nativeName: 'English'},
{code: 'zh', name: 'Chinese', nativeName: '中文'},
{code: 'de', name: 'German', nativeName: 'Deutsch'},
{code: 'fr', name: 'French', nativeName: 'Français'},
] as const;
export type SupportedLanguage = (typeof SUPPORTED_LANGUAGES)[number]['code'];
export function useLanguage() {
const {i18n} = useTranslation();
const currentLanguage = SUPPORTED_LANGUAGES.find(
(lang) => lang.code === i18n.language,
) ?? SUPPORTED_LANGUAGES[0];
const changeLanguage = (langCode: SupportedLanguage) => {
i18n.changeLanguage(langCode);
};
return {
currentLanguage,
changeLanguage,
supportedLanguages: SUPPORTED_LANGUAGES,
isLoading: !i18n.isInitialized,
};
}