diff --git a/client/src/components/LanguageSwitcher.tsx b/client/src/components/LanguageSwitcher.tsx
new file mode 100644
index 0000000..ba6155c
--- /dev/null
+++ b/client/src/components/LanguageSwitcher.tsx
@@ -0,0 +1,50 @@
+import { useTranslation } from 'react-i18next';
+import { Button } from '@/components/ui/button';
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from '@/components/ui/dropdown-menu';
+import { Languages } from 'lucide-react';
+
+export function LanguageSwitcher() {
+ const { i18n } = useTranslation();
+
+ const languages = [
+ { code: 'en', name: 'English', flag: '🇺🇸' },
+ { code: 'ar', name: 'العربية', flag: '🇸🇦' }
+ ];
+
+ const changeLanguage = (langCode: string) => {
+ i18n.changeLanguage(langCode);
+ // Update document direction for Arabic
+ document.documentElement.dir = langCode === 'ar' ? 'rtl' : 'ltr';
+ document.documentElement.lang = langCode;
+ };
+
+ const currentLanguage = languages.find(lang => lang.code === i18n.language) || languages[0];
+
+ return (
+
+
+
+
+
+ {languages.map((language) => (
+ changeLanguage(language.code)}
+ className={`gap-2 ${i18n.language === language.code ? 'bg-accent' : ''}`}
+ >
+ {language.flag}
+ {language.name}
+
+ ))}
+
+
+ );
+}
\ No newline at end of file
diff --git a/client/src/i18n/config.ts b/client/src/i18n/config.ts
new file mode 100644
index 0000000..5bb6f11
--- /dev/null
+++ b/client/src/i18n/config.ts
@@ -0,0 +1,36 @@
+import i18n from 'i18next';
+import { initReactI18next } from 'react-i18next';
+import LanguageDetector from 'i18next-browser-languagedetector';
+
+// Import translation files
+import enTranslations from './locales/en.json';
+import arTranslations from './locales/ar.json';
+
+i18n
+ .use(LanguageDetector)
+ .use(initReactI18next)
+ .init({
+ resources: {
+ en: {
+ translation: enTranslations
+ },
+ ar: {
+ translation: arTranslations
+ }
+ },
+ lng: 'en', // default language
+ fallbackLng: 'en',
+ debug: false,
+
+ interpolation: {
+ escapeValue: false, // React already does escaping
+ },
+
+ detection: {
+ order: ['localStorage', 'navigator', 'htmlTag'],
+ lookupLocalStorage: 'language',
+ caches: ['localStorage'],
+ },
+ });
+
+export default i18n;
\ No newline at end of file
diff --git a/client/src/i18n/locales/ar.json b/client/src/i18n/locales/ar.json
new file mode 100644
index 0000000..7e1de16
--- /dev/null
+++ b/client/src/i18n/locales/ar.json
@@ -0,0 +1,199 @@
+{
+ "common": {
+ "save": "حفظ",
+ "cancel": "إلغاء",
+ "delete": "حذف",
+ "edit": "تعديل",
+ "add": "إضافة",
+ "create": "إنشاء",
+ "update": "تحديث",
+ "search": "بحث",
+ "filter": "تصفية",
+ "loading": "جاري التحميل...",
+ "error": "خطأ",
+ "success": "نجح",
+ "confirm": "تأكيد",
+ "back": "رجوع",
+ "next": "التالي",
+ "previous": "السابق",
+ "close": "إغلاق",
+ "open": "فتح",
+ "yes": "نعم",
+ "no": "لا"
+ },
+ "navigation": {
+ "dashboard": "لوحة التحكم",
+ "tasks": "المهام",
+ "finances": "الماليات",
+ "ai": "المساعد الذكي",
+ "voice": "الصوت",
+ "analytics": "التحليلات",
+ "settings": "الإعدادات",
+ "logout": "تسجيل الخروج",
+ "profile": "الملف الشخصي"
+ },
+ "tasks": {
+ "title": "المهام",
+ "createTask": "إنشاء مهمة",
+ "editTask": "تعديل المهمة",
+ "taskTitle": "عنوان المهمة",
+ "description": "الوصف",
+ "priority": "الأولوية",
+ "dueDate": "تاريخ الاستحقاق",
+ "status": "الحالة",
+ "category": "الفئة",
+ "project": "المشروع",
+ "assignee": "المكلف",
+ "tags": "العلامات",
+ "subtasks": "المهام الفرعية",
+ "attachments": "المرفقات",
+ "timeTracking": "تتبع الوقت",
+ "completed": "مكتمل",
+ "inProgress": "قيد التنفيذ",
+ "todo": "للعمل",
+ "high": "عالية",
+ "medium": "متوسطة",
+ "low": "منخفضة",
+ "addSubtask": "إضافة مهمة فرعية",
+ "addAttachment": "إضافة مرفق",
+ "startTimer": "بدء المؤقت",
+ "stopTimer": "إيقاف المؤقت",
+ "totalTime": "إجمالي الوقت",
+ "noTasks": "لا توجد مهام",
+ "taskCreated": "تم إنشاء المهمة بنجاح",
+ "taskUpdated": "تم تحديث المهمة بنجاح",
+ "taskDeleted": "تم حذف المهمة بنجاح"
+ },
+ "finances": {
+ "title": "الماليات",
+ "overview": "نظرة عامة مالية",
+ "income": "الدخل",
+ "expenses": "المصروفات",
+ "balance": "الرصيد",
+ "budget": "الميزانية",
+ "categories": "الفئات",
+ "transactions": "المعاملات",
+ "recurring": "متكررة",
+ "reports": "التقارير",
+ "addTransaction": "إضافة معاملة",
+ "editTransaction": "تعديل المعاملة",
+ "amount": "المبلغ",
+ "type": "النوع",
+ "date": "التاريخ",
+ "description": "الوصف",
+ "category": "الفئة",
+ "tags": "العلامات",
+ "recurring": "متكررة",
+ "frequency": "التكرار",
+ "budgetPlanning": "تخطيط الميزانية",
+ "budgetLimit": "حد الميزانية",
+ "budgetUsed": "الميزانية المستخدمة",
+ "budgetRemaining": "الميزانية المتبقية",
+ "monthlyBudget": "الميزانية الشهرية",
+ "yearlyBudget": "الميزانية السنوية",
+ "exportData": "تصدير البيانات",
+ "billReminders": "تذكيرات الفواتير",
+ "upcomingBills": "الفواتير القادمة",
+ "overdueBills": "الفواتير المتأخرة"
+ },
+ "ai": {
+ "title": "المساعد الذكي",
+ "chat": "المحادثة",
+ "history": "تاريخ المحادثات",
+ "dailyJoke": "نكتة اليوم",
+ "insights": "رؤى ذكية",
+ "askAnything": "اسأل عن أي شيء...",
+ "generating": "جاري إنشاء الرد...",
+ "voiceCommand": "أمر صوتي",
+ "personalizedResponse": "رد شخصي",
+ "conversationSaved": "تم حفظ المحادثة",
+ "noHistory": "لا يوجد تاريخ محادثات"
+ },
+ "voice": {
+ "title": "الأوامر الصوتية",
+ "listening": "جاري الاستماع...",
+ "notListening": "غير مفعل",
+ "startListening": "بدء الاستماع",
+ "stopListening": "إيقاف الاستماع",
+ "voiceSettings": "إعدادات الصوت",
+ "language": "اللغة",
+ "microphone": "الميكروفون",
+ "speakers": "السماعات",
+ "voiceCommands": "الأوامر المتاحة",
+ "customCommands": "أوامر مخصصة",
+ "voiceTraining": "تدريب الصوت",
+ "accuracy": "الدقة",
+ "confidence": "الثقة",
+ "transcript": "النص المكتوب",
+ "voiceActivated": "مفعل صوتياً",
+ "speakingRate": "معدل الكلام",
+ "voicePitch": "نبرة الصوت",
+ "voiceVolume": "مستوى الصوت"
+ },
+ "settings": {
+ "title": "الإعدادات",
+ "general": "عام",
+ "appearance": "المظهر",
+ "language": "اللغة",
+ "notifications": "الإشعارات",
+ "privacy": "الخصوصية",
+ "account": "الحساب",
+ "advanced": "متقدم",
+ "theme": "السمة",
+ "lightMode": "الوضع الفاتح",
+ "darkMode": "الوضع الداكن",
+ "systemTheme": "سمة النظام",
+ "fontSize": "حجم الخط",
+ "accessibility": "إمكانية الوصول",
+ "dataExport": "تصدير البيانات",
+ "dataImport": "استيراد البيانات",
+ "changePassword": "تغيير كلمة المرور",
+ "deleteAccount": "حذف الحساب",
+ "backup": "نسخ احتياطي",
+ "restore": "استعادة"
+ },
+ "auth": {
+ "login": "تسجيل الدخول",
+ "register": "تسجيل جديد",
+ "logout": "تسجيل الخروج",
+ "email": "البريد الإلكتروني",
+ "password": "كلمة المرور",
+ "confirmPassword": "تأكيد كلمة المرور",
+ "firstName": "الاسم الأول",
+ "lastName": "الاسم الأخير",
+ "username": "اسم المستخدم",
+ "forgotPassword": "نسيت كلمة المرور؟",
+ "resetPassword": "إعادة تعيين كلمة المرور",
+ "loginSuccess": "تم تسجيل الدخول بنجاح",
+ "registerSuccess": "تم التسجيل بنجاح",
+ "invalidCredentials": "بيانات اعتماد غير صحيحة",
+ "passwordMismatch": "كلمات المرور غير متطابقة"
+ },
+ "dashboard": {
+ "title": "لوحة التحكم",
+ "welcome": "مرحباً",
+ "quickStats": "إحصائيات سريعة",
+ "recentTasks": "المهام الأخيرة",
+ "recentTransactions": "المعاملات الأخيرة",
+ "todaysTasks": "مهام اليوم",
+ "weeklyOverview": "نظرة أسبوعية",
+ "monthlyOverview": "نظرة شهرية",
+ "productivity": "الإنتاجية",
+ "financial": "الملخص المالي"
+ },
+ "errors": {
+ "general": "حدث خطأ",
+ "networkError": "خطأ في الشبكة",
+ "unauthorized": "وصول غير مصرح",
+ "notFound": "غير موجود",
+ "serverError": "خطأ في الخادم",
+ "validationError": "خطأ في التحقق",
+ "tryAgain": "حاول مرة أخرى"
+ },
+ "success": {
+ "saved": "تم الحفظ بنجاح",
+ "deleted": "تم الحذف بنجاح",
+ "updated": "تم التحديث بنجاح",
+ "created": "تم الإنشاء بنجاح"
+ }
+}
\ No newline at end of file
diff --git a/client/src/i18n/locales/en.json b/client/src/i18n/locales/en.json
new file mode 100644
index 0000000..4d77e24
--- /dev/null
+++ b/client/src/i18n/locales/en.json
@@ -0,0 +1,199 @@
+{
+ "common": {
+ "save": "Save",
+ "cancel": "Cancel",
+ "delete": "Delete",
+ "edit": "Edit",
+ "add": "Add",
+ "create": "Create",
+ "update": "Update",
+ "search": "Search",
+ "filter": "Filter",
+ "loading": "Loading...",
+ "error": "Error",
+ "success": "Success",
+ "confirm": "Confirm",
+ "back": "Back",
+ "next": "Next",
+ "previous": "Previous",
+ "close": "Close",
+ "open": "Open",
+ "yes": "Yes",
+ "no": "No"
+ },
+ "navigation": {
+ "dashboard": "Dashboard",
+ "tasks": "Tasks",
+ "finances": "Finances",
+ "ai": "AI Assistant",
+ "voice": "Voice",
+ "analytics": "Analytics",
+ "settings": "Settings",
+ "logout": "Logout",
+ "profile": "Profile"
+ },
+ "tasks": {
+ "title": "Tasks",
+ "createTask": "Create Task",
+ "editTask": "Edit Task",
+ "taskTitle": "Task Title",
+ "description": "Description",
+ "priority": "Priority",
+ "dueDate": "Due Date",
+ "status": "Status",
+ "category": "Category",
+ "project": "Project",
+ "assignee": "Assignee",
+ "tags": "Tags",
+ "subtasks": "Subtasks",
+ "attachments": "Attachments",
+ "timeTracking": "Time Tracking",
+ "completed": "Completed",
+ "inProgress": "In Progress",
+ "todo": "To Do",
+ "high": "High",
+ "medium": "Medium",
+ "low": "Low",
+ "addSubtask": "Add Subtask",
+ "addAttachment": "Add Attachment",
+ "startTimer": "Start Timer",
+ "stopTimer": "Stop Timer",
+ "totalTime": "Total Time",
+ "noTasks": "No tasks found",
+ "taskCreated": "Task created successfully",
+ "taskUpdated": "Task updated successfully",
+ "taskDeleted": "Task deleted successfully"
+ },
+ "finances": {
+ "title": "Finances",
+ "overview": "Financial Overview",
+ "income": "Income",
+ "expenses": "Expenses",
+ "balance": "Balance",
+ "budget": "Budget",
+ "categories": "Categories",
+ "transactions": "Transactions",
+ "recurring": "Recurring",
+ "reports": "Reports",
+ "addTransaction": "Add Transaction",
+ "editTransaction": "Edit Transaction",
+ "amount": "Amount",
+ "type": "Type",
+ "date": "Date",
+ "description": "Description",
+ "category": "Category",
+ "tags": "Tags",
+ "recurring": "Recurring",
+ "frequency": "Frequency",
+ "budgetPlanning": "Budget Planning",
+ "budgetLimit": "Budget Limit",
+ "budgetUsed": "Budget Used",
+ "budgetRemaining": "Budget Remaining",
+ "monthlyBudget": "Monthly Budget",
+ "yearlyBudget": "Yearly Budget",
+ "exportData": "Export Data",
+ "billReminders": "Bill Reminders",
+ "upcomingBills": "Upcoming Bills",
+ "overdueBills": "Overdue Bills"
+ },
+ "ai": {
+ "title": "AI Assistant",
+ "chat": "Chat",
+ "history": "Conversation History",
+ "dailyJoke": "Daily Joke",
+ "insights": "AI Insights",
+ "askAnything": "Ask me anything...",
+ "generating": "Generating response...",
+ "voiceCommand": "Voice Command",
+ "personalizedResponse": "Personalized Response",
+ "conversationSaved": "Conversation saved",
+ "noHistory": "No conversation history"
+ },
+ "voice": {
+ "title": "Voice Commands",
+ "listening": "Listening...",
+ "notListening": "Not Listening",
+ "startListening": "Start Listening",
+ "stopListening": "Stop Listening",
+ "voiceSettings": "Voice Settings",
+ "language": "Language",
+ "microphone": "Microphone",
+ "speakers": "Speakers",
+ "voiceCommands": "Available Commands",
+ "customCommands": "Custom Commands",
+ "voiceTraining": "Voice Training",
+ "accuracy": "Accuracy",
+ "confidence": "Confidence",
+ "transcript": "Transcript",
+ "voiceActivated": "Voice Activated",
+ "speakingRate": "Speaking Rate",
+ "voicePitch": "Voice Pitch",
+ "voiceVolume": "Voice Volume"
+ },
+ "settings": {
+ "title": "Settings",
+ "general": "General",
+ "appearance": "Appearance",
+ "language": "Language",
+ "notifications": "Notifications",
+ "privacy": "Privacy",
+ "account": "Account",
+ "advanced": "Advanced",
+ "theme": "Theme",
+ "lightMode": "Light Mode",
+ "darkMode": "Dark Mode",
+ "systemTheme": "System Theme",
+ "fontSize": "Font Size",
+ "accessibility": "Accessibility",
+ "dataExport": "Data Export",
+ "dataImport": "Data Import",
+ "changePassword": "Change Password",
+ "deleteAccount": "Delete Account",
+ "backup": "Backup",
+ "restore": "Restore"
+ },
+ "auth": {
+ "login": "Login",
+ "register": "Register",
+ "logout": "Logout",
+ "email": "Email",
+ "password": "Password",
+ "confirmPassword": "Confirm Password",
+ "firstName": "First Name",
+ "lastName": "Last Name",
+ "username": "Username",
+ "forgotPassword": "Forgot Password?",
+ "resetPassword": "Reset Password",
+ "loginSuccess": "Login successful",
+ "registerSuccess": "Registration successful",
+ "invalidCredentials": "Invalid credentials",
+ "passwordMismatch": "Passwords do not match"
+ },
+ "dashboard": {
+ "title": "Dashboard",
+ "welcome": "Welcome",
+ "quickStats": "Quick Stats",
+ "recentTasks": "Recent Tasks",
+ "recentTransactions": "Recent Transactions",
+ "todaysTasks": "Today's Tasks",
+ "weeklyOverview": "Weekly Overview",
+ "monthlyOverview": "Monthly Overview",
+ "productivity": "Productivity",
+ "financial": "Financial Summary"
+ },
+ "errors": {
+ "general": "An error occurred",
+ "networkError": "Network error",
+ "unauthorized": "Unauthorized access",
+ "notFound": "Not found",
+ "serverError": "Server error",
+ "validationError": "Validation error",
+ "tryAgain": "Please try again"
+ },
+ "success": {
+ "saved": "Successfully saved",
+ "deleted": "Successfully deleted",
+ "updated": "Successfully updated",
+ "created": "Successfully created"
+ }
+}
\ No newline at end of file
diff --git a/client/src/main.tsx b/client/src/main.tsx
index 696e0d2..e77c81c 100644
--- a/client/src/main.tsx
+++ b/client/src/main.tsx
@@ -1,5 +1,6 @@
import { createRoot } from "react-dom/client";
import App from "./App";
import "./index.css";
+import "./i18n/config";
createRoot(document.getElementById("root")!).render();
diff --git a/package-lock.json b/package-lock.json
index 5fef6db..afbadf4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -53,6 +53,9 @@
"express": "^4.21.2",
"express-session": "^1.18.1",
"framer-motion": "^11.13.1",
+ "i18next": "^25.2.1",
+ "i18next-browser-languagedetector": "^8.1.0",
+ "i18next-http-backend": "^3.0.2",
"input-otp": "^1.4.2",
"lucide-react": "^0.453.0",
"memorystore": "^1.6.7",
@@ -66,6 +69,7 @@
"react-day-picker": "^8.10.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.55.0",
+ "react-i18next": "^15.5.2",
"react-icons": "^5.4.0",
"react-resizable-panels": "^2.1.7",
"recharts": "^2.15.2",
@@ -385,12 +389,10 @@
}
},
"node_modules/@babel/runtime": {
- "version": "7.27.0",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz",
- "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==",
- "dependencies": {
- "regenerator-runtime": "^0.14.0"
- },
+ "version": "7.27.6",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz",
+ "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==",
+ "license": "MIT",
"engines": {
"node": ">=6.9.0"
}
@@ -4225,6 +4227,15 @@
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
"license": "MIT"
},
+ "node_modules/cross-fetch": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
+ "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==",
+ "license": "MIT",
+ "dependencies": {
+ "node-fetch": "^2.6.12"
+ }
+ },
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -5808,6 +5819,15 @@
"node": ">= 0.4"
}
},
+ "node_modules/html-parse-stringify": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
+ "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==",
+ "license": "MIT",
+ "dependencies": {
+ "void-elements": "3.1.0"
+ }
+ },
"node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
@@ -5833,6 +5853,55 @@
"ms": "^2.0.0"
}
},
+ "node_modules/i18next": {
+ "version": "25.2.1",
+ "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.2.1.tgz",
+ "integrity": "sha512-+UoXK5wh+VlE1Zy5p6MjcvctHXAhRwQKCxiJD8noKZzIXmnAX8gdHX5fLPA3MEVxEN4vbZkQFy8N0LyD9tUqPw==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://locize.com"
+ },
+ {
+ "type": "individual",
+ "url": "https://locize.com/i18next.html"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.27.1"
+ },
+ "peerDependencies": {
+ "typescript": "^5"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/i18next-browser-languagedetector": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.1.0.tgz",
+ "integrity": "sha512-mHZxNx1Lq09xt5kCauZ/4bsXOEA2pfpwSoU11/QTJB+pD94iONFwp+ohqi///PwiFvjFOxe1akYCdHyFo1ng5Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.23.2"
+ }
+ },
+ "node_modules/i18next-http-backend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-3.0.2.tgz",
+ "integrity": "sha512-PdlvPnvIp4E1sYi46Ik4tBYh/v/NbYfFFgTjkwFl0is8A18s7/bx9aXqsrOax9WUbeNS6mD2oix7Z0yGGf6m5g==",
+ "license": "MIT",
+ "dependencies": {
+ "cross-fetch": "4.0.0"
+ }
+ },
"node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -7395,6 +7464,32 @@
"react": "^16.8.0 || ^17 || ^18 || ^19"
}
},
+ "node_modules/react-i18next": {
+ "version": "15.5.2",
+ "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.5.2.tgz",
+ "integrity": "sha512-ePODyXgmZQAOYTbZXQn5rRsSBu3Gszo69jxW6aKmlSgxKAI1fOhDwSu6bT4EKHciWPKQ7v7lPrjeiadR6Gi+1A==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.25.0",
+ "html-parse-stringify": "^3.0.1"
+ },
+ "peerDependencies": {
+ "i18next": ">= 23.2.3",
+ "react": ">= 16.8.0",
+ "typescript": "^5"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ },
+ "react-native": {
+ "optional": true
+ },
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
"node_modules/react-icons": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.4.0.tgz",
@@ -7597,11 +7692,6 @@
"decimal.js-light": "^2.4.1"
}
},
- "node_modules/regenerator-runtime": {
- "version": "0.14.1",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
- "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
- },
"node_modules/regexparam": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/regexparam/-/regexparam-3.0.0.tgz",
@@ -8719,7 +8809,7 @@
"version": "5.6.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz",
"integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
@@ -9385,6 +9475,15 @@
"@esbuild/win32-x64": "0.21.5"
}
},
+ "node_modules/void-elements": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
+ "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/web-streams-polyfill": {
"version": "4.0.0-beta.3",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz",
diff --git a/package.json b/package.json
index 47f9576..ec420f8 100644
--- a/package.json
+++ b/package.json
@@ -55,6 +55,9 @@
"express": "^4.21.2",
"express-session": "^1.18.1",
"framer-motion": "^11.13.1",
+ "i18next": "^25.2.1",
+ "i18next-browser-languagedetector": "^8.1.0",
+ "i18next-http-backend": "^3.0.2",
"input-otp": "^1.4.2",
"lucide-react": "^0.453.0",
"memorystore": "^1.6.7",
@@ -68,6 +71,7 @@
"react-day-picker": "^8.10.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.55.0",
+ "react-i18next": "^15.5.2",
"react-icons": "^5.4.0",
"react-resizable-panels": "^2.1.7",
"recharts": "^2.15.2",