feat(login): add language dropdown, browser auto-detection and configurable default

Replace the language cycling button on the login page with a dropdown
showing all 14 supported languages. Add automatic browser/OS language
detection via navigator.languages, falling back to a configurable
DEFAULT_LANGUAGE env var, then 'en' as last resort.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Isaias Tavares
2026-04-11 17:54:50 -03:00
parent 34df665944
commit 57503a6a10
8 changed files with 130 additions and 28 deletions
+3
View File
@@ -6,6 +6,9 @@ NODE_ENV=development # development = development mode; production = production m
# existing encrypted data remains readable, then re-save credentials via the admin panel.
# Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
TZ=UTC # Timezone for logs, reminders and scheduled tasks (e.g. Europe/Berlin)
# DEFAULT_LANGUAGE=en # Default language on the login page for users with no saved preference (default: en)
# Supported values: de, en, es, fr, hu, nl, br, cs, pl, ru, zh, zh-TW, it, ar
# Note: browser/OS language is detected automatically first; this is the fallback when no match is found.
LOG_LEVEL=info # info = concise user actions; debug = verbose admin-level details
ALLOWED_ORIGINS=https://trek.example.com # Comma-separated origins for CORS and email links
+2 -1
View File
@@ -6,7 +6,7 @@ import path from 'node:path';
import fs from 'node:fs';
import jwt from 'jsonwebtoken';
import { JWT_SECRET } from './config';
import { JWT_SECRET, DEFAULT_LANGUAGE } from './config';
import { logDebug, logWarn, logError } from './services/auditLog';
import { enforceGlobalMfaPolicy } from './middleware/mfaPolicy';
import { authenticate } from './middleware/auth';
@@ -193,6 +193,7 @@ export function createApp(): express.Application {
app.use('/api/trips/:tripId/reservations', reservationsRoutes);
app.use('/api/trips/:tripId/days/:dayId/notes', dayNotesRoutes);
app.get('/api/health', (_req: Request, res: Response) => res.json({ status: 'ok' }));
app.get('/api/config', (_req: Request, res: Response) => res.json({ defaultLanguage: DEFAULT_LANGUAGE }));
app.use('/api', assignmentsRoutes);
app.use('/api/tags', tagsRoutes);
app.use('/api/categories', categoriesRoutes);
+5
View File
@@ -98,3 +98,8 @@ if (_encryptionKey) {
}
export const ENCRYPTION_KEY = _encryptionKey;
// DEFAULT_LANGUAGE sets the language shown on the login page before the user
// selects one. Only applies when the user has no saved language preference.
// Supported values: de, en, es, fr, hu, nl, br, cs, pl, ru, zh, zh-TW, it, ar
export const DEFAULT_LANGUAGE = process.env.DEFAULT_LANGUAGE || 'en';