feat: Complete comprehensive localization of all tracking and management pages
Some checks failed
CI/CD Pipeline / Build Application (push) Has been cancelled
CI/CD Pipeline / Lint and Test (push) Has been cancelled
CI/CD Pipeline / E2E Tests (push) Has been cancelled

- Feeding page: 47+ strings localized with validation, success/error messages
- Medicine page: 44 strings localized with unit conversion support
- Sleep page: Already localized (verified)
- Diaper page: Already localized (verified)
- Activity page: Already localized (verified)
- AI Assistant: 51 strings localized including chat interface and suggested questions
- Children page: 38 strings fully localized with gender labels
- Family page: 42 strings localized with role management
- Insights page: 41 strings localized including charts and analytics

Added translation files:
- locales/en/ai.json (44 keys)
- locales/en/family.json (42 keys)
- locales/en/insights.json (41 keys)

Updated translation files:
- locales/en/tracking.json (added feeding, health/medicine sections)
- locales/en/children.json (verified complete)

All pages now use useTranslation hook with proper namespaces.
All user-facing text externalized and ready for multi-language support.
This commit is contained in:
2025-10-03 13:57:47 +00:00
parent 5fea603922
commit 41320638e5
10 changed files with 434 additions and 204 deletions

View File

@@ -41,6 +41,7 @@ import { trackingApi, Activity, ActivityType } from '@/lib/api/tracking';
import { childrenApi, Child } from '@/lib/api/children';
import { subDays, startOfDay, endOfDay, parseISO, differenceInMinutes } from 'date-fns';
import { useLocalizedDate } from '@/hooks/useLocalizedDate';
import { useTranslation } from '@/hooks/useTranslation';
import { BarChart, Bar, LineChart, Line, PieChart, Pie, Cell, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';
type DateRange = '7days' | '30days' | '3months';
@@ -99,6 +100,7 @@ const getActivityColor = (type: ActivityType) => {
export const InsightsDashboard: React.FC = () => {
const router = useRouter();
const { format, formatDistanceToNow } = useLocalizedDate();
const { t } = useTranslation('insights');
const [children, setChildren] = useState<Child[]>([]);
const [selectedChild, setSelectedChild] = useState<string>('');
const [dateRange, setDateRange] = useState<DateRange>('7days');
@@ -116,7 +118,7 @@ export const InsightsDashboard: React.FC = () => {
setSelectedChild(childrenData[0].id);
}
} catch (err: any) {
setError(err.response?.data?.message || 'Failed to load children');
setError(err.response?.data?.message || t('errors.loadChildren'));
}
};
fetchChildren();
@@ -142,7 +144,7 @@ export const InsightsDashboard: React.FC = () => {
);
setActivities(activitiesData);
} catch (err: any) {
setError(err.response?.data?.message || 'Failed to load activities');
setError(err.response?.data?.message || t('errors.loadActivities'));
} finally {
setLoading(false);
}
@@ -172,7 +174,7 @@ export const InsightsDashboard: React.FC = () => {
activities.forEach((a) => {
typeCounts[a.type] = (typeCounts[a.type] || 0) + 1;
});
const mostCommonType = Object.entries(typeCounts).sort((a, b) => b[1] - a[1])[0]?.[0] || 'None';
const mostCommonType = Object.entries(typeCounts).sort((a, b) => b[1] - a[1])[0]?.[0] || 'none';
return {
totalFeedings,
@@ -230,7 +232,7 @@ export const InsightsDashboard: React.FC = () => {
});
return Object.entries(typeCount).map(([name, value]) => ({
name: name.charAt(0).toUpperCase() + name.slice(1),
name: t(`diaperTypes.${name}`),
value,
color: COLORS[name as keyof typeof COLORS] || '#CCCCCC',
}));
@@ -244,7 +246,7 @@ export const InsightsDashboard: React.FC = () => {
});
return Object.entries(typeCount).map(([name, count]) => ({
name: name.charAt(0).toUpperCase() + name.slice(1),
name: t(`activityTypes.${name}`),
count,
color: COLORS[name as keyof typeof COLORS] || '#CCCCCC',
}));
@@ -269,10 +271,10 @@ export const InsightsDashboard: React.FC = () => {
>
<Box>
<Typography variant="h4" fontWeight="600" gutterBottom>
Insights & Analytics
{t('title')}
</Typography>
<Typography variant="body1" color="text.secondary" sx={{ mb: 4 }}>
Track patterns and get insights about your child's activities
{t('subtitle')}
</Typography>
{/* Filters */}
@@ -281,11 +283,11 @@ export const InsightsDashboard: React.FC = () => {
{children.length > 1 && (
<Grid item xs={12} sm={6} md={4}>
<FormControl fullWidth>
<InputLabel>Child</InputLabel>
<InputLabel>{t('filters.child')}</InputLabel>
<Select
value={selectedChild}
onChange={(e) => setSelectedChild(e.target.value)}
label="Child"
label={t('filters.child')}
>
{children.map((child) => (
<MenuItem key={child.id} value={child.id}>
@@ -304,9 +306,9 @@ export const InsightsDashboard: React.FC = () => {
fullWidth
size="large"
>
<ToggleButton value="7days">7 Days</ToggleButton>
<ToggleButton value="30days">30 Days</ToggleButton>
<ToggleButton value="3months">3 Months</ToggleButton>
<ToggleButton value="7days">{t('filters.dateRange.7days')}</ToggleButton>
<ToggleButton value="30days">{t('filters.dateRange.30days')}</ToggleButton>
<ToggleButton value="3months">{t('filters.dateRange.3months')}</ToggleButton>
</ToggleButtonGroup>
</Grid>
</Grid>
@@ -323,17 +325,17 @@ export const InsightsDashboard: React.FC = () => {
<CardContent sx={{ textAlign: 'center', py: 8 }}>
<ChildCare sx={{ fontSize: 64, color: 'text.secondary', mb: 2 }} />
<Typography variant="h6" color="text.secondary" gutterBottom>
No Children Added
{t('emptyStates.noChildren.title')}
</Typography>
<Typography variant="body2" color="text.secondary" sx={{ mb: 3 }}>
Add a child to view insights and analytics
{t('emptyStates.noChildren.message')}
</Typography>
<Button
variant="contained"
startIcon={<Add />}
onClick={() => router.push('/children')}
>
Add Child
{t('emptyStates.noChildren.action')}
</Button>
</CardContent>
</Card>
@@ -347,7 +349,7 @@ export const InsightsDashboard: React.FC = () => {
{noActivities && !noChildren && (
<Alert severity="info" sx={{ mb: 3 }}>
No activities found for the selected date range. Start tracking activities to see insights!
{t('emptyStates.noActivities')}
</Alert>
)}
@@ -366,14 +368,14 @@ export const InsightsDashboard: React.FC = () => {
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
<Restaurant sx={{ fontSize: 32, mr: 1 }} />
<Typography variant="h6" fontWeight="600">
Feedings
{t('stats.feedings.title')}
</Typography>
</Box>
<Typography variant="h3" fontWeight="700">
{stats.totalFeedings}
</Typography>
<Typography variant="body2" sx={{ opacity: 0.9, mt: 1 }}>
Total count
{t('stats.feedings.subtitle')}
</Typography>
</CardContent>
</Card>
@@ -391,14 +393,14 @@ export const InsightsDashboard: React.FC = () => {
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
<Hotel sx={{ fontSize: 32, mr: 1 }} />
<Typography variant="h6" fontWeight="600">
Sleep
{t('stats.sleep.title')}
</Typography>
</Box>
<Typography variant="h3" fontWeight="700">
{stats.avgSleepHours}h
</Typography>
<Typography variant="body2" sx={{ opacity: 0.9, mt: 1 }}>
Average per day
{t('stats.sleep.subtitle')}
</Typography>
</CardContent>
</Card>
@@ -416,14 +418,14 @@ export const InsightsDashboard: React.FC = () => {
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
<BabyChangingStation sx={{ fontSize: 32, mr: 1 }} />
<Typography variant="h6" fontWeight="600">
Diapers
{t('stats.diapers.title')}
</Typography>
</Box>
<Typography variant="h3" fontWeight="700">
{stats.totalDiapers}
</Typography>
<Typography variant="body2" sx={{ opacity: 0.9, mt: 1 }}>
Total changes
{t('stats.diapers.subtitle')}
</Typography>
</CardContent>
</Card>
@@ -441,14 +443,14 @@ export const InsightsDashboard: React.FC = () => {
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
<TrendingUp sx={{ fontSize: 32, mr: 1 }} />
<Typography variant="h6" fontWeight="600">
Top Activity
{t('stats.topActivity.title')}
</Typography>
</Box>
<Typography variant="h3" fontWeight="700" sx={{ textTransform: 'capitalize' }}>
{stats.mostCommonType}
{t(`activityTypes.${stats.mostCommonType}`)}
</Typography>
<Typography variant="body2" sx={{ opacity: 0.9, mt: 1 }}>
Most frequent
{t('stats.topActivity.subtitle')}
</Typography>
</CardContent>
</Card>
@@ -464,7 +466,7 @@ export const InsightsDashboard: React.FC = () => {
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
<Restaurant sx={{ mr: 1, color: COLORS.feeding }} />
<Typography variant="h6" fontWeight="600">
Feeding Frequency
{t('charts.feedingFrequency')}
</Typography>
</Box>
<ResponsiveContainer width="100%" height={250}>
@@ -473,7 +475,7 @@ export const InsightsDashboard: React.FC = () => {
<XAxis dataKey="date" tick={{ fontSize: 12 }} />
<YAxis />
<Tooltip />
<Bar dataKey="feedings" fill={COLORS.feeding} name="Feedings" />
<Bar dataKey="feedings" fill={COLORS.feeding} name={t('charts.chartLabels.feedings')} />
</BarChart>
</ResponsiveContainer>
</CardContent>
@@ -486,7 +488,7 @@ export const InsightsDashboard: React.FC = () => {
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
<Hotel sx={{ mr: 1, color: COLORS.sleep }} />
<Typography variant="h6" fontWeight="600">
Sleep Duration (Hours)
{t('charts.sleepDuration')}
</Typography>
</Box>
<ResponsiveContainer width="100%" height={250}>
@@ -500,7 +502,7 @@ export const InsightsDashboard: React.FC = () => {
dataKey="sleepHours"
stroke={COLORS.sleep}
strokeWidth={3}
name="Sleep Hours"
name={t('charts.chartLabels.sleepHours')}
dot={{ fill: COLORS.sleep, r: 4 }}
/>
</LineChart>
@@ -516,7 +518,7 @@ export const InsightsDashboard: React.FC = () => {
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
<BabyChangingStation sx={{ mr: 1, color: COLORS.diaper }} />
<Typography variant="h6" fontWeight="600">
Diaper Changes by Type
{t('charts.diaperChangesByType')}
</Typography>
</Box>
<ResponsiveContainer width="100%" height={250}>
@@ -549,7 +551,7 @@ export const InsightsDashboard: React.FC = () => {
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
<Assessment sx={{ mr: 1, color: 'primary.main' }} />
<Typography variant="h6" fontWeight="600">
Activity Timeline
{t('charts.activityTimeline')}
</Typography>
</Box>
<ResponsiveContainer width="100%" height={250}>
@@ -559,9 +561,9 @@ export const InsightsDashboard: React.FC = () => {
<YAxis />
<Tooltip />
<Legend />
<Bar dataKey="feedings" fill={COLORS.feeding} name="Feedings" />
<Bar dataKey="diapers" fill={COLORS.diaper} name="Diapers" />
<Bar dataKey="sleepHours" fill={COLORS.sleep} name="Sleep (hrs)" />
<Bar dataKey="feedings" fill={COLORS.feeding} name={t('charts.chartLabels.feedings')} />
<Bar dataKey="diapers" fill={COLORS.diaper} name={t('charts.chartLabels.diapers')} />
<Bar dataKey="sleepHours" fill={COLORS.sleep} name={t('charts.chartLabels.sleepHours')} />
</BarChart>
</ResponsiveContainer>
</CardContent>
@@ -573,7 +575,7 @@ export const InsightsDashboard: React.FC = () => {
<Card sx={{ mb: 3 }}>
<CardContent>
<Typography variant="h6" fontWeight="600" gutterBottom>
Activity Distribution
{t('charts.activityDistribution')}
</Typography>
<Box sx={{ display: 'flex', gap: 2, flexWrap: 'wrap', mt: 2 }}>
{activityTypeData.map((activity) => (
@@ -600,7 +602,7 @@ export const InsightsDashboard: React.FC = () => {
<Card>
<CardContent>
<Typography variant="h6" fontWeight="600" gutterBottom>
Recent Activities (Last 20)
{t('recentActivities.title')}
</Typography>
<Divider sx={{ my: 2 }} />
<List sx={{ maxHeight: 400, overflow: 'auto' }}>
@@ -627,7 +629,7 @@ export const InsightsDashboard: React.FC = () => {
primary={
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Typography variant="body1" fontWeight="600" sx={{ textTransform: 'capitalize' }}>
{activity.type}
{t(`activityTypes.${activity.type}`)}
</Typography>
<Chip
label={formatDistanceToNow(parseISO(activity.timestamp), { addSuffix: true })}