refactor(i18n): a description is one sentence, in all six languages

Measured before the sweep: 317 description and hint strings under the
`settings` and `admin` namespaces, median 83 characters, 113 of them two
sentences or more. The rule the design standards now carry is one sentence,
guideline 120 characters, and this is the sweep that makes it true.

Almost nothing was retranslated. A second sentence is already written in
every language — it is just in the wrong slot. `scripts/split-description.mjs`
cuts a description at its first sentence boundary in all six bundles at once
and either drops the tail or files it under its own key; the twenty-one keys
that moved now render in the card body as foreground `text-sm`, which is
where multi-sentence prose belongs and where a reader can actually read it
(the slot is muted `text-xs`). Eight tails were dropped outright, because
"Off by default" under a switch, "Topics and frequency are adjustable" above
the topic switches, and "Only admins can create invitations" on an
admin-gated route are not information.

Twenty-eight keys are gone: the privacy page's intro, which was a table of
contents for the four cards under it; two About descriptions that listed
verbatim the labels and the buttons beneath them; a card description that
repeated its own title; the API-token sentence that said what the paragraph
below it said with the actual instruction; and the eight integration
secondary sentences. Six descriptions were rewritten shorter in all six
languages where trimming was honest rather than lossy.

On the strings that changed: 4391 characters of English down to 2290, and 32
multi-sentence descriptions down to zero.

The rule is now a gate. `card-description-length.test.ts` reads the keys that
actually reach a card header's description slot — statically, from the JSX —
and checks them in EN and DE, because a rule that only holds in English
produces a slot that wraps to two lines in half the locales. Its exception
list is empty and carries a reason requirement plus a test that removes a
stale entry. Verified by putting a second sentence back: it fails.
This commit is contained in:
Marc-André Bombeck committed 2026-08-07 20:53:05 +02:00
1 parent 330f60115a
commit ba0cd63cf5
33 files changed
+779 -392

No files matched your search

