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:
andupetcu
2025-09-20 15:43:51 +03:00
parent dd5e1102eb
commit a0969e88df
21 changed files with 695 additions and 123 deletions

View 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>
</>
)
}

View File

@@ -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>