// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2026 Pierre Gronau, ndaal in Cologne
//! Info pages (About/License/System/Privacy/Security, plus the four
//! document pages) (`routes::info`) body-content translations.
//!
//! Split into one small helper per page (`tr_about`, `tr_license`, ...) so
//! no single match arm exceeds the project's 50-line function threshold;
//! [`tr`] itself is just the `.or_else()` chain over them. (The Info
//! dropdown's own *menu-label* text lives in [`super::nav`], since
//! `routes::layout::wrap_page` renders it, not `routes::info`; the
//! `info.subnav_*` keys here back the identically-worded sub-nav card that
//! `routes::info` renders atop every page in this section.)
use super::Entry;
/// Look up an `info.*` translation key.
pub(super) fn tr(key: &str) -> Option<Entry> {
tr_subnav(key)
.or_else(|| tr_about(key))
.or_else(|| tr_license(key))
.or_else(|| tr_system(key))
.or_else(|| tr_privacy(key))
.or_else(|| tr_security(key))
.or_else(|| tr_docs(key))
}
/// The sub-nav card repeated atop every Info-section page. Wording matches
/// the corresponding `nav.info_*` entries (same labels, new keys, since
/// this file must not reach into `super::nav`'s private table).
fn tr_subnav(key: &str) -> Option<Entry> {
Some(match key {
"info.subnav_about" => ("About", "Über", "À propos", "Acerca de", "Informazioni su"),
"info.subnav_license" => ("License", "Lizenz", "Licence", "Licencia", "Licenza"),
"info.subnav_system" => (
"System Info",
"Systeminfo",
"Infos système",
"Info del sistema",
"Info di sistema",
),
"info.subnav_privacy" => (
"Privacy",
"Datenschutz",
"Confidentialité",
"Privacidad",
"Privacy",
),
"info.subnav_security" => (
"Security",
"Sicherheit",
"Sécurité",
"Seguridad",
"Sicurezza",
),
"info.subnav_changelog" => (
"Changelog",
"Changelog",
"Changelog",
"Changelog",
"Changelog",
),
"info.subnav_readme" => ("README", "LIESMICH", "LISEZMOI", "LEEME", "LEGGIMI"),
"info.subnav_administrator" => (
"Administrator Guide",
"Administratorhandbuch",
"Guide d'administration",
"Guía del administrador",
"Guida dell'amministratore",
),
"info.subnav_user" => (
"User Guide",
"Benutzerhandbuch",
"Guide utilisateur",
"Guía del usuario",
"Guida utente",
),
_ => return None,
})
}
/// `GET /info/about`. `info.about_heading` backs both the `<title>`/nav
/// text and the page's own `<h1>` -- one key, two spots, no drift.
fn tr_about(key: &str) -> Option<Entry> {
Some(match key {
"info.about_heading" => ("About", "Über", "À propos", "Acerca de", "Informazioni su"),
"info.about_intro" => (
"<strong>CSAF</strong> is a web-based management tool for creating, reading, updating, and deleting Common Security Advisory Framework (CSAF) documents conforming to OASIS CSAF versions 2.0 and 2.1.",
"<strong>CSAF</strong> ist ein webbasiertes Verwaltungswerkzeug zum Erstellen, Lesen, Aktualisieren und Löschen von Dokumenten nach dem Common Security Advisory Framework (CSAF) gemäß den OASIS-CSAF-Versionen 2.0 und 2.1.",
"<strong>CSAF</strong> est un outil de gestion web permettant de créer, consulter, mettre à jour et supprimer des documents Common Security Advisory Framework (CSAF) conformes aux versions OASIS CSAF 2.0 et 2.1.",
"<strong>CSAF</strong> es una herramienta de gestión basada en web para crear, consultar, actualizar y eliminar documentos Common Security Advisory Framework (CSAF) conformes a las versiones OASIS CSAF 2.0 y 2.1.",
"<strong>CSAF</strong> è uno strumento di gestione basato sul web per creare, leggere, aggiornare ed eliminare documenti Common Security Advisory Framework (CSAF) conformi alle versioni OASIS CSAF 2.0 e 2.1.",
),
"info.about_version_label" => ("Version", "Version", "Version", "Versión", "Versione"),
"info.about_features_heading" => (
"Features",
"Funktionen",
"Fonctionnalités",
"Funciones",
"Funzionalità",
),
"info.about_features_list" => (
" <li>Full CRUD operations for CSAF security advisories, VEX, and informational advisories</li>\n <li>Built-in validation against CSAF 2.0/2.1 schema rules</li>\n <li>CVSS v3.1 and v4.0 scoring display</li>\n <li>SHA-256 and SHA3-512 sidecar hash file generation</li>\n <li>Bulk import/export with filesystem directory scanning</li>\n <li>Audit trail for all document operations</li>\n <li>HATEOAS-compliant REST API</li>\n <li>Embedded storage (redb + SQLite) — no external database required</li>\n <li>TLS 1.3 with automatic certificate generation</li>",
" <li>Vollständige CRUD-Operationen für CSAF-Sicherheitshinweise, VEX- und Informationsdokumente</li>\n <li>Integrierte Validierung nach den CSAF-2.0/2.1-Schemaregeln</li>\n <li>Anzeige der CVSS-Bewertung v3.1 und v4.0</li>\n <li>Erzeugung von SHA-256- und SHA3-512-Sidecar-Hashdateien</li>\n <li>Massenimport/-export mit Verzeichnis-Scan im Dateisystem</li>\n <li>Prüfpfad für alle Dokumentvorgänge</li>\n <li>HATEOAS-konforme REST-API</li>\n <li>Eingebetteter Speicher (redb + SQLite) — keine externe Datenbank erforderlich</li>\n <li>TLS 1.3 mit automatischer Zertifikatserzeugung</li>",
" <li>Opérations CRUD complètes pour les avis de sécurité CSAF, les documents VEX et informatifs</li>\n <li>Validation intégrée selon les règles de schéma CSAF 2.0/2.1</li>\n <li>Affichage des scores CVSS v3.1 et v4.0</li>\n <li>Génération de fichiers de hachage sidecar SHA-256 et SHA3-512</li>\n <li>Import/export en masse avec analyse de répertoires du système de fichiers</li>\n <li>Piste d'audit pour toutes les opérations sur les documents</li>\n <li>API REST conforme HATEOAS</li>\n <li>Stockage embarqué (redb + SQLite) — aucune base de données externe requise</li>\n <li>TLS 1.3 avec génération automatique de certificats</li>",
" <li>Operaciones CRUD completas para avisos de seguridad CSAF, documentos VEX e informativos</li>\n <li>Validación integrada según las reglas de esquema CSAF 2.0/2.1</li>\n <li>Visualización de puntuación CVSS v3.1 y v4.0</li>\n <li>Generación de archivos hash sidecar SHA-256 y SHA3-512</li>\n <li>Importación/exportación masiva con exploración de directorios del sistema de archivos</li>\n <li>Registro de auditoría para todas las operaciones sobre documentos</li>\n <li>API REST compatible con HATEOAS</li>\n <li>Almacenamiento embebido (redb + SQLite) — no requiere base de datos externa</li>\n <li>TLS 1.3 con generación automática de certificados</li>",
" <li>Operazioni CRUD complete per advisory di sicurezza CSAF, documenti VEX e informativi</li>\n <li>Validazione integrata secondo le regole dello schema CSAF 2.0/2.1</li>\n <li>Visualizzazione del punteggio CVSS v3.1 e v4.0</li>\n <li>Generazione di file hash sidecar SHA-256 e SHA3-512</li>\n <li>Importazione/esportazione in blocco con scansione delle directory del file system</li>\n <li>Registro di controllo per tutte le operazioni sui documenti</li>\n <li>API REST conforme a HATEOAS</li>\n <li>Archiviazione integrata (redb + SQLite) — nessun database esterno richiesto</li>\n <li>TLS 1.3 con generazione automatica dei certificati</li>",
),
"info.about_standards_heading" => {
("Standards", "Standards", "Normes", "Normas", "Standard")
},
"info.about_source_heading" => (
"Source code",
"Quellcode",
"Code source",
"Código fuente",
"Codice sorgente",
),
"info.about_source_intro" => (
"CSAF CRUD is free software under the Apache License 2.0. The complete source, issue tracker and release archives — including the five checksum sidecars published with every binary — live in the project repository. The workspace is also published to crates.io, so it can be built or installed from source with <code>cargo install csaf-crud</code>, or fetched pre-built with <code>cargo binstall csaf-crud</code>.",
"CSAF CRUD ist freie Software unter der Apache License 2.0. Der vollständige Quellcode, das Issue-Tracking und die Release-Archive — einschließlich der fünf Prüfsummen-Sidecars, die zu jeder Binärdatei veröffentlicht werden — liegen im Projekt-Repository. Der Workspace wird zusätzlich auf crates.io veröffentlicht und kann daher mit <code>cargo install csaf-crud</code> aus dem Quellcode gebaut oder installiert, oder mit <code>cargo binstall csaf-crud</code> vorgebaut bezogen werden.",
"CSAF CRUD est un logiciel libre sous licence Apache License 2.0. Le code source complet, le suivi des tickets et les archives de version — y compris les cinq sommes de contrôle sidecar publiées avec chaque binaire — se trouvent dans le dépôt du projet. L'espace de travail est également publié sur crates.io ; il peut donc être compilé ou installé depuis les sources avec <code>cargo install csaf-crud</code>, ou récupéré déjà compilé avec <code>cargo binstall csaf-crud</code>.",
"CSAF CRUD es software libre bajo la Apache License 2.0. El código fuente completo, el seguimiento de incidencias y los archivos de versión — incluidas las cinco sumas de comprobación sidecar publicadas con cada binario — se encuentran en el repositorio del proyecto. El workspace también se publica en crates.io, por lo que puede compilarse o instalarse desde el código fuente con <code>cargo install csaf-crud</code>, o descargarse ya compilado con <code>cargo binstall csaf-crud</code>.",
"CSAF CRUD è software libero distribuito con licenza Apache License 2.0. Il codice sorgente completo, il tracciamento dei problemi e gli archivi delle release — comprese le cinque somme di controllo sidecar pubblicate con ogni binario — si trovano nel repository del progetto. Il workspace è pubblicato anche su crates.io e può quindi essere compilato o installato dai sorgenti con <code>cargo install csaf-crud</code>, oppure scaricato già compilato con <code>cargo binstall csaf-crud</code>.",
),
"info.about_repository_label" => (
"Repository:",
"Repository:",
"Dépôt :",
"Repositorio:",
"Repository:",
),
"info.about_releases_label" => (
"Releases:",
"Releases:",
"Versions publiées :",
"Versiones publicadas:",
"Release:",
),
"info.about_cratesio_label" => (
"crates.io:",
"crates.io:",
"crates.io :",
"crates.io:",
"crates.io:",
),
"info.about_sha_note" => (
"The <code>SHA256SUMS</code> manifest in each release is what this binary's own <code>--self-update</code> verifies a download against. That proves <em>integrity</em>, not authenticity — the artifacts are not signed, so whoever can rewrite a release archive at source can rewrite the manifest beside it.",
"Anhand des <code>SHA256SUMS</code>-Manifests in jedem Release prüft die eigene <code>--self-update</code>-Funktion dieser Binärdatei einen Download. Das belegt <em>Integrität</em>, nicht Authentizität — die Artefakte sind nicht signiert, sodass jeder, der ein Release-Archiv an der Quelle umschreiben kann, auch das danebenliegende Manifest umschreiben kann.",
"Le manifeste <code>SHA256SUMS</code> de chaque version est ce que la fonction <code>--self-update</code> intégrée à ce binaire vérifie lors d'un téléchargement. Cela prouve l'<em>intégrité</em>, pas l'authenticité — les artefacts ne sont pas signés, donc quiconque peut réécrire une archive de version à la source peut aussi réécrire le manifeste qui l'accompagne.",
"El manifiesto <code>SHA256SUMS</code> de cada versión es lo que la propia función <code>--self-update</code> de este binario verifica al descargar. Eso demuestra <em>integridad</em>, no autenticidad — los artefactos no están firmados, por lo que quien pueda reescribir un archivo de versión en el origen también puede reescribir el manifiesto adjunto.",
"Il manifesto <code>SHA256SUMS</code> di ogni release è ciò rispetto a cui la funzione <code>--self-update</code> integrata in questo binario verifica un download. Questo dimostra l'<em>integrità</em>, non l'autenticità — gli artefatti non sono firmati, quindi chiunque possa riscrivere un archivio di release all'origine può riscrivere anche il manifesto accanto ad esso.",
),
"info.about_contact_heading" => ("Contact", "Kontakt", "Contact", "Contacto", "Contatti"),
"info.about_contact_text" => (
"Developed by <strong>ndaal Gesellschaft für Sicherheit in der Informationstechnik mbH & Co KG</strong>, Cologne, Germany.",
"Entwickelt von der <strong>ndaal Gesellschaft für Sicherheit in der Informationstechnik mbH & Co KG</strong>, Köln.",
"Développé par <strong>ndaal Gesellschaft für Sicherheit in der Informationstechnik mbH & Co KG</strong>, Cologne, Allemagne.",
"Desarrollado por <strong>ndaal Gesellschaft für Sicherheit in der Informationstechnik mbH & Co KG</strong>, Colonia, Alemania.",
"Sviluppato da <strong>ndaal Gesellschaft für Sicherheit in der Informationstechnik mbH & Co KG</strong>, Colonia, Germania.",
),
_ => return None,
})
}
/// `GET /info/license`.
fn tr_license(key: &str) -> Option<Entry> {
Some(match key {
"info.license_heading" => ("License", "Lizenz", "Licence", "Licencia", "Licenza"),
"info.license_intro" => (
"CSAF is licensed under the <strong>Apache License, Version 2.0</strong>.",
"CSAF steht unter der <strong>Apache License, Version 2.0</strong>.",
"CSAF est distribué sous <strong>Apache License, Version 2.0</strong>.",
"CSAF se distribuye bajo la <strong>Apache License, Version 2.0</strong>.",
"CSAF è distribuito con licenza <strong>Apache License, Version 2.0</strong>.",
),
"info.license_spdx_label" => (
"SPDX-License-Identifier:",
"SPDX-License-Identifier:",
"SPDX-License-Identifier:",
"SPDX-License-Identifier:",
"SPDX-License-Identifier:",
),
"info.license_summary_heading" => (
"Apache License 2.0 Summary",
"Zusammenfassung der Apache License 2.0",
"Résumé d'Apache License 2.0",
"Resumen de la Apache License 2.0",
"Riepilogo della Apache License 2.0",
),
"info.license_summary_list" => (
" <li>You may use, modify, and distribute this software freely.</li>\n <li>You must include the copyright notice and license in any distribution.</li>\n <li>The software is provided \"AS IS\", without warranties.</li>\n <li>Contributors grant a patent license for their contributions.</li>",
" <li>Sie dürfen diese Software frei verwenden, verändern und weitergeben.</li>\n <li>Bei jeder Weitergabe müssen der Urheberrechtshinweis und die Lizenz beigefügt werden.</li>\n <li>Die Software wird \"WIE BESEHEN\" bereitgestellt, ohne jegliche Gewährleistung.</li>\n <li>Mitwirkende gewähren eine Patentlizenz für ihre Beiträge.</li>",
" <li>Vous pouvez utiliser, modifier et distribuer ce logiciel librement.</li>\n <li>Vous devez inclure la mention de copyright et la licence dans toute distribution.</li>\n <li>Le logiciel est fourni \"EN L'ÉTAT\", sans garantie.</li>\n <li>Les contributeurs accordent une licence de brevet pour leurs contributions.</li>",
" <li>Puede usar, modificar y distribuir este software libremente.</li>\n <li>Debe incluir el aviso de copyright y la licencia en cualquier distribución.</li>\n <li>El software se proporciona \"TAL CUAL\", sin garantías.</li>\n <li>Los colaboradores conceden una licencia de patente para sus contribuciones.</li>",
" <li>È possibile utilizzare, modificare e distribuire liberamente questo software.</li>\n <li>È necessario includere l'avviso di copyright e la licenza in ogni distribuzione.</li>\n <li>Il software è fornito \"COSÌ COM'È\", senza garanzie.</li>\n <li>I contributori concedono una licenza di brevetto per i loro contributi.</li>",
),
"info.license_full_text_label" => (
"Full license text:",
"Vollständiger Lizenztext:",
"Texte intégral de la licence :",
"Texto completo de la licencia:",
"Testo completo della licenza:",
),
_ => return None,
})
}
/// `GET /info/system`.
fn tr_system(key: &str) -> Option<Entry> {
Some(match key {
"info.system_heading" => (
"System Info",
"Systeminfo",
"Infos système",
"Info del sistema",
"Info di sistema",
),
"info.system_host_heading" => ("Host", "Host", "Hôte", "Host", "Host"),
"info.system_hostname_label" => (
"Hostname",
"Hostname",
"Nom d'hôte",
"Nombre de host",
"Hostname",
),
"info.system_os_label" => ("OS", "Betriebssystem", "Système d'exploitation", "SO", "SO"),
"info.system_kernel_label" => ("Kernel", "Kernel", "Noyau", "Núcleo", "Kernel"),
"info.system_cpus_label" => ("CPUs", "CPUs", "Processeurs", "CPU", "CPU"),
"info.system_memory_label" => ("Memory", "Speicher", "Mémoire", "Memoria", "Memoria"),
"info.system_application_heading" => (
"Application",
"Anwendung",
"Application",
"Aplicación",
"Applicazione",
),
"info.system_version_label" => ("Version", "Version", "Version", "Versión", "Versione"),
"info.system_rust_version_label" => (
"Rust Version",
"Rust-Version",
"Version de Rust",
"Versión de Rust",
"Versione di Rust",
),
"info.system_listen_address_label" => (
"Listen Address",
"Listen-Adresse",
"Adresse d'écoute",
"Dirección de escucha",
"Indirizzo di ascolto",
),
"info.system_data_directory_label" => (
"Data Directory",
"Datenverzeichnis",
"Répertoire de données",
"Directorio de datos",
"Directory dei dati",
),
"info.system_storage_status_label" => (
"Storage Status",
"Speicherstatus",
"État du stockage",
"Estado del almacenamiento",
"Stato dell'archiviazione",
),
"info.system_documents_stored_label" => (
"Documents Stored",
"Gespeicherte Dokumente",
"Documents stockés",
"Documentos almacenados",
"Documenti memorizzati",
),
"info.system_csaf_mode_label" => (
"CSAF Mode",
"CSAF-Modus",
"Mode CSAF",
"Modo CSAF",
"Modalità CSAF",
),
"info.system_status_healthy" => (
"Healthy",
"Funktionsfähig",
"Opérationnel",
"Operativo",
"Operativo",
),
"info.system_status_degraded" => (
"Degraded",
"Beeinträchtigt",
"Dégradé",
"Degradado",
"Degradato",
),
_ => return None,
})
}
/// `GET /info/privacy`.
fn tr_privacy(key: &str) -> Option<Entry> {
Some(match key {
"info.privacy_heading" => (
"Privacy",
"Datenschutz",
"Confidentialité",
"Privacidad",
"Privacy",
),
"info.privacy_data_processing_heading" => (
"Data Processing",
"Datenverarbeitung",
"Traitement des données",
"Tratamiento de datos",
"Trattamento dei dati",
),
"info.privacy_data_processing_text" => (
"CSAF is designed as a self-hosted application. All data is stored locally on the server where the application is deployed.",
"CSAF ist als selbst gehostete Anwendung konzipiert. Alle Daten werden lokal auf dem Server gespeichert, auf dem die Anwendung bereitgestellt wird.",
"CSAF est conçu comme une application auto-hébergée. Toutes les données sont stockées localement sur le serveur où l'application est déployée.",
"CSAF está diseñado como una aplicación autoalojada. Todos los datos se almacenan localmente en el servidor donde se despliega la aplicación.",
"CSAF è progettato come applicazione autoospitata. Tutti i dati vengono memorizzati localmente sul server in cui l'applicazione è distribuita.",
),
"info.privacy_data_collected_heading" => (
"Data Collected",
"Erhobene Daten",
"Données collectées",
"Datos recopilados",
"Dati raccolti",
),
"info.privacy_data_collected_list" => (
" <li><strong>CSAF documents</strong> — stored in the embedded redb database</li>\n <li><strong>User accounts</strong> — login credentials (hashed) stored in SQLite</li>\n <li><strong>Audit logs</strong> — timestamps and actions for document operations</li>\n <li><strong>Session data</strong> — temporary session tokens for authentication</li>",
" <li><strong>CSAF-Dokumente</strong> — gespeichert in der eingebetteten redb-Datenbank</li>\n <li><strong>Benutzerkonten</strong> — Anmeldedaten (gehasht) in SQLite gespeichert</li>\n <li><strong>Audit-Protokolle</strong> — Zeitstempel und Aktionen zu Dokumentvorgängen</li>\n <li><strong>Sitzungsdaten</strong> — temporäre Sitzungs-Token zur Authentifizierung</li>",
" <li><strong>Documents CSAF</strong> — stockés dans la base de données redb embarquée</li>\n <li><strong>Comptes utilisateur</strong> — identifiants de connexion (hachés) stockés dans SQLite</li>\n <li><strong>Journaux d'audit</strong> — horodatages et actions des opérations sur les documents</li>\n <li><strong>Données de session</strong> — jetons de session temporaires pour l'authentification</li>",
" <li><strong>Documentos CSAF</strong> — almacenados en la base de datos redb embebida</li>\n <li><strong>Cuentas de usuario</strong> — credenciales de acceso (con hash) almacenadas en SQLite</li>\n <li><strong>Registros de auditoría</strong> — marcas de tiempo y acciones de las operaciones sobre documentos</li>\n <li><strong>Datos de sesión</strong> — tókenes de sesión temporales para la autenticación</li>",
" <li><strong>Documenti CSAF</strong> — memorizzati nel database redb integrato</li>\n <li><strong>Account utente</strong> — credenziali di accesso (con hash) memorizzate in SQLite</li>\n <li><strong>Log di audit</strong> — timestamp e azioni delle operazioni sui documenti</li>\n <li><strong>Dati di sessione</strong> — token di sessione temporanei per l'autenticazione</li>",
),
"info.privacy_no_external_heading" => (
"No External Communication",
"Keine externe Kommunikation",
"Aucune communication externe",
"Sin comunicación externa",
"Nessuna comunicazione esterna",
),
"info.privacy_no_external_text" => (
"The application does not send data to external services, analytics platforms, or third-party APIs. All processing occurs locally.",
"Die Anwendung sendet keine Daten an externe Dienste, Analyseplattformen oder APIs Dritter. Die gesamte Verarbeitung erfolgt lokal.",
"L'application n'envoie aucune donnée à des services externes, des plateformes d'analyse ou des API tierces. Tout le traitement se déroule localement.",
"La aplicación no envía datos a servicios externos, plataformas de analítica ni API de terceros. Todo el procesamiento se realiza localmente.",
"L'applicazione non invia dati a servizi esterni, piattaforme di analisi o API di terze parti. Tutta l'elaborazione avviene localmente.",
),
"info.privacy_retention_heading" => (
"Data Retention",
"Datenspeicherung",
"Conservation des données",
"Retención de datos",
"Conservazione dei dati",
),
"info.privacy_retention_text" => (
"Data is retained until explicitly deleted by the user or administrator. Audit logs are stored indefinitely for compliance purposes.",
"Daten werden aufbewahrt, bis sie von Benutzer oder Administrator ausdrücklich gelöscht werden. Audit-Protokolle werden aus Compliance-Gründen unbegrenzt gespeichert.",
"Les données sont conservées jusqu'à leur suppression explicite par l'utilisateur ou l'administrateur. Les journaux d'audit sont conservés indéfiniment à des fins de conformité.",
"Los datos se conservan hasta que el usuario o el administrador los elimine explícitamente. Los registros de auditoría se almacenan de forma indefinida por motivos de cumplimiento normativo.",
"I dati vengono conservati finché non vengono eliminati esplicitamente dall'utente o dall'amministratore. I log di audit vengono conservati a tempo indeterminato per finalità di conformità.",
),
"info.privacy_gdpr_heading" => (
"GDPR Compliance",
"DSGVO-Konformität",
"Conformité RGPD",
"Cumplimiento del RGPD",
"Conformità al GDPR",
),
"info.privacy_gdpr_text" => (
"As a self-hosted tool, GDPR compliance responsibility lies with the deploying organisation. The application supports data export and deletion to facilitate compliance.",
"Als selbst gehostetes Werkzeug liegt die Verantwortung für die DSGVO-Konformität bei der betreibenden Organisation. Die Anwendung unterstützt Datenexport und -löschung, um die Einhaltung zu erleichtern.",
"En tant qu'outil auto-hébergé, la responsabilité de la conformité au RGPD incombe à l'organisation qui l'exploite. L'application prend en charge l'export et la suppression des données pour faciliter cette conformité.",
"Al tratarse de una herramienta autoalojada, la responsabilidad del cumplimiento del RGPD recae en la organización que la despliega. La aplicación admite la exportación y eliminación de datos para facilitar dicho cumplimiento.",
"Trattandosi di uno strumento autoospitato, la responsabilità della conformità al GDPR spetta all'organizzazione che lo utilizza. L'applicazione supporta l'esportazione e l'eliminazione dei dati per facilitare la conformità.",
),
_ => return None,
})
}
/// `GET /info/security`.
fn tr_security(key: &str) -> Option<Entry> {
Some(match key {
"info.security_heading" => (
"Security",
"Sicherheit",
"Sécurité",
"Seguridad",
"Sicurezza",
),
"info.security_transport_heading" => (
"Transport Security",
"Transportsicherheit",
"Sécurité du transport",
"Seguridad del transporte",
"Sicurezza del trasporto",
),
"info.security_transport_list" => (
" <li>TLS 1.3 enforced via Rustls (no OpenSSL dependency)</li>\n <li>RSA ciphers excluded; only elliptic-curve cryptography (ECC) used</li>\n <li>Automatic self-signed certificate generation (45-day validity)</li>\n <li>HTTP/1.1 + HTTP/2 auto-negotiated via ALPN over TCP</li>\n <li>HTTP/3 served over QUIC/UDP</li>",
" <li>TLS 1.3 wird über Rustls erzwungen (keine OpenSSL-Abhängigkeit)</li>\n <li>RSA-Chiffren ausgeschlossen; es wird ausschließlich Elliptic-Curve-Kryptografie (ECC) verwendet</li>\n <li>Automatische Erzeugung selbstsignierter Zertifikate (45 Tage Gültigkeit)</li>\n <li>HTTP/1.1 + HTTP/2 werden über ALPN auf TCP automatisch ausgehandelt</li>\n <li>HTTP/3 wird über QUIC/UDP bereitgestellt</li>",
" <li>TLS 1.3 imposé via Rustls (aucune dépendance à OpenSSL)</li>\n <li>Chiffrements RSA exclus ; seule la cryptographie à courbes elliptiques (ECC) est utilisée</li>\n <li>Génération automatique de certificats auto-signés (validité de 45 jours)</li>\n <li>HTTP/1.1 + HTTP/2 négociés automatiquement via ALPN sur TCP</li>\n <li>HTTP/3 servi via QUIC/UDP</li>",
" <li>TLS 1.3 forzado mediante Rustls (sin dependencia de OpenSSL)</li>\n <li>Cifrados RSA excluidos; solo se usa criptografía de curva elíptica (ECC)</li>\n <li>Generación automática de certificados autofirmados (validez de 45 días)</li>\n <li>HTTP/1.1 + HTTP/2 negociados automáticamente mediante ALPN sobre TCP</li>\n <li>HTTP/3 servido sobre QUIC/UDP</li>",
" <li>TLS 1.3 imposto tramite Rustls (nessuna dipendenza da OpenSSL)</li>\n <li>Cifrari RSA esclusi; viene utilizzata solo la crittografia a curva ellittica (ECC)</li>\n <li>Generazione automatica di certificati autofirmati (validità di 45 giorni)</li>\n <li>HTTP/1.1 + HTTP/2 negoziati automaticamente tramite ALPN su TCP</li>\n <li>HTTP/3 servito su QUIC/UDP</li>",
),
"info.security_auth_heading" => (
"Authentication",
"Authentifizierung",
"Authentification",
"Autenticación",
"Autenticazione",
),
"info.security_auth_list" => (
" <li>Password hashing using Argon2id (RFC 9106)</li>\n <li>API key authentication for programmatic access</li>\n <li>Session-based authentication for the web UI</li>",
" <li>Passwort-Hashing mit Argon2id (RFC 9106)</li>\n <li>API-Schlüssel-Authentifizierung für den programmatischen Zugriff</li>\n <li>Sitzungsbasierte Authentifizierung für die Web-Oberfläche</li>",
" <li>Hachage des mots de passe avec Argon2id (RFC 9106)</li>\n <li>Authentification par clé API pour l'accès programmatique</li>\n <li>Authentification par session pour l'interface web</li>",
" <li>Hash de contraseñas mediante Argon2id (RFC 9106)</li>\n <li>Autenticación por clave de API para el acceso programático</li>\n <li>Autenticación basada en sesión para la interfaz web</li>",
" <li>Hashing delle password con Argon2id (RFC 9106)</li>\n <li>Autenticazione tramite chiave API per l'accesso programmatico</li>\n <li>Autenticazione basata su sessione per l'interfaccia web</li>",
),
"info.security_integrity_heading" => (
"Data Integrity",
"Datenintegrität",
"Intégrité des données",
"Integridad de los datos",
"Integrità dei dati",
),
"info.security_integrity_list" => (
" <li>SHA-256 and SHA3-512 sidecar hash files for exported documents</li>\n <li>CSAF document validation on every create, update, import, and export</li>\n <li>Audit trail for all document lifecycle operations</li>",
" <li>SHA-256- und SHA3-512-Sidecar-Hashdateien für exportierte Dokumente</li>\n <li>CSAF-Dokumentvalidierung bei jedem Erstellen, Aktualisieren, Importieren und Exportieren</li>\n <li>Prüfpfad für alle Vorgänge im Dokumentenlebenszyklus</li>",
" <li>Fichiers de hachage sidecar SHA-256 et SHA3-512 pour les documents exportés</li>\n <li>Validation des documents CSAF à chaque création, mise à jour, import et export</li>\n <li>Piste d'audit pour toutes les opérations du cycle de vie des documents</li>",
" <li>Archivos hash sidecar SHA-256 y SHA3-512 para los documentos exportados</li>\n <li>Validación de documentos CSAF en cada creación, actualización, importación y exportación</li>\n <li>Registro de auditoría de todas las operaciones del ciclo de vida del documento</li>",
" <li>File hash sidecar SHA-256 e SHA3-512 per i documenti esportati</li>\n <li>Validazione dei documenti CSAF ad ogni creazione, aggiornamento, importazione ed esportazione</li>\n <li>Registro di controllo per tutte le operazioni del ciclo di vita dei documenti</li>",
),
"info.security_input_heading" => (
"Input Validation",
"Eingabevalidierung",
"Validation des entrées",
"Validación de entradas",
"Convalida degli input",
),
"info.security_input_list" => (
" <li>All CSAF documents validated against structural and semantic rules</li>\n <li>CVSS score range validation (0.0 to 10.0)</li>\n <li>Product ID cross-reference validation</li>\n <li>HTML output is escaped to prevent XSS</li>\n <li>RFC 9457 Problem Details for all API errors</li>",
" <li>Alle CSAF-Dokumente werden gegen strukturelle und semantische Regeln validiert</li>\n <li>Validierung des CVSS-Wertebereichs (0,0 bis 10,0)</li>\n <li>Validierung der Produkt-ID-Querverweise</li>\n <li>HTML-Ausgaben werden zur Vermeidung von XSS escaped</li>\n <li>RFC 9457 Problem Details für alle API-Fehler</li>",
" <li>Tous les documents CSAF sont validés selon des règles structurelles et sémantiques</li>\n <li>Validation de la plage de scores CVSS (0,0 à 10,0)</li>\n <li>Validation des références croisées d'ID produit</li>\n <li>La sortie HTML est échappée pour empêcher les attaques XSS</li>\n <li>RFC 9457 Problem Details pour toutes les erreurs d'API</li>",
" <li>Todos los documentos CSAF se validan según reglas estructurales y semánticas</li>\n <li>Validación del rango de puntuación CVSS (0,0 a 10,0)</li>\n <li>Validación de referencias cruzadas de ID de producto</li>\n <li>La salida HTML se escapa para evitar XSS</li>\n <li>RFC 9457 Problem Details para todos los errores de la API</li>",
" <li>Tutti i documenti CSAF vengono validati secondo regole strutturali e semantiche</li>\n <li>Validazione dell'intervallo del punteggio CVSS (da 0,0 a 10,0)</li>\n <li>Validazione dei riferimenti incrociati dell'ID prodotto</li>\n <li>L'output HTML viene sottoposto a escape per prevenire XSS</li>\n <li>RFC 9457 Problem Details per tutti gli errori dell'API</li>",
),
"info.security_reporting_heading" => (
"Reporting Vulnerabilities",
"Melden von Sicherheitslücken",
"Signalement des vulnérabilités",
"Notificación de vulnerabilidades",
"Segnalazione delle vulnerabilità",
),
"info.security_reporting_text" => (
"If you discover a security vulnerability in CSAF, please report it responsibly by contacting the development team at ndaal in Cologne.",
"Wenn Sie eine Sicherheitslücke in CSAF entdecken, melden Sie diese bitte verantwortungsvoll, indem Sie das Entwicklungsteam bei ndaal in Köln kontaktieren.",
"Si vous découvrez une vulnérabilité de sécurité dans CSAF, veuillez la signaler de manière responsable en contactant l'équipe de développement chez ndaal à Cologne.",
"Si descubre una vulnerabilidad de seguridad en CSAF, notifíquela de forma responsable poniéndose en contacto con el equipo de desarrollo de ndaal en Colonia.",
"Se scopri una vulnerabilità di sicurezza in CSAF, segnalala in modo responsabile contattando il team di sviluppo di ndaal a Colonia.",
),
_ => return None,
})
}
/// The four embedded-document pages (Changelog/README/Administrator/User
/// Guide) share their title text with their `<h1>` -- the document bodies
/// themselves are raw file embeds and stay untranslated in every language.
fn tr_docs(key: &str) -> Option<Entry> {
Some(match key {
"info.changelog_heading" => (
"Changelog",
"Changelog",
"Changelog",
"Changelog",
"Changelog",
),
"info.readme_heading" => ("README", "LIESMICH", "LISEZMOI", "LEEME", "LEGGIMI"),
"info.administrator_heading" => (
"Administrator Guide",
"Administratorhandbuch",
"Guide d'administration",
"Guía del administrador",
"Guida dell'amministratore",
),
"info.user_heading" => (
"User Guide",
"Benutzerhandbuch",
"Guide utilisateur",
"Guía del usuario",
"Guida utente",
),
_ => return None,
})
}
#[cfg(test)]
mod tests {
use super::super::{Lang, t};
use super::*;
#[test]
fn every_info_key_has_five_nonempty_translations() {
for key in [
"info.subnav_about",
"info.subnav_license",
"info.subnav_system",
"info.subnav_privacy",
"info.subnav_security",
"info.subnav_changelog",
"info.subnav_readme",
"info.subnav_administrator",
"info.subnav_user",
"info.about_heading",
"info.about_intro",
"info.about_version_label",
"info.about_features_heading",
"info.about_features_list",
"info.about_standards_heading",
"info.about_source_heading",
"info.about_source_intro",
"info.about_repository_label",
"info.about_releases_label",
"info.about_cratesio_label",
"info.about_sha_note",
"info.about_contact_heading",
"info.about_contact_text",
"info.license_heading",
"info.license_intro",
"info.license_spdx_label",
"info.license_summary_heading",
"info.license_summary_list",
"info.license_full_text_label",
"info.system_heading",
"info.system_host_heading",
"info.system_hostname_label",
"info.system_os_label",
"info.system_kernel_label",
"info.system_cpus_label",
"info.system_memory_label",
"info.system_application_heading",
"info.system_version_label",
"info.system_rust_version_label",
"info.system_listen_address_label",
"info.system_data_directory_label",
"info.system_storage_status_label",
"info.system_documents_stored_label",
"info.system_csaf_mode_label",
"info.system_status_healthy",
"info.system_status_degraded",
"info.privacy_heading",
"info.privacy_data_processing_heading",
"info.privacy_data_processing_text",
"info.privacy_data_collected_heading",
"info.privacy_data_collected_list",
"info.privacy_no_external_heading",
"info.privacy_no_external_text",
"info.privacy_retention_heading",
"info.privacy_retention_text",
"info.privacy_gdpr_heading",
"info.privacy_gdpr_text",
"info.security_heading",
"info.security_transport_heading",
"info.security_transport_list",
"info.security_auth_heading",
"info.security_auth_list",
"info.security_integrity_heading",
"info.security_integrity_list",
"info.security_input_heading",
"info.security_input_list",
"info.security_reporting_heading",
"info.security_reporting_text",
"info.changelog_heading",
"info.readme_heading",
"info.administrator_heading",
"info.user_heading",
] {
let (en, de, fr, es, it) = tr(key).unwrap_or_else(|| panic!("{key} resolves"));
assert!(
!en.is_empty()
&& !de.is_empty()
&& !fr.is_empty()
&& !es.is_empty()
&& !it.is_empty(),
"{key}"
);
}
}
#[test]
fn unknown_key_is_not_claimed_by_this_table() {
assert_eq!(tr("info.does_not_exist"), None);
}
#[test]
fn t_dispatches_info_keys_through_the_shared_lookup() {
assert_eq!(t(Lang::De, "info.license_heading"), "Lizenz");
assert_eq!(t(Lang::Fr, "info.readme_heading"), "LISEZMOI");
assert_eq!(t(Lang::It, "info.system_status_healthy"), "Operativo");
}
}