From 6773040d592ad328f9564593d9dccb73c093b2aa Mon Sep 17 00:00:00 2001 From: lobo Date: Thu, 24 Sep 2026 10:29:57 +0200 Subject: [PATCH] fix(login): translated error messages for failed and rate-limited logins Map INVALID_CREDENTIALS and TOO_MANY_ATTEMPTS to i18n keys. A non-JSON error body (Traefik 502/504) no longer shows as a network error. --- public/i18n/de.json | 5 ++++- public/i18n/en.json | 5 ++++- src/pages/LoginPage.jsx | 16 ++++++++++------ 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/public/i18n/de.json b/public/i18n/de.json index 9d2402e..e3f42ef 100644 --- a/public/i18n/de.json +++ b/public/i18n/de.json @@ -11,7 +11,10 @@ "SUBMIT": "Spiel betreten", "NO_ACCOUNT": "Noch kein Konto?", "ERROR_EMAIL": "Gib eine gültige Email-Adresse ein mit '@' und mit '.'.", - "ERROR_PASSWORD": "Das Passwort darf nicht leer sein." + "ERROR_PASSWORD": "Das Passwort darf nicht leer sein.", + "ERROR_INVALID_CREDENTIALS": "E-Mail oder Passwort ist falsch.", + "ERROR_TOO_MANY_ATTEMPTS": "Zu viele fehlgeschlagene Versuche. Bitte in {{minutes}} Minuten erneut versuchen.", + "ERROR_GENERIC": "Anmeldung fehlgeschlagen. Bitte später erneut versuchen." }, "REGISTRATION": { "TITLE": "Registrieren", diff --git a/public/i18n/en.json b/public/i18n/en.json index d5d2790..3bab297 100644 --- a/public/i18n/en.json +++ b/public/i18n/en.json @@ -11,7 +11,10 @@ "SUBMIT": "Enter the game", "NO_ACCOUNT": "No account yet?", "ERROR_EMAIL": "Enter a valid email address with '@' and with '.'.", - "ERROR_PASSWORD": "The password must have at least one character." + "ERROR_PASSWORD": "The password must have at least one character.", + "ERROR_INVALID_CREDENTIALS": "Email or password is wrong.", + "ERROR_TOO_MANY_ATTEMPTS": "Too many failed attempts. Please try again in {{minutes}} minutes.", + "ERROR_GENERIC": "Login failed. Please try again later." }, "REGISTRATION": { "TITLE": "Register", diff --git a/src/pages/LoginPage.jsx b/src/pages/LoginPage.jsx index 89b8773..14b03e4 100644 --- a/src/pages/LoginPage.jsx +++ b/src/pages/LoginPage.jsx @@ -10,6 +10,11 @@ import AuthLayout from '../components/AuthLayout.jsx'; import FeedbackAlert from '../components/FeedbackAlert.jsx'; import {getValidToken} from '../utils/auth.js'; +const LOGIN_ERROR_KEY = { + INVALID_CREDENTIALS: 'LOGIN.ERROR_INVALID_CREDENTIALS', + TOO_MANY_ATTEMPTS: 'LOGIN.ERROR_TOO_MANY_ATTEMPTS', +}; + export default function LoginPage() { const {t} = useTranslation(); const navigate = useNavigate(); @@ -40,17 +45,16 @@ export default function LoginPage() { body: JSON.stringify({email, password}), }); - // Parse the JSON response (matches our Spring Boot LoginResponse record) - const data = await response.json(); + // A proxy error (502/504) has an HTML body, so a parse failure is not a network error. + const data = await response.json().catch(() => ({})); if (response.ok) { - // HTTP 200 OK localStorage.setItem('tbp_jwt', data.token); navigate('/dashboard', {replace: true}); } else { - // HTTP 401 Unauthorized or other errors + const minutes = Math.ceil(Number(response.headers.get('Retry-After') ?? 900) / 60); setFeedback({ - message: `Error: ${data.message || 'Login failed'}`, + message: t(LOGIN_ERROR_KEY[data.code] ?? 'LOGIN.ERROR_GENERIC', {minutes}), isError: true }); } @@ -58,7 +62,7 @@ export default function LoginPage() { // Network errors (e.g., backend is not running or CORS issue) console.error('Login API Error:', error); setFeedback({ - message: 'Network error: Could not connect to the authentication service.', + message: t('INFO.ERROR.NETWORK_FAILURE'), isError: true }); } finally {