feat: Apply localization to Login, Dashboard, and Navigation (Phase 9 - Batch 1)
**Pages Localized:** - Login page: All UI strings (titles, labels, buttons, links) - Dashboard page: Welcome message, quick actions, daily summary, predictions - AppShell: Connection status and presence indicators - MobileNav: Menu items and app branding - TabBar: Bottom navigation labels **Translation Files:** - Created dashboard.json for all 5 languages (en, es, fr, pt, zh) - Enhanced common.json with navigation and connection strings - Updated i18n config to include dashboard namespace **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:
@@ -26,6 +26,7 @@ import { tokenStorage } from '@/lib/utils/tokenStorage';
|
||||
import { biometricApi } from '@/lib/api/biometric';
|
||||
import { startAuthentication } from '@simplewebauthn/browser';
|
||||
import Link from 'next/link';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
|
||||
const loginSchema = z.object({
|
||||
email: z.string().email('Invalid email address'),
|
||||
@@ -35,6 +36,7 @@ const loginSchema = z.object({
|
||||
type LoginFormData = z.infer<typeof loginSchema>;
|
||||
|
||||
export default function LoginPage() {
|
||||
const { t } = useTranslation('auth');
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
@@ -178,7 +180,7 @@ export default function LoginPage() {
|
||||
fontWeight="600"
|
||||
color="primary.main"
|
||||
>
|
||||
Welcome Back 👋
|
||||
{t('login.title')}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
@@ -186,7 +188,7 @@ export default function LoginPage() {
|
||||
color="text.secondary"
|
||||
sx={{ mb: 3 }}
|
||||
>
|
||||
Sign in to continue tracking your child's journey
|
||||
{t('login.subtitle')}
|
||||
</Typography>
|
||||
|
||||
{error && (
|
||||
@@ -198,7 +200,7 @@ export default function LoginPage() {
|
||||
<Box component="form" onSubmit={handleSubmit(onSubmit)}>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Email"
|
||||
label={t('login.email')}
|
||||
type="email"
|
||||
margin="normal"
|
||||
error={!!errors.email}
|
||||
@@ -213,7 +215,7 @@ export default function LoginPage() {
|
||||
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Password"
|
||||
label={t('login.password')}
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
margin="normal"
|
||||
error={!!errors.password}
|
||||
@@ -245,7 +247,7 @@ export default function LoginPage() {
|
||||
variant="body2"
|
||||
sx={{ cursor: 'pointer', textDecoration: 'none' }}
|
||||
>
|
||||
Forgot password?
|
||||
{t('login.forgotPassword')}
|
||||
</MuiLink>
|
||||
</Box>
|
||||
|
||||
@@ -260,14 +262,14 @@ export default function LoginPage() {
|
||||
{isLoading ? (
|
||||
<CircularProgress size={24} color="inherit" />
|
||||
) : (
|
||||
'Sign In'
|
||||
t('login.submit')
|
||||
)}
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
<Divider sx={{ my: 3 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
OR
|
||||
{t('login.or')}
|
||||
</Typography>
|
||||
</Divider>
|
||||
|
||||
@@ -279,7 +281,7 @@ export default function LoginPage() {
|
||||
disabled={isLoading}
|
||||
sx={{ mb: 2 }}
|
||||
>
|
||||
Continue with Google
|
||||
{t('login.continueWithGoogle')}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
@@ -289,7 +291,7 @@ export default function LoginPage() {
|
||||
size="large"
|
||||
disabled={isLoading}
|
||||
>
|
||||
Continue with Apple
|
||||
{t('login.continueWithApple')}
|
||||
</Button>
|
||||
|
||||
{isBiometricSupported && (
|
||||
@@ -302,16 +304,16 @@ export default function LoginPage() {
|
||||
onClick={handleBiometricLogin}
|
||||
sx={{ mt: 2 }}
|
||||
>
|
||||
{isBiometricLoading ? 'Authenticating...' : 'Sign in with Biometrics'}
|
||||
{isBiometricLoading ? t('login.biometric.authenticating') : t('login.biometric.useFaceId')}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Box sx={{ mt: 3, textAlign: 'center' }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Don't have an account?{' '}
|
||||
{t('login.noAccount')}{' '}
|
||||
<Link href="/register" passHref legacyBehavior>
|
||||
<MuiLink sx={{ cursor: 'pointer', fontWeight: 600 }}>
|
||||
Sign up
|
||||
{t('login.signUp')}
|
||||
</MuiLink>
|
||||
</Link>
|
||||
</Typography>
|
||||
|
||||
Reference in New Issue
Block a user