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

@@ -1,18 +1,20 @@
import { cookies } from 'next/headers';
import { cookies, headers } from 'next/headers';
import deMessages from '@/messages/de.json';
import enMessages from '@/messages/en.json';
import legalDe from '@/messages/legal-de.json';
import legalEn from '@/messages/legal-en.json';
import { env } from './env';
export type Locale = 'de' | 'en';
export const LOCALE_COOKIE_NAME = 'nexumi_locale';
export type Messages = typeof enMessages;
export const MESSAGES = {
de: { ...deMessages, legal: legalDe },
en: { ...enMessages, legal: legalEn }
} as const;
export const MESSAGES: Record<Locale, Messages> = {
de: deMessages,
en: enMessages
};
export type Messages = (typeof MESSAGES)['en'];
function resolve(messages: Messages, key: string): string | undefined {
const value = key.split('.').reduce<unknown>((acc, part) => {
@@ -43,12 +45,45 @@ function isLocale(value: string | undefined): value is Locale {
return value === 'de' || value === 'en';
}
function localeFromAcceptLanguage(header: string | null): Locale | undefined {
if (!header) {
return undefined;
}
const preferred = header
.split(',')
.map((part) => {
const [tag, qValue] = part.trim().split(';q=');
return { lang: tag.trim().toLowerCase().slice(0, 2), q: qValue ? Number(qValue) : 1 };
})
.filter((entry) => entry.lang.length === 2 && !Number.isNaN(entry.q))
.sort((a, b) => b.q - a.q);
for (const entry of preferred) {
if (isLocale(entry.lang)) {
return entry.lang;
}
}
return undefined;
}
/**
* Resolves the active locale for the current request from the
* `nexumi_locale` cookie, falling back to the server's configured default.
* `nexumi_locale` cookie, then Accept-Language, then the server default.
*/
export async function getLocale(): Promise<Locale> {
const cookieStore = await cookies();
const cookieLocale = cookieStore.get(LOCALE_COOKIE_NAME)?.value;
return isLocale(cookieLocale) ? cookieLocale : env.DEFAULT_LOCALE;
if (isLocale(cookieLocale)) {
return cookieLocale;
}
const headerList = await headers();
const fromHeader = localeFromAcceptLanguage(headerList.get('accept-language'));
if (fromHeader) {
return fromHeader;
}
return env.DEFAULT_LOCALE;
}