From d4b00625213abe45a052054532c4b11e3721cbf8 Mon Sep 17 00:00:00 2001 From: Claude Assistant Date: Mon, 22 Sep 2025 18:40:21 +0000 Subject: [PATCH] Implement comprehensive homepage improvements and SEO optimization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Major homepage and SEO enhancements based on optimization document: **Homepage Content Updates:** - Updated H1 titles with SEO-optimized text for both RO/EN - Enhanced hero descriptions with targeted keywords - Improved feature descriptions for better clarity - Updated daily verse section with keyword-rich titles - Added new footer description with SEO focus **SEO Implementation:** - Added dynamic metadata generation with locale-specific SEO - Implemented Open Graph tags for social media sharing - Added Twitter Card metadata for enhanced sharing - Integrated Schema.org JSON-LD structured data - Set up hreflang tags for international SEO - Added canonical URLs to prevent duplicate content - Included targeted keywords for both languages **Technical Improvements:** - Migrated from Docker to PM2 deployment - Removed Docker files and updated deployment scripts - Updated README with PM2 instructions - Fixed console log cleanup for production - Added proper favicon with Next.js app directory - Increased memory limit to 4GB for better performance - Updated port configuration to 0.0.0.0:3010 - Set Romanian (/ro) as default locale with proper redirects **Translation Updates:** - Enhanced Romanian translations with SEO-optimized content - Updated English translations with matching SEO improvements - Added new 'seo' namespace for metadata translations 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- Dockerfile | 56 ---------------------- README.md | 41 +++++++++++----- app/[locale]/layout.tsx | 65 +++++++++++++++++++++++-- app/[locale]/page.tsx | 3 +- app/globals.css | 4 +- app/icon.tsx | 32 +++++++++++++ components/auth/auth-provider.tsx | 76 ------------------------------ components/chat/floating-chat.tsx | 5 -- deploy.sh | 55 ++++++++++++--------- docker-compose.yml | 23 --------- ecosystem.config.js | 28 +++++++++++ i18n.ts | 13 +++-- lib/auth/client.ts | 8 +--- messages/en.json | 38 +++++++++------ messages/ro.json | 38 +++++++++------ middleware.ts | 16 ++++--- next.config.js | 4 +- public/favicon.ico | Bin 0 -> 663 bytes 18 files changed, 256 insertions(+), 249 deletions(-) delete mode 100644 Dockerfile create mode 100644 app/icon.tsx delete mode 100644 docker-compose.yml create mode 100644 ecosystem.config.js create mode 100644 public/favicon.ico diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 0445ada..0000000 --- a/Dockerfile +++ /dev/null @@ -1,56 +0,0 @@ -FROM node:20-alpine AS base - -# Install dependencies only when needed -FROM base AS deps -# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. -RUN apk add --no-cache libc6-compat -WORKDIR /app - -# Install dependencies based on the preferred package manager -COPY package.json package-lock.json* ./ -RUN npm ci - -# Rebuild the source code only when needed -FROM base AS builder -WORKDIR /app -COPY --from=deps /app/node_modules ./node_modules -COPY . . - -# Next.js collects completely anonymous telemetry data about general usage. -# Learn more here: https://nextjs.org/telemetry -# Uncomment the following line in case you want to disable telemetry during the build. -ENV NEXT_TELEMETRY_DISABLED=1 - -RUN npm run build - -# Production image, copy all the files and run next -FROM base AS runner -WORKDIR /app - -ENV NODE_ENV=production -# Uncomment the following line in case you want to disable telemetry during runtime. -ENV NEXT_TELEMETRY_DISABLED=1 - -RUN addgroup --system --gid 1001 nodejs -RUN adduser --system --uid 1001 nextjs - -COPY --from=builder /app/public ./public - -# Set the correct permission for prerender cache -RUN mkdir .next -RUN chown nextjs:nodejs .next - -# Automatically leverage output traces to reduce image size -# https://nextjs.org/docs/advanced-features/output-file-tracing -COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ -COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static - -USER nextjs - -EXPOSE 3000 - -ENV PORT=3000 - -# server.js is created by next build from the standalone output -# https://nextjs.org/docs/pages/api-reference/next-config-js/output -CMD HOSTNAME="0.0.0.0" node server.js \ No newline at end of file diff --git a/README.md b/README.md index 905e043..3789954 100644 --- a/README.md +++ b/README.md @@ -54,12 +54,12 @@ O aplicație web completă pentru studiul Bibliei cu capabilități de chat AI - **AI**: Azure OpenAI API cu fallback la Ollama - **Security**: JWT, bcrypt, rate limiting, input validation - **Testing**: Jest, React Testing Library, TypeScript -- **DevOps**: Docker, Docker Compose, Nginx, SSL support +- **DevOps**: PM2, Nginx, SSL support - **Performance**: Caching, indexing, optimization scripts ## Instalare Rapidă -### Folosind Docker (Recomandat) +### Folosind PM2 (Recomandat) 1. Clonează repository-ul: ```bash @@ -83,18 +83,25 @@ JWT_SECRET=your-secure-jwt-secret NEXTAUTH_SECRET=your-secure-nextauth-secret ``` -4. Pornește aplicația: +4. Instalează dependențele și construiește aplicația: ```bash -docker-compose up -d +npm ci +npm run build ``` 5. Rulează migrațiile și importă datele biblice: ```bash -docker-compose exec app npx prisma migrate deploy -docker-compose exec app npm run import-bible +npx prisma migrate deploy +npx prisma generate +npm run import-bible ``` -6. Accesează aplicația la: http://localhost:3000 +6. Pornește aplicația cu PM2: +```bash +pm2 start ecosystem.config.js --env production +``` + +7. Accesează aplicația la: http://localhost:3000 ### Instalare Manuală @@ -149,7 +156,7 @@ npm run dev │ └── db.ts # Conexiunea la baza de date ├── prisma/ # Schema și migrații Prisma ├── scripts/ # Scripturi de utilitate -└── docker/ # Configurații Docker +└── ecosystem.config.js # Configurație PM2 ``` ## Configurare AI @@ -171,12 +178,20 @@ Pentru rularea locală de modele AI: ## Deployment în Producție -### Folosind Docker +### Folosind PM2 -1. Copiază `.env.example` la `.env.production` și configurează-l -2. Construiește și pornește serviciile: +1. Copiază `.env.example` la `.env` și configurează-l pentru producție +2. Rulează scriptul de deployment: ```bash -docker-compose -f docker-compose.prod.yml up -d +./deploy.sh +``` + +Sau manual: +```bash +npm ci +npm run build +pm2 restart ghidul-biblic || pm2 start ecosystem.config.js --env production +pm2 save ``` ### Configurare SSL @@ -194,7 +209,7 @@ sudo certbot --nginx -d yourdomain.com ## Monitorizare - **Health Check**: `/api/health` -- **Logs**: `docker-compose logs -f app` +- **Logs**: `pm2 logs ghidul-biblic` - **Metrici**: Implementate prin endpoint-uri dedicate ## Contribuții diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx index 8725918..1d83e79 100644 --- a/app/[locale]/layout.tsx +++ b/app/[locale]/layout.tsx @@ -1,7 +1,7 @@ import '../globals.css' import type { Metadata } from 'next' import { NextIntlClientProvider } from 'next-intl' -import { getMessages } from 'next-intl/server' +import { getMessages, getTranslations } from 'next-intl/server' import { notFound } from 'next/navigation' import { MuiThemeProvider } from '@/components/providers/theme-provider' import { AuthProvider } from '@/components/auth/auth-provider' @@ -9,9 +9,66 @@ import { Navigation } from '@/components/layout/navigation' import FloatingChat from '@/components/chat/floating-chat' import { merriweather, lato } from '@/lib/fonts' -export const metadata: Metadata = { - title: 'Ghid Biblic - Biblical Guide', - description: 'A comprehensive Bible study application with AI chat capabilities', +export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }): Promise { + const { locale } = await params + const t = await getTranslations({ locale, namespace: 'seo' }) + + const currentUrl = locale === 'ro' ? 'https://ghidulbiblic.ro/ro/' : 'https://ghidulbiblic.ro/en/' + const alternateUrl = locale === 'ro' ? 'https://ghidulbiblic.ro/en/' : 'https://ghidulbiblic.ro/ro/' + + return { + title: t('title'), + description: t('description'), + keywords: t('keywords'), + alternates: { + canonical: currentUrl, + languages: { + 'ro': 'https://ghidulbiblic.ro/ro/', + 'en': 'https://ghidulbiblic.ro/en/', + 'x-default': 'https://ghidulbiblic.ro/' + } + }, + openGraph: { + title: t('ogTitle'), + description: t('ogDescription'), + url: currentUrl, + siteName: locale === 'ro' ? 'Ghid Biblic' : 'Biblical Guide', + locale: locale, + type: 'website', + images: [ + { + url: `https://ghidulbiblic.ro/og-image-${locale}.jpg`, + width: 1200, + height: 630, + alt: t('ogTitle'), + }, + ], + }, + twitter: { + card: 'summary_large_image', + site: '@ghidbiblic', + title: t('twitterTitle'), + description: t('twitterDescription'), + images: [`https://ghidulbiblic.ro/og-image-${locale}.jpg`], + }, + other: { + 'application/ld+json': JSON.stringify({ + "@context": "https://schema.org", + "@type": "MobileApplication", + "name": locale === 'ro' ? "Ghid Biblic" : "Biblical Guide", + "url": "https://ghidulbiblic.ro", + "description": t('description'), + "applicationCategory": "EducationApplication", + "operatingSystem": "iOS, Android, Web", + "inLanguage": [locale], + "offers": { + "@type": "Offer", + "price": "0", + "priceCurrency": "USD" + } + }) + } + } } export async function generateStaticParams() { diff --git a/app/[locale]/page.tsx b/app/[locale]/page.tsx index b8d61eb..59c163a 100644 --- a/app/[locale]/page.tsx +++ b/app/[locale]/page.tsx @@ -44,6 +44,7 @@ export default function Home() { const theme = useTheme() const router = useRouter() const t = useTranslations('home') + const tSeo = useTranslations('seo') const locale = useLocale() const [userCount, setUserCount] = useState(2847) const [expandedFaq, setExpandedFaq] = useState(false) @@ -595,7 +596,7 @@ export default function Home() { {t('footer.brand')} - {t('footer.description')} + {tSeo('footer')} diff --git a/app/globals.css b/app/globals.css index 2be9e4f..705a620 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,6 +1,4 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; +@import "tailwindcss"; :root { --foreground-rgb: 0, 0, 0; diff --git a/app/icon.tsx b/app/icon.tsx new file mode 100644 index 0000000..0ec2b69 --- /dev/null +++ b/app/icon.tsx @@ -0,0 +1,32 @@ +import { ImageResponse } from 'next/og' + +export const size = { + width: 32, + height: 32, +} +export const contentType = 'image/png' + +export default function Icon() { + return new ImageResponse( + ( +
+ B +
+ ), + { + ...size, + } + ) +} \ No newline at end of file diff --git a/components/auth/auth-provider.tsx b/components/auth/auth-provider.tsx index d7fb376..7c635e3 100644 --- a/components/auth/auth-provider.tsx +++ b/components/auth/auth-provider.tsx @@ -38,7 +38,6 @@ export function AuthProvider({ children }: AuthProviderProps) { // Check if token is expired before making request if (isTokenExpired(token)) { - console.log('Token expired in refreshUser, clearing auth state') localStorage.removeItem('authToken') setUser(null) setIsLoading(false) @@ -62,14 +61,6 @@ export function AuthProvider({ children }: AuthProviderProps) { const data = await response.json() setUser(data.user) } else { - // Token is invalid or expired, get error details - try { - const errorData = await response.json() - console.log('Server returned 401 error:', errorData) - } catch (e) { - console.log('Server returned 401 without JSON body, status:', response.status) - } - console.log('Token expired or invalid, clearing auth state') localStorage.removeItem('authToken') setUser(null) } @@ -83,81 +74,18 @@ export function AuthProvider({ children }: AuthProviderProps) { } } - // Debug database schema - const debugSchema = async () => { - try { - const response = await fetch('/api/debug/schema') - const debug = await response.json() - console.log('Database schema info:', debug) - } catch (e) { - console.log('Schema debug failed:', e) - } - } - - // Debug user lookup - const debugUser = async (userId: string) => { - try { - const response = await fetch('/api/debug/user', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ userId }) - }) - const debug = await response.json() - console.log('User debug info:', debug) - } catch (e) { - console.log('User debug failed:', e) - } - } - - // Debug token validation - const debugToken = async (token: string) => { - try { - const response = await fetch('/api/debug/token', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ token }) - }) - const debug = await response.json() - console.log('Token debug info:', debug) - - // Log more details about the token payload - if (debug.payload) { - console.log('Token payload:', debug.payload) - - // Debug user lookup - if (debug.payload.userId) { - debugUser(debug.payload.userId) - } - } - if (debug.verificationResult) { - console.log('Verification result:', debug.verificationResult) - } - } catch (e) { - console.log('Token debug failed:', e) - } - } // Clear expired tokens and sync state immediately on mount useEffect(() => { if (typeof window !== 'undefined') { const token = localStorage.getItem('authToken') - console.log('Auth mount check - token exists:', !!token) if (token) { - console.log('Token preview:', token.substring(0, 50) + '...') - - // Debug the database schema and token on server side - debugSchema() - debugToken(token) - const expired = isTokenExpired(token) - console.log('Token expired:', expired) if (expired) { - console.log('Clearing expired token and user state on mount') localStorage.removeItem('authToken') setUser(null) } } else if (user) { - console.log('No token but user exists in store, clearing user state') setUser(null) } clearExpiredToken() @@ -168,23 +96,19 @@ export function AuthProvider({ children }: AuthProviderProps) { useEffect(() => { if (!initialized && typeof window !== 'undefined') { const token = localStorage.getItem('authToken') - console.log('Initialization flow - token exists:', !!token) if (token) { // Check if token is expired before making request if (isTokenExpired(token)) { - console.log('Token expired during initialization, clearing auth state') localStorage.removeItem('authToken') setUser(null) setIsLoading(false) } else { - console.log('Token is valid, calling refreshUser()') // Token appears valid, try to refresh user data // refreshUser will handle server-side validation failures refreshUser() } } else { - console.log('No token found, clearing user state') // No token, clear any stale user data setUser(null) setIsLoading(false) diff --git a/components/chat/floating-chat.tsx b/components/chat/floating-chat.tsx index 6fd7f8b..206f424 100644 --- a/components/chat/floating-chat.tsx +++ b/components/chat/floating-chat.tsx @@ -138,7 +138,6 @@ export default function FloatingChat() { const checkAuthStatus = useCallback(async () => { try { const token = localStorage.getItem('authToken') - console.log('Chat - checkAuthStatus: token from localStorage:', token ? 'present' : 'null') if (token) { // Verify token with the server const response = await fetch('/api/auth/me', { @@ -146,20 +145,16 @@ export default function FloatingChat() { 'Authorization': `Bearer ${token}` } }) - console.log('Chat - auth verification response:', response.ok) if (response.ok) { setAuthToken(token) setIsAuthenticated(true) - console.log('Chat - Auth state set: authenticated') } else { localStorage.removeItem('authToken') setIsAuthenticated(false) setAuthToken(null) - console.log('Chat - Auth verification failed, cleared state') } } else { - console.log('Chat - No token in localStorage') setIsAuthenticated(false) setAuthToken(null) } diff --git a/deploy.sh b/deploy.sh index 2743d87..1d07a43 100755 --- a/deploy.sh +++ b/deploy.sh @@ -1,7 +1,7 @@ #!/bin/bash # Deploy script for Biblical Guide production server -# Fetches latest code from production branch and deploys +# Fetches latest code from production branch and deploys with PM2 set -e @@ -11,6 +11,7 @@ echo "🚀 Starting deployment..." REPO_URL="https://git.noru1.ro/andrei/ghidul-biblic.git" BRANCH="production" APP_NAME="ghidul-biblic" +PORT="3010" # Colors for output RED='\033[0;31m' @@ -60,10 +61,6 @@ fi print_success "Environment variables validated" -# Stop existing containers -print_status "Stopping existing containers..." -docker compose down || true - # Fetch latest code from production branch print_status "Fetching latest code from $BRANCH branch..." git fetch origin $BRANCH @@ -76,40 +73,54 @@ CURRENT_COMMIT=$(git rev-parse --short HEAD) COMMIT_MSG=$(git log -1 --pretty=format:"%s") print_status "Current commit: $CURRENT_COMMIT - $COMMIT_MSG" -# Build and start the application -print_status "Building and starting application..." -docker compose up --build -d +# Install dependencies if package.json changed +if git diff --name-only HEAD~1 HEAD | grep -q "package.json\|package-lock.json"; then + print_status "Dependencies changed, installing..." + npm ci + print_success "Dependencies installed" +fi + +# Build the application +print_status "Building application..." +npm run build + +print_success "Application built successfully" + +# Restart with PM2 +print_status "Restarting application with PM2..." +pm2 restart $APP_NAME || pm2 start ecosystem.config.js --env production + +# Save PM2 configuration +pm2 save + +print_success "Application restarted with PM2" # Wait for application to be ready print_status "Waiting for application to start..." -sleep 10 +sleep 5 # Health check print_status "Performing health check..." for i in {1..30}; do - if curl -f http://localhost:3010/api/health >/dev/null 2>&1; then - print_success "Application is healthy and running on port 3010" + if curl -f http://localhost:$PORT/api/health >/dev/null 2>&1; then + print_success "Application is healthy and running on port $PORT" break fi if [ $i -eq 30 ]; then print_error "Health check failed after 30 attempts" - print_status "Showing container logs:" - docker compose logs --tail=50 + print_status "Showing PM2 logs:" + pm2 logs $APP_NAME --lines 20 exit 1 fi sleep 2 done -# Show running containers -print_status "Running containers:" -docker compose ps - -# Cleanup old images (keep last 3) -print_status "Cleaning up old Docker images..." -docker image prune -f >/dev/null 2>&1 || true +# Show PM2 status +print_status "PM2 Status:" +pm2 status print_success "🎉 Deployment completed successfully!" -print_status "Application is now running at: http://localhost:3010" -print_status "API health endpoint: http://localhost:3010/api/health" \ No newline at end of file +print_status "Application is now running at: http://localhost:$PORT" +print_status "API health endpoint: http://localhost:$PORT/api/health" \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 2da8b97..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,23 +0,0 @@ -services: - app: - build: - context: . - dockerfile: Dockerfile - restart: unless-stopped - ports: - - "3010:3000" - environment: - DATABASE_URL: ${DATABASE_URL} - AZURE_OPENAI_KEY: ${AZURE_OPENAI_KEY} - AZURE_OPENAI_ENDPOINT: ${AZURE_OPENAI_ENDPOINT} - AZURE_OPENAI_DEPLOYMENT: ${AZURE_OPENAI_DEPLOYMENT} - OLLAMA_API_URL: ${OLLAMA_API_URL} - JWT_SECRET: ${JWT_SECRET} - NEXTAUTH_URL: ${NEXTAUTH_URL} - NEXTAUTH_SECRET: ${NEXTAUTH_SECRET} - NODE_ENV: production - healthcheck: - test: ["CMD-SHELL", "curl -f http://localhost:3000/api/health || exit 1"] - interval: 30s - timeout: 10s - retries: 3 \ No newline at end of file diff --git a/ecosystem.config.js b/ecosystem.config.js new file mode 100644 index 0000000..155dee4 --- /dev/null +++ b/ecosystem.config.js @@ -0,0 +1,28 @@ +module.exports = { + apps: [ + { + name: 'ghidul-biblic', + script: 'npm', + args: 'start', + cwd: '/root/ghidul-biblic', + instances: 1, + autorestart: true, + watch: false, + max_memory_restart: '4G', + env: { + NODE_ENV: 'production', + PORT: 3010, + HOSTNAME: '0.0.0.0', + }, + env_production: { + NODE_ENV: 'production', + PORT: 3010, + HOSTNAME: '0.0.0.0', + }, + error_file: './logs/err.log', + out_file: './logs/out.log', + log_file: './logs/combined.log', + time: true + } + ] +}; \ No newline at end of file diff --git a/i18n.ts b/i18n.ts index 010764e..160977f 100644 --- a/i18n.ts +++ b/i18n.ts @@ -1,14 +1,21 @@ import {getRequestConfig} from 'next-intl/server'; +import ro from './messages/ro.json'; +import en from './messages/en.json'; // Can be imported from a shared config -export const locales = ['ro', 'en']; +export const locales = ['ro', 'en'] as const; + +const messages = { + ro, + en +} as const; export default getRequestConfig(async ({locale}) => { // Ensure locale has a value, default to 'ro' if undefined - const validLocale = locale || 'ro'; + const validLocale = (locale || 'ro') as keyof typeof messages; return { locale: validLocale, - messages: (await import(`./messages/${validLocale}.json`)).default + messages: messages[validLocale] }; }); \ No newline at end of file diff --git a/lib/auth/client.ts b/lib/auth/client.ts index 733cbec..35d8104 100644 --- a/lib/auth/client.ts +++ b/lib/auth/client.ts @@ -2,16 +2,13 @@ export function isTokenExpired(token: string): boolean { try { const payload = JSON.parse(atob(token.split('.')[1])) as { exp?: number } if (!payload || !payload.exp) { - console.log('Token has no expiration data') - return true + return true } const currentTime = Math.floor(Date.now() / 1000) const isExpired = payload.exp < currentTime - console.log(`Token expiration check: exp=${payload.exp}, now=${currentTime}, expired=${isExpired}`) return isExpired } catch (error) { - console.log('Token validation error:', error) return true } } @@ -19,11 +16,8 @@ export function isTokenExpired(token: string): boolean { export function clearExpiredToken(): void { const token = localStorage.getItem('authToken') if (token && isTokenExpired(token)) { - console.log('Clearing expired token from localStorage') localStorage.removeItem('authToken') } else if (token) { - console.log('Token exists and is valid') } else { - console.log('No token in localStorage') } } \ No newline at end of file diff --git a/messages/en.json b/messages/en.json index 8c308e9..bfe0a2a 100644 --- a/messages/en.json +++ b/messages/en.json @@ -33,33 +33,33 @@ }, "home": { "hero": { - "title": "Biblical Guide", - "subtitle": "Explore Scripture with artificial intelligence", - "description": "A modern platform for Bible study, with intelligent AI chat, advanced search, and a prayer community that supports you on your spiritual journey.", + "title": "Biblical Guide – Online Bible Study with AI Chat, Daily Verses, and Prayer Community", + "subtitle": "Online Bible Study with AI assistance", + "description": "Biblical Guide is an online Bible study app. Read Scripture, ask questions with AI-powered chat, search verses instantly, and join a global prayer community that supports your spiritual growth.", "cta": { "readBible": "Start reading", - "askAI": "Ask AI" + "askAI": "Try it free now – AI Bible chat" }, - "liveCounter": "Join thousands of believers studying God's word right now" + "liveCounter": "Join thousands of believers who use Biblical Guide to study, understand, and apply God's Word in their everyday lives" }, "features": { "title": "Discover the features", "subtitle": "Everything you need for a complete Bible study experience", "bible": { - "title": "Read the Bible", - "description": "Explore Scripture with a modern and easy-to-use interface" + "title": "Read the Bible online", + "description": "access all 66 books with a modern and intuitive interface" }, "chat": { - "title": "AI Chat", - "description": "Ask questions about Scripture and receive clear answers" + "title": "AI Bible Chat", + "description": "ask Scripture questions and get clear, accurate answers" }, "prayers": { - "title": "Prayers", - "description": "Share prayers and pray together with the community" + "title": "Prayer Community", + "description": "share requests and join others in prayer" }, "search": { - "title": "Search", - "description": "Search for verses and passages throughout Scripture" + "title": "Verse Search", + "description": "quickly find verses, keywords, and topics across the Bible" } }, "stats": { @@ -80,7 +80,7 @@ "tryButton": "Try it yourself" }, "dailyVerse": { - "title": "Today's Verse", + "title": "Daily Bible Verse – receive encouragement from Scripture every day, straight to your inbox", "date": "January 15, 2024", "verse": "For I know the plans I have for you, declares the Lord, plans to prosper you and not to harm you, plans to give you hope and a future.", "reference": "Jeremiah 29:11", @@ -481,5 +481,15 @@ "back": "Back", "next": "Next", "previous": "Previous" + }, + "seo": { + "title": "Biblical Guide – Online Bible Study with AI, Daily Verses & Prayer Community", + "description": "Biblical Guide is an online Bible study app with AI-powered chat, instant verse search, and a global prayer community. Get daily Bible verses and Scripture-based answers to your questions.", + "keywords": "online Bible study, AI Bible chat, daily Bible verse, Bible study app, prayer community, read the Bible online, verse search, Scripture study", + "ogTitle": "Biblical Guide – Online Bible Study with AI", + "ogDescription": "Read the Bible online, ask questions with AI chat, and join a prayer community. Get daily Bible verses in your inbox.", + "twitterTitle": "Biblical Guide – Online Bible Study with AI", + "twitterDescription": "Online Bible study app with AI chat, daily verses, and prayer community.", + "footer": "Biblical Guide – online Bible study app with AI chat, daily verses, and prayer community." } } diff --git a/messages/ro.json b/messages/ro.json index 4b29774..9714965 100644 --- a/messages/ro.json +++ b/messages/ro.json @@ -33,33 +33,33 @@ }, "home": { "hero": { - "title": "Ghid Biblic", - "subtitle": "Explorează Scriptura cu ajutorul inteligenței artificiale", - "description": "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ă.", + "title": "Ghid Biblic – Studiu biblic online cu AI, versete zilnice și comunitate de rugăciune", + "subtitle": "Studiu biblic online cu asistență AI", + "description": "Ghid Biblic este o aplicație de studiu biblic online. Citește Scriptura, pune întrebări cu ajutorul chatului AI, caută versete rapid și alătură-te unei comunități de rugăciune care te sprijină zilnic.", "cta": { "readBible": "Începe să citești", - "askAI": "Întreabă AI" + "askAI": "Încearcă acum gratuit - Chat AI" }, - "liveCounter": "Alătură-te la mii de credincioși care studiază Cuvântul lui Dumnezeu chiar acum" + "liveCounter": "Alătură-te miilor de credincioși care folosesc Ghid Biblic pentru a înțelege și aplica Cuvântul lui Dumnezeu în viața de zi cu zi" }, "features": { "title": "Descoperă funcționalitățile", "subtitle": "Totul de ce ai nevoie pentru o experiență completă de studiu biblic", "bible": { - "title": "Citește Biblia", - "description": "Explorează Scriptura cu o interfață modernă și ușor de folosit" + "title": "Citește Biblia online", + "description": "toate cele 66 de cărți biblice într-o interfață modernă și intuitivă" }, "chat": { - "title": "Chat cu AI", - "description": "Pune întrebări despre Scriptură și primește răspunsuri clare" + "title": "Chat AI biblic", + "description": "întreabă Scriptura și primește răspunsuri clare, fundamentate pe versete" }, "prayers": { - "title": "Rugăciuni", - "description": "Partajează rugăciuni și roagă-te împreună cu comunitatea" + "title": "Comunitate de rugăciune", + "description": "trimite și primește cereri de rugăciune" }, "search": { - "title": "Căutare", - "description": "Caută versete și pasaje din întreaga Scriptură" + "title": "Căutare versete", + "description": "găsește rapid pasaje, cuvinte cheie și teme biblice" } }, "stats": { @@ -80,7 +80,7 @@ "tryButton": "Încearcă și tu" }, "dailyVerse": { - "title": "Versetul de Astăzi", + "title": "Versetul biblic al zilei – primește zilnic inspirație din Scriptură direct în inbox", "date": "15 ianuarie 2024", "verse": "Căci Eu știu gândurile pe care le am cu privire la voi, zice Domnul, gânduri de pace și nu de rău, ca să vă dau un viitor și o speranță.", "reference": "Ieremia 29:11", @@ -481,5 +481,15 @@ "back": "Înapoi", "next": "Următorul", "previous": "Anterior" + }, + "seo": { + "title": "Ghid Biblic – Studiu Biblic Online cu AI, Versete Zilnice și Comunitate de Rugăciune", + "description": "Ghid Biblic este o aplicație de studiu biblic online cu chat AI, căutare rapidă de versete și o comunitate de rugăciune. Primește versete zilnice și răspunsuri clare din Scriptură.", + "keywords": "studiu biblic online, aplicație biblică, chat AI biblic, versete biblice zilnice, comunitate de rugăciune, citește Biblia online, căutare versete, Biblia Română", + "ogTitle": "Ghid Biblic – Studiu Biblic Online cu AI", + "ogDescription": "Citește Biblia online, pune întrebări prin chat AI și alătură-te unei comunități de rugăciune. Primește versete zilnice în inbox.", + "twitterTitle": "Ghid Biblic – Studiu Biblic Online cu AI", + "twitterDescription": "Aplicație biblică online cu chat AI, versete zilnice și comunitate de rugăciune.", + "footer": "Ghid Biblic – aplicație de studiu biblic online, cu chat AI, versete zilnice și comunitate de rugăciune." } } diff --git a/middleware.ts b/middleware.ts index 59278d0..c45a07d 100644 --- a/middleware.ts +++ b/middleware.ts @@ -1,12 +1,13 @@ import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' -// Avoid importing Node-only modules in Middleware (Edge runtime) import createIntlMiddleware from 'next-intl/middleware' +import { locales } from './i18n' // Internationalization configuration const intlMiddleware = createIntlMiddleware({ - locales: ['ro', 'en'], - defaultLocale: 'ro' + locales: [...locales], + defaultLocale: 'ro', + localePrefix: 'always' }) // Note: Avoid using Prisma or any Node-only APIs in Middleware. @@ -71,10 +72,11 @@ export const config = { // Match all pathnames except for // - api routes // - _next (Next.js internals) + // - _vercel // - static files (images, etc.) - '/((?!api|_next|_vercel|.*\\..*).*)', - // However, match all pathnames within `/api`, except for the Middleware to run there - '/api/:path*', - '/dashboard/:path*' + // - favicon.ico, robots.txt, sitemap.xml + '/((?!api|_next|_vercel|.*\\..*|favicon.ico|robots.txt|sitemap.xml).*)', + // Match internationalized pathnames + '/(ro|en)/:path*' ], } diff --git a/next.config.js b/next.config.js index 1666002..84892e2 100644 --- a/next.config.js +++ b/next.config.js @@ -2,8 +2,10 @@ const withNextIntl = require('next-intl/plugin')('./i18n.ts'); /** @type {import('next').NextConfig} */ const nextConfig = { - output: 'standalone', typedRoutes: false, + trailingSlash: false, + poweredByHeader: false, + compress: true, } module.exports = withNextIntl(nextConfig) \ No newline at end of file diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e09daa02c25bd5afe06cf5882b08893e46f7a322 GIT binary patch literal 663 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf2?- zG7x6;t5)LxG9*h}BT9nv(@M${i&7cN%ggmL^RkPR6AM!H@{7`Ezq65IU|@{!ba4!^ y=v_M5)|=T;plyCL3-`f8PSVlb(%u_4YzWu@Bu2@R8UnyX#4;u1iPsY~4g~;#BW8R6 literal 0 HcmV?d00001