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.
This commit is contained in:
lobo committed 2026-09-24 10:29:57 +02:00
1 parent e480369a98
commit 6773040d59
3 files changed
+18 -8

No files matched your search

+4 -1
View File
@@ -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",
+4 -1
View File
@@ -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",
+10 -6
View File
@@ -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 {