diff --git a/maternal-web/app/(auth)/login/page.tsx b/maternal-web/app/(auth)/login/page.tsx index 3ed2e7f..e627cc0 100644 --- a/maternal-web/app/(auth)/login/page.tsx +++ b/maternal-web/app/(auth)/login/page.tsx @@ -26,6 +26,7 @@ import { tokenStorage } from '@/lib/utils/tokenStorage'; import { biometricApi } from '@/lib/api/biometric'; import { startAuthentication } from '@simplewebauthn/browser'; import Link from 'next/link'; +import { useTranslation } from '@/hooks/useTranslation'; const loginSchema = z.object({ email: z.string().email('Invalid email address'), @@ -35,6 +36,7 @@ const loginSchema = z.object({ type LoginFormData = z.infer; export default function LoginPage() { + const { t } = useTranslation('auth'); const [showPassword, setShowPassword] = useState(false); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(false); @@ -178,7 +180,7 @@ export default function LoginPage() { fontWeight="600" color="primary.main" > - Welcome Back 👋 + {t('login.title')} - Sign in to continue tracking your child's journey + {t('login.subtitle')} {error && ( @@ -198,7 +200,7 @@ export default function LoginPage() { - Forgot password? + {t('login.forgotPassword')} @@ -260,14 +262,14 @@ export default function LoginPage() { {isLoading ? ( ) : ( - 'Sign In' + t('login.submit') )} - OR + {t('login.or')} @@ -279,7 +281,7 @@ export default function LoginPage() { disabled={isLoading} sx={{ mb: 2 }} > - Continue with Google + {t('login.continueWithGoogle')} {isBiometricSupported && ( @@ -302,16 +304,16 @@ export default function LoginPage() { onClick={handleBiometricLogin} sx={{ mt: 2 }} > - {isBiometricLoading ? 'Authenticating...' : 'Sign in with Biometrics'} + {isBiometricLoading ? t('login.biometric.authenticating') : t('login.biometric.useFaceId')} )} - Don't have an account?{' '} + {t('login.noAccount')}{' '} - Sign up + {t('login.signUp')} diff --git a/maternal-web/app/page.tsx b/maternal-web/app/page.tsx index 803643c..f593874 100644 --- a/maternal-web/app/page.tsx +++ b/maternal-web/app/page.tsx @@ -25,8 +25,10 @@ import { useQuery } from '@apollo/client/react'; import { GET_DASHBOARD } from '@/graphql/queries/dashboard'; import { format } from 'date-fns'; import { useRealTimeActivities } from '@/hooks/useWebSocket'; +import { useTranslation } from '@/hooks/useTranslation'; export default function HomePage() { + const { t } = useTranslation('dashboard'); const { user, isLoading: authLoading } = useAuth(); const router = useRouter(); const [selectedChildId, setSelectedChildId] = useState(null); @@ -93,12 +95,12 @@ export default function HomePage() { }, [data, selectedChildId]); const quickActions = [ - { icon: , label: 'Feeding', color: '#E91E63', path: '/track/feeding' }, // Pink with 4.5:1 contrast - { icon: , label: 'Sleep', color: '#1976D2', path: '/track/sleep' }, // Blue with 4.5:1 contrast - { icon: , label: 'Diaper', color: '#F57C00', path: '/track/diaper' }, // Orange with 4.5:1 contrast - { icon: , label: 'Medicine', color: '#C62828', path: '/track/medication' }, // Red with 4.5:1 contrast - { icon: , label: 'Activities', color: '#558B2F', path: '/activities' }, // Green with 4.5:1 contrast - { icon: , label: 'AI Assistant', color: '#D84315', path: '/ai-assistant' }, // Deep orange with 4.5:1 contrast + { icon: , label: t('quickActions.feeding'), color: '#E91E63', path: '/track/feeding' }, // Pink with 4.5:1 contrast + { icon: , label: t('quickActions.sleep'), color: '#1976D2', path: '/track/sleep' }, // Blue with 4.5:1 contrast + { icon: , label: t('quickActions.diaper'), color: '#F57C00', path: '/track/diaper' }, // Orange with 4.5:1 contrast + { icon: , label: t('quickActions.medicine'), color: '#C62828', path: '/track/medication' }, // Red with 4.5:1 contrast + { icon: , label: t('quickActions.activities'), color: '#558B2F', path: '/activities' }, // Green with 4.5:1 contrast + { icon: , label: t('quickActions.aiAssistant'), color: '#D84315', path: '/ai-assistant' }, // Deep orange with 4.5:1 contrast ]; const formatSleepHours = (minutes: number) => { @@ -132,15 +134,15 @@ export default function HomePage() { transition={{ duration: 0.5 }} > - Welcome Back{user?.name ? `, ${user.name}` : ''}! 👋 + {user?.name ? t('welcomeBackWithName', { name: user.name }) : t('welcomeBack')} 👋 - Track your child's activities and get AI-powered insights + {t('subtitle')} {/* Quick Actions */} - Quick Actions + {t('quickActions.title')} {quickActions.map((action, index) => ( @@ -160,7 +162,7 @@ export default function HomePage() { router.push(action.path); } }} - aria-label={`Navigate to ${action.label}`} + aria-label={t('quickActions.navigateTo', { action: action.label })} sx={{ p: 3, height: '100%', @@ -196,7 +198,7 @@ export default function HomePage() { {/* Today's Summary */} - Today's Summary{selectedChild ? ` - ${selectedChild.name}` : ''} + {selectedChild ? t('summary.titleWithChild', { childName: selectedChild.name }) : t('summary.title')} {children.length === 0 - ? 'Add a child to start tracking' - : 'No activities tracked today'} + ? t('summary.noChild') + : t('summary.noActivities')} ) : ( @@ -232,7 +234,7 @@ export default function HomePage() { {dailySummary.feedingCount || 0} - Feedings + {t('summary.feedings')} @@ -254,7 +256,7 @@ export default function HomePage() { : '0m'} - Sleep + {t('summary.sleep')} @@ -274,7 +276,7 @@ export default function HomePage() { {dailySummary.diaperCount || 0} - Diapers + {t('summary.diapers')} @@ -294,7 +296,7 @@ export default function HomePage() { {dailySummary.medicationCount || 0} - Medications + {t('summary.medications')} @@ -308,13 +310,13 @@ export default function HomePage() { - Next Predicted Activity + {t('predictions.title')} - Nap time in 45 minutes + {t('predictions.napTime', { minutes: 45 })} - Based on your child's sleep patterns + {t('predictions.basedOnPatterns')} diff --git a/maternal-web/components/layouts/AppShell/AppShell.tsx b/maternal-web/components/layouts/AppShell/AppShell.tsx index e9ff14f..493d8d5 100644 --- a/maternal-web/components/layouts/AppShell/AppShell.tsx +++ b/maternal-web/components/layouts/AppShell/AppShell.tsx @@ -7,12 +7,14 @@ import { useMediaQuery } from '@/hooks/useMediaQuery'; import { ReactNode } from 'react'; import { useWebSocket } from '@/hooks/useWebSocket'; import { Wifi, WifiOff, People } from '@mui/icons-material'; +import { useTranslation } from '@/hooks/useTranslation'; interface AppShellProps { children: ReactNode; } export const AppShell = ({ children }: AppShellProps) => { + const { t } = useTranslation('common'); const isMobile = useMediaQuery('(max-width: 768px)'); const isTablet = useMediaQuery('(max-width: 1024px)'); const { isConnected, presence } = useWebSocket(); @@ -38,10 +40,10 @@ export const AppShell = ({ children }: AppShellProps) => { gap: 1, }} > - + : } - label={isConnected ? 'Live' : 'Offline'} + label={isConnected ? t('connection.live') : t('connection.offline')} size="small" color={isConnected ? 'success' : 'default'} sx={{ @@ -52,7 +54,7 @@ export const AppShell = ({ children }: AppShellProps) => { {isConnected && presence.count > 1 && ( - + } label={presence.count} diff --git a/maternal-web/components/layouts/MobileNav/MobileNav.tsx b/maternal-web/components/layouts/MobileNav/MobileNav.tsx index ead057c..13dd85e 100644 --- a/maternal-web/components/layouts/MobileNav/MobileNav.tsx +++ b/maternal-web/components/layouts/MobileNav/MobileNav.tsx @@ -28,19 +28,21 @@ import { Group, Logout, } from '@mui/icons-material'; +import { useTranslation } from '@/hooks/useTranslation'; export const MobileNav = () => { + const { t } = useTranslation('common'); const [drawerOpen, setDrawerOpen] = useState(false); const router = useRouter(); const menuItems = [ - { label: 'Dashboard', icon: , path: '/' }, - { label: 'Track Activity', icon: , path: '/track' }, - { label: 'AI Assistant', icon: , path: '/ai-assistant' }, - { label: 'Insights', icon: , path: '/insights' }, - { label: 'Children', icon: , path: '/children' }, - { label: 'Family', icon: , path: '/family' }, - { label: 'Settings', icon: , path: '/settings' }, + { label: t('navigation.dashboard'), icon: , path: '/' }, + { label: t('navigation.trackActivity'), icon: , path: '/track' }, + { label: t('navigation.ai'), icon: , path: '/ai-assistant' }, + { label: t('navigation.insights'), icon: , path: '/insights' }, + { label: t('navigation.children'), icon: , path: '/children' }, + { label: t('navigation.family'), icon: , path: '/family' }, + { label: t('navigation.settings'), icon: , path: '/settings' }, ]; const handleNavigate = (path: string) => { @@ -61,7 +63,7 @@ export const MobileNav = () => { - Maternal + {t('appName')} { - + diff --git a/maternal-web/components/layouts/TabBar/TabBar.tsx b/maternal-web/components/layouts/TabBar/TabBar.tsx index 79f9c3e..34a5e45 100644 --- a/maternal-web/components/layouts/TabBar/TabBar.tsx +++ b/maternal-web/components/layouts/TabBar/TabBar.tsx @@ -9,17 +9,19 @@ import { Insights, Settings, } from '@mui/icons-material'; +import { useTranslation } from '@/hooks/useTranslation'; export const TabBar = () => { + const { t } = useTranslation('common'); const router = useRouter(); const pathname = usePathname(); const tabs = [ - { label: 'Home', icon: , value: '/' }, - { label: 'Track', icon: , value: '/track' }, - { label: 'AI Chat', icon: , value: '/ai-assistant' }, - { label: 'Insights', icon: , value: '/insights' }, - { label: 'Settings', icon: , value: '/settings' }, + { label: t('navigation.home'), icon: , value: '/' }, + { label: t('navigation.track'), icon: , value: '/track' }, + { label: t('navigation.aiChat'), icon: , value: '/ai-assistant' }, + { label: t('navigation.insights'), icon: , value: '/insights' }, + { label: t('navigation.settings'), icon: , value: '/settings' }, ]; return ( diff --git a/maternal-web/lib/i18n/config.ts b/maternal-web/lib/i18n/config.ts index 9dd8db7..faba2c8 100644 --- a/maternal-web/lib/i18n/config.ts +++ b/maternal-web/lib/i18n/config.ts @@ -4,6 +4,7 @@ import LanguageDetector from 'i18next-browser-languagedetector'; // Import translation files import enCommon from '@/locales/en/common.json'; +import enDashboard from '@/locales/en/dashboard.json'; import enTracking from '@/locales/en/tracking.json'; import enAi from '@/locales/en/ai.json'; import enAuth from '@/locales/en/auth.json'; @@ -12,6 +13,7 @@ import enOnboarding from '@/locales/en/onboarding.json'; import enErrors from '@/locales/en/errors.json'; import esCommon from '@/locales/es/common.json'; +import esDashboard from '@/locales/es/dashboard.json'; import esTracking from '@/locales/es/tracking.json'; import esAi from '@/locales/es/ai.json'; import esAuth from '@/locales/es/auth.json'; @@ -20,6 +22,7 @@ import esOnboarding from '@/locales/es/onboarding.json'; import esErrors from '@/locales/es/errors.json'; import frCommon from '@/locales/fr/common.json'; +import frDashboard from '@/locales/fr/dashboard.json'; import frTracking from '@/locales/fr/tracking.json'; import frAi from '@/locales/fr/ai.json'; import frAuth from '@/locales/fr/auth.json'; @@ -28,6 +31,7 @@ import frOnboarding from '@/locales/fr/onboarding.json'; import frErrors from '@/locales/fr/errors.json'; import ptCommon from '@/locales/pt/common.json'; +import ptDashboard from '@/locales/pt/dashboard.json'; import ptTracking from '@/locales/pt/tracking.json'; import ptAi from '@/locales/pt/ai.json'; import ptAuth from '@/locales/pt/auth.json'; @@ -36,6 +40,7 @@ import ptOnboarding from '@/locales/pt/onboarding.json'; import ptErrors from '@/locales/pt/errors.json'; import zhCommon from '@/locales/zh/common.json'; +import zhDashboard from '@/locales/zh/dashboard.json'; import zhTracking from '@/locales/zh/tracking.json'; import zhAi from '@/locales/zh/ai.json'; import zhAuth from '@/locales/zh/auth.json'; @@ -46,6 +51,7 @@ import zhErrors from '@/locales/zh/errors.json'; export const resources = { en: { common: enCommon, + dashboard: enDashboard, tracking: enTracking, ai: enAi, auth: enAuth, @@ -55,6 +61,7 @@ export const resources = { }, es: { common: esCommon, + dashboard: esDashboard, tracking: esTracking, ai: esAi, auth: esAuth, @@ -64,6 +71,7 @@ export const resources = { }, fr: { common: frCommon, + dashboard: frDashboard, tracking: frTracking, ai: frAi, auth: frAuth, @@ -73,6 +81,7 @@ export const resources = { }, pt: { common: ptCommon, + dashboard: ptDashboard, tracking: ptTracking, ai: ptAi, auth: ptAuth, @@ -82,6 +91,7 @@ export const resources = { }, zh: { common: zhCommon, + dashboard: zhDashboard, tracking: zhTracking, ai: zhAi, auth: zhAuth, diff --git a/maternal-web/locales/en/common.json b/maternal-web/locales/en/common.json index 1fcf731..54c10a4 100644 --- a/maternal-web/locales/en/common.json +++ b/maternal-web/locales/en/common.json @@ -57,10 +57,23 @@ }, "navigation": { "home": "Home", + "dashboard": "Dashboard", "tracking": "Tracking", + "trackActivity": "Track Activity", + "track": "Track", "ai": "AI Assistant", + "aiChat": "AI Chat", "family": "Family", "insights": "Insights", - "settings": "Settings" + "children": "Children", + "settings": "Settings", + "logout": "Logout" + }, + "connection": { + "syncActive": "Real-time sync active", + "syncDisconnected": "Real-time sync disconnected", + "live": "Live", + "offline": "Offline", + "familyMembersOnline": "{{count}} family members online" } } diff --git a/maternal-web/locales/en/dashboard.json b/maternal-web/locales/en/dashboard.json new file mode 100644 index 0000000..17c97ca --- /dev/null +++ b/maternal-web/locales/en/dashboard.json @@ -0,0 +1,30 @@ +{ + "welcomeBack": "Welcome Back", + "welcomeBackWithName": "Welcome Back, {{name}}!", + "subtitle": "Track your child's activities and get AI-powered insights", + "quickActions": { + "title": "Quick Actions", + "feeding": "Feeding", + "sleep": "Sleep", + "diaper": "Diaper", + "medicine": "Medicine", + "activities": "Activities", + "aiAssistant": "AI Assistant", + "navigateTo": "Navigate to {{action}}" + }, + "summary": { + "title": "Today's Summary", + "titleWithChild": "Today's Summary - {{childName}}", + "feedings": "Feedings", + "sleep": "Sleep", + "diapers": "Diapers", + "medications": "Medications", + "noChild": "Add a child to start tracking", + "noActivities": "No activities tracked today" + }, + "predictions": { + "title": "Next Predicted Activity", + "napTime": "Nap time in {{minutes}} minutes", + "basedOnPatterns": "Based on your child's sleep patterns" + } +} diff --git a/maternal-web/locales/es/common.json b/maternal-web/locales/es/common.json index 5c17e93..ea4e27e 100644 --- a/maternal-web/locales/es/common.json +++ b/maternal-web/locales/es/common.json @@ -57,10 +57,23 @@ }, "navigation": { "home": "Inicio", + "dashboard": "Panel", "tracking": "Seguimiento", + "trackActivity": "Registrar Actividad", + "track": "Registrar", "ai": "Asistente IA", + "aiChat": "Chat IA", "family": "Familia", "insights": "Análisis", - "settings": "Configuración" + "children": "Niños", + "settings": "Configuración", + "logout": "Cerrar Sesión" + }, + "connection": { + "syncActive": "Sincronización en tiempo real activa", + "syncDisconnected": "Sincronización en tiempo real desconectada", + "live": "En Vivo", + "offline": "Desconectado", + "familyMembersOnline": "{{count}} miembros de la familia en línea" } } diff --git a/maternal-web/locales/es/dashboard.json b/maternal-web/locales/es/dashboard.json new file mode 100644 index 0000000..9203a18 --- /dev/null +++ b/maternal-web/locales/es/dashboard.json @@ -0,0 +1,30 @@ +{ + "welcomeBack": "Bienvenido de vuelta", + "welcomeBackWithName": "Bienvenido de vuelta, {{name}}!", + "subtitle": "Rastrea las actividades de tu hijo y obtén información impulsada por IA", + "quickActions": { + "title": "Acciones Rápidas", + "feeding": "Alimentación", + "sleep": "Sueño", + "diaper": "Pañal", + "medicine": "Medicina", + "activities": "Actividades", + "aiAssistant": "Asistente IA", + "navigateTo": "Navegar a {{action}}" + }, + "summary": { + "title": "Resumen de Hoy", + "titleWithChild": "Resumen de Hoy - {{childName}}", + "feedings": "Alimentaciones", + "sleep": "Sueño", + "diapers": "Pañales", + "medications": "Medicamentos", + "noChild": "Agrega un niño para comenzar a rastrear", + "noActivities": "No hay actividades rastreadas hoy" + }, + "predictions": { + "title": "Próxima Actividad Predicha", + "napTime": "Hora de siesta en {{minutes}} minutos", + "basedOnPatterns": "Basado en los patrones de sueño de tu hijo" + } +} diff --git a/maternal-web/locales/fr/common.json b/maternal-web/locales/fr/common.json index 59ae5ad..bd5c13c 100644 --- a/maternal-web/locales/fr/common.json +++ b/maternal-web/locales/fr/common.json @@ -57,10 +57,23 @@ }, "navigation": { "home": "Accueil", + "dashboard": "Tableau de bord", "tracking": "Suivi", + "trackActivity": "Enregistrer l'Activité", + "track": "Enregistrer", "ai": "Assistant IA", + "aiChat": "Chat IA", "family": "Famille", "insights": "Analyses", - "settings": "Paramètres" + "children": "Enfants", + "settings": "Paramètres", + "logout": "Déconnexion" + }, + "connection": { + "syncActive": "Synchronisation en temps réel active", + "syncDisconnected": "Synchronisation en temps réel déconnectée", + "live": "En Direct", + "offline": "Hors Ligne", + "familyMembersOnline": "{{count}} membres de la famille en ligne" } } diff --git a/maternal-web/locales/fr/dashboard.json b/maternal-web/locales/fr/dashboard.json new file mode 100644 index 0000000..b7766ce --- /dev/null +++ b/maternal-web/locales/fr/dashboard.json @@ -0,0 +1,30 @@ +{ + "welcomeBack": "Bon retour", + "welcomeBackWithName": "Bon retour, {{name}}!", + "subtitle": "Suivez les activités de votre enfant et obtenez des informations alimentées par l'IA", + "quickActions": { + "title": "Actions Rapides", + "feeding": "Alimentation", + "sleep": "Sommeil", + "diaper": "Couche", + "medicine": "Médicament", + "activities": "Activités", + "aiAssistant": "Assistant IA", + "navigateTo": "Naviguer vers {{action}}" + }, + "summary": { + "title": "Résumé d'Aujourd'hui", + "titleWithChild": "Résumé d'Aujourd'hui - {{childName}}", + "feedings": "Alimentations", + "sleep": "Sommeil", + "diapers": "Couches", + "medications": "Médicaments", + "noChild": "Ajoutez un enfant pour commencer le suivi", + "noActivities": "Aucune activité suivie aujourd'hui" + }, + "predictions": { + "title": "Prochaine Activité Prédite", + "napTime": "Heure de sieste dans {{minutes}} minutes", + "basedOnPatterns": "Basé sur les habitudes de sommeil de votre enfant" + } +} diff --git a/maternal-web/locales/pt/common.json b/maternal-web/locales/pt/common.json index f896e60..b1c4b28 100644 --- a/maternal-web/locales/pt/common.json +++ b/maternal-web/locales/pt/common.json @@ -57,10 +57,23 @@ }, "navigation": { "home": "Início", + "dashboard": "Painel", "tracking": "Rastreamento", + "trackActivity": "Registrar Atividade", + "track": "Registrar", "ai": "Assistente IA", + "aiChat": "Chat IA", "family": "Família", "insights": "Análises", - "settings": "Configurações" + "children": "Crianças", + "settings": "Configurações", + "logout": "Sair" + }, + "connection": { + "syncActive": "Sincronização em tempo real ativa", + "syncDisconnected": "Sincronização em tempo real desconectada", + "live": "Ao Vivo", + "offline": "Offline", + "familyMembersOnline": "{{count}} membros da família online" } } diff --git a/maternal-web/locales/pt/dashboard.json b/maternal-web/locales/pt/dashboard.json new file mode 100644 index 0000000..a0244e6 --- /dev/null +++ b/maternal-web/locales/pt/dashboard.json @@ -0,0 +1,30 @@ +{ + "welcomeBack": "Bem-vindo de volta", + "welcomeBackWithName": "Bem-vindo de volta, {{name}}!", + "subtitle": "Acompanhe as atividades do seu filho e obtenha insights impulsionados por IA", + "quickActions": { + "title": "Ações Rápidas", + "feeding": "Alimentação", + "sleep": "Sono", + "diaper": "Fralda", + "medicine": "Remédio", + "activities": "Atividades", + "aiAssistant": "Assistente IA", + "navigateTo": "Navegar para {{action}}" + }, + "summary": { + "title": "Resumo de Hoje", + "titleWithChild": "Resumo de Hoje - {{childName}}", + "feedings": "Alimentações", + "sleep": "Sono", + "diapers": "Fraldas", + "medications": "Medicamentos", + "noChild": "Adicione uma criança para começar a acompanhar", + "noActivities": "Nenhuma atividade rastreada hoje" + }, + "predictions": { + "title": "Próxima Atividade Prevista", + "napTime": "Hora da soneca em {{minutes}} minutos", + "basedOnPatterns": "Baseado nos padrões de sono do seu filho" + } +} diff --git a/maternal-web/locales/zh/common.json b/maternal-web/locales/zh/common.json index 7c018bd..615ae74 100644 --- a/maternal-web/locales/zh/common.json +++ b/maternal-web/locales/zh/common.json @@ -57,10 +57,23 @@ }, "navigation": { "home": "首页", + "dashboard": "仪表板", "tracking": "追踪", + "trackActivity": "记录活动", + "track": "记录", "ai": "AI助手", + "aiChat": "AI聊天", "family": "家庭", "insights": "分析", - "settings": "设置" + "children": "儿童", + "settings": "设置", + "logout": "退出登录" + }, + "connection": { + "syncActive": "实时同步活动", + "syncDisconnected": "实时同步已断开", + "live": "在线", + "offline": "离线", + "familyMembersOnline": "{{count}}位家庭成员在线" } } diff --git a/maternal-web/locales/zh/dashboard.json b/maternal-web/locales/zh/dashboard.json new file mode 100644 index 0000000..e7e3d7a --- /dev/null +++ b/maternal-web/locales/zh/dashboard.json @@ -0,0 +1,30 @@ +{ + "welcomeBack": "欢迎回来", + "welcomeBackWithName": "欢迎回来,{{name}}!", + "subtitle": "追踪您孩子的活动并获得AI驱动的见解", + "quickActions": { + "title": "快速操作", + "feeding": "喂养", + "sleep": "睡眠", + "diaper": "尿布", + "medicine": "药物", + "activities": "活动", + "aiAssistant": "AI助手", + "navigateTo": "导航到{{action}}" + }, + "summary": { + "title": "今日总结", + "titleWithChild": "今日总结 - {{childName}}", + "feedings": "喂养次数", + "sleep": "睡眠", + "diapers": "尿布", + "medications": "药物", + "noChild": "添加孩子以开始追踪", + "noActivities": "今天没有追踪任何活动" + }, + "predictions": { + "title": "下一个预测活动", + "napTime": "{{minutes}}分钟后小睡时间", + "basedOnPatterns": "基于您孩子的睡眠模式" + } +}