+74 -61
View File
@@ -3925,12 +3925,13 @@
},
"pillOrder": {
"title": "Navigations-Pills sortieren",
"description": "Ziehe die Pills in die gewünschte Reihenfolge und blende sie über das Augensymbol ein oder aus. Ausgeblendete Detailseiten verschwinden auch aus der oberen Navigation.",
"description": "Ziehe die Pills in die gewünschte Reihenfolge und blende sie über das Augensymbol ein oder aus.",
"dragHandle": "Verschieben",
"saveSuccess": "Reihenfolge der Pills gespeichert.",
"saveError": "Speichern der Pill-Reihenfolge fehlgeschlagen.",
"save": "Speichern",
"empty": "Noch keine Navigations-Pills verfügbar."
"empty": "Noch keine Navigations-Pills verfügbar.",
"detail": "Ausgeblendete Detailseiten verschwinden auch aus der oberen Navigation."
},
"customize": "Insights anpassen",
"regenerateRateLimited": "Aktualisierungslimit erreicht — bitte später erneut versuchen.",
@@ -4412,7 +4413,7 @@
"settings": {
"researchMode": {
"sectionTitle": "Forschungsmodus",
"subtitle": "Optionale Anzeige einer geschätzten GLP-1-Wirkstoffkurve auf Basis der von der EMA veröffentlichten Populations-Pharmakokinetik. Nur zur Anschauung — niemals eine Messung oder ein medizinischer Rat.",
"subtitle": "Optionale Kurve des geschätzten GLP-1-Wirkstoffspiegels nach EMA-Daten.",
"toggleLabel": "Geschätzte Wirkstoffkurve anzeigen",
"disabledStatus": "Deaktiviert. Die Wirkstoffkurve ist ausgeblendet.",
"enabledStaleStatus": "Der Disclaimer wurde aktualisiert. Bestätige unten erneut, um die Kurve wieder anzuzeigen.",
@@ -4420,7 +4421,8 @@
"rePromptTitle": "Disclaimer aktualisiert",
"rePromptBody": "Der Disclaimer des Forschungsmodus wurde seit deiner letzten Bestätigung aktualisiert. Lies ihn erneut und bestätige, damit die Kurve sichtbar bleibt.",
"rePromptCta": "Disclaimer erneut bestätigen",
"disableError": "Forschungsmodus konnte nicht deaktiviert werden. Bitte erneut versuchen."
"disableError": "Forschungsmodus konnte nicht deaktiviert werden. Bitte erneut versuchen.",
"subtitleDetail": "Nur zur Anschauung — niemals eine Messung oder ein medizinischer Rat."
},
"shell": {
"sectionsNav": "Einstellungs-Bereiche",
@@ -4547,7 +4549,8 @@
},
"unavailableTitle": "In diesem geteilten Datensatz nicht verfügbar",
"unavailableDescription": "Dieser Einstellungsbereich ist beim Anzeigen des Datensatzes einer anderen Person bewusst nicht verfügbar.",
"integrationStatusDescription": "Verbindungsstatus für diesen verwalteten Datensatz. Verbindungssteuerungen sind nicht verfügbar."
"integrationStatusDescription": "Verbindungsstatus für diesen verwalteten Datensatz.",
"integrationStatusDetail": "Verbindungssteuerungen sind nicht verfügbar."
},
"sections": {
"account": {
@@ -4622,7 +4625,7 @@
"integrationsHint": "Neue Datenquellen verbindest oder trennst du auf der Integrationen-Seite.",
"integrationsHintLink": "Integrationen öffnen",
"cardTitle": "Quellen-Priorität pro Metrik",
"help": "Liefern mehrere Quellen denselben Wert am selben Tag, zählt die oberste Quelle als kanonisch. Die übrigen bleiben für die Audit-Spur gespeichert.",
"help": "Liefern mehrere Quellen denselben Wert am selben Tag, zählt die oberste Quelle als kanonisch.",
"resetDefaults": "Auf Standard zurücksetzen",
"resetDefaultsTitle": "Quellen-Reihenfolge zurücksetzen?",
"resetDefaultsBody": "Jede von dir angeordnete Rangfolge kehrt zur Standardreihenfolge zurück, für alle Messgrößen und Gerätetypen auf einmal. Das wird sofort gespeichert und lässt sich nicht wiederherstellen.",
@@ -4682,13 +4685,15 @@
"subtitle": "Lege fest, welche Quelle gewinnt, wenn zwei dieselbe Messgröße liefern.",
"nutrients": {
"title": "Synchronisierte Nährstoffzufuhr",
"description": "Tagessummen aus Apple Health der letzten {days} Tage. Nur lesend — die Quell-App besitzt die Werte.",
"description": "Tagessummen aus Apple Health der letzten {days} Tage.",
"empty": "Noch keine Nährstoffdaten synchronisiert.",
"latest": "{amount} {unit} am {date}",
"daysWithData": "{count} von {days} Tagen",
"moduleHint": "Die Synchronisierung steuert das Modul „Nährstoffzufuhr“.",
"moduleHintLink": "Module verwalten"
}
"moduleHintLink": "Module verwalten",
"detail": "Nur lesend — die Quell-App besitzt die Werte."
},
"helpDetail": "Die übrigen bleiben für die Audit-Spur gespeichert."
},
"ai": {
"title": "KI-Anbieter",
@@ -4938,7 +4943,7 @@
"error": "Konnte nicht gespeichert werden. Bitte erneut versuchen.",
"toggleable": {
"title": "Was du trackst",
"description": "Optionale Bereiche. Deaktivierte Module verschwinden aus Navigation, Kacheln und Auswertungen — deine Daten bleiben erhalten."
"description": "Deaktivierte Module verschwinden aus Navigation, Kacheln und Auswertungen — deine Daten bleiben erhalten."
},
"manageIn": "In {section} verwalten",
"operatorDisabled": "Serverweit deaktiviert",
@@ -5326,7 +5331,7 @@
"testReasonUnreachable": "Der KI-Anbieter war nicht erreichbar.",
"coachMemory": {
"title": "Woran sich der Coach erinnert",
"description": "Dauerhafte Angaben, die du dem Coach mitgeteilt hast. Er nutzt sie, damit seine Antworten relevant bleiben. Entferne alles, was er besser vergessen sollte.",
"description": "Dauerhafte Angaben, die du dem Coach mitgeteilt hast.",
"empty": "Der Coach hat noch keine Angaben gespeichert. Im Gespräch merkt er sich dauerhafte Details, die du teilst – deine Ziele, Beschwerden, Vorlieben – und du kannst sie hier jederzeit löschen.",
"learnedPrefix": "gelernt {when}",
"categoryPreference": "Vorlieben",
@@ -5349,11 +5354,12 @@
"forgotToast": "Angabe vergessen.",
"forgotError": "Konnte diese Angabe nicht vergessen. Bitte erneut versuchen.",
"clearedToast": "{count} gespeicherte Angaben vergessen.",
"clearError": "Konnte die gespeicherten Angaben nicht löschen. Bitte erneut versuchen."
"clearError": "Konnte die gespeicherten Angaben nicht löschen. Bitte erneut versuchen.",
"detail": "Er nutzt sie, damit seine Antworten relevant bleiben. Entferne alles, was er besser vergessen sollte."
},
"coachReminders": {
"title": "Deine Erinnerungen",
"description": "Dinge, um die du den Coach gebeten hast, später darauf zurückzukommen. Fällige erscheinen hier und beim Coach.",
"description": "Dinge, um die du den Coach gebeten hast, später darauf zurückzukommen.",
"empty": "Noch keine Erinnerungen. Bitte den Coach, dich an etwas zu erinnern — dann erscheint es hier.",
"loadError": "Konnte deine Erinnerungen nicht laden.",
"dueHeading": "Jetzt fällig",
@@ -5374,7 +5380,8 @@
},
"deleteConfirmTitle": "Diese Erinnerung löschen?",
"deleteConfirmBody": "Sie wird nicht mehr ausgelöst. An bereits erfassten Daten ändert sich nichts.",
"deleteConfirmAction": "Löschen"
"deleteConfirmAction": "Löschen",
"detail": "Fällige erscheinen hier und beim Coach."
},
"aboutMe": {
"title": "Über mich",
@@ -5647,7 +5654,7 @@
"workouts": "Workouts"
},
"integrationFallback": {
"description": "Für dieses Konto eingerichtet, hat aber keine eigene Einstellungskarte. Diese Zeile sorgt dafür, dass eine Verbindung nicht unbemerkt ausfallen kann.",
"description": "Für dieses Konto eingerichtet, hat aber keine eigene Einstellungskarte.",
"lastAttempt": "Letzter Versuch {relative}",
"noAttempt": "Kein Sync-Versuch aufgezeichnet"
},
@@ -5656,7 +5663,7 @@
"fitbitViewData": "Schlaf ansehen",
"googleHealthViewData": "Schritte ansehen",
"apiTokens": "API-Tokens",
"apiTokensDescription": "API-Tokens für die externe Medikamenten-Einnahme prüfen und widerrufen. Tokens werden pro Medikament über dessen API-Endpoint-Schalter ausgestellt.",
"apiTokensDescription": "API-Tokens für die externe Medikamenten-Einnahme prüfen und widerrufen.",
"tokenMintMovedDescription": "Tokens werden pro Medikament ausgestellt. Öffne ein Medikament und aktiviere dessen API-Endpoint, um ein Token zu erhalten, das nur für dieses Medikament gilt.",
"tokenNamePlaceholder": "Token-Name (z.B. iPhone Shortcut)",
"tokenCreated": "Token erstellt — kopiere ihn jetzt! Er wird nicht erneut angezeigt.",
@@ -5687,14 +5694,14 @@
"apiEndpointAuth": "Authentifizierung",
"apiEndpointExample": "Body-Beispiel",
"dangerZone": "Alle Daten löschen",
"dangerZoneDescription": "Löscht deine gesamte Gesundheitsakte und jede Integrationsverbindung. Dein Konto und deine Anmeldedaten bleiben erhalten.",
"dangerZoneDescription": "Löscht deine gesamte Gesundheitsakte und jede Integrationsverbindung.",
"dangerZoneConfirm": "Alle Daten löschen?",
"dangerZoneConfirmDescription": "Dieser Vorgang löscht unwiderruflich alles in deiner Akte: Messwerte, Medikamente, Stimmungseinträge, Laborwerte, Zykluseinträge, Dokumente, KI-Gespräche und jede Integrationsverbindung, dazu alles daraus Abgeleitete und alles, was du früher gelöscht hast. Freigabe-Links, die du an eine Praxis gegeben hast, funktionieren sofort nicht mehr. Dein Konto bleibt bestehen, ebenso deine Anmeldedaten und Anzeigeeinstellungen wie Sprache, Zeitzone und Einheiten. Das lässt sich nicht rückgängig machen.",
"dangerZoneSuccess": "Deine Gesundheitsakte wurde gelöscht. Dein Konto und deine Anmeldung bleiben unverändert.",
"dangerZoneDeleteFailed": "Löschen fehlgeschlagen",
"finalDelete": "Endgültig löschen",
"deleteAccountCardTitle": "Konto vollständig löschen",
"deleteAccountCardDescription": "Löscht dein Konto inklusive Passkeys, Audit-Log und Sessions. Dieser Vorgang kann nicht rückgängig gemacht werden.",
"deleteAccountCardDescription": "Löscht dein Konto inklusive Passkeys, Audit-Log und Sessions.",
"deleteAccountCta": "Konto löschen",
"deleteAccountConfirmTitle": "Konto endgültig löschen?",
"deleteAccountConfirmDescription": "Dein Konto, alle Gesundheitsdaten, Passkeys, Sessions und Audit-Einträge werden unwiderruflich gelöscht. Du wirst danach automatisch abgemeldet.",
@@ -5712,8 +5719,6 @@
"changelog": "Changelog",
"docs": "Dokumentation",
"linksHeading": "Quellen & Dokumentation",
"identityDescription": "Version, Lizenz und Build-Details dieser Instanz.",
"linksDescription": "Quellcode, Änderungsprotokoll und Dokumentation.",
"newerAvailable": "Neue Version: {tag}"
},
"testConnection": {
@@ -5741,7 +5746,7 @@
"notificationStatus": {
"title": "Zustellstatus",
"description": "Pro Kanal aktueller Status; \"Automatisch deaktiviert\" stoppt nach dauerhaften Fehlern.",
"emptyDescription": "Noch keine Kanäle konfiguriert. Richte unter Zustellkanäle einen Kanal ein, um die Zustellung zu verfolgen.",
"emptyDescription": "Richte unter Einstellungen › Integrationen › Zustellkanäle einen Kanal ein.",
"stateActive": "Aktiv",
"stateAutoDisabled": "Automatisch deaktiviert",
"stateSendingPaused": "Versand pausiert",
@@ -5752,7 +5757,8 @@
"disabledReason": "Grund",
"nextRetry": "Nächster Versuch",
"reEnable": "Wieder aktivieren",
"sendTest": "Test senden"
"sendTest": "Test senden",
"emptyTitle": "Noch keine Kanäle konfiguriert"
},
"identity": {
"description": "Optionale Angaben für die Gesundheitsakte (PDF-Deckblatt + FHIR-Export). Alle Felder sind freiwillig.",
@@ -5765,7 +5771,7 @@
},
"healthRecord": {
"title": "Gesundheitsakte exportieren",
"description": "Erstelle eine vollständige Gesundheitsakte für die ärztliche Vorlage — als PDF, strukturiert (FHIR) oder als kombiniertes Paket.",
"description": "Eine vollständige Akte für die ärztliche Vorlage — als PDF, als FHIR oder beides.",
"format": "Format",
"format_pdf": "PDF",
"format_fhir": "Strukturiert (FHIR)",
@@ -5790,10 +5796,10 @@
"errorModuleDisabled": "Der Gesundheitsakten-Export ist für dieses Konto ausgeschaltet."
},
"globalExcludedInjectionSitesLabel": "Global ausgeschlossene Injektionsstellen",
"globalExcludedInjectionSitesHint": "Hier gelistete Stellen werden für keine Medikation angeboten und beim Erfassen einer Dosis abgelehnt — auch wenn eine Medikation sie bevorzugt.",
"globalExcludedInjectionSitesHint": "Diese Stellen werden nie angeboten und beim Erfassen einer Dosis abgelehnt.",
"sharing": {
"createTitle": "Neuer Freigabe-Link",
"createDescription": "Der Link ist schreibgeschützt und zeitlich begrenzt. Der Token wird nur einmal bei der Erstellung angezeigt — sichere ihn dann; er lässt sich nicht wiederherstellen.",
"createDescription": "Der Link ist schreibgeschützt und zeitlich begrenzt.",
"label": "Bezeichnung",
"labelPlaceholder": "z. B. Dr. Schmidt — Jahres-Check-up",
"range": "Tage Verlauf",
@@ -5843,15 +5849,17 @@
"scopeInsuranceExcluded": "Versichertendaten werden über einen Link nie geteilt.",
"statusNeedsReselect": "Inhalt neu wählen",
"reselectReason": "Dieser Link lieferte einen Umfang, den niemand gewählt hatte, und wurde deshalb geschlossen. Legen Sie einen neuen an und wählen Sie den Inhalt.",
"reselectAction": "Ersatz anlegen"
"reselectAction": "Ersatz anlegen",
"createDetail": "Der Token wird nur einmal bei der Erstellung angezeigt — sichere ihn dann; er lässt sich nicht wiederherstellen."
},
"genderOther": "Divers",
"cycleTracking": {
"title": "Zyklus-Tracking",
"description": "Verfolge deinen Menstruationszyklus, Vorhersagen, Symptome und Phasen-Insights. Standardmäßig an für als weiblich eingestellte Konten; für alle verfügbar.",
"description": "Verfolge deinen Menstruationszyklus, Vorhersagen, Symptome und Phasen-Insights.",
"enable": "Zyklus-Tracking aktivieren",
"manage": "Zyklus-Tracking öffnen",
"error": "Zyklus-Tracking konnte nicht aktualisiert werden."
"error": "Zyklus-Tracking konnte nicht aktualisiert werden.",
"detail": "Standardmäßig an für als weiblich eingestellte Konten; für alle verfügbar."
},
"nightscout": "Nightscout",
"nightscoutTag": "CGM",
@@ -5937,7 +5945,7 @@
"coach": {
"activate": {
"title": "Coach aktivieren",
"description": "Zeigt den Coach-Button und das Coach-Panel. Standardmäßig an — ausschalten, um den Coach überall auszublenden.",
"description": "Zeigt den Coach-Button und das Coach-Panel.",
"toggleAria": "Coach-Button und Coach-Panel aktivieren",
"savedOn": "Coach ist aktiviert.",
"savedOff": "Coach ist ausgeblendet.",
@@ -6057,19 +6065,13 @@
"signOutEverywhereConfirmBody": "Jedes Gerät außer diesem muss sich neu anmelden. Dieses Gerät bleibt angemeldet. An deinen erfassten Daten ändert sich nichts."
},
"privacy": {
"intro": {
"title": "Deine Daten bleiben deine",
"body": "HealthLog wird selbst gehostet. Diese Seite fasst zusammen, wie deine Daten geschützt werden, wie lange sie gespeichert bleiben, wie du sie exportierst und wie du sie löschst."
},
"encryption": {
"title": "Verschlüsselt gespeichert",
"description": "Sensible Felder werden in der Datenbank verschlüsselt.",
"summary": "Deine sensiblen Daten werden mit {algorithm} verschlüsselt gespeichert – über {columns} Felder in {models} Datensatztypen.",
"statement": "Deine sensiblen Daten werden mit AES-256-GCM verschlüsselt gespeichert."
},
"stored": {
"title": "Was gespeichert wird",
"description": "Die Datenkategorien, die dieses Konto enthält.",
"measurements": "Messwerte (Gewicht, Blutdruck, Puls, Glukose, Schlaf, Körperzusammensetzung)",
"medications": "Medikamente und Einnahmeverlauf",
"moodLabs": "Stimmungseinträge und Laborwerte",
@@ -6087,7 +6089,7 @@
},
"export": {
"title": "Meine Daten herunterladen",
"description": "Exportiere deine Datensätze als CSV oder JSON, optional passphrasen-verschlüsselt, oder erstelle eine vollständige Gesundheitsakte.",
"description": "Deine Datensätze als CSV oder JSON, wahlweise mit Passphrase verschlüsselt.",
"openExport": "Export & Backup",
"openHealthRecord": "Gesundheitsakte"
},
@@ -6106,26 +6108,28 @@
},
"mcp": {
"enableTitle": "Remote-MCP-Endpunkt",
"enableDescription": "Erlaube einem MCP-fähigen Assistenten (Claude.ai, ChatGPT), deine Gesundheitsdaten über ein begrenztes Token zu lesen. Standardmäßig aus.",
"enableDescription": "Erlaube einem MCP-fähigen Assistenten (Claude.ai, ChatGPT), deine Gesundheitsdaten über ein begrenztes Token zu lesen.",
"enabled": "Aktiv",
"disabled": "Inaktiv",
"enableToggleLabel": "MCP-Endpunkt aktivieren",
"endpointLabel": "Connector-URL",
"connectDescription": "Füge diese URL in die Einrichtung des eigenen Connectors deines Assistenten ein und autorisiere dich mit dem Token unten oder melde dich bei Aufforderung an.",
"tokensTitle": "Connector-Tokens",
"tokensDescription": "Erzeuge ein schreibgeschütztes Token, um einen Assistenten zu verbinden. Das Token wird nur einmal angezeigt.",
"tokensDescription": "Erzeuge ein schreibgeschütztes Token, um einen Assistenten zu verbinden.",
"scopeNote": "Tokens sind auf health:read beschränkt — nur Lesezugriff auf deine eigenen Daten. Sie können niemals Admin-Funktionen erreichen.",
"writeScopeToggleLabel": "Eintragen erlauben (health:write)",
"writeScopeNote": "Fügt den Scope health:write hinzu, damit der Assistent neue Messwerte und Stimmungseinträge in dein Konto eintragen kann. Jeder Schreibvorgang wird zuvor bestätigt. Das Token kann weiterhin keine vorhandenen Einträge löschen oder ändern, keine Medikamente bearbeiten und keine REST- oder Admin-Funktionen erreichen. Für reinen Lesezugriff ausgeschaltet lassen.",
"connectionsTitle": "Verbundene Assistenten",
"connectionsDescription": "Assistenten, die du über die Anmeldung autorisiert hast. Das Widerrufen einer Verbindung beendet den Zugriff sofort, einschließlich der Möglichkeit zur Erneuerung.",
"connectionsDescription": "Assistenten, die du über die Anmeldung autorisiert hast.",
"noConnections": "Keine verbundenen Assistenten.",
"connectionConnected": "Verbunden",
"connectionLastUsed": "Zuletzt verwendet",
"connectionNeverUsed": "Nie verwendet",
"connectionRevoke": "Trennen",
"connectionRevokeDescription": "Dies beendet den Zugriff des Assistenten sofort und verhindert eine Erneuerung. Für eine erneute Verbindung ist eine neue Autorisierung nötig.",
"connectionRevokeFailed": "Assistent konnte nicht getrennt werden."
"connectionRevokeFailed": "Assistent konnte nicht getrennt werden.",
"tokensDetail": "Das Token wird nur einmal angezeigt.",
"connectionsDetail": "Das Widerrufen einer Verbindung beendet den Zugriff sofort, einschließlich der Möglichkeit zur Erneuerung."
},
"strava": "Strava",
"stravaDescription": "Verbinde dein Strava-Konto, um deine Aktivitäten in deinen Trainingsverlauf zu übernehmen.",
@@ -6194,7 +6198,9 @@
"explainer": "Aus werden Werte gegen das allgemeine nicht-diabetische Referenzband bewertet. An werden sie gegen das engere glykämische Zielband der ADA Standards of Care bewertet, und der Coach begründet seine Glukose-Aussagen auf diesem Band. Deine eigenen Zielwert-Überschreibungen unten haben weiterhin Vorrang.",
"disclaimer": "Das wählt ein Referenzband aus. Es ist keine Diagnose, wird nie aus einem Messwert abgeleitet, und die App handelt klinisch nicht danach. Welche Zielwerte für dich gelten, klärst du mit deiner Ärztin oder deinem Arzt.",
"error": "Die Einstellung konnte nicht gespeichert werden."
}
},
"dangerZoneDetail": "Dein Konto und deine Anmeldedaten bleiben erhalten.",
"deleteAccountCardDetail": "Dieser Vorgang kann nicht rückgängig gemacht werden."
},
"admin": {
"title": "Administration",
@@ -6225,7 +6231,7 @@
},
"aiServerKey": {
"title": "KI-Server-Schlüssel",
"description": "Ein betreiberweiter Provider-Schlüssel, auf den jedes Konto zurückfallen kann, wenn kein eigener Schlüssel hinterlegt ist.",
"description": "Ein Betreiber-Schlüssel für Konten, die keinen eigenen hinterlegt haben.",
"byokHint": "Der eigene Schlüssel (BYOK, ChatGPT-Login oder lokaler Provider) hat immer Vorrang — der Server-Schlüssel ist die letzte Stufe der Provider-Kette.",
"consentHint": "Anfragen über den Server-Schlüssel erfordern die aktive KI-Einwilligung der Person; ohne Einwilligungsbeleg wird nichts gesendet.",
"statusConfigured": "Eingerichtet",
@@ -6331,7 +6337,7 @@
"monitoringTestFailed": "Monitoring-Test fehlgeschlagen",
"assistant": {
"title": "Assistenz-Oberflächen",
"description": "Jeder Toggle versteckt eine modellgestützte Oberfläche app-weit, ohne die Provider-Konfiguration zu entfernen. Der Hauptschalter erzwingt jede Unter-Option aus.",
"description": "Jeder Toggle versteckt eine modellgestützte Oberfläche app-weit, ohne die Provider-Konfiguration zu entfernen.",
"master": {
"title": "Assistent aktiv",
"description": "Hauptschalter für jede modellgestützte Oberfläche. Wenn aus, sind alle Unter-Optionen unten zwangsweise aus."
@@ -6355,12 +6361,13 @@
},
"modules": {
"title": "Modulverfügbarkeit",
"description": "Schalte ein Modul für den gesamten Server ab. Ein hier deaktiviertes Modul ist für jedes Konto aus – unabhängig von den persönlichen Einstellungen. Kerndaten (Gewicht, Blutdruck, Puls, Medikamente) sind immer aktiv und lassen sich nicht abschalten.",
"saveError": "Modulverfügbarkeit konnte nicht aktualisiert werden."
"description": "Schalte ein Modul für den gesamten Server ab.",
"saveError": "Modulverfügbarkeit konnte nicht aktualisiert werden.",
"detail": "Ein hier deaktiviertes Modul ist für jedes Konto aus – unabhängig von den persönlichen Einstellungen. Kerndaten (Gewicht, Blutdruck, Puls, Medikamente) sind immer aktiv und lassen sich nicht abschalten."
},
"invites": {
"title": "Einladungen",
"description": "Einladungslinks für die Registrierung — funktionieren auch bei geschlossener Registrierung. Der Link wird nur einmal angezeigt.",
"description": "Einladungslinks für die Registrierung — funktionieren auch bei geschlossener Registrierung.",
"create": "Einladung erstellen",
"createTitle": "Einladung erstellen",
"createDescription": "Der Link erlaubt eine Registrierung auch bei geschlossener Registrierung.",
@@ -6466,7 +6473,7 @@
"outcomeFailed": "Fehlgeschlagen",
"auditDetails": "Details",
"deleteAllData": "Alle Daten global löschen",
"deleteAllDescription": "Löscht alle Mess-, Medikamenten-, Integrations- und Protokoll-Daten aller Nutzer. Nutzerkonten bleiben bestehen.",
"deleteAllDescription": "Löscht alle Mess-, Medikamenten-, Integrations- und Protokoll-Daten aller Nutzer.",
"deleteAllConfirm": "Alle Daten wirklich löschen?",
"deleteAllConfirmDescription": "Dieser Vorgang löscht alle Messwerte, Medikamente, Einnahmen, API-Tokens, Withings-Verbindungen, offene Authentifizierungs-Challenges, Benachrichtigungskanäle (Telegram, ntfy, Web Push), Push-Abonnements und in der Warteschlange befindliche Telegram-Nachrichtenlöschungen aller Nutzer unwiderruflich. Nutzerkonten, das Audit-Log und eingereichtes Feedback bleiben erhalten, damit der Löschvorgang nachvollziehbar bleibt und Bug-Reports weiter triagierbar sind.",
"deleteButton": "Alle Daten löschen",
@@ -6611,7 +6618,7 @@
"app-logs": {
"title": "App-Logs",
"subtitle": "Letzte strukturierte Wide-Events dieses Prozesses anzeigen.",
"processNote": "Es werden nur App-Prozess-Logs angezeigt — Worker-Logs landen in Loki, sofern die Integration konfiguriert ist. Der Puffer ist flüchtig und auf 500 Einträge begrenzt.",
"processNote": "Es werden nur App-Prozess-Logs angezeigt — Worker-Logs landen in Loki, sofern die Integration konfiguriert ist.",
"filterTraceId": "Trace-ID",
"filterAction": "Aktion enthält…",
"filterLevel": "Level",
@@ -6632,7 +6639,8 @@
"emptyDescription": "Es passen noch keine Events zu den Filtern. Nach etwas Traffic auf Aktualisieren tippen.",
"refresh": "Aktualisieren",
"eventDetails": "Event-Details",
"closeDetails": "Details schließen"
"closeDetails": "Details schließen",
"processDetail": "Der Puffer ist flüchtig und auf 500 Einträge begrenzt."
},
"backups": {
"title": "Backups",
@@ -6699,7 +6707,7 @@
"loading": "Verschlüsselungsstatus wird geladen…",
"loadError": "Verschlüsselungsstatus konnte nicht geladen werden.",
"coverageTitle": "Abdeckung",
"coverageDescription": "Wie viele verschlüsselte Zeilen unter welcher Schlüssel-ID liegen. Es werden nur Schlüssel-IDs und Anzahlen gezeigt — niemals Schlüsselmaterial.",
"coverageDescription": "Wie viele verschlüsselte Zeilen unter welcher Schlüssel-ID liegen.",
"activeKeyId": "Aktive Schlüssel-ID",
"configuredKeys": "Konfigurierte Schlüssel",
"totalRows": "Verschlüsselte Zeilen",
@@ -6707,7 +6715,7 @@
"safeToDropLegacy": "Alter Schlüssel kann entfernt werden",
"rotationIncomplete": "{count} Zeilen brauchen noch eine Rotation",
"rotationTitle": "Schlüsselrotation",
"rotationDescription": "Jede verschlüsselte Spalte auf den aktiven Schlüssel neu verschlüsseln. Das Kommandozeilen-Werkzeug bleibt der kanonische Weg; dieser Auslöser ist eine Bequemlichkeit.",
"rotationDescription": "Jede verschlüsselte Spalte auf den aktiven Schlüssel neu verschlüsseln.",
"rotationState": "Status",
"lastRun": "Letzter Lauf: {scanned} geprüft, {rotated} rotiert, {errors} Fehler.",
"rotateNow": "Jetzt rotieren",
@@ -6730,7 +6738,8 @@
"running": "Läuft",
"completed": "Abgeschlossen",
"failed": "Fehlgeschlagen"
}
},
"coverageDetail": "Es werden nur Schlüssel-IDs und Anzahlen gezeigt — niemals Schlüsselmaterial."
}
},
"carrier": "Netzbetreiber",
@@ -6776,7 +6785,8 @@
"countFew": "{count} Fehlschläge",
"countOther": "{count} Fehlschläge",
"lastFailed": "Letzter Fehlschlag {when}"
}
},
"deleteAllDetail": "Nutzerkonten bleiben bestehen."
},
"achievements": {
"title": "Erfolge",
@@ -7102,7 +7112,7 @@
},
"moodReminder": {
"title": "Tägliche Stimmungs-Erinnerung",
"description": "Sanfter Anstoß zur eingestellten Zeit, falls du heute noch keine Stimmung erfasst hast. Greift nur auf bereits eingerichtete Kanäle (Telegram, ntfy, Web Push, Apple Push) zurück und schweigt, sobald du heute geloggt hast.",
"description": "Sanfter Anstoß zur eingestellten Zeit, falls du heute noch keine Stimmung erfasst hast.",
"toggleAria": "Tägliche Stimmungs-Erinnerung umschalten",
"statusOn": "Aktiv zu deiner eingestellten Zeit",
"statusOff": "Aus",
@@ -7111,11 +7121,12 @@
"saveError": "Speichern fehlgeschlagen. Bitte später erneut versuchen.",
"hourLabel": "Erinnerungszeit",
"hourAria": "Stunde der Stimmungs-Erinnerung",
"hourSavedToast": "Erinnerungszeit aktualisiert."
"hourSavedToast": "Erinnerungszeit aktualisiert.",
"detail": "Greift nur auf bereits eingerichtete Kanäle (Telegram, ntfy, Web Push, Apple Push) zurück und schweigt, sobald du heute geloggt hast."
},
"coachNudge": {
"title": "Proaktiver Coach",
"description": "Meldet sich, wenn deine Daten einen Blick in den Coach nahelegen — etwa bei niedriger Therapietreue oder erhöhtem Blutdruck-Wochenmittel. Themen und Häufigkeit sind einstellbar.",
"description": "Meldet sich, wenn deine Daten einen Blick in den Coach nahelegen.",
"toggleAria": "Proaktive Coach-Hinweise umschalten",
"statusOn": "Aktiv",
"statusOff": "Aus",
@@ -7142,7 +7153,7 @@
},
"lowStock": {
"title": "Vorratswarnung",
"description": "Prüft deine erfassten Medikamentenvorräte einmal täglich und benachrichtigt dich, wenn einer weniger Tage abdeckt als diese Schwelle. Eine Benachrichtigung pro Engpass; nach dem Auffüllen meldet sie sich wieder.",
"description": "Meldet täglich, wenn ein Vorrat weniger Tage abdeckt als diese Schwelle.",
"toggleAria": "Vorratswarnungen umschalten",
"statusOn": "Aktiv",
"statusOff": "Aus",
@@ -7156,7 +7167,8 @@
"enabledToast": "Vorratswarnungen aktiviert.",
"disabledToast": "Vorratswarnungen deaktiviert.",
"saveError": "Speichern fehlgeschlagen. Bitte später erneut versuchen.",
"medicationsLink": "Zu deinen Medikamenten"
"medicationsLink": "Zu deinen Medikamenten",
"detail": "Eine Benachrichtigung pro Engpass; nach dem Auffüllen meldet sie sich wieder."
},
"eventMedicationReminder": "Medikamenten-Erinnerung",
"eventMedicationReminderDesc": "Wird zu jeder geplanten Einnahmezeit gesendet.",
@@ -9536,7 +9548,7 @@
},
"invite": {
"title": "Jemanden einladen",
"description": "Vor der Zustimmung wird nichts geteilt. Bis dahin liegt die Einladung in den Einstellungen der anderen Person und gibt ihr nichts.",
"description": "Vor der Zustimmung wird nichts geteilt.",
"identifierLabel": "Benutzername oder E-Mail",
"identifierPlaceholder": "Der Name, mit dem sie sich anmeldet",
"identifierHint": "Die Person braucht ein Konto auf dieser Instanz. An eine E-Mail-Adresse ohne Registrierung lässt sich keine Einladung schicken.",
@@ -9590,7 +9602,7 @@
},
"received": {
"title": "Akten, die ich öffnen kann",
"description": "Eine Einladung gibt dir nichts, bevor du sie annimmst. Du kannst den Zugriff jederzeit zurückgeben.",
"description": "Eine Einladung gibt dir nichts, bevor du sie annimmst.",
"emptyTitle": "Niemand hat eine Akte mit dir geteilt",
"emptyDescription": "Wenn dich jemand einlädt, erscheint die Einladung hier.",
"loadError": "Die Liste konnte nicht geladen werden.",
@@ -9602,7 +9614,8 @@
"renounce": "Zurückgeben",
"renounceTitle": "Akte von {name} zurückgeben?",
"renounceBody": "Dein Zugriff endet sofort. Die Person kann dich jederzeit erneut einladen.",
"renounceConfirm": "Zurückgeben"
"renounceConfirm": "Zurückgeben",
"detail": "Du kannst den Zugriff jederzeit zurückgeben."
},
"activity": {
"title": "Wann meine Akte geöffnet wurde",
+75 -62
View File
@@ -3925,12 +3925,13 @@
},
"pillOrder": {
"title": "Sort the navigation pills",
"description": "Drag the pills into the order you want and show or hide them via the eye icon. Hidden detail pages are also removed from the top navigation.",
"description": "Drag the pills into the order you want and show or hide them via the eye icon.",
"dragHandle": "Reorder",
"saveSuccess": "Pill order saved.",
"saveError": "Saving the pill order failed.",
"save": "Save",
"empty": "No navigation pills available yet."
"empty": "No navigation pills available yet.",
"detail": "Hidden detail pages are also removed from the top navigation."
},
"customize": "Customize Insights",
"regenerateRateLimited": "Refresh limit reached — please try again later.",
@@ -4412,7 +4413,7 @@
"settings": {
"researchMode": {
"sectionTitle": "Research Mode",
"subtitle": "Opt-in display of an estimated GLP-1 drug-level curve, derived from EMA-published population pharmacokinetics. Reading-only — never a measurement or medical advice.",
"subtitle": "Opt-in curve of the estimated GLP-1 drug level, from EMA-published data.",
"toggleLabel": "Show the estimated drug-level chart",
"disabledStatus": "Disabled. The drug-level chart is hidden.",
"enabledStaleStatus": "The disclaimer was updated. Re-acknowledge below to bring the chart back.",
@@ -4420,7 +4421,8 @@
"rePromptTitle": "Disclaimer updated",
"rePromptBody": "The Research Mode disclaimer was updated since you last acknowledged it. Re-read and re-confirm to keep the chart visible.",
"rePromptCta": "Re-acknowledge disclaimer",
"disableError": "Could not disable Research Mode. Please try again."
"disableError": "Could not disable Research Mode. Please try again.",
"subtitleDetail": "Reading-only — never a measurement or medical advice."
},
"shell": {
"sectionsNav": "Settings sections",
@@ -4547,7 +4549,8 @@
},
"unavailableTitle": "Unavailable in this shared record",
"unavailableDescription": "This Settings area is intentionally unavailable while viewing another person's record.",
"integrationStatusDescription": "Connection status for this managed record. Connection controls are unavailable."
"integrationStatusDescription": "Connection status for this managed record.",
"integrationStatusDetail": "Connection controls are unavailable."
},
"sections": {
"account": {
@@ -4622,7 +4625,7 @@
"integrationsHint": "Connect or disconnect data sources on the Integrations page.",
"integrationsHintLink": "Open integrations",
"cardTitle": "Source priority by metric",
"help": "When several sources deliver the same value on the same day, the top source counts as canonical. The others stay stored for the audit trail.",
"help": "When several sources deliver the same value on the same day, the top source counts as canonical.",
"resetDefaults": "Reset to defaults",
"resetDefaultsTitle": "Reset the source order?",
"resetDefaultsBody": "Every ladder you arranged goes back to the default order, for all metrics and device types at once. This is saved immediately and cannot be brought back.",
@@ -4682,13 +4685,15 @@
"subtitle": "Decide which source wins when two report the same metric.",
"nutrients": {
"title": "Synced nutrient intake",
"description": "Daily totals synced from Apple Health over the last {days} days. Read-only — the source app owns the numbers.",
"description": "Daily totals synced from Apple Health over the last {days} days.",
"empty": "No nutrient data has been synced yet.",
"latest": "{amount} {unit} on {date}",
"daysWithData": "{count} of {days} days",
"moduleHint": "Syncing is controlled by the Nutrient intake module.",
"moduleHintLink": "Manage modules"
}
"moduleHintLink": "Manage modules",
"detail": "Read-only — the source app owns the numbers."
},
"helpDetail": "The others stay stored for the audit trail."
},
"ai": {
"title": "AI provider",
@@ -4938,7 +4943,7 @@
"error": "Couldn't save. Please try again.",
"toggleable": {
"title": "What you track",
"description": "Optional areas. Disabled modules drop out of the navigation, tiles, and insights — your data stays untouched."
"description": "Disabled modules drop out of the navigation, tiles, and insights — your data stays untouched."
},
"manageIn": "Manage in {section}",
"operatorDisabled": "Disabled server-wide",
@@ -5326,7 +5331,7 @@
"testReasonUnreachable": "Could not reach the AI provider.",
"coachMemory": {
"title": "What the Coach remembers",
"description": "Durable facts you have told the Coach. It uses these to keep its replies relevant. Remove anything you would rather it forgot.",
"description": "Durable facts you have told the Coach.",
"empty": "The Coach has not stored any facts yet. As you chat, it remembers durable details you share — your goals, conditions, preferences — and you can clear them here at any time.",
"learnedPrefix": "learned {when}",
"categoryPreference": "Preferences",
@@ -5349,11 +5354,12 @@
"forgotToast": "Fact forgotten.",
"forgotError": "Could not forget that fact. Please try again.",
"clearedToast": "Forgot {count} stored facts.",
"clearError": "Could not clear the stored facts. Please try again."
"clearError": "Could not clear the stored facts. Please try again.",
"detail": "It uses these to keep its replies relevant. Remove anything you would rather it forgot."
},
"coachReminders": {
"title": "Your reminders",
"description": "Things you asked the Coach to bring back later. Due ones show up here and on the Coach.",
"description": "Things you asked the Coach to bring back later.",
"empty": "No reminders yet. Ask the Coach to remind you about something and it will appear here.",
"loadError": "Could not load your reminders.",
"dueHeading": "Due now",
@@ -5374,7 +5380,8 @@
},
"deleteConfirmTitle": "Delete this reminder?",
"deleteConfirmBody": "It stops being raised. Anything you have already recorded is untouched.",
"deleteConfirmAction": "Delete"
"deleteConfirmAction": "Delete",
"detail": "Due ones show up here and on the Coach."
},
"aboutMe": {
"title": "About me",
@@ -5647,7 +5654,7 @@
"workouts": "Workouts"
},
"integrationFallback": {
"description": "Configured on this account without a settings card of its own. This row exists so a connection cannot stop working unnoticed.",
"description": "Configured on this account without a settings card of its own.",
"lastAttempt": "Last attempt {relative}",
"noAttempt": "No sync attempt recorded"
},
@@ -5656,7 +5663,7 @@
"fitbitViewData": "View your sleep",
"googleHealthViewData": "View your steps",
"apiTokens": "API Tokens",
"apiTokensDescription": "Review and revoke API tokens for external medication intake. Tokens are issued per medication from the medication's API endpoint toggle.",
"apiTokensDescription": "Review and revoke API tokens for external medication intake.",
"tokenMintMovedDescription": "Tokens are issued per medication. Open a medication and enable its API endpoint to get a token scoped to that medication alone.",
"tokenNamePlaceholder": "Token name (e.g., iPhone Shortcut)",
"tokenCreated": "Token created — copy it now! It won't be shown again.",
@@ -5687,14 +5694,14 @@
"apiEndpointAuth": "Authentication",
"apiEndpointExample": "Body example",
"dangerZone": "Delete All Data",
"dangerZoneDescription": "Erases your whole health record and every integration connection. Your account and the credentials you sign in with are kept.",
"dangerZoneDescription": "Erases your whole health record and every integration connection.",
"dangerZoneConfirm": "Delete all data?",
"dangerZoneConfirmDescription": "This permanently deletes everything in your record: measurements, medications, mood entries, lab results, cycle entries, documents, AI conversations and every integration connection, together with everything derived from them and anything you deleted earlier. Share links you gave to a doctor stop working immediately. Your account stays, along with the credentials you sign in with and display settings such as language, timezone and units. This cannot be undone.",
"dangerZoneSuccess": "Your health record has been deleted. Your account and sign-in are unchanged.",
"dangerZoneDeleteFailed": "Deletion failed",
"finalDelete": "Delete permanently",
"deleteAccountCardTitle": "Delete account entirely",
"deleteAccountCardDescription": "Deletes your account along with passkeys, audit log, and sessions. This cannot be undone.",
"deleteAccountCardDescription": "Deletes your account along with passkeys, audit log, and sessions.",
"deleteAccountCta": "Delete account",
"deleteAccountConfirmTitle": "Delete account permanently?",
"deleteAccountConfirmDescription": "Your account, all health data, passkeys, sessions, and audit entries will be permanently deleted. You will be signed out immediately after.",
@@ -5712,8 +5719,6 @@
"changelog": "Changelog",
"docs": "Documentation",
"linksHeading": "Sources & docs",
"identityDescription": "Version, license, and build details for this instance.",
"linksDescription": "Source code, changelog, and documentation.",
"newerAvailable": "Newer version: {tag}"
},
"testConnection": {
@@ -5741,7 +5746,7 @@
"notificationStatus": {
"title": "Delivery status",
"description": "Per-channel state; \"Auto-disabled\" stops retries after permanent errors.",
"emptyDescription": "No channels configured yet. Set one up under Delivery channels to start tracking delivery.",
"emptyDescription": "Set up a channel under Settings › Integrations › Delivery channels.",
"stateActive": "Active",
"stateAutoDisabled": "Auto-disabled",
"stateSendingPaused": "Sending paused",
@@ -5752,7 +5757,8 @@
"disabledReason": "Reason",
"nextRetry": "Next retry",
"reEnable": "Re-enable",
"sendTest": "Send test"
"sendTest": "Send test",
"emptyTitle": "No channels configured yet"
},
"identity": {
"description": "Optional details for the health-record export (PDF cover + FHIR export). All fields are optional.",
@@ -5765,7 +5771,7 @@
},
"healthRecord": {
"title": "Export health record",
"description": "Generate a complete health record for your doctor — as a PDF, structured (FHIR), or a combined package.",
"description": "A complete record for your doctor — as a PDF, as FHIR, or both.",
"format": "Format",
"format_pdf": "PDF",
"format_fhir": "Structured (FHIR)",
@@ -5790,10 +5796,10 @@
"errorModuleDisabled": "The health record export is switched off for this account."
},
"globalExcludedInjectionSitesLabel": "Globally excluded injection sites",
"globalExcludedInjectionSitesHint": "Sites listed here are never offered for any medication and rejected when logging a dose, even if a medication prefers them.",
"globalExcludedInjectionSitesHint": "These sites are never offered and are rejected when a dose is logged.",
"sharing": {
"createTitle": "New share link",
"createDescription": "The link is read-only and time-boxed. The token is shown once on creation — store it then; it cannot be recovered.",
"createDescription": "The link is read-only and time-boxed.",
"label": "Label",
"labelPlaceholder": "e.g. Dr. Smith — annual check-up",
"range": "Days of history",
@@ -5843,15 +5849,17 @@
"scopeInsuranceExcluded": "Insurance details are never shared through a link.",
"statusNeedsReselect": "Pick contents again",
"reselectReason": "This link served a scope that was never chosen, so it was closed. Create a new one and pick what it should contain.",
"reselectAction": "Create a replacement"
"reselectAction": "Create a replacement",
"createDetail": "The token is shown once on creation — store it then; it cannot be recovered."
},
"genderOther": "Other",
"cycleTracking": {
"title": "Cycle tracking",
"description": "Track your menstrual cycle, predictions, symptoms, and phase insights. On by default for accounts set to female; available to everyone.",
"description": "Track your menstrual cycle, predictions, symptoms, and phase insights.",
"enable": "Enable cycle tracking",
"manage": "Open cycle tracking",
"error": "Couldn't update cycle tracking."
"error": "Couldn't update cycle tracking.",
"detail": "On by default for accounts set to female; available to everyone."
},
"nightscout": "Nightscout",
"nightscoutTag": "CGM",
@@ -5937,7 +5945,7 @@
"coach": {
"activate": {
"title": "Activate Coach",
"description": "Show the Coach button and drawer. On by default — turn it off to hide the Coach everywhere.",
"description": "Show the Coach button and drawer.",
"toggleAria": "Activate the Coach button and drawer",
"savedOn": "Coach activated.",
"savedOff": "Coach hidden.",
@@ -6057,19 +6065,13 @@
"signOutEverywhereConfirmBody": "Every device except this one will need to sign in again. This device stays signed in. Nothing you have recorded is affected."
},
"privacy": {
"intro": {
"title": "Your data stays yours",
"body": "HealthLog is self-hosted. This page brings together how your data is protected, how long it is kept, how to get it out, and how to delete it."
},
"encryption": {
"title": "Encrypted at rest",
"description": "Sensitive fields are encrypted in the database.",
"summary": "Your sensitive data is encrypted at rest with {algorithm} across {columns} fields in {models} record types.",
"statement": "Your sensitive data is encrypted at rest with AES-256-GCM."
},
"stored": {
"title": "What's stored",
"description": "The categories of data this account holds.",
"measurements": "Measurements (weight, blood pressure, pulse, glucose, sleep, body composition)",
"medications": "Medications and intake history",
"moodLabs": "Mood entries and lab results",
@@ -6087,7 +6089,7 @@
},
"export": {
"title": "Download my data",
"description": "Export your records as CSV or JSON, optionally passphrase-encrypted, or generate a full health record.",
"description": "Your records as CSV or JSON, optionally passphrase-encrypted.",
"openExport": "Export & backup",
"openHealthRecord": "Health record"
},
@@ -6106,26 +6108,28 @@
},
"mcp": {
"enableTitle": "Remote MCP endpoint",
"enableDescription": "Let an MCP-capable assistant (Claude.ai, ChatGPT) read your health data over a scoped token. Off by default.",
"enableDescription": "Let an MCP-capable assistant (Claude.ai, ChatGPT) read your health data over a scoped token.",
"enabled": "Enabled",
"disabled": "Disabled",
"enableToggleLabel": "Enable the MCP endpoint",
"endpointLabel": "Connector URL",
"connectDescription": "Paste this URL into your assistant's custom-connector setup, then authorize with the token below or sign in when prompted.",
"tokensTitle": "Connector tokens",
"tokensDescription": "Mint a read-only token to connect an assistant. The token is shown once.",
"tokensDescription": "Mint a read-only token to connect an assistant.",
"scopeNote": "Tokens are scoped to health:read — read-only access to your own records. They can never reach admin functions.",
"writeScopeToggleLabel": "Allow logging (health:write)",
"writeScopeNote": "Adds the health:write scope so the assistant can log new measurements and mood entries to your account. Every write is confirmed first. The token still can't delete or edit existing entries, change medications, or reach REST or admin functions. Leave off for read-only access.",
"connectionsTitle": "Connected assistants",
"connectionsDescription": "Assistants you authorized through the sign-in flow. Revoking a connection ends its access immediately, including its ability to refresh.",
"connectionsDescription": "Assistants you authorized through the sign-in flow.",
"noConnections": "No connected assistants.",
"connectionConnected": "Connected",
"connectionLastUsed": "Last used",
"connectionNeverUsed": "Never used",
"connectionRevoke": "Disconnect",
"connectionRevokeDescription": "This ends the assistant's access immediately and stops it from renewing. It will need to be re-authorized to reconnect.",
"connectionRevokeFailed": "Could not disconnect the assistant."
"connectionRevokeFailed": "Could not disconnect the assistant.",
"connectionsDetail": "Revoking a connection ends its access immediately, including its ability to refresh.",
"tokensDetail": "The token is shown once."
},
"strava": "Strava",
"stravaDescription": "Connect your Strava account to sync your activities into your workout history.",
@@ -6158,7 +6162,7 @@
},
"appleHealth": {
"title": "Apple Health",
"description": "Live sync comes from the HealthLog iOS app. It is not a web or OAuth connection.",
"description": "Live sync comes from the HealthLog iOS app, not from a web or OAuth connection.",
"status": {
"unavailable": "Apple Health status unavailable"
},
@@ -6194,7 +6198,9 @@
"explainer": "Off, readings are judged against the general non-diabetic reference band. On, they are judged against the tighter glycaemic goal band from the ADA Standards of Care, and the Coach grounds its glucose remarks on that band instead. Your own target overrides below still take precedence.",
"disclaimer": "This selects a reference band. It is not a diagnosis, it is never inferred from a reading, and the app takes no clinical action on it. Which targets apply to you is a question for your clinician.",
"error": "Could not save the setting."
}
},
"dangerZoneDetail": "Your account and the credentials you sign in with are kept.",
"deleteAccountCardDetail": "This cannot be undone."
},
"admin": {
"title": "Administration",
@@ -6225,7 +6231,7 @@
},
"aiServerKey": {
"title": "AI server key",
"description": "An operator-wide provider key every account can fall back to when no personal key is configured.",
"description": "An operator key for accounts that have not configured one of their own.",
"byokHint": "A user's own key (BYOK, ChatGPT login or local provider) always takes precedence — the server key is the last step in the provider chain.",
"consentHint": "Requests over the server key require the user's active AI consent; without a consent receipt nothing is sent.",
"statusConfigured": "Configured",
@@ -6331,7 +6337,7 @@
"monitoringTestFailed": "Monitoring test failed",
"assistant": {
"title": "Assistant Surfaces",
"description": "Each toggle hides a model-driven surface app-wide without removing the provider configuration. The master kills every sub-flag at once.",
"description": "Each toggle hides a model-driven surface app-wide without removing the provider configuration.",
"master": {
"title": "Assistant enabled",
"description": "Master switch for every model-driven surface. When off, every sub-flag below is forced off."
@@ -6355,12 +6361,13 @@
},
"modules": {
"title": "Module availability",
"description": "Turn a module off for the whole server. A module disabled here is off for every account regardless of personal preference. Core domains (weight, blood pressure, pulse, medications) are always on and cannot be disabled.",
"saveError": "Could not update module availability."
"description": "Turn a module off for the whole server.",
"saveError": "Could not update module availability.",
"detail": "A module disabled here is off for every account regardless of personal preference. Core domains (weight, blood pressure, pulse, medications) are always on and cannot be disabled."
},
"invites": {
"title": "Invites",
"description": "Registration invite links — they work even while registration is closed. The link is shown only once.",
"description": "Registration invite links — they work even while registration is closed.",
"create": "Create invite",
"createTitle": "Create invite",
"createDescription": "The link admits a signup even while registration is closed.",
@@ -6466,7 +6473,7 @@
"outcomeFailed": "Failed",
"auditDetails": "Details",
"deleteAllData": "Delete all data globally",
"deleteAllDescription": "Deletes all measurement, medication, integration, and log data for all users. User accounts will be preserved.",
"deleteAllDescription": "Deletes all measurement, medication, integration, and log data for all users.",
"deleteAllConfirm": "Really delete all data?",
"deleteAllConfirmDescription": "This will permanently delete all measurements, medications, intake events, API tokens, Withings connections, open authentication challenges, notification channels (Telegram, ntfy, Web Push), push subscriptions, and queued Telegram message deletions for all users. User accounts, the audit log, and submitted feedback are preserved so the wipe itself remains traceable and bug reports stay triageable.",
"deleteButton": "Delete all data",
@@ -6611,7 +6618,7 @@
"app-logs": {
"title": "App Logs",
"subtitle": "Tail the most recent structured wide-events emitted by this process.",
"processNote": "Showing app-process logs only — worker-process logs go to Loki when that integration is configured. Buffer is volatile and capped at 500 entries.",
"processNote": "Showing app-process logs only — worker-process logs go to Loki when that integration is configured.",
"filterTraceId": "Trace ID",
"filterAction": "Action contains…",
"filterLevel": "Level",
@@ -6632,7 +6639,8 @@
"emptyDescription": "No events match the current filters yet. Hit refresh after triggering some traffic.",
"refresh": "Refresh",
"eventDetails": "Event details",
"closeDetails": "Close details"
"closeDetails": "Close details",
"processDetail": "Buffer is volatile and capped at 500 entries."
},
"backups": {
"title": "Backups",
@@ -6699,7 +6707,7 @@
"loading": "Loading encryption status…",
"loadError": "Failed to load encryption status.",
"coverageTitle": "Coverage",
"coverageDescription": "How many encrypted rows sit under each key id. Only key ids and counts are shown — never key material.",
"coverageDescription": "How many encrypted rows sit under each key id.",
"activeKeyId": "Active key id",
"configuredKeys": "Configured keys",
"totalRows": "Encrypted rows",
@@ -6707,7 +6715,7 @@
"safeToDropLegacy": "Safe to drop the legacy key",
"rotationIncomplete": "{count} rows still need rotation",
"rotationTitle": "Key rotation",
"rotationDescription": "Re-encrypt every encrypted column to the active key. The command-line tool stays the canonical path; this trigger is a convenience.",
"rotationDescription": "Re-encrypt every encrypted column to the active key.",
"rotationState": "State",
"lastRun": "Last run: scanned {scanned}, rotated {rotated}, errors {errors}.",
"rotateNow": "Rotate now",
@@ -6730,7 +6738,8 @@
"running": "Running",
"completed": "Completed",
"failed": "Failed"
}
},
"coverageDetail": "Only key ids and counts are shown — never key material."
}
},
"carrier": "Carrier",
@@ -6776,7 +6785,8 @@
"countFew": "{count} failures",
"countOther": "{count} failures",
"lastFailed": "Last failure {when}"
}
},
"deleteAllDetail": "User accounts will be preserved."
},
"achievements": {
"title": "Achievements",
@@ -7102,7 +7112,7 @@
},
"moodReminder": {
"title": "Daily mood reminder",
"description": "A gentle nudge at your chosen time when you haven't logged a mood for the day. Routes only through channels you've already configured (Telegram, ntfy, Web Push, Apple Push) and stays silent once you've logged today.",
"description": "A gentle nudge at your chosen time when you haven't logged a mood for the day.",
"toggleAria": "Toggle daily mood reminder",
"statusOn": "Active at your chosen time",
"statusOff": "Off",
@@ -7111,11 +7121,12 @@
"saveError": "Save failed. Please try again.",
"hourLabel": "Reminder time",
"hourAria": "Mood reminder hour",
"hourSavedToast": "Reminder time updated."
"hourSavedToast": "Reminder time updated.",
"detail": "Routes only through channels you've already configured (Telegram, ntfy, Web Push, Apple Push) and stays silent once you've logged today."
},
"coachNudge": {
"title": "Proactive Coach",
"description": "Reaches out when your data suggests a look at the Coach — for example low medication adherence or an elevated weekly blood-pressure average. Topics and frequency are adjustable.",
"description": "Reaches out when your data suggests a look at the Coach.",
"toggleAria": "Toggle proactive Coach nudges",
"statusOn": "Active",
"statusOff": "Off",
@@ -7142,7 +7153,7 @@
},
"lowStock": {
"title": "Low-stock alert",
"description": "Checks your tracked medication supplies once a day and notifies you when one covers fewer days than this threshold. One notification per shortage; it re-arms after a refill.",
"description": "Reports daily when a supply covers fewer days than this threshold.",
"toggleAria": "Toggle low-stock alerts",
"statusOn": "Active",
"statusOff": "Off",
@@ -7156,7 +7167,8 @@
"enabledToast": "Low-stock alerts enabled.",
"disabledToast": "Low-stock alerts disabled.",
"saveError": "Could not save. Please try again.",
"medicationsLink": "Go to your medications"
"medicationsLink": "Go to your medications",
"detail": "One notification per shortage; it re-arms after a refill."
},
"eventMedicationReminder": "Medication reminder",
"eventMedicationReminderDesc": "Sent at each scheduled dose time.",
@@ -9536,7 +9548,7 @@
},
"invite": {
"title": "Invite someone",
"description": "Nothing is shared until they accept. Until then the invitation sits in their settings and gives them nothing.",
"description": "Nothing is shared until they accept.",
"identifierLabel": "Username or e-mail",
"identifierPlaceholder": "The name they sign in with",
"identifierHint": "They need an account on this instance. Invitations cannot be sent to an e-mail address that has not registered.",
@@ -9590,7 +9602,7 @@
},
"received": {
"title": "Records I can open",
"description": "An invitation gives you nothing until you accept it. You can hand access back whenever you want.",
"description": "An invitation gives you nothing until you accept it.",
"emptyTitle": "Nobody has shared a record with you",
"emptyDescription": "When someone invites you, the invitation appears here.",
"loadError": "The list could not be loaded.",
@@ -9602,7 +9614,8 @@
"renounce": "Hand back",
"renounceTitle": "Hand {name}'s record back?",
"renounceBody": "You lose access immediately. They can invite you again at any time.",
"renounceConfirm": "Hand back"
"renounceConfirm": "Hand back",
"detail": "You can hand access back whenever you want."
},
"activity": {
"title": "When my record was opened",
+74 -61
View File
@@ -3925,12 +3925,13 @@
},
"pillOrder": {
"title": "Ordenar las pestañas de navegación",
"description": "Arrastra las pestañas al orden que quieras y muéstralas u ocúltalas con el icono del ojo. Las páginas de detalle ocultas también se quitan de la navegación superior.",
"description": "Arrastra las pestañas al orden que quieras y muéstralas u ocúltalas con el icono del ojo.",
"dragHandle": "Reordenar",
"saveSuccess": "Orden de las pestañas guardado.",
"saveError": "No se pudo guardar el orden de las pestañas.",
"save": "Guardar",
"empty": "Aún no hay pestañas de navegación disponibles."
"empty": "Aún no hay pestañas de navegación disponibles.",
"detail": "Las páginas de detalle ocultas también se quitan de la navegación superior."
},
"customize": "Personalizar Análisis",
"regenerateRateLimited": "Límite de actualizaciones alcanzado: inténtalo de nuevo más tarde.",
@@ -4412,7 +4413,7 @@
"settings": {
"researchMode": {
"sectionTitle": "Modo Investigación",
"subtitle": "Visualización opcional de una curva estimada del nivel de fármaco GLP-1, derivada de la farmacocinética poblacional publicada por la EMA. Solo lectura: nunca una medición ni un consejo médico.",
"subtitle": "Curva opcional del nivel estimado del fármaco GLP-1, según datos de la EMA.",
"toggleLabel": "Mostrar la curva estimada del nivel de fármaco",
"disabledStatus": "Desactivado. La curva está oculta.",
"enabledStaleStatus": "El disclaimer se actualizó. Reconfirma debajo para volver a mostrar la curva.",
@@ -4420,7 +4421,8 @@
"rePromptTitle": "Disclaimer actualizado",
"rePromptBody": "El disclaimer del modo Investigación se actualizó desde tu última confirmación. Relelo y reconfirma para mantener la curva visible.",
"rePromptCta": "Reconfirmar disclaimer",
"disableError": "No se pudo desactivar el modo Investigación. Inténtalo de nuevo."
"disableError": "No se pudo desactivar el modo Investigación. Inténtalo de nuevo.",
"subtitleDetail": "Solo lectura: nunca una medición ni un consejo médico."
},
"shell": {
"sectionsNav": "Secciones de ajustes",
@@ -4547,7 +4549,8 @@
},
"unavailableTitle": "No disponible en este registro compartido",
"unavailableDescription": "Esta sección de Ajustes no está disponible intencionadamente mientras consultas el registro de otra persona.",
"integrationStatusDescription": "Estado de conexión de este registro gestionado. Los controles de conexión no están disponibles."
"integrationStatusDescription": "Estado de conexión de este registro gestionado.",
"integrationStatusDetail": "Los controles de conexión no están disponibles."
},
"sections": {
"account": {
@@ -4622,7 +4625,7 @@
"integrationsHint": "Conecta o desconecta fuentes de datos en la página de integraciones.",
"integrationsHintLink": "Abrir integraciones",
"cardTitle": "Prioridad de fuentes por métrica",
"help": "Si varias fuentes entregan el mismo valor el mismo día, la fuente superior cuenta como canónica. Las demás se conservan para la traza de auditoría.",
"help": "Si varias fuentes entregan el mismo valor el mismo día, la fuente superior cuenta como canónica.",
"resetDefaults": "Restablecer valores predeterminados",
"resetDefaultsTitle": "¿Restablecer el orden de fuentes?",
"resetDefaultsBody": "Cada orden que configuraste vuelve al predeterminado, para todas las métricas y tipos de dispositivo a la vez. Se guarda de inmediato y no se puede recuperar.",
@@ -4682,13 +4685,15 @@
"subtitle": "Decide qué fuente prevalece cuando dos informan la misma métrica.",
"nutrients": {
"title": "Ingesta de nutrientes sincronizada",
"description": "Totales diarios sincronizados desde Apple Health en los últimos {days} días. Solo lectura: la app de origen es la dueña de los valores.",
"description": "Totales diarios sincronizados desde Apple Health en los últimos {days} días.",
"empty": "Todavía no se han sincronizado datos de nutrientes.",
"latest": "{amount} {unit} el {date}",
"daysWithData": "{count} de {days} días",
"moduleHint": "La sincronización se controla con el módulo de ingesta de nutrientes.",
"moduleHintLink": "Gestionar módulos"
}
"moduleHintLink": "Gestionar módulos",
"detail": "Solo lectura: la app de origen es la dueña de los valores."
},
"helpDetail": "Las demás se conservan para la traza de auditoría."
},
"ai": {
"title": "Proveedor de IA",
@@ -4938,7 +4943,7 @@
"error": "No se pudo guardar. Inténtalo de nuevo.",
"toggleable": {
"title": "Qué registras",
"description": "Áreas opcionales. Los módulos desactivados desaparecen de la navegación, los paneles y los análisis; tus datos se conservan."
"description": "Los módulos desactivados desaparecen de la navegación, los paneles y los análisis; tus datos se conservan."
},
"manageIn": "Gestionar en {section}",
"operatorDisabled": "Desactivado en todo el servidor",
@@ -5326,7 +5331,7 @@
"testReasonUnreachable": "No se pudo contactar con el proveedor de IA.",
"coachMemory": {
"title": "Lo que el Coach recuerda",
"description": "Datos duraderos que le has contado al Coach. Los usa para que sus respuestas sigan siendo relevantes. Elimina lo que prefieras que olvide.",
"description": "Datos duraderos que le has contado al Coach.",
"empty": "El Coach aún no ha guardado ningún dato. A medida que conversas, recuerda detalles duraderos que compartes: tus objetivos, afecciones y preferencias, y puedes borrarlos aquí cuando quieras.",
"learnedPrefix": "aprendido {when}",
"categoryPreference": "Preferencias",
@@ -5349,11 +5354,12 @@
"forgotToast": "Dato olvidado.",
"forgotError": "No se pudo olvidar ese dato. Inténtalo de nuevo.",
"clearedToast": "Se olvidaron {count} datos guardados.",
"clearError": "No se pudieron borrar los datos guardados. Inténtalo de nuevo."
"clearError": "No se pudieron borrar los datos guardados. Inténtalo de nuevo.",
"detail": "Los usa para que sus respuestas sigan siendo relevantes. Elimina lo que prefieras que olvide."
},
"coachReminders": {
"title": "Tus recordatorios",
"description": "Cosas que le pediste al Coach que retomara más tarde. Las que vencen aparecen aquí y en el Coach.",
"description": "Cosas que le pediste al Coach que retomara más tarde.",
"empty": "Aún no hay recordatorios. Pídele al Coach que te recuerde algo y aparecerá aquí.",
"loadError": "No se pudieron cargar tus recordatorios.",
"dueHeading": "Vencen ahora",
@@ -5374,7 +5380,8 @@
},
"deleteConfirmTitle": "¿Eliminar este recordatorio?",
"deleteConfirmBody": "Deja de aparecer. Lo que ya has registrado no cambia.",
"deleteConfirmAction": "Eliminar"
"deleteConfirmAction": "Eliminar",
"detail": "Las que vencen aparecen aquí y en el Coach."
},
"aboutMe": {
"title": "Sobre mí",
@@ -5647,7 +5654,7 @@
"workouts": "Entrenamientos"
},
"integrationFallback": {
"description": "Configurado en esta cuenta pero sin tarjeta de ajustes propia. Esta fila existe para que una conexión no deje de funcionar sin que nadie lo note.",
"description": "Configurado en esta cuenta pero sin tarjeta de ajustes propia.",
"lastAttempt": "Último intento {relative}",
"noAttempt": "Ningún intento de sincronización registrado"
},
@@ -5656,7 +5663,7 @@
"fitbitViewData": "Ver tu sueño",
"googleHealthViewData": "Ver tus pasos",
"apiTokens": "Tokens de API",
"apiTokensDescription": "Revisa y revoca tokens de API para el registro externo de tomas de medicación. Los tokens se emiten por medicamento desde su interruptor de endpoint de API.",
"apiTokensDescription": "Revisa y revoca tokens de API para el registro externo de tomas de medicación.",
"tokenMintMovedDescription": "Los tokens se emiten por medicamento. Abre un medicamento y activa su endpoint de API para obtener un token limitado únicamente a ese medicamento.",
"tokenNamePlaceholder": "Nombre del token (p. ej., Atajo de iPhone)",
"tokenCreated": "Token creado — ¡cópialo ahora! No se mostrará de nuevo.",
@@ -5687,14 +5694,14 @@
"apiEndpointAuth": "Autenticación",
"apiEndpointExample": "Ejemplo de cuerpo",
"dangerZone": "Eliminar todos los datos",
"dangerZoneDescription": "Borra todo tu historial de salud y todas las conexiones de integración. Tu cuenta y las credenciales con las que inicias sesión se conservan.",
"dangerZoneDescription": "Borra todo tu historial de salud y todas las conexiones de integración.",
"dangerZoneConfirm": "¿Eliminar todos los datos?",
"dangerZoneConfirmDescription": "Esto elimina de forma permanente todo lo que hay en tu historial: mediciones, medicamentos, entradas de ánimo, resultados de laboratorio, entradas de ciclo, documentos, conversaciones con la IA y todas las conexiones de integración, junto con todo lo derivado de ello y todo lo que eliminaste antes. Los enlaces compartidos que diste a una consulta dejan de funcionar de inmediato. Tu cuenta se mantiene, junto con las credenciales con las que inicias sesión y los ajustes de presentación como el idioma, la zona horaria y las unidades. Esto no se puede deshacer.",
"dangerZoneSuccess": "Se ha eliminado tu historial de salud. Tu cuenta y tu inicio de sesión no han cambiado.",
"dangerZoneDeleteFailed": "Falló la eliminación",
"finalDelete": "Eliminar permanentemente",
"deleteAccountCardTitle": "Eliminar la cuenta por completo",
"deleteAccountCardDescription": "Elimina tu cuenta junto con las claves de acceso, el registro de auditoría y las sesiones. Esto no se puede deshacer.",
"deleteAccountCardDescription": "Elimina tu cuenta junto con las claves de acceso, el registro de auditoría y las sesiones.",
"deleteAccountCta": "Eliminar cuenta",
"deleteAccountConfirmTitle": "¿Eliminar la cuenta de forma permanente?",
"deleteAccountConfirmDescription": "Tu cuenta, todos los datos de salud, las claves de acceso, las sesiones y las entradas de auditoría se eliminarán de forma permanente. Se cerrará tu sesión inmediatamente después.",
@@ -5712,8 +5719,6 @@
"changelog": "Changelog",
"docs": "Documentación",
"linksHeading": "Fuentes y documentación",
"identityDescription": "Versión, licencia y detalles de compilación de esta instancia.",
"linksDescription": "Código fuente, registro de cambios y documentación.",
"newerAvailable": "Nueva versión: {tag}"
},
"testConnection": {
@@ -5741,7 +5746,7 @@
"notificationStatus": {
"title": "Fiabilidad de los canales",
"description": "Estado por canal; «Desactivado automáticamente» detiene los reintentos tras errores permanentes.",
"emptyDescription": "Aún no hay canales configurados. Configura uno en Canales de entrega para seguir la entrega.",
"emptyDescription": "Configura un canal en Ajustes › Integraciones › Canales de entrega.",
"stateActive": "Activo",
"stateAutoDisabled": "Desactivado automáticamente",
"stateSendingPaused": "Envío en pausa",
@@ -5752,7 +5757,8 @@
"disabledReason": "Motivo",
"nextRetry": "Próximo intento",
"reEnable": "Reactivar",
"sendTest": "Enviar prueba"
"sendTest": "Enviar prueba",
"emptyTitle": "Aún no hay canales configurados"
},
"identity": {
"description": "Datos opcionales para la exportación del historial de salud (portada PDF + exportación FHIR). Todos los campos son opcionales.",
@@ -5765,7 +5771,7 @@
},
"healthRecord": {
"title": "Exportar historial de salud",
"description": "Genere un historial de salud completo para su médico — como PDF, estructurado (FHIR) o un paquete combinado.",
"description": "Un historial completo para tu médico: en PDF, en FHIR o ambos.",
"format": "Formato",
"format_pdf": "PDF",
"format_fhir": "Estructurado (FHIR)",
@@ -5790,10 +5796,10 @@
"errorModuleDisabled": "La exportación del expediente está desactivada para esta cuenta."
},
"globalExcludedInjectionSitesLabel": "Sitios de inyección excluidos globalmente",
"globalExcludedInjectionSitesHint": "Los sitios listados aquí no se ofrecen para ningún medicamento y se rechazan al registrar una dosis, aunque un medicamento los prefiera.",
"globalExcludedInjectionSitesHint": "Estos sitios nunca se ofrecen y se rechazan al registrar una dosis.",
"sharing": {
"createTitle": "Nuevo enlace para compartir",
"createDescription": "El enlace es de solo lectura y limitado en el tiempo. El token se muestra una sola vez al crearlo — guárdalo entonces; no se puede recuperar.",
"createDescription": "El enlace es de solo lectura y limitado en el tiempo.",
"label": "Etiqueta",
"labelPlaceholder": "p. ej. Dra. García — revisión anual",
"range": "Días de historial",
@@ -5843,15 +5849,17 @@
"scopeInsuranceExcluded": "Los datos del seguro nunca se comparten por enlace.",
"statusNeedsReselect": "Elegir contenido de nuevo",
"reselectReason": "Este enlace mostraba un alcance que nadie eligió, por eso se cerró. Cree uno nuevo y elija su contenido.",
"reselectAction": "Crear un reemplazo"
"reselectAction": "Crear un reemplazo",
"createDetail": "El token se muestra una sola vez al crearlo — guárdalo entonces; no se puede recuperar."
},
"genderOther": "Otro",
"cycleTracking": {
"title": "Seguimiento del ciclo",
"description": "Registra tu ciclo menstrual, predicciones, síntomas e información de fases. Activado por defecto en cuentas configuradas como femeninas; disponible para todos.",
"description": "Registra tu ciclo menstrual, predicciones, síntomas e información de fases.",
"enable": "Activar el seguimiento del ciclo",
"manage": "Abrir seguimiento del ciclo",
"error": "No se pudo actualizar el seguimiento del ciclo."
"error": "No se pudo actualizar el seguimiento del ciclo.",
"detail": "Activado por defecto en cuentas configuradas como femeninas; disponible para todos."
},
"nightscout": "Nightscout",
"nightscoutTag": "CGM",
@@ -5937,7 +5945,7 @@
"coach": {
"activate": {
"title": "Activar el Coach",
"description": "Muestra el botón y el panel del Coach. Activado por defecto: desactívalo para ocultar el Coach en todas partes.",
"description": "Muestra el botón y el panel del Coach.",
"toggleAria": "Activar el botón y el panel del Coach",
"savedOn": "Coach activado.",
"savedOff": "Coach oculto.",
@@ -6057,19 +6065,13 @@
"signOutEverywhereConfirmBody": "Todos los dispositivos salvo este tendrán que iniciar sesión de nuevo. Este seguirá con la sesión abierta. Nada de lo que has registrado se ve afectado."
},
"privacy": {
"intro": {
"title": "Tus datos siguen siendo tuyos",
"body": "HealthLog es autoalojado. Esta página reúne cómo se protegen tus datos, cuánto tiempo se conservan, cómo exportarlos y cómo eliminarlos."
},
"encryption": {
"title": "Cifrado en reposo",
"description": "Los campos sensibles se cifran en la base de datos.",
"summary": "Tus datos sensibles se cifran en reposo con {algorithm} en {columns} campos de {models} tipos de registro.",
"statement": "Tus datos sensibles se cifran en reposo con AES-256-GCM."
},
"stored": {
"title": "Qué se almacena",
"description": "Las categorías de datos que contiene esta cuenta.",
"measurements": "Mediciones (peso, presión arterial, pulso, glucosa, sueño, composición corporal)",
"medications": "Medicamentos e historial de tomas",
"moodLabs": "Entradas de estado de ánimo y resultados de laboratorio",
@@ -6087,7 +6089,7 @@
},
"export": {
"title": "Descargar mis datos",
"description": "Exporta tus registros como CSV o JSON, opcionalmente cifrados con frase de contraseña, o genera un historial médico completo.",
"description": "Tus registros en CSV o JSON, con cifrado por frase de contraseña opcional.",
"openExport": "Exportar y copia de seguridad",
"openHealthRecord": "Historial médico"
},
@@ -6106,26 +6108,28 @@
},
"mcp": {
"enableTitle": "Endpoint MCP remoto",
"enableDescription": "Permite que un asistente compatible con MCP (Claude.ai, ChatGPT) lea tus datos de salud mediante un token limitado. Desactivado por defecto.",
"enableDescription": "Permite que un asistente compatible con MCP (Claude.ai, ChatGPT) lea tus datos de salud mediante un token limitado.",
"enabled": "Activado",
"disabled": "Desactivado",
"enableToggleLabel": "Activar el endpoint MCP",
"endpointLabel": "URL del conector",
"connectDescription": "Pega esta URL en la configuración del conector personalizado de tu asistente y autoriza con el token de abajo o inicia sesión cuando se te solicite.",
"tokensTitle": "Tokens del conector",
"tokensDescription": "Genera un token de solo lectura para conectar un asistente. El token se muestra una sola vez.",
"tokensDescription": "Genera un token de solo lectura para conectar un asistente.",
"scopeNote": "Los tokens se limitan a health:read: acceso de solo lectura a tus propios registros. Nunca pueden acceder a funciones de administración.",
"writeScopeToggleLabel": "Permitir registro (health:write)",
"writeScopeNote": "Añade el ámbito health:write para que el asistente pueda registrar nuevas mediciones y entradas de estado de ánimo en tu cuenta. Cada escritura se confirma antes. El token sigue sin poder eliminar ni editar entradas existentes, cambiar medicamentos ni acceder a funciones REST o de administración. Déjalo desactivado para acceso de solo lectura.",
"connectionsTitle": "Asistentes conectados",
"connectionsDescription": "Asistentes que autorizaste mediante el inicio de sesión. Revocar una conexión finaliza su acceso de inmediato, incluida su capacidad de renovación.",
"connectionsDescription": "Asistentes que autorizaste mediante el inicio de sesión.",
"noConnections": "No hay asistentes conectados.",
"connectionConnected": "Conectado",
"connectionLastUsed": "Último uso",
"connectionNeverUsed": "Nunca usado",
"connectionRevoke": "Desconectar",
"connectionRevokeDescription": "Esto finaliza el acceso del asistente de inmediato e impide su renovación. Será necesario autorizarlo de nuevo para volver a conectarlo.",
"connectionRevokeFailed": "No se pudo desconectar el asistente."
"connectionRevokeFailed": "No se pudo desconectar el asistente.",
"tokensDetail": "El token se muestra una sola vez.",
"connectionsDetail": "Revocar una conexión finaliza su acceso de inmediato, incluida su capacidad de renovación."
},
"strava": "Strava",
"stravaDescription": "Conecta tu cuenta de Strava para sincronizar tus actividades con tu historial de entrenamientos.",
@@ -6194,7 +6198,9 @@
"explainer": "Desactivado, los valores se evalúan con la banda de referencia general no diabética. Activado, se evalúan con la banda objetivo glucémica más estrecha de los ADA Standards of Care, y el Coach fundamenta sus comentarios sobre glucosa en esa banda. Tus propios valores objetivo personalizados de abajo siguen teniendo prioridad.",
"disclaimer": "Esto selecciona una banda de referencia. No es un diagnóstico, nunca se deduce de una medición y la aplicación no actúa clínicamente en consecuencia. Qué objetivos se aplican a ti es una pregunta para tu profesional sanitario.",
"error": "No se pudo guardar el ajuste."
}
},
"dangerZoneDetail": "Tu cuenta y las credenciales con las que inicias sesión se conservan.",
"deleteAccountCardDetail": "Esto no se puede deshacer."
},
"admin": {
"title": "Administration",
@@ -6225,7 +6231,7 @@
},
"aiServerKey": {
"title": "Clave de IA del servidor",
"description": "Una clave de proveedor a nivel de operador a la que puede recurrir cualquier cuenta cuando no hay una clave personal configurada.",
"description": "Una clave del operador para las cuentas que no tienen una propia.",
"byokHint": "La clave propia del usuario (BYOK, inicio de sesión de ChatGPT o proveedor local) siempre tiene prioridad — la clave del servidor es el último paso de la cadena de proveedores.",
"consentHint": "Las solicitudes con la clave del servidor requieren el consentimiento de IA activo del usuario; sin un recibo de consentimiento no se envía nada.",
"statusConfigured": "Configurada",
@@ -6331,7 +6337,7 @@
"monitoringTestFailed": "La prueba de monitorización falló",
"assistant": {
"title": "Superficies del asistente",
"description": "Cada conmutador oculta una superficie impulsada por el modelo en toda la aplicación sin retirar la configuración del proveedor. El interruptor maestro fuerza cada opción secundaria a off.",
"description": "Cada conmutador oculta una superficie impulsada por el modelo en toda la aplicación sin retirar la configuración del proveedor.",
"master": {
"title": "Asistente activado",
"description": "Interruptor maestro para cada superficie impulsada por el modelo. Cuando está desactivado, todas las opciones secundarias se fuerzan a off."
@@ -6355,12 +6361,13 @@
},
"modules": {
"title": "Disponibilidad de módulos",
"description": "Desactiva un módulo para todo el servidor. Un módulo desactivado aquí queda apagado para todas las cuentas, sin importar la preferencia personal. Los dominios principales (peso, presión arterial, pulso, medicación) están siempre activos y no se pueden desactivar.",
"saveError": "No se pudo actualizar la disponibilidad de módulos."
"description": "Desactiva un módulo para todo el servidor.",
"saveError": "No se pudo actualizar la disponibilidad de módulos.",
"detail": "Un módulo desactivado aquí queda apagado para todas las cuentas, sin importar la preferencia personal. Los dominios principales (peso, presión arterial, pulso, medicación) están siempre activos y no se pueden desactivar."
},
"invites": {
"title": "Invitaciones",
"description": "Enlaces de invitación para el registro: funcionan incluso con el registro cerrado. El enlace se muestra solo una vez.",
"description": "Enlaces de invitación para el registro: funcionan incluso con el registro cerrado.",
"create": "Crear invitación",
"createTitle": "Crear invitación",
"createDescription": "El enlace permite registrarse incluso con el registro cerrado.",
@@ -6466,7 +6473,7 @@
"outcomeFailed": "Fallido",
"auditDetails": "Detalles",
"deleteAllData": "Eliminar todos los datos globalmente",
"deleteAllDescription": "Elimina todos los datos de mediciones, medicación, integraciones y registros de todos los usuarios. Las cuentas de usuario se conservarán.",
"deleteAllDescription": "Elimina todos los datos de mediciones, medicación, integraciones y registros de todos los usuarios.",
"deleteAllConfirm": "¿Eliminar realmente todos los datos?",
"deleteAllConfirmDescription": "Esto eliminará permanentemente todas las mediciones, medicamentos, eventos de toma, tokens de API, conexiones de Withings, retos de autenticación abiertos, canales de notificación (Telegram, ntfy, Web Push), suscripciones push y eliminaciones de mensajes de Telegram en cola de todos los usuarios. Las cuentas de usuario, el registro de auditoría y los comentarios enviados se conservan para que el borrado siga siendo rastreable y los informes de errores se puedan seguir clasificando.",
"deleteButton": "Eliminar todos los datos",
@@ -6611,7 +6618,7 @@
"app-logs": {
"title": "Registros de la app",
"subtitle": "Muestra los wide-events estructurados más recientes emitidos por este proceso.",
"processNote": "Solo se muestran los registros del proceso de la app; los registros del proceso del worker van a Loki cuando esa integración está configurada. El búfer es volátil y está limitado a 500 entradas.",
"processNote": "Solo se muestran los registros del proceso de la app; los registros del proceso del worker van a Loki cuando esa integración está configurada.",
"filterTraceId": "ID de traza",
"filterAction": "La acción contiene…",
"filterLevel": "Nivel",
@@ -6632,7 +6639,8 @@
"emptyDescription": "Aún no hay eventos que coincidan con los filtros actuales. Pulsa actualizar tras generar algo de tráfico.",
"refresh": "Actualizar",
"eventDetails": "Detalles del evento",
"closeDetails": "Cerrar detalles"
"closeDetails": "Cerrar detalles",
"processDetail": "El búfer es volátil y está limitado a 500 entradas."
},
"backups": {
"title": "Copias de seguridad",
@@ -6699,7 +6707,7 @@
"loading": "Cargando el estado de cifrado...",
"loadError": "No se pudo cargar el estado de cifrado.",
"coverageTitle": "Cobertura",
"coverageDescription": "Cuántas filas cifradas hay bajo cada id de clave. Solo se muestran los id de clave y los recuentos, nunca el material de la clave.",
"coverageDescription": "Cuántas filas cifradas hay bajo cada id de clave.",
"activeKeyId": "Id de clave activa",
"configuredKeys": "Claves configuradas",
"totalRows": "Filas cifradas",
@@ -6707,7 +6715,7 @@
"safeToDropLegacy": "Es seguro eliminar la clave antigua",
"rotationIncomplete": "Aún quedan {count} filas por rotar",
"rotationTitle": "Rotación de claves",
"rotationDescription": "Vuelve a cifrar cada columna cifrada con la clave activa. La herramienta de línea de comandos sigue siendo la vía canónica; este disparador es una comodidad.",
"rotationDescription": "Vuelve a cifrar cada columna cifrada con la clave activa.",
"rotationState": "Estado",
"lastRun": "Última ejecución: {scanned} revisadas, {rotated} rotadas, {errors} errores.",
"rotateNow": "Rotar ahora",
@@ -6730,7 +6738,8 @@
"running": "En curso",
"completed": "Completado",
"failed": "Fallido"
}
},
"coverageDetail": "Solo se muestran los id de clave y los recuentos, nunca el material de la clave."
}
},
"carrier": "Operador",
@@ -6776,7 +6785,8 @@
"countFew": "{count} fallos",
"countOther": "{count} fallos",
"lastFailed": "Último fallo {when}"
}
},
"deleteAllDetail": "Las cuentas de usuario se conservarán."
},
"achievements": {
"title": "Logros",
@@ -7102,7 +7112,7 @@
},
"moodReminder": {
"title": "Recordatorio diario de ánimo",
"description": "Aviso suave a la hora que elijas si aún no has registrado el ánimo del día. Se envía solo por canales ya configurados (Telegram, ntfy, Web Push, Apple Push) y permanece en silencio una vez que has registrado el ánimo hoy.",
"description": "Aviso suave a la hora que elijas si aún no has registrado el ánimo del día.",
"toggleAria": "Alternar recordatorio diario de ánimo",
"statusOn": "Activo a la hora que elijas",
"statusOff": "Desactivado",
@@ -7111,11 +7121,12 @@
"saveError": "No se pudo guardar. Inténtalo de nuevo.",
"hourLabel": "Hora del recordatorio",
"hourAria": "Hora del recordatorio de ánimo",
"hourSavedToast": "Hora del recordatorio actualizada."
"hourSavedToast": "Hora del recordatorio actualizada.",
"detail": "Se envía solo por canales ya configurados (Telegram, ntfy, Web Push, Apple Push) y permanece en silencio una vez que has registrado el ánimo hoy."
},
"coachNudge": {
"title": "Coach proactivo",
"description": "Se pone en contacto cuando tus datos sugieren consultar al Coach, por ejemplo baja adherencia a la medicación o una media semanal de tensión elevada. Los temas y la frecuencia son ajustables.",
"description": "Se pone en contacto cuando tus datos sugieren consultar al Coach.",
"toggleAria": "Activar o desactivar los avisos proactivos del Coach",
"statusOn": "Activo",
"groupsLabel": "Temas",
@@ -7142,7 +7153,7 @@
},
"lowStock": {
"title": "Aviso de existencias bajas",
"description": "Comprueba una vez al día las existencias registradas de tus medicamentos y te avisa cuando alguna cubre menos días que este umbral. Un aviso por escasez; se reactiva tras reponer.",
"description": "Avisa a diario cuando unas existencias cubren menos días que este umbral.",
"toggleAria": "Activar o desactivar los avisos de existencias bajas",
"statusOn": "Activo",
"statusOff": "Desactivado",
@@ -7156,7 +7167,8 @@
"enabledToast": "Avisos de existencias bajas activados.",
"disabledToast": "Avisos de existencias bajas desactivados.",
"saveError": "No se pudo guardar. Inténtalo de nuevo.",
"medicationsLink": "Ir a tus medicamentos"
"medicationsLink": "Ir a tus medicamentos",
"detail": "Un aviso por escasez; se reactiva tras reponer."
},
"eventMedicationReminder": "Recordatorio de medicación",
"eventMedicationReminderDesc": "Se envía a cada hora de dosis programada.",
@@ -9536,7 +9548,7 @@
},
"invite": {
"title": "Invitar a alguien",
"description": "No se comparte nada hasta que acepte. Hasta entonces la invitación está en sus ajustes y no le da nada.",
"description": "No se comparte nada hasta que acepte.",
"identifierLabel": "Usuario o correo electrónico",
"identifierPlaceholder": "El nombre con el que inicia sesión",
"identifierHint": "Necesita una cuenta en esta instancia. No se pueden enviar invitaciones a una dirección que no se haya registrado.",
@@ -9590,7 +9602,7 @@
},
"received": {
"title": "Historiales que puedo abrir",
"description": "Una invitación no te da nada hasta que la aceptas. Puedes devolver el acceso cuando quieras.",
"description": "Una invitación no te da nada hasta que la aceptas.",
"emptyTitle": "Nadie ha compartido un historial contigo",
"emptyDescription": "Cuando alguien te invite, la invitación aparecerá aquí.",
"loadError": "No se pudo cargar la lista.",
@@ -9602,7 +9614,8 @@
"renounce": "Devolver",
"renounceTitle": "¿Devolver el historial de {name}?",
"renounceBody": "Pierdes el acceso de inmediato. Esa persona puede volver a invitarte cuando quiera.",
"renounceConfirm": "Devolver"
"renounceConfirm": "Devolver",
"detail": "Puedes devolver el acceso cuando quieras."
},
"activity": {
"title": "Cuándo se abrió mi historial",
+74 -61
View File
@@ -3925,12 +3925,13 @@
},
"pillOrder": {
"title": "Trier les onglets de navigation",
"description": "Faites glisser les onglets dans l'ordre souhaité et affichez-les ou masquez-les via l'icône en forme d'œil. Les pages de détail masquées disparaissent aussi de la navigation supérieure.",
"description": "Faites glisser les onglets dans l'ordre souhaité et affichez-les ou masquez-les via l'icône en forme d'œil.",
"dragHandle": "Réorganiser",
"saveSuccess": "Ordre des onglets enregistré.",
"saveError": "L'enregistrement de l'ordre des onglets a échoué.",
"save": "Enregistrer",
"empty": "Aucun onglet de navigation disponible pour le moment."
"empty": "Aucun onglet de navigation disponible pour le moment.",
"detail": "Les pages de détail masquées disparaissent aussi de la navigation supérieure."
},
"customize": "Personnaliser les Analyses",
"regenerateRateLimited": "Limite d'actualisation atteinte — réessayez plus tard.",
@@ -4412,7 +4413,7 @@
"settings": {
"researchMode": {
"sectionTitle": "Mode Recherche",
"subtitle": "Affichage optionnel d'une courbe de niveau estimé de médicament GLP-1, dérivée de la pharmacocinétique de population publiée par l'EMA. Lecture seule — jamais une mesure ni un conseil médical.",
"subtitle": "Courbe optionnelle du niveau estimé de GLP-1, d'après les données de l'EMA.",
"toggleLabel": "Afficher la courbe estimée du niveau de médicament",
"disabledStatus": "Désactivé. La courbe est masquée.",
"enabledStaleStatus": "Le disclaimer a été mis à jour. Reconfirmez ci-dessous pour réafficher la courbe.",
@@ -4420,7 +4421,8 @@
"rePromptTitle": "Disclaimer mis à jour",
"rePromptBody": "Le disclaimer du mode Recherche a été mis à jour depuis votre dernière confirmation. Relisez-le et reconfirmez pour garder la courbe visible.",
"rePromptCta": "Reconfirmer le disclaimer",
"disableError": "Impossible de désactiver le mode Recherche. Veuillez réessayer."
"disableError": "Impossible de désactiver le mode Recherche. Veuillez réessayer.",
"subtitleDetail": "Lecture seule — jamais une mesure ni un conseil médical."
},
"shell": {
"sectionsNav": "Sections des réglages",
@@ -4547,7 +4549,8 @@
},
"unavailableTitle": "Indisponible dans ce dossier partagé",
"unavailableDescription": "Cette section des réglages est volontairement indisponible lorsque vous consultez le dossier d’une autre personne.",
"integrationStatusDescription": "État de connexion pour ce dossier géré. Les commandes de connexion ne sont pas disponibles."
"integrationStatusDescription": "État de connexion pour ce dossier géré.",
"integrationStatusDetail": "Les commandes de connexion ne sont pas disponibles."
},
"sections": {
"account": {
@@ -4622,7 +4625,7 @@
"integrationsHint": "Connectez ou déconnectez des sources de données sur la page Intégrations.",
"integrationsHintLink": "Ouvrir les intégrations",
"cardTitle": "Priorité des sources par métrique",
"help": "Si plusieurs sources fournissent la même valeur le même jour, la source en tête compte comme canonique. Les autres restent enregistrées pour la piste d’audit.",
"help": "Si plusieurs sources fournissent la même valeur le même jour, la source en tête compte comme canonique.",
"resetDefaults": "Rétablir les valeurs par défaut",
"resetDefaultsTitle": "Réinitialiser l’ordre des sources ?",
"resetDefaultsBody": "Chaque classement que vous avez défini revient à l’ordre par défaut, pour toutes les mesures et tous les types d’appareil à la fois. L’enregistrement est immédiat et irréversible.",
@@ -4682,13 +4685,15 @@
"subtitle": "Choisissez la source prioritaire en cas de doublon de mesure.",
"nutrients": {
"title": "Apports en nutriments synchronisés",
"description": "Totaux quotidiens synchronisés depuis Apple Health sur les {days} derniers jours. Lecture seule : l'app source reste propriétaire des valeurs.",
"description": "Totaux quotidiens synchronisés depuis Apple Health sur les {days} derniers jours.",
"empty": "Aucune donnée de nutriments synchronisée pour l'instant.",
"latest": "{amount} {unit} le {date}",
"daysWithData": "{count} jours sur {days}",
"moduleHint": "La synchronisation est contrôlée par le module Apports en nutriments.",
"moduleHintLink": "Gérer les modules"
}
"moduleHintLink": "Gérer les modules",
"detail": "Lecture seule : l'app source reste propriétaire des valeurs."
},
"helpDetail": "Les autres restent enregistrées pour la piste d’audit."
},
"ai": {
"title": "Fournisseur d'IA",
@@ -4938,7 +4943,7 @@
"error": "Échec de l'enregistrement. Veuillez réessayer.",
"toggleable": {
"title": "Ce que vous suivez",
"description": "Domaines optionnels. Les modules désactivés disparaissent de la navigation, des tuiles et des analyses ; vos données sont conservées."
"description": "Les modules désactivés disparaissent de la navigation, des tuiles et des analyses ; vos données sont conservées."
},
"manageIn": "Gérer dans {section}",
"operatorDisabled": "Désactivé sur tout le serveur",
@@ -5326,7 +5331,7 @@
"testReasonUnreachable": "Impossible de joindre le fournisseur d’IA.",
"coachMemory": {
"title": "Ce dont le Coach se souvient",
"description": "Les informations durables que vous avez confiées au Coach. Il s'en sert pour garder ses réponses pertinentes. Supprimez tout ce qu'il vaut mieux qu'il oublie.",
"description": "Les informations durables que vous avez confiées au Coach.",
"empty": "Le Coach n'a encore enregistré aucune information. Au fil de vos échanges, il retient les détails durables que vous partagez — vos objectifs, vos affections, vos préférences — et vous pouvez les effacer ici à tout moment.",
"learnedPrefix": "appris {when}",
"categoryPreference": "Préférences",
@@ -5349,11 +5354,12 @@
"forgotToast": "Information oubliée.",
"forgotError": "Impossible d'oublier cette information. Veuillez réessayer.",
"clearedToast": "{count} informations enregistrées oubliées.",
"clearError": "Impossible d'effacer les informations enregistrées. Veuillez réessayer."
"clearError": "Impossible d'effacer les informations enregistrées. Veuillez réessayer.",
"detail": "Il s'en sert pour garder ses réponses pertinentes. Supprimez tout ce qu'il vaut mieux qu'il oublie."
},
"coachReminders": {
"title": "Tes rappels",
"description": "Les choses que tu as demandé au Coach de te rappeler plus tard. Celles qui arrivent à échéance apparaissent ici et chez le Coach.",
"description": "Les choses que tu as demandé au Coach de te rappeler plus tard.",
"empty": "Aucun rappel pour l'instant. Demande au Coach de te rappeler quelque chose et cela apparaîtra ici.",
"loadError": "Impossible de charger tes rappels.",
"dueHeading": "À échéance",
@@ -5374,7 +5380,8 @@
},
"deleteConfirmTitle": "Supprimer ce rappel ?",
"deleteConfirmBody": "Il ne se déclenchera plus. Ce que tu as déjà enregistré reste intact.",
"deleteConfirmAction": "Supprimer"
"deleteConfirmAction": "Supprimer",
"detail": "Celles qui arrivent à échéance apparaissent ici et chez le Coach."
},
"aboutMe": {
"title": "À propos de moi",
@@ -5647,7 +5654,7 @@
"workouts": "Séances"
},
"integrationFallback": {
"description": "Configuré sur ce compte sans carte de réglages dédiée. Cette ligne existe pour qu'une connexion ne puisse pas cesser de fonctionner sans que personne ne le voie.",
"description": "Configuré sur ce compte sans carte de réglages dédiée.",
"lastAttempt": "Dernière tentative {relative}",
"noAttempt": "Aucune tentative de synchronisation enregistrée"
},
@@ -5656,7 +5663,7 @@
"fitbitViewData": "Voir votre sommeil",
"googleHealthViewData": "Voir vos pas",
"apiTokens": "Jetons API",
"apiTokensDescription": "Consultez et révoquez les jetons API pour l’enregistrement externe des prises de médicaments. Les jetons sont émis par médicament depuis son interrupteur de point de terminaison API.",
"apiTokensDescription": "Consultez et révoquez les jetons API pour l’enregistrement externe des prises de médicaments.",
"tokenMintMovedDescription": "Les jetons sont émis par médicament. Ouvrez un médicament et activez son point de terminaison API pour obtenir un jeton limité à ce seul médicament.",
"tokenNamePlaceholder": "Nom du jeton (par exemple Raccourci iPhone)",
"tokenCreated": "Jeton créé — copiez-le maintenant ! Il ne sera plus affiché.",
@@ -5687,14 +5694,14 @@
"apiEndpointAuth": "Authentification",
"apiEndpointExample": "Exemple de corps",
"dangerZone": "Supprimer toutes les données",
"dangerZoneDescription": "Efface l’intégralité de votre dossier de santé et toutes les connexions d’intégration. Votre compte et vos identifiants de connexion sont conservés.",
"dangerZoneDescription": "Efface l’intégralité de votre dossier de santé et toutes les connexions d’intégration.",
"dangerZoneConfirm": "Supprimer toutes les données ?",
"dangerZoneConfirmDescription": "Cette action supprime définitivement tout ce que contient votre dossier : mesures, médicaments, entrées d’humeur, résultats de laboratoire, entrées de cycle, documents, conversations avec l’IA et toutes les connexions d’intégration, ainsi que tout ce qui en découle et tout ce que vous aviez supprimé auparavant. Les liens de partage remis à un cabinet cessent aussitôt de fonctionner. Votre compte reste en place, de même que vos identifiants de connexion et vos réglages d’affichage comme la langue, le fuseau horaire et les unités. Cette action est irréversible.",
"dangerZoneSuccess": "Votre dossier de santé a été supprimé. Votre compte et votre connexion sont inchangés.",
"dangerZoneDeleteFailed": "Échec de la suppression",
"finalDelete": "Supprimer définitivement",
"deleteAccountCardTitle": "Supprimer entièrement le compte",
"deleteAccountCardDescription": "Supprime votre compte ainsi que les clés d’accès, le journal d’audit et les sessions. Cette action est irréversible.",
"deleteAccountCardDescription": "Supprime votre compte ainsi que les clés d’accès, le journal d’audit et les sessions.",
"deleteAccountCta": "Supprimer le compte",
"deleteAccountConfirmTitle": "Supprimer définitivement le compte ?",
"deleteAccountConfirmDescription": "Votre compte, toutes vos données de santé, vos clés d’accès, vos sessions et vos entrées d’audit seront définitivement supprimés. Vous serez déconnecté immédiatement après.",
@@ -5712,8 +5719,6 @@
"changelog": "Changelog",
"docs": "Documentation",
"linksHeading": "Sources et documentation",
"identityDescription": "Version, licence et détails de build de cette instance.",
"linksDescription": "Code source, journal des modifications et documentation.",
"newerAvailable": "Nouvelle version : {tag}"
},
"testConnection": {
@@ -5741,7 +5746,7 @@
"notificationStatus": {
"title": "Fiabilité des canaux",
"description": "État par canal ; « Désactivé automatiquement » arrête les nouvelles tentatives après des erreurs permanentes.",
"emptyDescription": "Aucun canal configuré pour l’instant. Ajoutez-en un dans Canaux de diffusion pour suivre la distribution.",
"emptyDescription": "Ajoutez un canal dans Paramètres › Intégrations › Canaux de diffusion.",
"stateActive": "Actif",
"stateAutoDisabled": "Désactivé automatiquement",
"stateSendingPaused": "Envoi en pause",
@@ -5752,7 +5757,8 @@
"disabledReason": "Raison",
"nextRetry": "Prochaine tentative",
"reEnable": "Réactiver",
"sendTest": "Envoyer un test"
"sendTest": "Envoyer un test",
"emptyTitle": "Aucun canal configuré pour l’instant"
},
"identity": {
"description": "Informations facultatives pour l’export du dossier de santé (couverture PDF + export FHIR). Tous les champs sont facultatifs.",
@@ -5765,7 +5771,7 @@
},
"healthRecord": {
"title": "Exporter le dossier de santé",
"description": "Générez un dossier de santé complet pour votre médecin — en PDF, structuré (FHIR) ou en paquet combiné.",
"description": "Un dossier complet pour votre médecin — en PDF, en FHIR ou les deux.",
"format": "Format",
"format_pdf": "PDF",
"format_fhir": "Structuré (FHIR)",
@@ -5790,10 +5796,10 @@
"errorModuleDisabled": "L'export du dossier est désactivé pour ce compte."
},
"globalExcludedInjectionSitesLabel": "Sites d'injection exclus globalement",
"globalExcludedInjectionSitesHint": "Les sites listés ici ne sont proposés pour aucun médicament et sont refusés lors de l'enregistrement d'une dose, même si un médicament les privilégie.",
"globalExcludedInjectionSitesHint": "Ces sites ne sont jamais proposés et sont refusés à l'enregistrement d'une dose.",
"sharing": {
"createTitle": "Nouveau lien de partage",
"createDescription": "Le lien est en lecture seule et limité dans le temps. Le jeton n'est affiché qu'une fois à la création — conservez-le alors ; il ne peut pas être récupéré.",
"createDescription": "Le lien est en lecture seule et limité dans le temps.",
"label": "Libellé",
"labelPlaceholder": "ex. Dr Martin — bilan annuel",
"range": "Jours d'historique",
@@ -5843,15 +5849,17 @@
"scopeInsuranceExcluded": "Les données d'assurance ne sont jamais partagées via un lien.",
"statusNeedsReselect": "Choisir à nouveau le contenu",
"reselectReason": "Ce lien diffusait un périmètre que personne n'avait choisi ; il a donc été fermé. Créez-en un nouveau et choisissez son contenu.",
"reselectAction": "Créer un remplacement"
"reselectAction": "Créer un remplacement",
"createDetail": "Le jeton n'est affiché qu'une fois à la création — conservez-le alors ; il ne peut pas être récupéré."
},
"genderOther": "Autre",
"cycleTracking": {
"title": "Suivi du cycle",
"description": "Suivez votre cycle menstruel, les prédictions, les symptômes et les analyses de phases. Activé par défaut pour les comptes définis comme féminins ; disponible pour tous.",
"description": "Suivez votre cycle menstruel, les prédictions, les symptômes et les analyses de phases.",
"enable": "Activer le suivi du cycle",
"manage": "Ouvrir le suivi du cycle",
"error": "Impossible de mettre à jour le suivi du cycle."
"error": "Impossible de mettre à jour le suivi du cycle.",
"detail": "Activé par défaut pour les comptes définis comme féminins ; disponible pour tous."
},
"nightscout": "Nightscout",
"nightscoutTag": "CGM",
@@ -5937,7 +5945,7 @@
"coach": {
"activate": {
"title": "Activer le Coach",
"description": "Affiche le bouton et le panneau du Coach. Activé par défaut — désactivez-le pour masquer le Coach partout.",
"description": "Affiche le bouton et le panneau du Coach.",
"toggleAria": "Activer le bouton et le panneau du Coach",
"savedOn": "Coach activé.",
"savedOff": "Coach masqué.",
@@ -6057,19 +6065,13 @@
"signOutEverywhereConfirmBody": "Chaque appareil sauf celui-ci devra se reconnecter. Celui-ci reste connecté. Rien de ce que tu as enregistré n'est affecté."
},
"privacy": {
"intro": {
"title": "Vos données vous appartiennent",
"body": "HealthLog est auto-hébergé. Cette page réunit la façon dont vos données sont protégées, leur durée de conservation, comment les exporter et comment les supprimer."
},
"encryption": {
"title": "Chiffré au repos",
"description": "Les champs sensibles sont chiffrés dans la base de données.",
"summary": "Vos données sensibles sont chiffrées au repos avec {algorithm} sur {columns} champs dans {models} types d'enregistrement.",
"statement": "Vos données sensibles sont chiffrées au repos avec AES-256-GCM."
},
"stored": {
"title": "Ce qui est stocké",
"description": "Les catégories de données que contient ce compte.",
"measurements": "Mesures (poids, tension, pouls, glycémie, sommeil, composition corporelle)",
"medications": "Médicaments et historique des prises",
"moodLabs": "Entrées d'humeur et résultats de laboratoire",
@@ -6087,7 +6089,7 @@
},
"export": {
"title": "Télécharger mes données",
"description": "Exportez vos enregistrements en CSV ou JSON, éventuellement chiffrés par phrase secrète, ou générez un dossier médical complet.",
"description": "Vos enregistrements en CSV ou JSON, chiffrés par phrase secrète si vous voulez.",
"openExport": "Export et sauvegarde",
"openHealthRecord": "Dossier médical"
},
@@ -6106,26 +6108,28 @@
},
"mcp": {
"enableTitle": "Point de terminaison MCP distant",
"enableDescription": "Autorisez un assistant compatible MCP (Claude.ai, ChatGPT) à lire vos données de santé via un jeton restreint. Désactivé par défaut.",
"enableDescription": "Autorisez un assistant compatible MCP (Claude.ai, ChatGPT) à lire vos données de santé via un jeton restreint.",
"enabled": "Activé",
"disabled": "Désactivé",
"enableToggleLabel": "Activer le point de terminaison MCP",
"endpointLabel": "URL du connecteur",
"connectDescription": "Collez cette URL dans la configuration du connecteur personnalisé de votre assistant, puis autorisez avec le jeton ci-dessous ou connectez-vous lorsque vous y êtes invité.",
"tokensTitle": "Jetons du connecteur",
"tokensDescription": "Générez un jeton en lecture seule pour connecter un assistant. Le jeton n'est affiché qu'une seule fois.",
"tokensDescription": "Générez un jeton en lecture seule pour connecter un assistant.",
"scopeNote": "Les jetons sont limités à health:read : accès en lecture seule à vos propres données. Ils ne peuvent jamais atteindre les fonctions d'administration.",
"writeScopeToggleLabel": "Autoriser l'enregistrement (health:write)",
"writeScopeNote": "Ajoute la portée health:write pour que l'assistant puisse enregistrer de nouvelles mesures et entrées d'humeur dans votre compte. Chaque écriture est d'abord confirmée. Le jeton ne peut toujours pas supprimer ni modifier des entrées existantes, changer les médicaments, ni atteindre les fonctions REST ou d'administration. Laissez désactivé pour un accès en lecture seule.",
"connectionsTitle": "Assistants connectés",
"connectionsDescription": "Assistants que vous avez autorisés via la connexion. Révoquer une connexion met fin à son accès immédiatement, y compris sa capacité à se renouveler.",
"connectionsDescription": "Assistants que vous avez autorisés via la connexion.",
"noConnections": "Aucun assistant connecté.",
"connectionConnected": "Connecté",
"connectionLastUsed": "Dernière utilisation",
"connectionNeverUsed": "Jamais utilisé",
"connectionRevoke": "Déconnecter",
"connectionRevokeDescription": "Cela met fin immédiatement à l'accès de l'assistant et l'empêche de se renouveler. Une nouvelle autorisation sera nécessaire pour le reconnecter.",
"connectionRevokeFailed": "Impossible de déconnecter l'assistant."
"connectionRevokeFailed": "Impossible de déconnecter l'assistant.",
"tokensDetail": "Le jeton n'est affiché qu'une seule fois.",
"connectionsDetail": "Révoquer une connexion met fin à son accès immédiatement, y compris sa capacité à se renouveler."
},
"strava": "Strava",
"stravaDescription": "Connecte ton compte Strava pour synchroniser tes activités avec ton historique d'entraînement.",
@@ -6194,7 +6198,9 @@
"explainer": "Désactivé, les valeurs sont comparées à la plage de référence générale non diabétique. Activé, elles sont comparées à la plage cible glycémique plus stricte des ADA Standards of Care, et le Coach fonde ses remarques sur le glucose sur cette plage. Vos propres valeurs cibles personnalisées ci-dessous restent prioritaires.",
"disclaimer": "Ceci sélectionne une plage de référence. Ce n'est pas un diagnostic, ce n'est jamais déduit d'une mesure, et l'application n'en tire aucune conséquence clinique. Les cibles qui s'appliquent à vous relèvent de votre médecin.",
"error": "Le réglage n'a pas pu être enregistré."
}
},
"dangerZoneDetail": "Votre compte et vos identifiants de connexion sont conservés.",
"deleteAccountCardDetail": "Cette action est irréversible."
},
"admin": {
"title": "Administration",
@@ -6225,7 +6231,7 @@
},
"aiServerKey": {
"title": "Clé IA du serveur",
"description": "Une clé de fournisseur à l'échelle de l'opérateur sur laquelle chaque compte peut se rabattre lorsqu'aucune clé personnelle n'est configurée.",
"description": "Une clé de l'opérateur pour les comptes qui n'en ont pas configuré une.",
"byokHint": "La clé personnelle de l'utilisateur (BYOK, connexion ChatGPT ou fournisseur local) a toujours la priorité — la clé du serveur est la dernière étape de la chaîne de fournisseurs.",
"consentHint": "Les requêtes via la clé du serveur nécessitent le consentement IA actif de l'utilisateur ; sans justificatif de consentement, rien n'est envoyé.",
"statusConfigured": "Configurée",
@@ -6331,7 +6337,7 @@
"monitoringTestFailed": "Échec du test de surveillance",
"assistant": {
"title": "Surfaces d'assistance",
"description": "Chaque bascule masque une surface pilotée par le modèle dans toute l'application sans retirer la configuration du fournisseur. Le commutateur principal force chaque option secondaire à off.",
"description": "Chaque bascule masque une surface pilotée par le modèle dans toute l'application sans retirer la configuration du fournisseur.",
"master": {
"title": "Assistant activé",
"description": "Commutateur principal pour chaque surface pilotée par le modèle. Lorsqu'il est désactivé, toutes les options secondaires ci-dessous sont forcées à off."
@@ -6355,12 +6361,13 @@
},
"modules": {
"title": "Disponibilité des modules",
"description": "Désactivez un module pour tout le serveur. Un module désactivé ici est coupé pour chaque compte, quelle que soit la préférence personnelle. Les domaines essentiels (poids, tension, pouls, médicaments) restent toujours actifs et ne peuvent pas être désactivés.",
"saveError": "Impossible de mettre à jour la disponibilité des modules."
"description": "Désactivez un module pour tout le serveur.",
"saveError": "Impossible de mettre à jour la disponibilité des modules.",
"detail": "Un module désactivé ici est coupé pour chaque compte, quelle que soit la préférence personnelle. Les domaines essentiels (poids, tension, pouls, médicaments) restent toujours actifs et ne peuvent pas être désactivés."
},
"invites": {
"title": "Invitations",
"description": "Liens d'invitation pour l'inscription — ils fonctionnent même lorsque les inscriptions sont fermées. Le lien n'est affiché qu'une seule fois.",
"description": "Liens d'invitation pour l'inscription — ils fonctionnent même lorsque les inscriptions sont fermées.",
"create": "Créer une invitation",
"createTitle": "Créer une invitation",
"createDescription": "Le lien permet une inscription même lorsque les inscriptions sont fermées.",
@@ -6466,7 +6473,7 @@
"outcomeFailed": "Échec",
"auditDetails": "Détails",
"deleteAllData": "Supprimer toutes les données globalement",
"deleteAllDescription": "Supprime toutes les données de mesures, de médicaments, d'intégrations et de journaux pour tous les utilisateurs. Les comptes utilisateurs sont conservés.",
"deleteAllDescription": "Supprime toutes les données de mesures, de médicaments, d'intégrations et de journaux pour tous les utilisateurs.",
"deleteAllConfirm": "Vraiment supprimer toutes les données ?",
"deleteAllConfirmDescription": "Cette action supprimera définitivement toutes les mesures, médicaments, événements de prise, jetons d'API, connexions Withings, défis d'authentification ouverts, canaux de notification (Telegram, ntfy, Web Push), abonnements push et suppressions de messages Telegram en file d'attente pour tous les utilisateurs. Les comptes utilisateurs, le journal d'audit et les retours soumis sont conservés afin que l'effacement reste traçable et que les rapports de bug restent triables.",
"deleteButton": "Supprimer toutes les données",
@@ -6611,7 +6618,7 @@
"app-logs": {
"title": "Journaux de l'app",
"subtitle": "Affichez les wide-events structurés les plus récents émis par ce processus.",
"processNote": "Seuls les journaux du processus de l'app sont affichés — ceux du processus worker vont vers Loki lorsque cette intégration est configurée. Le tampon est volatil et limité à 500 entrées.",
"processNote": "Seuls les journaux du processus de l'app sont affichés — ceux du processus worker vont vers Loki lorsque cette intégration est configurée.",
"filterTraceId": "ID de trace",
"filterAction": "L'action contient…",
"filterLevel": "Niveau",
@@ -6632,7 +6639,8 @@
"emptyDescription": "Aucun événement ne correspond encore aux filtres actuels. Cliquez sur Actualiser après avoir généré du trafic.",
"refresh": "Actualiser",
"eventDetails": "Détails de l'événement",
"closeDetails": "Fermer les détails"
"closeDetails": "Fermer les détails",
"processDetail": "Le tampon est volatil et limité à 500 entrées."
},
"backups": {
"title": "Sauvegardes",
@@ -6699,7 +6707,7 @@
"loading": "Chargement de l'état du chiffrement...",
"loadError": "Échec du chargement de l'état du chiffrement.",
"coverageTitle": "Couverture",
"coverageDescription": "Combien de lignes chiffrées relèvent de chaque id de clé. Seuls les id de clé et les décomptes sont affichés, jamais le matériel de clé.",
"coverageDescription": "Combien de lignes chiffrées relèvent de chaque id de clé.",
"activeKeyId": "Id de clé active",
"configuredKeys": "Clés configurées",
"totalRows": "Lignes chiffrées",
@@ -6707,7 +6715,7 @@
"safeToDropLegacy": "La clé ancienne peut être retirée",
"rotationIncomplete": "{count} lignes restent à faire tourner",
"rotationTitle": "Rotation des clés",
"rotationDescription": "Re-chiffre chaque colonne chiffrée avec la clé active. L'outil en ligne de commande reste la voie canonique ; ce déclencheur est un confort.",
"rotationDescription": "Re-chiffre chaque colonne chiffrée avec la clé active.",
"rotationState": "État",
"lastRun": "Dernière exécution : {scanned} analysées, {rotated} tournées, {errors} erreurs.",
"rotateNow": "Faire tourner maintenant",
@@ -6730,7 +6738,8 @@
"running": "En cours",
"completed": "Terminé",
"failed": "Échoué"
}
},
"coverageDetail": "Seuls les id de clé et les décomptes sont affichés, jamais le matériel de clé."
}
},
"carrier": "Opérateur",
@@ -6776,7 +6785,8 @@
"countFew": "{count} échecs",
"countOther": "{count} échecs",
"lastFailed": "Dernier échec {when}"
}
},
"deleteAllDetail": "Les comptes utilisateurs sont conservés."
},
"achievements": {
"title": "Réussites",
@@ -7102,7 +7112,7 @@
},
"moodReminder": {
"title": "Rappel quotidien d’humeur",
"description": "Petit rappel à l’heure que tu as choisie si tu n’as pas encore noté ton humeur du jour. Utilise uniquement les canaux déjà configurés (Telegram, ntfy, Web Push, Apple Push) et reste silencieux dès que tu as noté ton humeur aujourd’hui.",
"description": "Petit rappel à l’heure que tu as choisie si tu n’as pas encore noté ton humeur du jour.",
"toggleAria": "Activer / désactiver le rappel quotidien d’humeur",
"statusOn": "Actif à l’heure que tu as choisie",
"statusOff": "Désactivé",
@@ -7111,11 +7121,12 @@
"saveError": "Échec de l’enregistrement. Réessayez.",
"hourLabel": "Heure du rappel",
"hourAria": "Heure du rappel d’humeur",
"hourSavedToast": "Heure du rappel mise à jour."
"hourSavedToast": "Heure du rappel mise à jour.",
"detail": "Utilise uniquement les canaux déjà configurés (Telegram, ntfy, Web Push, Apple Push) et reste silencieux dès que tu as noté ton humeur aujourd’hui."
},
"coachNudge": {
"title": "Coach proactif",
"description": "Vous contacte lorsque vos données suggèrent de consulter le Coach — par exemple une faible observance ou une moyenne hebdomadaire de tension élevée. Les thèmes et la fréquence sont réglables.",
"description": "Vous contacte lorsque vos données suggèrent de consulter le Coach.",
"toggleAria": "Activer ou désactiver les rappels proactifs du Coach",
"statusOn": "Actif",
"groupsLabel": "Thèmes",
@@ -7142,7 +7153,7 @@
},
"lowStock": {
"title": "Alerte de stock faible",
"description": "Vérifie une fois par jour les stocks suivis de vos médicaments et vous prévient quand l’un d’eux couvre moins de jours que ce seuil. Une notification par pénurie ; elle se réarme après un réapprovisionnement.",
"description": "Signale chaque jour un stock couvrant moins de jours que ce seuil.",
"toggleAria": "Activer ou désactiver les alertes de stock faible",
"statusOn": "Actif",
"statusOff": "Désactivé",
@@ -7156,7 +7167,8 @@
"enabledToast": "Alertes de stock faible activées.",
"disabledToast": "Alertes de stock faible désactivées.",
"saveError": "Échec de l’enregistrement. Réessayez.",
"medicationsLink": "Voir tes médicaments"
"medicationsLink": "Voir tes médicaments",
"detail": "Une notification par pénurie ; elle se réarme après un réapprovisionnement."
},
"eventMedicationReminder": "Rappel de médicament",
"eventMedicationReminderDesc": "Envoyé à chaque heure de prise prévue.",
@@ -9536,7 +9548,7 @@
},
"invite": {
"title": "Inviter quelqu'un",
"description": "Rien n'est partagé avant son acceptation. D'ici là, l'invitation reste dans ses réglages et ne lui donne rien.",
"description": "Rien n'est partagé avant son acceptation.",
"identifierLabel": "Nom d'utilisateur ou e-mail",
"identifierPlaceholder": "Le nom avec lequel elle se connecte",
"identifierHint": "Cette personne a besoin d'un compte sur cette instance. Une invitation ne peut pas être envoyée à une adresse non inscrite.",
@@ -9590,7 +9602,7 @@
},
"received": {
"title": "Dossiers que je peux ouvrir",
"description": "Une invitation ne vous donne rien avant que vous l'acceptiez. Vous pouvez rendre l'accès quand vous voulez.",
"description": "Une invitation ne vous donne rien avant que vous l'acceptiez.",
"emptyTitle": "Personne n'a partagé de dossier avec vous",
"emptyDescription": "Quand quelqu'un vous invite, l'invitation apparaît ici.",
"loadError": "La liste n'a pas pu être chargée.",
@@ -9602,7 +9614,8 @@
"renounce": "Rendre",
"renounceTitle": "Rendre le dossier de {name} ?",
"renounceBody": "Vous perdez l'accès immédiatement. Cette personne peut vous inviter de nouveau à tout moment.",
"renounceConfirm": "Rendre"
"renounceConfirm": "Rendre",
"detail": "Vous pouvez rendre l'accès quand vous voulez."
},
"activity": {
"title": "Quand mon dossier a été ouvert",
+74 -61
View File
@@ -3925,12 +3925,13 @@
},
"pillOrder": {
"title": "Ordina le schede di navigazione",
"description": "Trascina le schede nell'ordine desiderato e mostrale o nascondile con l'icona dell'occhio. Le pagine di dettaglio nascoste vengono rimosse anche dalla navigazione in alto.",
"description": "Trascina le schede nell'ordine desiderato e mostrale o nascondile con l'icona dell'occhio.",
"dragHandle": "Riordina",
"saveSuccess": "Ordine delle schede salvato.",
"saveError": "Salvataggio dell'ordine delle schede non riuscito.",
"save": "Salva",
"empty": "Nessuna scheda di navigazione ancora disponibile."
"empty": "Nessuna scheda di navigazione ancora disponibile.",
"detail": "Le pagine di dettaglio nascoste vengono rimosse anche dalla navigazione in alto."
},
"customize": "Personalizza gli Approfondimenti",
"regenerateRateLimited": "Limite di aggiornamento raggiunto — riprova più tardi.",
@@ -4412,7 +4413,7 @@
"settings": {
"researchMode": {
"sectionTitle": "Modalità Ricerca",
"subtitle": "Visualizzazione opzionale di una curva stimata del livello del farmaco GLP-1, derivata dalla farmacocinetica di popolazione pubblicata dall'EMA. Solo lettura: mai una misurazione né un consiglio medico.",
"subtitle": "Curva opzionale del livello stimato di GLP-1, da dati pubblicati dall'EMA.",
"toggleLabel": "Mostra la curva stimata del livello del farmaco",
"disabledStatus": "Disattivata. La curva è nascosta.",
"enabledStaleStatus": "Il disclaimer è stato aggiornato. Riconferma sotto per rivedere la curva.",
@@ -4420,7 +4421,8 @@
"rePromptTitle": "Disclaimer aggiornato",
"rePromptBody": "Il disclaimer della modalità Ricerca è stato aggiornato dall'ultima tua conferma. Rileggilo e riconferma per mantenere visibile la curva.",
"rePromptCta": "Riconferma disclaimer",
"disableError": "Impossibile disattivare la modalità Ricerca. Riprova."
"disableError": "Impossibile disattivare la modalità Ricerca. Riprova.",
"subtitleDetail": "Solo lettura: mai una misurazione né un consiglio medico."
},
"shell": {
"sectionsNav": "Sezioni delle impostazioni",
@@ -4547,7 +4549,8 @@
},
"unavailableTitle": "Non disponibile in questo record condiviso",
"unavailableDescription": "Questa area delle Impostazioni non è disponibile intenzionalmente mentre visualizzi il record di un’altra persona.",
"integrationStatusDescription": "Stato di connessione per questo record gestito. I controlli di connessione non sono disponibili."
"integrationStatusDescription": "Stato di connessione per questo record gestito.",
"integrationStatusDetail": "I controlli di connessione non sono disponibili."
},
"sections": {
"account": {
@@ -4622,7 +4625,7 @@
"integrationsHint": "Collega o scollega le fonti dati nella pagina Integrazioni.",
"integrationsHintLink": "Apri le integrazioni",
"cardTitle": "Priorità delle fonti per metrica",
"help": "Se più fonti forniscono lo stesso valore nello stesso giorno, la fonte in cima conta come canonica. Le altre restano memorizzate per la traccia di controllo.",
"help": "Se più fonti forniscono lo stesso valore nello stesso giorno, la fonte in cima conta come canonica.",
"resetDefaults": "Ripristina i valori predefiniti",
"resetDefaultsTitle": "Reimpostare l’ordine delle fonti?",
"resetDefaultsBody": "Ogni gerarchia che hai impostato torna all’ordine predefinito, per tutte le metriche e tutti i tipi di dispositivo insieme. Viene salvato subito e non può essere recuperato.",
@@ -4682,13 +4685,15 @@
"subtitle": "Decidi quale fonte prevale quando due riportano la stessa metrica.",
"nutrients": {
"title": "Assunzione di nutrienti sincronizzata",
"description": "Totali giornalieri sincronizzati da Apple Health negli ultimi {days} giorni. Sola lettura: i valori appartengono all'app di origine.",
"description": "Totali giornalieri sincronizzati da Apple Health negli ultimi {days} giorni.",
"empty": "Nessun dato sui nutrienti ancora sincronizzato.",
"latest": "{amount} {unit} il {date}",
"daysWithData": "{count} giorni su {days}",
"moduleHint": "La sincronizzazione è controllata dal modulo Assunzione di nutrienti.",
"moduleHintLink": "Gestisci moduli"
}
"moduleHintLink": "Gestisci moduli",
"detail": "Sola lettura: i valori appartengono all'app di origine."
},
"helpDetail": "Le altre restano memorizzate per la traccia di controllo."
},
"ai": {
"title": "Provider IA",
@@ -4938,7 +4943,7 @@
"error": "Salvataggio non riuscito. Riprova.",
"toggleable": {
"title": "Cosa monitori",
"description": "Ambiti opzionali. I moduli disattivati spariscono da navigazione, riquadri e analisi; i tuoi dati restano intatti."
"description": "I moduli disattivati spariscono da navigazione, riquadri e analisi; i tuoi dati restano intatti."
},
"manageIn": "Gestisci in {section}",
"operatorDisabled": "Disattivato a livello di server",
@@ -5326,7 +5331,7 @@
"testReasonUnreachable": "Impossibile raggiungere il provider IA.",
"coachMemory": {
"title": "Ciò che il Coach ricorda",
"description": "Informazioni durature che hai comunicato al Coach. Le usa per mantenere pertinenti le sue risposte. Rimuovi tutto ciò che preferisci dimentichi.",
"description": "Informazioni durature che hai comunicato al Coach.",
"empty": "Il Coach non ha ancora memorizzato alcuna informazione. Mentre chatti, ricorda i dettagli duraturi che condividi — i tuoi obiettivi, le condizioni, le preferenze — e puoi cancellarli qui in qualsiasi momento.",
"learnedPrefix": "appreso {when}",
"categoryPreference": "Preferenze",
@@ -5349,11 +5354,12 @@
"forgotToast": "Informazione dimenticata.",
"forgotError": "Impossibile dimenticare questa informazione. Riprova.",
"clearedToast": "Dimenticate {count} informazioni memorizzate.",
"clearError": "Impossibile cancellare le informazioni memorizzate. Riprova."
"clearError": "Impossibile cancellare le informazioni memorizzate. Riprova.",
"detail": "Le usa per mantenere pertinenti le sue risposte. Rimuovi tutto ciò che preferisci dimentichi."
},
"coachReminders": {
"title": "I tuoi promemoria",
"description": "Cose che hai chiesto al Coach di riprendere più tardi. Quelle in scadenza compaiono qui e nel Coach.",
"description": "Cose che hai chiesto al Coach di riprendere più tardi.",
"empty": "Ancora nessun promemoria. Chiedi al Coach di ricordarti qualcosa e comparirà qui.",
"loadError": "Impossibile caricare i tuoi promemoria.",
"dueHeading": "In scadenza",
@@ -5374,7 +5380,8 @@
},
"deleteConfirmTitle": "Eliminare questo promemoria?",
"deleteConfirmBody": "Non verrà più mostrato. Quanto hai già registrato resta invariato.",
"deleteConfirmAction": "Elimina"
"deleteConfirmAction": "Elimina",
"detail": "Quelle in scadenza compaiono qui e nel Coach."
},
"aboutMe": {
"title": "Su di me",
@@ -5647,7 +5654,7 @@
"workouts": "Allenamenti"
},
"integrationFallback": {
"description": "Configurato su questo account ma senza una scheda di impostazioni propria. Questa riga esiste perché una connessione non possa smettere di funzionare senza che nessuno se ne accorga.",
"description": "Configurato su questo account ma senza una scheda di impostazioni propria.",
"lastAttempt": "Ultimo tentativo {relative}",
"noAttempt": "Nessun tentativo di sincronizzazione registrato"
},
@@ -5656,7 +5663,7 @@
"fitbitViewData": "Vedi il tuo sonno",
"googleHealthViewData": "Vedi i tuoi passi",
"apiTokens": "Token API",
"apiTokensDescription": "Rivedi e revoca i token API per la registrazione esterna delle assunzioni di farmaci. I token vengono emessi per singolo farmaco dal relativo interruttore dell’endpoint API.",
"apiTokensDescription": "Rivedi e revoca i token API per la registrazione esterna delle assunzioni di farmaci.",
"tokenMintMovedDescription": "I token vengono emessi per singolo farmaco. Apri un farmaco e attiva il suo endpoint API per ottenere un token valido solo per quel farmaco.",
"tokenNamePlaceholder": "Nome del token (ad es. Comando rapido iPhone)",
"tokenCreated": "Token creato — copialo ora! Non verrà mostrato di nuovo.",
@@ -5687,14 +5694,14 @@
"apiEndpointAuth": "Autenticazione",
"apiEndpointExample": "Esempio di body",
"dangerZone": "Elimina tutti i dati",
"dangerZoneDescription": "Cancella l’intera cartella sanitaria e ogni connessione di integrazione. Il tuo account e le credenziali con cui accedi restano.",
"dangerZoneDescription": "Cancella l’intera cartella sanitaria e ogni connessione di integrazione.",
"dangerZoneConfirm": "Eliminare tutti i dati?",
"dangerZoneConfirmDescription": "Questa azione elimina definitivamente tutto ciò che contiene la tua cartella: misurazioni, farmaci, voci dell’umore, risultati di laboratorio, voci del ciclo, documenti, conversazioni con l’IA e ogni connessione di integrazione, insieme a tutto ciò che ne deriva e a tutto quello che avevi eliminato prima. I link di condivisione consegnati a uno studio medico smettono subito di funzionare. Il tuo account resta, insieme alle credenziali con cui accedi e alle impostazioni di visualizzazione come lingua, fuso orario e unità. Non può essere annullato.",
"dangerZoneSuccess": "La tua cartella sanitaria è stata eliminata. Account e accesso restano invariati.",
"dangerZoneDeleteFailed": "Eliminazione non riuscita",
"finalDelete": "Elimina definitivamente",
"deleteAccountCardTitle": "Elimina completamente l’account",
"deleteAccountCardDescription": "Elimina il tuo account insieme a passkey, registro di controllo e sessioni. Questa azione non può essere annullata.",
"deleteAccountCardDescription": "Elimina il tuo account insieme a passkey, registro di controllo e sessioni.",
"deleteAccountCta": "Elimina account",
"deleteAccountConfirmTitle": "Eliminare definitivamente l’account?",
"deleteAccountConfirmDescription": "Il tuo account, tutti i dati sanitari, le passkey, le sessioni e le voci di audit verranno eliminati definitivamente. Verrai disconnesso subito dopo.",
@@ -5712,8 +5719,6 @@
"changelog": "Changelog",
"docs": "Documentazione",
"linksHeading": "Fonti e documentazione",
"identityDescription": "Versione, licenza e dettagli di build di questa istanza.",
"linksDescription": "Codice sorgente, registro delle modifiche e documentazione.",
"newerAvailable": "Nuova versione: {tag}"
},
"testConnection": {
@@ -5741,7 +5746,7 @@
"notificationStatus": {
"title": "Affidabilità dei canali",
"description": "Stato per canale; «Disattivato automaticamente» interrompe i tentativi dopo errori permanenti.",
"emptyDescription": "Nessun canale ancora configurato. Aggiungine uno in Canali di recapito per monitorarne la consegna.",
"emptyDescription": "Aggiungi un canale in Impostazioni › Integrazioni › Canali di recapito.",
"stateActive": "Attivo",
"stateAutoDisabled": "Disattivato automaticamente",
"stateSendingPaused": "Invio in pausa",
@@ -5752,7 +5757,8 @@
"disabledReason": "Motivo",
"nextRetry": "Prossimo tentativo",
"reEnable": "Riattiva",
"sendTest": "Invia prova"
"sendTest": "Invia prova",
"emptyTitle": "Nessun canale ancora configurato"
},
"identity": {
"description": "Dati facoltativi per l’esportazione della cartella clinica (copertina PDF + esportazione FHIR). Tutti i campi sono facoltativi.",
@@ -5765,7 +5771,7 @@
},
"healthRecord": {
"title": "Esporta cartella clinica",
"description": "Genera una cartella clinica completa per il medico — come PDF, strutturata (FHIR) o pacchetto combinato.",
"description": "Una cartella completa per il medico — in PDF, in FHIR o entrambi.",
"format": "Formato",
"format_pdf": "PDF",
"format_fhir": "Strutturato (FHIR)",
@@ -5790,10 +5796,10 @@
"errorModuleDisabled": "L'esportazione della cartella è disattivata per questo account."
},
"globalExcludedInjectionSitesLabel": "Siti di iniezione esclusi globalmente",
"globalExcludedInjectionSitesHint": "I siti elencati qui non vengono proposti per alcun farmaco e vengono rifiutati al momento della registrazione di una dose, anche se un farmaco li preferisce.",
"globalExcludedInjectionSitesHint": "Questi siti non vengono mai proposti e sono rifiutati quando registri una dose.",
"sharing": {
"createTitle": "Nuovo link di condivisione",
"createDescription": "Il link è di sola lettura e a tempo. Il token viene mostrato una sola volta alla creazione — salvalo subito; non può essere recuperato.",
"createDescription": "Il link è di sola lettura e a tempo.",
"label": "Etichetta",
"labelPlaceholder": "es. Dott. Rossi — controllo annuale",
"range": "Giorni di cronologia",
@@ -5843,15 +5849,17 @@
"scopeInsuranceExcluded": "I dati assicurativi non vengono mai condivisi tramite link.",
"statusNeedsReselect": "Scegli di nuovo il contenuto",
"reselectReason": "Questo link mostrava un ambito che nessuno aveva scelto, perciò è stato chiuso. Creane uno nuovo e scegli cosa deve contenere.",
"reselectAction": "Crea un sostituto"
"reselectAction": "Crea un sostituto",
"createDetail": "Il token viene mostrato una sola volta alla creazione — salvalo subito; non può essere recuperato."
},
"genderOther": "Altro",
"cycleTracking": {
"title": "Monitoraggio del ciclo",
"description": "Monitora il ciclo mestruale, le previsioni, i sintomi e gli approfondimenti sulle fasi. Attivo per impostazione predefinita per gli account impostati come femminili; disponibile per tutti.",
"description": "Monitora il ciclo mestruale, le previsioni, i sintomi e gli approfondimenti sulle fasi.",
"enable": "Attiva il monitoraggio del ciclo",
"manage": "Apri il monitoraggio del ciclo",
"error": "Impossibile aggiornare il monitoraggio del ciclo."
"error": "Impossibile aggiornare il monitoraggio del ciclo.",
"detail": "Attivo per impostazione predefinita per gli account impostati come femminili; disponibile per tutti."
},
"nightscout": "Nightscout",
"nightscoutTag": "CGM",
@@ -5937,7 +5945,7 @@
"coach": {
"activate": {
"title": "Attiva il Coach",
"description": "Mostra il pulsante e il pannello del Coach. Attivo per impostazione predefinita: disattivalo per nascondere il Coach ovunque.",
"description": "Mostra il pulsante e il pannello del Coach.",
"toggleAria": "Attiva il pulsante e il pannello del Coach",
"savedOn": "Coach attivato.",
"savedOff": "Coach nascosto.",
@@ -6057,19 +6065,13 @@
"signOutEverywhereConfirmBody": "Ogni dispositivo tranne questo dovrà accedere di nuovo. Questo resta connesso. Nulla di ciò che hai registrato viene toccato."
},
"privacy": {
"intro": {
"title": "I tuoi dati restano tuoi",
"body": "HealthLog è self-hosted. Questa pagina riunisce come sono protetti i tuoi dati, per quanto tempo vengono conservati, come esportarli e come eliminarli."
},
"encryption": {
"title": "Cifrati a riposo",
"description": "I campi sensibili sono cifrati nel database.",
"summary": "I tuoi dati sensibili sono cifrati a riposo con {algorithm} su {columns} campi in {models} tipi di record.",
"statement": "I tuoi dati sensibili sono cifrati a riposo con AES-256-GCM."
},
"stored": {
"title": "Cosa viene memorizzato",
"description": "Le categorie di dati contenute in questo account.",
"measurements": "Misurazioni (peso, pressione, polso, glicemia, sonno, composizione corporea)",
"medications": "Farmaci e cronologia delle assunzioni",
"moodLabs": "Voci dell'umore e referti di laboratorio",
@@ -6087,7 +6089,7 @@
},
"export": {
"title": "Scarica i miei dati",
"description": "Esporta i tuoi record come CSV o JSON, eventualmente cifrati con passphrase, oppure genera un fascicolo sanitario completo.",
"description": "I tuoi record in CSV o JSON, se vuoi cifrati con una passphrase.",
"openExport": "Esportazione e backup",
"openHealthRecord": "Fascicolo sanitario"
},
@@ -6106,26 +6108,28 @@
},
"mcp": {
"enableTitle": "Endpoint MCP remoto",
"enableDescription": "Consenti a un assistente compatibile con MCP (Claude.ai, ChatGPT) di leggere i tuoi dati sulla salute tramite un token limitato. Disattivato per impostazione predefinita.",
"enableDescription": "Consenti a un assistente compatibile con MCP (Claude.ai, ChatGPT) di leggere i tuoi dati sulla salute tramite un token limitato.",
"enabled": "Attivo",
"disabled": "Inattivo",
"enableToggleLabel": "Attiva l'endpoint MCP",
"endpointLabel": "URL del connettore",
"connectDescription": "Incolla questo URL nella configurazione del connettore personalizzato del tuo assistente e autorizza con il token qui sotto o accedi quando richiesto.",
"tokensTitle": "Token del connettore",
"tokensDescription": "Genera un token in sola lettura per collegare un assistente. Il token viene mostrato una sola volta.",
"tokensDescription": "Genera un token in sola lettura per collegare un assistente.",
"scopeNote": "I token sono limitati a health:read: accesso in sola lettura ai tuoi dati. Non possono mai raggiungere funzioni di amministrazione.",
"writeScopeToggleLabel": "Consenti la registrazione (health:write)",
"writeScopeNote": "Aggiunge l'ambito health:write così l'assistente può registrare nuove misurazioni e voci dell'umore nel tuo account. Ogni scrittura viene prima confermata. Il token non può comunque eliminare o modificare voci esistenti, cambiare i farmaci o raggiungere funzioni REST o di amministrazione. Lascia disattivato per l'accesso in sola lettura.",
"connectionsTitle": "Assistenti collegati",
"connectionsDescription": "Assistenti che hai autorizzato tramite l'accesso. Revocare una connessione ne termina subito l'accesso, inclusa la capacità di rinnovarsi.",
"connectionsDescription": "Assistenti che hai autorizzato tramite l'accesso.",
"noConnections": "Nessun assistente collegato.",
"connectionConnected": "Collegato",
"connectionLastUsed": "Ultimo utilizzo",
"connectionNeverUsed": "Mai usato",
"connectionRevoke": "Disconnetti",
"connectionRevokeDescription": "Questo termina immediatamente l'accesso dell'assistente e ne impedisce il rinnovo. Per riconnetterlo sarà necessaria una nuova autorizzazione.",
"connectionRevokeFailed": "Impossibile disconnettere l'assistente."
"connectionRevokeFailed": "Impossibile disconnettere l'assistente.",
"tokensDetail": "Il token viene mostrato una sola volta.",
"connectionsDetail": "Revocare una connessione ne termina subito l'accesso, inclusa la capacità di rinnovarsi."
},
"strava": "Strava",
"stravaDescription": "Collega il tuo account Strava per sincronizzare le tue attività con la cronologia degli allenamenti.",
@@ -6194,7 +6198,9 @@
"explainer": "Disattivato, i valori vengono valutati con la banda di riferimento generale non diabetica. Attivato, vengono valutati con la banda obiettivo glicemica più stretta degli ADA Standards of Care, e il Coach fonda su quella banda le sue osservazioni sulla glicemia. I tuoi valori obiettivo personalizzati qui sotto hanno comunque la precedenza.",
"disclaimer": "Questo seleziona una banda di riferimento. Non è una diagnosi, non viene mai dedotto da una misurazione e l'app non agisce clinicamente di conseguenza. Quali obiettivi valgano per te è una domanda per il tuo medico.",
"error": "Non è stato possibile salvare l'impostazione."
}
},
"dangerZoneDetail": "Il tuo account e le credenziali con cui accedi restano.",
"deleteAccountCardDetail": "Questa azione non può essere annullata."
},
"admin": {
"title": "Administration",
@@ -6225,7 +6231,7 @@
},
"aiServerKey": {
"title": "Chiave IA del server",
"description": "Una chiave del provider a livello di operatore su cui ogni account può ripiegare quando non è configurata una chiave personale.",
"description": "Una chiave dell'operatore per gli account che non ne hanno una propria.",
"byokHint": "La chiave personale dell'utente (BYOK, accesso ChatGPT o provider locale) ha sempre la precedenza — la chiave del server è l'ultimo passo della catena dei provider.",
"consentHint": "Le richieste tramite la chiave del server richiedono il consenso IA attivo dell'utente; senza una ricevuta di consenso non viene inviato nulla.",
"statusConfigured": "Configurata",
@@ -6331,7 +6337,7 @@
"monitoringTestFailed": "Test di monitoraggio non riuscito",
"assistant": {
"title": "Superfici dell'assistente",
"description": "Ogni interruttore nasconde una superficie guidata dal modello in tutta l'app senza rimuovere la configurazione del provider. Il master forza ogni opzione secondaria su off.",
"description": "Ogni interruttore nasconde una superficie guidata dal modello in tutta l'app senza rimuovere la configurazione del provider.",
"master": {
"title": "Assistente attivo",
"description": "Interruttore master per ogni superficie guidata dal modello. Quando spento, ogni opzione secondaria qui sotto è forzata su off."
@@ -6355,12 +6361,13 @@
},
"modules": {
"title": "Disponibilità dei moduli",
"description": "Disattiva un modulo per l'intero server. Un modulo disattivato qui è spento per ogni account, indipendentemente dalle preferenze personali. I domini principali (peso, pressione, polso, farmaci) sono sempre attivi e non possono essere disattivati.",
"saveError": "Impossibile aggiornare la disponibilità dei moduli."
"description": "Disattiva un modulo per l'intero server.",
"saveError": "Impossibile aggiornare la disponibilità dei moduli.",
"detail": "Un modulo disattivato qui è spento per ogni account, indipendentemente dalle preferenze personali. I domini principali (peso, pressione, polso, farmaci) sono sempre attivi e non possono essere disattivati."
},
"invites": {
"title": "Inviti",
"description": "Link di invito per la registrazione: funzionano anche quando la registrazione è chiusa. Il link viene mostrato una sola volta.",
"description": "Link di invito per la registrazione: funzionano anche quando la registrazione è chiusa.",
"create": "Crea invito",
"createTitle": "Crea invito",
"createDescription": "Il link consente una registrazione anche quando la registrazione è chiusa.",
@@ -6466,7 +6473,7 @@
"outcomeFailed": "Non riuscito",
"auditDetails": "Dettagli",
"deleteAllData": "Elimina tutti i dati globalmente",
"deleteAllDescription": "Elimina tutti i dati di misurazioni, farmaci, integrazioni e log di tutti gli utenti. Gli account utente vengono conservati.",
"deleteAllDescription": "Elimina tutti i dati di misurazioni, farmaci, integrazioni e log di tutti gli utenti.",
"deleteAllConfirm": "Eliminare davvero tutti i dati?",
"deleteAllConfirmDescription": "Questa operazione eliminerà definitivamente tutte le misurazioni, i farmaci, gli eventi di assunzione, i token API, le connessioni Withings, le challenge di autenticazione aperte, i canali di notifica (Telegram, ntfy, Web Push), le sottoscrizioni push e le eliminazioni di messaggi Telegram in coda di tutti gli utenti. Gli account utente, il log di audit e i feedback inviati vengono conservati affinché la cancellazione resti tracciabile e le segnalazioni di bug restino gestibili.",
"deleteButton": "Elimina tutti i dati",
@@ -6611,7 +6618,7 @@
"app-logs": {
"title": "Log dell'app",
"subtitle": "Visualizza i wide-event strutturati più recenti emessi da questo processo.",
"processNote": "Vengono mostrati solo i log del processo dell'app: i log del processo worker vanno su Loki quando l'integrazione è configurata. Il buffer è volatile e limitato a 500 voci.",
"processNote": "Vengono mostrati solo i log del processo dell'app: i log del processo worker vanno su Loki quando l'integrazione è configurata.",
"filterTraceId": "ID traccia",
"filterAction": "L'azione contiene…",
"filterLevel": "Livello",
@@ -6632,7 +6639,8 @@
"emptyDescription": "Nessun evento corrisponde ancora ai filtri attuali. Premi Aggiorna dopo aver generato un po' di traffico.",
"refresh": "Aggiorna",
"eventDetails": "Dettagli dell'evento",
"closeDetails": "Chiudi dettagli"
"closeDetails": "Chiudi dettagli",
"processDetail": "Il buffer è volatile e limitato a 500 voci."
},
"backups": {
"title": "Backup",
@@ -6699,7 +6707,7 @@
"loading": "Caricamento dello stato della crittografia...",
"loadError": "Impossibile caricare lo stato della crittografia.",
"coverageTitle": "Copertura",
"coverageDescription": "Quante righe cifrate stanno sotto ogni id di chiave. Vengono mostrati solo gli id di chiave e i conteggi, mai il materiale della chiave.",
"coverageDescription": "Quante righe cifrate stanno sotto ogni id di chiave.",
"activeKeyId": "Id chiave attiva",
"configuredKeys": "Chiavi configurate",
"totalRows": "Righe cifrate",
@@ -6707,7 +6715,7 @@
"safeToDropLegacy": "È sicuro rimuovere la vecchia chiave",
"rotationIncomplete": "Restano {count} righe da ruotare",
"rotationTitle": "Rotazione delle chiavi",
"rotationDescription": "Ricifra ogni colonna cifrata con la chiave attiva. Lo strumento da riga di comando resta la via canonica; questo comando è una comodità.",
"rotationDescription": "Ricifra ogni colonna cifrata con la chiave attiva.",
"rotationState": "Stato",
"lastRun": "Ultima esecuzione: {scanned} esaminate, {rotated} ruotate, {errors} errori.",
"rotateNow": "Ruota ora",
@@ -6730,7 +6738,8 @@
"running": "In corso",
"completed": "Completato",
"failed": "Fallito"
}
},
"coverageDetail": "Vengono mostrati solo gli id di chiave e i conteggi, mai il materiale della chiave."
}
},
"carrier": "Operatore",
@@ -6776,7 +6785,8 @@
"countFew": "{count} errori",
"countOther": "{count} errori",
"lastFailed": "Ultimo errore {when}"
}
},
"deleteAllDetail": "Gli account utente vengono conservati."
},
"achievements": {
"title": "Obiettivi",
@@ -7102,7 +7112,7 @@
},
"moodReminder": {
"title": "Promemoria quotidiano dell’umore",
"description": "Promemoria leggero all’orario che scegli se non hai ancora registrato l’umore di oggi. Usa solo i canali già configurati (Telegram, ntfy, Web Push, Apple Push) e tace una volta registrato l’umore di oggi.",
"description": "Promemoria leggero all’orario che scegli se non hai ancora registrato l’umore di oggi.",
"toggleAria": "Attiva o disattiva il promemoria quotidiano dell’umore",
"statusOn": "Attivo all’orario che scegli",
"statusOff": "Disattivato",
@@ -7111,11 +7121,12 @@
"saveError": "Salvataggio non riuscito. Riprova.",
"hourLabel": "Ora del promemoria",
"hourAria": "Ora del promemoria dell’umore",
"hourSavedToast": "Ora del promemoria aggiornata."
"hourSavedToast": "Ora del promemoria aggiornata.",
"detail": "Usa solo i canali già configurati (Telegram, ntfy, Web Push, Apple Push) e tace una volta registrato l’umore di oggi."
},
"coachNudge": {
"title": "Coach proattivo",
"description": "Si fa sentire quando i tuoi dati suggeriscono di consultare il Coach — ad esempio bassa aderenza terapeutica o una media settimanale della pressione elevata. Temi e frequenza sono regolabili.",
"description": "Si fa sentire quando i tuoi dati suggeriscono di consultare il Coach.",
"toggleAria": "Attiva o disattiva gli avvisi proattivi del Coach",
"statusOn": "Attivo",
"groupsLabel": "Temi",
@@ -7142,7 +7153,7 @@
},
"lowStock": {
"title": "Avviso scorte basse",
"description": "Controlla una volta al giorno le scorte tracciate dei tuoi farmaci e ti avvisa quando una copre meno giorni di questa soglia. Un avviso per carenza; si riattiva dopo il rifornimento.",
"description": "Segnala ogni giorno una scorta che copre meno giorni di questa soglia.",
"toggleAria": "Attiva o disattiva gli avvisi di scorte basse",
"statusOn": "Attivo",
"statusOff": "Disattivato",
@@ -7156,7 +7167,8 @@
"enabledToast": "Avvisi di scorte basse attivati.",
"disabledToast": "Avvisi di scorte basse disattivati.",
"saveError": "Salvataggio non riuscito. Riprova.",
"medicationsLink": "Vai ai tuoi farmaci"
"medicationsLink": "Vai ai tuoi farmaci",
"detail": "Un avviso per carenza; si riattiva dopo il rifornimento."
},
"eventMedicationReminder": "Promemoria farmaco",
"eventMedicationReminderDesc": "Inviato a ogni orario di dose programmato.",
@@ -9536,7 +9548,7 @@
},
"invite": {
"title": "Invita qualcuno",
"description": "Nulla viene condiviso finché non accetta. Fino ad allora l'invito resta nelle sue impostazioni e non le dà nulla.",
"description": "Nulla viene condiviso finché non accetta.",
"identifierLabel": "Nome utente o e-mail",
"identifierPlaceholder": "Il nome con cui accede",
"identifierHint": "Serve un account su questa istanza. Non è possibile inviare inviti a un indirizzo non registrato.",
@@ -9590,7 +9602,7 @@
},
"received": {
"title": "Cartelle che posso aprire",
"description": "Un invito non ti dà nulla finché non lo accetti. Puoi restituire l'accesso quando vuoi.",
"description": "Un invito non ti dà nulla finché non lo accetti.",
"emptyTitle": "Nessuno ha condiviso una cartella con te",
"emptyDescription": "Quando qualcuno ti invita, l'invito compare qui.",
"loadError": "Non è stato possibile caricare l'elenco.",
@@ -9602,7 +9614,8 @@
"renounce": "Restituisci",
"renounceTitle": "Restituire la cartella di {name}?",
"renounceBody": "Perdi l'accesso subito. Quella persona può invitarti di nuovo quando vuole.",
"renounceConfirm": "Restituisci"
"renounceConfirm": "Restituisci",
"detail": "Puoi restituire l'accesso quando vuoi."
},
"activity": {
"title": "Quando è stata aperta la mia cartella",
+74 -61
View File
@@ -3925,12 +3925,13 @@
},
"pillOrder": {
"title": "Sortuj pigułki nawigacyjne",
"description": "Przeciągnij pigułki w wybranej kolejności i pokaż lub ukryj je ikoną oka. Ukryte strony szczegółów znikają również z górnej nawigacji.",
"description": "Przeciągnij pigułki w wybranej kolejności i pokaż lub ukryj je ikoną oka.",
"dragHandle": "Zmień kolejność",
"saveSuccess": "Zapisano kolejność pigułek.",
"saveError": "Zapisanie kolejności pigułek nie powiodło się.",
"save": "Zapisz",
"empty": "Brak dostępnych pigułek nawigacyjnych."
"empty": "Brak dostępnych pigułek nawigacyjnych.",
"detail": "Ukryte strony szczegółów znikają również z górnej nawigacji."
},
"customize": "Dostosuj Analizy",
"regenerateRateLimited": "Osiągnięto limit odświeżeń — spróbuj ponownie później.",
@@ -4412,7 +4413,7 @@
"settings": {
"researchMode": {
"sectionTitle": "Tryb Badawczy",
"subtitle": "Opcjonalne wyświetlanie szacowanej krzywej poziomu leku GLP-1, wyprowadzonej z farmakokinetyki populacyjnej opublikowanej przez EMA. Tylko do odczytu — nigdy pomiar ani porada medyczna.",
"subtitle": "Opcjonalna krzywa szacowanego poziomu leku GLP-1 wg danych EMA.",
"toggleLabel": "Pokaż szacowaną krzywą poziomu leku",
"disabledStatus": "Wyłączone. Krzywa jest ukryta.",
"enabledStaleStatus": "Disclaimer został zaktualizowany. Potwierdź ponownie poniżej, aby przywrócić krzywą.",
@@ -4420,7 +4421,8 @@
"rePromptTitle": "Zaktualizowano disclaimer",
"rePromptBody": "Disclaimer trybu Badawczego został zaktualizowany od czasu twojego ostatniego potwierdzenia. Przeczytaj go ponownie i potwierdź, aby krzywa pozostała widoczna.",
"rePromptCta": "Potwierdź ponownie disclaimer",
"disableError": "Nie udało się wyłączyć trybu Badawczego. Spróbuj ponownie."
"disableError": "Nie udało się wyłączyć trybu Badawczego. Spróbuj ponownie.",
"subtitleDetail": "Tylko do odczytu — nigdy pomiar ani porada medyczna."
},
"shell": {
"sectionsNav": "Sekcje ustawień",
@@ -4547,7 +4549,8 @@
},
"unavailableTitle": "Niedostępne w tym udostępnionym rekordzie",
"unavailableDescription": "Ten obszar Ustawień jest celowo niedostępny podczas przeglądania rekordu innej osoby.",
"integrationStatusDescription": "Stan połączenia dla tego zarządzanego rekordu. Elementy sterujące połączeniem są niedostępne."
"integrationStatusDescription": "Stan połączenia dla tego zarządzanego rekordu.",
"integrationStatusDetail": "Elementy sterujące połączeniem są niedostępne."
},
"sections": {
"account": {
@@ -4622,7 +4625,7 @@
"integrationsHint": "Źródła danych podłączysz lub odłączysz na stronie integracji.",
"integrationsHintLink": "Otwórz integracje",
"cardTitle": "Priorytet źródeł według metryki",
"help": "Gdy kilka źródeł dostarcza tę samą wartość tego samego dnia, źródło na górze liczy się jako kanoniczne. Pozostałe pozostają zapisane na potrzeby audytu.",
"help": "Gdy kilka źródeł dostarcza tę samą wartość tego samego dnia, źródło na górze liczy się jako kanoniczne.",
"resetDefaults": "Przywróć wartości domyślne",
"resetDefaultsTitle": "Zresetować kolejność źródeł?",
"resetDefaultsBody": "Każda ustawiona przez ciebie kolejność wraca do domyślnej, dla wszystkich metryk i typów urządzeń naraz. Zapis następuje natychmiast i nie da się go cofnąć.",
@@ -4682,13 +4685,15 @@
"subtitle": "Zdecyduj, które źródło wygrywa, gdy dwa podają tę samą metrykę.",
"nutrients": {
"title": "Zsynchronizowane spożycie składników",
"description": "Dzienne sumy z Apple Health z ostatnich {days} dni. Tylko do odczytu — wartości należą do aplikacji źródłowej.",
"description": "Dzienne sumy z Apple Health z ostatnich {days} dni.",
"empty": "Nie zsynchronizowano jeszcze danych o składnikach odżywczych.",
"latest": "{amount} {unit} dnia {date}",
"daysWithData": "{count} z {days} dni",
"moduleHint": "Synchronizacją steruje moduł spożycia składników odżywczych.",
"moduleHintLink": "Zarządzaj modułami"
}
"moduleHintLink": "Zarządzaj modułami",
"detail": "Tylko do odczytu — wartości należą do aplikacji źródłowej."
},
"helpDetail": "Pozostałe pozostają zapisane na potrzeby audytu."
},
"ai": {
"title": "Dostawca AI",
@@ -4938,7 +4943,7 @@
"error": "Nie udało się zapisać. Spróbuj ponownie.",
"toggleable": {
"title": "Co śledzisz",
"description": "Obszary opcjonalne. Wyłączone moduły znikają z nawigacji, kafelków i analiz — Twoje dane pozostają nienaruszone."
"description": "Wyłączone moduły znikają z nawigacji, kafelków i analiz — Twoje dane pozostają nienaruszone."
},
"manageIn": "Zarządzaj w {section}",
"operatorDisabled": "Wyłączone na całym serwerze",
@@ -5326,7 +5331,7 @@
"testReasonUnreachable": "Nie udało się połączyć z dostawcą AI.",
"coachMemory": {
"title": "Co Coach pamięta",
"description": "Trwałe informacje, które przekazałeś Coachowi. Wykorzystuje je, aby jego odpowiedzi pozostawały trafne. Usuń wszystko, o czym wolisz, by zapomniał.",
"description": "Trwałe informacje, które przekazałeś Coachowi.",
"empty": "Coach nie zapisał jeszcze żadnych informacji. Podczas rozmowy zapamiętuje trwałe szczegóły, którymi się dzielisz — Twoje cele, dolegliwości, preferencje — i możesz je tutaj w każdej chwili usunąć.",
"learnedPrefix": "zapamiętano {when}",
"categoryPreference": "Preferencje",
@@ -5349,11 +5354,12 @@
"forgotToast": "Informacja zapomniana.",
"forgotError": "Nie udało się zapomnieć tej informacji. Spróbuj ponownie.",
"clearedToast": "Zapomniano {count} zapisanych informacji.",
"clearError": "Nie udało się usunąć zapisanych informacji. Spróbuj ponownie."
"clearError": "Nie udało się usunąć zapisanych informacji. Spróbuj ponownie.",
"detail": "Wykorzystuje je, aby jego odpowiedzi pozostawały trafne. Usuń wszystko, o czym wolisz, by zapomniał."
},
"coachReminders": {
"title": "Twoje przypomnienia",
"description": "Rzeczy, o których przypomnienie poprosiłeś Coacha. Te, których termin nadszedł, pojawiają się tutaj i w Coachu.",
"description": "Rzeczy, o których przypomnienie poprosiłeś Coacha.",
"empty": "Brak przypomnień. Poproś Coacha, aby przypomniał Ci o czymś, a pojawi się tutaj.",
"loadError": "Nie udało się wczytać Twoich przypomnień.",
"dueHeading": "Termin teraz",
@@ -5374,7 +5380,8 @@
},
"deleteConfirmTitle": "Usunąć to przypomnienie?",
"deleteConfirmBody": "Przestanie się pojawiać. Zapisane wcześniej dane pozostają bez zmian.",
"deleteConfirmAction": "Usuń"
"deleteConfirmAction": "Usuń",
"detail": "Te, których termin nadszedł, pojawiają się tutaj i w Coachu."
},
"aboutMe": {
"title": "O mnie",
@@ -5647,7 +5654,7 @@
"workouts": "Treningi"
},
"integrationFallback": {
"description": "Skonfigurowane na tym koncie, ale bez własnej karty ustawień. Ten wiersz istnieje po to, by przerwane połączenie nie pozostało niezauważone.",
"description": "Skonfigurowane na tym koncie, ale bez własnej karty ustawień.",
"lastAttempt": "Ostatnia próba {relative}",
"noAttempt": "Brak zapisanej próby synchronizacji"
},
@@ -5656,7 +5663,7 @@
"fitbitViewData": "Zobacz swój sen",
"googleHealthViewData": "Zobacz swoje kroki",
"apiTokens": "Tokeny API",
"apiTokensDescription": "Przeglądaj i unieważniaj tokeny API do zewnętrznego rejestrowania przyjęć leków. Tokeny są wydawane dla pojedynczego leku z jego przełącznika punktu końcowego API.",
"apiTokensDescription": "Przeglądaj i unieważniaj tokeny API do zewnętrznego rejestrowania przyjęć leków.",
"tokenMintMovedDescription": "Tokeny są wydawane dla pojedynczego leku. Otwórz lek i włącz jego punkt końcowy API, aby otrzymać token obejmujący wyłącznie ten lek.",
"tokenNamePlaceholder": "Nazwa tokena (np. Skrót iPhone)",
"tokenCreated": "Token utworzony — skopiuj go teraz! Nie zostanie pokazany ponownie.",
@@ -5687,14 +5694,14 @@
"apiEndpointAuth": "Uwierzytelnianie",
"apiEndpointExample": "Przykład treści",
"dangerZone": "Usuń wszystkie dane",
"dangerZoneDescription": "Kasuje całą Twoją dokumentację zdrowotną i wszystkie połączenia integracji. Konto i dane logowania zostają zachowane.",
"dangerZoneDescription": "Kasuje całą Twoją dokumentację zdrowotną i wszystkie połączenia integracji.",
"dangerZoneConfirm": "Usunąć wszystkie dane?",
"dangerZoneConfirmDescription": "Ta czynność trwale usuwa wszystko z Twojej dokumentacji: pomiary, leki, wpisy nastroju, wyniki badań, wpisy cyklu, dokumenty, rozmowy z SI oraz wszystkie połączenia integracji, a wraz z nimi wszystko, co z nich wyliczono, i wszystko, co usunięto wcześniej. Linki udostępnione poradni natychmiast przestają działać. Konto zostaje, razem z danymi logowania i ustawieniami wyświetlania, takimi jak język, strefa czasowa i jednostki. Nie można tego cofnąć.",
"dangerZoneSuccess": "Twoja dokumentacja zdrowotna została usunięta. Konto i logowanie pozostają bez zmian.",
"dangerZoneDeleteFailed": "Usuwanie nie powiodło się",
"finalDelete": "Usuń trwale",
"deleteAccountCardTitle": "Usuń całe konto",
"deleteAccountCardDescription": "Usuwa Twoje konto wraz z kluczami dostępu, dziennikiem audytu i sesjami. Tej operacji nie można cofnąć.",
"deleteAccountCardDescription": "Usuwa Twoje konto wraz z kluczami dostępu, dziennikiem audytu i sesjami.",
"deleteAccountCta": "Usuń konto",
"deleteAccountConfirmTitle": "Trwale usunąć konto?",
"deleteAccountConfirmDescription": "Twoje konto, wszystkie dane zdrowotne, klucze dostępu, sesje i wpisy audytu zostaną trwale usunięte. Zostaniesz wylogowany natychmiast po tym.",
@@ -5712,8 +5719,6 @@
"changelog": "Changelog",
"docs": "Dokumentacja",
"linksHeading": "Źródła i dokumentacja",
"identityDescription": "Wersja, licencja i szczegóły kompilacji tej instancji.",
"linksDescription": "Kod źródłowy, dziennik zmian i dokumentacja.",
"newerAvailable": "Nowa wersja: {tag}"
},
"testConnection": {
@@ -5741,7 +5746,7 @@
"notificationStatus": {
"title": "Niezawodność kanałów",
"description": "Stan każdego kanału; „Automatycznie wyłączone” przerywa ponawianie po trwałych błędach.",
"emptyDescription": "Nie skonfigurowano jeszcze żadnych kanałów. Dodaj kanał w sekcji Kanały dostarczania, aby śledzić dostarczalność.",
"emptyDescription": "Dodaj kanał w Ustawienia › Integracje › Kanały dostarczania.",
"stateActive": "Aktywne",
"stateAutoDisabled": "Automatycznie wyłączone",
"stateSendingPaused": "Wysyłanie wstrzymane",
@@ -5752,7 +5757,8 @@
"disabledReason": "Powód",
"nextRetry": "Następna próba",
"reEnable": "Włącz ponownie",
"sendTest": "Wyślij test"
"sendTest": "Wyślij test",
"emptyTitle": "Nie skonfigurowano jeszcze kanałów"
},
"identity": {
"description": "Opcjonalne dane do eksportu dokumentacji zdrowotnej (okładka PDF + eksport FHIR). Wszystkie pola są opcjonalne.",
@@ -5765,7 +5771,7 @@
},
"healthRecord": {
"title": "Eksportuj dokumentację zdrowotną",
"description": "Wygeneruj pełną dokumentację zdrowotną dla lekarza — jako PDF, ustrukturyzowaną (FHIR) lub łączny pakiet.",
"description": "Pełna dokumentacja dla lekarza — jako PDF, jako FHIR lub oba.",
"format": "Format",
"format_pdf": "PDF",
"format_fhir": "Ustrukturyzowany (FHIR)",
@@ -5790,10 +5796,10 @@
"errorModuleDisabled": "Eksport dokumentacji jest wyłączony dla tego konta."
},
"globalExcludedInjectionSitesLabel": "Globalnie wykluczone miejsca iniekcji",
"globalExcludedInjectionSitesHint": "Miejsca wymienione tutaj nie są proponowane dla żadnego leku i są odrzucane przy rejestrowaniu dawki, nawet jeśli lek je preferuje.",
"globalExcludedInjectionSitesHint": "Te miejsca nigdy nie są proponowane i są odrzucane przy rejestrowaniu dawki.",
"sharing": {
"createTitle": "Nowy link udostępniania",
"createDescription": "Link jest tylko do odczytu i ograniczony czasowo. Token jest pokazywany tylko raz przy tworzeniu — zapisz go wtedy; nie można go odzyskać.",
"createDescription": "Link jest tylko do odczytu i ograniczony czasowo.",
"label": "Etykieta",
"labelPlaceholder": "np. dr Kowalski — kontrola roczna",
"range": "Dni historii",
@@ -5843,15 +5849,17 @@
"scopeInsuranceExcluded": "Dane ubezpieczenia nigdy nie są udostępniane przez link.",
"statusNeedsReselect": "Wybierz zawartość ponownie",
"reselectReason": "Ten link udostępniał zakres, którego nikt nie wybrał, więc został zamknięty. Utwórz nowy i wybierz jego zawartość.",
"reselectAction": "Utwórz zamiennik"
"reselectAction": "Utwórz zamiennik",
"createDetail": "Token jest pokazywany tylko raz przy tworzeniu — zapisz go wtedy; nie można go odzyskać."
},
"genderOther": "Inna",
"cycleTracking": {
"title": "Śledzenie cyklu",
"description": "Śledź swój cykl menstruacyjny, prognozy, objawy i analizy faz. Domyślnie włączone dla kont ustawionych jako kobiece; dostępne dla wszystkich.",
"description": "Śledź swój cykl menstruacyjny, prognozy, objawy i analizy faz.",
"enable": "Włącz śledzenie cyklu",
"manage": "Otwórz śledzenie cyklu",
"error": "Nie udało się zaktualizować śledzenia cyklu."
"error": "Nie udało się zaktualizować śledzenia cyklu.",
"detail": "Domyślnie włączone dla kont ustawionych jako kobiece; dostępne dla wszystkich."
},
"nightscout": "Nightscout",
"nightscoutTag": "CGM",
@@ -5937,7 +5945,7 @@
"coach": {
"activate": {
"title": "Włącz Coacha",
"description": "Pokazuje przycisk i panel Coacha. Domyślnie włączony — wyłącz, aby ukryć Coacha wszędzie.",
"description": "Pokazuje przycisk i panel Coacha.",
"toggleAria": "Włącz przycisk i panel Coacha",
"savedOn": "Coach włączony.",
"savedOff": "Coach ukryty.",
@@ -6057,19 +6065,13 @@
"signOutEverywhereConfirmBody": "Każde urządzenie poza tym będzie musiało zalogować się ponownie. To urządzenie pozostaje zalogowane. Zapisane dane pozostają bez zmian."
},
"privacy": {
"intro": {
"title": "Twoje dane pozostają Twoje",
"body": "HealthLog jest hostowany samodzielnie. Ta strona zbiera w jednym miejscu informacje o tym, jak chronione są Twoje dane, jak długo są przechowywane, jak je wyeksportować i jak je usunąć."
},
"encryption": {
"title": "Szyfrowane w spoczynku",
"description": "Wrażliwe pola są szyfrowane w bazie danych.",
"summary": "Twoje wrażliwe dane są szyfrowane w spoczynku algorytmem {algorithm} w {columns} polach w {models} typach rekordów.",
"statement": "Twoje wrażliwe dane są szyfrowane w spoczynku algorytmem AES-256-GCM."
},
"stored": {
"title": "Co jest przechowywane",
"description": "Kategorie danych przechowywanych na tym koncie.",
"measurements": "Pomiary (waga, ciśnienie, tętno, glukoza, sen, skład ciała)",
"medications": "Leki i historia przyjmowania",
"moodLabs": "Wpisy nastroju i wyniki badań laboratoryjnych",
@@ -6087,7 +6089,7 @@
},
"export": {
"title": "Pobierz moje dane",
"description": "Wyeksportuj swoje rekordy jako CSV lub JSON, opcjonalnie zaszyfrowane hasłem, albo wygeneruj pełną dokumentację medyczną.",
"description": "Twoje rekordy jako CSV lub JSON, opcjonalnie zaszyfrowane hasłem.",
"openExport": "Eksport i kopia zapasowa",
"openHealthRecord": "Dokumentacja medyczna"
},
@@ -6106,26 +6108,28 @@
},
"mcp": {
"enableTitle": "Zdalny punkt końcowy MCP",
"enableDescription": "Zezwól asystentowi obsługującemu MCP (Claude.ai, ChatGPT) na odczyt danych zdrowotnych za pomocą ograniczonego tokenu. Domyślnie wyłączone.",
"enableDescription": "Zezwól asystentowi obsługującemu MCP (Claude.ai, ChatGPT) na odczyt danych zdrowotnych za pomocą ograniczonego tokenu.",
"enabled": "Włączony",
"disabled": "Wyłączony",
"enableToggleLabel": "Włącz punkt końcowy MCP",
"endpointLabel": "Adres URL łącznika",
"connectDescription": "Wklej ten adres URL w konfiguracji własnego łącznika asystenta, a następnie autoryzuj za pomocą poniższego tokenu lub zaloguj się po wyświetleniu monitu.",
"tokensTitle": "Tokeny łącznika",
"tokensDescription": "Wygeneruj token tylko do odczytu, aby połączyć asystenta. Token jest wyświetlany tylko raz.",
"tokensDescription": "Wygeneruj token tylko do odczytu, aby połączyć asystenta.",
"scopeNote": "Tokeny są ograniczone do health:read — dostęp tylko do odczytu własnych danych. Nigdy nie mogą uzyskać dostępu do funkcji administracyjnych.",
"writeScopeToggleLabel": "Zezwól na zapis (health:write)",
"writeScopeNote": "Dodaje zakres health:write, aby asystent mógł zapisywać nowe pomiary i wpisy nastroju na Twoim koncie. Każdy zapis jest najpierw potwierdzany. Token nadal nie może usuwać ani edytować istniejących wpisów, zmieniać leków ani uzyskiwać dostępu do funkcji REST lub administracyjnych. Pozostaw wyłączone, aby uzyskać dostęp tylko do odczytu.",
"connectionsTitle": "Połączeni asystenci",
"connectionsDescription": "Asystenci autoryzowani podczas logowania. Cofnięcie połączenia natychmiast kończy dostęp, w tym możliwość odnawiania.",
"connectionsDescription": "Asystenci autoryzowani podczas logowania.",
"noConnections": "Brak połączonych asystentów.",
"connectionConnected": "Połączono",
"connectionLastUsed": "Ostatnio użyto",
"connectionNeverUsed": "Nigdy nie użyto",
"connectionRevoke": "Rozłącz",
"connectionRevokeDescription": "To natychmiast kończy dostęp asystenta i uniemożliwia odnawianie. Ponowne połączenie będzie wymagać nowej autoryzacji.",
"connectionRevokeFailed": "Nie udało się rozłączyć asystenta."
"connectionRevokeFailed": "Nie udało się rozłączyć asystenta.",
"tokensDetail": "Token jest wyświetlany tylko raz.",
"connectionsDetail": "Cofnięcie połączenia natychmiast kończy dostęp, w tym możliwość odnawiania."
},
"strava": "Strava",
"stravaDescription": "Połącz konto Strava, aby synchronizować aktywności z historią treningów.",
@@ -6194,7 +6198,9 @@
"explainer": "Wyłączone — wyniki są oceniane względem ogólnego zakresu referencyjnego dla osób bez cukrzycy. Włączone — są oceniane względem węższego zakresu docelowego glikemii z ADA Standards of Care, a Coach opiera na nim swoje uwagi o glukozie. Twoje własne wartości docelowe poniżej nadal mają pierwszeństwo.",
"disclaimer": "To wybiera zakres referencyjny. Nie jest to diagnoza, nigdy nie jest wywnioskowane z pomiaru, a aplikacja nie podejmuje na tej podstawie działań klinicznych. O to, jakie cele dotyczą ciebie, zapytaj swojego lekarza.",
"error": "Nie udało się zapisać ustawienia."
}
},
"dangerZoneDetail": "Konto i dane logowania zostają zachowane.",
"deleteAccountCardDetail": "Tej operacji nie można cofnąć."
},
"admin": {
"title": "Administracja",
@@ -6225,7 +6231,7 @@
},
"aiServerKey": {
"title": "Serwerowy klucz AI",
"description": "Klucz dostawcy na poziomie operatora, z którego może skorzystać każde konto, gdy nie skonfigurowano klucza osobistego.",
"description": "Klucz operatora dla kont, które nie mają własnego.",
"byokHint": "Własny klucz użytkownika (BYOK, logowanie ChatGPT lub dostawca lokalny) zawsze ma pierwszeństwo — klucz serwera jest ostatnim ogniwem łańcucha dostawców.",
"consentHint": "Żądania przez klucz serwera wymagają aktywnej zgody użytkownika na AI; bez potwierdzenia zgody nic nie jest wysyłane.",
"statusConfigured": "Skonfigurowany",
@@ -6331,7 +6337,7 @@
"monitoringTestFailed": "Test monitorowania nie powiódł się",
"assistant": {
"title": "Powierzchnie asystenta",
"description": "Każdy przełącznik ukrywa powierzchnię opartą na modelu w całej aplikacji bez usuwania konfiguracji dostawcy. Główny wyłącznik wymusza wyłączenie każdej opcji podrzędnej.",
"description": "Każdy przełącznik ukrywa powierzchnię opartą na modelu w całej aplikacji bez usuwania konfiguracji dostawcy.",
"master": {
"title": "Asystent włączony",
"description": "Główny wyłącznik dla każdej powierzchni opartej na modelu. Gdy wyłączony, każda opcja podrzędna jest wymuszana na off."
@@ -6355,12 +6361,13 @@
},
"modules": {
"title": "Dostępność modułów",
"description": "Wyłącz moduł dla całego serwera. Moduł wyłączony tutaj jest nieaktywny dla każdego konta, niezależnie od preferencji użytkownika. Domeny podstawowe (waga, ciśnienie, tętno, leki) są zawsze włączone i nie można ich wyłączyć.",
"saveError": "Nie udało się zaktualizować dostępności modułów."
"description": "Wyłącz moduł dla całego serwera.",
"saveError": "Nie udało się zaktualizować dostępności modułów.",
"detail": "Moduł wyłączony tutaj jest nieaktywny dla każdego konta, niezależnie od preferencji użytkownika. Domeny podstawowe (waga, ciśnienie, tętno, leki) są zawsze włączone i nie można ich wyłączyć."
},
"invites": {
"title": "Zaproszenia",
"description": "Linki z zaproszeniem do rejestracji — działają nawet przy zamkniętej rejestracji. Link jest wyświetlany tylko raz.",
"description": "Linki z zaproszeniem do rejestracji — działają nawet przy zamkniętej rejestracji.",
"create": "Utwórz zaproszenie",
"createTitle": "Utwórz zaproszenie",
"createDescription": "Link umożliwia rejestrację nawet przy zamkniętej rejestracji.",
@@ -6466,7 +6473,7 @@
"outcomeFailed": "Nieudane",
"auditDetails": "Szczegóły",
"deleteAllData": "Usuń wszystkie dane globalnie",
"deleteAllDescription": "Usuwa wszystkie dane pomiarów, leków, integracji i logów wszystkich użytkowników. Konta użytkowników zostaną zachowane.",
"deleteAllDescription": "Usuwa wszystkie dane pomiarów, leków, integracji i logów wszystkich użytkowników.",
"deleteAllConfirm": "Na pewno usunąć wszystkie dane?",
"deleteAllConfirmDescription": "Spowoduje to trwałe usunięcie wszystkich pomiarów, leków, zdarzeń przyjęcia, tokenów API, połączeń Withings, otwartych wyzwań uwierzytelniania, kanałów powiadomień (Telegram, ntfy, Web Push), subskrypcji push oraz zakolejkowanych usunięć wiadomości Telegram wszystkich użytkowników. Konta użytkowników, dziennik audytu i przesłane opinie zostają zachowane, aby samo usunięcie pozostało możliwe do prześledzenia, a zgłoszenia błędów — do obsłużenia.",
"deleteButton": "Usuń wszystkie dane",
@@ -6611,7 +6618,7 @@
"app-logs": {
"title": "Logi aplikacji",
"subtitle": "Wyświetl najnowsze ustrukturyzowane wide-events emitowane przez ten proces.",
"processNote": "Wyświetlane są tylko logi procesu aplikacji — logi procesu workera trafiają do Loki, gdy ta integracja jest skonfigurowana. Bufor jest ulotny i ograniczony do 500 wpisów.",
"processNote": "Wyświetlane są tylko logi procesu aplikacji — logi procesu workera trafiają do Loki, gdy ta integracja jest skonfigurowana.",
"filterTraceId": "Identyfikator śladu",
"filterAction": "Akcja zawiera…",
"filterLevel": "Poziom",
@@ -6632,7 +6639,8 @@
"emptyDescription": "Żadne zdarzenie nie pasuje jeszcze do bieżących filtrów. Kliknij Odśwież po wygenerowaniu ruchu.",
"refresh": "Odśwież",
"eventDetails": "Szczegóły zdarzenia",
"closeDetails": "Zamknij szczegóły"
"closeDetails": "Zamknij szczegóły",
"processDetail": "Bufor jest ulotny i ograniczony do 500 wpisów."
},
"backups": {
"title": "Kopie zapasowe",
@@ -6699,7 +6707,7 @@
"loading": "Ładowanie statusu szyfrowania...",
"loadError": "Nie udało się wczytać statusu szyfrowania.",
"coverageTitle": "Pokrycie",
"coverageDescription": "Ile zaszyfrowanych wierszy przypada na każdy identyfikator klucza. Pokazywane są tylko identyfikatory kluczy i liczby — nigdy materiał klucza.",
"coverageDescription": "Ile zaszyfrowanych wierszy przypada na każdy identyfikator klucza.",
"activeKeyId": "Identyfikator aktywnego klucza",
"configuredKeys": "Skonfigurowane klucze",
"totalRows": "Zaszyfrowane wiersze",
@@ -6707,7 +6715,7 @@
"safeToDropLegacy": "Można bezpiecznie usunąć stary klucz",
"rotationIncomplete": "Pozostało {count} wierszy do rotacji",
"rotationTitle": "Rotacja kluczy",
"rotationDescription": "Ponownie szyfruje każdą zaszyfrowaną kolumnę aktywnym kluczem. Narzędzie wiersza poleceń pozostaje kanoniczną drogą; ten przycisk to udogodnienie.",
"rotationDescription": "Ponownie szyfruje każdą zaszyfrowaną kolumnę aktywnym kluczem.",
"rotationState": "Status",
"lastRun": "Ostatni przebieg: sprawdzono {scanned}, zrotowano {rotated}, błędy {errors}.",
"rotateNow": "Rotuj teraz",
@@ -6730,7 +6738,8 @@
"running": "W toku",
"completed": "Zakończono",
"failed": "Niepowodzenie"
}
},
"coverageDetail": "Pokazywane są tylko identyfikatory kluczy i liczby — nigdy materiał klucza."
}
},
"carrier": "Operator",
@@ -6776,7 +6785,8 @@
"countFew": "{count} niepowodzenia",
"countOther": "{count} niepowodzeń",
"lastFailed": "Ostatnie niepowodzenie {when}"
}
},
"deleteAllDetail": "Konta użytkowników zostaną zachowane."
},
"achievements": {
"title": "Osiągnięcia",
@@ -7102,7 +7112,7 @@
},
"moodReminder": {
"title": "Codzienne przypomnienie o nastroju",
"description": "Delikatne przypomnienie o wybranej przez Ciebie godzinie, jeśli dziś jeszcze nie zapisano nastroju. Wysyłane tylko skonfigurowanymi już kanałami (Telegram, ntfy, Web Push, Apple Push) i milknie, gdy dziś zapisano nastrój.",
"description": "Delikatne przypomnienie o wybranej przez Ciebie godzinie, jeśli dziś jeszcze nie zapisano nastroju.",
"toggleAria": "Włącz lub wyłącz codzienne przypomnienie o nastroju",
"statusOn": "Aktywne o wybranej godzinie",
"statusOff": "Wyłączone",
@@ -7111,11 +7121,12 @@
"saveError": "Zapis nieudany. Spróbuj ponownie.",
"hourLabel": "Godzina przypomnienia",
"hourAria": "Godzina przypomnienia o nastroju",
"hourSavedToast": "Godzina przypomnienia zaktualizowana."
"hourSavedToast": "Godzina przypomnienia zaktualizowana.",
"detail": "Wysyłane tylko skonfigurowanymi już kanałami (Telegram, ntfy, Web Push, Apple Push) i milknie, gdy dziś zapisano nastrój."
},
"coachNudge": {
"title": "Proaktywny Coach",
"description": "Odzywa się, gdy Twoje dane sugerują zajrzenie do Coacha — np. niska systematyczność przyjmowania leków lub podwyższona tygodniowa średnia ciśnienia. Tematy i częstotliwość można dostosować.",
"description": "Odzywa się, gdy Twoje dane sugerują zajrzenie do Coacha.",
"toggleAria": "Przełącz proaktywne podpowiedzi Coacha",
"statusOn": "Aktywne",
"groupsLabel": "Tematy",
@@ -7142,7 +7153,7 @@
},
"lowStock": {
"title": "Alert niskiego zapasu",
"description": "Raz dziennie sprawdza śledzone zapasy leków i powiadamia, gdy któryś wystarcza na mniej dni niż ten próg. Jedno powiadomienie na niedobór; po uzupełnieniu uzbraja się ponownie.",
"description": "Codziennie zgłasza zapas wystarczający na mniej dni niż ten próg.",
"toggleAria": "Przełącz alerty niskiego zapasu",
"statusOn": "Aktywne",
"statusOff": "Wyłączone",
@@ -7156,7 +7167,8 @@
"enabledToast": "Alerty niskiego zapasu włączone.",
"disabledToast": "Alerty niskiego zapasu wyłączone.",
"saveError": "Zapis nieudany. Spróbuj ponownie.",
"medicationsLink": "Przejdź do swoich leków"
"medicationsLink": "Przejdź do swoich leków",
"detail": "Jedno powiadomienie na niedobór; po uzupełnieniu uzbraja się ponownie."
},
"eventMedicationReminder": "Przypomnienie o leku",
"eventMedicationReminderDesc": "Wysyłane o każdej zaplanowanej godzinie dawki.",
@@ -9536,7 +9548,7 @@
},
"invite": {
"title": "Zaproś kogoś",
"description": "Nic nie jest udostępniane, dopóki ta osoba nie zaakceptuje. Do tego czasu zaproszenie leży w jej ustawieniach i nic jej nie daje.",
"description": "Nic nie jest udostępniane, dopóki ta osoba nie zaakceptuje.",
"identifierLabel": "Nazwa użytkownika lub e-mail",
"identifierPlaceholder": "Nazwa, którą się loguje",
"identifierHint": "Ta osoba potrzebuje konta na tej instancji. Nie można wysłać zaproszenia na niezarejestrowany adres.",
@@ -9590,7 +9602,7 @@
},
"received": {
"title": "Karty, które mogę otworzyć",
"description": "Zaproszenie nic ci nie daje, dopóki go nie zaakceptujesz. Dostęp możesz zwrócić w dowolnej chwili.",
"description": "Zaproszenie nic ci nie daje, dopóki go nie zaakceptujesz.",
"emptyTitle": "Nikt nie udostępnił ci karty",
"emptyDescription": "Kiedy ktoś cię zaprosi, zaproszenie pojawi się tutaj.",
"loadError": "Nie udało się wczytać listy.",
@@ -9602,7 +9614,8 @@
"renounce": "Zwróć",
"renounceTitle": "Zwrócić kartę użytkownika {name}?",
"renounceBody": "Tracisz dostęp natychmiast. Ta osoba może zaprosić cię ponownie w dowolnej chwili.",
"renounceConfirm": "Zwróć"
"renounceConfirm": "Zwróć",
"detail": "Dostęp możesz zwrócić w dowolnej chwili."
},
"activity": {
"title": "Kiedy otwierano moją kartę",
+64
View File
@@ -0,0 +1,64 @@
/**
* Measures the description / hint strings under the `settings` and `admin`
* namespaces against the design standards' §3 rule: one sentence, guideline
* ≤ 120 characters.
*
* node scripts/measure-description-lengths.mjs [locale=de] [--top N]
*
* A review tool, not a gate — the gate is
* `src/components/settings/__tests__/card-description-length.test.ts`, which
* checks the strings that actually reach a card header's description slot.
* This one sweeps wider so a long hint in a body still shows up in a sweep.
*/
import { readFileSync } from "node:fs";
const locale = process.argv[2]?.startsWith("--") ? "de" : (process.argv[2] ?? "de");
const topIdx = process.argv.indexOf("--top");
const top = topIdx === -1 ? 40 : Number(process.argv[topIdx + 1]);
const NAME = /(description|subtitle|hint|note|explainer|help|body)$/i;
function walk(node, path, out) {
for (const [key, value] of Object.entries(node)) {
const next = path ? `${path}.${key}` : key;
if (typeof value === "string") {
if (NAME.test(key)) out.push([next, value]);
} else if (value && typeof value === "object") {
walk(value, next, out);
}
}
}
/** Sentence count: terminators followed by a space + capital, or at the end. */
function sentences(text) {
const trimmed = text.trim();
if (!trimmed) return 0;
const matches = trimmed.match(/[.!?](\s+[^\s]|$)/g);
return Math.max(1, matches ? matches.length : 1);
}
const bundle = JSON.parse(readFileSync(`messages/${locale}.json`, "utf8"));
const entries = [];
for (const ns of ["settings", "admin"]) {
if (bundle[ns]) walk(bundle[ns], ns, entries);
}
const lengths = entries.map(([, v]) => v.length).sort((a, b) => a - b);
const at = (q) => lengths[Math.min(lengths.length - 1, Math.floor(lengths.length * q))];
const multi = entries.filter(([, v]) => sentences(v) >= 2);
console.log(`locale ${locale} — ${entries.length} description/hint strings`);
console.log(
`length: min ${lengths[0]}, median ${at(0.5)}, p75 ${at(0.75)}, p90 ${at(0.9)}, max ${lengths.at(-1)}`,
);
console.log(
`> 120 chars: ${lengths.filter((n) => n > 120).length}, > 200: ${lengths.filter((n) => n > 200).length}`,
);
console.log(`>= 2 sentences: ${multi.length}, >= 3: ${entries.filter(([, v]) => sentences(v) >= 3).length}`);
console.log(`\nLONGEST ${top}:`);
for (const [key, value] of [...entries]
.sort((a, b) => b[1].length - a[1].length)
.slice(0, top)) {
console.log(`${String(value.length).padStart(4)}ch ${sentences(value)}S ${key}`);
console.log(` ${value.slice(0, 130)}${value.length > 130 ? "…" : ""}`);
}
+78
View File
@@ -0,0 +1,78 @@
/**
* Splits an over-long description at its first sentence boundary, in every
* locale, WITHOUT retranslating anything: the tail is already written in each
* language, it is just in the wrong slot.
*
* node scripts/split-description.mjs --dry <key> [<key> …]
* node scripts/split-description.mjs drop <key> [<key> …]
* node scripts/split-description.mjs move <key> <tailKey>
*
* drop — the description keeps sentence 1; the tail is deleted.
* move — the description keeps sentence 1; the tail lands under `tailKey`,
* to be rendered in the card body as foreground `text-sm`.
* --dry — print what each locale would become, change nothing.
*
* A terminator followed by whitespace and an upper-case letter (or a digit,
* or an opening quote) is a sentence boundary. `Claude.ai` and `z. B.` do not
* match: the first has no whitespace, the second continues lower-case.
*/
import { readFileSync, writeFileSync } from "node:fs";
const LOCALES = ["de", "en", "es", "fr", "it", "pl"];
const BOUNDARY = /(?<=[.!?])\s+(?=[A-ZÄÖÜÀÂÉÈÊÎÔÙÛÇŁŚŹŻĄĆĘŃÓ0-9„"„«])/u;
function get(bundle, path) {
return path
.split(".")
.reduce((node, part) => (node == null ? node : node[part]), bundle);
}
function set(bundle, path, value) {
const parts = path.split(".");
const last = parts.pop();
let node = bundle;
for (const part of parts) {
if (typeof node[part] !== "object" || node[part] === null) node[part] = {};
node = node[part];
}
node[last] = value;
}
const [mode, ...rest] = process.argv.slice(2);
const dry = mode === "--dry";
const keys = dry ? rest : mode === "move" ? [rest[0]] : rest;
const tailKey = mode === "move" ? rest[1] : null;
if (!mode || keys.length === 0) {
console.error("usage: --dry|drop|move <key> [tailKey]");
process.exit(1);
}
for (const locale of LOCALES) {
const file = `messages/${locale}.json`;
const bundle = JSON.parse(readFileSync(file, "utf8"));
let changed = false;
for (const key of keys) {
const value = get(bundle, key);
if (typeof value !== "string") {
console.log(`${locale} ${key}: MISSING`);
continue;
}
const parts = value.split(BOUNDARY);
if (parts.length < 2) {
console.log(`${locale} ${key}: single sentence, untouched`);
continue;
}
const head = parts[0].trim();
const tail = parts.slice(1).join(" ").trim();
if (dry) {
console.log(`${locale} ${key}\n head(${head.length}): ${head}\n tail: ${tail}`);
continue;
}
set(bundle, key, head);
if (tailKey) set(bundle, tailKey, tail);
changed = true;
}
if (changed) {
writeFileSync(file, `${JSON.stringify(bundle, null, 2)}\n`, "utf8");
console.log(`${locale}: written`);
}
}
@@ -133,6 +133,9 @@ export function AppLogPreviewSection() {
<RefreshCw
className={`h-4 w-4 ${isFetching ? "animate-spin" : ""} motion-reduce:animate-none`}
/>
<p className="text-sm">
{t("admin.section.app-logs.processDetail")}
</p>
</Button>
}
/>
@@ -97,6 +97,8 @@ export function DangerZoneSection() {
title={t("admin.deleteAllData")}
description={t("admin.deleteAllDescription")}
/>
<p className="text-sm">{t("admin.deleteAllDetail")}</p>
{wipeMsg && (
<p
className={`mt-3 text-sm ${
@@ -115,6 +115,10 @@ export function EncryptionSection() {
title={t("admin.section.encryption.coverageTitle")}
description={t("admin.section.encryption.coverageDescription")}
/>
<p className="text-sm">
{t("admin.section.encryption.coverageDetail")}
</p>
{statusQuery.isLoading ? (
<div className="text-muted-foreground flex items-center gap-2 text-sm">
<Loader2 className="h-4 w-4 animate-spin motion-reduce:animate-none" />
@@ -94,6 +94,8 @@ export function ModuleAvailabilitySection() {
title={t("admin.modules.title")}
description={t("admin.modules.description")}
/>
<p className="text-sm">{t("admin.modules.detail")}</p>
<div className="space-y-4">
{MODULE_KEYS
// A module switched off in code (pending a rebuild) is hard-off
@@ -0,0 +1,146 @@
import { describe, it, expect } from "vitest";
import { readFileSync, readdirSync, statSync } from "node:fs";
import { join } from "node:path";
/**
* The description slot holds ONE sentence, guideline ≤ 120 characters
* (design standards §3).
*
* Scope is deliberately the strings that actually reach a card header's
* description slot — `<SettingsCardHeader description={t("…")}>` — because
* that is the slot the rule is about. A hint in a card body is prose and is
* allowed to be prose; `scripts/measure-description-lengths.mjs` sweeps those
* for review without gating them.
*
* Both EN and DE are checked. DE runs 15-25% longer than EN, and a rule that
* only holds in English produces a slot that wraps to two lines in half the
* locales — which is the same defect, moved.
*/
const ROOT = process.cwd();
const MAX_CHARS = 120;
/**
* Strings that stay longer than the guideline, each with the reason. The
* guideline is not a hard cut: a sentence that needs 130 characters to stay
* true is fine. Two sentences never are, and that half of the rule has no
* exceptions.
*/
const LENGTH_EXCEPTIONS: Record<string, string> = {
// Empty on purpose: the sweep that introduced this guard left nothing over
// the guideline in EN or DE. An entry here needs a sentence saying what the
// extra characters buy, and `carries no stale exception` deletes it again
// the moment the string fits or stops being a description.
};
function walk(dir: string, out: string[] = []): string[] {
for (const entry of readdirSync(dir)) {
const full = join(dir, entry);
if (statSync(full).isDirectory()) {
if (entry === "__tests__" || entry === "node_modules") continue;
walk(full, out);
} else if (entry.endsWith(".tsx")) {
out.push(full);
}
}
return out;
}
/** Every `description={t("…")}` inside a `<SettingsCardHeader …>` element. */
function describedKeys(): Map<string, string[]> {
const keys = new Map<string, string[]>();
for (const dir of ["settings", "admin", "measurement-reminders"]) {
for (const file of walk(join(ROOT, "src", "components", dir))) {
const source = readFileSync(file, "utf8");
const header = /<SettingsCardHeader\b/g;
let match: RegExpExecArray | null;
while ((match = header.exec(source))) {
// The element ends at the first `/>` or `>` that closes the opening
// tag; a `description={t("x")}` after that belongs to something else.
const end = source.indexOf("/>", match.index);
const slice = source.slice(match.index, end === -1 ? undefined : end);
const desc = /description=\{t\(\s*"([^"]+)"/.exec(slice);
if (!desc) continue;
const list = keys.get(desc[1]) ?? [];
list.push(file.slice(ROOT.length + 1));
keys.set(desc[1], list);
}
}
}
return keys;
}
function resolve(bundle: unknown, key: string): string | undefined {
const value = key
.split(".")
.reduce<unknown>(
(node, part) =>
node && typeof node === "object"
? (node as Record<string, unknown>)[part]
: undefined,
bundle,
);
return typeof value === "string" ? value : undefined;
}
/** Sentence terminators followed by a space or the end of the string. */
function sentenceCount(text: string): number {
const trimmed = text.trim();
if (!trimmed) return 0;
const matches = trimmed.match(/[.!?](\s+\S|$)/g);
return Math.max(1, matches ? matches.length : 1);
}
describe("card header descriptions — one sentence, ≤120 characters", () => {
const keys = describedKeys();
it("finds the description slots it is meant to check", () => {
// A matcher that quietly matches nothing passes every assertion below it.
expect(keys.size).toBeGreaterThan(25);
});
for (const locale of ["en", "de"] as const) {
const bundle = JSON.parse(
readFileSync(join(ROOT, "messages", `${locale}.json`), "utf8"),
);
it(`${locale}: every description is a single sentence`, () => {
const offenders: string[] = [];
for (const [key, files] of keys) {
const value = resolve(bundle, key);
if (value === undefined) continue; // the i18n coverage guard owns this
if (sentenceCount(value) > 1) {
offenders.push(`${key} (${sentenceCount(value)} sentences) — ${files[0]}`);
}
}
expect(offenders).toEqual([]);
});
it(`${locale}: every description is within the length guideline`, () => {
const offenders: string[] = [];
for (const [key, files] of keys) {
const value = resolve(bundle, key);
if (value === undefined) continue;
if (value.length > MAX_CHARS && !(key in LENGTH_EXCEPTIONS)) {
offenders.push(`${key} (${value.length} chars) — ${files[0]}`);
}
}
expect(offenders).toEqual([]);
});
}
it("carries no stale exception", () => {
const bundle = JSON.parse(
readFileSync(join(ROOT, "messages", "en.json"), "utf8"),
);
for (const [key, reason] of Object.entries(LENGTH_EXCEPTIONS)) {
expect(keys.has(key), `${key} is allowlisted but no longer a description`)
.toBe(true);
expect(
resolve(bundle, key)!.length,
`${key} is allowlisted but now fits the guideline`,
).toBeGreaterThan(MAX_CHARS);
expect(reason.length).toBeGreaterThan(20);
}
});
});
@@ -55,7 +55,7 @@ describe("<CoachSection> — SSR smoke", () => {
expect(html).toContain('data-testid="settings-disable-coach-switch"');
// v1.18.1 (D7) — polarity flipped to activate/default-on.
expect(html).toContain("Activate Coach");
expect(html).toContain("Show the Coach button and drawer. On by default");
expect(html).toContain("Show the Coach button and drawer");
// v1.18.6 (W9) — the proactive Coach nudge card moved here from
// Benachrichtigungen; its anchor renders under the enabled-coach gate.
expect(html).toContain('id="coach-nudge"');
@@ -68,7 +68,6 @@ describe("<CoachSection> — SSR smoke", () => {
authState.disableCoach = false;
const html = render("de");
expect(html).toContain("Coach aktivieren");
expect(html).toContain("Standardmäßig an");
expect(html).not.toContain("settings.sections.coach.");
expect(html).not.toContain("settings.coach.activate.");
});
@@ -150,6 +150,6 @@ describe("Settings — DisableCoachCard", () => {
const html = render();
// v1.18.1 (D7) — polarity flipped to activate/default-on.
expect(html).toContain("Activate Coach");
expect(html).toContain("Show the Coach button and drawer. On by default");
expect(html).toContain("Show the Coach button and drawer");
});
});
+1 -6
View File
@@ -194,11 +194,7 @@ export function AboutSection() {
which read as a separate field even though the two values belong
on the same row. */}
<SettingsCard>
<SettingsCardHeader
icon={Info}
title="HealthLog"
description={t("settings.about.identityDescription")}
/>
<SettingsCardHeader icon={Info} title="HealthLog" />
{isLoading || !version ? (
<div className="flex items-center gap-2">
@@ -271,7 +267,6 @@ export function AboutSection() {
<SettingsCardHeader
icon={BookOpen}
title={t("settings.about.linksHeading")}
description={t("settings.about.linksDescription")}
/>
<SettingsCardActions align="start">
<Button
@@ -68,6 +68,8 @@ export function GrantsReceivedCard() {
title={t("recordSharing.received.title")}
description={t("recordSharing.received.description")}
/>
<p className="text-sm">{t("recordSharing.received.detail")}</p>
<div>
{isLoading && (
<Loader2
@@ -148,6 +148,7 @@ function ResearchModeCard() {
title={t("settings.researchMode.sectionTitle")}
description={t("settings.researchMode.subtitle")}
/>
<p className="text-sm">{t("settings.researchMode.subtitleDetail")}</p>
{showRePrompt && (
<SettingsInfoTile
@@ -262,6 +263,7 @@ function DataResetCard() {
title={t("settings.dangerZone")}
description={t("settings.dangerZoneDescription")}
/>
<p className="text-sm">{t("settings.dangerZoneDetail")}</p>
{msg && (
<p
@@ -399,6 +401,7 @@ function AccountDeleteCard() {
title={t("settings.deleteAccountCardTitle")}
description={t("settings.deleteAccountCardDescription")}
/>
<p className="text-sm">{t("settings.deleteAccountCardDetail")}</p>
{msg && (
<p
+1 -3
View File
@@ -213,9 +213,7 @@ function ApiTokensCard() {
authenticated route accepted. The per-medication API-endpoint
toggle issues the pair that actually works, scoped to one
medication. This card lists and revokes. */}
<p className="text-muted-foreground text-sm">
{t("settings.tokenMintMovedDescription")}
</p>
<p className="text-sm">{t("settings.tokenMintMovedDescription")}</p>
<div>
<p className="mb-2 text-sm font-medium">
@@ -149,6 +149,7 @@ export function CoachMemorySection({
title={t("settings.ai.coachMemory.title")}
description={t("settings.ai.coachMemory.description")}
/>
<p className="text-sm">{t("settings.ai.coachMemory.detail")}</p>
{query.isError && (
<p
@@ -162,6 +162,7 @@ export function CoachRemindersSection({
title={t("settings.ai.coachReminders.title")}
description={t("settings.ai.coachReminders.description")}
/>
<p className="text-sm">{t("settings.ai.coachReminders.detail")}</p>
{query.isError && (
<p
@@ -66,6 +66,8 @@ export function CycleTrackingCard({
title={t("settings.cycleTracking.title")}
description={t("settings.cycleTracking.description")}
/>
<p className="text-sm">{t("settings.cycleTracking.detail")}</p>
<div className="space-y-4">
<div className="flex items-start justify-between gap-4">
<Label htmlFor="cycle-tracking-enable" className="text-sm">
@@ -265,6 +265,7 @@ export function InsightsPillOrderSection({ id }: { id?: string }) {
title={t("insights.pillOrder.title")}
description={t("insights.pillOrder.description")}
/>
<p className="text-sm">{t("insights.pillOrder.detail")}</p>
{isLoading ? (
<p
@@ -61,7 +61,7 @@ describe("<AppleHealthCard>", () => {
expect(html).toContain('data-testid="apple-health-card"');
expect(html).toContain("HealthLog iOS app");
expect(html).toContain("not a web or OAuth connection");
expect(html).toContain("not from a web or OAuth connection");
expect(html).toContain("Settings → Apple Health");
expect(html).toContain("when iOS grants background time");
expect(html).not.toContain(">Connected<");
@@ -234,6 +234,8 @@ export function LowStockCard({
disabled={!isAuthenticated || saving}
aria-label={t("notifications.lowStock.toggleAria")}
/>
<p className="text-sm">{t("notifications.lowStock.detail")}</p>
<span className="text-muted-foreground text-xs">
{enabled
? t("notifications.lowStock.statusOn")
@@ -91,6 +91,9 @@ export function ManagedIntegrationStatus() {
title={t("settings.sections.integrations.title")}
description={t("settings.sharedRecord.integrationStatusDescription")}
/>
<p className="text-sm">
{t("settings.sharedRecord.integrationStatusDetail")}
</p>
</SettingsCard>
);
}
+2
View File
@@ -107,6 +107,7 @@ function McpConnectionsCard() {
title={t("settings.mcp.connectionsTitle")}
description={t("settings.mcp.connectionsDescription")}
/>
<p className="text-sm">{t("settings.mcp.connectionsDetail")}</p>
<div className="space-y-4">
{list.length === 0 ? (
@@ -336,6 +337,7 @@ function McpTokensCard() {
title={t("settings.mcp.tokensTitle")}
description={t("settings.mcp.tokensDescription")}
/>
<p className="text-sm">{t("settings.mcp.tokensDetail")}</p>
<div className="space-y-4">
<p className="text-muted-foreground text-xs leading-relaxed">
@@ -193,6 +193,8 @@ export function MoodReminderCard({
disabled={!isAuthenticated || saving}
aria-label={t("notifications.moodReminder.toggleAria")}
/>
<p className="text-sm">{t("notifications.moodReminder.detail")}</p>
<span className="text-muted-foreground text-xs">
{enabled
? t("notifications.moodReminder.statusOn")
@@ -30,6 +30,7 @@ import {
} from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { EmptyState } from "@/components/ui/empty-state";
import { Button } from "@/components/ui/button";
import { useAuth } from "@/hooks/use-auth";
import { useMounted } from "@/hooks/use-mounted";
@@ -148,6 +149,10 @@ export function NotificationStatusCard() {
<SettingsCardHeader
icon={Bell}
title={t("settings.notificationStatus.title")}
/>
<EmptyState
size="compact"
title={t("settings.notificationStatus.emptyTitle")}
description={t("settings.notificationStatus.emptyDescription")}
/>
</SettingsCard>
@@ -78,6 +78,9 @@ export function NutrientIntakeCard() {
days: WINDOW_DAYS,
})}
/>
<p className="text-sm">
{t("settings.sections.sources.nutrients.detail")}
</p>
{overview.isLoading ? (
<div className="space-y-1" aria-hidden="true">
<Skeleton className="h-9 w-full rounded-md" />
@@ -16,7 +16,6 @@ import Link from "next/link";
import {
Database,
Download,
Lock,
ShieldCheck,
Trash2,
Clock,
@@ -67,21 +66,11 @@ export function PrivacySection() {
return (
<div className="space-y-6">
{/* Intro */}
<SettingsInfoTile
icon={Lock}
tone="info"
title={t("settings.privacy.intro.title")}
>
{t("settings.privacy.intro.body")}
</SettingsInfoTile>
{/* Encryption at rest */}
<SettingsCard>
<SettingsCardHeader
icon={ShieldCheck}
title={t("settings.privacy.encryption.title")}
description={t("settings.privacy.encryption.description")}
/>
<div className="space-y-3">
<p className="text-sm">
@@ -101,7 +90,6 @@ export function PrivacySection() {
<SettingsCardHeader
icon={Database}
title={t("settings.privacy.stored.title")}
description={t("settings.privacy.stored.description")}
/>
<ul className="text-muted-foreground list-disc space-y-1 text-sm">
<li>{t("settings.privacy.stored.measurements")}</li>
@@ -138,6 +138,7 @@ function ShareLinksCard() {
title={t("settings.sharing.createTitle")}
description={t("settings.sharing.createDescription")}
/>
<p className="text-sm">{t("settings.sharing.createDetail")}</p>
<ShareLinkCreateForm
key={prefill?.key ?? "new"}
@@ -315,6 +315,8 @@ export function SourcesSection() {
}
/>
<p className="text-sm">{t("settings.sections.sources.helpDetail")}</p>
{isLoading || !priority ? (
<SourcesSkeletonList />
) : (