diff --git a/index.html b/index.html index e384824..7d03814 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ - 数字人直播 + diff --git a/package.json b/package.json index 9ae8779..e993d7f 100644 --- a/package.json +++ b/package.json @@ -26,10 +26,12 @@ "dayjs": "^1.11.11", "file-saver": "^2.0.5", "flv.js": "^1.6.2", + "i18next": "^24.2.1", "jszip": "^3.10.1", "qs": "^6.12.1", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-i18next": "^15.4.0", "react-player": "^2.16.0", "react-router-dom": "^6.28.0", "sass": "^1.81.0", diff --git a/src/components/button-batch.tsx b/src/components/button-batch.tsx index 2b4f9cc..3abce15 100644 --- a/src/components/button-batch.tsx +++ b/src/components/button-batch.tsx @@ -6,6 +6,7 @@ import {showErrorToast, showToast} from "@/components/message.ts"; import {BizError} from "@/service/types.ts"; import {IconWarningCircle} from "@/components/icons"; import {LoadingOutlined} from "@ant-design/icons"; +import {useTranslation} from "react-i18next"; type Props = { selected: any[], @@ -29,6 +30,7 @@ export default function ButtonBatch( selected, emptyMessage, successMessage, children, icon, title, confirmMessage, onProcess, onSuccess, className }: Props) { + const {t} = useTranslation() const [loading, setLoading] = useState(false) const {modal} = App.useApp() const onBatchProcess = async () => { @@ -54,7 +56,7 @@ export default function ButtonBatch( if(confirmMessage){ modal.confirm({ wrapClassName: 'root-modal-confirm', - title: title || '操作提示', + title: title || t('notice.title'), centered: true, icon: , content: confirmMessage, diff --git a/src/components/document.tsx b/src/components/document.tsx index ad677c2..1fb21a9 100644 --- a/src/components/document.tsx +++ b/src/components/document.tsx @@ -2,12 +2,12 @@ import React, {useEffect} from "react"; type DocumentTitleProps = { children?: string; - text?: string; + title?: string; } -export const DocumentTitle: React.FC = ({children, text}) => { +export const DocumentTitle: React.FC = ({children, title}) => { useEffect(() => { - if (text || children) { - document.title = text || children || ''; + if (title || children) { + document.title = title || children || ''; } }, []); return <> diff --git a/src/contexts/config/index.tsx b/src/contexts/config/index.tsx index 0597b07..67852dc 100644 --- a/src/contexts/config/index.tsx +++ b/src/contexts/config/index.tsx @@ -1,5 +1,5 @@ import useLocalStorage from "@/hooks/useLocalStorage"; -import {createContext} from "react"; +import React, {createContext} from "react"; const config: ConfigProps = { fontFamily: `'Public Sans', sans-serif`, @@ -21,7 +21,7 @@ const initialState: CustomizationProps = { const ConfigContext = createContext(initialState); export const ConfigProvider = ({children}: { children: React.ReactNode }) => { - const [config, setConfig] = useLocalStorage('app-payment-config', initialState); + const [config, setConfig] = useLocalStorage('app-video-admin-config', initialState); // 改变语言 const onChangeLocalization = (lang: I18n) => { setConfig({ diff --git a/src/i18n/config.ts b/src/i18n/config.ts new file mode 100644 index 0000000..0a0e201 --- /dev/null +++ b/src/i18n/config.ts @@ -0,0 +1,14 @@ +import i18next from 'i18next'; +import {initReactI18next} from 'react-i18next'; +import LangEN from './translations/en-US.json'; +import LangCN from './translations/zh-CN.json'; + +console.log('AppConfig',AppMode) +i18next.use(initReactI18next).init({ + debug: true, + fallbackLng: 'en-US', + resources: { + 'en-US': {translation:LangEN}, + 'zh-CN': {translation:LangCN}, + }, +}); \ No newline at end of file diff --git a/src/i18n/index.ts b/src/i18n/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/i18n/translations/en-US.json b/src/i18n/translations/en-US.json new file mode 100644 index 0000000..1c463d9 --- /dev/null +++ b/src/i18n/translations/en-US.json @@ -0,0 +1,10 @@ +{ + "AppTitle": "Digital Human Live", + "Hello": "Hello", + "login": { + "text": "Login" + }, + "notice": { + "title": "操作提示" + } +} \ No newline at end of file diff --git a/src/i18n/translations/zh-CN.json b/src/i18n/translations/zh-CN.json new file mode 100644 index 0000000..adc9c1c --- /dev/null +++ b/src/i18n/translations/zh-CN.json @@ -0,0 +1,10 @@ +{ + "AppTitle": "数字人直播", + "Hello": "你好", + "login": { + "text": "登录" + }, + "notice": { + "title": "Notice" + } +} \ No newline at end of file diff --git a/src/routes/index.tsx b/src/routes/index.tsx index bbb6c44..bc5be41 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -1,6 +1,6 @@ import {createBrowserRouter, RouterProvider,} from "react-router-dom"; -import {Suspense,} from "react"; -import {ConfigProvider,App} from "antd"; +import {Suspense, useEffect,} from "react"; +import {ConfigProvider, App} from "antd"; import zhCN from 'antd/locale/zh_CN'; // for date-picker i18n import dayjs from "dayjs"; @@ -8,10 +8,11 @@ import 'dayjs/locale/zh-cn'; import ErrorBoundary from "./error.tsx"; import Loader from "@/components/loader.tsx"; import routes from "@/routes/routes.tsx"; +import {DocumentTitle} from "@/components/document.tsx"; +import useConfig from "@/hooks/useConfig.ts"; +import {useTranslation} from "react-i18next"; -dayjs.locale('zh-cn'); - const router = createBrowserRouter([ ...routes, {path: '*', element: } @@ -28,16 +29,23 @@ const router = createBrowserRouter([ // future={{v7_startTransition: true,v7_relativeSplatPath: true}} const AppRouter = () => { + const {t} = useTranslation(); + const {i18n} = useConfig(); + useEffect(() => { + if (i18n && i18n == 'zh-CN') { + dayjs.locale('zh-cn'); + } + }, [i18n]) + return ( + }> diff --git a/src/routes/layout/dashboard-layout.tsx b/src/routes/layout/dashboard-layout.tsx index 69979fb..eed4917 100644 --- a/src/routes/layout/dashboard-layout.tsx +++ b/src/routes/layout/dashboard-layout.tsx @@ -12,6 +12,7 @@ import useAuth from "@/hooks/useAuth.ts"; import {hidePhone} from "@/util/strings.ts"; import {defaultCache} from "@/hooks/useCache.ts"; import {IconVideo} from "@/components/icons"; +import {useTranslation} from "react-i18next"; type LayoutProps = { @@ -19,6 +20,7 @@ type LayoutProps = { } const NavigationUserContainer = () => { + const {t } = useTranslation() const {logout, user} = useAuth() const navigate = useNavigate() const handleLogout = ()=>{ @@ -41,7 +43,7 @@ const NavigationUserContainer = () => { className={`flex items-center rounded-3xl ${user ? 'bg-[#e3eeff]' : 'bg-primary-blue'} p-1 pr-2 cursor-pointer rounded`}> {user ? {hidePhone(user.nickname)} : ( - 登录 + {t('login.text')} )} ) return (
diff --git a/src/types/config.d.ts b/src/types/config.d.ts index 6616ce4..d4f9cb0 100644 --- a/src/types/config.d.ts +++ b/src/types/config.d.ts @@ -1,4 +1,4 @@ -type I18n = 'en-US' | 'zh-CN' | 'zh-HK'; +type I18n = 'en-US' | 'zh-CN' | 'zh-HK' | string; type ConfigProps = { fontFamily: string; diff --git a/yarn.lock b/yarn.lock index 08195e2..95b3e33 100644 --- a/yarn.lock +++ b/yarn.lock @@ -201,7 +201,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.25.9" -"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.25.7": +"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.25.0", "@babel/runtime@^7.25.7": version "7.26.0" resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1" integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== @@ -1870,6 +1870,20 @@ hasown@^2.0.0, hasown@^2.0.2: dependencies: function-bind "^1.1.2" +html-parse-stringify@^3.0.1: + version "3.0.1" + resolved "https://registry.npmmirror.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2" + integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg== + dependencies: + void-elements "3.1.0" + +i18next@^24.2.1: + version "24.2.1" + resolved "https://registry.npmmirror.com/i18next/-/i18next-24.2.1.tgz#91e8f11fc9bd7042ec0bd36bed2dd0457aaa35fa" + integrity sha512-Q2wC1TjWcSikn1VAJg13UGIjc+okpFxQTxjVAymOnSA3RpttBQNMPf2ovcgoFVsV4QNxTfNZMAxorXZXsk4fBA== + dependencies: + "@babel/runtime" "^7.23.2" + ignore@^5.2.0, ignore@^5.3.1: version "5.3.2" resolved "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" @@ -2905,6 +2919,14 @@ react-fast-compare@^3.0.1, react-fast-compare@^3.2.2: resolved "https://registry.npmmirror.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz#929a97a532304ce9fee4bcae44234f1ce2c21d49" integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ== +react-i18next@^15.4.0: + version "15.4.0" + resolved "https://registry.npmmirror.com/react-i18next/-/react-i18next-15.4.0.tgz#87c755fb6d7a567eec134e4759b022a0baacb19e" + integrity sha512-Py6UkX3zV08RTvL6ZANRoBh9sL/ne6rQq79XlkHEdd82cZr2H9usbWpUNVadJntIZP2pu3M2rL1CN+5rQYfYFw== + dependencies: + "@babel/runtime" "^7.25.0" + html-parse-stringify "^3.0.1" + react-is@^16.13.1: version "16.13.1" resolved "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -3444,6 +3466,11 @@ vite@^5.2.0: optionalDependencies: fsevents "~2.3.3" +void-elements@3.1.0: + version "3.1.0" + resolved "https://registry.npmmirror.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09" + integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w== + webworkify-webpack@^2.1.5: version "2.1.5" resolved "https://registry.npmmirror.com/webworkify-webpack/-/webworkify-webpack-2.1.5.tgz#bf4336624c0626cbe85cf1ffde157f7aa90b1d1c"