feat: Apply localization to Track and Children pages (Phase 9 - Batch 2)

**Pages Localized:**
- Track main page: Activity selection menu with all tracking options
- Children page: Complete localization including age formatting with pluralization

**Translation Files:**
- Enhanced tracking.json: Added trackActivity, selectActivity, and activities keys
- Created children.json for all 5 languages with comprehensive strings
- Updated i18n config to include children namespace

**Key Features:**
- Localized age calculation with proper pluralization (year/years, month/months)
- All error messages translated
- Gender labels localized
- Properly formatted age display for all languages

**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:23:21 +00:00
parent acadfe7905
commit 9fad81921d
13 changed files with 230 additions and 26 deletions

View File

@@ -23,8 +23,10 @@ import { childrenApi, Child, CreateChildData } from '@/lib/api/children';
import { ChildDialog } from '@/components/children/ChildDialog';
import { DeleteConfirmDialog } from '@/components/children/DeleteConfirmDialog';
import { motion } from 'framer-motion';
import { useTranslation } from '@/hooks/useTranslation';
export default function ChildrenPage() {
const { t } = useTranslation('children');
const { user } = useAuth();
const [children, setChildren] = useState<Child[]>([]);
const [loading, setLoading] = useState(true);
@@ -43,7 +45,7 @@ export default function ChildrenPage() {
fetchChildren();
} else {
setLoading(false);
setError('No family found. Please complete onboarding first.');
setError(t('errors.noFamily'));
}
}, [familyId]);
@@ -57,7 +59,7 @@ export default function ChildrenPage() {
setChildren(data);
} catch (err: any) {
console.error('Failed to fetch children:', err);
setError(err.response?.data?.message || 'Failed to load children');
setError(err.response?.data?.message || t('errors.loadFailed'));
} finally {
setLoading(false);
}
@@ -80,7 +82,7 @@ export default function ChildrenPage() {
const handleSubmit = async (data: CreateChildData) => {
if (!familyId) {
throw new Error('No family ID found');
throw new Error(t('errors.noFamilyId'));
}
try {
@@ -94,7 +96,7 @@ export default function ChildrenPage() {
setDialogOpen(false);
} catch (err: any) {
console.error('Failed to save child:', err);
throw new Error(err.response?.data?.message || 'Failed to save child');
throw new Error(err.response?.data?.message || t('errors.saveFailed'));
} finally {
setActionLoading(false);
}
@@ -111,7 +113,7 @@ export default function ChildrenPage() {
setChildToDelete(null);
} catch (err: any) {
console.error('Failed to delete child:', err);
setError(err.response?.data?.message || 'Failed to delete child');
setError(err.response?.data?.message || t('errors.deleteFailed'));
} finally {
setActionLoading(false);
}
@@ -138,11 +140,11 @@ export default function ChildrenPage() {
}
if (years === 0) {
return `${months} month${months !== 1 ? 's' : ''}`;
return `${months} ${months !== 1 ? t('ageFormat.months') : t('ageFormat.month')}`;
} else if (months === 0) {
return `${years} year${years !== 1 ? 's' : ''}`;
return `${years} ${years !== 1 ? t('ageFormat.years') : t('ageFormat.year')}`;
} else {
return `${years} year${years !== 1 ? 's' : ''}, ${months} month${months !== 1 ? 's' : ''}`;
return `${years} ${years !== 1 ? t('ageFormat.years') : t('ageFormat.year')}, ${months} ${months !== 1 ? t('ageFormat.months') : t('ageFormat.month')}`;
}
};
@@ -153,10 +155,10 @@ export default function ChildrenPage() {
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 4 }}>
<Box>
<Typography variant="h4" component="h1" fontWeight="600" gutterBottom>
Children
{t('title')}
</Typography>
<Typography variant="body1" color="text.secondary">
Manage your family's children profiles
{t('subtitle')}
</Typography>
</Box>
<Button
@@ -165,7 +167,7 @@ export default function ChildrenPage() {
onClick={handleAddChild}
disabled={loading || !familyId}
>
Add Child
{t('addChild')}
</Button>
</Box>
@@ -186,10 +188,10 @@ export default function ChildrenPage() {
<CardContent sx={{ textAlign: 'center', py: 8 }}>
<ChildCare sx={{ fontSize: 64, color: 'text.secondary', mb: 2 }} />
<Typography variant="h6" component="h2" color="text.secondary" gutterBottom>
No children added yet
{t('noChildren')}
</Typography>
<Typography variant="body2" color="text.secondary" sx={{ mb: 3 }}>
Add your first child to start tracking their activities
{t('noChildrenSubtitle')}
</Typography>
<Button
variant="contained"
@@ -197,7 +199,7 @@ export default function ChildrenPage() {
onClick={handleAddChild}
disabled={!familyId}
>
Add First Child
{t('addFirstChild')}
</Button>
</CardContent>
</Card>
@@ -257,7 +259,7 @@ export default function ChildrenPage() {
fontWeight="600"
sx={{ mt: 1 }}
>
Age: {calculateAge(child.birthDate)}
{t('age')}: {calculateAge(child.birthDate)}
</Typography>
</CardContent>

View File

@@ -5,37 +5,39 @@ import { Restaurant, Hotel, BabyChangingStation, ChildCare, MedicalServices } fr
import { useRouter } from 'next/navigation';
import { AppShell } from '@/components/layouts/AppShell/AppShell';
import { ProtectedRoute } from '@/components/common/ProtectedRoute';
import { useTranslation } from '@/hooks/useTranslation';
export default function TrackPage() {
const { t } = useTranslation('tracking');
const router = useRouter();
const trackingOptions = [
{
title: 'Feeding',
title: t('activities.feeding'),
icon: <Restaurant sx={{ fontSize: 48, color: 'primary.main' }} />,
path: '/track/feeding',
color: '#FFE4E1',
},
{
title: 'Sleep',
title: t('activities.sleep'),
icon: <Hotel sx={{ fontSize: 48, color: 'info.main' }} />,
path: '/track/sleep',
color: '#E1F5FF',
},
{
title: 'Diaper',
title: t('activities.diaper'),
icon: <BabyChangingStation sx={{ fontSize: 48, color: 'warning.main' }} />,
path: '/track/diaper',
color: '#FFF4E1',
},
{
title: 'Medicine',
title: t('activities.medicine'),
icon: <MedicalServices sx={{ fontSize: 48, color: 'error.main' }} />,
path: '/track/medicine',
color: '#FFE8E8',
},
{
title: 'Activity',
title: t('activities.activity'),
icon: <ChildCare sx={{ fontSize: 48, color: 'success.main' }} />,
path: '/track/activity',
color: '#E8F5E9',
@@ -47,10 +49,10 @@ export default function TrackPage() {
<AppShell>
<Box>
<Typography variant="h4" fontWeight="600" gutterBottom>
Track Activity
{t('trackActivity')}
</Typography>
<Typography variant="body1" color="text.secondary" sx={{ mb: 4 }}>
Select an activity to track
{t('selectActivity')}
</Typography>
<Grid container spacing={3}>