diff --git a/gerbil-manager-web/src/genetics/genotype.ts b/gerbil-manager-web/src/genetics/genotype.ts index 3702b9f..6441c93 100644 --- a/gerbil-manager-web/src/genetics/genotype.ts +++ b/gerbil-manager-web/src/genetics/genotype.ts @@ -79,12 +79,14 @@ export function wildType(): Genotype { * The Sls locus is OMITTED when wild-type (sl/sl) so legacy 8-locus strings and * the colour catalog stay byte-identical; it only appears for WP/Sls carriers * (e.g. "… spsp rere Slsl"). Round-trips: a missing Sls re-parses to sl/sl. + * GEN-3c: unknown alleles are STORED as '?' but DISPLAYED as '-' (breeder + * convention) — e.g. ['C','?'] renders "C-". */ export function toDisplayString(g: Genotype): string { return LOCUS_ORDER.filter( (locus) => locus !== 'Sls' || !(g.Sls[0] === 'sl' && g.Sls[1] === 'sl'), ) - .map((locus) => g[locus][0] + g[locus][1]) + .map((locus) => (g[locus][0] + g[locus][1]).replace(/\?/g, '-')) .join(' ') } @@ -144,6 +146,9 @@ function normalizeToken(tok: string): string | null { if (t === 'WP') t = 'Slsl' t = t.replace(/S\(l\)/g, 'Sl').replace(/s\(l\)/g, 'sl') t = t.replace(/Uw/g, 'G').replace(/uw/g, 'g') + // GEN-3c: '-' is the breeder's UNKNOWN marker on input; store internally as '?' + // (the frozen storage contract keeps '?'; only DISPLAY renders '-'). + t = t.replace(/-/g, '?') return t } @@ -193,7 +198,9 @@ export function fromDisplayString(input: string): Genotype { const [a, b] = splitToken(token) const refAllele = a === WILDCARD ? b : a if (refAllele === WILDCARD) { - throw new Error(`Token "${token}" is fully unknown; cannot infer its locus`) + // Fully-unknown token ("--"/"??", e.g. a positional placeholder): the locus + // can't be inferred — skip it (defaults to wild-type), don't throw. + continue } const locus = ALLELE_TO_LOCUS[refAllele] if (!locus) throw new Error(`Unknown allele "${refAllele}" in token "${token}"`) diff --git a/gerbil-manager-web/src/strings/de.ts b/gerbil-manager-web/src/strings/de.ts index 124ab90..d9ae5bb 100644 --- a/gerbil-manager-web/src/strings/de.ts +++ b/gerbil-manager-web/src/strings/de.ts @@ -1,665 +1,665 @@ -/** - * Zentrale deutsche Texte der Anwendung. - * Alle für Benutzer sichtbaren Zeichenketten gehören hierher — - * niemals Texte direkt in Komponenten hartkodieren. - */ -export const de = { - app: { - title: 'Rennmaus-Manager', - tagline: 'Verwaltung für Rennmäuse, Würfe und Züchter', - }, - nav: { - home: 'Start', - gerbils: 'Rennmäuse', - litters: 'Würfe', - breeders: 'Züchter', - genetics: 'Genetik', - // FEAT-2 (Oscar): Becken + Kontakte - enclosures: 'Becken', - contacts: 'Kontakte', - // FEAT-7 (Kelly): Statistik - statistics: 'Statistik', - // FEAT-7 (Kevin): „Mehr“-Overflow auf dem Smartphone - more: 'Mehr', - // FEAT-12a (Kevin): Abgabe - forSale: 'Abgabe', - // FEAT-13 (Kelly): Verträge + Einstellungen - contracts: 'Verträge', - settings: 'Einstellungen', - openMenu: 'Menü öffnen', - closeMenu: 'Menü schließen', - mainNavigation: 'Hauptnavigation', - // HELP-1 - help: 'Hilfe', - }, - pages: { - home: { - title: 'Willkommen', - intro: - 'Hier verwaltest du deine Rennmäuse, Würfe und Züchter. Wähle einen Bereich aus der Navigation.', - }, - gerbils: { - title: 'Rennmäuse', - // Liste - newButton: 'Neue Rennmaus', - empty: 'Keine Rennmäuse gefunden.', - countLabel: 'Tiere', - searchPlaceholder: 'Name suchen …', - // Filter-Beschriftungen - filters: { - title: 'Filter', - status: 'Status', - gender: 'Geschlecht', - colorVariety: 'Farbschlag', - all: 'Alle', - reset: 'Filter zurücksetzen', - sortBy: 'Sortieren nach', - }, - // Sortier-Optionen - sort: { - nameAsc: 'Name (A–Z)', - nameDesc: 'Name (Z–A)', - birthDesc: 'Geburtsdatum (neueste)', - birthAsc: 'Geburtsdatum (älteste)', - }, - // Feld-Beschriftungen (Stammdaten) - fields: { - name: 'Name', - gender: 'Geschlecht', - status: 'Status', - dateOfBirth: 'Geburtsdatum', - dateOfDeath: 'Todesdatum', - causeOfDeath: 'Todesursache', - goHomeDate: 'Abgabedatum', - litter: 'Wurf', - enclosure: 'Becken', - colorVariety: 'Farbschlag', - origin: 'Herkunft', - receiver: 'Abnehmer', - genotype: 'Genotyp', - notes: 'Notizen', - }, - // Detailseite - detail: { - masterData: 'Stammdaten', - genetics: 'Genetik', - resolvedFarbschlag: 'Errechneter Farbschlag', - resolvedPrefix: 'Errechnet', - farbschlagMismatch: 'Weicht vom eingetragenen Farbschlag ab.', - genotypeNotSet: 'Kein Genotyp hinterlegt.', - testMating: 'Probeverpaarung', - edit: 'Bearbeiten', - back: 'Zurück zur Liste', - tabs: { - photos: 'Fotos', - health: 'Gesundheit', - weight: 'Gewicht', - }, - tabPlaceholder: 'Dieser Bereich entsteht in einem späteren Schritt.', - notFound: 'Diese Rennmaus wurde nicht gefunden.', - moreData: 'Weitere Daten', - }, - // Formular (anlegen/bearbeiten) - form: { - createTitle: 'Neue Rennmaus anlegen', - editTitle: 'Rennmaus bearbeiten', - none: '— keine Angabe —', - genotypeHint: - 'Optional. Format z. B. „Aa CC Dd EE GG Pp Spsp rere“. Unbekannte Allele als „?“.', - save: 'Speichern', - cancel: 'Abbrechen', - saving: 'Speichern …', - }, - // Validierungsmeldungen - validation: { - nameRequired: 'Bitte einen Namen eingeben.', - genderRequired: 'Bitte ein Geschlecht auswählen.', - genotypeInvalid: 'Der Genotyp ist ungültig.', - dateOfDeathBeforeBirth: 'Das Todesdatum darf nicht vor dem Geburtsdatum liegen.', - }, - // Aufzählungen - genderLabels: { - unknown: 'Unbekannt', - male: 'Männlich', - female: 'Weiblich', - }, - statusLabels: { - Active: 'Aktiv', - Deceased: 'Verstorben', - GivenAway: 'Abgegeben', - ForSale: 'Zur Abgabe', - }, - }, - // ── FEAT-3 (Kevin): Zucht — Würfe + Zuchtpaare ── - litters: { - title: 'Würfe', - tabs: { - litters: 'Würfe', - pairs: 'Zuchtpaare', - }, - // Liste - newButton: 'Neuer Wurf', - empty: 'Keine Würfe gefunden.', - countLabel: 'Würfe', - filterYear: 'Jahr', - allYears: 'Alle Jahre', - sort: { - dateDesc: 'Wurfdatum (neueste)', - dateAsc: 'Wurfdatum (älteste)', - }, - // Felder - fields: { - name: 'Bezeichnung', - date: 'Wurfdatum', - father: 'Vater', - mother: 'Mutter', - totalBorn: 'Wurfstärke', - expectedGoHomeDate: 'Voraussichtliches Abgabedatum', - notes: 'Notizen', - }, - // Detail / Arbeitsbereich - detail: { - parents: 'Elterntiere', - registered: 'Erfasste Jungtiere', - juveniles: 'Jungtiere', - registerJuvenile: 'Jungtier erfassen', - expectedColors: 'Erwartete Farbschläge dieser Verpaarung', - needParentsGenotype: - 'Für die Farbvorhersage müssen bei Vater und Mutter Genotypen hinterlegt sein.', - noJuveniles: 'Noch keine Jungtiere erfasst.', - unknownParent: 'Unbekannt', - edit: 'Bearbeiten', - back: 'Zurück zur Liste', - notFound: 'Dieser Wurf wurde nicht gefunden.', - }, - // Formular - form: { - createTitle: 'Neuen Wurf anlegen', - editTitle: 'Wurf bearbeiten', - none: '— keine Angabe —', - goHomeHint: 'Vorschlag: 35 Tage nach dem Wurfdatum. Anpassbar.', - save: 'Speichern', - cancel: 'Abbrechen', - saving: 'Speichern …', - }, - validation: { - nameRequired: 'Bitte eine Bezeichnung eingeben.', - dateRequired: 'Bitte ein Wurfdatum angeben.', - invalidParentGender: 'Der Vater muss männlich und die Mutter weiblich sein.', - }, - // Zuchtpaar-Übersicht - pairs: { - title: 'Zuchtpaare', - empty: 'Noch keine Zuchtpaare — diese ergeben sich aus den erfassten Würfen.', - litterCount: 'Würfe', - lastLitter: 'Letzter Wurf', - testMating: 'Probeverpaarung', - }, - }, - breeders: { - title: 'Züchter', - comingSoon: 'Die Züchter-Verwaltung entsteht gerade. Bald kannst du hier Züchter erfassen.', - }, - notFound: { - title: 'Seite nicht gefunden', - message: 'Die angeforderte Seite existiert nicht.', - backHome: 'Zur Startseite', - }, - // ── FEAT-5 (Kevin): Genetik / Probeverpaarung ── - genetik: { - title: 'Genetik', - subtitle: 'Probeverpaarung – Farbschläge der Nachkommen vorhersagen', - father: 'Vater', - mother: 'Mutter', - pickAnimal: 'Tier auswählen', - pickAnimalSearch: 'Name suchen …', - pickAnimalNone: 'Keine passenden Tiere gefunden.', - pickAnimalNoGenotype: 'Für dieses Tier ist kein Genotyp hinterlegt – bitte unten eingeben.', - clearAnimal: 'Auswahl entfernen', - genotypeLabel: 'Genotyp', - genotypeHint: 'Format z. B. „Aa CC Dd EE GG Pp Spsp rere“. Unbekannte Allele als „?“.', - genotypeInvalid: 'Der Genotyp ist ungültig.', - genotypePreview: 'Farbschlag', - run: 'Probeverpaarung berechnen', - needBoth: 'Bitte für Vater und Mutter einen gültigen Genotyp angeben.', - resultsTitle: 'Mögliche Nachkommen', - byFarbschlag: 'Nach Farbschlag', - byGenotype: 'Nach Genotyp', - showGenotypes: 'Genotypen anzeigen', - hideGenotypes: 'Genotypen ausblenden', - probability: 'Wahrscheinlichkeit', - noOffspring: 'Keine lebend geborenen Nachkommen möglich.', - warningsTitle: 'Hinweise', - }, - // ── FEAT-12a (Kevin): Abgabe (For-Sale / Inserate) ── - abgabe: { - title: 'Abgabe', - subtitle: 'Abgabetiere gruppieren und Inserate erstellen', - empty: 'Keine Tiere zur Abgabe. Stelle Tiere über „Zur Abgabe stellen“ ein.', - markAction: 'Zur Abgabe stellen', - markMultiAction: 'Ausgewählte zur Abgabe stellen', - grouping: { - title: 'Gruppen', - byEnclosure: 'Nach Becken vorgeschlagen', - ungrouped: 'Ohne Becken', - moveTo: 'Verschieben nach', - newGroup: 'Neue Gruppe', - groupLabel: 'Gruppe', - }, - // Inserat-Komposer (Vorlage der Züchterin) - listing: { - headingFor: (n: number, gender: string) => `Gruppe ${n} (${gender})`, - genderMale: 'männlich', - genderFemale: 'weiblich', - genderMixed: 'gemischt', - status: 'Status', - statusFree: 'FREI', - statusLooseReserved: 'LOCKER RESERVIERT', - statusReserved: 'RESERVIERT', - reservedName: 'Name (bei Reservierung)', - tagline: 'Überschrift (emotionaler Einzeiler)', - taglinePlaceholder: 'z. B. „Großer und kleiner Bruder“ – Männliches Rennmaus-Duo sucht ein Zuhause', - bornOn: 'geboren am', - personality: 'Charakter', - personalityPlaceholder: 'Ein paar Sätze zum Charakter des Tieres …', - photos: 'Fotos', - noPhotos: 'Für dieses Tier sind keine Fotos hinterlegt.', - }, - // Export / Paste-Paket - export: { - copyText: 'Text kopieren', - copied: 'Text in die Zwischenablage kopiert.', - copyFailed: 'Kopieren fehlgeschlagen.', - downloadPhotos: 'Fotos herunterladen', - zipping: 'Fotos werden gepackt …', - noPhotosSelected: 'Keine Fotos ausgewählt.', - zipFailed: 'Fotos konnten nicht gepackt werden.', - finish: 'Abgabe abschließen', - finishHint: 'Vertrag erstellen (folgt in einem späteren Schritt).', - }, - // KI-Assistent (Backend folgt mit API-Schlüssel) - ai: { - improve: 'Text mit KI verbessern', - generating: 'KI erstellt den Text …', - hintsLabel: 'Hinweise für die KI (optional)', - notConfigured: 'KI-Funktion: API-Schlüssel noch nicht konfiguriert.', - }, - }, - // ── FEAT-2 (Oscar): Becken (Enclosures) ── - becken: { - title: 'Becken', - newButton: 'Neues Becken', - empty: 'Keine Becken gefunden.', - countLabel: 'Becken', - searchPlaceholder: 'Name suchen …', - fields: { - name: 'Name', - notes: 'Notizen', - }, - occupancy: { - title: 'Bewohner', - empty: 'Dieses Becken ist derzeit leer.', - countOne: 'Tier', - countMany: 'Tiere', - }, - detail: { - edit: 'Bearbeiten', - back: 'Zurück zur Liste', - notFound: 'Dieses Becken wurde nicht gefunden.', - }, - form: { - createTitle: 'Neues Becken anlegen', - editTitle: 'Becken bearbeiten', - save: 'Speichern', - cancel: 'Abbrechen', - saving: 'Speichern …', - }, - validation: { - nameRequired: 'Bitte einen Namen eingeben.', - }, - delete: { - action: 'Löschen', - confirmMessage: 'Soll dieses Becken wirklich gelöscht werden?', - conflict: - 'Becken ist nicht leer — bitte zuerst alle Tiere in ein anderes Becken umsetzen.', - }, - }, - // ── FEAT-4 (Kelly): Stammbaum (Ahnentafel) ── - stammbaum: { - title: 'Stammbaum', - /** Überschrift mit Tiername. */ - titleFor: (name: string) => `Stammbaum von ${name}`, - /** Button auf der Tier-Detailseite (Integration durch FEAT-1). */ - openButton: 'Stammbaum', - backToAnimal: 'Zur Tierakte', - notFound: 'Diese Rennmaus wurde nicht gefunden.', - unknown: 'unbekannt', - /** Auf Karten am Rand: weitere Vorfahren nachladen. */ - expand: 'Vorfahren laden', - tapHint: 'Tippe auf ein Tier, um dessen Stammbaum anzuzeigen.', - zoomIn: 'Vergrößern', - zoomOut: 'Verkleinern', - zoomFit: 'Ansicht einpassen', - print: 'Drucken / PDF', - inbreeding: { - label: 'Inzuchtkoeffizient', - unavailable: 'wird berechnet …', - }, - printView: { - /** Spaltenüberschriften der Ahnentafel, Index = Generation. */ - generations: ['Tier', 'Eltern', 'Großeltern', 'Urgroßeltern', 'Ur-Urgroßeltern'], - createdOn: 'Erstellt am', - born: 'geb.', - }, - }, - // ── FEAT-7 (Kelly): Statistik & Berichte ── - statistik: { - title: 'Statistik & Berichte', - empty: - 'Noch keine Daten — lege zuerst Tiere und Würfe an oder importiere deinen Bestand.', - sectionEmpty: 'Noch keine Daten für diese Auswertung.', - littersPerYear: 'Würfe pro Jahr', - avgLitterSize: 'Durchschnittliche Wurfstärke pro Jahr', - avgLitterSizeHint: 'Nur Würfe mit erfasster Wurfstärke.', - farbschlag: 'Farbschlag-Verteilung', - farbschlagHint: 'Nur aktive Tiere.', - withoutFarbschlag: 'ohne Farbschlag', - population: 'Bestandsentwicklung', - populationHint: - 'Lebende, nicht abgegebene Tiere im Zeitverlauf. Tiere ohne Geburtsdatum sind nicht enthalten.', - populationUnit: 'Tiere', - losses: 'Verluste pro Jahr', - lossesHint: 'Verstorbene Tiere nach Todesjahr.', - }, - // ── FEAT-13 (Kelly): Abgabeverträge ── - vertraege: { - title: 'Abgabeverträge', - newButton: 'Neuer Vertrag', - empty: 'Noch keine Verträge — erstelle den ersten über „Neuer Vertrag“ oder den Abgabe-Bereich.', - countText: (n: number) => (n === 1 ? '1 Vertrag' : `${n} Verträge`), - download: 'Herunterladen', - delete: 'Löschen', - confirmDelete: - 'Vertrag wirklich löschen? Die Word-Datei wird mit entfernt; der Status der Tiere bleibt unverändert.', - animalsCount: (n: number) => (n === 1 ? '1 Tier' : `${n} Tiere`), - fields: { - contact: 'Abnehmer', - animals: 'Tiere', - price: 'Kaufpreis', - handoverDate: 'Übergabedatum', - contractDate: 'Vertragsdatum', - createdAt: 'Erstellt', - }, - // Assistent (/vertraege/neu) - wizard: { - title: 'Abgabe abschließen', - steps: ['Abnehmer', 'Tiere', 'Preis & Datum', 'Vertrag'], - back: 'Zurück', - next: 'Weiter', - cancel: 'Abbrechen', - // Schritt 1 - pickContact: 'Abnehmer auswählen', - searchContact: 'Name suchen …', - noContacts: 'Keine Kontakte gefunden.', - orCreateNew: 'Oder neuen Kontakt anlegen', - createContact: 'Kontakt anlegen und auswählen', - contactNameRequired: 'Bitte einen Namen für den Kontakt eingeben.', - // Schritt 2 - pickAnimals: 'Tiere bestätigen', - pickAnimalsHint: 'Nur lebende, nicht abgegebene Tiere werden angezeigt.', - noAnimals: 'Keine abgebbaren Tiere gefunden.', - animalsRequired: 'Bitte mindestens ein Tier auswählen.', - // Schritt 3 - priceLabel: 'Kaufpreis (€)', - pricePlaceholder: 'z. B. 72,00', - priceInvalid: 'Bitte einen gültigen Preis eingeben (z. B. 72,00).', - handoverLabel: 'Übergabedatum', - contractDateLabel: 'Vertragsdatum (optional, Standard = Übergabedatum)', - // Schritt 4 - summaryTitle: 'Zusammenfassung', - profileIncomplete: - 'Das Zuchtprofil ist unvollständig — Name, Adresse und Ort erscheinen im Vertrag. Jetzt unter Einstellungen ergänzen?', - profileLink: 'Zu den Einstellungen', - generate: 'Vertrag erzeugen', - generating: 'Vertrag wird erzeugt …', - successTitle: 'Vertrag erstellt!', - successText: - 'Die Tiere wurden als „Abgegeben“ markiert (Abnehmer und Abgabedatum gesetzt).', - downloadDocx: 'Vertrag herunterladen (.docx)', - toList: 'Zur Vertragsliste', - anotherOne: 'Weiteren Vertrag erstellen', - }, - }, - // ── FEAT-13 (Kelly): Einstellungen (Zuchtprofil) ── - einstellungen: { - title: 'Einstellungen', - zuchtprofil: { - title: 'Zuchtprofil', - intro: - 'Diese Angaben erscheinen als Verkäufer-Block in jedem Abgabevertrag.', - incompleteHint: - 'Noch unvollständig: Name, Adresse und Ort werden für den Vertrag benötigt.', - fields: { - zuchtName: 'Zuchtname', - name: 'Vor- und Nachname', - nameHint: 'Mit Anrede, z. B. „Frau Erika Muster“.', - address: 'Adresse', - addressHint: 'Straße Nr, PLZ Ort.', - phone: 'Telefon', - email: 'E-Mail', - homepage: 'Homepage', - city: 'Ort (Unterschriftszeile)', - }, - save: 'Speichern', - saving: 'Speichern …', - saved: 'Gespeichert.', - }, - }, - // ── FEAT-2 (Oscar): Kontakte (Contacts — Herkunft/Abnehmer) ── - kontakte: { - title: 'Kontakte', - newButton: 'Neuer Kontakt', - empty: 'Keine Kontakte gefunden.', - countLabel: 'Kontakte', - searchPlaceholder: 'Name suchen …', - // FEAT-13: contactInfo (Freitext) wurde durch strukturierte Felder ersetzt - // (DATA-2-Schema: email/phone/address) — gebraucht für den Käufer-Block der Verträge. - fields: { - name: 'Name', - email: 'E-Mail', - phone: 'Telefon', - address: 'Adresse', - notes: 'Notizen', - }, - linked: { - title: 'Verknüpfte Tiere', - empty: 'Diesem Kontakt sind keine Tiere zugeordnet.', - originRole: 'Herkunft von', - receiverRole: 'Abnehmer von', - }, - detail: { - edit: 'Bearbeiten', - back: 'Zurück zur Liste', - notFound: 'Dieser Kontakt wurde nicht gefunden.', - }, - form: { - createTitle: 'Neuen Kontakt anlegen', - editTitle: 'Kontakt bearbeiten', - addressHint: 'Straße Nr, PLZ Ort — so erscheint sie im Abgabevertrag.', - save: 'Speichern', - cancel: 'Abbrechen', - saving: 'Speichern …', - }, - validation: { - nameRequired: 'Bitte einen Namen eingeben.', - }, - delete: { - action: 'Löschen', - confirmMessage: 'Soll dieser Kontakt wirklich gelöscht werden?', - conflict: - 'Kontakt ist noch mit Tieren verknüpft (Herkunft/Abnehmer) und kann nicht gelöscht werden.', - }, - }, - // ── FEAT-6 (Oscar): Tier-Detail-Tabs (Gesundheit / Gewicht / Fotos) ── - tierTabs: { - actions: { - save: 'Speichern', - saving: 'Speichern …', - cancel: 'Abbrechen', - edit: 'Bearbeiten', - delete: 'Löschen', - }, - health: { - empty: 'Noch keine Gesundheitseinträge.', - newButton: 'Neuer Eintrag', - createTitle: 'Neuer Gesundheitseintrag', - editTitle: 'Gesundheitseintrag bearbeiten', - fields: { - date: 'Datum', - type: 'Art', - description: 'Beschreibung', - veterinarian: 'Tierarzt', - }, - typeLabels: { - Examination: 'Untersuchung', - Treatment: 'Behandlung', - Injury: 'Verletzung', - Vaccination: 'Impfung', - Other: 'Sonstiges', - }, - validation: { - dateRequired: 'Bitte ein Datum angeben.', - descriptionRequired: 'Bitte eine Beschreibung eingeben.', - }, - deleteConfirm: 'Diesen Gesundheitseintrag wirklich löschen?', - }, - weight: { - addTitle: 'Gewicht erfassen', - fields: { - date: 'Datum', - weightGrams: 'Gewicht (g)', - }, - addButton: 'Hinzufügen', - adding: 'Speichern …', - historyTitle: 'Verlauf', - chartTitle: 'Gewichtsverlauf', - chartNeedsTwo: 'Für die Verlaufskurve sind mindestens zwei Einträge nötig.', - empty: 'Noch keine Gewichtseinträge.', - gramsSuffix: 'g', - validation: { - dateRequired: 'Bitte ein Datum angeben.', - weightRequired: 'Bitte ein Gewicht in Gramm eingeben.', - weightRange: 'Bitte ein plausibles Gewicht zwischen 1 und 500 g eingeben.', - }, - deleteConfirm: 'Diesen Gewichtseintrag wirklich löschen?', - }, - photos: { - empty: 'Noch keine Fotos.', - uploadTitle: 'Foto hochladen', - chooseFile: 'Foto auswählen', - captionLabel: 'Bildunterschrift (optional)', - uploadButton: 'Hochladen', - uploading: 'Hochladen …', - profileBadge: 'Profilfoto', - deleteConfirm: 'Dieses Foto wirklich löschen?', - validation: { - fileRequired: 'Bitte zuerst ein Foto auswählen.', - }, - }, - }, - // ── EXPORT-1 (Oscar): Datenexport (Karte auf /einstellungen) ── - datenexport: { - title: 'Datenexport', - intro: - 'Sicherung deiner Daten — alle Tiere, Würfe und Kontakte als Tabellen (CSV für Excel) plus eine vollständige Datensicherung (JSON), gebündelt als Zip.', - button: 'Export herunterladen', - photoNote: - 'Fotos sind nicht enthalten — sie liegen als Bilddateien im Datenordner der Anwendung und können von dort gesichert werden.', - }, - }, - // ── HELP-1: In-App-Anleitung ── - hilfe: { - title: 'Hilfe & Anleitung', - subtitle: 'Kurzanleitung für den Rennmaus-Manager', - sections: { - ersteSchritte: 'Erste Schritte', - tiere: 'Rennmäuse anlegen & Fotos', - becken: 'Becken', - wuerfe: 'Würfe & Jungtiere', - genetik: 'Probeverpaarung & Genetik', - stammbaum: 'Stammbaum', - abgabe: 'Abgabe-Ablauf', - // FEAT-13-Followup (Kelly): Vertrag + Zuchtprofil - vertraege: 'Abgabevertrag erstellen', - einstellungen: 'Zuchtprofil (Einstellungen)', - statistik: 'Statistik', - datensicherung: 'Datensicherung', - }, - }, - api: { - errors: { - network: 'Verbindung zum Server fehlgeschlagen. Bitte später erneut versuchen.', - notFound: 'Der Eintrag wurde nicht gefunden.', - server: 'Auf dem Server ist ein Fehler aufgetreten.', - unknown: 'Ein unbekannter Fehler ist aufgetreten.', - }, - }, - common: { - loading: 'Lädt …', - retry: 'Erneut versuchen', - previous: 'Zurück', - next: 'Weiter', - page: 'Seite', - of: 'von', - yes: 'Ja', - no: 'Nein', - delete: 'Löschen', - confirmDelete: 'Wirklich löschen?', - pageNav: 'Seitennavigation', - }, - // Genetik-Warnungen: der Engine (src/genetics) liefert nur CODES, - // die deutschen Texte stehen hier. Schlüssel = GeneticsWarningCode-Werte. - genetics: { - warnings: { - SCHECKE_LETHAL: - 'Schecke × Schecke: Reinerbige Tiere (SpSp) sterben bereits im Mutterleib — etwa ein Viertel weniger Jungtiere.', - REX_SEMI_LETHAL: - 'Rex × Rex: Reinerbige Tiere (ReRe) sind nur eingeschränkt lebensfähig.', - SLS_LETHAL: - 'WP × WP: Reinerbige Tiere (S(l)S(l)) sterben bereits im Mutterleib (Rumpback) — weniger Jungtiere.', - SUPERSCHECKE_DEAF: - 'Schecke × WP: Superschecken (sehr hoher Weißanteil) sind möglich — erhöhtes Taubheitsrisiko.', - }, - unknownFarbschlag: 'Unbekannter Farbschlag', - }, - // ── FEAT-14 (Kevin): Charakterbogen — Eigenschaften + Notiz, speist den KI-Verkaufstext ── - // Traits: stabile KEYS (gespeichert) + deutsche LABELS (UI + KI-Prompt). - // Erweitern = eine Zeile in der Liste; Julian verfeinert die Auswahl. - character: { - sectionTitle: 'Charakter & Eigenschaften', - noteLabel: 'Notizen zum Charakter', - notePlaceholder: 'Ein paar Sätze zum Charakter dieses Tieres …', - save: 'Charakter speichern', - saved: 'Charakter gespeichert.', - none: 'Noch keine Eigenschaften ausgewählt.', - traits: [ - { key: 'zutraulich', label: 'zutraulich' }, - { key: 'handzahm', label: 'handzahm' }, - { key: 'neugierig', label: 'neugierig' }, - { key: 'aufgeschlossen', label: 'aufgeschlossen' }, - { key: 'ruhig', label: 'ruhig / ausgeglichen' }, - { key: 'lebhaft', label: 'lebhaft / aktiv' }, - { key: 'verschmust', label: 'verschmust' }, - { key: 'eigenstaendig', label: 'eigenständig' }, - { key: 'anfaengergeeignet', label: 'anfängergeeignet' }, - { key: 'futterfreudig', label: 'futterfreudig' }, - { key: 'buddelt', label: 'buddelt gern' }, - { key: 'klettert', label: 'klettert gern' }, - { key: 'laufrad', label: 'läuft gern im Laufrad' }, - { key: 'vertraeglich', label: 'gut verträglich' }, - { key: 'schreckhaft', label: 'schreckhaft' }, - ], - }, -} as const - -export type Strings = typeof de +/** + * Zentrale deutsche Texte der Anwendung. + * Alle für Benutzer sichtbaren Zeichenketten gehören hierher — + * niemals Texte direkt in Komponenten hartkodieren. + */ +export const de = { + app: { + title: 'Rennmaus-Manager', + tagline: 'Verwaltung für Rennmäuse, Würfe und Züchter', + }, + nav: { + home: 'Start', + gerbils: 'Rennmäuse', + litters: 'Würfe', + breeders: 'Züchter', + genetics: 'Genetik', + // FEAT-2 (Oscar): Becken + Kontakte + enclosures: 'Becken', + contacts: 'Kontakte', + // FEAT-7 (Kelly): Statistik + statistics: 'Statistik', + // FEAT-7 (Kevin): „Mehr“-Overflow auf dem Smartphone + more: 'Mehr', + // FEAT-12a (Kevin): Abgabe + forSale: 'Abgabe', + // FEAT-13 (Kelly): Verträge + Einstellungen + contracts: 'Verträge', + settings: 'Einstellungen', + openMenu: 'Menü öffnen', + closeMenu: 'Menü schließen', + mainNavigation: 'Hauptnavigation', + // HELP-1 + help: 'Hilfe', + }, + pages: { + home: { + title: 'Willkommen', + intro: + 'Hier verwaltest du deine Rennmäuse, Würfe und Züchter. Wähle einen Bereich aus der Navigation.', + }, + gerbils: { + title: 'Rennmäuse', + // Liste + newButton: 'Neue Rennmaus', + empty: 'Keine Rennmäuse gefunden.', + countLabel: 'Tiere', + searchPlaceholder: 'Name suchen …', + // Filter-Beschriftungen + filters: { + title: 'Filter', + status: 'Status', + gender: 'Geschlecht', + colorVariety: 'Farbschlag', + all: 'Alle', + reset: 'Filter zurücksetzen', + sortBy: 'Sortieren nach', + }, + // Sortier-Optionen + sort: { + nameAsc: 'Name (A–Z)', + nameDesc: 'Name (Z–A)', + birthDesc: 'Geburtsdatum (neueste)', + birthAsc: 'Geburtsdatum (älteste)', + }, + // Feld-Beschriftungen (Stammdaten) + fields: { + name: 'Name', + gender: 'Geschlecht', + status: 'Status', + dateOfBirth: 'Geburtsdatum', + dateOfDeath: 'Todesdatum', + causeOfDeath: 'Todesursache', + goHomeDate: 'Abgabedatum', + litter: 'Wurf', + enclosure: 'Becken', + colorVariety: 'Farbschlag', + origin: 'Herkunft', + receiver: 'Abnehmer', + genotype: 'Genotyp', + notes: 'Notizen', + }, + // Detailseite + detail: { + masterData: 'Stammdaten', + genetics: 'Genetik', + resolvedFarbschlag: 'Errechneter Farbschlag', + resolvedPrefix: 'Errechnet', + farbschlagMismatch: 'Weicht vom eingetragenen Farbschlag ab.', + genotypeNotSet: 'Kein Genotyp hinterlegt.', + testMating: 'Probeverpaarung', + edit: 'Bearbeiten', + back: 'Zurück zur Liste', + tabs: { + photos: 'Fotos', + health: 'Gesundheit', + weight: 'Gewicht', + }, + tabPlaceholder: 'Dieser Bereich entsteht in einem späteren Schritt.', + notFound: 'Diese Rennmaus wurde nicht gefunden.', + moreData: 'Weitere Daten', + }, + // Formular (anlegen/bearbeiten) + form: { + createTitle: 'Neue Rennmaus anlegen', + editTitle: 'Rennmaus bearbeiten', + none: '— keine Angabe —', + genotypeHint: + 'Optional. Format z. B. „Aa CC Dd EE GG Pp Spsp rere“. Unbekannte Allele als „-“.', + save: 'Speichern', + cancel: 'Abbrechen', + saving: 'Speichern …', + }, + // Validierungsmeldungen + validation: { + nameRequired: 'Bitte einen Namen eingeben.', + genderRequired: 'Bitte ein Geschlecht auswählen.', + genotypeInvalid: 'Der Genotyp ist ungültig.', + dateOfDeathBeforeBirth: 'Das Todesdatum darf nicht vor dem Geburtsdatum liegen.', + }, + // Aufzählungen + genderLabels: { + unknown: 'Unbekannt', + male: 'Männlich', + female: 'Weiblich', + }, + statusLabels: { + Active: 'Aktiv', + Deceased: 'Verstorben', + GivenAway: 'Abgegeben', + ForSale: 'Zur Abgabe', + }, + }, + // ── FEAT-3 (Kevin): Zucht — Würfe + Zuchtpaare ── + litters: { + title: 'Würfe', + tabs: { + litters: 'Würfe', + pairs: 'Zuchtpaare', + }, + // Liste + newButton: 'Neuer Wurf', + empty: 'Keine Würfe gefunden.', + countLabel: 'Würfe', + filterYear: 'Jahr', + allYears: 'Alle Jahre', + sort: { + dateDesc: 'Wurfdatum (neueste)', + dateAsc: 'Wurfdatum (älteste)', + }, + // Felder + fields: { + name: 'Bezeichnung', + date: 'Wurfdatum', + father: 'Vater', + mother: 'Mutter', + totalBorn: 'Wurfstärke', + expectedGoHomeDate: 'Voraussichtliches Abgabedatum', + notes: 'Notizen', + }, + // Detail / Arbeitsbereich + detail: { + parents: 'Elterntiere', + registered: 'Erfasste Jungtiere', + juveniles: 'Jungtiere', + registerJuvenile: 'Jungtier erfassen', + expectedColors: 'Erwartete Farbschläge dieser Verpaarung', + needParentsGenotype: + 'Für die Farbvorhersage müssen bei Vater und Mutter Genotypen hinterlegt sein.', + noJuveniles: 'Noch keine Jungtiere erfasst.', + unknownParent: 'Unbekannt', + edit: 'Bearbeiten', + back: 'Zurück zur Liste', + notFound: 'Dieser Wurf wurde nicht gefunden.', + }, + // Formular + form: { + createTitle: 'Neuen Wurf anlegen', + editTitle: 'Wurf bearbeiten', + none: '— keine Angabe —', + goHomeHint: 'Vorschlag: 35 Tage nach dem Wurfdatum. Anpassbar.', + save: 'Speichern', + cancel: 'Abbrechen', + saving: 'Speichern …', + }, + validation: { + nameRequired: 'Bitte eine Bezeichnung eingeben.', + dateRequired: 'Bitte ein Wurfdatum angeben.', + invalidParentGender: 'Der Vater muss männlich und die Mutter weiblich sein.', + }, + // Zuchtpaar-Übersicht + pairs: { + title: 'Zuchtpaare', + empty: 'Noch keine Zuchtpaare — diese ergeben sich aus den erfassten Würfen.', + litterCount: 'Würfe', + lastLitter: 'Letzter Wurf', + testMating: 'Probeverpaarung', + }, + }, + breeders: { + title: 'Züchter', + comingSoon: 'Die Züchter-Verwaltung entsteht gerade. Bald kannst du hier Züchter erfassen.', + }, + notFound: { + title: 'Seite nicht gefunden', + message: 'Die angeforderte Seite existiert nicht.', + backHome: 'Zur Startseite', + }, + // ── FEAT-5 (Kevin): Genetik / Probeverpaarung ── + genetik: { + title: 'Genetik', + subtitle: 'Probeverpaarung – Farbschläge der Nachkommen vorhersagen', + father: 'Vater', + mother: 'Mutter', + pickAnimal: 'Tier auswählen', + pickAnimalSearch: 'Name suchen …', + pickAnimalNone: 'Keine passenden Tiere gefunden.', + pickAnimalNoGenotype: 'Für dieses Tier ist kein Genotyp hinterlegt – bitte unten eingeben.', + clearAnimal: 'Auswahl entfernen', + genotypeLabel: 'Genotyp', + genotypeHint: 'Format z. B. „Aa CC Dd EE GG Pp Spsp rere“. Unbekannte Allele als „-“.', + genotypeInvalid: 'Der Genotyp ist ungültig.', + genotypePreview: 'Farbschlag', + run: 'Probeverpaarung berechnen', + needBoth: 'Bitte für Vater und Mutter einen gültigen Genotyp angeben.', + resultsTitle: 'Mögliche Nachkommen', + byFarbschlag: 'Nach Farbschlag', + byGenotype: 'Nach Genotyp', + showGenotypes: 'Genotypen anzeigen', + hideGenotypes: 'Genotypen ausblenden', + probability: 'Wahrscheinlichkeit', + noOffspring: 'Keine lebend geborenen Nachkommen möglich.', + warningsTitle: 'Hinweise', + }, + // ── FEAT-12a (Kevin): Abgabe (For-Sale / Inserate) ── + abgabe: { + title: 'Abgabe', + subtitle: 'Abgabetiere gruppieren und Inserate erstellen', + empty: 'Keine Tiere zur Abgabe. Stelle Tiere über „Zur Abgabe stellen“ ein.', + markAction: 'Zur Abgabe stellen', + markMultiAction: 'Ausgewählte zur Abgabe stellen', + grouping: { + title: 'Gruppen', + byEnclosure: 'Nach Becken vorgeschlagen', + ungrouped: 'Ohne Becken', + moveTo: 'Verschieben nach', + newGroup: 'Neue Gruppe', + groupLabel: 'Gruppe', + }, + // Inserat-Komposer (Vorlage der Züchterin) + listing: { + headingFor: (n: number, gender: string) => `Gruppe ${n} (${gender})`, + genderMale: 'männlich', + genderFemale: 'weiblich', + genderMixed: 'gemischt', + status: 'Status', + statusFree: 'FREI', + statusLooseReserved: 'LOCKER RESERVIERT', + statusReserved: 'RESERVIERT', + reservedName: 'Name (bei Reservierung)', + tagline: 'Überschrift (emotionaler Einzeiler)', + taglinePlaceholder: 'z. B. „Großer und kleiner Bruder“ – Männliches Rennmaus-Duo sucht ein Zuhause', + bornOn: 'geboren am', + personality: 'Charakter', + personalityPlaceholder: 'Ein paar Sätze zum Charakter des Tieres …', + photos: 'Fotos', + noPhotos: 'Für dieses Tier sind keine Fotos hinterlegt.', + }, + // Export / Paste-Paket + export: { + copyText: 'Text kopieren', + copied: 'Text in die Zwischenablage kopiert.', + copyFailed: 'Kopieren fehlgeschlagen.', + downloadPhotos: 'Fotos herunterladen', + zipping: 'Fotos werden gepackt …', + noPhotosSelected: 'Keine Fotos ausgewählt.', + zipFailed: 'Fotos konnten nicht gepackt werden.', + finish: 'Abgabe abschließen', + finishHint: 'Vertrag erstellen (folgt in einem späteren Schritt).', + }, + // KI-Assistent (Backend folgt mit API-Schlüssel) + ai: { + improve: 'Text mit KI verbessern', + generating: 'KI erstellt den Text …', + hintsLabel: 'Hinweise für die KI (optional)', + notConfigured: 'KI-Funktion: API-Schlüssel noch nicht konfiguriert.', + }, + }, + // ── FEAT-2 (Oscar): Becken (Enclosures) ── + becken: { + title: 'Becken', + newButton: 'Neues Becken', + empty: 'Keine Becken gefunden.', + countLabel: 'Becken', + searchPlaceholder: 'Name suchen …', + fields: { + name: 'Name', + notes: 'Notizen', + }, + occupancy: { + title: 'Bewohner', + empty: 'Dieses Becken ist derzeit leer.', + countOne: 'Tier', + countMany: 'Tiere', + }, + detail: { + edit: 'Bearbeiten', + back: 'Zurück zur Liste', + notFound: 'Dieses Becken wurde nicht gefunden.', + }, + form: { + createTitle: 'Neues Becken anlegen', + editTitle: 'Becken bearbeiten', + save: 'Speichern', + cancel: 'Abbrechen', + saving: 'Speichern …', + }, + validation: { + nameRequired: 'Bitte einen Namen eingeben.', + }, + delete: { + action: 'Löschen', + confirmMessage: 'Soll dieses Becken wirklich gelöscht werden?', + conflict: + 'Becken ist nicht leer — bitte zuerst alle Tiere in ein anderes Becken umsetzen.', + }, + }, + // ── FEAT-4 (Kelly): Stammbaum (Ahnentafel) ── + stammbaum: { + title: 'Stammbaum', + /** Überschrift mit Tiername. */ + titleFor: (name: string) => `Stammbaum von ${name}`, + /** Button auf der Tier-Detailseite (Integration durch FEAT-1). */ + openButton: 'Stammbaum', + backToAnimal: 'Zur Tierakte', + notFound: 'Diese Rennmaus wurde nicht gefunden.', + unknown: 'unbekannt', + /** Auf Karten am Rand: weitere Vorfahren nachladen. */ + expand: 'Vorfahren laden', + tapHint: 'Tippe auf ein Tier, um dessen Stammbaum anzuzeigen.', + zoomIn: 'Vergrößern', + zoomOut: 'Verkleinern', + zoomFit: 'Ansicht einpassen', + print: 'Drucken / PDF', + inbreeding: { + label: 'Inzuchtkoeffizient', + unavailable: 'wird berechnet …', + }, + printView: { + /** Spaltenüberschriften der Ahnentafel, Index = Generation. */ + generations: ['Tier', 'Eltern', 'Großeltern', 'Urgroßeltern', 'Ur-Urgroßeltern'], + createdOn: 'Erstellt am', + born: 'geb.', + }, + }, + // ── FEAT-7 (Kelly): Statistik & Berichte ── + statistik: { + title: 'Statistik & Berichte', + empty: + 'Noch keine Daten — lege zuerst Tiere und Würfe an oder importiere deinen Bestand.', + sectionEmpty: 'Noch keine Daten für diese Auswertung.', + littersPerYear: 'Würfe pro Jahr', + avgLitterSize: 'Durchschnittliche Wurfstärke pro Jahr', + avgLitterSizeHint: 'Nur Würfe mit erfasster Wurfstärke.', + farbschlag: 'Farbschlag-Verteilung', + farbschlagHint: 'Nur aktive Tiere.', + withoutFarbschlag: 'ohne Farbschlag', + population: 'Bestandsentwicklung', + populationHint: + 'Lebende, nicht abgegebene Tiere im Zeitverlauf. Tiere ohne Geburtsdatum sind nicht enthalten.', + populationUnit: 'Tiere', + losses: 'Verluste pro Jahr', + lossesHint: 'Verstorbene Tiere nach Todesjahr.', + }, + // ── FEAT-13 (Kelly): Abgabeverträge ── + vertraege: { + title: 'Abgabeverträge', + newButton: 'Neuer Vertrag', + empty: 'Noch keine Verträge — erstelle den ersten über „Neuer Vertrag“ oder den Abgabe-Bereich.', + countText: (n: number) => (n === 1 ? '1 Vertrag' : `${n} Verträge`), + download: 'Herunterladen', + delete: 'Löschen', + confirmDelete: + 'Vertrag wirklich löschen? Die Word-Datei wird mit entfernt; der Status der Tiere bleibt unverändert.', + animalsCount: (n: number) => (n === 1 ? '1 Tier' : `${n} Tiere`), + fields: { + contact: 'Abnehmer', + animals: 'Tiere', + price: 'Kaufpreis', + handoverDate: 'Übergabedatum', + contractDate: 'Vertragsdatum', + createdAt: 'Erstellt', + }, + // Assistent (/vertraege/neu) + wizard: { + title: 'Abgabe abschließen', + steps: ['Abnehmer', 'Tiere', 'Preis & Datum', 'Vertrag'], + back: 'Zurück', + next: 'Weiter', + cancel: 'Abbrechen', + // Schritt 1 + pickContact: 'Abnehmer auswählen', + searchContact: 'Name suchen …', + noContacts: 'Keine Kontakte gefunden.', + orCreateNew: 'Oder neuen Kontakt anlegen', + createContact: 'Kontakt anlegen und auswählen', + contactNameRequired: 'Bitte einen Namen für den Kontakt eingeben.', + // Schritt 2 + pickAnimals: 'Tiere bestätigen', + pickAnimalsHint: 'Nur lebende, nicht abgegebene Tiere werden angezeigt.', + noAnimals: 'Keine abgebbaren Tiere gefunden.', + animalsRequired: 'Bitte mindestens ein Tier auswählen.', + // Schritt 3 + priceLabel: 'Kaufpreis (€)', + pricePlaceholder: 'z. B. 72,00', + priceInvalid: 'Bitte einen gültigen Preis eingeben (z. B. 72,00).', + handoverLabel: 'Übergabedatum', + contractDateLabel: 'Vertragsdatum (optional, Standard = Übergabedatum)', + // Schritt 4 + summaryTitle: 'Zusammenfassung', + profileIncomplete: + 'Das Zuchtprofil ist unvollständig — Name, Adresse und Ort erscheinen im Vertrag. Jetzt unter Einstellungen ergänzen?', + profileLink: 'Zu den Einstellungen', + generate: 'Vertrag erzeugen', + generating: 'Vertrag wird erzeugt …', + successTitle: 'Vertrag erstellt!', + successText: + 'Die Tiere wurden als „Abgegeben“ markiert (Abnehmer und Abgabedatum gesetzt).', + downloadDocx: 'Vertrag herunterladen (.docx)', + toList: 'Zur Vertragsliste', + anotherOne: 'Weiteren Vertrag erstellen', + }, + }, + // ── FEAT-13 (Kelly): Einstellungen (Zuchtprofil) ── + einstellungen: { + title: 'Einstellungen', + zuchtprofil: { + title: 'Zuchtprofil', + intro: + 'Diese Angaben erscheinen als Verkäufer-Block in jedem Abgabevertrag.', + incompleteHint: + 'Noch unvollständig: Name, Adresse und Ort werden für den Vertrag benötigt.', + fields: { + zuchtName: 'Zuchtname', + name: 'Vor- und Nachname', + nameHint: 'Mit Anrede, z. B. „Frau Erika Muster“.', + address: 'Adresse', + addressHint: 'Straße Nr, PLZ Ort.', + phone: 'Telefon', + email: 'E-Mail', + homepage: 'Homepage', + city: 'Ort (Unterschriftszeile)', + }, + save: 'Speichern', + saving: 'Speichern …', + saved: 'Gespeichert.', + }, + }, + // ── FEAT-2 (Oscar): Kontakte (Contacts — Herkunft/Abnehmer) ── + kontakte: { + title: 'Kontakte', + newButton: 'Neuer Kontakt', + empty: 'Keine Kontakte gefunden.', + countLabel: 'Kontakte', + searchPlaceholder: 'Name suchen …', + // FEAT-13: contactInfo (Freitext) wurde durch strukturierte Felder ersetzt + // (DATA-2-Schema: email/phone/address) — gebraucht für den Käufer-Block der Verträge. + fields: { + name: 'Name', + email: 'E-Mail', + phone: 'Telefon', + address: 'Adresse', + notes: 'Notizen', + }, + linked: { + title: 'Verknüpfte Tiere', + empty: 'Diesem Kontakt sind keine Tiere zugeordnet.', + originRole: 'Herkunft von', + receiverRole: 'Abnehmer von', + }, + detail: { + edit: 'Bearbeiten', + back: 'Zurück zur Liste', + notFound: 'Dieser Kontakt wurde nicht gefunden.', + }, + form: { + createTitle: 'Neuen Kontakt anlegen', + editTitle: 'Kontakt bearbeiten', + addressHint: 'Straße Nr, PLZ Ort — so erscheint sie im Abgabevertrag.', + save: 'Speichern', + cancel: 'Abbrechen', + saving: 'Speichern …', + }, + validation: { + nameRequired: 'Bitte einen Namen eingeben.', + }, + delete: { + action: 'Löschen', + confirmMessage: 'Soll dieser Kontakt wirklich gelöscht werden?', + conflict: + 'Kontakt ist noch mit Tieren verknüpft (Herkunft/Abnehmer) und kann nicht gelöscht werden.', + }, + }, + // ── FEAT-6 (Oscar): Tier-Detail-Tabs (Gesundheit / Gewicht / Fotos) ── + tierTabs: { + actions: { + save: 'Speichern', + saving: 'Speichern …', + cancel: 'Abbrechen', + edit: 'Bearbeiten', + delete: 'Löschen', + }, + health: { + empty: 'Noch keine Gesundheitseinträge.', + newButton: 'Neuer Eintrag', + createTitle: 'Neuer Gesundheitseintrag', + editTitle: 'Gesundheitseintrag bearbeiten', + fields: { + date: 'Datum', + type: 'Art', + description: 'Beschreibung', + veterinarian: 'Tierarzt', + }, + typeLabels: { + Examination: 'Untersuchung', + Treatment: 'Behandlung', + Injury: 'Verletzung', + Vaccination: 'Impfung', + Other: 'Sonstiges', + }, + validation: { + dateRequired: 'Bitte ein Datum angeben.', + descriptionRequired: 'Bitte eine Beschreibung eingeben.', + }, + deleteConfirm: 'Diesen Gesundheitseintrag wirklich löschen?', + }, + weight: { + addTitle: 'Gewicht erfassen', + fields: { + date: 'Datum', + weightGrams: 'Gewicht (g)', + }, + addButton: 'Hinzufügen', + adding: 'Speichern …', + historyTitle: 'Verlauf', + chartTitle: 'Gewichtsverlauf', + chartNeedsTwo: 'Für die Verlaufskurve sind mindestens zwei Einträge nötig.', + empty: 'Noch keine Gewichtseinträge.', + gramsSuffix: 'g', + validation: { + dateRequired: 'Bitte ein Datum angeben.', + weightRequired: 'Bitte ein Gewicht in Gramm eingeben.', + weightRange: 'Bitte ein plausibles Gewicht zwischen 1 und 500 g eingeben.', + }, + deleteConfirm: 'Diesen Gewichtseintrag wirklich löschen?', + }, + photos: { + empty: 'Noch keine Fotos.', + uploadTitle: 'Foto hochladen', + chooseFile: 'Foto auswählen', + captionLabel: 'Bildunterschrift (optional)', + uploadButton: 'Hochladen', + uploading: 'Hochladen …', + profileBadge: 'Profilfoto', + deleteConfirm: 'Dieses Foto wirklich löschen?', + validation: { + fileRequired: 'Bitte zuerst ein Foto auswählen.', + }, + }, + }, + // ── EXPORT-1 (Oscar): Datenexport (Karte auf /einstellungen) ── + datenexport: { + title: 'Datenexport', + intro: + 'Sicherung deiner Daten — alle Tiere, Würfe und Kontakte als Tabellen (CSV für Excel) plus eine vollständige Datensicherung (JSON), gebündelt als Zip.', + button: 'Export herunterladen', + photoNote: + 'Fotos sind nicht enthalten — sie liegen als Bilddateien im Datenordner der Anwendung und können von dort gesichert werden.', + }, + }, + // ── HELP-1: In-App-Anleitung ── + hilfe: { + title: 'Hilfe & Anleitung', + subtitle: 'Kurzanleitung für den Rennmaus-Manager', + sections: { + ersteSchritte: 'Erste Schritte', + tiere: 'Rennmäuse anlegen & Fotos', + becken: 'Becken', + wuerfe: 'Würfe & Jungtiere', + genetik: 'Probeverpaarung & Genetik', + stammbaum: 'Stammbaum', + abgabe: 'Abgabe-Ablauf', + // FEAT-13-Followup (Kelly): Vertrag + Zuchtprofil + vertraege: 'Abgabevertrag erstellen', + einstellungen: 'Zuchtprofil (Einstellungen)', + statistik: 'Statistik', + datensicherung: 'Datensicherung', + }, + }, + api: { + errors: { + network: 'Verbindung zum Server fehlgeschlagen. Bitte später erneut versuchen.', + notFound: 'Der Eintrag wurde nicht gefunden.', + server: 'Auf dem Server ist ein Fehler aufgetreten.', + unknown: 'Ein unbekannter Fehler ist aufgetreten.', + }, + }, + common: { + loading: 'Lädt …', + retry: 'Erneut versuchen', + previous: 'Zurück', + next: 'Weiter', + page: 'Seite', + of: 'von', + yes: 'Ja', + no: 'Nein', + delete: 'Löschen', + confirmDelete: 'Wirklich löschen?', + pageNav: 'Seitennavigation', + }, + // Genetik-Warnungen: der Engine (src/genetics) liefert nur CODES, + // die deutschen Texte stehen hier. Schlüssel = GeneticsWarningCode-Werte. + genetics: { + warnings: { + SCHECKE_LETHAL: + 'Schecke × Schecke: Reinerbige Tiere (SpSp) sterben bereits im Mutterleib — etwa ein Viertel weniger Jungtiere.', + REX_SEMI_LETHAL: + 'Rex × Rex: Reinerbige Tiere (ReRe) sind nur eingeschränkt lebensfähig.', + SLS_LETHAL: + 'WP × WP: Reinerbige Tiere (S(l)S(l)) sterben bereits im Mutterleib (Rumpback) — weniger Jungtiere.', + SUPERSCHECKE_DEAF: + 'Schecke × WP: Superschecken (sehr hoher Weißanteil) sind möglich — erhöhtes Taubheitsrisiko.', + }, + unknownFarbschlag: 'Unbekannter Farbschlag', + }, + // ── FEAT-14 (Kevin): Charakterbogen — Eigenschaften + Notiz, speist den KI-Verkaufstext ── + // Traits: stabile KEYS (gespeichert) + deutsche LABELS (UI + KI-Prompt). + // Erweitern = eine Zeile in der Liste; Julian verfeinert die Auswahl. + character: { + sectionTitle: 'Charakter & Eigenschaften', + noteLabel: 'Notizen zum Charakter', + notePlaceholder: 'Ein paar Sätze zum Charakter dieses Tieres …', + save: 'Charakter speichern', + saved: 'Charakter gespeichert.', + none: 'Noch keine Eigenschaften ausgewählt.', + traits: [ + { key: 'zutraulich', label: 'zutraulich' }, + { key: 'handzahm', label: 'handzahm' }, + { key: 'neugierig', label: 'neugierig' }, + { key: 'aufgeschlossen', label: 'aufgeschlossen' }, + { key: 'ruhig', label: 'ruhig / ausgeglichen' }, + { key: 'lebhaft', label: 'lebhaft / aktiv' }, + { key: 'verschmust', label: 'verschmust' }, + { key: 'eigenstaendig', label: 'eigenständig' }, + { key: 'anfaengergeeignet', label: 'anfängergeeignet' }, + { key: 'futterfreudig', label: 'futterfreudig' }, + { key: 'buddelt', label: 'buddelt gern' }, + { key: 'klettert', label: 'klettert gern' }, + { key: 'laufrad', label: 'läuft gern im Laufrad' }, + { key: 'vertraeglich', label: 'gut verträglich' }, + { key: 'schreckhaft', label: 'schreckhaft' }, + ], + }, +} as const + +export type Strings = typeof de