feat: Apply localization to Login, Dashboard, and Navigation (Phase 9 - Batch 1)

**Pages Localized:**
- Login page: All UI strings (titles, labels, buttons, links)
- Dashboard page: Welcome message, quick actions, daily summary, predictions
- AppShell: Connection status and presence indicators
- MobileNav: Menu items and app branding
- TabBar: Bottom navigation labels

**Translation Files:**
- Created dashboard.json for all 5 languages (en, es, fr, pt, zh)
- Enhanced common.json with navigation and connection strings
- Updated i18n config to include dashboard namespace

**Languages Supported:**
- English, Spanish, French, Portuguese, Chinese (Simplified)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-03 11:17:47 +00:00
parent de691525fb
commit acadfe7905
16 changed files with 289 additions and 54 deletions

View File

@@ -26,6 +26,7 @@ import { tokenStorage } from '@/lib/utils/tokenStorage';
import { biometricApi } from '@/lib/api/biometric'; import { biometricApi } from '@/lib/api/biometric';
import { startAuthentication } from '@simplewebauthn/browser'; import { startAuthentication } from '@simplewebauthn/browser';
import Link from 'next/link'; import Link from 'next/link';
import { useTranslation } from '@/hooks/useTranslation';
const loginSchema = z.object({ const loginSchema = z.object({
email: z.string().email('Invalid email address'), email: z.string().email('Invalid email address'),
@@ -35,6 +36,7 @@ const loginSchema = z.object({
type LoginFormData = z.infer<typeof loginSchema>; type LoginFormData = z.infer<typeof loginSchema>;
export default function LoginPage() { export default function LoginPage() {
const { t } = useTranslation('auth');
const [showPassword, setShowPassword] = useState(false); const [showPassword, setShowPassword] = useState(false);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
@@ -178,7 +180,7 @@ export default function LoginPage() {
fontWeight="600" fontWeight="600"
color="primary.main" color="primary.main"
> >
Welcome Back 👋 {t('login.title')}
</Typography> </Typography>
<Typography <Typography
variant="body2" variant="body2"
@@ -186,7 +188,7 @@ export default function LoginPage() {
color="text.secondary" color="text.secondary"
sx={{ mb: 3 }} sx={{ mb: 3 }}
> >
Sign in to continue tracking your child's journey {t('login.subtitle')}
</Typography> </Typography>
{error && ( {error && (
@@ -198,7 +200,7 @@ export default function LoginPage() {
<Box component="form" onSubmit={handleSubmit(onSubmit)}> <Box component="form" onSubmit={handleSubmit(onSubmit)}>
<TextField <TextField
fullWidth fullWidth
label="Email" label={t('login.email')}
type="email" type="email"
margin="normal" margin="normal"
error={!!errors.email} error={!!errors.email}
@@ -213,7 +215,7 @@ export default function LoginPage() {
<TextField <TextField
fullWidth fullWidth
label="Password" label={t('login.password')}
type={showPassword ? 'text' : 'password'} type={showPassword ? 'text' : 'password'}
margin="normal" margin="normal"
error={!!errors.password} error={!!errors.password}
@@ -245,7 +247,7 @@ export default function LoginPage() {
variant="body2" variant="body2"
sx={{ cursor: 'pointer', textDecoration: 'none' }} sx={{ cursor: 'pointer', textDecoration: 'none' }}
> >
Forgot password? {t('login.forgotPassword')}
</MuiLink> </MuiLink>
</Box> </Box>
@@ -260,14 +262,14 @@ export default function LoginPage() {
{isLoading ? ( {isLoading ? (
<CircularProgress size={24} color="inherit" /> <CircularProgress size={24} color="inherit" />
) : ( ) : (
'Sign In' t('login.submit')
)} )}
</Button> </Button>
</Box> </Box>
<Divider sx={{ my: 3 }}> <Divider sx={{ my: 3 }}>
<Typography variant="body2" color="text.secondary"> <Typography variant="body2" color="text.secondary">
OR {t('login.or')}
</Typography> </Typography>
</Divider> </Divider>
@@ -279,7 +281,7 @@ export default function LoginPage() {
disabled={isLoading} disabled={isLoading}
sx={{ mb: 2 }} sx={{ mb: 2 }}
> >
Continue with Google {t('login.continueWithGoogle')}
</Button> </Button>
<Button <Button
@@ -289,7 +291,7 @@ export default function LoginPage() {
size="large" size="large"
disabled={isLoading} disabled={isLoading}
> >
Continue with Apple {t('login.continueWithApple')}
</Button> </Button>
{isBiometricSupported && ( {isBiometricSupported && (
@@ -302,16 +304,16 @@ export default function LoginPage() {
onClick={handleBiometricLogin} onClick={handleBiometricLogin}
sx={{ mt: 2 }} sx={{ mt: 2 }}
> >
{isBiometricLoading ? 'Authenticating...' : 'Sign in with Biometrics'} {isBiometricLoading ? t('login.biometric.authenticating') : t('login.biometric.useFaceId')}
</Button> </Button>
)} )}
<Box sx={{ mt: 3, textAlign: 'center' }}> <Box sx={{ mt: 3, textAlign: 'center' }}>
<Typography variant="body2" color="text.secondary"> <Typography variant="body2" color="text.secondary">
Don't have an account?{' '} {t('login.noAccount')}{' '}
<Link href="/register" passHref legacyBehavior> <Link href="/register" passHref legacyBehavior>
<MuiLink sx={{ cursor: 'pointer', fontWeight: 600 }}> <MuiLink sx={{ cursor: 'pointer', fontWeight: 600 }}>
Sign up {t('login.signUp')}
</MuiLink> </MuiLink>
</Link> </Link>
</Typography> </Typography>

View File

@@ -25,8 +25,10 @@ import { useQuery } from '@apollo/client/react';
import { GET_DASHBOARD } from '@/graphql/queries/dashboard'; import { GET_DASHBOARD } from '@/graphql/queries/dashboard';
import { format } from 'date-fns'; import { format } from 'date-fns';
import { useRealTimeActivities } from '@/hooks/useWebSocket'; import { useRealTimeActivities } from '@/hooks/useWebSocket';
import { useTranslation } from '@/hooks/useTranslation';
export default function HomePage() { export default function HomePage() {
const { t } = useTranslation('dashboard');
const { user, isLoading: authLoading } = useAuth(); const { user, isLoading: authLoading } = useAuth();
const router = useRouter(); const router = useRouter();
const [selectedChildId, setSelectedChildId] = useState<string | null>(null); const [selectedChildId, setSelectedChildId] = useState<string | null>(null);
@@ -93,12 +95,12 @@ export default function HomePage() {
}, [data, selectedChildId]); }, [data, selectedChildId]);
const quickActions = [ const quickActions = [
{ icon: <Restaurant />, label: 'Feeding', color: '#E91E63', path: '/track/feeding' }, // Pink with 4.5:1 contrast { icon: <Restaurant />, label: t('quickActions.feeding'), color: '#E91E63', path: '/track/feeding' }, // Pink with 4.5:1 contrast
{ icon: <Hotel />, label: 'Sleep', color: '#1976D2', path: '/track/sleep' }, // Blue with 4.5:1 contrast { icon: <Hotel />, label: t('quickActions.sleep'), color: '#1976D2', path: '/track/sleep' }, // Blue with 4.5:1 contrast
{ icon: <BabyChangingStation />, label: 'Diaper', color: '#F57C00', path: '/track/diaper' }, // Orange with 4.5:1 contrast { icon: <BabyChangingStation />, label: t('quickActions.diaper'), color: '#F57C00', path: '/track/diaper' }, // Orange with 4.5:1 contrast
{ icon: <MedicalServices />, label: 'Medicine', color: '#C62828', path: '/track/medication' }, // Red with 4.5:1 contrast { icon: <MedicalServices />, label: t('quickActions.medicine'), color: '#C62828', path: '/track/medication' }, // Red with 4.5:1 contrast
{ icon: <Insights />, label: 'Activities', color: '#558B2F', path: '/activities' }, // Green with 4.5:1 contrast { icon: <Insights />, label: t('quickActions.activities'), color: '#558B2F', path: '/activities' }, // Green with 4.5:1 contrast
{ icon: <SmartToy />, label: 'AI Assistant', color: '#D84315', path: '/ai-assistant' }, // Deep orange with 4.5:1 contrast { icon: <SmartToy />, label: t('quickActions.aiAssistant'), color: '#D84315', path: '/ai-assistant' }, // Deep orange with 4.5:1 contrast
]; ];
const formatSleepHours = (minutes: number) => { const formatSleepHours = (minutes: number) => {
@@ -132,15 +134,15 @@ export default function HomePage() {
transition={{ duration: 0.5 }} transition={{ duration: 0.5 }}
> >
<Typography variant="h4" component="h1" gutterBottom fontWeight="600" sx={{ mb: 1 }}> <Typography variant="h4" component="h1" gutterBottom fontWeight="600" sx={{ mb: 1 }}>
Welcome Back{user?.name ? `, ${user.name}` : ''}! 👋 {user?.name ? t('welcomeBackWithName', { name: user.name }) : t('welcomeBack')} 👋
</Typography> </Typography>
<Typography variant="body1" sx={{ mb: 4, color: 'text.primary' }}> <Typography variant="body1" sx={{ mb: 4, color: 'text.primary' }}>
Track your child's activities and get AI-powered insights {t('subtitle')}
</Typography> </Typography>
{/* Quick Actions */} {/* Quick Actions */}
<Typography variant="h6" component="h2" gutterBottom fontWeight="600" sx={{ mb: 2 }}> <Typography variant="h6" component="h2" gutterBottom fontWeight="600" sx={{ mb: 2 }}>
Quick Actions {t('quickActions.title')}
</Typography> </Typography>
<Grid container spacing={2} sx={{ mb: 4 }}> <Grid container spacing={2} sx={{ mb: 4 }}>
{quickActions.map((action, index) => ( {quickActions.map((action, index) => (
@@ -160,7 +162,7 @@ export default function HomePage() {
router.push(action.path); router.push(action.path);
} }
}} }}
aria-label={`Navigate to ${action.label}`} aria-label={t('quickActions.navigateTo', { action: action.label })}
sx={{ sx={{
p: 3, p: 3,
height: '100%', height: '100%',
@@ -196,7 +198,7 @@ export default function HomePage() {
{/* Today's Summary */} {/* Today's Summary */}
<Typography variant="h6" component="h2" gutterBottom fontWeight="600" sx={{ mb: 2 }}> <Typography variant="h6" component="h2" gutterBottom fontWeight="600" sx={{ mb: 2 }}>
Today's Summary{selectedChild ? ` - ${selectedChild.name}` : ''} {selectedChild ? t('summary.titleWithChild', { childName: selectedChild.name }) : t('summary.title')}
</Typography> </Typography>
<ErrorBoundary <ErrorBoundary
isolate isolate
@@ -210,8 +212,8 @@ export default function HomePage() {
<Box sx={{ textAlign: 'center', py: 4 }}> <Box sx={{ textAlign: 'center', py: 4 }}>
<Typography variant="body2" sx={{ color: 'rgba(0, 0, 0, 0.7)' }}> <Typography variant="body2" sx={{ color: 'rgba(0, 0, 0, 0.7)' }}>
{children.length === 0 {children.length === 0
? 'Add a child to start tracking' ? t('summary.noChild')
: 'No activities tracked today'} : t('summary.noActivities')}
</Typography> </Typography>
</Box> </Box>
) : ( ) : (
@@ -232,7 +234,7 @@ export default function HomePage() {
{dailySummary.feedingCount || 0} {dailySummary.feedingCount || 0}
</Typography> </Typography>
<Typography variant="body2" sx={{ color: 'rgba(0, 0, 0, 0.7)' }}> <Typography variant="body2" sx={{ color: 'rgba(0, 0, 0, 0.7)' }}>
Feedings {t('summary.feedings')}
</Typography> </Typography>
</Box> </Box>
</Grid> </Grid>
@@ -254,7 +256,7 @@ export default function HomePage() {
: '0m'} : '0m'}
</Typography> </Typography>
<Typography variant="body2" sx={{ color: 'rgba(0, 0, 0, 0.7)' }}> <Typography variant="body2" sx={{ color: 'rgba(0, 0, 0, 0.7)' }}>
Sleep {t('summary.sleep')}
</Typography> </Typography>
</Box> </Box>
</Grid> </Grid>
@@ -274,7 +276,7 @@ export default function HomePage() {
{dailySummary.diaperCount || 0} {dailySummary.diaperCount || 0}
</Typography> </Typography>
<Typography variant="body2" sx={{ color: 'rgba(0, 0, 0, 0.7)' }}> <Typography variant="body2" sx={{ color: 'rgba(0, 0, 0, 0.7)' }}>
Diapers {t('summary.diapers')}
</Typography> </Typography>
</Box> </Box>
</Grid> </Grid>
@@ -294,7 +296,7 @@ export default function HomePage() {
{dailySummary.medicationCount || 0} {dailySummary.medicationCount || 0}
</Typography> </Typography>
<Typography variant="body2" sx={{ color: 'rgba(0, 0, 0, 0.7)' }}> <Typography variant="body2" sx={{ color: 'rgba(0, 0, 0, 0.7)' }}>
Medications {t('summary.medications')}
</Typography> </Typography>
</Box> </Box>
</Grid> </Grid>
@@ -308,13 +310,13 @@ export default function HomePage() {
<Box sx={{ mt: 4 }}> <Box sx={{ mt: 4 }}>
<Paper sx={{ p: 3, bgcolor: 'primary.light' }}> <Paper sx={{ p: 3, bgcolor: 'primary.light' }}>
<Typography variant="body2" sx={{ color: 'rgba(0, 0, 0, 0.7)' }} gutterBottom> <Typography variant="body2" sx={{ color: 'rgba(0, 0, 0, 0.7)' }} gutterBottom>
Next Predicted Activity {t('predictions.title')}
</Typography> </Typography>
<Typography variant="h6" fontWeight="600" gutterBottom> <Typography variant="h6" fontWeight="600" gutterBottom>
Nap time in 45 minutes {t('predictions.napTime', { minutes: 45 })}
</Typography> </Typography>
<Typography variant="body2" sx={{ color: 'rgba(0, 0, 0, 0.7)' }}> <Typography variant="body2" sx={{ color: 'rgba(0, 0, 0, 0.7)' }}>
Based on your child's sleep patterns {t('predictions.basedOnPatterns')}
</Typography> </Typography>
</Paper> </Paper>
</Box> </Box>

View File

@@ -7,12 +7,14 @@ import { useMediaQuery } from '@/hooks/useMediaQuery';
import { ReactNode } from 'react'; import { ReactNode } from 'react';
import { useWebSocket } from '@/hooks/useWebSocket'; import { useWebSocket } from '@/hooks/useWebSocket';
import { Wifi, WifiOff, People } from '@mui/icons-material'; import { Wifi, WifiOff, People } from '@mui/icons-material';
import { useTranslation } from '@/hooks/useTranslation';
interface AppShellProps { interface AppShellProps {
children: ReactNode; children: ReactNode;
} }
export const AppShell = ({ children }: AppShellProps) => { export const AppShell = ({ children }: AppShellProps) => {
const { t } = useTranslation('common');
const isMobile = useMediaQuery('(max-width: 768px)'); const isMobile = useMediaQuery('(max-width: 768px)');
const isTablet = useMediaQuery('(max-width: 1024px)'); const isTablet = useMediaQuery('(max-width: 1024px)');
const { isConnected, presence } = useWebSocket(); const { isConnected, presence } = useWebSocket();
@@ -38,10 +40,10 @@ export const AppShell = ({ children }: AppShellProps) => {
gap: 1, gap: 1,
}} }}
> >
<Tooltip title={isConnected ? 'Real-time sync active' : 'Real-time sync disconnected'}> <Tooltip title={isConnected ? t('connection.syncActive') : t('connection.syncDisconnected')}>
<Chip <Chip
icon={isConnected ? <Wifi /> : <WifiOff />} icon={isConnected ? <Wifi /> : <WifiOff />}
label={isConnected ? 'Live' : 'Offline'} label={isConnected ? t('connection.live') : t('connection.offline')}
size="small" size="small"
color={isConnected ? 'success' : 'default'} color={isConnected ? 'success' : 'default'}
sx={{ sx={{
@@ -52,7 +54,7 @@ export const AppShell = ({ children }: AppShellProps) => {
</Tooltip> </Tooltip>
{isConnected && presence.count > 1 && ( {isConnected && presence.count > 1 && (
<Tooltip title={`${presence.count} family members online`}> <Tooltip title={t('connection.familyMembersOnline', { count: presence.count })}>
<Chip <Chip
icon={<People />} icon={<People />}
label={presence.count} label={presence.count}

View File

@@ -28,19 +28,21 @@ import {
Group, Group,
Logout, Logout,
} from '@mui/icons-material'; } from '@mui/icons-material';
import { useTranslation } from '@/hooks/useTranslation';
export const MobileNav = () => { export const MobileNav = () => {
const { t } = useTranslation('common');
const [drawerOpen, setDrawerOpen] = useState(false); const [drawerOpen, setDrawerOpen] = useState(false);
const router = useRouter(); const router = useRouter();
const menuItems = [ const menuItems = [
{ label: 'Dashboard', icon: <Home />, path: '/' }, { label: t('navigation.dashboard'), icon: <Home />, path: '/' },
{ label: 'Track Activity', icon: <Timeline />, path: '/track' }, { label: t('navigation.trackActivity'), icon: <Timeline />, path: '/track' },
{ label: 'AI Assistant', icon: <Chat />, path: '/ai-assistant' }, { label: t('navigation.ai'), icon: <Chat />, path: '/ai-assistant' },
{ label: 'Insights', icon: <Insights />, path: '/insights' }, { label: t('navigation.insights'), icon: <Insights />, path: '/insights' },
{ label: 'Children', icon: <ChildCare />, path: '/children' }, { label: t('navigation.children'), icon: <ChildCare />, path: '/children' },
{ label: 'Family', icon: <Group />, path: '/family' }, { label: t('navigation.family'), icon: <Group />, path: '/family' },
{ label: 'Settings', icon: <Settings />, path: '/settings' }, { label: t('navigation.settings'), icon: <Settings />, path: '/settings' },
]; ];
const handleNavigate = (path: string) => { const handleNavigate = (path: string) => {
@@ -61,7 +63,7 @@ export const MobileNav = () => {
<MenuIcon /> <MenuIcon />
</IconButton> </IconButton>
<Typography variant="h6" component="div" sx={{ flexGrow: 1, color: '#DB7093', fontWeight: 600 }}> <Typography variant="h6" component="div" sx={{ flexGrow: 1, color: '#DB7093', fontWeight: 600 }}>
Maternal {t('appName')}
</Typography> </Typography>
<IconButton <IconButton
color="primary" color="primary"
@@ -113,7 +115,7 @@ export const MobileNav = () => {
<ListItemIcon sx={{ color: 'error.main' }}> <ListItemIcon sx={{ color: 'error.main' }}>
<Logout /> <Logout />
</ListItemIcon> </ListItemIcon>
<ListItemText primary="Logout" /> <ListItemText primary={t('navigation.logout')} />
</ListItemButton> </ListItemButton>
</ListItem> </ListItem>
</List> </List>

View File

@@ -9,17 +9,19 @@ import {
Insights, Insights,
Settings, Settings,
} from '@mui/icons-material'; } from '@mui/icons-material';
import { useTranslation } from '@/hooks/useTranslation';
export const TabBar = () => { export const TabBar = () => {
const { t } = useTranslation('common');
const router = useRouter(); const router = useRouter();
const pathname = usePathname(); const pathname = usePathname();
const tabs = [ const tabs = [
{ label: 'Home', icon: <Home />, value: '/' }, { label: t('navigation.home'), icon: <Home />, value: '/' },
{ label: 'Track', icon: <Timeline />, value: '/track' }, { label: t('navigation.track'), icon: <Timeline />, value: '/track' },
{ label: 'AI Chat', icon: <Chat />, value: '/ai-assistant' }, { label: t('navigation.aiChat'), icon: <Chat />, value: '/ai-assistant' },
{ label: 'Insights', icon: <Insights />, value: '/insights' }, { label: t('navigation.insights'), icon: <Insights />, value: '/insights' },
{ label: 'Settings', icon: <Settings />, value: '/settings' }, { label: t('navigation.settings'), icon: <Settings />, value: '/settings' },
]; ];
return ( return (

View File

@@ -4,6 +4,7 @@ import LanguageDetector from 'i18next-browser-languagedetector';
// Import translation files // Import translation files
import enCommon from '@/locales/en/common.json'; import enCommon from '@/locales/en/common.json';
import enDashboard from '@/locales/en/dashboard.json';
import enTracking from '@/locales/en/tracking.json'; import enTracking from '@/locales/en/tracking.json';
import enAi from '@/locales/en/ai.json'; import enAi from '@/locales/en/ai.json';
import enAuth from '@/locales/en/auth.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 enErrors from '@/locales/en/errors.json';
import esCommon from '@/locales/es/common.json'; import esCommon from '@/locales/es/common.json';
import esDashboard from '@/locales/es/dashboard.json';
import esTracking from '@/locales/es/tracking.json'; import esTracking from '@/locales/es/tracking.json';
import esAi from '@/locales/es/ai.json'; import esAi from '@/locales/es/ai.json';
import esAuth from '@/locales/es/auth.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 esErrors from '@/locales/es/errors.json';
import frCommon from '@/locales/fr/common.json'; import frCommon from '@/locales/fr/common.json';
import frDashboard from '@/locales/fr/dashboard.json';
import frTracking from '@/locales/fr/tracking.json'; import frTracking from '@/locales/fr/tracking.json';
import frAi from '@/locales/fr/ai.json'; import frAi from '@/locales/fr/ai.json';
import frAuth from '@/locales/fr/auth.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 frErrors from '@/locales/fr/errors.json';
import ptCommon from '@/locales/pt/common.json'; import ptCommon from '@/locales/pt/common.json';
import ptDashboard from '@/locales/pt/dashboard.json';
import ptTracking from '@/locales/pt/tracking.json'; import ptTracking from '@/locales/pt/tracking.json';
import ptAi from '@/locales/pt/ai.json'; import ptAi from '@/locales/pt/ai.json';
import ptAuth from '@/locales/pt/auth.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 ptErrors from '@/locales/pt/errors.json';
import zhCommon from '@/locales/zh/common.json'; import zhCommon from '@/locales/zh/common.json';
import zhDashboard from '@/locales/zh/dashboard.json';
import zhTracking from '@/locales/zh/tracking.json'; import zhTracking from '@/locales/zh/tracking.json';
import zhAi from '@/locales/zh/ai.json'; import zhAi from '@/locales/zh/ai.json';
import zhAuth from '@/locales/zh/auth.json'; import zhAuth from '@/locales/zh/auth.json';
@@ -46,6 +51,7 @@ import zhErrors from '@/locales/zh/errors.json';
export const resources = { export const resources = {
en: { en: {
common: enCommon, common: enCommon,
dashboard: enDashboard,
tracking: enTracking, tracking: enTracking,
ai: enAi, ai: enAi,
auth: enAuth, auth: enAuth,
@@ -55,6 +61,7 @@ export const resources = {
}, },
es: { es: {
common: esCommon, common: esCommon,
dashboard: esDashboard,
tracking: esTracking, tracking: esTracking,
ai: esAi, ai: esAi,
auth: esAuth, auth: esAuth,
@@ -64,6 +71,7 @@ export const resources = {
}, },
fr: { fr: {
common: frCommon, common: frCommon,
dashboard: frDashboard,
tracking: frTracking, tracking: frTracking,
ai: frAi, ai: frAi,
auth: frAuth, auth: frAuth,
@@ -73,6 +81,7 @@ export const resources = {
}, },
pt: { pt: {
common: ptCommon, common: ptCommon,
dashboard: ptDashboard,
tracking: ptTracking, tracking: ptTracking,
ai: ptAi, ai: ptAi,
auth: ptAuth, auth: ptAuth,
@@ -82,6 +91,7 @@ export const resources = {
}, },
zh: { zh: {
common: zhCommon, common: zhCommon,
dashboard: zhDashboard,
tracking: zhTracking, tracking: zhTracking,
ai: zhAi, ai: zhAi,
auth: zhAuth, auth: zhAuth,

View File

@@ -57,10 +57,23 @@
}, },
"navigation": { "navigation": {
"home": "Home", "home": "Home",
"dashboard": "Dashboard",
"tracking": "Tracking", "tracking": "Tracking",
"trackActivity": "Track Activity",
"track": "Track",
"ai": "AI Assistant", "ai": "AI Assistant",
"aiChat": "AI Chat",
"family": "Family", "family": "Family",
"insights": "Insights", "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"
} }
} }

View File

@@ -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"
}
}

View File

@@ -57,10 +57,23 @@
}, },
"navigation": { "navigation": {
"home": "Inicio", "home": "Inicio",
"dashboard": "Panel",
"tracking": "Seguimiento", "tracking": "Seguimiento",
"trackActivity": "Registrar Actividad",
"track": "Registrar",
"ai": "Asistente IA", "ai": "Asistente IA",
"aiChat": "Chat IA",
"family": "Familia", "family": "Familia",
"insights": "Análisis", "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"
} }
} }

View File

@@ -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"
}
}

View File

@@ -57,10 +57,23 @@
}, },
"navigation": { "navigation": {
"home": "Accueil", "home": "Accueil",
"dashboard": "Tableau de bord",
"tracking": "Suivi", "tracking": "Suivi",
"trackActivity": "Enregistrer l'Activité",
"track": "Enregistrer",
"ai": "Assistant IA", "ai": "Assistant IA",
"aiChat": "Chat IA",
"family": "Famille", "family": "Famille",
"insights": "Analyses", "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"
} }
} }

View File

@@ -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"
}
}

View File

@@ -57,10 +57,23 @@
}, },
"navigation": { "navigation": {
"home": "Início", "home": "Início",
"dashboard": "Painel",
"tracking": "Rastreamento", "tracking": "Rastreamento",
"trackActivity": "Registrar Atividade",
"track": "Registrar",
"ai": "Assistente IA", "ai": "Assistente IA",
"aiChat": "Chat IA",
"family": "Família", "family": "Família",
"insights": "Análises", "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"
} }
} }

View File

@@ -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"
}
}

View File

@@ -57,10 +57,23 @@
}, },
"navigation": { "navigation": {
"home": "首页", "home": "首页",
"dashboard": "仪表板",
"tracking": "追踪", "tracking": "追踪",
"trackActivity": "记录活动",
"track": "记录",
"ai": "AI助手", "ai": "AI助手",
"aiChat": "AI聊天",
"family": "家庭", "family": "家庭",
"insights": "分析", "insights": "分析",
"settings": "设置" "children": "儿童",
"settings": "设置",
"logout": "退出登录"
},
"connection": {
"syncActive": "实时同步活动",
"syncDisconnected": "实时同步已断开",
"live": "在线",
"offline": "离线",
"familyMembersOnline": "{{count}}位家庭成员在线"
} }
} }

View File

@@ -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": "基于您孩子的睡眠模式"
}
}