Englisch als zweite Sprache fuer die oeffentlichen Seiten
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:
@@ -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>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user