Implementing I18N

This commit is contained in:
2025-04-27 19:47:03 +02:00
parent f71dccf166
commit d38bb7d986
10 changed files with 591 additions and 76 deletions

View File

@@ -8,8 +8,10 @@ import * as React from "react";
import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box";
import Stack from "@mui/material/Stack";
import { useTranslation } from "@refinedev/core";
export const Login = () => {
const { translate } = useTranslation();
const [searchParams] = useSearchParams();
if (searchParams.get("oauth") == "success") {
const redirect_to = localStorage.getItem("redirect_after_login")
@@ -24,12 +26,12 @@ export const Login = () => {
rememberMe={false}
providers={[{
name: "google",
label: "Sign in with Google",
label: translate("pages.login.oauth.google"),
icon: (<GoogleIcon style={{ fontSize: 24, }} />),
},
{
name: "discord",
label: "Sign in with Discord",
label: translate("pages.login.oauth.discord"),
icon: (<DiscordIcon style={{ fontSize: 24, }} />),
},
]}
@@ -50,7 +52,7 @@ export const Login = () => {
underline="none"
to="/auth/forgot-password"
>
Forgot password?
{translate("pages.login.buttons.forgotPassword")}
</MuiLink>
</Stack>
}
@@ -69,7 +71,7 @@ export const Login = () => {
component="span"
fontSize="12px"
>
Dont have an account?
{translate("pages.login.buttons.noAccount")}
</Typography>
<MuiLink
ml="4px"
@@ -81,7 +83,7 @@ export const Login = () => {
to="/auth/register"
fontWeight="bold"
>
Sign up
{translate("pages.login.signup")}
</MuiLink>
</Box>
}

View File

@@ -1,5 +1,51 @@
import { AuthPage } from "@refinedev/mui";
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import MuiLink from "@mui/material/Link";
import * as React from "react";
import { useTranslation } from "@refinedev/core";
import { Link } from "react-router";
export const Register = () => {
return <AuthPage type="register" />;
const { translate } = useTranslation();
const loginLink = (
<Box
display="flex"
justifyContent="flex-end"
alignItems="center"
sx={{
mt: "24px",
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
<Typography variant="body2" component="span" fontSize="12px">
{translate(
"pages.register.buttons.haveAccount",
translate(
"pages.login.buttons.haveAccount",
"Have an account?",
),
)}
</Typography>
<MuiLink
ml="4px"
variant="body2"
color="primary"
component={Link}
underline="none"
to="/auth/login"
fontSize="12px"
fontWeight="bold"
>
{translate(
"pages.register.signin",
translate("pages.login.signin", "Sign in"),
)}
</MuiLink>
</Box>
)
return <AuthPage type="register" loginLink={loginLink}/>;
};

View File

@@ -0,0 +1,41 @@
import Autocomplete from "@mui/material/Autocomplete";
import TextField from "@mui/material/TextField";
import Box from "@mui/material/Box";
import React from "react";
import { useTranslation } from "react-i18next";
import { useTranslation as useRefineTranslation } from "@refinedev/core";
const I18nPicker = () => {
const { i18n } = useTranslation();
const { getLocale, changeLocale } = useRefineTranslation();
const currentLocale = getLocale();
return (
<Autocomplete
value={currentLocale}
options={i18n.languages}
disableClearable={true}
renderInput={(params) => {
return <TextField {...params} label={ "Language" } variant="outlined" />
}}
renderOption={(props, option) => {
const { key, ...optionProps } = props;
return (
<Box
key={key}
component="li"
sx={{ '& > img': { mr: 2, flexShrink: 0 } }}
{...optionProps}
>
{ option }
</Box>
);
}}
onChange={(event, value) => {
changeLocale(value);
}}
/>
)
}
export default I18nPicker;

View File

@@ -18,17 +18,11 @@ import { FirmContext } from "../../contexts/FirmContext";
import { Logout } from "../auth/Logout";
import { IUser } from "../../interfaces";
import MuiLink from "@mui/material/Link";
import { useTranslation } from "react-i18next";
import { useTranslation as useTranslationR } from "@refinedev/core";
import { useSetLocale } from "@refinedev/core";
import I18nPicker from "./I18nPicker";
export const Header: React.FC<RefineThemedLayoutV2HeaderProps> = ({
sticky = true,
}) => {
const { i18n } = useTranslation();
const { getLocale, changeLocale } = useTranslationR();
const currentLocale = getLocale();
const collapsed = false;
const { mode, setMode } = useContext(ColorModeContext);
const { currentFirm } = useContext(FirmContext);
@@ -44,16 +38,6 @@ export const Header: React.FC<RefineThemedLayoutV2HeaderProps> = ({
setAnchorEl(null);
};
const [anchorIn, setAnchorIn] = React.useState<null | HTMLElement>(null);
const openI18nMenu = Boolean(anchorEl);
const handleOpenI18nMenu = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorIn(event.currentTarget);
}
const handleCloseI18nMenu = () => {
setAnchorIn(null);
};
return (
<AppBar position={sticky ? "sticky" : "relative"}>
<Toolbar>
@@ -149,43 +133,7 @@ export const Header: React.FC<RefineThemedLayoutV2HeaderProps> = ({
{!user && (
<Link to="/auth/login"><Button>Login</Button></Link>
)}
<Button
id="i18n-button"
aria-controls={openI18nMenu ? 'i18n-menu' : undefined}
aria-haspopup="true"
aria-expanded={openI18nMenu ? 'true' : undefined}
onClick={handleOpenI18nMenu}>
<Typography
sx={{
display: {
xs: "none",
sm: "inline-block",
},
}}
variant="subtitle2"
>
{currentLocale}
</Typography>&nbsp;
<Avatar src={`/images/flags/${currentLocale}.svg`} alt={currentLocale}/>
</Button>
<Menu
id="i18n-menu"
open={openI18nMenu}
anchorEl={anchorIn}
onClose={handleCloseI18nMenu}
>
{[...(i18n.languages || [])].sort().map((lang: string) => (
<MenuItem
key={lang}
onClick={() => changeLocale(lang)}
>
<span style={{ marginRight: 8 }}>
<Avatar src={`/images/flags/${lang}.svg`} alt={lang}/>
</span>
{lang === "en" ? "English" : "Français"}
</MenuItem>
))}
</Menu>
<I18nPicker />
</Stack>
</Stack>
</Toolbar>

View File

@@ -8,13 +8,13 @@ i18n
.use(detector)
.use(initReactI18next)
.init({
supportedLngs: ["en", "fr"],
supportedLngs: ["EN", "FR"],
backend: {
loadPath: "/locales/{{lng}}/{{ns}}.json", // locale files path
},
ns: ["common"],
defaultNS: "common",
fallbackLng: ["en", "fr"],
fallbackLng: ["EN", "FR"],
});
export default i18n;