@@ -50,6 +50,7 @@ | |||
"react-to-print": "^2.14.13", | |||
"react-toastify": "^9.1.3", | |||
"react-window": "^1.8.7", | |||
"react-intl": "^6.4.7", | |||
"redux": "^4.2.0", | |||
"simplebar": "^5.3.8", | |||
"simplebar-react": "^2.4.1", | |||
@@ -0,0 +1,47 @@ | |||
import React, {useState, useEffect, createContext} from 'react'; | |||
import { IntlProvider } from 'react-intl'; | |||
import enMessages from '../translations/en.json'; | |||
import cnMessages from '../translations/zh-CN.json'; | |||
import hkMessages from '../translations/zh-HK.json'; | |||
const LocaleContext = createContext(); | |||
export const I18nProvider = ({ children }) => { | |||
const systemMessages = { | |||
"en": enMessages, | |||
"zh": hkMessages, | |||
"zh-HK": hkMessages, | |||
"zh-CN": cnMessages | |||
}; | |||
const [locale, setLocale] = useState('en'); // Default locale, you can change this as per your requirement | |||
const [messages, setMessages] = useState(systemMessages[locale]); | |||
useEffect(() => { | |||
if(localStorage.getItem('locale') === null){ | |||
//no locale case | |||
localStorage.setItem('locale','en'); | |||
} | |||
else{ | |||
setLocale(localStorage.getItem('locale')); | |||
} | |||
}, []); | |||
useEffect(() => { | |||
// Load the messages for the selected locale | |||
const fetchMessages = async () => { | |||
setMessages(systemMessages[locale]); | |||
}; | |||
fetchMessages(); | |||
}, [locale]); | |||
return ( | |||
<LocaleContext.Provider value={{ locale, setLocale }} > | |||
<IntlProvider locale={locale} messages={messages}> | |||
{children} | |||
</IntlProvider> | |||
</LocaleContext.Provider> | |||
); | |||
} | |||
export default LocaleContext; |
@@ -16,6 +16,7 @@ import 'assets/third-party/apex-chart.css'; | |||
import App from './App'; | |||
import { store } from 'store'; | |||
import reportWebVitals from './reportWebVitals'; | |||
import {I18nProvider} from "./components/I18nProvider"; | |||
// ==============================|| MAIN - REACT DOM RENDER ||============================== // | |||
@@ -26,9 +27,11 @@ const root = createRoot(container); // createRoot(container!) if you use TypeScr | |||
root.render( | |||
<StrictMode> | |||
<ReduxProvider store={store}> | |||
<BrowserRouter basename="/"> | |||
<I18nProvider> | |||
<BrowserRouter basename="/"> | |||
<App /> | |||
</BrowserRouter> | |||
</BrowserRouter> | |||
</I18nProvider> | |||
</ReduxProvider> | |||
</StrictMode> | |||
); | |||
@@ -0,0 +1,142 @@ | |||
import {useContext, useRef, useState} from 'react'; | |||
import ListItem from '@mui/material/ListItem'; | |||
// material-ui | |||
import { useTheme } from '@mui/material/styles'; | |||
import { | |||
Box, | |||
ClickAwayListener, | |||
IconButton, | |||
List, | |||
ListItemButton, | |||
ListItemText, | |||
Paper, | |||
Popper, | |||
useMediaQuery | |||
} from '@mui/material'; | |||
import Transitions from 'components/@extended/Transitions'; | |||
import LanguageIcon from '@mui/icons-material/Language'; | |||
import {FormattedMessage} from "react-intl"; | |||
import * as React from "react"; | |||
import LocaleContext from "../../../../components/I18nProvider"; | |||
// ==============================|| HEADER CONTENT - NOTIFICATION ||============================== // | |||
const LocaleSelector = () => { | |||
const theme = useTheme(); | |||
const matchesXs = useMediaQuery(theme.breakpoints.down('md')); | |||
const { setLocale } = useContext(LocaleContext); | |||
const anchorRef = useRef(null); | |||
const [open, setOpen] = useState(false); | |||
const handleToggle = () => { | |||
setOpen((prevOpen) => !prevOpen); | |||
}; | |||
const handleClose = (event) => { | |||
if (anchorRef.current && anchorRef.current.contains(event.target)) { | |||
return; | |||
} | |||
setOpen(false); | |||
}; | |||
const iconBackColorOpen = 'grey.300'; | |||
const iconBackColor = 'grey.100'; | |||
return ( | |||
<Box sx={{ flexShrink: 0, ml: 0.75 }}> | |||
<IconButton | |||
disableRipple | |||
color="secondary" | |||
sx={{ color: 'text.primary', bgcolor: open ? iconBackColorOpen : iconBackColor }} | |||
aria-label="open profile" | |||
ref={anchorRef} | |||
aria-controls={open ? 'profile-grow' : undefined} | |||
aria-haspopup="true" | |||
onClick={handleToggle} | |||
> | |||
<LanguageIcon /> | |||
</IconButton> | |||
<Popper | |||
placement={matchesXs ? 'bottom' : 'bottom-end'} | |||
open={open} | |||
anchorEl={anchorRef.current} | |||
role={undefined} | |||
transition | |||
disablePortal | |||
popperOptions={{ | |||
modifiers: [ | |||
{ | |||
name: 'offset', | |||
options: { | |||
offset: [matchesXs ? -5 : 0, 9] | |||
} | |||
} | |||
] | |||
}} | |||
> | |||
{({ TransitionProps }) => ( | |||
<Transitions type="fade" in={open} {...TransitionProps}> | |||
<Paper | |||
sx={{ | |||
boxShadow: theme.customShadows.z1, | |||
width: '100%', | |||
minWidth: 285, | |||
maxWidth: 420, | |||
[theme.breakpoints.down('md')]: { | |||
maxWidth: 285 | |||
} | |||
}} | |||
> | |||
<ClickAwayListener onClickAway={handleClose}> | |||
<List | |||
component="nav" | |||
> | |||
<ListItem disablePadding> | |||
<ListItemButton | |||
onClick={() => { | |||
setLocale("en") | |||
localStorage.setItem('locale','en'); | |||
}} | |||
> | |||
<ListItemText | |||
primary= <FormattedMessage id="en"/> | |||
/> | |||
</ListItemButton> | |||
</ListItem> | |||
<ListItem disablePadding> | |||
<ListItemButton | |||
onClick={() => { | |||
setLocale("zh-HK") | |||
localStorage.setItem('locale','zh-HK'); | |||
}} | |||
> | |||
<ListItemText | |||
primary= <FormattedMessage id="zh-HK"/> | |||
/> | |||
</ListItemButton> | |||
</ListItem> | |||
<ListItem disablePadding> | |||
<ListItemButton | |||
onClick={() => { | |||
setLocale("zh-CN") | |||
localStorage.setItem('locale','zh-CN'); | |||
}} | |||
> | |||
<ListItemText | |||
primary= <FormattedMessage id="zh-CN"/> | |||
/> | |||
</ListItemButton> | |||
</ListItem> | |||
</List> | |||
</ClickAwayListener> | |||
</Paper> | |||
</Transitions> | |||
)} | |||
</Popper> | |||
</Box> | |||
); | |||
}; | |||
export default LocaleSelector; |
@@ -7,6 +7,7 @@ import { Button ,Box } from '@mui/material'; | |||
// project import | |||
// import Search from './Search'; | |||
import Profile from './Profile'; | |||
import LocaleSelector from "./LocaleSelector"; | |||
// import Notification from './Notification'; | |||
// import MobileSection from './MobileSection'; | |||
@@ -34,7 +35,7 @@ const HeaderContent = () => { | |||
> | |||
<GithubOutlined /> | |||
</IconButton> */} | |||
<LocaleSelector/> | |||
{/* <Notification /> */} | |||
<Profile /> | |||
{/* <MobileSection /> */} | |||
@@ -50,6 +50,7 @@ import * as UrlUtils from "utils/ApiPathConst" | |||
// import { MenuFoldOutlined,MenuOutlined } from '@ant-design/icons'; | |||
// import { AppBar } from '../../../../node_modules/@mui/material/index'; | |||
import { Link } from "react-router-dom"; | |||
import LocaleSelector from "./HeaderContent/LocaleSelector"; | |||
const drawerWidth = 240; | |||
@@ -383,6 +384,8 @@ function Header(props) { | |||
<ul id="navbar" width="100%" > | |||
{logoutContent} | |||
</ul> | |||
<LocaleSelector/> | |||
{/* <Profile /> */} | |||
</Stack> | |||
</Box> | |||
@@ -43,6 +43,7 @@ import { useDispatch } from "react-redux"; | |||
import { handleLogin } from "auth/index"; | |||
import useJwt from "../../../auth/jwt/useJwt"; | |||
import { handleLogoutFunction } from 'auth/index'; | |||
import {FormattedMessage} from "react-intl"; | |||
// ============================|| FIREBASE - LOGIN ||============================ // | |||
const AuthLoginCustom = () => { | |||
@@ -214,7 +215,11 @@ const AuthLoginCustom = () => { | |||
<Grid container spacing={3}> | |||
<Grid item xs={12}> | |||
<Stack spacing={1}> | |||
<InputLabel htmlFor="email-login"><Typography variant="h5">用戶登入名稱</Typography></InputLabel> | |||
<InputLabel htmlFor="email-login"> | |||
<Typography variant="h5"> | |||
<FormattedMessage id="userLoginName"/> | |||
</Typography> | |||
</InputLabel> | |||
<OutlinedInput | |||
id="username" | |||
name="username" | |||
@@ -0,0 +1,10 @@ | |||
{ | |||
"en": "English", | |||
"zh-HK": "Traditional Chinese", | |||
"zh-CN": "Simplified Chinese", | |||
"userLoginName": "User login name", | |||
"Dashboard": "Dashboard", | |||
"event": "Event" | |||
} |
@@ -0,0 +1,10 @@ | |||
{ | |||
"en": "英文", | |||
"zh-HK": "繁体中文", | |||
"zh-CN": "简体中文", | |||
"userLoginName": "用戶登入名稱", | |||
"Dashboard": "仪表板", | |||
"event": "活动" | |||
} |
@@ -0,0 +1,10 @@ | |||
{ | |||
"en": "英文", | |||
"zh-HK": "繁體中文", | |||
"zh-CN": "簡體中文", | |||
"userLoginName": "用戶登入名稱", | |||
"Dashboard": "儀表板", | |||
"event": "活動" | |||
} |