mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-02 03:55:55 +00:00
perf: complete sidebar menu
This commit is contained in:
@@ -11,16 +11,17 @@ import { sidebarConfigList } from '@/app/home/components/home-sidebar/sidbarConf
|
|||||||
import langbotIcon from '@/app/assets/langbot-logo.webp';
|
import langbotIcon from '@/app/assets/langbot-logo.webp';
|
||||||
import { systemInfo } from '@/app/infra/http/HttpClient';
|
import { systemInfo } from '@/app/infra/http/HttpClient';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import { Moon, Sun, Monitor } from 'lucide-react';
|
||||||
DropdownMenu,
|
import { useTheme } from 'next-themes';
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuLabel,
|
|
||||||
DropdownMenuSeparator,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from '@/components/ui/dropdown-menu';
|
|
||||||
|
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from '@/components/ui/popover';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
|
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
|
||||||
|
import { LanguageSelector } from '@/components/ui/language-selector';
|
||||||
|
|
||||||
// TODO 侧边导航栏要加动画
|
// TODO 侧边导航栏要加动画
|
||||||
export default function HomeSidebar({
|
export default function HomeSidebar({
|
||||||
@@ -37,8 +38,10 @@ export default function HomeSidebar({
|
|||||||
}, [pathname]);
|
}, [pathname]);
|
||||||
|
|
||||||
const [selectedChild, setSelectedChild] = useState<SidebarChildVO>();
|
const [selectedChild, setSelectedChild] = useState<SidebarChildVO>();
|
||||||
|
const { theme, setTheme } = useTheme();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const [popoverOpen, setPopoverOpen] = useState(false);
|
||||||
|
const [languageSelectorOpen, setLanguageSelectorOpen] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initSelect();
|
initSelect();
|
||||||
@@ -178,9 +181,32 @@ export default function HomeSidebar({
|
|||||||
}
|
}
|
||||||
name={t('common.helpDocs')}
|
name={t('common.helpDocs')}
|
||||||
/>
|
/>
|
||||||
|
{/* <SidebarChild
|
||||||
|
onClick={() => {
|
||||||
|
handleLogout();
|
||||||
|
}}
|
||||||
|
isSelected={false}
|
||||||
|
icon={
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
<path d="M4 18H6V20H18V4H6V6H4V3C4 2.44772 4.44772 2 5 2H19C19.5523 2 20 2.44772 20 3V21C20 21.5523 19.5523 22 19 22H5C4.44772 22 4 21.5523 4 21V18ZM6 11H13V13H6V16L1 12L6 8V11Z"></path>
|
||||||
|
</svg>
|
||||||
|
}
|
||||||
|
name={t('common.logout')}
|
||||||
|
/> */}
|
||||||
|
|
||||||
<DropdownMenu>
|
<Popover
|
||||||
<DropdownMenuTrigger>
|
open={popoverOpen}
|
||||||
|
onOpenChange={(open) => {
|
||||||
|
// 防止语言选择器打开时关闭popover
|
||||||
|
if (!open && languageSelectorOpen) return;
|
||||||
|
setPopoverOpen(open);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PopoverTrigger>
|
||||||
<SidebarChild
|
<SidebarChild
|
||||||
onClick={() => {}}
|
onClick={() => {}}
|
||||||
isSelected={false}
|
isSelected={false}
|
||||||
@@ -195,26 +221,65 @@ export default function HomeSidebar({
|
|||||||
}
|
}
|
||||||
name={t('common.accountOptions')}
|
name={t('common.accountOptions')}
|
||||||
/>
|
/>
|
||||||
</DropdownMenuTrigger>
|
</PopoverTrigger>
|
||||||
<DropdownMenuContent side="right" align="end">
|
<PopoverContent
|
||||||
{/* <DropdownMenuLabel>My Account</DropdownMenuLabel> */}
|
side="right"
|
||||||
<DropdownMenuItem>{/* 主题颜色切换 */}</DropdownMenuItem>
|
align="end"
|
||||||
<DropdownMenuItem
|
className="w-auto p-4 flex flex-col gap-4"
|
||||||
onClick={() => {
|
>
|
||||||
handleLogout();
|
<div className="flex flex-col gap-2 w-full">
|
||||||
}}
|
<span className="text-sm font-medium">{t('common.theme')}</span>
|
||||||
>
|
<ToggleGroup
|
||||||
<svg
|
type="single"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
value={theme}
|
||||||
viewBox="0 0 24 24"
|
onValueChange={(value) => {
|
||||||
fill="currentColor"
|
if (value) setTheme(value);
|
||||||
|
}}
|
||||||
|
className="justify-start"
|
||||||
>
|
>
|
||||||
<path d="M4 18H6V20H18V4H6V6H4V3C4 2.44772 4.44772 2 5 2H19C19.5523 2 20 2.44772 20 3V21C20 21.5523 19.5523 22 19 22H5C4.44772 22 4 21.5523 4 21V18ZM6 11H13V13H6V16L1 12L6 8V11Z"></path>
|
<ToggleGroupItem value="light" size="sm">
|
||||||
</svg>
|
<Sun className="h-4 w-4" />
|
||||||
{t('common.logout')}
|
</ToggleGroupItem>
|
||||||
</DropdownMenuItem>
|
<ToggleGroupItem value="dark" size="sm">
|
||||||
</DropdownMenuContent>
|
<Moon className="h-4 w-4" />
|
||||||
</DropdownMenu>
|
</ToggleGroupItem>
|
||||||
|
<ToggleGroupItem value="system" size="sm">
|
||||||
|
<Monitor className="h-4 w-4" />
|
||||||
|
</ToggleGroupItem>
|
||||||
|
</ToggleGroup>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-2 w-full">
|
||||||
|
<span className="text-sm font-medium">
|
||||||
|
{t('common.language')}
|
||||||
|
</span>
|
||||||
|
<LanguageSelector
|
||||||
|
triggerClassName="w-full"
|
||||||
|
onOpenChange={setLanguageSelectorOpen}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-2 w-full">
|
||||||
|
<span className="text-sm font-medium">{t('common.account')}</span>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
className="w-full justify-start font-normal"
|
||||||
|
onClick={() => {
|
||||||
|
handleLogout();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
<path d="M4 18H6V20H18V4H6V6H4V3C4 2.44772 4.44772 2 5 2H19C19.5523 2 20 2.44772 20 3V21C20 21.5523 19.5523 22 19 22H5C4.44772 22 4 21.5523 4 21V18ZM6 11H13V13H6V16L1 12L6 8V11Z"></path>
|
||||||
|
</svg>
|
||||||
|
{t('common.logout')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,13 +8,7 @@ import {
|
|||||||
CardTitle,
|
CardTitle,
|
||||||
CardDescription,
|
CardDescription,
|
||||||
} from '@/components/ui/card';
|
} from '@/components/ui/card';
|
||||||
import {
|
import { LanguageSelector } from '@/components/ui/language-selector';
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from '@/components/ui/select';
|
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import * as z from 'zod';
|
import * as z from 'zod';
|
||||||
@@ -26,14 +20,13 @@ import {
|
|||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from '@/components/ui/form';
|
} from '@/components/ui/form';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { httpClient } from '@/app/infra/http/HttpClient';
|
import { httpClient } from '@/app/infra/http/HttpClient';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { Mail, Lock, Globe } from 'lucide-react';
|
import { Mail, Lock } from 'lucide-react';
|
||||||
import langbotIcon from '@/app/assets/langbot-logo.webp';
|
import langbotIcon from '@/app/assets/langbot-logo.webp';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import i18n from '@/i18n';
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { ThemeToggle } from '@/components/ui/theme-toggle';
|
import { ThemeToggle } from '@/components/ui/theme-toggle';
|
||||||
|
|
||||||
@@ -46,7 +39,6 @@ const formSchema = (t: (key: string) => string) =>
|
|||||||
export default function Login() {
|
export default function Login() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [currentLanguage, setCurrentLanguage] = useState<string>(i18n.language);
|
|
||||||
|
|
||||||
const form = useForm<z.infer<ReturnType<typeof formSchema>>>({
|
const form = useForm<z.infer<ReturnType<typeof formSchema>>>({
|
||||||
resolver: zodResolver(formSchema(t)),
|
resolver: zodResolver(formSchema(t)),
|
||||||
@@ -57,57 +49,10 @@ export default function Login() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
judgeLanguage();
|
|
||||||
getIsInitialized();
|
getIsInitialized();
|
||||||
checkIfAlreadyLoggedIn();
|
checkIfAlreadyLoggedIn();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const judgeLanguage = () => {
|
|
||||||
if (i18n.language === 'zh-CN' || i18n.language === 'zh-Hans') {
|
|
||||||
setCurrentLanguage('zh-Hans');
|
|
||||||
localStorage.setItem('langbot_language', 'zh-Hans');
|
|
||||||
} else if (i18n.language === 'zh-TW' || i18n.language === 'zh-Hant') {
|
|
||||||
setCurrentLanguage('zh-Hant');
|
|
||||||
localStorage.setItem('langbot_language', 'zh-Hant');
|
|
||||||
} else if (i18n.language === 'ja' || i18n.language === 'ja-JP') {
|
|
||||||
setCurrentLanguage('ja-JP');
|
|
||||||
localStorage.setItem('langbot_language', 'ja-JP');
|
|
||||||
} else {
|
|
||||||
setCurrentLanguage('en-US');
|
|
||||||
localStorage.setItem('langbot_language', 'en-US');
|
|
||||||
}
|
|
||||||
// check if the language is already set
|
|
||||||
const lang = localStorage.getItem('langbot_language');
|
|
||||||
if (lang) {
|
|
||||||
i18n.changeLanguage(lang);
|
|
||||||
setCurrentLanguage(lang);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
const language = navigator.language;
|
|
||||||
if (language) {
|
|
||||||
let lang = 'zh-Hans';
|
|
||||||
if (language === 'zh-CN') {
|
|
||||||
lang = 'zh-Hans';
|
|
||||||
} else if (language === 'zh-TW') {
|
|
||||||
lang = 'zh-Hant';
|
|
||||||
} else if (language === 'ja' || language === 'ja-JP') {
|
|
||||||
lang = 'ja-JP';
|
|
||||||
} else {
|
|
||||||
lang = 'en-US';
|
|
||||||
}
|
|
||||||
i18n.changeLanguage(lang);
|
|
||||||
setCurrentLanguage(lang);
|
|
||||||
localStorage.setItem('langbot_language', lang);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleLanguageChange = (value: string) => {
|
|
||||||
i18n.changeLanguage(value);
|
|
||||||
setCurrentLanguage(value);
|
|
||||||
localStorage.setItem('langbot_language', value);
|
|
||||||
};
|
|
||||||
|
|
||||||
function getIsInitialized() {
|
function getIsInitialized() {
|
||||||
httpClient
|
httpClient
|
||||||
.checkIfInited()
|
.checkIfInited()
|
||||||
@@ -161,21 +106,7 @@ export default function Login() {
|
|||||||
<CardHeader>
|
<CardHeader>
|
||||||
<div className="flex justify-between items-center mb-6">
|
<div className="flex justify-between items-center mb-6">
|
||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
<Select
|
<LanguageSelector />
|
||||||
value={currentLanguage}
|
|
||||||
onValueChange={handleLanguageChange}
|
|
||||||
>
|
|
||||||
<SelectTrigger className="w-[140px]">
|
|
||||||
<Globe className="h-4 w-4 mr-2" />
|
|
||||||
<SelectValue placeholder={t('common.language')} />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="zh-Hans">简体中文</SelectItem>
|
|
||||||
<SelectItem value="zh-Hant">繁體中文</SelectItem>
|
|
||||||
<SelectItem value="en-US">English</SelectItem>
|
|
||||||
<SelectItem value="ja-JP">日本語</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
</div>
|
||||||
<img
|
<img
|
||||||
src={langbotIcon.src}
|
src={langbotIcon.src}
|
||||||
|
|||||||
@@ -8,13 +8,7 @@ import {
|
|||||||
CardTitle,
|
CardTitle,
|
||||||
CardDescription,
|
CardDescription,
|
||||||
} from '@/components/ui/card';
|
} from '@/components/ui/card';
|
||||||
import {
|
import { LanguageSelector } from '@/components/ui/language-selector';
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from '@/components/ui/select';
|
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import * as z from 'zod';
|
import * as z from 'zod';
|
||||||
@@ -26,14 +20,13 @@ import {
|
|||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from '@/components/ui/form';
|
} from '@/components/ui/form';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { httpClient } from '@/app/infra/http/HttpClient';
|
import { httpClient } from '@/app/infra/http/HttpClient';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { Mail, Lock, Globe } from 'lucide-react';
|
import { Mail, Lock } from 'lucide-react';
|
||||||
import langbotIcon from '@/app/assets/langbot-logo.webp';
|
import langbotIcon from '@/app/assets/langbot-logo.webp';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import i18n from '@/i18n';
|
|
||||||
import { ThemeToggle } from '@/components/ui/theme-toggle';
|
import { ThemeToggle } from '@/components/ui/theme-toggle';
|
||||||
|
|
||||||
const formSchema = (t: (key: string) => string) =>
|
const formSchema = (t: (key: string) => string) =>
|
||||||
@@ -45,7 +38,6 @@ const formSchema = (t: (key: string) => string) =>
|
|||||||
export default function Register() {
|
export default function Register() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [currentLanguage, setCurrentLanguage] = useState<string>(i18n.language);
|
|
||||||
|
|
||||||
const form = useForm<z.infer<ReturnType<typeof formSchema>>>({
|
const form = useForm<z.infer<ReturnType<typeof formSchema>>>({
|
||||||
resolver: zodResolver(formSchema(t)),
|
resolver: zodResolver(formSchema(t)),
|
||||||
@@ -56,58 +48,9 @@ export default function Register() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
judgeLanguage();
|
|
||||||
getIsInitialized();
|
getIsInitialized();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const judgeLanguage = () => {
|
|
||||||
if (i18n.language === 'zh-CN' || i18n.language === 'zh-Hans') {
|
|
||||||
setCurrentLanguage('zh-Hans');
|
|
||||||
localStorage.setItem('langbot_language', 'zh-Hans');
|
|
||||||
} else if (i18n.language === 'zh-TW' || i18n.language === 'zh-Hant') {
|
|
||||||
setCurrentLanguage('zh-Hant');
|
|
||||||
localStorage.setItem('langbot_language', 'zh-Hant');
|
|
||||||
} else if (i18n.language === 'ja' || i18n.language === 'ja-JP') {
|
|
||||||
setCurrentLanguage('ja-JP');
|
|
||||||
localStorage.setItem('langbot_language', 'ja-JP');
|
|
||||||
} else {
|
|
||||||
setCurrentLanguage('en-US');
|
|
||||||
localStorage.setItem('langbot_language', 'en-US');
|
|
||||||
}
|
|
||||||
// check if the language is already set
|
|
||||||
const lang = localStorage.getItem('langbot_language');
|
|
||||||
console.log('lang: ', lang);
|
|
||||||
if (lang) {
|
|
||||||
i18n.changeLanguage(lang);
|
|
||||||
setCurrentLanguage(lang);
|
|
||||||
} else {
|
|
||||||
const language = navigator.language;
|
|
||||||
if (language) {
|
|
||||||
let lang = 'zh-Hans';
|
|
||||||
if (language === 'zh-CN') {
|
|
||||||
lang = 'zh-Hans';
|
|
||||||
} else if (language === 'zh-TW') {
|
|
||||||
lang = 'zh-Hant';
|
|
||||||
} else if (language === 'ja' || language === 'ja-JP') {
|
|
||||||
lang = 'ja-JP';
|
|
||||||
} else {
|
|
||||||
lang = 'en-US';
|
|
||||||
}
|
|
||||||
console.log('language: ', lang);
|
|
||||||
i18n.changeLanguage(lang);
|
|
||||||
setCurrentLanguage(lang);
|
|
||||||
localStorage.setItem('langbot_language', lang);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleLanguageChange = (value: string) => {
|
|
||||||
console.log('handleLanguageChange: ', value);
|
|
||||||
i18n.changeLanguage(value);
|
|
||||||
setCurrentLanguage(value);
|
|
||||||
localStorage.setItem('langbot_language', value);
|
|
||||||
};
|
|
||||||
|
|
||||||
function getIsInitialized() {
|
function getIsInitialized() {
|
||||||
httpClient
|
httpClient
|
||||||
.checkIfInited()
|
.checkIfInited()
|
||||||
@@ -145,21 +88,7 @@ export default function Register() {
|
|||||||
<CardHeader>
|
<CardHeader>
|
||||||
<div className="flex justify-between items-center mb-6">
|
<div className="flex justify-between items-center mb-6">
|
||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
<Select
|
<LanguageSelector />
|
||||||
value={currentLanguage}
|
|
||||||
onValueChange={handleLanguageChange}
|
|
||||||
>
|
|
||||||
<SelectTrigger className="w-[140px]">
|
|
||||||
<Globe className="h-4 w-4 mr-2" />
|
|
||||||
<SelectValue placeholder={t('common.language')} />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="zh-Hans">简体中文</SelectItem>
|
|
||||||
<SelectItem value="zh-Hant">繁體中文</SelectItem>
|
|
||||||
<SelectItem value="en-US">English</SelectItem>
|
|
||||||
<SelectItem value="ja-JP">日本語</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
</div>
|
||||||
<img
|
<img
|
||||||
src={langbotIcon.src}
|
src={langbotIcon.src}
|
||||||
|
|||||||
98
web/src/components/ui/language-selector.tsx
Normal file
98
web/src/components/ui/language-selector.tsx
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select';
|
||||||
|
import { Globe } from 'lucide-react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import i18n from '@/i18n';
|
||||||
|
|
||||||
|
interface LanguageSelectorProps {
|
||||||
|
className?: string;
|
||||||
|
triggerClassName?: string;
|
||||||
|
onOpenChange?: (open: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LanguageSelector({
|
||||||
|
triggerClassName,
|
||||||
|
onOpenChange,
|
||||||
|
}: LanguageSelectorProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [currentLanguage, setCurrentLanguage] = useState<string>(i18n.language);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
initializeLanguage();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const initializeLanguage = () => {
|
||||||
|
if (i18n.language === 'zh-CN' || i18n.language === 'zh-Hans') {
|
||||||
|
setCurrentLanguage('zh-Hans');
|
||||||
|
localStorage.setItem('langbot_language', 'zh-Hans');
|
||||||
|
} else if (i18n.language === 'zh-TW' || i18n.language === 'zh-Hant') {
|
||||||
|
setCurrentLanguage('zh-Hant');
|
||||||
|
localStorage.setItem('langbot_language', 'zh-Hant');
|
||||||
|
} else if (i18n.language === 'ja' || i18n.language === 'ja-JP') {
|
||||||
|
setCurrentLanguage('ja-JP');
|
||||||
|
localStorage.setItem('langbot_language', 'ja-JP');
|
||||||
|
} else {
|
||||||
|
setCurrentLanguage('en-US');
|
||||||
|
localStorage.setItem('langbot_language', 'en-US');
|
||||||
|
}
|
||||||
|
|
||||||
|
const savedLanguage = localStorage.getItem('langbot_language');
|
||||||
|
if (savedLanguage) {
|
||||||
|
i18n.changeLanguage(savedLanguage);
|
||||||
|
setCurrentLanguage(savedLanguage);
|
||||||
|
} else {
|
||||||
|
const browserLanguage = navigator.language;
|
||||||
|
if (browserLanguage) {
|
||||||
|
let detectedLanguage = 'zh-Hans';
|
||||||
|
if (browserLanguage === 'zh-CN') {
|
||||||
|
detectedLanguage = 'zh-Hans';
|
||||||
|
} else if (browserLanguage === 'zh-TW') {
|
||||||
|
detectedLanguage = 'zh-Hant';
|
||||||
|
} else if (browserLanguage === 'ja' || browserLanguage === 'ja-JP') {
|
||||||
|
detectedLanguage = 'ja-JP';
|
||||||
|
} else {
|
||||||
|
detectedLanguage = 'en-US';
|
||||||
|
}
|
||||||
|
i18n.changeLanguage(detectedLanguage);
|
||||||
|
setCurrentLanguage(detectedLanguage);
|
||||||
|
localStorage.setItem('langbot_language', detectedLanguage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLanguageChange = (value: string) => {
|
||||||
|
i18n.changeLanguage(value);
|
||||||
|
setCurrentLanguage(value);
|
||||||
|
localStorage.setItem('langbot_language', value);
|
||||||
|
|
||||||
|
// 刷新页面以应用新的语言设置
|
||||||
|
window.location.reload();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
value={currentLanguage}
|
||||||
|
onValueChange={handleLanguageChange}
|
||||||
|
onOpenChange={onOpenChange}
|
||||||
|
>
|
||||||
|
<SelectTrigger className={triggerClassName || 'w-[140px]'}>
|
||||||
|
<Globe className="h-4 w-4 mr-2" />
|
||||||
|
<SelectValue placeholder={t('common.language')} />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="zh-Hans">简体中文</SelectItem>
|
||||||
|
<SelectItem value="zh-Hant">繁體中文</SelectItem>
|
||||||
|
<SelectItem value="en-US">English</SelectItem>
|
||||||
|
<SelectItem value="ja-JP">日本語</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -2,7 +2,8 @@ const enUS = {
|
|||||||
common: {
|
common: {
|
||||||
login: 'Login',
|
login: 'Login',
|
||||||
logout: 'Logout',
|
logout: 'Logout',
|
||||||
accountOptions: 'Account Options',
|
accountOptions: 'Account',
|
||||||
|
account: 'Account',
|
||||||
email: 'Email',
|
email: 'Email',
|
||||||
password: 'Password',
|
password: 'Password',
|
||||||
welcome: 'Welcome back to LangBot 👋',
|
welcome: 'Welcome back to LangBot 👋',
|
||||||
@@ -42,6 +43,7 @@ const enUS = {
|
|||||||
test: 'Test',
|
test: 'Test',
|
||||||
forgotPassword: 'Forgot Password?',
|
forgotPassword: 'Forgot Password?',
|
||||||
loading: 'Loading...',
|
loading: 'Loading...',
|
||||||
|
theme: 'Theme',
|
||||||
},
|
},
|
||||||
notFound: {
|
notFound: {
|
||||||
title: 'Page not found',
|
title: 'Page not found',
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ const jaJP = {
|
|||||||
login: 'ログイン',
|
login: 'ログイン',
|
||||||
logout: 'ログアウト',
|
logout: 'ログアウト',
|
||||||
accountOptions: 'アカウントオプション',
|
accountOptions: 'アカウントオプション',
|
||||||
|
account: 'アカウント',
|
||||||
email: 'メールアドレス',
|
email: 'メールアドレス',
|
||||||
password: 'パスワード',
|
password: 'パスワード',
|
||||||
welcome: 'LangBot へおかえりなさい 👋',
|
welcome: 'LangBot へおかえりなさい 👋',
|
||||||
@@ -43,6 +44,7 @@ const jaJP = {
|
|||||||
test: 'テスト',
|
test: 'テスト',
|
||||||
forgotPassword: 'パスワードを忘れた?',
|
forgotPassword: 'パスワードを忘れた?',
|
||||||
loading: '読み込み中...',
|
loading: '読み込み中...',
|
||||||
|
theme: 'テーマ',
|
||||||
},
|
},
|
||||||
notFound: {
|
notFound: {
|
||||||
title: 'ページが見つかりません',
|
title: 'ページが見つかりません',
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ const zhHans = {
|
|||||||
login: '登录',
|
login: '登录',
|
||||||
logout: '退出登录',
|
logout: '退出登录',
|
||||||
accountOptions: '账户选项',
|
accountOptions: '账户选项',
|
||||||
|
account: '账户',
|
||||||
email: '邮箱',
|
email: '邮箱',
|
||||||
password: '密码',
|
password: '密码',
|
||||||
welcome: '欢迎回到 LangBot 👋',
|
welcome: '欢迎回到 LangBot 👋',
|
||||||
@@ -42,6 +43,7 @@ const zhHans = {
|
|||||||
test: '测试',
|
test: '测试',
|
||||||
forgotPassword: '忘记密码?',
|
forgotPassword: '忘记密码?',
|
||||||
loading: '加载中...',
|
loading: '加载中...',
|
||||||
|
theme: '主题',
|
||||||
},
|
},
|
||||||
notFound: {
|
notFound: {
|
||||||
title: '页面不存在',
|
title: '页面不存在',
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ const zhHant = {
|
|||||||
login: '登入',
|
login: '登入',
|
||||||
logout: '登出',
|
logout: '登出',
|
||||||
accountOptions: '帳戶選項',
|
accountOptions: '帳戶選項',
|
||||||
|
account: '帳戶',
|
||||||
email: '電子郵件',
|
email: '電子郵件',
|
||||||
password: '密碼',
|
password: '密碼',
|
||||||
welcome: '歡迎回到 LangBot 👋',
|
welcome: '歡迎回到 LangBot 👋',
|
||||||
@@ -42,6 +43,7 @@ const zhHant = {
|
|||||||
test: '測試',
|
test: '測試',
|
||||||
forgotPassword: '忘記密碼?',
|
forgotPassword: '忘記密碼?',
|
||||||
loading: '載入中...',
|
loading: '載入中...',
|
||||||
|
theme: '主題',
|
||||||
},
|
},
|
||||||
notFound: {
|
notFound: {
|
||||||
title: '頁面不存在',
|
title: '頁面不存在',
|
||||||
|
|||||||
Reference in New Issue
Block a user