Implement multilingual support: Integrate i18next for language translation across components, update configuration for multilingual descriptions and keywords, and enhance user interface elements with dynamic language switching. Add new dependencies for i18next and related libraries in package.json and package-lock.json.
This commit is contained in:
248
src/components/LanguageSwitcher.js
Normal file
248
src/components/LanguageSwitcher.js
Normal file
@@ -0,0 +1,248 @@
|
||||
import React, { Component } from 'react';
|
||||
import Box from '@mui/material/Box';
|
||||
import Button from '@mui/material/Button';
|
||||
import Menu from '@mui/material/Menu';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { withI18n } from '../i18n/withTranslation.js';
|
||||
|
||||
class LanguageSwitcher extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
anchorEl: null,
|
||||
loadedFlags: {}
|
||||
};
|
||||
}
|
||||
|
||||
handleClick = (event) => {
|
||||
this.setState({ anchorEl: event.currentTarget });
|
||||
};
|
||||
|
||||
handleClose = () => {
|
||||
this.setState({ anchorEl: null });
|
||||
};
|
||||
|
||||
handleLanguageChange = (language) => {
|
||||
const { languageContext } = this.props;
|
||||
if (languageContext) {
|
||||
languageContext.changeLanguage(language);
|
||||
}
|
||||
this.handleClose();
|
||||
};
|
||||
|
||||
// Lazy load flag components
|
||||
loadFlagComponent = async (lang) => {
|
||||
if (this.state.loadedFlags[lang]) {
|
||||
return this.state.loadedFlags[lang];
|
||||
}
|
||||
|
||||
try {
|
||||
const flagMap = {
|
||||
'de': () => import('country-flag-icons/react/3x2').then(m => m.DE),
|
||||
'en': () => import('country-flag-icons/react/3x2').then(m => m.US),
|
||||
'es': () => import('country-flag-icons/react/3x2').then(m => m.ES),
|
||||
'fr': () => import('country-flag-icons/react/3x2').then(m => m.FR),
|
||||
'it': () => import('country-flag-icons/react/3x2').then(m => m.IT),
|
||||
'pl': () => import('country-flag-icons/react/3x2').then(m => m.PL),
|
||||
'hu': () => import('country-flag-icons/react/3x2').then(m => m.HU),
|
||||
'sr': () => import('country-flag-icons/react/3x2').then(m => m.RS),
|
||||
'bg': () => import('country-flag-icons/react/3x2').then(m => m.BG),
|
||||
'ru': () => import('country-flag-icons/react/3x2').then(m => m.RU),
|
||||
'uk': () => import('country-flag-icons/react/3x2').then(m => m.UA),
|
||||
'sk': () => import('country-flag-icons/react/3x2').then(m => m.SK),
|
||||
'cs': () => import('country-flag-icons/react/3x2').then(m => m.CZ),
|
||||
'ro': () => import('country-flag-icons/react/3x2').then(m => m.RO)
|
||||
};
|
||||
|
||||
const flagLoader = flagMap[lang];
|
||||
if (flagLoader) {
|
||||
const FlagComponent = await flagLoader();
|
||||
this.setState(prevState => ({
|
||||
loadedFlags: {
|
||||
...prevState.loadedFlags,
|
||||
[lang]: FlagComponent
|
||||
}
|
||||
}));
|
||||
return FlagComponent;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Failed to load flag for language: ${lang}`, error);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
getLanguageFlag = (lang) => {
|
||||
const FlagComponent = this.state.loadedFlags[lang];
|
||||
|
||||
if (FlagComponent) {
|
||||
return (
|
||||
<FlagComponent
|
||||
style={{
|
||||
width: '20px',
|
||||
height: '14px',
|
||||
borderRadius: '2px',
|
||||
border: '1px solid #ddd'
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Loading placeholder or fallback
|
||||
return (
|
||||
<Box
|
||||
component="span"
|
||||
sx={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
minWidth: '20px',
|
||||
height: '14px',
|
||||
backgroundColor: '#f5f5f5',
|
||||
color: '#666',
|
||||
fontSize: '8px',
|
||||
fontWeight: 'bold',
|
||||
borderRadius: '2px',
|
||||
fontFamily: 'monospace',
|
||||
border: '1px solid #ddd'
|
||||
}}
|
||||
>
|
||||
{this.getLanguageLabel(lang)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
// Load flags when menu opens
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
const { anchorEl } = this.state;
|
||||
const { languageContext } = this.props;
|
||||
|
||||
if (anchorEl && !prevState.anchorEl && languageContext) {
|
||||
// Menu just opened, lazy load all flags
|
||||
languageContext.availableLanguages.forEach(lang => {
|
||||
if (!this.state.loadedFlags[lang]) {
|
||||
this.loadFlagComponent(lang);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getLanguageLabel = (lang) => {
|
||||
const labels = {
|
||||
'de': 'DE',
|
||||
'en': 'US',
|
||||
'es': 'ES',
|
||||
'fr': 'FR',
|
||||
'it': 'IT',
|
||||
'pl': 'PL',
|
||||
'hu': 'HU',
|
||||
'sr': 'RS',
|
||||
'bg': 'BG',
|
||||
'ru': 'RU',
|
||||
'uk': 'UA',
|
||||
'sk': 'SK',
|
||||
'cs': 'CZ',
|
||||
'ro': 'RO'
|
||||
};
|
||||
return labels[lang] || lang.toUpperCase();
|
||||
};
|
||||
|
||||
getLanguageName = (lang) => {
|
||||
const names = {
|
||||
'de': 'Deutsch',
|
||||
'en': 'English',
|
||||
'es': 'Español',
|
||||
'fr': 'Français',
|
||||
'it': 'Italiano',
|
||||
'pl': 'Polski',
|
||||
'hu': 'Magyar',
|
||||
'sr': 'Српски',
|
||||
'bg': 'Български',
|
||||
'ru': 'Русский',
|
||||
'uk': 'Українська',
|
||||
'sk': 'Slovenčina',
|
||||
'cs': 'Čeština',
|
||||
'ro': 'Română'
|
||||
};
|
||||
return names[lang] || lang;
|
||||
};
|
||||
|
||||
render() {
|
||||
const { languageContext } = this.props;
|
||||
const { anchorEl } = this.state;
|
||||
|
||||
if (!languageContext) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { currentLanguage, availableLanguages } = languageContext;
|
||||
const open = Boolean(anchorEl);
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Button
|
||||
aria-controls={open ? 'language-menu' : undefined}
|
||||
aria-haspopup="true"
|
||||
aria-expanded={open ? 'true' : undefined}
|
||||
onClick={this.handleClick}
|
||||
color="inherit"
|
||||
size="small"
|
||||
sx={{
|
||||
my: 1,
|
||||
mx: 0.5,
|
||||
minWidth: 'auto',
|
||||
textTransform: 'none',
|
||||
fontSize: '0.875rem'
|
||||
}}
|
||||
>
|
||||
{this.getLanguageLabel(currentLanguage)}
|
||||
</Button>
|
||||
<Menu
|
||||
id="language-menu"
|
||||
anchorEl={anchorEl}
|
||||
open={open}
|
||||
onClose={this.handleClose}
|
||||
disableScrollLock={true}
|
||||
MenuListProps={{
|
||||
'aria-labelledby': 'language-button',
|
||||
}}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
>
|
||||
{availableLanguages.map((language) => (
|
||||
<MenuItem
|
||||
key={language}
|
||||
onClick={() => this.handleLanguageChange(language)}
|
||||
selected={language === currentLanguage}
|
||||
sx={{
|
||||
minWidth: 160,
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
gap: 2
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
{this.getLanguageFlag(language)}
|
||||
<Typography variant="body2">
|
||||
{this.getLanguageName(language)}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ ml: 'auto' }}>
|
||||
{this.getLanguageLabel(language)}
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withI18n()(LanguageSwitcher);
|
||||
@@ -22,6 +22,7 @@ import GoogleLoginButton from './GoogleLoginButton.js';
|
||||
import CartSyncDialog from './CartSyncDialog.js';
|
||||
import { localAndArchiveServer, mergeCarts } from '../utils/cartUtils.js';
|
||||
import config from '../config.js';
|
||||
import { withI18n } from '../i18n/withTranslation.js';
|
||||
|
||||
// Lazy load GoogleAuthProvider
|
||||
const GoogleAuthProvider = lazy(() => import('../providers/GoogleAuthProvider.js'));
|
||||
@@ -510,7 +511,7 @@ export class LoginComponent extends Component {
|
||||
color={isAdmin ? 'secondary' : 'inherit'}
|
||||
sx={{ my: 1, mx: 1.5 }}
|
||||
>
|
||||
Profil
|
||||
{this.props.t ? this.props.t('auth.profile') : 'Profil'}
|
||||
</Button>
|
||||
<Menu
|
||||
disableScrollLock={true}
|
||||
@@ -526,14 +527,28 @@ export class LoginComponent extends Component {
|
||||
horizontal: 'right',
|
||||
}}
|
||||
>
|
||||
<MenuItem component={Link} to="/profile" onClick={this.handleUserMenuClose}>Profil</MenuItem>
|
||||
<MenuItem component={Link} to="/profile#cart" onClick={this.handleUserMenuClose} sx={{ pl: 4 }}>Bestellabschluss</MenuItem>
|
||||
<MenuItem component={Link} to="/profile#orders" onClick={this.handleUserMenuClose} sx={{ pl: 4 }}>Bestellungen</MenuItem>
|
||||
<MenuItem component={Link} to="/profile#settings" onClick={this.handleUserMenuClose} sx={{ pl: 4 }}>Einstellungen</MenuItem>
|
||||
<MenuItem component={Link} to="/profile" onClick={this.handleUserMenuClose}>
|
||||
{this.props.t ? this.props.t('auth.menu.profile') : 'Profil'}
|
||||
</MenuItem>
|
||||
<MenuItem component={Link} to="/profile#cart" onClick={this.handleUserMenuClose} sx={{ pl: 4 }}>
|
||||
{this.props.t ? this.props.t('auth.menu.checkout') : 'Bestellabschluss'}
|
||||
</MenuItem>
|
||||
<MenuItem component={Link} to="/profile#orders" onClick={this.handleUserMenuClose} sx={{ pl: 4 }}>
|
||||
{this.props.t ? this.props.t('auth.menu.orders') : 'Bestellungen'}
|
||||
</MenuItem>
|
||||
<MenuItem component={Link} to="/profile#settings" onClick={this.handleUserMenuClose} sx={{ pl: 4 }}>
|
||||
{this.props.t ? this.props.t('auth.menu.settings') : 'Einstellungen'}
|
||||
</MenuItem>
|
||||
<Divider />
|
||||
{isAdmin ? <MenuItem component={Link} to="/admin" onClick={this.handleUserMenuClose}>Admin Dashboard</MenuItem> : null}
|
||||
{isAdmin ? <MenuItem component={Link} to="/admin/users" onClick={this.handleUserMenuClose}>Admin Users</MenuItem> : null}
|
||||
<MenuItem onClick={this.handleLogout}>Abmelden</MenuItem>
|
||||
{isAdmin ? <MenuItem component={Link} to="/admin" onClick={this.handleUserMenuClose}>
|
||||
{this.props.t ? this.props.t('auth.menu.adminDashboard') : 'Admin Dashboard'}
|
||||
</MenuItem> : null}
|
||||
{isAdmin ? <MenuItem component={Link} to="/admin/users" onClick={this.handleUserMenuClose}>
|
||||
{this.props.t ? this.props.t('auth.menu.adminUsers') : 'Admin Users'}
|
||||
</MenuItem> : null}
|
||||
<MenuItem onClick={this.handleLogout}>
|
||||
{this.props.t ? this.props.t('auth.logout') : 'Abmelden'}
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</>
|
||||
) : (
|
||||
@@ -543,7 +558,7 @@ export class LoginComponent extends Component {
|
||||
onClick={this.handleOpen}
|
||||
sx={{ my: 1, mx: 1.5 }}
|
||||
>
|
||||
Login
|
||||
{this.props.t ? this.props.t('auth.login') : 'Login'}
|
||||
</Button>
|
||||
)
|
||||
)}
|
||||
@@ -558,7 +573,10 @@ export class LoginComponent extends Component {
|
||||
<DialogTitle sx={{ bgcolor: 'white', pb: 0 }}>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Typography variant="h6" color="#2e7d32" fontWeight="bold">
|
||||
{tabValue === 0 ? 'Anmelden' : 'Registrieren'}
|
||||
{tabValue === 0 ?
|
||||
(this.props.t ? this.props.t('auth.login') : 'Anmelden') :
|
||||
(this.props.t ? this.props.t('auth.register') : 'Registrieren')
|
||||
}
|
||||
</Typography>
|
||||
<IconButton edge="end" onClick={this.handleClose} aria-label="close">
|
||||
<CloseIcon />
|
||||
@@ -578,14 +596,14 @@ export class LoginComponent extends Component {
|
||||
textColor="inherit"
|
||||
>
|
||||
<Tab
|
||||
label="ANMELDEN"
|
||||
label={this.props.t ? this.props.t('auth.login').toUpperCase() : "ANMELDEN"}
|
||||
sx={{
|
||||
color: tabValue === 0 ? '#2e7d32' : 'inherit',
|
||||
fontWeight: 'bold'
|
||||
}}
|
||||
/>
|
||||
<Tab
|
||||
label="REGISTRIEREN"
|
||||
label={this.props.t ? this.props.t('auth.register').toUpperCase() : "REGISTRIEREN"}
|
||||
sx={{
|
||||
color: tabValue === 1 ? '#2e7d32' : 'inherit',
|
||||
fontWeight: 'bold'
|
||||
@@ -598,7 +616,14 @@ export class LoginComponent extends Component {
|
||||
<Box sx={{ width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', mb: 2 }}>
|
||||
{!privacyConfirmed && (
|
||||
<Typography variant="caption" sx={{ mb: 1, textAlign: 'center' }}>
|
||||
Mit dem Click auf "Mit Google anmelden" akzeptiere ich die <Link to="/datenschutz" style={{ color: '#4285F4' }}>Datenschutzbestimmungen</Link>
|
||||
{this.props.t ?
|
||||
<>
|
||||
{this.props.t('auth.privacyAccept')} <Link to="/datenschutz" style={{ color: '#4285F4' }}>{this.props.t('auth.privacyPolicy')}</Link>
|
||||
</> :
|
||||
<>
|
||||
Mit dem Click auf "Mit Google anmelden" akzeptiere ich die <Link to="/datenschutz" style={{ color: '#4285F4' }}>Datenschutzbestimmungen</Link>
|
||||
</>
|
||||
}
|
||||
</Typography>
|
||||
)}
|
||||
{!showGoogleAuth && (
|
||||
@@ -611,7 +636,7 @@ export class LoginComponent extends Component {
|
||||
}}
|
||||
sx={{ width: '100%', backgroundColor: '#4285F4', color: 'white' }}
|
||||
>
|
||||
Mit Google anmelden
|
||||
{this.props.t ? this.props.t('auth.loginWithGoogle') : 'Mit Google anmelden'}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@@ -643,7 +668,9 @@ export class LoginComponent extends Component {
|
||||
{/* OR Divider */}
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', my: 2 }}>
|
||||
<Box sx={{ flex: 1, height: '1px', backgroundColor: '#e0e0e0' }} />
|
||||
<Typography variant="body2" sx={{ px: 2, color: '#757575' }}>ODER</Typography>
|
||||
<Typography variant="body2" sx={{ px: 2, color: '#757575' }}>
|
||||
{this.props.t ? this.props.t('auth.or') : 'ODER'}
|
||||
</Typography>
|
||||
<Box sx={{ flex: 1, height: '1px', backgroundColor: '#e0e0e0' }} />
|
||||
</Box>
|
||||
|
||||
@@ -654,7 +681,7 @@ export class LoginComponent extends Component {
|
||||
<Box sx={{ py: 1 }}>
|
||||
<TextField
|
||||
margin="dense"
|
||||
label="E-Mail"
|
||||
label={this.props.t ? this.props.t('auth.email') : 'E-Mail'}
|
||||
type="email"
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
@@ -665,7 +692,7 @@ export class LoginComponent extends Component {
|
||||
|
||||
<TextField
|
||||
margin="dense"
|
||||
label="Passwort"
|
||||
label={this.props.t ? this.props.t('auth.password') : 'Passwort'}
|
||||
type="password"
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
@@ -687,7 +714,7 @@ export class LoginComponent extends Component {
|
||||
'&:hover': { backgroundColor: 'transparent', textDecoration: 'underline' }
|
||||
}}
|
||||
>
|
||||
Passwort vergessen?
|
||||
{this.props.t ? this.props.t('auth.forgotPassword') : 'Passwort vergessen?'}
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
@@ -740,4 +767,4 @@ export class LoginComponent extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(LoginComponent);
|
||||
export default withRouter(withI18n()(LoginComponent));
|
||||
@@ -10,10 +10,12 @@ import ChevronRight from "@mui/icons-material/ChevronRight";
|
||||
import { Link } from "react-router-dom";
|
||||
import SharedCarousel from "./SharedCarousel.js";
|
||||
import { getCombinedAnimatedBorderStyles } from "../utils/animatedBorderStyles.js";
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const MainPageLayout = () => {
|
||||
const location = useLocation();
|
||||
const currentPath = location.pathname;
|
||||
const { t } = useTranslation();
|
||||
|
||||
// Determine which page we're on
|
||||
const isHome = currentPath === "/";
|
||||
@@ -24,18 +26,18 @@ const MainPageLayout = () => {
|
||||
const getNavigationConfig = () => {
|
||||
if (isHome) {
|
||||
return {
|
||||
leftNav: { text: "Aktionen", link: "/aktionen" },
|
||||
rightNav: { text: "Filiale", link: "/filiale" }
|
||||
leftNav: { text: t('navigation.aktionen'), link: "/aktionen" },
|
||||
rightNav: { text: t('navigation.filiale'), link: "/filiale" }
|
||||
};
|
||||
} else if (isAktionen) {
|
||||
return {
|
||||
leftNav: { text: "Filiale", link: "/filiale" },
|
||||
rightNav: { text: "Home", link: "/" }
|
||||
leftNav: { text: t('navigation.filiale'), link: "/filiale" },
|
||||
rightNav: { text: t('navigation.home'), link: "/" }
|
||||
};
|
||||
} else if (isFiliale) {
|
||||
return {
|
||||
leftNav: { text: "Home", link: "/" },
|
||||
rightNav: { text: "Aktionen", link: "/aktionen" }
|
||||
leftNav: { text: t('navigation.home'), link: "/" },
|
||||
rightNav: { text: t('navigation.aktionen'), link: "/aktionen" }
|
||||
};
|
||||
}
|
||||
return { leftNav: null, rightNav: null };
|
||||
@@ -51,13 +53,13 @@ const MainPageLayout = () => {
|
||||
const allContentBoxes = {
|
||||
home: [
|
||||
{
|
||||
title: "Seeds",
|
||||
title: t('sections.seeds'),
|
||||
image: "/assets/images/seeds.jpg",
|
||||
bgcolor: "#e1f0d3",
|
||||
link: "/Kategorie/Seeds"
|
||||
},
|
||||
{
|
||||
title: "Stecklinge",
|
||||
title: t('sections.stecklinge'),
|
||||
image: "/assets/images/cutlings.jpg",
|
||||
bgcolor: "#e8f5d6",
|
||||
link: "/Kategorie/Stecklinge"
|
||||
@@ -65,13 +67,13 @@ const MainPageLayout = () => {
|
||||
],
|
||||
aktionen: [
|
||||
{
|
||||
title: "Ölpresse ausleihen",
|
||||
title: t('sections.oilPress'),
|
||||
image: "/assets/images/presse.jpg",
|
||||
bgcolor: "#e1f0d3",
|
||||
link: "/presseverleih"
|
||||
},
|
||||
{
|
||||
title: "THC Test",
|
||||
title: t('sections.thcTest'),
|
||||
image: "/assets/images/purpl.jpg",
|
||||
bgcolor: "#e8f5d6",
|
||||
link: "/thc-test"
|
||||
@@ -79,13 +81,13 @@ const MainPageLayout = () => {
|
||||
],
|
||||
filiale: [
|
||||
{
|
||||
title: "Trachenberger Straße 14",
|
||||
title: t('sections.address1'),
|
||||
image: "/assets/images/filiale1.jpg",
|
||||
bgcolor: "#e1f0d3",
|
||||
link: "/filiale"
|
||||
},
|
||||
{
|
||||
title: "01129 Dresden",
|
||||
title: t('sections.address2'),
|
||||
image: "/assets/images/filiale2.jpg",
|
||||
bgcolor: "#e8f5d6",
|
||||
link: "/filiale"
|
||||
@@ -103,6 +105,13 @@ const MainPageLayout = () => {
|
||||
|
||||
const navConfig = getNavigationConfig();
|
||||
|
||||
// Navigation text mapping for translation
|
||||
const navTexts = [
|
||||
{ key: 'aktionen', text: t('navigation.aktionen'), link: '/aktionen' },
|
||||
{ key: 'filiale', text: t('navigation.filiale'), link: '/filiale' },
|
||||
{ key: 'home', text: t('navigation.home'), link: '/' }
|
||||
];
|
||||
|
||||
return (
|
||||
<Container maxWidth="lg" sx={{ py: 2 }}>
|
||||
<style>{getCombinedAnimatedBorderStyles(['seeds', 'cutlings'])}</style>
|
||||
@@ -126,13 +135,13 @@ const MainPageLayout = () => {
|
||||
position: "relative",
|
||||
mr: 2
|
||||
}}>
|
||||
{["Aktionen", "Filiale", "Home"].map((text, index) => {
|
||||
const isActive = navConfig.leftNav && navConfig.leftNav.text === text;
|
||||
const link = text === "Aktionen" ? "/aktionen" : text === "Filiale" ? "/filiale" : "/";
|
||||
{navTexts.map((navItem, index) => {
|
||||
const isActive = navConfig.leftNav && navConfig.leftNav.text === navItem.text;
|
||||
const link = navItem.link;
|
||||
|
||||
return (
|
||||
<Box
|
||||
key={text}
|
||||
key={navItem.key}
|
||||
component={Link}
|
||||
to={link}
|
||||
sx={{
|
||||
@@ -161,7 +170,7 @@ const MainPageLayout = () => {
|
||||
whiteSpace: "nowrap"
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
{navItem.text}
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
@@ -216,13 +225,13 @@ const MainPageLayout = () => {
|
||||
position: "relative",
|
||||
ml: 2
|
||||
}}>
|
||||
{["Aktionen", "Filiale", "Home"].map((text, index) => {
|
||||
const isActive = navConfig.rightNav && navConfig.rightNav.text === text;
|
||||
const link = text === "Aktionen" ? "/aktionen" : text === "Filiale" ? "/filiale" : "/";
|
||||
{navTexts.map((navItem, index) => {
|
||||
const isActive = navConfig.rightNav && navConfig.rightNav.text === navItem.text;
|
||||
const link = navItem.link;
|
||||
|
||||
return (
|
||||
<Box
|
||||
key={text}
|
||||
key={navItem.key}
|
||||
component={Link}
|
||||
to={link}
|
||||
sx={{
|
||||
@@ -250,7 +259,7 @@ const MainPageLayout = () => {
|
||||
whiteSpace: "nowrap"
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
{navItem.text}
|
||||
</Typography>
|
||||
<ChevronRight sx={{ fontSize: "2rem", ml: 1 }} />
|
||||
</Box>
|
||||
|
||||
@@ -10,7 +10,9 @@ import CloseIcon from '@mui/icons-material/Close';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import LoginComponent from '../LoginComponent.js';
|
||||
import CartDropdown from '../CartDropdown.js';
|
||||
import LanguageSwitcher from '../LanguageSwitcher.js';
|
||||
import { isUserLoggedIn } from '../LoginComponent.js';
|
||||
import { withI18n } from '../../i18n/withTranslation.js';
|
||||
|
||||
function getBadgeNumber() {
|
||||
let count = 0;
|
||||
@@ -116,14 +118,14 @@ class ButtonGroup extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { socket, navigate } = this.props;
|
||||
const { socket, navigate, t } = this.props;
|
||||
const { isCartOpen } = this.state;
|
||||
const cartItems = Array.isArray(window.cart) ? window.cart : [];
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex', gap: { xs: 0.5, sm: 1 } }}>
|
||||
|
||||
|
||||
<LanguageSwitcher />
|
||||
<LoginComponent socket={socket} />
|
||||
|
||||
<IconButton
|
||||
@@ -164,7 +166,7 @@ class ButtonGroup extends Component {
|
||||
>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
<Typography variant="h6">Warenkorb</Typography>
|
||||
<Typography variant="h6">{t ? t('cart.title') : 'Warenkorb'}</Typography>
|
||||
</Box>
|
||||
<Divider sx={{ mb: 2 }} />
|
||||
|
||||
@@ -189,10 +191,11 @@ class ButtonGroup extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapper for ButtonGroup to provide navigate function
|
||||
// Wrapper for ButtonGroup to provide navigate function and translations
|
||||
const ButtonGroupWithRouter = (props) => {
|
||||
const navigate = useNavigate();
|
||||
return <ButtonGroup {...props} navigate={navigate} />;
|
||||
const ButtonGroupWithTranslation = withI18n()(ButtonGroup);
|
||||
return <ButtonGroupWithTranslation {...props} navigate={navigate} />;
|
||||
};
|
||||
|
||||
export default ButtonGroupWithRouter;
|
||||
@@ -8,6 +8,7 @@ import { Link } from "react-router-dom";
|
||||
import HomeIcon from "@mui/icons-material/Home";
|
||||
import MenuIcon from "@mui/icons-material/Menu";
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
import { withI18n } from "../../i18n/withTranslation.js";
|
||||
|
||||
class CategoryList extends Component {
|
||||
findCategoryById = (category, targetId) => {
|
||||
@@ -410,7 +411,7 @@ class CategoryList extends Component {
|
||||
zIndex: 2,
|
||||
}}
|
||||
>
|
||||
Startseite
|
||||
{this.props.t ? this.props.t('navigation.home') : 'Startseite'}
|
||||
</Box>
|
||||
{/* Thin text (positioned on top) */}
|
||||
<Box
|
||||
@@ -424,7 +425,7 @@ class CategoryList extends Component {
|
||||
zIndex: 1,
|
||||
}}
|
||||
>
|
||||
Startseite
|
||||
{this.props.t ? this.props.t('navigation.home') : 'Startseite'}
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
@@ -595,7 +596,10 @@ class CategoryList extends Component {
|
||||
onClick={this.handleMobileMenuToggle}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label={mobileMenuOpen ? "Kategorien schließen" : "Kategorien öffnen"}
|
||||
aria-label={this.props.t ?
|
||||
(mobileMenuOpen ? this.props.t('navigation.categories') + ' schließen' : this.props.t('navigation.categories') + ' öffnen') :
|
||||
(mobileMenuOpen ? "Kategorien schließen" : "Kategorien öffnen")
|
||||
}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
@@ -607,7 +611,7 @@ class CategoryList extends Component {
|
||||
fontWeight: "bold",
|
||||
textShadow: "0 1px 2px rgba(0,0,0,0.3)"
|
||||
}}>
|
||||
Kategorien
|
||||
{this.props.t ? this.props.t('navigation.categories') : 'Kategorien'}
|
||||
</Typography>
|
||||
<Box sx={{ display: "flex", alignItems: "center" }}>
|
||||
{mobileMenuOpen ? <CloseIcon /> : <MenuIcon />}
|
||||
@@ -628,4 +632,4 @@ class CategoryList extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
export default CategoryList;
|
||||
export default withI18n()(CategoryList);
|
||||
|
||||
Reference in New Issue
Block a user