diff --git a/src/components/LanguageSwitcher.tsx b/src/components/LanguageSwitcher.tsx index f6e7d4d..c8062db 100644 --- a/src/components/LanguageSwitcher.tsx +++ b/src/components/LanguageSwitcher.tsx @@ -1,8 +1,9 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { Button, Menu, MenuItem } from '@mui/material'; -import { LanguageAbbreviation } from '../locales'; -import type { LanguageAbbreviationType } from '../locales'; +import { useLanguageSetting } from '@/hooks/setting-hooks'; +import logger from '@/utils/logger'; +import { LanguageAbbreviation, LanguageAbbreviationMap } from '@/constants/common'; interface LanguageSwitcherProps { textColor?: string; @@ -12,6 +13,7 @@ const LanguageSwitcher: React.FC = ({ textColor = '#fff' const { t, i18n } = useTranslation(); const [anchorEl, setAnchorEl] = React.useState(null); const open = Boolean(anchorEl); + const { changeLanguage } = useLanguageSetting(); const handleClick = (event: React.MouseEvent) => { setAnchorEl(event.currentTarget); @@ -21,13 +23,23 @@ const LanguageSwitcher: React.FC = ({ textColor = '#fff' setAnchorEl(null); }; - const handleLanguageChange = (language: LanguageAbbreviationType) => { - i18n.changeLanguage(language); - handleClose(); + const handleLanguageChange = async (language: LanguageAbbreviation) => { + try { + await changeLanguage(language); + i18n.changeLanguage(language); + handleClose(); + } catch (error) { + logger.error('更新语言设置失败:', error); + } }; + const supportedLangs: LanguageAbbreviation[] = [LanguageAbbreviation.Zh, LanguageAbbreviation.En]; + const getCurrentLanguageLabel = () => { - return i18n.language === LanguageAbbreviation.Zh ? '中文' : 'English'; + const curLang = supportedLangs.includes(i18n.language as LanguageAbbreviation) + ? (i18n.language as LanguageAbbreviation) + : LanguageAbbreviation.En; + return LanguageAbbreviationMap[curLang] || 'English'; }; return ( @@ -42,23 +54,16 @@ const LanguageSwitcher: React.FC = ({ textColor = '#fff' > {getCurrentLanguageLabel()} - - handleLanguageChange(LanguageAbbreviation.Zh)} - selected={i18n.language === LanguageAbbreviation.Zh} - > - 中文 - - handleLanguageChange(LanguageAbbreviation.En)} - selected={i18n.language === LanguageAbbreviation.En} - > - English - + + {supportedLangs.map((lang) => ( + handleLanguageChange(lang)} + selected={i18n.language === lang} + > + {LanguageAbbreviationMap[lang]} + + ))} ); diff --git a/src/constants/common.ts b/src/constants/common.ts index 840c746..a59f35c 100644 --- a/src/constants/common.ts +++ b/src/constants/common.ts @@ -42,60 +42,76 @@ export const fileIconMap = { export const LanguageList = [ 'English', 'Chinese', - 'Traditional Chinese', - 'Indonesia', - 'Spanish', - 'Vietnamese', - 'Japanese', - 'Portuguese BR', - 'German', - 'French', + // 'Traditional Chinese', + // 'Indonesia', + // 'Spanish', + // 'Vietnamese', + // 'Japanese', + // 'Portuguese BR', + // 'German', + // 'French', ]; -export const LanguageMap = { + +export const LanguageMap: Record = { English: 'English', Chinese: '简体中文', - 'Traditional Chinese': '繁體中文', - Indonesia: 'Indonesia', - Spanish: 'Español', - Vietnamese: 'Tiếng việt', - Japanese: '日本語', - 'Portuguese BR': 'Português BR', - German: 'German', - French: 'Français', + // 'Traditional Chinese': '繁體中文', + // Indonesia: 'Indonesia', + // Spanish: 'Español', + // Vietnamese: 'Tiếng việt', + // Japanese: '日本語', + // 'Portuguese BR': 'Português BR', + // German: 'German', + // French: 'Français', }; -export const LANUGAGE_ABBREVIATION_TYPE = Object.freeze({ - En: 'en', - Zh: 'zh', - ZhTraditional: 'zh-TRADITIONAL', - Id: 'id', - Ja: 'ja', - Es: 'es', - Vi: 'vi', - PtBr: 'pt-BR', - De: 'de', - Fr: 'fr', -} as const) - -export type LanguageAbbreviation = (typeof LANUGAGE_ABBREVIATION_TYPE)[keyof typeof LANUGAGE_ABBREVIATION_TYPE] +export enum LanguageAbbreviation { + En = 'en', + Zh = 'zh', + ZhTraditional = 'zh-TRADITIONAL', + Ru = 'ru', + Id = 'id', + Ja = 'ja', + Es = 'es', + Vi = 'vi', + PtBr = 'pt-BR', + De = 'de', + Fr = 'fr', +} export const LanguageAbbreviationMap = { - [LANUGAGE_ABBREVIATION_TYPE.En]: 'English', - [LANUGAGE_ABBREVIATION_TYPE.Zh]: '简体中文', - [LANUGAGE_ABBREVIATION_TYPE.ZhTraditional]: '繁體中文', - [LANUGAGE_ABBREVIATION_TYPE.Id]: 'Indonesia', - [LANUGAGE_ABBREVIATION_TYPE.Es]: 'Español', - [LANUGAGE_ABBREVIATION_TYPE.Vi]: 'Tiếng việt', - [LANUGAGE_ABBREVIATION_TYPE.Ja]: '日本語', - [LANUGAGE_ABBREVIATION_TYPE.PtBr]: 'Português BR', - [LANUGAGE_ABBREVIATION_TYPE.De]: 'Deutsch', - [LANUGAGE_ABBREVIATION_TYPE.Fr]: 'Français', + [LanguageAbbreviation.En]: 'English', + [LanguageAbbreviation.Zh]: '简体中文', + [LanguageAbbreviation.ZhTraditional]: '繁體中文', + [LanguageAbbreviation.Ru]: 'Русский', + [LanguageAbbreviation.Id]: 'Indonesia', + [LanguageAbbreviation.Es]: 'Español', + [LanguageAbbreviation.Vi]: 'Tiếng việt', + [LanguageAbbreviation.Ja]: '日本語', + [LanguageAbbreviation.PtBr]: 'Português BR', + [LanguageAbbreviation.De]: 'Deutsch', + [LanguageAbbreviation.Fr]: 'Français', }; -export const LanguageTranslationMap = { +export const LanguageAbbreviationKeysMap: Record = { + [LanguageAbbreviation.En]: 'English', + [LanguageAbbreviation.Zh]: 'Chinese', + [LanguageAbbreviation.ZhTraditional]: 'Traditional Chinese', + [LanguageAbbreviation.Ru]: 'Russian', + [LanguageAbbreviation.Id]: 'Indonesia', + [LanguageAbbreviation.Es]: 'Spanish', + [LanguageAbbreviation.Vi]: 'Vietnamese', + [LanguageAbbreviation.Ja]: 'Japanese', + [LanguageAbbreviation.PtBr]: 'Portuguese BR', + [LanguageAbbreviation.De]: 'German', + [LanguageAbbreviation.Fr]: 'French', +}; + +export const LanguageTranslationMap: Record = { English: 'en', Chinese: 'zh', 'Traditional Chinese': 'zh-TRADITIONAL', + Russian: 'ru', Indonesia: 'id', Spanish: 'es', Vietnamese: 'vi', diff --git a/src/hooks/setting-hooks.ts b/src/hooks/setting-hooks.ts index f95b425..6231ed9 100644 --- a/src/hooks/setting-hooks.ts +++ b/src/hooks/setting-hooks.ts @@ -9,6 +9,7 @@ import type { LLMFactory } from "@/constants/llm"; import type { IMcpServer, IMcpServerListResponse } from "@/interfaces/database/mcp"; import type { IImportMcpServersRequestBody, ITestMcpRequestBody, ICreateMcpServerRequestBody } from "@/interfaces/request/mcp"; import type { IPaginationBody } from "@/interfaces/request/base"; +import { LanguageAbbreviation, LanguageAbbreviationKeysMap, LanguageTranslationMap } from "@/constants/common"; /** * 个人中心设置 @@ -49,6 +50,28 @@ export function useProfileSetting() { }; } +export function useLanguageSetting() { + const { userInfo, updateUserInfo } = useProfileSetting(); + const [language, setLanguage] = useState(userInfo?.language || 'English'); + + const changeLanguage = async (language: LanguageAbbreviation) => { + try { + // LanguageAbbreviation to language + const translatedLanguage = LanguageAbbreviationKeysMap[language]; + await updateUserInfo({ language: translatedLanguage }); + setLanguage(translatedLanguage); + } catch (error) { + logger.error('更新语言设置失败:', error); + throw error; + } + } + + return { + language, + changeLanguage, + } +} + /** * LLM 模型设置 */ diff --git a/src/hooks/useUserData.ts b/src/hooks/useUserData.ts index 8302e70..879d17b 100644 --- a/src/hooks/useUserData.ts +++ b/src/hooks/useUserData.ts @@ -2,6 +2,8 @@ import { useEffect, useCallback } from 'react'; import { useUserStore } from '@/stores/userStore'; import userService from '@/services/user_service'; import type { IUserInfo } from '@/interfaces/database/user-setting'; +import i18n from '@/locales'; +import { LanguageTranslationMap } from '@/constants/common'; /** * 用户数据管理Hook @@ -31,6 +33,9 @@ export const useUserData = () => { if (response.data.code === 0) { const userInfoData: IUserInfo | null = response.data.data || null; setUserInfo(userInfoData); + i18n.changeLanguage( + LanguageTranslationMap[userInfoData?.language as keyof typeof LanguageTranslationMap], + ); return userInfoData; } else { throw new Error(response.data.message || '获取用户信息失败'); @@ -95,15 +100,15 @@ export const useUserData = () => { // 检查是否有失败的请求 const errors: string[] = []; - + if (userInfoResult.status === 'rejected') { errors.push(`用户信息: ${userInfoResult.reason.message}`); } - + if (tenantInfoResult.status === 'rejected') { errors.push(`租户信息: ${tenantInfoResult.reason.message}`); } - + if (tenantListResult.status === 'rejected') { errors.push(`租户列表: ${tenantListResult.reason.message}`); } @@ -144,7 +149,7 @@ export const useUserData = () => { tenantList, isLoading, error, - + // 方法 initializeUserData, refreshUserData, diff --git a/src/locales/index.ts b/src/locales/index.ts index 63cd076..171c6f3 100644 --- a/src/locales/index.ts +++ b/src/locales/index.ts @@ -2,19 +2,13 @@ import i18n from 'i18next'; import LanguageDetector from 'i18next-browser-languagedetector'; import { initReactI18next } from 'react-i18next'; import dayjs from 'dayjs'; +import {LanguageAbbreviation} from '@/constants/common'; import 'dayjs/locale/zh-cn'; import 'dayjs/locale/en'; import en from './en'; import zh from './zh'; -export const LanguageAbbreviation = Object.freeze({ - En: 'en', - Zh: 'zh', -} as const) - -export type LanguageAbbreviationType = (typeof LanguageAbbreviation)[keyof typeof LanguageAbbreviation] - const resources = { [LanguageAbbreviation.En]: en, [LanguageAbbreviation.Zh]: zh, diff --git a/src/pages/knowledge/components/DocumentListComponent.tsx b/src/pages/knowledge/components/DocumentListComponent.tsx index d5a0604..1751cc0 100644 --- a/src/pages/knowledge/components/DocumentListComponent.tsx +++ b/src/pages/knowledge/components/DocumentListComponent.tsx @@ -53,9 +53,9 @@ import { zhCN, enUS } from '@mui/x-data-grid/locales'; import type { IKnowledgeFile } from '@/interfaces/database/knowledge'; import type { IDocumentInfoFilter } from '@/interfaces/database/document'; import { RunningStatus } from '@/constants/knowledge'; -import { LanguageAbbreviation } from '@/locales'; import dayjs from 'dayjs'; import logger from '@/utils/logger'; +import { LanguageAbbreviation } from '@/constants/common'; interface DocumentListComponentProps { diff --git a/src/pages/knowledge/overview.tsx b/src/pages/knowledge/overview.tsx index f2fee30..ecdc034 100644 --- a/src/pages/knowledge/overview.tsx +++ b/src/pages/knowledge/overview.tsx @@ -9,9 +9,10 @@ import { useTranslation } from 'react-i18next'; import { useNavigate, useParams } from 'react-router-dom'; import { useKnowledgeOverview, useKnowledgeDetail } from '@/hooks/knowledge-hooks'; import logger from '@/utils/logger'; -import i18n, { LanguageAbbreviation } from '@/locales'; +import i18n from '@/locales'; import { enUS, zhCN } from '@mui/x-data-grid/locales'; import KnowledgeBreadcrumbs from './components/KnowledgeBreadcrumbs'; +import { LanguageAbbreviation } from '@/constants/common'; const PROCESSING_TYPES = { diff --git a/src/pages/setting/components/ProfileForm.tsx b/src/pages/setting/components/ProfileForm.tsx index 091c238..1cf332c 100644 --- a/src/pages/setting/components/ProfileForm.tsx +++ b/src/pages/setting/components/ProfileForm.tsx @@ -20,18 +20,10 @@ import { useProfileSetting } from '@/hooks/setting-hooks'; import { useMessage } from '@/hooks/useSnackbar'; import type { IUserInfo } from '@/interfaces/database/user-setting'; import { TimezoneList } from '@/constants/setting'; +import { LanguageList, LanguageMap } from '@/constants/common'; // 语言选项 -const languageOptions = [ - { value: 'Chinese', label: '简体中文' }, - { value: 'English', label: 'English' }, - { value: 'Spanish', label: 'Español' }, - { value: 'French', label: 'Français' }, - { value: 'German', label: 'Deutsch' }, - { value: 'Japanese', label: '日本語' }, - { value: 'Korean', label: '한국어' }, - { value: 'Vietnamese', label: 'Tiếng Việt' } -]; +const languageOptions = LanguageList.map(x => ({ value: x, label: LanguageMap[x as keyof typeof LanguageMap] })); // 时区选项 const timezoneOptions = TimezoneList.map(x => ({ value: x, label: x }));