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:
@@ -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>
|
||||
|
||||
|
||||
@@ -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}>
|
||||
|
||||
@@ -6,6 +6,7 @@ import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
import enCommon from '@/locales/en/common.json';
|
||||
import enDashboard from '@/locales/en/dashboard.json';
|
||||
import enTracking from '@/locales/en/tracking.json';
|
||||
import enChildren from '@/locales/en/children.json';
|
||||
import enAi from '@/locales/en/ai.json';
|
||||
import enAuth from '@/locales/en/auth.json';
|
||||
import enSettings from '@/locales/en/settings.json';
|
||||
@@ -15,6 +16,7 @@ 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 esChildren from '@/locales/es/children.json';
|
||||
import esAi from '@/locales/es/ai.json';
|
||||
import esAuth from '@/locales/es/auth.json';
|
||||
import esSettings from '@/locales/es/settings.json';
|
||||
@@ -24,6 +26,7 @@ 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 frChildren from '@/locales/fr/children.json';
|
||||
import frAi from '@/locales/fr/ai.json';
|
||||
import frAuth from '@/locales/fr/auth.json';
|
||||
import frSettings from '@/locales/fr/settings.json';
|
||||
@@ -33,6 +36,7 @@ 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 ptChildren from '@/locales/pt/children.json';
|
||||
import ptAi from '@/locales/pt/ai.json';
|
||||
import ptAuth from '@/locales/pt/auth.json';
|
||||
import ptSettings from '@/locales/pt/settings.json';
|
||||
@@ -42,6 +46,7 @@ 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 zhChildren from '@/locales/zh/children.json';
|
||||
import zhAi from '@/locales/zh/ai.json';
|
||||
import zhAuth from '@/locales/zh/auth.json';
|
||||
import zhSettings from '@/locales/zh/settings.json';
|
||||
@@ -53,6 +58,7 @@ export const resources = {
|
||||
common: enCommon,
|
||||
dashboard: enDashboard,
|
||||
tracking: enTracking,
|
||||
children: enChildren,
|
||||
ai: enAi,
|
||||
auth: enAuth,
|
||||
settings: enSettings,
|
||||
@@ -63,6 +69,7 @@ export const resources = {
|
||||
common: esCommon,
|
||||
dashboard: esDashboard,
|
||||
tracking: esTracking,
|
||||
children: esChildren,
|
||||
ai: esAi,
|
||||
auth: esAuth,
|
||||
settings: esSettings,
|
||||
@@ -73,6 +80,7 @@ export const resources = {
|
||||
common: frCommon,
|
||||
dashboard: frDashboard,
|
||||
tracking: frTracking,
|
||||
children: frChildren,
|
||||
ai: frAi,
|
||||
auth: frAuth,
|
||||
settings: frSettings,
|
||||
@@ -83,6 +91,7 @@ export const resources = {
|
||||
common: ptCommon,
|
||||
dashboard: ptDashboard,
|
||||
tracking: ptTracking,
|
||||
children: ptChildren,
|
||||
ai: ptAi,
|
||||
auth: ptAuth,
|
||||
settings: ptSettings,
|
||||
@@ -93,6 +102,7 @@ export const resources = {
|
||||
common: zhCommon,
|
||||
dashboard: zhDashboard,
|
||||
tracking: zhTracking,
|
||||
children: zhChildren,
|
||||
ai: zhAi,
|
||||
auth: zhAuth,
|
||||
settings: zhSettings,
|
||||
|
||||
29
maternal-web/locales/en/children.json
Normal file
29
maternal-web/locales/en/children.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"title": "Children",
|
||||
"subtitle": "Manage your family's children profiles",
|
||||
"addChild": "Add Child",
|
||||
"addFirstChild": "Add First Child",
|
||||
"editChild": "Edit Child",
|
||||
"deleteChild": "Delete Child",
|
||||
"noChildren": "No children added yet",
|
||||
"noChildrenSubtitle": "Add your first child to start tracking their activities",
|
||||
"age": "Age",
|
||||
"gender": {
|
||||
"male": "Male",
|
||||
"female": "Female",
|
||||
"other": "Other"
|
||||
},
|
||||
"errors": {
|
||||
"noFamily": "No family found. Please complete onboarding first.",
|
||||
"loadFailed": "Failed to load children",
|
||||
"saveFailed": "Failed to save child",
|
||||
"deleteFailed": "Failed to delete child",
|
||||
"noFamilyId": "No family ID found"
|
||||
},
|
||||
"ageFormat": {
|
||||
"year": "year",
|
||||
"years": "years",
|
||||
"month": "month",
|
||||
"months": "months"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,14 @@
|
||||
{
|
||||
"title": "Activity Tracking",
|
||||
"trackActivity": "Track Activity",
|
||||
"selectActivity": "Select an activity to track",
|
||||
"activities": {
|
||||
"feeding": "Feeding",
|
||||
"sleep": "Sleep",
|
||||
"diaper": "Diaper",
|
||||
"medicine": "Medicine",
|
||||
"activity": "Activity"
|
||||
},
|
||||
"feeding": {
|
||||
"title": "Feeding",
|
||||
"addFeeding": "Add Feeding",
|
||||
|
||||
29
maternal-web/locales/es/children.json
Normal file
29
maternal-web/locales/es/children.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"title": "Niños",
|
||||
"subtitle": "Gestiona los perfiles de los niños de tu familia",
|
||||
"addChild": "Agregar Niño",
|
||||
"addFirstChild": "Agregar Primer Niño",
|
||||
"editChild": "Editar Niño",
|
||||
"deleteChild": "Eliminar Niño",
|
||||
"noChildren": "Aún no se han agregado niños",
|
||||
"noChildrenSubtitle": "Agrega tu primer niño para comenzar a rastrear sus actividades",
|
||||
"age": "Edad",
|
||||
"gender": {
|
||||
"male": "Masculino",
|
||||
"female": "Femenino",
|
||||
"other": "Otro"
|
||||
},
|
||||
"errors": {
|
||||
"noFamily": "No se encontró familia. Por favor completa la incorporación primero.",
|
||||
"loadFailed": "Error al cargar niños",
|
||||
"saveFailed": "Error al guardar niño",
|
||||
"deleteFailed": "Error al eliminar niño",
|
||||
"noFamilyId": "No se encontró ID de familia"
|
||||
},
|
||||
"ageFormat": {
|
||||
"year": "año",
|
||||
"years": "años",
|
||||
"month": "mes",
|
||||
"months": "meses"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,14 @@
|
||||
{
|
||||
"title": "Activity Tracking",
|
||||
"title": "Seguimiento de Actividades",
|
||||
"trackActivity": "Registrar Actividad",
|
||||
"selectActivity": "Selecciona una actividad para registrar",
|
||||
"activities": {
|
||||
"feeding": "Alimentación",
|
||||
"sleep": "Sueño",
|
||||
"diaper": "Pañal",
|
||||
"medicine": "Medicina",
|
||||
"activity": "Actividad"
|
||||
},
|
||||
"feeding": {
|
||||
"title": "Feeding",
|
||||
"addFeeding": "Add Feeding",
|
||||
|
||||
29
maternal-web/locales/fr/children.json
Normal file
29
maternal-web/locales/fr/children.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"title": "Enfants",
|
||||
"subtitle": "Gérez les profils des enfants de votre famille",
|
||||
"addChild": "Ajouter un Enfant",
|
||||
"addFirstChild": "Ajouter le Premier Enfant",
|
||||
"editChild": "Modifier l'Enfant",
|
||||
"deleteChild": "Supprimer l'Enfant",
|
||||
"noChildren": "Aucun enfant ajouté pour le moment",
|
||||
"noChildrenSubtitle": "Ajoutez votre premier enfant pour commencer à suivre ses activités",
|
||||
"age": "Âge",
|
||||
"gender": {
|
||||
"male": "Masculin",
|
||||
"female": "Féminin",
|
||||
"other": "Autre"
|
||||
},
|
||||
"errors": {
|
||||
"noFamily": "Aucune famille trouvée. Veuillez d'abord terminer l'intégration.",
|
||||
"loadFailed": "Échec du chargement des enfants",
|
||||
"saveFailed": "Échec de l'enregistrement de l'enfant",
|
||||
"deleteFailed": "Échec de la suppression de l'enfant",
|
||||
"noFamilyId": "Aucun ID de famille trouvé"
|
||||
},
|
||||
"ageFormat": {
|
||||
"year": "an",
|
||||
"years": "ans",
|
||||
"month": "mois",
|
||||
"months": "mois"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,14 @@
|
||||
{
|
||||
"title": "Activity Tracking",
|
||||
"title": "Suivi des Activités",
|
||||
"trackActivity": "Enregistrer une Activité",
|
||||
"selectActivity": "Sélectionnez une activité à suivre",
|
||||
"activities": {
|
||||
"feeding": "Alimentation",
|
||||
"sleep": "Sommeil",
|
||||
"diaper": "Couche",
|
||||
"medicine": "Médicament",
|
||||
"activity": "Activité"
|
||||
},
|
||||
"feeding": {
|
||||
"title": "Feeding",
|
||||
"addFeeding": "Add Feeding",
|
||||
|
||||
29
maternal-web/locales/pt/children.json
Normal file
29
maternal-web/locales/pt/children.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"title": "Crianças",
|
||||
"subtitle": "Gerencie os perfis das crianças da sua família",
|
||||
"addChild": "Adicionar Criança",
|
||||
"addFirstChild": "Adicionar Primeira Criança",
|
||||
"editChild": "Editar Criança",
|
||||
"deleteChild": "Excluir Criança",
|
||||
"noChildren": "Nenhuma criança adicionada ainda",
|
||||
"noChildrenSubtitle": "Adicione sua primeira criança para começar a rastrear suas atividades",
|
||||
"age": "Idade",
|
||||
"gender": {
|
||||
"male": "Masculino",
|
||||
"female": "Feminino",
|
||||
"other": "Outro"
|
||||
},
|
||||
"errors": {
|
||||
"noFamily": "Nenhuma família encontrada. Por favor, complete a integração primeiro.",
|
||||
"loadFailed": "Falha ao carregar crianças",
|
||||
"saveFailed": "Falha ao salvar criança",
|
||||
"deleteFailed": "Falha ao excluir criança",
|
||||
"noFamilyId": "Nenhum ID de família encontrado"
|
||||
},
|
||||
"ageFormat": {
|
||||
"year": "ano",
|
||||
"years": "anos",
|
||||
"month": "mês",
|
||||
"months": "meses"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,14 @@
|
||||
{
|
||||
"title": "Activity Tracking",
|
||||
"title": "Rastreamento de Atividades",
|
||||
"trackActivity": "Registrar Atividade",
|
||||
"selectActivity": "Selecione uma atividade para rastrear",
|
||||
"activities": {
|
||||
"feeding": "Alimentação",
|
||||
"sleep": "Sono",
|
||||
"diaper": "Fralda",
|
||||
"medicine": "Remédio",
|
||||
"activity": "Atividade"
|
||||
},
|
||||
"feeding": {
|
||||
"title": "Feeding",
|
||||
"addFeeding": "Add Feeding",
|
||||
|
||||
29
maternal-web/locales/zh/children.json
Normal file
29
maternal-web/locales/zh/children.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"title": "儿童",
|
||||
"subtitle": "管理您家庭的儿童档案",
|
||||
"addChild": "添加儿童",
|
||||
"addFirstChild": "添加第一个儿童",
|
||||
"editChild": "编辑儿童",
|
||||
"deleteChild": "删除儿童",
|
||||
"noChildren": "还没有添加儿童",
|
||||
"noChildrenSubtitle": "添加您的第一个儿童以开始追踪他们的活动",
|
||||
"age": "年龄",
|
||||
"gender": {
|
||||
"male": "男",
|
||||
"female": "女",
|
||||
"other": "其他"
|
||||
},
|
||||
"errors": {
|
||||
"noFamily": "未找到家庭。请先完成入职流程。",
|
||||
"loadFailed": "加载儿童失败",
|
||||
"saveFailed": "保存儿童失败",
|
||||
"deleteFailed": "删除儿童失败",
|
||||
"noFamilyId": "未找到家庭ID"
|
||||
},
|
||||
"ageFormat": {
|
||||
"year": "年",
|
||||
"years": "年",
|
||||
"month": "个月",
|
||||
"months": "个月"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,14 @@
|
||||
{
|
||||
"title": "Activity Tracking",
|
||||
"title": "活动追踪",
|
||||
"trackActivity": "记录活动",
|
||||
"selectActivity": "选择要追踪的活动",
|
||||
"activities": {
|
||||
"feeding": "喂养",
|
||||
"sleep": "睡眠",
|
||||
"diaper": "尿布",
|
||||
"medicine": "药物",
|
||||
"activity": "活动"
|
||||
},
|
||||
"feeding": {
|
||||
"title": "Feeding",
|
||||
"addFeeding": "Add Feeding",
|
||||
|
||||
Reference in New Issue
Block a user