feat: Complete high-priority i18n localization with date/time support
This commit implements comprehensive localization for high-priority components: ## Tracking Pages (4 files) - Localized feeding, sleep, diaper, and medicine tracking pages - Replaced hardcoded strings with translation keys from tracking namespace - Added useTranslation hook integration - All form labels, buttons, and messages now support multiple languages ## Child Dialog Components (2 files) - Localized ChildDialog (add/edit child form) - Localized DeleteConfirmDialog - Added new translation keys to children.json for dialog content - Includes validation messages and action buttons ## Date/Time Localization (14 files + new hook) - Created useLocalizedDate hook wrapping date-fns with locale support - Supports 5 languages: English, Spanish, French, Portuguese, Chinese - Updated all date formatting across: * Tracking pages (feeding, sleep, diaper, medicine) * Activity pages (activities, history, track activity) * Settings components (sessions, biometric, device trust) * Analytics components (insights, growth, sleep chart, feeding graph) - Date displays automatically adapt to user's language (e.g., "2 hours ago" → "hace 2 horas") ## Translation Updates - Enhanced children.json with dialog section containing: * Form field labels (name, birthDate, gender, photoUrl) * Action buttons (add, update, delete, cancel, saving, deleting) * Delete confirmation messages * Validation error messages Files changed: 17 files (+164, -113) Languages supported: en, es, fr, pt-BR, zh-CN 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
||||
Alert,
|
||||
} from '@mui/material';
|
||||
import { Child, CreateChildData } from '@/lib/api/children';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
|
||||
interface ChildDialogProps {
|
||||
open: boolean;
|
||||
@@ -23,6 +24,7 @@ interface ChildDialogProps {
|
||||
}
|
||||
|
||||
export function ChildDialog({ open, onClose, onSubmit, child, isLoading = false }: ChildDialogProps) {
|
||||
const { t } = useTranslation('children');
|
||||
const [formData, setFormData] = useState<CreateChildData>({
|
||||
name: '',
|
||||
birthDate: '',
|
||||
@@ -61,11 +63,11 @@ export function ChildDialog({ open, onClose, onSubmit, child, isLoading = false
|
||||
|
||||
// Validation
|
||||
if (!formData.name.trim()) {
|
||||
setError('Please enter a name');
|
||||
setError(t('dialog.validation.nameRequired'));
|
||||
return;
|
||||
}
|
||||
if (!formData.birthDate) {
|
||||
setError('Please select a birth date');
|
||||
setError(t('dialog.validation.birthDateRequired'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -74,7 +76,7 @@ export function ChildDialog({ open, onClose, onSubmit, child, isLoading = false
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
if (selectedDate > today) {
|
||||
setError('Birth date cannot be in the future');
|
||||
setError(t('dialog.validation.birthDateFuture'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -82,7 +84,7 @@ export function ChildDialog({ open, onClose, onSubmit, child, isLoading = false
|
||||
await onSubmit(formData);
|
||||
onClose();
|
||||
} catch (err: any) {
|
||||
setError(err.message || 'Failed to save child');
|
||||
setError(err.message || t('errors.saveFailed'));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -95,7 +97,7 @@ export function ChildDialog({ open, onClose, onSubmit, child, isLoading = false
|
||||
aria-labelledby="child-dialog-title"
|
||||
aria-describedby="child-dialog-description"
|
||||
>
|
||||
<DialogTitle id="child-dialog-title">{child ? 'Edit Child' : 'Add Child'}</DialogTitle>
|
||||
<DialogTitle id="child-dialog-title">{child ? t('editChild') : t('addChild')}</DialogTitle>
|
||||
<DialogContent>
|
||||
<Box
|
||||
id="child-dialog-description"
|
||||
@@ -108,7 +110,7 @@ export function ChildDialog({ open, onClose, onSubmit, child, isLoading = false
|
||||
)}
|
||||
|
||||
<TextField
|
||||
label="Name"
|
||||
label={t('dialog.name')}
|
||||
value={formData.name}
|
||||
onChange={handleChange('name')}
|
||||
fullWidth
|
||||
@@ -118,7 +120,7 @@ export function ChildDialog({ open, onClose, onSubmit, child, isLoading = false
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="Birth Date"
|
||||
label={t('dialog.birthDate')}
|
||||
type="date"
|
||||
value={formData.birthDate}
|
||||
onChange={handleChange('birthDate')}
|
||||
@@ -131,7 +133,7 @@ export function ChildDialog({ open, onClose, onSubmit, child, isLoading = false
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="Gender"
|
||||
label={t('dialog.gender')}
|
||||
value={formData.gender}
|
||||
onChange={handleChange('gender')}
|
||||
fullWidth
|
||||
@@ -139,27 +141,27 @@ export function ChildDialog({ open, onClose, onSubmit, child, isLoading = false
|
||||
select
|
||||
disabled={isLoading}
|
||||
>
|
||||
<MenuItem value="male">Male</MenuItem>
|
||||
<MenuItem value="female">Female</MenuItem>
|
||||
<MenuItem value="other">Other</MenuItem>
|
||||
<MenuItem value="male">{t('gender.male')}</MenuItem>
|
||||
<MenuItem value="female">{t('gender.female')}</MenuItem>
|
||||
<MenuItem value="other">{t('gender.other')}</MenuItem>
|
||||
</TextField>
|
||||
|
||||
<TextField
|
||||
label="Photo URL (Optional)"
|
||||
label={t('dialog.photoUrl')}
|
||||
value={formData.photoUrl}
|
||||
onChange={handleChange('photoUrl')}
|
||||
fullWidth
|
||||
placeholder="https://example.com/photo.jpg"
|
||||
placeholder={t('dialog.photoPlaceholder')}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={onClose} disabled={isLoading}>
|
||||
Cancel
|
||||
{t('dialog.cancel')}
|
||||
</Button>
|
||||
<Button onClick={handleSubmit} variant="contained" disabled={isLoading}>
|
||||
{isLoading ? 'Saving...' : child ? 'Update' : 'Add'}
|
||||
{isLoading ? t('dialog.saving') : child ? t('dialog.update') : t('dialog.add')}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
Reference in New Issue
Block a user