From 44ebdcb8eef111b2898d95e8f891576339fdfa4d Mon Sep 17 00:00:00 2001 From: Michelle Date: Tue, 24 Feb 2026 18:40:22 +0100 Subject: [PATCH] fix language selection and missing locales --- package.json | 2 +- src/App.jsx | 10 ++++++++++ src/i18n/de.json | 29 ++++++++++++++++++++++++++++- src/i18n/en.json | 29 ++++++++++++++++++++++++++++- src/pages/GuestJoin.jsx | 36 +++++++++++++++++++----------------- src/pages/Settings.jsx | 13 ++++++++++++- 6 files changed, 98 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index 559a84f..a1b983e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redlight", "private": true, - "version": "1.0.0", + "version": "1.0.1", "type": "module", "scripts": { "dev": "concurrently -n client,server -c blue,green \"vite\" \"node --watch server/index.js\"", diff --git a/src/App.jsx b/src/App.jsx index e26924e..29674ab 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,5 +1,7 @@ +import { useEffect } from 'react'; import { Routes, Route, Navigate } from 'react-router-dom'; import { useAuth } from './contexts/AuthContext'; +import { useLanguage } from './contexts/LanguageContext'; import Layout from './components/Layout'; import ProtectedRoute from './components/ProtectedRoute'; import Home from './pages/Home'; @@ -13,6 +15,14 @@ import GuestJoin from './pages/GuestJoin'; export default function App() { const { user, loading } = useAuth(); + const { setLanguage } = useLanguage(); + + // Sync language from server when user loads + useEffect(() => { + if (user?.language) { + setLanguage(user.language); + } + }, [user?.language, setLanguage]); if (loading) { return ( diff --git a/src/i18n/de.json b/src/i18n/de.json index 21aacb6..9f8e411 100644 --- a/src/i18n/de.json +++ b/src/i18n/de.json @@ -157,7 +157,34 @@ "joinFailed": "Beitritt fehlgeschlagen", "endConfirm": "Meeting wirklich beenden?", "enterAccessCode": "Zugangscode eingeben:", - "notFound": "Raum nicht gefunden" + "notFound": "Raum nicht gefunden", + "guestAccessTitle": "Gastzugang", + "guestAccess": "Gastzugang aktivieren", + "guestAccessHint": "Ermöglicht nicht angemeldeten Benutzern, dem Meeting beizutreten.", + "moderatorCode": "Moderator-Code", + "moderatorCodeHint": "Optionaler Code für Moderator-Rechte", + "moderatorCodeDesc": "Gäste, die diesen Code eingeben, erhalten Moderator-Rechte.", + "guestLink": "Gast-Einladungslink", + "guestLinkCopied": "Gast-Link kopiert!", + "guestJoinTitle": "Meeting beitreten", + "guestCreatedBy": "Erstellt von", + "guestMeetingRunning": "Meeting läuft", + "guestMeetingNotStarted": "Noch nicht gestartet", + "guestYourName": "Ihr Name", + "guestNamePlaceholder": "Max Mustermann", + "guestAccessCode": "Zugangscode", + "guestAccessCodePlaceholder": "Code eingeben", + "guestModeratorCode": "Moderator-Code", + "guestModeratorOptional": "(optional)", + "guestModeratorPlaceholder": "Nur wenn Sie Moderator sind", + "guestJoinButton": "Meeting beitreten", + "guestWaitingMessage": "Das Meeting wurde noch nicht gestartet. Bitte warten Sie, bis der Moderator es startet.", + "guestAccessDenied": "Zugang nicht möglich", + "guestNameRequired": "Name ist erforderlich", + "guestJoinFailed": "Beitritt fehlgeschlagen", + "guestHasAccount": "Haben Sie ein Konto?", + "guestSignIn": "Anmelden", + "guestRoomNotFound": "Raum nicht gefunden" }, "recordings": { "title": "Aufnahmen", diff --git a/src/i18n/en.json b/src/i18n/en.json index 0dd55d3..6bc541b 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -157,7 +157,34 @@ "joinFailed": "Join failed", "endConfirm": "Really end meeting?", "enterAccessCode": "Enter access code:", - "notFound": "Room not found" + "notFound": "Room not found", + "guestAccessTitle": "Guest Access", + "guestAccess": "Enable guest access", + "guestAccessHint": "Allows unauthenticated users to join the meeting.", + "moderatorCode": "Moderator Code", + "moderatorCodeHint": "Optional code for moderator rights", + "moderatorCodeDesc": "Guests who enter this code will receive moderator rights.", + "guestLink": "Guest Invite Link", + "guestLinkCopied": "Guest link copied!", + "guestJoinTitle": "Join Meeting", + "guestCreatedBy": "Created by", + "guestMeetingRunning": "Meeting in progress", + "guestMeetingNotStarted": "Not started yet", + "guestYourName": "Your Name", + "guestNamePlaceholder": "John Doe", + "guestAccessCode": "Access Code", + "guestAccessCodePlaceholder": "Enter code", + "guestModeratorCode": "Moderator Code", + "guestModeratorOptional": "(optional)", + "guestModeratorPlaceholder": "Only if you are a moderator", + "guestJoinButton": "Join meeting", + "guestWaitingMessage": "The meeting has not started yet. Please wait for the moderator to start it.", + "guestAccessDenied": "Access denied", + "guestNameRequired": "Name is required", + "guestJoinFailed": "Join failed", + "guestHasAccount": "Have an account?", + "guestSignIn": "Sign in", + "guestRoomNotFound": "Room not found" }, "recordings": { "title": "Recordings", diff --git a/src/pages/GuestJoin.jsx b/src/pages/GuestJoin.jsx index fd1b1bc..694ed7f 100644 --- a/src/pages/GuestJoin.jsx +++ b/src/pages/GuestJoin.jsx @@ -3,9 +3,11 @@ import { useParams, Link } from 'react-router-dom'; import { Video, User, Lock, Shield, ArrowRight, Loader2, Users, Radio } from 'lucide-react'; import api from '../services/api'; import toast from 'react-hot-toast'; +import { useLanguage } from '../contexts/LanguageContext'; export default function GuestJoin() { const { uid } = useParams(); + const { t } = useLanguage(); const [roomInfo, setRoomInfo] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); @@ -22,7 +24,7 @@ export default function GuestJoin() { setRoomInfo(res.data.room); setStatus({ running: res.data.running }); } catch (err) { - setError(err.response?.data?.error || 'Raum nicht gefunden'); + setError(err.response?.data?.error || t('room.guestRoomNotFound')); } finally { setLoading(false); } @@ -44,7 +46,7 @@ export default function GuestJoin() { const handleJoin = async (e) => { e.preventDefault(); if (!name.trim()) { - toast.error('Name ist erforderlich'); + toast.error(t('room.guestNameRequired')); return; } @@ -59,7 +61,7 @@ export default function GuestJoin() { window.location.href = res.data.joinUrl; } } catch (err) { - toast.error(err.response?.data?.error || 'Beitritt fehlgeschlagen'); + toast.error(err.response?.data?.error || t('room.guestJoinFailed')); } finally { setJoining(false); } @@ -87,10 +89,10 @@ export default function GuestJoin() {
-

Zugang nicht möglich

+

{t('room.guestAccessDenied')}

{error}

- Zum Login + {t('auth.login')} @@ -124,7 +126,7 @@ export default function GuestJoin() {

{roomInfo.name}

- Erstellt von {roomInfo.owner_name} + {t('room.guestCreatedBy')} {roomInfo.owner_name}

{status.running ? : } - {status.running ? 'Meeting läuft' : 'Noch nicht gestartet'} + {status.running ? t('room.guestMeetingRunning') : t('room.guestMeetingNotStarted')}
{/* Join form */}
- +
setName(e.target.value)} className="input-field pl-11" - placeholder="Max Mustermann" + placeholder={t('room.guestNamePlaceholder')} required autoFocus /> @@ -157,7 +159,7 @@ export default function GuestJoin() { {roomInfo.has_access_code && (
- +
setAccessCode(e.target.value)} className="input-field pl-11" - placeholder="Code eingeben" + placeholder={t('room.guestAccessCodePlaceholder')} />
@@ -173,8 +175,8 @@ export default function GuestJoin() {
@@ -183,7 +185,7 @@ export default function GuestJoin() { value={moderatorCode} onChange={e => setModeratorCode(e.target.value)} className="input-field pl-11" - placeholder="Nur wenn Sie Moderator sind" + placeholder={t('room.guestModeratorPlaceholder')} />
@@ -197,7 +199,7 @@ export default function GuestJoin() { ) : ( <> - Meeting beitreten + {t('room.guestJoinButton')} )} @@ -205,14 +207,14 @@ export default function GuestJoin() { {!status.running && (

- Das Meeting wurde noch nicht gestartet. Bitte warten Sie, bis der Moderator es startet. + {t('room.guestWaitingMessage')}

)}
- Haben Sie ein Konto? Anmelden + {t('room.guestHasAccount')} {t('room.guestSignIn')}
diff --git a/src/pages/Settings.jsx b/src/pages/Settings.jsx index 951bffd..f732ba8 100644 --- a/src/pages/Settings.jsx +++ b/src/pages/Settings.jsx @@ -23,6 +23,16 @@ export default function Settings() { }); const [savingProfile, setSavingProfile] = useState(false); const [savingPassword, setSavingPassword] = useState(false); + + const handleLanguageChange = async (lang) => { + setLanguage(lang); + try { + const res = await api.put('/auth/profile', { language: lang }); + updateUser(res.data.user); + } catch { + // Language is still saved locally even if API fails + } + }; const [activeSection, setActiveSection] = useState('profile'); const [uploadingAvatar, setUploadingAvatar] = useState(false); const fileInputRef = useRef(null); @@ -44,6 +54,7 @@ export default function Settings() { name: profile.name, email: profile.email, theme, + language, avatar_color: user?.avatar_color, }); updateUser(res.data.user); @@ -349,7 +360,7 @@ export default function Settings() { ].map(lang => (