Update legal documents and localization for compliance and clarity

- Updated .env.example with legal operator details for compliance.
- Refactored Impressum, Privacy, and Terms pages to utilize a new LegalDocument component for improved structure and maintainability.
- Enhanced localization files to include legal content in both German and English, ensuring accurate representation of legal information.
- Improved i18n handling to support locale detection from Accept-Language headers.
This commit is contained in:
TheOnlyMace
2026-07-22 19:18:28 +02:00
parent 8c02b95934
commit bda5c116fe
21 changed files with 1700 additions and 240 deletions

View File

@@ -0,0 +1,143 @@
<?php
declare(strict_types=1);
/**
* @param array<string, mixed> $config
* @param array<string, mixed> $messages
* @param 'de'|'en' $locale
*/
function render_header(array $config, array $messages, string $locale, string $title, string $description): void
{
$inviteUrl = bot_invite_url($config);
$dashboardUrl = rtrim((string) $config['dashboard_url'], '/');
$loginUrl = $dashboardUrl . '/login';
$supportUrl = isset($config['support_url']) && $config['support_url'] !== ''
? (string) $config['support_url']
: null;
$statusUrl = (string) $config['status_url'];
?>
<!DOCTYPE html>
<html lang="<?= h($locale) ?>">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?= h($title) ?></title>
<meta name="description" content="<?= h($description) ?>">
<link rel="icon" href="assets/nexumi-logo.svg" type="image/svg+xml">
<link rel="stylesheet" href="assets/style.css">
</head>
<body>
<div class="shell">
<header class="border-b">
<div class="container bar">
<a class="brand" href="index.php">
<img src="assets/nexumi-logo.svg" alt="Nexumi" width="28" height="28">
Nexumi
</a>
<nav>
<a class="btn btn-ghost" href="<?= h($statusUrl) ?>"><?= h(t($messages, 'nav.status')) ?></a>
<a class="btn btn-ghost" href="<?= h($inviteUrl) ?>" target="_blank" rel="noreferrer"><?= h(t($messages, 'nav.invite')) ?></a>
<?php if ($supportUrl): ?>
<a class="btn btn-ghost" href="<?= h($supportUrl) ?>" target="_blank" rel="noreferrer"><?= h(t($messages, 'nav.support')) ?></a>
<?php endif; ?>
<a class="btn btn-primary" href="<?= h($loginUrl) ?>"><?= h(t($messages, 'nav.login')) ?></a>
</nav>
</div>
</header>
<main>
<?php
}
/**
* @param array<string, mixed> $config
* @param array<string, mixed> $messages
*/
function render_footer(array $config, array $messages): void
{
$dashboardUrl = rtrim((string) $config['dashboard_url'], '/');
$year = (int) date('Y');
?>
</main>
<footer class="border-t">
<div class="container bar">
<p>© <?= $year ?> Nexumi</p>
<nav>
<a href="<?= h((string) $config['status_url']) ?>"><?= h(t($messages, 'nav.status')) ?></a>
<a href="impressum.php"><?= h(t($messages, 'nav.impressum')) ?></a>
<a href="datenschutz.php"><?= h(t($messages, 'nav.privacy')) ?></a>
<a href="nutzungsbedingungen.php"><?= h(t($messages, 'nav.terms')) ?></a>
<a href="<?= h($dashboardUrl . '/dashboard') ?>"><?= h(t($messages, 'nav.dashboard')) ?></a>
</nav>
</div>
</footer>
</div>
</body>
</html>
<?php
}
/**
* @param array<int, array<string, mixed>> $sections
*/
function render_legal_sections(array $sections): void
{
foreach ($sections as $section) {
$heading = (string) ($section['heading'] ?? '');
echo '<section class="legal-section">';
if ($heading !== '') {
echo '<h2>' . h($heading) . '</h2>';
}
foreach (($section['paragraphs'] ?? []) as $paragraph) {
echo '<p>' . h((string) $paragraph) . '</p>';
}
if (!empty($section['lines']) && is_array($section['lines'])) {
echo '<p class="legal-lines">';
foreach ($section['lines'] as $i => $line) {
if ($i > 0) {
echo '<br>';
}
echo h((string) $line);
}
echo '</p>';
}
if (!empty($section['list']) && is_array($section['list'])) {
echo '<ul>';
foreach ($section['list'] as $item) {
echo '<li>' . h((string) $item) . '</li>';
}
echo '</ul>';
}
echo '</section>';
}
}
/**
* @param array<string, mixed> $config
* @param array<string, mixed> $messages
* @param 'de'|'en' $locale
* @param 'impressum'|'privacy'|'terms' $pageKey
*/
function render_legal_page(array $config, array $messages, string $locale, string $pageKey): void
{
/** @var array<string, mixed> $page */
$page = $messages['legal'][$pageKey];
$title = (string) $page['title'];
$description = t($messages, 'meta.description');
render_header($config, $messages, $locale, $title . ' · Nexumi', $description);
?>
<div class="container legal-layout">
<aside class="legal-nav">
<p class="legal-nav-title"><?= h(t($messages, 'legal.nav_title')) ?></p>
<a href="impressum.php" class="<?= $pageKey === 'impressum' ? 'active' : '' ?>"><?= h(t($messages, 'nav.impressum')) ?></a>
<a href="datenschutz.php" class="<?= $pageKey === 'privacy' ? 'active' : '' ?>"><?= h(t($messages, 'nav.privacy')) ?></a>
<a href="nutzungsbedingungen.php" class="<?= $pageKey === 'terms' ? 'active' : '' ?>"><?= h(t($messages, 'nav.terms')) ?></a>
</aside>
<article class="legal-article">
<h1><?= h($title) ?></h1>
<p class="legal-updated"><?= h(t($messages, 'legal.updated')) ?></p>
<?php render_legal_sections($page['sections']); ?>
</article>
</div>
<?php
render_footer($config, $messages);
}