From 39f8567519b8bb721a6b09093440ae6aba5328c7 Mon Sep 17 00:00:00 2001 From: Yolando Date: Sat, 28 Mar 2026 19:59:39 +0700 Subject: [PATCH] feat: implement internationalization with next-intl and add core portfolio sections --- messages/en.json | 143 ++++ messages/id.json | 143 ++++ next.config.ts | 5 +- package-lock.json | 688 +++++++++++++++++- package.json | 1 + src/app/{ => [locale]}/layout.tsx | 27 +- src/app/{ => [locale]}/page.tsx | 0 .../experience/experience-section.tsx | 99 ++- src/features/hero/hero-section.tsx | 31 +- src/features/messages/contact-section.tsx | 28 +- src/features/projects/projects-section.tsx | 141 ++-- src/features/skills/tech-stack-section.tsx | 122 ++-- src/i18n/request.ts | 15 + src/i18n/routing.ts | 10 + src/middleware.ts | 9 + src/shared/components/footer.tsx | 8 +- src/shared/components/navbar.tsx | 87 ++- 17 files changed, 1304 insertions(+), 253 deletions(-) create mode 100644 messages/en.json create mode 100644 messages/id.json rename src/app/{ => [locale]}/layout.tsx (63%) rename src/app/{ => [locale]}/page.tsx (100%) create mode 100644 src/i18n/request.ts create mode 100644 src/i18n/routing.ts create mode 100644 src/middleware.ts diff --git a/messages/en.json b/messages/en.json new file mode 100644 index 0000000..14bd72d --- /dev/null +++ b/messages/en.json @@ -0,0 +1,143 @@ +{ + "Navigation": { + "experience": "Experience", + "techStack": "Tech Stack", + "projects": "Projects", + "contact": "Contact" + }, + "Hero": { + "badge": "Available for opportunities", + "titlePart1": "Building", + "titleHighlight": "Secure, Scalable", + "titlePart2": "Enterprise-Grade Systems", + "yearsExp": "3+ Years", + "subtitle": "in Banking Technology. Backend Developer specializing in Java Spring Boot, Microservices Architecture, and Enterprise Security.", + "ctaContact": "Get in Touch", + "ctaProjects": "View Projects", + "scroll": "Scroll" + }, + "Experience": { + "badge": "Career Journey", + "title": "Experience & Evolution", + "subtitle": "A timeline of building enterprise-grade systems in the banking technology industry.", + "jobs": { + "enterprise": { + "year": "2024 — Present", + "title": "Senior Backend Developer", + "company": "Enterprise Banking Corp", + "description": "Leading microservices architecture design and implementation for core banking platform.", + "achievements": [ + "Architected event-driven system processing 500K+ transactions/day", + "Reduced API response time by 40% through caching optimization", + "Mentored team of 4 junior developers" + ] + }, + "digital": { + "year": "2023 — 2024", + "title": "Backend Developer", + "company": "Digital Banking Solutions", + "description": "Developed and maintained Spring Boot microservices for payment processing and customer management.", + "achievements": [ + "Built real-time notification service with Apache Kafka", + "Implemented OAuth2 + JWT authentication system", + "Achieved 99.9% uptime on production services" + ] + }, + "fintech": { + "year": "2021 — 2023", + "title": "Junior Backend Developer", + "company": "FinTech Startup", + "description": "Started career building REST APIs and database management for financial applications.", + "achievements": [ + "Designed PostgreSQL schema handling 1M+ records", + "Created automated CI/CD pipeline with Jenkins", + "Developed unit & integration test coverage to 85%" + ] + } + } + }, + "TechStack": { + "badge": "Tech Arsenal", + "title": "Technology Stack", + "subtitle": "A comprehensive toolkit forged through years of enterprise development, from backend infrastructure to mobile interfaces.", + "categories": { + "backend": { + "title": "Enterprise Backend", + "description": "Core systems & microservices" + }, + "infra": { + "title": "Database & Infrastructure", + "description": "Data management & DevOps" + }, + "frontend": { + "title": "Frontend Development", + "description": "Modern web interfaces" + }, + "mobile": { + "title": "Mobile Development", + "description": "Cross-platform apps" + } + } + }, + "Projects": { + "badge": "Portfolio", + "title": "Projects & Case Studies", + "subtitle": "Real-world enterprise solutions built for scale, security, and reliability.", + "filters": { + "all": "All Projects", + "backend": "Backend", + "frontend": "Frontend", + "mobile": "Mobile" + }, + "items": { + "apiGateway": { + "title": "Core Banking API Gateway", + "description": "High-performance API Gateway handling 500K+ daily transactions with rate limiting, circuit breaker pattern, and distributed tracing across 12 microservices.", + "metrics": "Increased throughput by 40%, 99.9% uptime" + }, + "paymentEngine": { + "title": "Payment Processing Engine", + "description": "Event-driven payment system with Saga pattern for distributed transactions, supporting real-time transfers, bill payments, and batch processing.", + "metrics": "Processing 200K+ payments/day" + }, + "onboarding": { + "title": "Customer Onboarding Portal", + "description": "Modern onboarding portal with multi-step KYC verification, document upload, and real-time status tracking for banking customers." + }, + "dashboard": { + "title": "Internal Dashboard", + "description": "Admin dashboard for monitoring API performance, user analytics, and system health with real-time WebSocket updates." + }, + "mobileApp": { + "title": "Mobile Banking App", + "description": "Cross-platform mobile banking application with biometric authentication, push notifications, and offline transaction history." + }, + "authService": { + "title": "Authentication Microservice", + "description": "Centralized auth service with OAuth2, JWT, MFA, and session management. Supports SSO across 8 internal applications.", + "metrics": "Securing 50K+ active users" + } + } + }, + "Contact": { + "badge": "Let's Connect", + "title": "Get in Touch", + "subtitle": "Interested in working together? Whether you're a recruiter, hiring manager, or potential collaborator — I'd love to hear from you.", + "form": { + "nameLabel": "Full Name", + "namePlaceholder": "John Doe", + "emailLabel": "Email Address", + "emailPlaceholder": "john@company.com", + "messageLabel": "Message", + "messagePlaceholder": "Tell me about the opportunity or project you have in mind...", + "submit": "Send Message", + "submitting": "Sending...", + "successTitle": "Message Sent!", + "successDesc": "Thank you for reaching out. I'll get back to you soon." + } + }, + "Footer": { + "backToTop": "Back to top", + "copyright": "Yolando. Built with Next.js & crafted with purpose." + } +} diff --git a/messages/id.json b/messages/id.json new file mode 100644 index 0000000..6c86653 --- /dev/null +++ b/messages/id.json @@ -0,0 +1,143 @@ +{ + "Navigation": { + "experience": "Pengalaman", + "techStack": "Tech Stack", + "projects": "Proyek", + "contact": "Kontak" + }, + "Hero": { + "badge": "Tersedia untuk peluang baru", + "titlePart1": "Membangun Sistem", + "titleHighlight": "Aman & Skalabel", + "titlePart2": "Skala Enterprise", + "yearsExp": "3+ Tahun", + "subtitle": "di Teknologi Perbankan. Backend Developer dengan spesialisasi Java Spring Boot, Arsitektur Microservices, dan Keamanan Enterprise.", + "ctaContact": "Hubungi Saya", + "ctaProjects": "Lihat Proyek", + "scroll": "Scroll" + }, + "Experience": { + "badge": "Perjalanan Karir", + "title": "Pengalaman & Evolusi", + "subtitle": "Garis waktu (timeline) membangun sistem skala enterprise di industri teknologi perbankan.", + "jobs": { + "enterprise": { + "year": "2024 — Sekarang", + "title": "Senior Backend Developer", + "company": "Enterprise Banking Corp", + "description": "Memimpin desain dan implementasi arsitektur microservices untuk platform perbankan inti.", + "achievements": [ + "Merancang sistem event-driven yang memproses 500Ribu+ transaksi/hari", + "Meningkatkan kecepatan respons API hingga 40% melalui optimasi caching", + "Mementori 4 junior developer" + ] + }, + "digital": { + "year": "2023 — 2024", + "title": "Backend Developer", + "company": "Digital Banking Solutions", + "description": "Mengembangkan dan memelihara microservices Spring Boot untuk pemrosesan pembayaran dan manajemen pelanggan.", + "achievements": [ + "Membangun layanan notifikasi real-time dengan Apache Kafka", + "Menerapkan sistem autentikasi OAuth2 + JWT", + "Mencapai 99.9% uptime pada layanan produksi" + ] + }, + "fintech": { + "year": "2021 — 2023", + "title": "Junior Backend Developer", + "company": "FinTech Startup", + "description": "Memulai karir membangun REST API dan manajemen database untuk aplikasi finansial.", + "achievements": [ + "Mendesain skema PostgreSQL yang menangani 1Juta+ data", + "Membuat pipeline CI/CD otomatis dengan Jenkins", + "Meningkatkan cakupan unit & integration test hingga 85%" + ] + } + } + }, + "TechStack": { + "badge": "Arsenal Teknologi", + "title": "Tech Stack", + "subtitle": "Perangkat komprehensif yang ditempa dari pengalaman pengembangan enterprise, mulai dari infrastruktur backend hingga antarmuka mobile.", + "categories": { + "backend": { + "title": "Enterprise Backend", + "description": "Sistem inti & microservices" + }, + "infra": { + "title": "Database & Infrastruktur", + "description": "Manajemen data & DevOps" + }, + "frontend": { + "title": "Frontend Development", + "description": "Antarmuka web modern" + }, + "mobile": { + "title": "Mobile Development", + "description": "Aplikasi cross-platform" + } + } + }, + "Projects": { + "badge": "Portofolio", + "title": "Proyek & Studi Kasus", + "subtitle": "Solusi enterprise dunia nyata yang dibangun untuk skalabilitas, keamanan, dan keandalan.", + "filters": { + "all": "Semua Proyek", + "backend": "Backend", + "frontend": "Frontend", + "mobile": "Mobile" + }, + "items": { + "apiGateway": { + "title": "Core Banking API Gateway", + "description": "API Gateway performa tinggi yang menangani 500Ribu+ transaksi harian dengan rate limiting, pola circuit breaker, dan distributed tracing pada 12 microservices.", + "metrics": "Meningkatkan throughput 40%, 99.9% uptime" + }, + "paymentEngine": { + "title": "Mesin Pemroses Pembayaran", + "description": "Sistem pembayaran event-driven dengan pola Saga untuk transaksi terdistribusi, mendukung transfer real-time, pembayaran tagihan, dan pemrosesan batch.", + "metrics": "Memproses 200Ribu+ pembayaran/hari" + }, + "onboarding": { + "title": "Portal Onboarding Pelanggan", + "description": "Portal onboarding modern dengan verifikasi KYC multi-langkah, unggah dokumen, dan pelacakan status real-time untuk pelanggan perbankan." + }, + "dashboard": { + "title": "Dashboard Internal", + "description": "Dashboard admin untuk memantau performa API, analitik pengguna, dan kesehatan sistem dengan update WebSocket real-time." + }, + "mobileApp": { + "title": "Aplikasi Mobile Banking", + "description": "Aplikasi mobile banking cross-platform dengan autentikasi biometrik, notifikasi push, dan riwayat transaksi offline." + }, + "authService": { + "title": "Microservice Autentikasi", + "description": "Layanan autentikasi terpusat dengan OAuth2, JWT, MFA, dan manajemen sesi. Mendukung SSO di 8 aplikasi internal.", + "metrics": "Mengamankan 50Ribu+ pengguna aktif" + } + } + }, + "Contact": { + "badge": "Mari Terhubung", + "title": "Hubungi Saya", + "subtitle": "Tertarik bekerja sama? Baik Anda seorang recruiter, hiring manager, atau kolaborator potensial — saya sangat ingin mendengarnya.", + "form": { + "nameLabel": "Nama Lengkap", + "namePlaceholder": "Budi Santoso", + "emailLabel": "Alamat Email", + "emailPlaceholder": "budi@perusahaan.com", + "messageLabel": "Pesan", + "messagePlaceholder": "Ceritakan tentang peluang atau proyek yang Anda pikirkan...", + "submit": "Kirim Pesan", + "submitting": "Mengirim...", + "successTitle": "Pesan Terkirim!", + "successDesc": "Terima kasih telah menghubungi. Saya akan segera membalasnya." + } + }, + "Footer": { + "backToTop": "Kembali ke atas", + "copyright": "Yolando. Dibangun dengan Next.js & dibuat dengan penuh tujuan." + } +} diff --git a/next.config.ts b/next.config.ts index e9ffa30..99c836d 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,7 +1,10 @@ import type { NextConfig } from "next"; +import createNextIntlPlugin from 'next-intl/plugin'; + +const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts'); const nextConfig: NextConfig = { /* config options here */ }; -export default nextConfig; +export default withNextIntl(nextConfig); diff --git a/package-lock.json b/package-lock.json index a788c51..47e1124 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "framer-motion": "^12.38.0", "lucide-react": "^1.7.0", "next": "^15.5.14", + "next-intl": "^4.8.3", "next-themes": "^0.4.6", "react": "19.2.4", "react-dom": "19.2.4" @@ -203,6 +204,51 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@formatjs/bigdecimal": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@formatjs/bigdecimal/-/bigdecimal-0.2.0.tgz", + "integrity": "sha512-GeaxHZbUoYvHL9tC5eltHLs+1zU70aPw0s7LwqgktIzF5oMhNY4o4deEtusJMsq7WFJF3Ye2zQEzdG8beVk73w==" + }, + "node_modules/@formatjs/ecma402-abstract": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-3.2.0.tgz", + "integrity": "sha512-dHnqHgBo6GXYGRsepaE1wmsC2etaivOWd5VaJstZd+HI2zR3DCUjbDVZRtoPGkkXZmyHvBwrdEUuqfvzhF/DtQ==", + "dependencies": { + "@formatjs/bigdecimal": "0.2.0", + "@formatjs/fast-memoize": "3.1.1", + "@formatjs/intl-localematcher": "0.8.2" + } + }, + "node_modules/@formatjs/fast-memoize": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-3.1.1.tgz", + "integrity": "sha512-CbNbf+tlJn1baRnPkNePnBqTLxGliG6DDgNa/UtV66abwIjwsliPMOt0172tzxABYzSuxZBZfcp//qI8AvBWPg==" + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-3.5.3.tgz", + "integrity": "sha512-HJWZ9S6JWey6iY5+YXE3Kd0ofWU1sC2KTTp56e1168g/xxWvVvr8k9G4fexIgwYV9wbtjY7kGYK5FjoWB3B2OQ==", + "dependencies": { + "@formatjs/ecma402-abstract": "3.2.0", + "@formatjs/icu-skeleton-parser": "2.1.3" + } + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-2.1.3.tgz", + "integrity": "sha512-9mFp8TJ166ZM2pcjKwsBWXrDnOJGT7vMEScVgLygUODPOsE8S6f/FHoacvrlHK1B4dYZk8vSCNruyPU64AfgJQ==", + "dependencies": { + "@formatjs/ecma402-abstract": "3.2.0" + } + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.8.2.tgz", + "integrity": "sha512-q05KMYGJLyqFNFtIb8NhWLF5X3aK/k0wYt7dnRFuy6aLQL+vUwQ1cg5cO4qawEiINybeCPXAWlprY2mSBjSXAQ==", + "dependencies": { + "@formatjs/fast-memoize": "3.1.1" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -917,6 +963,298 @@ "node": ">=12.4.0" } }, + "node_modules/@parcel/watcher": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", + "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", + "hasInstallScript": true, + "dependencies": { + "detect-libc": "^2.0.3", + "is-glob": "^4.0.3", + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.6", + "@parcel/watcher-darwin-arm64": "2.5.6", + "@parcel/watcher-darwin-x64": "2.5.6", + "@parcel/watcher-freebsd-x64": "2.5.6", + "@parcel/watcher-linux-arm-glibc": "2.5.6", + "@parcel/watcher-linux-arm-musl": "2.5.6", + "@parcel/watcher-linux-arm64-glibc": "2.5.6", + "@parcel/watcher-linux-arm64-musl": "2.5.6", + "@parcel/watcher-linux-x64-glibc": "2.5.6", + "@parcel/watcher-linux-x64-musl": "2.5.6", + "@parcel/watcher-win32-arm64": "2.5.6", + "@parcel/watcher-win32-ia32": "2.5.6", + "@parcel/watcher-win32-x64": "2.5.6" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz", + "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz", + "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz", + "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz", + "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz", + "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz", + "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz", + "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz", + "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz", + "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz", + "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz", + "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", + "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", + "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -929,6 +1267,196 @@ "integrity": "sha512-TvZbIpeKqGQQ7X0zSCvPH9riMSFQFSggnfBjFZ1mEoILW+UuXCKwOoPcgjMwiUtRqFZ8jWhPJc4um14vC6I4ag==", "dev": true }, + "node_modules/@schummar/icu-type-parser": { + "version": "1.21.5", + "resolved": "https://registry.npmjs.org/@schummar/icu-type-parser/-/icu-type-parser-1.21.5.tgz", + "integrity": "sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==" + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.15.21", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.21.tgz", + "integrity": "sha512-SA8SFg9dp0qKRH8goWsax6bptFE2EdmPf2YRAQW9WoHGf3XKM1bX0nd5UdwxmC5hXsBUZAYf7xSciCler6/oyA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.15.21", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.21.tgz", + "integrity": "sha512-//fOVntgowz9+V90lVsNCtyyrtbHp3jWH6Rch7MXHXbcvbLmbCTmssl5DeedUWLLGiAAW1wksBdqdGYOTjaNLw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.15.21", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.21.tgz", + "integrity": "sha512-meNI4Sh6h9h8DvIfEc0l5URabYMSuNvyisLmG6vnoYAS43s8ON3NJR8sDHvdP7NJTrLe0q/x2XCn6yL/BeHcZg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.15.21", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.21.tgz", + "integrity": "sha512-QrXlNQnHeXqU2EzLlnsPoWEh8/GtNJLvfMiPsDhk+ht6Xv8+vhvZ5YZ/BokNWSIZiWPKLAqR0M7T92YF5tmD3g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.15.21", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.21.tgz", + "integrity": "sha512-8/yGCMO333ultDaMQivE5CjO6oXDPeeg1IV4sphojPkb0Pv0i6zvcRIkgp60xDB+UxLr6VgHgt+BBgqS959E9g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-ppc64-gnu": { + "version": "1.15.21", + "resolved": "https://registry.npmjs.org/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.15.21.tgz", + "integrity": "sha512-ucW0HzPx0s1dgRvcvuLSPSA/2Kk/VYTv9st8qe1Kc22Gu0Q0rH9+6TcBTmMuNIp0Xs4BPr1uBttmbO1wEGI49Q==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-s390x-gnu": { + "version": "1.15.21", + "resolved": "https://registry.npmjs.org/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.15.21.tgz", + "integrity": "sha512-ulTnOGc5I7YRObE/9NreAhQg94QkiR5qNhhcUZ1iFAYjzg/JGAi1ch+s/Ixe61pMIr8bfVrF0NOaB0f8wjaAfA==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.15.21", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.21.tgz", + "integrity": "sha512-D0RokxtM+cPvSqJIKR6uja4hbD+scI9ezo95mBhfSyLUs9wnPPl26sLp1ZPR/EXRdYm3F3S6RUtVi+8QXhT24Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.15.21", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.21.tgz", + "integrity": "sha512-nER8u7VeRfmU6fMDzl1NQAbbB/G7O2avmvCOwIul1uGkZ2/acbPH+DCL9h5+0yd/coNcxMBTL6NGepIew+7C2w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.15.21", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.21.tgz", + "integrity": "sha512-+/AgNBnjYugUA8C0Do4YzymgvnGbztv7j8HKSQLvR/DQgZPoXQ2B3PqB2mTtGh/X5DhlJWiqnunN35JUgWcAeQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.15.21", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.21.tgz", + "integrity": "sha512-IkSZj8PX/N4HcaFhMQtzmkV8YSnuNoJ0E6OvMwFiOfejPhiKXvl7CdDsn1f4/emYEIDO3fpgZW9DTaCRMDxaDA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.15.21", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.21.tgz", + "integrity": "sha512-zUyWso7OOENB6e1N1hNuNn8vbvLsTdKQ5WKLgt/JcBNfJhKy/6jmBmqI3GXk/MyvQKd5SLvP7A0F36p7TeDqvw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" + }, "node_modules/@swc/helpers": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", @@ -937,6 +1465,14 @@ "tslib": "^2.8.0" } }, + "node_modules/@swc/types": { + "version": "0.1.26", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.26.tgz", + "integrity": "sha512-lyMwd7WGgG79RS7EERZV3T8wMdmPq3xwyg+1nmAM64kIhx5yl+juO2PYIHb7vTiPgPCj8LYjsNV2T5wiQHUEaw==", + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", @@ -2246,7 +2782,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "optional": true, "engines": { "node": ">=8" } @@ -3335,6 +3870,20 @@ "node": ">= 0.4" } }, + "node_modules/icu-minify": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/icu-minify/-/icu-minify-4.8.3.tgz", + "integrity": "sha512-65Av7FLosNk7bPbmQx5z5XG2Y3T2GFppcjiXh4z1idHeVgQxlDpAmkGoYI0eFzAvrOnjpWTL5FmPDhsdfRMPEA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/amannn" + } + ], + "dependencies": { + "@formatjs/icu-messageformat-parser": "^3.4.0" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3383,6 +3932,16 @@ "node": ">= 0.4" } }, + "node_modules/intl-messageformat": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-11.2.0.tgz", + "integrity": "sha512-IhghAA8n4KSlXuWKzYsWyWb82JoYTzShfyvdSF85oJPnNOjvv4kAo7S7Jtkm3/vJ53C7dQNRO+Gpnj3iWgTjBQ==", + "dependencies": { + "@formatjs/ecma402-abstract": "3.2.0", + "@formatjs/fast-memoize": "3.1.1", + "@formatjs/icu-messageformat-parser": "3.5.3" + } + }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -3535,7 +4094,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3578,7 +4136,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -4091,6 +4648,14 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/next": { "version": "15.5.14", "resolved": "https://registry.npmjs.org/next/-/next-15.5.14.tgz", @@ -4142,6 +4707,91 @@ } } }, + "node_modules/next-intl": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/next-intl/-/next-intl-4.8.3.tgz", + "integrity": "sha512-PvdBDWg+Leh7BR7GJUQbCDVVaBRn37GwDBWc9sv0rVQOJDQ5JU1rVzx9EEGuOGYo0DHAl70++9LQ7HxTawdL7w==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/amannn" + } + ], + "dependencies": { + "@formatjs/intl-localematcher": "^0.8.1", + "@parcel/watcher": "^2.4.1", + "@swc/core": "^1.15.2", + "icu-minify": "^4.8.3", + "negotiator": "^1.0.0", + "next-intl-swc-plugin-extractor": "^4.8.3", + "po-parser": "^2.1.1", + "use-intl": "^4.8.3" + }, + "peerDependencies": { + "next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0", + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/next-intl-swc-plugin-extractor": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/next-intl-swc-plugin-extractor/-/next-intl-swc-plugin-extractor-4.8.3.tgz", + "integrity": "sha512-YcaT+R9z69XkGhpDarVFWUprrCMbxgIQYPUaXoE6LGVnLjGdo8hu3gL6bramDVjNKViYY8a/pXPy7Bna0mXORg==" + }, + "node_modules/next-intl/node_modules/@swc/core": { + "version": "1.15.21", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.21.tgz", + "integrity": "sha512-fkk7NJcBscrR3/F8jiqlMptRHP650NxqDnspBMrRe5d8xOoCy9MLL5kOBLFXjFLfMo3KQQHhk+/jUULOMlR1uQ==", + "hasInstallScript": true, + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.25" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.15.21", + "@swc/core-darwin-x64": "1.15.21", + "@swc/core-linux-arm-gnueabihf": "1.15.21", + "@swc/core-linux-arm64-gnu": "1.15.21", + "@swc/core-linux-arm64-musl": "1.15.21", + "@swc/core-linux-ppc64-gnu": "1.15.21", + "@swc/core-linux-s390x-gnu": "1.15.21", + "@swc/core-linux-x64-gnu": "1.15.21", + "@swc/core-linux-x64-musl": "1.15.21", + "@swc/core-win32-arm64-msvc": "1.15.21", + "@swc/core-win32-ia32-msvc": "1.15.21", + "@swc/core-win32-x64-msvc": "1.15.21" + }, + "peerDependencies": { + "@swc/helpers": ">=0.5.17" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/next-intl/node_modules/@swc/helpers": { + "version": "0.5.20", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.20.tgz", + "integrity": "sha512-2egEBHUMasdypIzrprsu8g+OEVd7Vp2MM3a2eVlM/cyFYto0nGz5BX5BTgh/ShZZI9ed+ozEq+Ngt+rgmUs8tw==", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/next-themes": { "version": "0.4.6", "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", @@ -4178,6 +4828,11 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==" + }, "node_modules/node-exports-info": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", @@ -4479,6 +5134,11 @@ "node": ">= 6" } }, + "node_modules/po-parser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/po-parser/-/po-parser-2.1.1.tgz", + "integrity": "sha512-ECF4zHLbUItpUgE3OTtLKlPjeBN+fKEczj2zYjDfCGOzicNs0GK3Vg2IoAYwx7LH/XYw43fZQP6xnZ4TkNxSLQ==" + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -5595,7 +6255,7 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -5701,6 +6361,26 @@ "punycode": "^2.1.0" } }, + "node_modules/use-intl": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.8.3.tgz", + "integrity": "sha512-nLxlC/RH+le6g3amA508Itnn/00mE+J22ui21QhOWo5V9hCEC43+WtnRAITbJW0ztVZphev5X9gvOf2/Dk9PLA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/amannn" + } + ], + "dependencies": { + "@formatjs/fast-memoize": "^3.1.0", + "@schummar/icu-type-parser": "1.21.5", + "icu-minify": "^4.8.3", + "intl-messageformat": "^11.1.0" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index 102b42b..6aa7b84 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "framer-motion": "^12.38.0", "lucide-react": "^1.7.0", "next": "^15.5.14", + "next-intl": "^4.8.3", "next-themes": "^0.4.6", "react": "19.2.4", "react-dom": "19.2.4" diff --git a/src/app/layout.tsx b/src/app/[locale]/layout.tsx similarity index 63% rename from src/app/layout.tsx rename to src/app/[locale]/layout.tsx index 488d212..7b46b1e 100644 --- a/src/app/layout.tsx +++ b/src/app/[locale]/layout.tsx @@ -1,7 +1,11 @@ import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import { ThemeProvider } from "@/shared/components/theme-provider"; -import "./globals.css"; +import { NextIntlClientProvider } from "next-intl"; +import { getMessages } from "next-intl/server"; +import { notFound } from "next/navigation"; +import { routing } from "@/i18n/routing"; +import "../globals.css"; const geistSans = Geist({ variable: "--font-geist-sans", @@ -35,19 +39,34 @@ export const metadata: Metadata = { }, }; -export default function RootLayout({ +export default async function RootLayout({ children, + params, }: Readonly<{ children: React.ReactNode; + params: Promise<{ locale: string }>; }>) { + const { locale } = await params; + + // Ensure that the incoming `locale` is valid + if (!routing.locales.includes(locale as any)) { + notFound(); + } + + // Providing all messages to the client + // side is the easiest way to get started + const messages = await getMessages(); + return ( - {children} + + {children} + ); diff --git a/src/app/page.tsx b/src/app/[locale]/page.tsx similarity index 100% rename from src/app/page.tsx rename to src/app/[locale]/page.tsx diff --git a/src/features/experience/experience-section.tsx b/src/features/experience/experience-section.tsx index 0b69374..6c7b830 100644 --- a/src/features/experience/experience-section.tsx +++ b/src/features/experience/experience-section.tsx @@ -3,67 +3,58 @@ import { AnimatedSection } from "@/shared/components/animated-section"; import { SectionHeading } from "@/shared/components/section-heading"; import { Briefcase, Award, Rocket, Code2, Building2 } from "lucide-react"; - -interface TimelineItem { - year: string; - title: string; - company: string; - description: string; - achievements: string[]; - icon: React.ReactNode; -} - -const timelineData: TimelineItem[] = [ - { - year: "2024 — Present", - title: "Senior Backend Developer", - company: "Enterprise Banking Corp", - description: - "Leading microservices architecture design and implementation for core banking platform.", - achievements: [ - "Architected event-driven system processing 500K+ transactions/day", - "Reduced API response time by 40% through caching optimization", - "Mentored team of 4 junior developers", - ], - icon: , - }, - { - year: "2023 — 2024", - title: "Backend Developer", - company: "Digital Banking Solutions", - description: - "Developed and maintained Spring Boot microservices for payment processing and customer management.", - achievements: [ - "Built real-time notification service with Apache Kafka", - "Implemented OAuth2 + JWT authentication system", - "Achieved 99.9% uptime on production services", - ], - icon: , - }, - { - year: "2021 — 2023", - title: "Junior Backend Developer", - company: "FinTech Startup", - description: - "Started career building REST APIs and database management for financial applications.", - achievements: [ - "Designed PostgreSQL schema handling 1M+ records", - "Created automated CI/CD pipeline with Jenkins", - "Developed unit & integration test coverage to 85%", - ], - icon: , - }, -]; +import { useTranslations } from "next-intl"; export function ExperienceSection() { + const t = useTranslations("Experience"); + + const timelineData = [ + { + year: t("jobs.enterprise.year"), + title: t("jobs.enterprise.title"), + company: t("jobs.enterprise.company"), + description: t("jobs.enterprise.description"), + achievements: [ + t("jobs.enterprise.achievements.0"), + t("jobs.enterprise.achievements.1"), + t("jobs.enterprise.achievements.2"), + ], + icon: , + }, + { + year: t("jobs.digital.year"), + title: t("jobs.digital.title"), + company: t("jobs.digital.company"), + description: t("jobs.digital.description"), + achievements: [ + t("jobs.digital.achievements.0"), + t("jobs.digital.achievements.1"), + t("jobs.digital.achievements.2"), + ], + icon: , + }, + { + year: t("jobs.fintech.year"), + title: t("jobs.fintech.title"), + company: t("jobs.fintech.company"), + description: t("jobs.fintech.description"), + achievements: [ + t("jobs.fintech.achievements.0"), + t("jobs.fintech.achievements.1"), + t("jobs.fintech.achievements.2"), + ], + icon: , + }, + ]; + return (
diff --git a/src/features/hero/hero-section.tsx b/src/features/hero/hero-section.tsx index e987031..a93a4cc 100644 --- a/src/features/hero/hero-section.tsx +++ b/src/features/hero/hero-section.tsx @@ -2,23 +2,23 @@ import { motion } from "framer-motion"; import { ArrowDown, FileText, Send } from "lucide-react"; +import { useTranslations } from "next-intl"; export function HeroSection() { + const t = useTranslations("Hero"); + return (
- {/* Background effects */}
- {/* Gradient orbs */}
- {/* Status badge */} - Available for opportunities + {t("badge")} - {/* Main heading */} - Building{" "} - Secure, Scalable + {t("titlePart1")}{" "} + {t("titleHighlight")}
- Enterprise-Grade Systems + {t("titlePart2")}
- {/* Subtitle */} - 3+ Years{" "} - in Banking Technology.{" "} - Backend Developer specializing in - Java Spring Boot, Microservices Architecture, and Enterprise Security. + {t("yearsExp")}{" "} + {t("subtitle")} - {/* CTA buttons */} - Get in Touch + {t("ctaContact")} - View Projects + {t("ctaProjects")} - {/* Tech badges */}
- {/* Scroll indicator */} - Scroll + {t("scroll")} ("idle"); const [formData, setFormData] = useState({ name: "", @@ -34,9 +36,9 @@ export function ContactSection() {
@@ -49,9 +51,9 @@ export function ContactSection() {
-

Message Sent!

+

{t("form.successTitle")}

- Thank you for reaching out. I'll get back to you soon. + {t("form.successDesc")}

@@ -66,7 +68,7 @@ export function ContactSection() { className="flex items-center gap-2 text-sm font-medium" > - Full Name + {t("form.nameLabel")} setFormData((prev) => ({ ...prev, name: e.target.value })) } - placeholder="John Doe" + placeholder={t("form.namePlaceholder")} className="w-full px-4 py-3 rounded-xl bg-muted/50 border border-border/50 text-sm placeholder:text-muted-foreground/50 focus:outline-none focus:ring-2 focus:ring-accent/50 focus:border-accent/50 transition-all" />
@@ -88,7 +90,7 @@ export function ContactSection() { className="flex items-center gap-2 text-sm font-medium" > - Email Address + {t("form.emailLabel")} setFormData((prev) => ({ ...prev, email: e.target.value })) } - placeholder="john@company.com" + placeholder={t("form.emailPlaceholder")} className="w-full px-4 py-3 rounded-xl bg-muted/50 border border-border/50 text-sm placeholder:text-muted-foreground/50 focus:outline-none focus:ring-2 focus:ring-accent/50 focus:border-accent/50 transition-all" />
@@ -111,7 +113,7 @@ export function ContactSection() { className="flex items-center gap-2 text-sm font-medium" > - Message + {t("form.messageLabel")}