feat: Localize Sleep, Diaper, Activity, and Settings pages
Added comprehensive localization to tracking and settings pages: **Translation Keys Added:** - Sleep: locations, status, duration formatting, success/delete messages - Diaper: conditions, rash severity and alert, success/delete messages - Activity: activity types, form labels, placeholders - Settings: profile, preferences, notifications, appearance, account actions - Common: shared labels (selectChild, noChildrenAdded, etc.) **Pages Localized:** 1. Sleep tracking page (/app/track/sleep/page.tsx) - All form labels and dropdowns - Location options (crib, bed, stroller, carrier, other) - Sleep status (completed/ongoing) - Duration display with interpolation - Success and delete messages 2. Diaper tracking page (/app/track/diaper/page.tsx) - Diaper types (wet, dirty, both, dry) - Conditions (normal, soft, hard, watery, mucus, blood) - Rash detection with severity levels - Alert message for diaper rash - Recent diapers display with translated labels 3. Activity tracking page (/app/track/activity/page.tsx) - Activity types (play, walk, music, reading, tummy time, outdoor, other) - Duration and description fields - Form placeholders - Recent activities display 4. Settings page (/app/settings/page.tsx) - Profile information section - Preferences, notifications, appearance sections - Account actions (logout) - Save/saving button states - Success message All pages now support multi-language translation and are ready for Spanish, French, Portuguese, and Chinese translations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -18,8 +18,10 @@ import { MeasurementUnitSelector } from '@/components/settings/MeasurementUnitSe
|
||||
import { TimeZoneSelector } from '@/components/settings/TimeZoneSelector';
|
||||
import { TimeFormatSelector } from '@/components/settings/TimeFormatSelector';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
|
||||
export default function SettingsPage() {
|
||||
const { t } = useTranslation('settings');
|
||||
const { user, logout, refreshUser } = useAuth();
|
||||
const [name, setName] = useState(user?.name || '');
|
||||
const [timezone, setTimezone] = useState(user?.timezone || 'UTC');
|
||||
@@ -61,7 +63,7 @@ export default function SettingsPage() {
|
||||
const handleSaveAll = async () => {
|
||||
// Validate name
|
||||
if (!name || name.trim() === '') {
|
||||
setNameError('Name cannot be empty');
|
||||
setNameError(t('profile.nameRequired'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -83,7 +85,7 @@ export default function SettingsPage() {
|
||||
// Refresh user to get latest data from server
|
||||
await refreshUser();
|
||||
|
||||
setSuccessMessage('Settings saved successfully!');
|
||||
setSuccessMessage(t('saved'));
|
||||
} catch (err: any) {
|
||||
console.error('❌ Failed to save settings:', err);
|
||||
console.error('Error response:', err.response);
|
||||
@@ -102,10 +104,10 @@ export default function SettingsPage() {
|
||||
<AppShell>
|
||||
<Box sx={{ maxWidth: 'md', mx: 'auto' }}>
|
||||
<Typography variant="h4" component="h1" fontWeight="600" gutterBottom>
|
||||
Settings
|
||||
{t('title')}
|
||||
</Typography>
|
||||
<Typography variant="body1" color="text.secondary" sx={{ mb: 4 }}>
|
||||
Manage your account settings and preferences
|
||||
{t('profile.title')}
|
||||
</Typography>
|
||||
|
||||
{/* Error Alert */}
|
||||
@@ -130,11 +132,11 @@ export default function SettingsPage() {
|
||||
<Card sx={{ mb: 3 }}>
|
||||
<CardContent>
|
||||
<Typography variant="h6" component="h2" fontWeight="600" gutterBottom>
|
||||
Profile Information
|
||||
{t('profile.title')}
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, mt: 2 }}>
|
||||
<TextField
|
||||
label="Name"
|
||||
label={t('profile.name')}
|
||||
value={name}
|
||||
onChange={(e) => {
|
||||
setName(e.target.value);
|
||||
@@ -146,11 +148,11 @@ export default function SettingsPage() {
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<TextField
|
||||
label="Email"
|
||||
label={t('profile.email')}
|
||||
value={user?.email || ''}
|
||||
fullWidth
|
||||
disabled
|
||||
helperText="Email cannot be changed"
|
||||
helperText={t('profile.emailNotEditable')}
|
||||
/>
|
||||
</Box>
|
||||
</CardContent>
|
||||
@@ -166,7 +168,7 @@ export default function SettingsPage() {
|
||||
<Card sx={{ mb: 3 }}>
|
||||
<CardContent>
|
||||
<Typography variant="h6" component="h2" fontWeight="600" gutterBottom>
|
||||
Preferences
|
||||
{t('preferences.title')}
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3, mt: 2 }}>
|
||||
<LanguageSelector />
|
||||
@@ -193,7 +195,7 @@ export default function SettingsPage() {
|
||||
<Card sx={{ mb: 3 }}>
|
||||
<CardContent>
|
||||
<Typography variant="h6" component="h2" fontWeight="600" gutterBottom>
|
||||
Notifications
|
||||
{t('notifications.title')}
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1, mt: 2 }}>
|
||||
<FormControlLabel
|
||||
@@ -204,7 +206,7 @@ export default function SettingsPage() {
|
||||
disabled={isLoading}
|
||||
/>
|
||||
}
|
||||
label="Push Notifications"
|
||||
label={t('notifications.push')}
|
||||
/>
|
||||
<FormControlLabel
|
||||
control={
|
||||
@@ -214,7 +216,7 @@ export default function SettingsPage() {
|
||||
disabled={isLoading}
|
||||
/>
|
||||
}
|
||||
label="Email Updates"
|
||||
label={t('notifications.email')}
|
||||
/>
|
||||
</Box>
|
||||
</CardContent>
|
||||
@@ -230,7 +232,7 @@ export default function SettingsPage() {
|
||||
<Card sx={{ mb: 3 }}>
|
||||
<CardContent>
|
||||
<Typography variant="h6" component="h2" fontWeight="600" gutterBottom>
|
||||
Appearance
|
||||
{t('appearance.title')}
|
||||
</Typography>
|
||||
<Box sx={{ mt: 2 }}>
|
||||
<FormControlLabel
|
||||
@@ -240,7 +242,7 @@ export default function SettingsPage() {
|
||||
onChange={(e) => setSettings({ ...settings, darkMode: e.target.checked })}
|
||||
/>
|
||||
}
|
||||
label="Dark Mode (Coming Soon)"
|
||||
label={t('appearance.darkMode')}
|
||||
disabled
|
||||
/>
|
||||
</Box>
|
||||
@@ -329,7 +331,7 @@ export default function SettingsPage() {
|
||||
disabled={isLoading}
|
||||
sx={{ minWidth: 200 }}
|
||||
>
|
||||
{isLoading ? 'Saving...' : 'Save Preferences'}
|
||||
{isLoading ? t('saving') : t('save')}
|
||||
</Button>
|
||||
</Box>
|
||||
</motion.div>
|
||||
@@ -343,7 +345,7 @@ export default function SettingsPage() {
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Typography variant="h6" component="h2" fontWeight="600" gutterBottom>
|
||||
Account Actions
|
||||
{t('accountActions.title')}
|
||||
</Typography>
|
||||
<Divider sx={{ my: 2 }} />
|
||||
<Button
|
||||
@@ -353,7 +355,7 @@ export default function SettingsPage() {
|
||||
onClick={handleLogout}
|
||||
fullWidth
|
||||
>
|
||||
Logout
|
||||
{t('accountActions.logout')}
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user