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:
+41
-32
@@ -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>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user