Implement complete multi-language support with Romanian/English
- Added next-intl for internationalization with Romanian as default locale - Restructured app directory with [locale] routing (/ro, /en) - Created comprehensive translation files for both languages - Fixed Next.js 15 async params compatibility in layout components - Updated all components to use proper i18n hooks and translations - Configured middleware for locale routing and fallbacks - Fixed FloatingChat component translation array handling - Restored complete home page with internationalized content - Fixed Material-UI Slide component prop error (mountOnExit → unmountOnExit) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
88
components/layout/language-switcher.tsx
Normal file
88
components/layout/language-switcher.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { useRouter, usePathname } from 'next/navigation'
|
||||
import { useLocale, useTranslations } from 'next-intl'
|
||||
import {
|
||||
IconButton,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Box,
|
||||
Typography,
|
||||
ListItemIcon,
|
||||
} from '@mui/material'
|
||||
import { Language, Check } from '@mui/icons-material'
|
||||
|
||||
const languages = [
|
||||
{ code: 'ro', name: 'Română', flag: '🇷🇴' },
|
||||
{ code: 'en', name: 'English', flag: '🇺🇸' },
|
||||
]
|
||||
|
||||
export function LanguageSwitcher() {
|
||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
const locale = useLocale()
|
||||
const t = useTranslations('navigation')
|
||||
|
||||
const handleOpen = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setAnchorEl(event.currentTarget)
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null)
|
||||
}
|
||||
|
||||
const handleLanguageChange = (newLocale: string) => {
|
||||
// Remove current locale from pathname and add new one
|
||||
const pathWithoutLocale = pathname.replace(`/${locale}`, '') || '/'
|
||||
const newPath = `/${newLocale}${pathWithoutLocale === '/' ? '' : pathWithoutLocale}`
|
||||
|
||||
router.push(newPath)
|
||||
handleClose()
|
||||
}
|
||||
|
||||
const currentLanguage = languages.find(lang => lang.code === locale)
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton
|
||||
onClick={handleOpen}
|
||||
sx={{ color: 'white' }}
|
||||
aria-label={t('language')}
|
||||
>
|
||||
<Language />
|
||||
</IconButton>
|
||||
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={handleClose}
|
||||
transformOrigin={{ horizontal: 'right', vertical: 'top' }}
|
||||
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
|
||||
>
|
||||
{languages.map((language) => (
|
||||
<MenuItem
|
||||
key={language.code}
|
||||
onClick={() => handleLanguageChange(language.code)}
|
||||
selected={language.code === locale}
|
||||
>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, minWidth: 120 }}>
|
||||
<Typography component="span" sx={{ fontSize: '1.2rem' }}>
|
||||
{language.flag}
|
||||
</Typography>
|
||||
<Typography sx={{ flexGrow: 1 }}>
|
||||
{language.name}
|
||||
</Typography>
|
||||
{language.code === locale && (
|
||||
<ListItemIcon sx={{ minWidth: 'auto' }}>
|
||||
<Check fontSize="small" />
|
||||
</ListItemIcon>
|
||||
)}
|
||||
</Box>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -32,15 +32,8 @@ import {
|
||||
Logout,
|
||||
} from '@mui/icons-material'
|
||||
import { useRouter } from 'next/navigation'
|
||||
|
||||
const pages = [
|
||||
{ name: 'Acasă', path: '/', icon: <Home /> },
|
||||
{ name: 'Biblia', path: '/bible', icon: <MenuBook /> },
|
||||
{ name: 'Rugăciuni', path: '/prayers', icon: <Prayer /> },
|
||||
{ name: 'Căutare', path: '/search', icon: <Search /> },
|
||||
]
|
||||
|
||||
const settings = ['Profil', 'Setări', 'Deconectare']
|
||||
import { useTranslations, useLocale } from 'next-intl'
|
||||
import { LanguageSwitcher } from './language-switcher'
|
||||
|
||||
export function Navigation() {
|
||||
const [anchorElNav, setAnchorElNav] = useState<null | HTMLElement>(null)
|
||||
@@ -49,6 +42,21 @@ export function Navigation() {
|
||||
const router = useRouter()
|
||||
const theme = useTheme()
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('md'))
|
||||
const t = useTranslations('navigation')
|
||||
const locale = useLocale()
|
||||
|
||||
const pages = [
|
||||
{ name: t('home'), path: '/', icon: <Home /> },
|
||||
{ name: t('bible'), path: '/bible', icon: <MenuBook /> },
|
||||
{ name: t('prayers'), path: '/prayers', icon: <Prayer /> },
|
||||
{ name: t('search'), path: '/search', icon: <Search /> },
|
||||
]
|
||||
|
||||
const settings = [
|
||||
{ name: t('profile'), icon: <AccountCircle /> },
|
||||
{ name: t('settings'), icon: <Settings /> },
|
||||
{ name: t('logout'), icon: <Logout /> },
|
||||
]
|
||||
|
||||
const handleOpenNavMenu = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setAnchorElNav(event.currentTarget)
|
||||
@@ -67,7 +75,8 @@ export function Navigation() {
|
||||
}
|
||||
|
||||
const handleNavigate = (path: string) => {
|
||||
router.push(path)
|
||||
const localizedPath = `/${locale}${path === '/' ? '' : path}`
|
||||
router.push(localizedPath)
|
||||
handleCloseNavMenu()
|
||||
setDrawerOpen(false)
|
||||
}
|
||||
@@ -104,7 +113,7 @@ export function Navigation() {
|
||||
variant="h6"
|
||||
noWrap
|
||||
component="a"
|
||||
href="/"
|
||||
href={`/${locale}`}
|
||||
sx={{
|
||||
mr: 2,
|
||||
display: { xs: 'none', md: 'flex' },
|
||||
@@ -138,7 +147,7 @@ export function Navigation() {
|
||||
variant="h5"
|
||||
noWrap
|
||||
component="a"
|
||||
href="/"
|
||||
href={`/${locale}`}
|
||||
sx={{
|
||||
mr: 2,
|
||||
display: { xs: 'flex', md: 'none' },
|
||||
@@ -177,9 +186,12 @@ export function Navigation() {
|
||||
))}
|
||||
</Box>
|
||||
|
||||
{/* Language Switcher */}
|
||||
<LanguageSwitcher />
|
||||
|
||||
{/* User Menu */}
|
||||
<Box sx={{ flexGrow: 0 }}>
|
||||
<Tooltip title="Deschide setări">
|
||||
<Tooltip title={t('settings')}>
|
||||
<IconButton onClick={handleOpenUserMenu} sx={{ p: 0 }}>
|
||||
<Avatar sx={{ bgcolor: 'secondary.main' }}>
|
||||
<AccountCircle />
|
||||
@@ -202,24 +214,14 @@ export function Navigation() {
|
||||
open={Boolean(anchorElUser)}
|
||||
onClose={handleCloseUserMenu}
|
||||
>
|
||||
<MenuItem onClick={handleCloseUserMenu}>
|
||||
<ListItemIcon>
|
||||
<AccountCircle fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<Typography textAlign="center">Profil</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={handleCloseUserMenu}>
|
||||
<ListItemIcon>
|
||||
<Settings fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<Typography textAlign="center">Setări</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={handleCloseUserMenu}>
|
||||
<ListItemIcon>
|
||||
<Logout fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<Typography textAlign="center">Deconectare</Typography>
|
||||
</MenuItem>
|
||||
{settings.map((setting) => (
|
||||
<MenuItem key={setting.name} onClick={handleCloseUserMenu}>
|
||||
<ListItemIcon>
|
||||
{setting.icon}
|
||||
</ListItemIcon>
|
||||
<Typography textAlign="center">{setting.name}</Typography>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
</Box>
|
||||
</Toolbar>
|
||||
|
||||
Reference in New Issue
Block a user