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

55
app/[locale]/layout.tsx Normal file
View File

@@ -0,0 +1,55 @@
import '../globals.css'
import type { Metadata } from 'next'
import { NextIntlClientProvider } from 'next-intl'
import { getMessages } from 'next-intl/server'
import { notFound } from 'next/navigation'
import { MuiThemeProvider } from '@/components/providers/theme-provider'
import { Navigation } from '@/components/layout/navigation'
import FloatingChat from '@/components/chat/floating-chat'
export const metadata: Metadata = {
title: 'Ghid Biblic - Biblical Guide',
description: 'A comprehensive Bible study application with AI chat capabilities',
}
export async function generateStaticParams() {
return [
{ locale: 'ro' },
{ locale: 'en' }
]
}
interface LocaleLayoutProps {
children: React.ReactNode
params: Promise<{ locale: string }>
}
const locales = ['ro', 'en']
export default async function LocaleLayout({
children,
params
}: LocaleLayoutProps) {
const { locale } = await params
// Validate locale
if (!locales.includes(locale)) {
notFound()
}
const messages = await getMessages()
return (
<html lang={locale}>
<body>
<NextIntlClientProvider messages={messages} locale={locale}>
<MuiThemeProvider>
<Navigation />
{children}
<FloatingChat />
</MuiThemeProvider>
</NextIntlClientProvider>
</body>
</html>
)
}

View File

