- Full UI dictionaries for French, Italian and Spanish (all views, wizard, legal gate, templates); browser-language auto-detection extended to all five locales - Fallback architecture: LText now carries optional fr/it/es entries; regulatory legal texts (content pack, classify reasons, document strings) fall back to English until legal translations land — clearly stated in the language setting - Locale-aware date formatting (DE dots, FR/IT/ES slashes, EN ISO) - PWA: web app manifest, network-first service worker with offline cache fallback, generated icons (512/192/apple-touch) — installable on phone home screen and macOS dock - 2 new tests incl. dictionary-completeness check for FR/IT/ES (58 total) Claude-Session: https://claude.ai/code/session_017YjohVNx1ZGNM4MX7tHpfv
87 lines
2.9 KiB
TypeScript
87 lines
2.9 KiB
TypeScript
import { useI18n } from '../i18n/context';
|
|
import type { Locale } from '../i18n/types';
|
|
import type { RiskClass, Role } from '../engine/types';
|
|
|
|
/** Kurze UI-Labels für Risikoklassen je Sprache. */
|
|
export const RISK_UI: Readonly<Record<Locale, Readonly<Record<RiskClass, string>>>> = {
|
|
de: {
|
|
'out-of-scope': 'Nicht anwendbar',
|
|
prohibited: 'Verboten',
|
|
high: 'Hochrisiko',
|
|
limited: 'Begrenztes Risiko',
|
|
minimal: 'Minimales Risiko',
|
|
},
|
|
en: {
|
|
'out-of-scope': 'Out of scope',
|
|
prohibited: 'Prohibited',
|
|
high: 'High-risk',
|
|
limited: 'Limited risk',
|
|
minimal: 'Minimal risk',
|
|
},
|
|
fr: {
|
|
'out-of-scope': 'Hors champ',
|
|
prohibited: 'Interdit',
|
|
high: 'Haut risque',
|
|
limited: 'Risque limité',
|
|
minimal: 'Risque minimal',
|
|
},
|
|
it: {
|
|
'out-of-scope': 'Fuori ambito',
|
|
prohibited: 'Vietato',
|
|
high: 'Alto rischio',
|
|
limited: 'Rischio limitato',
|
|
minimal: 'Rischio minimo',
|
|
},
|
|
es: {
|
|
'out-of-scope': 'Fuera de ámbito',
|
|
prohibited: 'Prohibido',
|
|
high: 'Alto riesgo',
|
|
limited: 'Riesgo limitado',
|
|
minimal: 'Riesgo mínimo',
|
|
},
|
|
};
|
|
|
|
/** Kurze UI-Labels für Rollen je Sprache. */
|
|
export const ROLE_UI: Readonly<Record<Locale, Readonly<Record<Role, string>>>> = {
|
|
de: { provider: 'Anbieter', deployer: 'Betreiber', importer: 'Einführer', distributor: 'Händler' },
|
|
en: { provider: 'Provider', deployer: 'Deployer', importer: 'Importer', distributor: 'Distributor' },
|
|
fr: { provider: 'Fournisseur', deployer: 'Déployeur', importer: 'Importateur', distributor: 'Distributeur' },
|
|
it: { provider: 'Fornitore', deployer: 'Deployer', importer: 'Importatore', distributor: 'Distributore' },
|
|
es: { provider: 'Proveedor', deployer: 'Responsable del despliegue', importer: 'Importador', distributor: 'Distribuidor' },
|
|
};
|
|
|
|
/** Farbiges Stempel-Badge für die Risikoklasse eines Systems. */
|
|
export function RiskBadge({ risk }: { readonly risk: RiskClass }): JSX.Element {
|
|
const { locale } = useI18n();
|
|
return <span className={`badge ${risk}`}>{RISK_UI[locale][risk]}</span>;
|
|
}
|
|
|
|
/**
|
|
* Löst einen Browser-Download für generierten Text aus.
|
|
*
|
|
* @param filename - Zieldateiname
|
|
* @param content - Dateiinhalt
|
|
* @param mime - MIME-Type (Default: Markdown)
|
|
*/
|
|
export function downloadText(filename: string, content: string, mime = 'text/markdown'): void {
|
|
const blob = new Blob([content], { type: `${mime};charset=utf-8` });
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = filename;
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
|
|
/** Heutiges Datum im ISO-Format (YYYY-MM-DD). */
|
|
export function todayISO(): string {
|
|
return new Date().toISOString().slice(0, 10);
|
|
}
|
|
|
|
/** Formatiert ein ISO-Datum für die UI (DE: TT.MM.JJJJ, FR/IT/ES: TT/MM/JJJJ, EN: ISO). */
|
|
export function uiDate(iso: string, locale: Locale): string {
|
|
if (locale === 'en') return iso;
|
|
const sep = locale === 'de' ? '.' : '/';
|
|
return iso.split('-').reverse().join(sep);
|
|
}
|