Englisch als zweite Sprache fuer die oeffentlichen Seiten
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled

Hub und Bot-Produktseite lassen sich jetzt auf Englisch umstellen. Die Wahl
haengt am Browser (Deutsch nur, wenn der Browser es will) und bleibt im
localStorage stehen.

- src/i18n.jsx: Context mit t(), tOr(), Datums- und Zahlenformat der Sprache.
  Keine Bibliothek, die Woerterbuecher werden mitgebaut — kein Nachlade-Blitzer.
- Platzhalter im %s-Stil, dieselbe Schreibweise wie in den Bot-Texten.
- Umschalter in der Navigation, zusaetzlich im Schubladen-Menue: .auth ist auf
  schmalen Schirmen ausgeblendet, sonst waere der Wechsel dort unerreichbar.
- Datums- und Zahlenformat folgen mit (31.07.2026 / 31/07/2026, 45.231 / 45,231).
- Die Funktionsliste kommt weiter deutsch vom Server; englische Modulnamen
  liegen unter feat.<id> im Woerterbuch. Fehlt einer, bleibt der Servertext
  stehen — ein neues Modul verschwindet nie von der Seite.

Das Config-Panel und die Discord-Texte bleiben deutsch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-31 22:41:32 +02:00
co-authored by Claude Fable 5
parent 570e45fe60
commit 5360c8229f
19 changed files with 1084 additions and 329 deletions
+41 -32
View File
@@ -17,12 +17,15 @@ import Impressum from './pages/Impressum.jsx';
import Datenschutz from './pages/Datenschutz.jsx';
import Seite from './pages/Seite.jsx';
import { IconChevronDown, IconMenu, IconX, IconLock } from './icons.jsx';
import { useT, LangSwitch } from './i18n.jsx';
// Beschriftung erst beim Rendern uebersetzen — der Pfad bleibt deutsch,
// damit geteilte Links weiter funktionieren.
const COMMUNITY_LINKS = [
['/galerie', 'Galerie'],
['/level', 'Level'],
['/events', 'Events'],
['/changelog', 'Changelog'],
['/galerie', 'nav.gallery'],
['/level', 'nav.level'],
['/events', 'nav.events'],
['/changelog', 'nav.changelog'],
];
/** Dropdown in der Navbar (schließt bei Klick daneben und bei Navigation) */
@@ -86,6 +89,7 @@ export default function App() {
const [scrolled, setScrolled] = useState(false);
const [drawer, setDrawer] = useState(false);
const location = useLocation();
const { t } = useT();
const communityActive = COMMUNITY_LINKS.some(([to]) => location.pathname.startsWith(to));
// Drawer bei Seitenwechsel schließen
@@ -125,21 +129,22 @@ export default function App() {
</NavLink>
<nav className="nav-links">
<NavLink to="/devlogs">Devlog</NavLink>
<NavLink to="/roadmap">Roadmap</NavLink>
<NavLink to="/server">Server</NavLink>
<NavDropdown label="Community" active={communityActive}>
{COMMUNITY_LINKS.map(([to, label]) => (
<NavLink to={to} key={to}>{label}</NavLink>
<NavLink to="/devlogs">{t('nav.devlog')}</NavLink>
<NavLink to="/roadmap">{t('nav.roadmap')}</NavLink>
<NavLink to="/server">{t('nav.server')}</NavLink>
<NavDropdown label={t('nav.community')} active={communityActive}>
{COMMUNITY_LINKS.map(([to, key]) => (
<NavLink to={to} key={to}>{t(key)}</NavLink>
))}
{pages.map((p) => (
<NavLink to={`/${p.slug}`} key={p.slug}>{p.title}</NavLink>
))}
</NavDropdown>
{(me.admin || me.scopes?.length > 0) && <NavLink to="/settings">Config</NavLink>}
{(me.admin || me.scopes?.length > 0) && <NavLink to="/settings">{t('nav.config')}</NavLink>}
</nav>
<div className="auth">
<LangSwitch />
{me.loading ? null : me.user ? (
<NavDropdown
label={
@@ -149,39 +154,44 @@ export default function App() {
</span>
}
>
<NavLink to="/profil">Profil</NavLink>
{me.admin && <NavLink to="/commits">Commits</NavLink>}
<a href="/auth/logout">Logout</a>
<NavLink to="/profil">{t('nav.profile')}</NavLink>
{me.admin && <NavLink to="/commits">{t('nav.commits')}</NavLink>}
<a href="/auth/logout">{t('nav.logout')}</a>
</NavDropdown>
) : (
<a className="btn-login" href="/auth/login">
<DiscordMark /> Login
<DiscordMark /> {t('nav.login')}
</a>
)}
</div>
<button className="burger" onClick={() => setDrawer((d) => !d)} aria-label="Menü">
<button className="burger" onClick={() => setDrawer((d) => !d)} aria-label={t('nav.menu')}>
{drawer ? <IconX size={20} /> : <IconMenu size={20} />}
</button>
</header>
{drawer && (
<nav className="drawer" onClick={() => setDrawer(false)}>
<NavLink to="/devlogs">Devlog</NavLink>
<NavLink to="/roadmap">Roadmap</NavLink>
<NavLink to="/server">Server</NavLink>
{COMMUNITY_LINKS.map(([to, label]) => (
<NavLink to={to} key={to}>{label}</NavLink>
<NavLink to="/devlogs">{t('nav.devlog')}</NavLink>
<NavLink to="/roadmap">{t('nav.roadmap')}</NavLink>
<NavLink to="/server">{t('nav.server')}</NavLink>
{COMMUNITY_LINKS.map(([to, key]) => (
<NavLink to={to} key={to}>{t(key)}</NavLink>
))}
{pages.map((p) => (
<NavLink to={`/${p.slug}`} key={p.slug}>{p.title}</NavLink>
))}
{me.user && <NavLink to="/profil">Profil</NavLink>}
{me.admin && <NavLink to="/commits">Commits</NavLink>}
{(me.admin || me.scopes?.length > 0) && <NavLink to="/settings">Config</NavLink>}
{me.user && <NavLink to="/profil">{t('nav.profile')}</NavLink>}
{me.admin && <NavLink to="/commits">{t('nav.commits')}</NavLink>}
{(me.admin || me.scopes?.length > 0) && <NavLink to="/settings">{t('nav.config')}</NavLink>}
{me.user
? <a href="/auth/logout">Logout</a>
: <a href="/auth/login">Login mit Discord</a>}
? <a href="/auth/logout">{t('nav.logout')}</a>
: <a href="/auth/login">{t('nav.loginDiscord')}</a>}
{/* Auf schmalen Schirmen ist .auth ausgeblendet — der
Sprachwechsel muss trotzdem erreichbar bleiben. */}
<div className="drawer-lang" onClick={(e) => e.stopPropagation()}>
<LangSwitch />
</div>
</nav>
)}
@@ -190,15 +200,14 @@ export default function App() {
<div className="gate-banner">
<span className="with-icon">
<IconLock />
Der Login ist Mitgliedern des Discord-Servers vorbehalten
tritt erst dem Server bei und logg dich dann nochmal ein.
{t('gate.text')}
</span>
{invite && (
<a className="btn btn-save" href={invite} target="_blank" rel="noreferrer">
Zum Discord
{t('gate.join')}
</a>
)}
<button className="card-delete" onClick={() => setGated(false)} title="Ausblenden"><IconX size={15} /></button>
<button className="card-delete" onClick={() => setGated(false)} title={t('gate.hide')}><IconX size={15} /></button>
</div>
)}
<Routes>
@@ -229,8 +238,8 @@ export default function App() {
git.d4rkst3r.de
</a>
<span className="footer-sep">//</span>
<NavLink to="/impressum">Impressum</NavLink>
<NavLink to="/datenschutz">Datenschutz</NavLink>
<NavLink to="/impressum">{t('footer.imprint')}</NavLink>
<NavLink to="/datenschutz">{t('footer.privacy')}</NavLink>
</footer>
</div>
);