refactor(language): move language constants to common module and improve language handling

This commit is contained in:
2025-11-14 17:42:10 +08:00
parent ef8076d87f
commit 4356813820
8 changed files with 124 additions and 88 deletions

View File

@@ -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<LanguageSwitcherProps> = ({ textColor = '#fff'
const { t, i18n } = useTranslation();
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const { changeLanguage } = useLanguageSetting();
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
@@ -21,13 +23,23 @@ const LanguageSwitcher: React.FC<LanguageSwitcherProps> = ({ 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<LanguageSwitcherProps> = ({ textColor = '#fff'
>
{getCurrentLanguageLabel()}
</Button>
<Menu
anchorEl={anchorEl}
open={open}
onClose={handleClose}
>
<MenuItem
onClick={() => handleLanguageChange(LanguageAbbreviation.Zh)}
selected={i18n.language === LanguageAbbreviation.Zh}
>
</MenuItem>
<MenuItem
onClick={() => handleLanguageChange(LanguageAbbreviation.En)}
selected={i18n.language === LanguageAbbreviation.En}
>
English
</MenuItem>
<Menu anchorEl={anchorEl} open={open} onClose={handleClose}>
{supportedLangs.map((lang) => (
<MenuItem
key={lang}
onClick={() => handleLanguageChange(lang)}
selected={i18n.language === lang}
>
{LanguageAbbreviationMap[lang]}
</MenuItem>
))}
</Menu>
</div>
);

View File

@@ -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<string, string> = {
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<string, string> = {
[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<string, string> = {
English: 'en',
Chinese: 'zh',
'Traditional Chinese': 'zh-TRADITIONAL',
Russian: 'ru',
Indonesia: 'id',
Spanish: 'es',
Vietnamese: 'vi',

View File

@@ -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 模型设置
*/

View File

@@ -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 || '获取用户信息失败');

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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 = {

View File

@@ -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 }));