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
+20 -18
View File
@@ -5,8 +5,9 @@ import { useEffect, useState } from 'react';
import { apiGet } from '../api.js';
import { SkeletonCards } from '../components/Skeleton.jsx';
import { IconGame, IconMapPin, IconClock, IconCheck } from '../icons.jsx';
import { useT, useFmt } from '../i18n.jsx';
const timeFmt = new Intl.DateTimeFormat('de-DE', { hour: '2-digit', minute: '2-digit' });
const UHRZEIT = { hour: '2-digit', minute: '2-digit' };
/** 24h-Verlauf als SVG-Fläche; Offline-Samples als rote Punkte auf der Nulllinie */
function HistoryChart({ history, max }) {
@@ -48,6 +49,8 @@ function historyStats(history) {
}
function ServerCard({ s }) {
const { t } = useT();
const { datum } = useFmt();
const [copied, setCopied] = useState(false);
const pct = s.online && s.max ? Math.round((s.players / s.max) * 100) : 0;
const stats = historyStats(s.history);
@@ -70,8 +73,8 @@ function ServerCard({ s }) {
<span className="srv-name">{s.name}</span>
<span className={`srv-status ${s.online ? 'on' : s.online === false ? 'off' : ''}`}>
{s.online === null
? <><IconClock size={13} /> Warte auf Check</>
: <><span className="srv-dot" /> {s.online ? 'Online' : 'Offline'}</>}
? <><IconClock size={13} /> {t('server.waiting')}</>
: <><span className="srv-dot" /> {s.online ? t('server.online') : t('server.offline')}</>}
{s.ping != null && s.online && <span className="srv-ping"> · {s.ping}ms</span>}
{s.map && s.online && (
<span className="srv-map"> · <IconMapPin size={13} /> {s.map}</span>
@@ -95,9 +98,9 @@ function ServerCard({ s }) {
{s.history.length > 1 && <HistoryChart history={s.history} max={s.max} />}
{stats && (
<div className="srv-stats">
<span>Spielerzahl · letzte 24h</span>
<span>{t('server.players24h')}</span>
<span>
Peak <b>{stats.peak}</b> · Uptime{' '}
{t('server.peak')} <b>{stats.peak}</b> · {t('server.uptime')}{' '}
<b className={stats.uptime >= 99 ? 'up-good' : stats.uptime >= 90 ? '' : 'up-bad'}>
{stats.uptime}%
</b>
@@ -109,24 +112,25 @@ function ServerCard({ s }) {
<div className="srv-connect">
{s.connect_url && (
<a className="btn btn-save with-icon" href={s.connect_url}>
<IconGame size={15} /> Connect
<IconGame size={15} /> {t('server.connect')}
</a>
)}
{s.address && (
<button className="alpha-key srv-addr" onClick={copyAddress} title="Adresse kopieren">
{copied ? <span className="with-icon"><IconCheck size={14} /> Kopiert!</span> : s.address}
<button className="alpha-key srv-addr" onClick={copyAddress} title={t('server.copyAddress')}>
{copied ? <span className="with-icon"><IconCheck size={14} /> {t('server.copied')}</span> : s.address}
</button>
)}
</div>
)}
{s.checkedAt && (
<p className="srv-checked">Zuletzt geprüft: {timeFmt.format(new Date(s.checkedAt))} Uhr</p>
<p className="srv-checked">{t('server.checkedAt', datum(s.checkedAt, UHRZEIT))}</p>
)}
</article>
);
}
export default function Server() {
const { t } = useT();
const [servers, setServers] = useState(null);
const [error, setError] = useState(false);
@@ -147,28 +151,26 @@ export default function Server() {
<div className="page-bg-text">SERVER</div>
<div className="page-header-grid" aria-hidden="true" />
<div className="page-header-content">
<div className="page-tag">// Live-Status</div>
<h1 className="page-title">Server</h1>
<p className="page-subtitle">
Alle Game-Server auf einen Blick aktualisiert alle 2 Minuten.
</p>
<div className="page-tag">{t('server.tag')}</div>
<h1 className="page-title">{t('nav.server')}</h1>
<p className="page-subtitle">{t('server.subtitle')}</p>
</div>
</section>
<section className="content">
{error && <p className="notice">Server-Status konnte nicht geladen werden.</p>}
{error && <p className="notice">{t('server.error')}</p>}
{!error && !servers && <SkeletonCards n={2} />}
{servers?.length === 0 && <p className="notice">Noch keine Server eingetragen.</p>}
{servers?.length === 0 && <p className="notice">{t('server.none')}</p>}
{servers?.length > 0 && (
<div className="srv-summary">
<div className="srv-sum-item">
<span className="srv-sum-n">{online.length}<span className="srv-sum-of"> / {servers.length}</span></span>
<span className="srv-sum-label">Server online</span>
<span className="srv-sum-label">{t('server.serversOnline')}</span>
</div>
<div className="srv-sum-item">
<span className="srv-sum-n">{totalPlayers}</span>
<span className="srv-sum-label">Spieler gerade drauf</span>
<span className="srv-sum-label">{t('server.playersNow')}</span>
</div>
</div>
)}