@@ -18,38 +18,40 @@ import {
AutoStories,
Favorite,
} from '@mui/icons-material'
import { Navigation } from '@/components/layout/navigation'
import { useRouter } from 'next/navigation'
import { useTranslations, useLocale } from 'next-intl'
export default function Home() {
const theme = useTheme()
const router = useRouter()
const t = useTranslations('home')
const locale = useLocale()
const features = [
{
title: 'Citește Biblia',
description: 'Explorează Scriptura cu o interfață modernă și ușor de folosit',
title: t('features.bible.title'),
description: t('features.bible.description'),
icon: <MenuBook sx={{ fontSize: 40, color: 'primary.main' }} />,
path: '/bible',
color: theme.palette.primary.main,
},
{
title: 'Chat cu AI',
description: 'Pune întrebări despre Scriptură și primește răspunsuri clare',
title: t('features.chat.title'),
description: t('features.chat.description'),
icon: <Chat sx={{ fontSize: 40, color: 'secondary.main' }} />,
path: '/chat',
color: theme.palette.secondary.main,
},
{
title: 'Rugăciuni',
description: 'Partajează rugăciuni și roagă-te împreună cu comunitatea',
title: t('features.prayers.title'),
description: t('features.prayers.description'),
icon: <Prayer sx={{ fontSize: 40, color: 'success.main' }} />,
path: '/prayers',
color: theme.palette.success.main,
},
{
title: 'Căutare',
description: 'Caută versete și pasaje din întreaga Scriptură',
title: t('features.search.title'),
description: t('features.search.description'),
icon: <Search sx={{ fontSize: 40, color: 'info.main' }} />,
path: '/search',
color: theme.palette.info.main,
@@ -58,8 +60,6 @@ export default function Home() {
return (
<Box>
<Navigation />
{/* Hero Section */}
<Box
sx={{
@@ -73,15 +73,13 @@ export default function Home() {
<Grid container spacing={4} alignItems="center">
<Grid item xs={12} md={8}>
<Typography variant="h2" component="h1" gutterBottom>
Ghid Biblic
{t('hero.title')}
</Typography>
<Typography variant="h5" component="h2" sx={{ mb: 3, opacity: 0.9 }}>
Explorează Scriptura cu ajutorul inteligenței artificiale
{t('hero.subtitle')}
</Typography>
<Typography variant="body1" sx={{ mb: 4, opacity: 0.8, maxWidth: 600 }}>
O platformă modernă pentru studiul Bibliei, cu chat AI inteligent,
căutare avansată și o comunitate de rugăciune care te sprijină în
călătoria ta spirituală.
{t('hero.description')}
</Typography>
<Box sx={{ display: 'flex', gap: 2, flexWrap: 'wrap' }}>
<Button
@@ -92,9 +90,9 @@ export default function Home() {
'&:hover': { bgcolor: 'secondary.dark' },
}}
startIcon={<AutoStories />}
onClick={() => router.push('/bible')}
onClick={() => router.push(`/${locale}/bible`)}
>
Începe citești
{t('hero.cta.readBible')}
</Button>
<Button
variant="outlined"
@@ -108,9 +106,9 @@ export default function Home() {
},
}}
startIcon={<Chat />}
onClick={() => router.push('/chat')}
onClick={() => router.push(`/${locale}/chat`)}
>
Întreabă AI
{t('hero.cta.askAI')}
</Button>
</Box>
</Grid>
@@ -126,7 +124,7 @@ export default function Home() {
{/* Features Section */}
<Container maxWidth="lg" sx={{ mb: 8 }}>
<Typography variant="h3" component="h2" textAlign="center" sx={{ mb: 2 }}>
Descoperă funcționalitățile
{t('features.title')}
</Typography>
<Typography
variant="body1"
@@ -134,7 +132,7 @@ export default function Home() {
color="text.secondary"
sx={{ mb: 6, maxWidth: 600, mx: 'auto' }}
>
Totul de ce ai nevoie pentru o experiență completă de studiu biblic
{t('features.subtitle')}
</Typography>
<Grid container spacing={4}>
@@ -152,7 +150,7 @@ export default function Home() {
boxShadow: 4,
},
}}
onClick={() => router.push(feature.path)}
onClick={() => router.push(`/${locale}${feature.path}`)}
>
<CardContent sx={{ flexGrow: 1, textAlign: 'center', p: 3 }}>
<Box sx={{ mb: 2 }}>
@@ -179,19 +177,19 @@ export default function Home() {
<Typography variant="h3" color="primary.main" gutterBottom>
66
</Typography>
<Typography variant="h6">Cărți biblice</Typography>
<Typography variant="h6">{t('stats.books')}</Typography>
</Grid>
<Grid item xs={12} sm={4}>
<Typography variant="h3" color="secondary.main" gutterBottom>
31,000+
</Typography>
<Typography variant="h6">Versete</Typography>
<Typography variant="h6">{t('stats.verses')}</Typography>
</Grid>
<Grid item xs={12} sm={4}>
<Typography variant="h3" color="success.main" gutterBottom>
24/7
</Typography>
<Typography variant="h6">Chat AI disponibil</Typography>
<Typography variant="h6">{t('stats.aiAvailable')}</Typography>
</Grid>
</Grid>
</Container>
@@ -200,19 +198,19 @@ export default function Home() {
{/* CTA Section */}
<Container maxWidth="sm" sx={{ textAlign: 'center', mb: 8 }}>
<Typography variant="h4" component="h2" gutterBottom>
Începe călătoria ta spirituală
{t('cta.title')}
</Typography>
<Typography variant="body1" color="text.secondary" sx={{ mb: 4 }}>
Alătură-te comunității noastre și descoperă înțelepciunea Scripturii
{t('cta.description')}
</Typography>
<Button
variant="contained"
size="large"
startIcon={<Favorite />}
sx={{ mr: 2 }}
onClick={() => router.push('/bible')}
onClick={() => router.push(`/${locale}/bible`)}
>
Începe acum
{t('cta.startNow')}
</Button>
</Container>
</Box>

View File

@@ -4,6 +4,7 @@ import { searchBibleHybrid, BibleVerse } from '@/lib/vector-search'
const chatRequestSchema = z.object({
message: z.string().min(1),
locale: z.string().optional().default('ro'),
history: z.array(z.object({
id: z.string(),
role: z.enum(['user', 'assistant']),
@@ -15,11 +16,10 @@ const chatRequestSchema = z.object({
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { message, history } = chatRequestSchema.parse(body)
const { message, locale, history } = chatRequestSchema.parse(body)
// For now, return a mock response
// TODO: Integrate with Azure OpenAI when ready
const response = await generateBiblicalResponse(message, history)
// Generate response using Azure OpenAI with vector search
const response = await generateBiblicalResponse(message, locale, history)
return NextResponse.json({
success: true,
@@ -49,10 +49,10 @@ export async function POST(request: NextRequest) {
}
}
async function generateBiblicalResponse(message: string, history: any[]): Promise<string> {
async function generateBiblicalResponse(message: string, locale: string, history: any[]): Promise<string> {
try {
// Search for relevant Bible verses using vector search
const relevantVerses = await searchBibleHybrid(message, 5)
// Search for relevant Bible verses using vector search with language filtering
const relevantVerses = await searchBibleHybrid(message, locale, 5)
// Create context from relevant verses
const versesContext = relevantVerses
@@ -65,8 +65,9 @@ async function generateBiblicalResponse(message: string, history: any[]): Promis
.map(msg => `${msg.role}: ${msg.content}`)
.join('\n')
// Construct prompt for Azure OpenAI
const systemPrompt = `Ești un asistent AI pentru întrebări biblice în limba română. Răspunde pe baza Scripturii, fiind respectuos și înțelept.
// Create language-specific system prompts
const systemPrompts = {
ro: `Ești un asistent AI pentru întrebări biblice în limba română. Răspunde pe baza Scripturii, fiind respectuos și înțelept.
Instrucțiuni:
- Folosește versurile biblice relevante pentru a răspunde la întrebare
@@ -81,7 +82,27 @@ ${versesContext}
Conversația anterioară:
${conversationHistory}
Întrebarea curentă: ${message}`
Întrebarea curentă: ${message}`,
en: `You are an AI assistant for biblical questions in English. Answer based on Scripture, being respectful and wise.
Instructions:
- Use the relevant Bible verses to answer the question
- Always cite biblical references (e.g., John 3:16)
- Respond in English
- Be empathetic and encouraging
- If unsure, encourage personal study and prayer
Relevant verses for this question:
${versesContext}
Previous conversation:
${conversationHistory}
Current question: ${message}`
}
const systemPrompt = systemPrompts[locale as keyof typeof systemPrompts] || systemPrompts.en
// Call Azure OpenAI
const response = await fetch(
@@ -120,11 +141,21 @@ ${conversationHistory}
} catch (error) {
console.error('Error calling Azure OpenAI:', error)
// Fallback to simple response if AI fails
return `Îmi pare rău, dar întâmpin o problemă tehnică în acest moment. Te încurajez să cercetezi acest subiect în Scripturi și să te rogi pentru înțelegere.
// Language-specific fallback responses
const fallbackResponses = {
ro: `Îmi pare rău, dar întâmpin o problemă tehnică în acest moment. Te încurajez să cercetezi acest subiect în Scripturi și să te rogi pentru înțelegere.
"Cercetați Scripturile, pentru că socotiți că în ele aveți viața veșnică, și tocmai ele mărturisesc despre Mine" (Ioan 5:39).
"Dacă vreunul dintre voi duce lipsă de înțelepciune, să ceară de la Dumnezeu, care dă tuturor cu dărnicie și fără mustrare, și i se va da" (Iacov 1:5).`
"Dacă vreunul dintre voi duce lipsă de înțelepciune, să ceară de la Dumnezeu, care dă tuturor cu dărnicie și fără mustrare, și i se va da" (Iacov 1:5).`,
en: `Sorry, I'm experiencing a technical issue at the moment. I encourage you to research this topic in Scripture and pray for understanding.
"You study the Scriptures diligently because you think that in them you have eternal life. These are the very Scriptures that testify about me" (John 5:39).
"If any of you lacks wisdom, you should ask God, who gives generously to all without finding fault, and it will be given to you" (James 1:5).`
}
return fallbackResponses[locale as keyof typeof fallbackResponses] || fallbackResponses.en
}
}

View File

@@ -1,26 +1,7 @@
import './globals.css'
import type { Metadata } from 'next'
import { MuiThemeProvider } from '@/components/providers/theme-provider'
import FloatingChat from '@/components/chat/floating-chat'
export const metadata: Metadata = {
title: 'Ghid Biblic - Biblical Guide',
description: 'A comprehensive Bible study application with AI chat capabilities',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="ro">
<body>
<MuiThemeProvider>
{children}
<FloatingChat />
</MuiThemeProvider>
</body>
</html>
)
return children
}