Issue #17 Added imprint and privacy policy page and added englisch language support to it.

imprint still unfinished
This commit is contained in:
2026-04-07 17:42:51 +02:00
parent fc94d812d2
commit 26e4d0dd27
18 changed files with 872 additions and 10 deletions
+1
View File
@@ -117,6 +117,7 @@ textarea {
.lang-switch {
min-width: 68px;
border-color: rgba(231, 191, 119, 0.45);
}
.nav-link:hover,
+111
View File
@@ -0,0 +1,111 @@
:root {
--footer-surface: rgba(18, 33, 45, 0.94);
--footer-line: rgba(231, 191, 119, 0.28);
--footer-text: #f7f1e6;
--footer-muted: rgba(247, 241, 230, 0.72);
--footer-link-bg: rgba(255, 255, 255, 0.06);
}
body {
display: flex;
flex-direction: column;
}
body > main,
body > .container {
flex: 1 0 auto;
}
body > main.auth-shell {
min-height: 0;
}
.page-footer {
margin-top: 32px;
border-top: 1px solid var(--footer-line);
background:
linear-gradient(180deg, rgba(17, 29, 39, 0.98), rgba(31, 49, 64, 0.96));
color: var(--footer-text);
box-shadow: 0 -18px 40px rgba(31, 49, 64, 0.08);
}
.page-footer-inner {
width: min(1200px, calc(100vw - 32px));
margin: 0 auto;
padding: 26px 0 30px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
}
.page-footer-copy {
display: grid;
gap: 10px;
}
.page-footer-brand {
display: inline-flex;
align-items: center;
gap: 12px;
font-weight: 700;
letter-spacing: 0.03em;
}
.page-footer-mark {
width: 38px;
height: 38px;
display: grid;
place-items: center;
border-radius: 8px;
background: linear-gradient(135deg, #c68b2c, #e7bf77);
color: #2c2215;
font-size: 21px;
}
.page-footer-text {
margin: 0;
font-family: "Trebuchet MS", "Segoe UI", sans-serif;
color: var(--footer-muted);
line-height: 1.6;
}
.page-footer-links {
display: flex;
align-items: center;
flex-wrap: wrap;
justify-content: flex-end;
gap: 12px;
}
.page-footer-links a {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 44px;
padding: 10px 16px;
border: 1px solid rgba(231, 191, 119, 0.24);
background: var(--footer-link-bg);
color: var(--footer-text);
font-family: "Trebuchet MS", "Segoe UI", sans-serif;
font-weight: 700;
transition: transform 0.2s ease, border-color 0.2s ease, background 0.2s ease;
}
.page-footer-links a:hover {
transform: translateY(-2px);
border-color: rgba(231, 191, 119, 0.45);
background: rgba(255, 255, 255, 0.1);
}
@media (max-width: 760px) {
.page-footer-inner {
padding: 22px 0 26px;
flex-direction: column;
align-items: flex-start;
}
.page-footer-links {
justify-content: flex-start;
}
}
+220
View File
@@ -0,0 +1,220 @@
:root {
--bg: #f4efe7;
--surface: rgba(255, 250, 242, 0.9);
--surface-strong: #fffaf2;
--text: #1e1f1d;
--muted: #5f615c;
--navy: #1f3140;
--navy-soft: #324d63;
--gold: #c68b2c;
--gold-soft: #e7bf77;
--line: rgba(31, 49, 64, 0.12);
--shadow: 0 24px 60px rgba(37, 42, 48, 0.14);
}
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
min-height: 100vh;
color: var(--text);
font-family: Georgia, "Times New Roman", serif;
background:
radial-gradient(circle at top left, rgba(255, 255, 255, 0.9), transparent 24%),
radial-gradient(circle at 82% 18%, rgba(198, 139, 44, 0.2), transparent 20%),
linear-gradient(180deg, #f7f0e5 0%, #efe5d5 52%, #f4efe7 100%);
}
a {
color: inherit;
text-decoration: none;
}
.legal-header {
position: sticky;
top: 0;
z-index: 10;
}
.legal-nav {
width: 100%;
padding: 18px max(16px, calc((100vw - 1200px) / 2)) 16px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
border-right: 4px solid #12212d;
border-bottom: 4px solid #12212d;
border-left: 4px solid #12212d;
background: linear-gradient(180deg, rgba(17, 29, 39, 0.96), rgba(31, 49, 64, 0.94));
backdrop-filter: blur(12px);
box-shadow: 0 18px 40px rgba(31, 49, 64, 0.18);
}
.brand {
display: inline-flex;
align-items: center;
gap: 12px;
color: #fff7eb;
font-size: 18px;
font-weight: 700;
letter-spacing: 0.03em;
}
.brand-mark {
width: 40px;
height: 40px;
display: grid;
place-items: center;
border-radius: 8px;
background: linear-gradient(135deg, var(--gold), var(--gold-soft));
color: #2c2215;
font-size: 22px;
}
.legal-nav-links {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 12px;
color: #ecf4fb;
}
.legal-nav-links a,
.legal-nav-links .lang-switch {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 46px;
padding: 11px 16px;
border: 2px solid transparent;
background: rgba(255, 255, 255, 0.06);
font-weight: 700;
transition: transform 0.2s ease, border-color 0.2s ease;
}
.lang-switch {
cursor: pointer;
border: 2px solid rgba(231, 191, 119, 0.45);
color: #ecf4fb;
appearance: none;
}
.legal-nav-links .lang-switch {
min-width: 68px;
font: inherit;
background: rgba(255, 255, 255, 0.06);
}
.legal-nav-links a:hover {
transform: translateY(-2px);
border-color: rgba(231, 191, 119, 0.45);
}
.legal-nav-links .lang-switch:hover {
transform: translateY(-2px);
border-color: rgba(255, 222, 170, 0.85);
}
.legal-main {
width: min(980px, calc(100vw - 32px));
margin: 0 auto;
padding: 40px 0 56px;
}
.legal-hero {
margin-bottom: 24px;
padding: 34px;
border: 1px solid var(--line);
background: linear-gradient(135deg, rgba(255, 250, 242, 0.94), rgba(245, 230, 205, 0.8));
box-shadow: var(--shadow);
}
.legal-eyebrow {
margin: 0 0 12px;
color: var(--navy-soft);
text-transform: uppercase;
letter-spacing: 0.14em;
font-size: 13px;
font-weight: 700;
}
.legal-hero h1,
.legal-card h2 {
margin-top: 0;
color: #182029;
}
.legal-hero h1 {
margin-bottom: 16px;
line-height: 0.98;
letter-spacing: -0.03em;
font-size: clamp(2.4rem, 4vw, 4.4rem);
}
.legal-lead,
.legal-card p,
.legal-list {
font-family: "Trebuchet MS", "Segoe UI", sans-serif;
color: var(--muted);
line-height: 1.7;
}
.legal-card {
margin-bottom: 20px;
padding: 28px 30px;
border: 1px solid var(--line);
background: rgba(255, 250, 242, 0.82);
box-shadow: var(--shadow);
}
.legal-note {
border-left: 6px solid var(--gold);
background: linear-gradient(135deg, rgba(255, 247, 235, 0.98), rgba(241, 221, 174, 0.3));
}
.legal-card h2 {
margin-bottom: 12px;
font-size: 1.45rem;
}
.legal-card p {
margin: 0;
}
.legal-card p+p,
.legal-card p+.legal-list,
.legal-list+p {
margin-top: 14px;
}
.legal-list {
margin: 0;
padding-left: 20px;
}
.legal-list li+li {
margin-top: 8px;
}
@media (max-width: 760px) {
.legal-nav {
padding-left: 16px;
padding-right: 16px;
flex-direction: column;
align-items: flex-start;
}
.legal-main {
padding-top: 28px;
}
.legal-hero,
.legal-card {
padding: 24px 22px;
}
}
+2 -3
View File
@@ -191,8 +191,7 @@ a {
#logoutButton {
margin-top: 0;
min-height: 40px;
padding: 8px 14px;
border-radius: 999px;
padding: 11px 16px;
font-size: 14px;
line-height: 1.1;
}
@@ -200,7 +199,7 @@ a {
.lang-switch {
min-width: 58px;
background: rgba(31, 49, 64, 0.08);
border: 1px solid rgba(31, 49, 64, 0.12);
border: 2px solid rgba(31, 49, 64, 0.18);
color: var(--navy);
}
+10 -2
View File
@@ -278,7 +278,11 @@ const translations = {
filterRooks: "Türme",
solverStepInfo: ({ currentStep, totalSteps }) => `Schritt ${currentStep} / ${totalSteps}`,
speedValue: ({ speed }) => `${speed} ms`,
languageSwitchAria: "Sprache wechseln"
languageSwitchAria: "Sprache wechseln",
footerText: "Schulprojekt rund um das Acht-Damen- und Acht-Türme-Problem.",
footerPrivacy: "Datenschutzerklärung",
footerImprint: "Impressum",
footerNavAria: "Rechtliche Hinweise"
},
en: {
pageTitle: "Dashboard",
@@ -425,7 +429,11 @@ const translations = {
filterRooks: "Rooks",
solverStepInfo: ({ currentStep, totalSteps }) => `Step ${currentStep} / ${totalSteps}`,
speedValue: ({ speed }) => `${speed} ms`,
languageSwitchAria: "Switch language"
languageSwitchAria: "Switch language",
footerText: "School project about the Eight Queens and Eight Rooks problem.",
footerPrivacy: "Privacy Policy",
footerImprint: "Imprint",
footerNavAria: "Legal information"
}
};
+21 -2
View File
@@ -55,7 +55,11 @@ const translations = {
"cta.title": "Mach aus einem bekannten Problem ein sichtbares Lernerlebnis.",
"cta.text": "Starte mit einem Account, speichere deine Zwischenstände und finde heraus, wie schnell du eine konfliktfreie Lösung erreichst.",
"cta.primaryCta": "Create Account",
"cta.secondaryCta": "Login"
"cta.secondaryCta": "Login",
"footerText": "Schulprojekt rund um das Acht-Damen- und Acht-Türme-Problem.",
"footerPrivacy": "Datenschutzerklärung",
"footerImprint": "Impressum",
"footerNavAria": "Rechtliche Hinweise"
},
en: {
"meta.title": "8 Queens and 8 Rooks Solver",
@@ -107,7 +111,11 @@ const translations = {
"cta.title": "Turn a famous puzzle into a visible learning experience.",
"cta.text": "Start with an account, save your checkpoints, and find out how quickly you can reach a conflict-free solution.",
"cta.primaryCta": "Create Account",
"cta.secondaryCta": "Login"
"cta.secondaryCta": "Login",
"footerText": "School project about the Eight Queens and Eight Rooks problem.",
"footerPrivacy": "Privacy Policy",
"footerImprint": "Imprint",
"footerNavAria": "Legal information"
}
};
@@ -169,6 +177,17 @@ function setLanguage(language) {
element.textContent = translatedText;
});
document.querySelectorAll("[data-i18n-aria-label]").forEach((element) => {
const key = element.dataset.i18nAriaLabel;
const translatedText = dictionary[key];
if (!translatedText) {
return;
}
element.setAttribute("aria-label", translatedText);
});
if (langSwitch) {
langSwitch.textContent = resolvedLanguage.toUpperCase();
langSwitch.setAttribute("aria-label", resolvedLanguage === "de" ? "Switch language to English" : "Sprache auf Deutsch wechseln");
+242
View File
@@ -0,0 +1,242 @@
const SUPPORTED_LANGUAGES = ["de", "en"];
const DEFAULT_LANGUAGE = "de";
const LANGUAGE_STORAGE_KEY = "preferredLanguage";
const langSwitch = document.getElementById("langSwitch");
const legalPage = document.body.dataset.legalPage;
const translations = {
de: {
common: {
navBrand: "8-Damen- und 8-Türme Solver",
navHome: "Startseite",
navDashboard: "Dashboard",
navLogin: "Login",
heroEyebrow: "Rechtliche Hinweise",
footerText: "Schulprojekt rund um das Acht-Damen- und Acht-Türme-Problem.",
footerPrivacy: "Datenschutzerklärung",
footerImprint: "Impressum",
footerNavAria: "Rechtliche Hinweise",
languageSwitchAria: "Sprache wechseln"
},
imprint: {
pageTitle: "Impressum",
heroTitle: "Impressum",
contactTitle: "Kontakt",
contactName: "Jérôme Bachmann",
contactEmailLabel: "E-Mail:",
projectResponsibilityTitle: "Projektverantwortung",
projectResponsibilityText: "Dieses Webprojekt wurde im Rahmen eines Schulprojekts zum Acht-Damen- und Acht-Türme-Problem erstellt. Inhaltlich verantwortlich ist die jeweils oben genannte verantwortliche Stelle.",
contentLiabilityTitle: "Haftung fuer Inhalte",
contentLiabilityText: "Die Inhalte dieser Website wurden mit Sorgfalt erstellt. Für die Richtigkeit, Vollstaendigkeit und Aktualitaet der Inhalte kann jedoch keine Gewaehr übernommen werden. Bei Bekanntwerden von Rechtsverletzungen werden entsprechende Inhalte umgehend überarbeitet oder entfernt.",
linksLiabilityTitle: "Haftung fuer Links",
linksLiabilityText: "Diese Website kann Links zu externen Angeboten enthalten. Fuer deren Inhalte sind ausschliesslich die jeweiligen Betreiber verantwortlich. Eine permanente inhaltliche Kontrolle verlinkter Seiten ist ohne konkrete Anhaltspunkte einer Rechtsverletzung nicht zumutbar.",
versionTitle: "Stand",
versionText: "Stand dieser Vorlage: April 2026"
},
privacy: {
pageTitle: "Datenschutzerklärung",
heroTitle: "Datenschutzerklärung",
heroLead: "Diese Seite beschreibt, welche personenbezogenen Daten im Rahmen dieses Schulprojekts verarbeitet werden.",
dataProcessedTitle: "1. Welche Daten verarbeitet werden",
dataProcessedText: "Im Rahmen der Nutzung dieser Webanwendung können insbesondere folgende Daten verarbeitet werden:",
dataProcessedItem1: "Benutzername und verschlüsseltes Passwort bei der Registrierung und beim Login",
dataProcessedItem2: "Daten zur Zwei-Faktor-Authentifizierung, falls diese freiwillig aktiviert wird",
dataProcessedItem3: "gespeicherte Spielstände, Notizen, Schrittzahlen, Zeiten und Favoriten",
dataProcessedItem4: "technische Verbindungsdaten, die durch den Server oder das Hosting entstehen koennen",
processingPurposeTitle: "3. Zweck der Verarbeitung",
processingPurposeText: "Die Verarbeitung erfolgt, um die Funktionen dieser Anwendung bereitzustellen. Dazu gehoeren insbesondere:",
processingPurposeItem1: "Erstellung und Verwaltung von Benutzerkonten",
processingPurposeItem2: "sicherer Login in das persönliche Dashboard",
processingPurposeItem3: "optionale Absicherung des Kontos per Authenticator-App",
processingPurposeItem4: "Speichern und Laden von Spielständen",
processingPurposeItem5: "technischer Betrieb, Fehleranalyse und Missbrauchsschutz",
legalBasisTitle: "4. Rechtsgrundlage",
legalBasisText: "Soweit personenbezogene Daten verarbeitet werden, erfolgt dies in der Regel zur Bereitstellung der Anwendung und ihrer Funktionen sowie aufgrund des berechtigten Interesses an einem sicheren und funktionsfaehigen Webangebot. Falls eine ausdrückliche Einwilligung erforderlich ist, wird diese gesondert eingeholt.",
storageTitle: "5. Speicherung und Löschung",
storageText: "Personenbezogene Daten werden nur so lange gespeichert, wie sie für den jeweiligen Zweck benötigt werden. Benutzerkonten und zugehörige Spielstände bleiben grundsätzlich gespeichert, bis sie gelöscht oder für das Projekt nicht mehr benötigt werden.",
thirdPartyTitle: "6. Weitergabe an Dritte",
thirdPartyText: "Eine Weitergabe personenbezogener Daten erfolgt nur, wenn dies für den Betrieb der Anwendung erforderlich ist, etwa durch einen Hosting-Anbieter oder technische Dienstleister. Ansonsten werden keine Daten weitergegeben.",
rightsTitle: "7. Rechte betroffener Personen",
rightsText: "Betroffene Personen haben im Rahmen des anwendbaren Datenschutzrechts insbesondere das Recht auf:",
rightsItem1: "Auskunft über die verarbeiteten personenbezogenen Daten",
rightsItem2: "Berichtigung unrichtiger Daten",
rightsItem3: "Löschung oder Einschraenkung der Verarbeitung",
rightsItem4: "Widerspruch gegen die Verarbeitung",
rightsItem5: "Herausgabe der bereitgestellten Daten, soweit anwendbar",
securityTitle: "8. Datensicherheit",
securityText: "Es werden technische und organisatorische Massnahmen getroffen, um gespeicherte Daten vor Verlust, Missbrauch oder unberechtigtem Zugriff zu schuetzen. Dazu gehoeren insbesondere passwortgeschuetzte Konten und die Möglichkeit, Zwei-Faktor-Authentifizierung zu aktivieren.",
versionTitle: "9. Stand",
versionText: "Stand dieser Vorlage: April 2026"
}
},
en: {
common: {
navBrand: "8 Queens and 8 Rooks Solver",
navHome: "Home",
navDashboard: "Dashboard",
navLogin: "Login",
heroEyebrow: "Legal information",
footerText: "School project about the Eight Queens and Eight Rooks problem.",
footerPrivacy: "Privacy Policy",
footerImprint: "Imprint",
footerNavAria: "Legal information",
languageSwitchAria: "Switch language"
},
imprint: {
pageTitle: "Imprint",
heroTitle: "Imprint",
contactTitle: "Contact",
contactName: "Jérôme Bachmann",
contactEmailLabel: "Email:",
projectResponsibilityTitle: "Project responsibility",
projectResponsibilityText: "This web project was created as part of a school project on the Eight Queens and Eight Rooks problem. The entity named above is responsible for the content.",
contentLiabilityTitle: "Liability for content",
contentLiabilityText: "The content of this website was created with care. However, no guarantee can be given for the accuracy, completeness, or timeliness of the content. If legal violations become known, the relevant content will be revised or removed without delay.",
linksLiabilityTitle: "Liability for links",
linksLiabilityText: "This website may contain links to external offers. Their content is the sole responsibility of the respective operators. Permanent monitoring of linked pages is not reasonable without concrete indications of a legal violation.",
versionTitle: "Version",
versionText: "Version of this template: April 2026"
},
privacy: {
pageTitle: "Privacy Policy",
heroTitle: "Privacy Policy",
heroLead: "This page describes which personal data is processed as part of this school project.",
dataProcessedTitle: "1. Which data is processed",
dataProcessedText: "When using this web application, the following data in particular may be processed:",
dataProcessedItem1: "username and encrypted password during registration and login",
dataProcessedItem2: "two-factor authentication data if this feature is activated voluntarily",
dataProcessedItem3: "saved game states, notes, step counts, times, and favorites",
dataProcessedItem4: "technical connection data that may arise through the server or hosting",
processingPurposeTitle: "3. Purpose of processing",
processingPurposeText: "The processing is carried out to provide the functions of this application. This includes in particular:",
processingPurposeItem1: "creating and managing user accounts",
processingPurposeItem2: "secure login to the personal dashboard",
processingPurposeItem3: "optional account protection using an authenticator app",
processingPurposeItem4: "saving and loading game states",
processingPurposeItem5: "technical operation, error analysis, and abuse prevention",
legalBasisTitle: "4. Legal basis",
legalBasisText: "Where personal data is processed, this is generally done to provide the application and its functions, as well as on the basis of the legitimate interest in a secure and functional web offering. If explicit consent is required, it will be obtained separately.",
storageTitle: "5. Storage and deletion",
storageText: "Personal data is stored only as long as it is needed for the respective purpose. User accounts and related game states generally remain stored until they are deleted or are no longer needed for the project.",
thirdPartyTitle: "6. Disclosure to third parties",
thirdPartyText: "Personal data is shared only if this is necessary for operating the application, for example through a hosting provider or technical service providers. Otherwise, no data is shared.",
rightsTitle: "7. Rights of data subjects",
rightsText: "Within the framework of applicable data protection law, data subjects have in particular the right to:",
rightsItem1: "information about the personal data being processed",
rightsItem2: "correction of incorrect data",
rightsItem3: "deletion of data or restriction of processing",
rightsItem4: "object to the processing",
rightsItem5: "receive the data that has been provided, where applicable",
securityTitle: "8. Data security",
securityText: "Technical and organizational measures are taken to protect stored data against loss, misuse, or unauthorized access. These include password-protected accounts and the option to enable two-factor authentication.",
versionTitle: "9. Version",
versionText: "Version of this template: April 2026"
}
}
};
function normalizeLanguage(language) {
const shortLanguage = String(language || "").trim().slice(0, 2).toLowerCase();
return SUPPORTED_LANGUAGES.includes(shortLanguage) ? shortLanguage : DEFAULT_LANGUAGE;
}
function getStoredLanguage() {
try {
return window.localStorage.getItem(LANGUAGE_STORAGE_KEY);
} catch (error) {
return null;
}
}
function storeLanguage(language) {
try {
window.localStorage.setItem(LANGUAGE_STORAGE_KEY, language);
} catch (error) {
return;
}
}
function detectBrowserLanguage() {
return normalizeLanguage(navigator.language || navigator.userLanguage || DEFAULT_LANGUAGE);
}
function t(language, key) {
const commonTranslation = translations[language]?.common?.[key];
if (commonTranslation) {
return commonTranslation;
}
return translations[language]?.[legalPage]?.[key] ?? null;
}
function translateFooter(language) {
const footerText = document.querySelector(".page-footer-text");
const footerNav = document.querySelector(".page-footer-links");
const privacyLink = document.querySelector('.page-footer-links a[href$="/privacy-policy"]');
const imprintLink = document.querySelector('.page-footer-links a[href$="/impressum"]');
if (footerText) {
footerText.textContent = t(language, "footerText") || footerText.textContent;
}
if (footerNav) {
footerNav.setAttribute("aria-label", t(language, "footerNavAria") || footerNav.getAttribute("aria-label") || "");
}
if (privacyLink) {
privacyLink.textContent = t(language, "footerPrivacy") || privacyLink.textContent;
}
if (imprintLink) {
imprintLink.textContent = t(language, "footerImprint") || imprintLink.textContent;
}
}
function applyTranslations(language) {
document.documentElement.lang = language;
document.body.dataset.language = language;
document.querySelectorAll("[data-i18n]").forEach((element) => {
const translatedText = t(language, element.dataset.i18n);
if (!translatedText) {
return;
}
element.textContent = translatedText;
});
document.querySelectorAll("[data-i18n-aria-label]").forEach((element) => {
const translatedText = t(language, element.dataset.i18nAriaLabel);
if (!translatedText) {
return;
}
element.setAttribute("aria-label", translatedText);
});
translateFooter(language);
if (langSwitch) {
langSwitch.textContent = language.toUpperCase();
langSwitch.setAttribute("aria-label", t(language, "languageSwitchAria") || "Sprache wechseln");
langSwitch.title = language === "de" ? "Switch to English" : "Zu Deutsch wechseln";
}
storeLanguage(language);
}
function setLanguage(language) {
applyTranslations(normalizeLanguage(language));
}
setLanguage(getStoredLanguage() || detectBrowserLanguage());
if (langSwitch) {
langSwitch.addEventListener("click", () => {
setLanguage(document.documentElement.lang === "de" ? "en" : "de");
});
}
+10 -2
View File
@@ -43,7 +43,11 @@ const translations = {
confirmPasswordLabel: "Neues Passwort bestätigen",
updatePasswordButton: "Passwort aktualisieren",
updatePasswordLoading: "Passwort wird aktualisiert...",
qrLoadError: "QR-Vorschau konnte nicht geladen werden."
qrLoadError: "QR-Vorschau konnte nicht geladen werden.",
footerText: "Schulprojekt rund um das Acht-Damen- und Acht-Türme-Problem.",
footerPrivacy: "Datenschutzerklärung",
footerImprint: "Impressum",
footerNavAria: "Rechtliche Hinweise"
},
en: {
pageTitle: "Login Settings",
@@ -83,7 +87,11 @@ const translations = {
confirmPasswordLabel: "Confirm new password",
updatePasswordButton: "Update password",
updatePasswordLoading: "Updating password...",
qrLoadError: "QR preview could not be loaded."
qrLoadError: "QR preview could not be loaded.",
footerText: "School project about the Eight Queens and Eight Rooks problem.",
footerPrivacy: "Privacy Policy",
footerImprint: "Imprint",
footerNavAria: "Legal information"
}
};