diff --git a/messages/de.json b/messages/de.json
index 36c227c57..21ee4c4f2 100644
--- a/messages/de.json
+++ b/messages/de.json
@@ -414,7 +414,7 @@
},
"measurements": {
"title": "Messungen",
- "subtitle": "Gewicht, Blutdruck, Puls und mehr",
+ "subtitle": "Werte erfassen und die vollständige Historie einsehen. Trends und Auswertungen findest du unter Auswertungen.",
"addMeasurement": "Hinzufügen",
"editMeasurement": "Messwert bearbeiten",
"type": "Typ",
@@ -1892,7 +1892,8 @@
"bmiEmptyAction": "Account-Einstellungen öffnen",
"noAnalysisYet": "Noch keine Einschätzung vorhanden.",
"assessmentPreparing": "Einschätzung wird vorbereitet…",
- "noProviderConfigured": "Einschätzung nicht verfügbar.",
+ "noProviderConfigured": "Verbinde einen KI-Anbieter, um hier eine Einschätzung zu deinen Werten zu sehen.",
+ "noProviderAction": "Zu den KI-Einstellungen",
"startAnalysis": "Einschätzung starten",
"refreshAnalysis": "Einschätzung aktualisieren",
"keyTakeaway": "Das Wichtigste",
@@ -1965,6 +1966,7 @@
"progressAria": "Gesundheitsscore-Fortschritt",
"deltaVsLastWeek": "{delta} im Vergleich zur Vorwoche",
"deltaUnavailable": "Noch keine Historie — schau nächste Woche wieder vorbei.",
+ "restModePaused": "Während einer Krankheitsphase pausiert — dein Score wird heute nicht bewertet.",
"componentBp": "Blutdruck",
"componentWeight": "Gewicht",
"componentMood": "Stimmung",
@@ -2007,7 +2009,7 @@
"emptyAction": "Briefing erstellen",
"loadingLabel": "Briefing wird zusammengestellt",
"noProviderTitle": "Kein KI-Anbieter verbunden",
- "noProviderDescription": "Verbinde einen KI-Anbieter in den Einstellungen, um tägliche Briefings zu erhalten.",
+ "noProviderDescription": "Das Tagesbriefing fasst deine Werte in Klartext zusammen. Dafür braucht HealthLog einen KI-Anbieter — nutze deinen eigenen Schlüssel (OpenAI, Anthropic) oder ein lokales Modell. Deine Daten bleiben dabei bei dir.",
"noProviderAction": "Zu den KI-Einstellungen"
},
"trendsRow": {
@@ -2072,6 +2074,8 @@
"refusalInjection": "In deiner Nachricht stehen Anweisungen, die meine Vorgaben überschreiben sollen. Ich bin der HealthLog-Coach und fasse ausschließlich deine eigenen Gesundheitsdaten zusammen — formuliere deine Frage bitte in diesem Rahmen neu.",
"errorBudget": "Du hast das heutige Coach-Limit erreicht. Das Budget setzt sich um Mitternacht (UTC) zurück.",
"errorProvider": "Der Coach konnte gerade keinen Auswertungs-Anbieter erreichen. Bitte versuche es in einem Moment erneut.",
+ "errorNoProvider": "Der Coach liest deine erfassten Gesundheitsdaten und beantwortet Fragen dazu (keine medizinische Beratung). Dafür braucht HealthLog einen KI-Anbieter — verbinde deinen eigenen Schlüssel oder ein lokales Modell.",
+ "errorNoProviderAction": "KI-Anbieter verbinden",
"errorNetwork": "Keine Internetverbindung — versuche es erneut, sobald du online bist.",
"prefsSaveError": "Coach-Einstellungen konnten nicht gespeichert werden. Bitte versuche es erneut.",
"errorCredentialExpired": "Deine Verbindung zum KI-Anbieter ist abgelaufen. Stelle sie in den Einstellungen wieder her, damit der Coach weiterläuft.",
@@ -2709,6 +2713,7 @@
"title": "Glukose-Überblick",
"subtitle": "Ein Blick auf deine letzten {days} Tage, aus {count} Messungen.",
"spotCaveat": "Das sind Schätzungen aus einzelnen Stichproben-Messungen, kein Profil eines kontinuierlichen Sensors – lies sie als Richtung, nicht als klinisches Urteil.",
+ "labsLink": "Laborergebnisse? HbA1c und Nüchternglukose aus einer Blutabnahme trägst du unter Labor ein.",
"learningTitle": "Lerne deine Glukose noch kennen",
"learningBody": "Bisher sind das {count} über {days}. Mit etwas mehr Verlauf wird das Bild klarer – mess weiter oder lass es ein verbundenes Gerät ergänzen.",
"learningBodyEmpty": "Sobald ein paar Messungen vorliegen – von dir erfasst oder von einem Gerät synchronisiert – nehmen Zeit-im-Zielbereich, Durchschnitt und Schwankung hier Gestalt an.",
@@ -3623,7 +3628,10 @@
"learning": "Manche Einblicke – Glukose, Schlafrhythmus, Blutdrucktrends, Zyklus – schärfen sich in den ersten ein bis zwei Wochen, sobald sich deine Daten ansammeln. Leere Kacheln heißen jetzt nur, dass wir deine Basislinie noch lernen.",
"connectCta": "Gerät verbinden",
"logCta": "Ersten Wert eintragen",
- "importCta": "Bestehende Daten importieren"
+ "importCta": "Bestehende Daten importieren",
+ "aiTitle": "KI-Einblicke aktivieren (optional)",
+ "aiBody": "Coach, Tagesbriefing und Einschätzungen fassen deine Werte in Klartext zusammen. Dafür verbindest du deinen eigenen KI-Schlüssel (OpenAI, Anthropic) oder ein lokales Modell — deine Daten bleiben bei dir.",
+ "aiCta": "KI-Anbieter verbinden"
},
"welcomeBack": {
"title": "Willkommen zurück",
diff --git a/messages/en.json b/messages/en.json
index 18cf5182a..e91951fe9 100644
--- a/messages/en.json
+++ b/messages/en.json
@@ -414,7 +414,7 @@
},
"measurements": {
"title": "Measurements",
- "subtitle": "Weight, blood pressure, pulse and more",
+ "subtitle": "Log readings and browse the full history. Trends and analysis live under Insights.",
"addMeasurement": "Add",
"editMeasurement": "Edit measurement",
"type": "Type",
@@ -1892,7 +1892,8 @@
"bmiEmptyTitle": "Set your height to see BMI",
"bmiEmptyDescription": "BMI needs your height. Add it under Settings → Account.",
"bmiEmptyAction": "Open account settings",
- "noProviderConfigured": "Assessment unavailable.",
+ "noProviderConfigured": "Connect an AI provider to see an assessment of your readings here.",
+ "noProviderAction": "Open AI settings",
"startAnalysis": "Start assessment",
"refreshAnalysis": "Refresh assessment",
"keyTakeaway": "Key Takeaway",
@@ -1965,6 +1966,7 @@
"progressAria": "Health Score progress",
"deltaVsLastWeek": "{delta} vs last week",
"deltaUnavailable": "No history yet — check back next week.",
+ "restModePaused": "Paused during illness — your score isn't being judged today.",
"componentBp": "BP",
"componentWeight": "Weight",
"componentMood": "Mood",
@@ -2007,7 +2009,7 @@
"emptyAction": "Generate briefing",
"loadingLabel": "Synthesising today's briefing",
"noProviderTitle": "No AI provider connected",
- "noProviderDescription": "Connect an AI provider in settings to receive daily briefings.",
+ "noProviderDescription": "The daily briefing turns your readings into plain-language summary. HealthLog needs an AI provider for that — use your own key (OpenAI, Anthropic) or a local model. Your data stays with you.",
"noProviderAction": "Open AI settings"
},
"trendsRow": {
@@ -2072,6 +2074,8 @@
"refusalInjection": "I noticed wording that tries to override my instructions. I am the HealthLog Coach and only summarise your own health data — please rephrase your question in those terms.",
"errorBudget": "You've reached today's Coach usage limit. The budget refreshes at midnight UTC.",
"errorProvider": "The Coach could not reach an Insights provider just now. Please try again in a moment.",
+ "errorNoProvider": "The Coach reads your logged health data and answers questions about it (not medical advice). HealthLog needs an AI provider for that — connect your own key or a local model.",
+ "errorNoProviderAction": "Connect an AI provider",
"errorNetwork": "No internet connection — try again once you're back online.",
"prefsSaveError": "Couldn't save your Coach preferences. Please try again.",
"errorCredentialExpired": "Your AI provider connection has expired. Reconnect it in Settings to keep the Coach running.",
@@ -2709,6 +2713,7 @@
"title": "Glucose overview",
"subtitle": "A read on your last {days} days, from {count} readings.",
"spotCaveat": "These are spot-reading estimates from individual measurements, not a continuous-monitor profile — read them as a direction of travel rather than a clinical verdict.",
+ "labsLink": "Lab results? Log a blood-draw HbA1c or fasting glucose under Labs.",
"learningTitle": "Still learning your glucose",
"learningBody": "So far that's {count} over {days}. A little more history and a fuller picture comes into focus — keep logging, or let a connected device fill it in.",
"learningBodyEmpty": "Once a handful of readings come in — logged by you or synced from a device — your time-in-range, average, and variability take shape here.",
@@ -3623,7 +3628,10 @@
"learning": "Some insights — glucose, sleep rhythm, blood-pressure trends, cycle — sharpen over the first week or two as your data builds up. Empty tiles now just mean we're still learning your baseline.",
"connectCta": "Connect a device",
"logCta": "Log your first measurement",
- "importCta": "Import existing history"
+ "importCta": "Import existing history",
+ "aiTitle": "Turn on AI Insights (optional)",
+ "aiBody": "Coach, the daily briefing and assessments turn your readings into plain language. For that you connect your own AI key (OpenAI, Anthropic) or a local model — your data stays with you.",
+ "aiCta": "Connect an AI provider"
},
"welcomeBack": {
"title": "Welcome back",
diff --git a/messages/es.json b/messages/es.json
index 1b2e2c7a0..6f2928108 100644
--- a/messages/es.json
+++ b/messages/es.json
@@ -414,7 +414,7 @@
},
"measurements": {
"title": "Mediciones",
- "subtitle": "Peso, tensión arterial, pulso y más",
+ "subtitle": "Registra lecturas y consulta el historial completo. Las tendencias y los análisis están en Análisis.",
"addMeasurement": "Añadir",
"editMeasurement": "Editar medición",
"type": "Tipo",
@@ -1892,7 +1892,8 @@
"bmiEmptyTitle": "Añade tu altura para calcular el IMC",
"bmiEmptyDescription": "El IMC necesita tu altura. Guárdala en Ajustes → Cuenta.",
"bmiEmptyAction": "Abrir ajustes de cuenta",
- "noProviderConfigured": "Valoración no disponible.",
+ "noProviderConfigured": "Conecta un proveedor de IA para ver aquí una valoración de tus lecturas.",
+ "noProviderAction": "Abrir ajustes de IA",
"startAnalysis": "Iniciar valoración",
"refreshAnalysis": "Actualizar valoración",
"keyTakeaway": "Lo más importante",
@@ -1965,6 +1966,7 @@
"progressAria": "Progreso de la puntuación de salud",
"deltaVsLastWeek": "{delta} respecto a la semana anterior",
"deltaUnavailable": "Aún no hay historial — vuelve la semana que viene.",
+ "restModePaused": "En pausa durante la enfermedad — hoy tu puntuación no se evalúa.",
"componentBp": "PA",
"componentWeight": "Peso",
"componentMood": "Estado de ánimo",
@@ -2007,7 +2009,7 @@
"emptyAction": "Generar resumen",
"loadingLabel": "Preparando el resumen",
"noProviderTitle": "Ningún proveedor de IA conectado",
- "noProviderDescription": "Conecta un proveedor de IA en los ajustes para recibir resúmenes diarios.",
+ "noProviderDescription": "El briefing diario resume tus lecturas en lenguaje claro. HealthLog necesita un proveedor de IA para ello: usa tu propia clave (OpenAI, Anthropic) o un modelo local. Tus datos siguen siendo tuyos.",
"noProviderAction": "Abrir ajustes de IA"
},
"trendsRow": {
@@ -2072,6 +2074,8 @@
"refusalInjection": "Tu mensaje contiene instrucciones que pretenden sobrescribir mis indicaciones. Soy el coach de HealthLog y solo resumo tus propios datos de salud — reformula tu pregunta dentro de ese marco.",
"errorBudget": "Has alcanzado el límite diario del coach. El cupo se reinicia a medianoche (UTC).",
"errorProvider": "El coach no ha podido contactar con un proveedor de análisis ahora mismo. Inténtalo en un momento.",
+ "errorNoProvider": "El coach lee tus datos de salud registrados y responde preguntas sobre ellos (no es consejo médico). HealthLog necesita un proveedor de IA para ello: conecta tu propia clave o un modelo local.",
+ "errorNoProviderAction": "Conectar un proveedor de IA",
"errorNetwork": "Sin conexión a Internet — inténtalo de nuevo cuando vuelvas a estar en línea.",
"prefsSaveError": "No se pudieron guardar tus preferencias del Coach. Inténtalo de nuevo.",
"errorCredentialExpired": "Tu conexión con el proveedor de IA ha caducado. Vuelve a conectarla en Ajustes para que el Coach siga funcionando.",
@@ -2709,6 +2713,7 @@
"title": "Resumen de glucosa",
"subtitle": "Una lectura de tus últimos {days} días, a partir de {count} mediciones.",
"spotCaveat": "Son estimaciones a partir de mediciones puntuales, no un perfil de un sensor continuo: léelas como una tendencia, no como un veredicto clínico.",
+ "labsLink": "¿Resultados de laboratorio? Registra una HbA1c o glucosa en ayunas de una analítica en Laboratorio.",
"learningTitle": "Aún conociendo tu glucosa",
"learningBody": "De momento son {count} en {days}. Con algo más de historial el panorama se aclara: sigue registrando o deja que lo complete un dispositivo conectado.",
"learningBodyEmpty": "En cuanto lleguen unas cuantas mediciones —registradas por ti o sincronizadas desde un dispositivo— tu tiempo en rango, promedio y variabilidad tomarán forma aquí.",
@@ -3623,7 +3628,10 @@
"learning": "Algunas conclusiones —glucosa, ritmo de sueño, tendencias de presión arterial, ciclo— se afinan durante la primera semana o dos a medida que se acumulan tus datos. Las casillas vacías ahora solo significan que aún estamos aprendiendo tu línea base.",
"connectCta": "Conectar un dispositivo",
"logCta": "Registra tu primera medición",
- "importCta": "Importar historial existente"
+ "importCta": "Importar historial existente",
+ "aiTitle": "Activar Insights con IA (opcional)",
+ "aiBody": "El coach, el briefing diario y las valoraciones convierten tus lecturas en lenguaje claro. Para ello conectas tu propia clave de IA (OpenAI, Anthropic) o un modelo local: tus datos siguen siendo tuyos.",
+ "aiCta": "Conectar un proveedor de IA"
},
"welcomeBack": {
"title": "Bienvenido de nuevo",
diff --git a/messages/fr.json b/messages/fr.json
index e278cc028..d2a4076fe 100644
--- a/messages/fr.json
+++ b/messages/fr.json
@@ -414,7 +414,7 @@
},
"measurements": {
"title": "Mesures",
- "subtitle": "Poids, tension artérielle, pouls et plus",
+ "subtitle": "Saisis tes mesures et consulte l'historique complet. Les tendances et analyses sont dans Analyses.",
"addMeasurement": "Ajouter",
"editMeasurement": "Modifier la mesure",
"type": "Type",
@@ -1892,7 +1892,8 @@
"bmiEmptyTitle": "Ajoute ta taille pour l’IMC",
"bmiEmptyDescription": "L’IMC nécessite ta taille. Renseigne-la dans Paramètres → Compte.",
"bmiEmptyAction": "Ouvrir les paramètres du compte",
- "noProviderConfigured": "Évaluation indisponible.",
+ "noProviderConfigured": "Connecte un fournisseur d'IA pour voir ici une évaluation de tes mesures.",
+ "noProviderAction": "Ouvrir les réglages IA",
"startAnalysis": "Lancer l’évaluation",
"refreshAnalysis": "Actualiser l’évaluation",
"keyTakeaway": "L’essentiel",
@@ -1965,6 +1966,7 @@
"progressAria": "Progression du score de santé",
"deltaVsLastWeek": "{delta} par rapport à la semaine précédente",
"deltaUnavailable": "Pas encore d’historique — reviens la semaine prochaine.",
+ "restModePaused": "En pause pendant la maladie — ton score n'est pas évalué aujourd'hui.",
"componentBp": "TA",
"componentWeight": "Poids",
"componentMood": "Humeur",
@@ -2007,7 +2009,7 @@
"emptyAction": "Générer le briefing",
"loadingLabel": "Préparation du briefing",
"noProviderTitle": "Aucun fournisseur d'IA connecté",
- "noProviderDescription": "Connecte un fournisseur d'IA dans les réglages pour recevoir des briefings quotidiens.",
+ "noProviderDescription": "Le briefing quotidien résume tes mesures en langage clair. HealthLog a besoin d'un fournisseur d'IA pour cela : utilise ta propre clé (OpenAI, Anthropic) ou un modèle local. Tes données restent chez toi.",
"noProviderAction": "Ouvrir les réglages IA"
},
"trendsRow": {
@@ -2072,6 +2074,8 @@
"refusalInjection": "Ton message contient des instructions qui cherchent à remplacer mes consignes. Je suis le coach HealthLog et je ne résume que tes propres données de santé — reformule ta question dans ce cadre.",
"errorBudget": "Tu as atteint la limite quotidienne du coach. Le quota se réinitialise à minuit (UTC).",
"errorProvider": "Le coach n’a pas pu joindre un fournisseur d’analyse maintenant. Réessaie dans un instant.",
+ "errorNoProvider": "Le coach lit tes données de santé enregistrées et répond aux questions à leur sujet (pas un avis médical). HealthLog a besoin d'un fournisseur d'IA pour cela : connecte ta propre clé ou un modèle local.",
+ "errorNoProviderAction": "Connecter un fournisseur d'IA",
"errorNetwork": "Pas de connexion Internet — réessaie une fois reconnecté.",
"prefsSaveError": "Impossible d’enregistrer vos préférences du Coach. Réessayez.",
"errorCredentialExpired": "Ta connexion au fournisseur d'IA a expiré. Reconnecte-la dans les Réglages pour que le Coach continue de fonctionner.",
@@ -2709,6 +2713,7 @@
"title": "Aperçu du glucose",
"subtitle": "Un point sur vos {days} derniers jours, à partir de {count} mesures.",
"spotCaveat": "Ce sont des estimations issues de mesures ponctuelles, pas le profil d’un capteur continu : lisez-les comme une tendance, non comme un verdict clinique.",
+ "labsLink": "Résultats de laboratoire ? Saisis une HbA1c ou une glycémie à jeun issue d'une prise de sang dans Laboratoire.",
"learningTitle": "Votre glucose se découvre encore",
"learningBody": "Pour l’instant, cela fait {count} sur {days}. Avec un peu plus d’historique, le tableau se précise : continuez à enregistrer ou laissez un appareil connecté compléter.",
"learningBodyEmpty": "Dès que quelques mesures arrivent — saisies par vous ou synchronisées depuis un appareil — votre temps dans la cible, votre moyenne et votre variabilité prennent forme ici.",
@@ -3623,7 +3628,10 @@
"learning": "Certaines analyses — glycémie, rythme de sommeil, tendances de tension, cycle — s'affinent au cours de la première semaine ou deux à mesure que vos données s'accumulent. Des tuiles vides signifient simplement que nous apprenons encore votre référence.",
"connectCta": "Connecter un appareil",
"logCta": "Enregistrez votre première mesure",
- "importCta": "Importer un historique existant"
+ "importCta": "Importer un historique existant",
+ "aiTitle": "Activer les Insights IA (facultatif)",
+ "aiBody": "Le coach, le briefing quotidien et les évaluations traduisent tes mesures en langage clair. Pour cela, connecte ta propre clé IA (OpenAI, Anthropic) ou un modèle local — tes données restent chez toi.",
+ "aiCta": "Connecter un fournisseur d'IA"
},
"welcomeBack": {
"title": "Bon retour",
diff --git a/messages/it.json b/messages/it.json
index e5fb79eb1..8873b3ec7 100644
--- a/messages/it.json
+++ b/messages/it.json
@@ -414,7 +414,7 @@
},
"measurements": {
"title": "Misurazioni",
- "subtitle": "Peso, pressione arteriosa, pulsazioni e altro",
+ "subtitle": "Registra le misurazioni e consulta lo storico completo. Tendenze e analisi sono in Analisi.",
"addMeasurement": "Aggiungi",
"editMeasurement": "Modifica misurazione",
"type": "Tipo",
@@ -1892,7 +1892,8 @@
"bmiEmptyTitle": "Aggiungi l’altezza per il BMI",
"bmiEmptyDescription": "Per il BMI serve la tua altezza. Salvala in Impostazioni → Account.",
"bmiEmptyAction": "Apri impostazioni account",
- "noProviderConfigured": "Valutazione non disponibile.",
+ "noProviderConfigured": "Collega un provider IA per vedere qui una valutazione delle tue misurazioni.",
+ "noProviderAction": "Apri impostazioni IA",
"startAnalysis": "Avvia valutazione",
"refreshAnalysis": "Aggiorna valutazione",
"keyTakeaway": "Ciò che conta",
@@ -1965,6 +1966,7 @@
"progressAria": "Avanzamento del punteggio di salute",
"deltaVsLastWeek": "{delta} rispetto alla settimana scorsa",
"deltaUnavailable": "Ancora nessuno storico — ripassa la prossima settimana.",
+ "restModePaused": "In pausa durante la malattia — oggi il tuo punteggio non viene valutato.",
"componentBp": "PA",
"componentWeight": "Peso",
"componentMood": "Umore",
@@ -2007,7 +2009,7 @@
"emptyAction": "Genera briefing",
"loadingLabel": "Preparazione del briefing",
"noProviderTitle": "Nessun provider IA collegato",
- "noProviderDescription": "Collega un provider IA nelle impostazioni per ricevere briefing giornalieri.",
+ "noProviderDescription": "Il briefing giornaliero riassume le tue misurazioni in linguaggio chiaro. HealthLog ha bisogno di un provider IA per questo: usa la tua chiave (OpenAI, Anthropic) o un modello locale. I tuoi dati restano tuoi.",
"noProviderAction": "Apri impostazioni IA"
},
"trendsRow": {
@@ -2072,6 +2074,8 @@
"refusalInjection": "Nel tuo messaggio ci sono istruzioni che cercano di sovrascrivere le mie indicazioni. Sono il coach di HealthLog e riassumo solo i tuoi dati di salute — riformula la domanda in questo perimetro.",
"errorBudget": "Hai raggiunto il limite giornaliero del coach. Il budget si reimposta a mezzanotte (UTC).",
"errorProvider": "Il coach non è riuscito a contattare un provider di analisi ora. Riprova fra poco.",
+ "errorNoProvider": "Il coach legge i tuoi dati di salute registrati e risponde a domande su di essi (non è consulenza medica). HealthLog ha bisogno di un provider IA per questo: collega la tua chiave o un modello locale.",
+ "errorNoProviderAction": "Collega un provider IA",
"errorNetwork": "Nessuna connessione Internet — riprova quando sei di nuovo online.",
"prefsSaveError": "Impossibile salvare le preferenze del Coach. Riprova.",
"errorCredentialExpired": "La connessione al provider di IA è scaduta. Riconnettila nelle Impostazioni per mantenere attivo il Coach.",
@@ -2709,6 +2713,7 @@
"title": "Panoramica della glicemia",
"subtitle": "Una lettura dei tuoi ultimi {days} giorni, da {count} misurazioni.",
"spotCaveat": "Sono stime da singole misurazioni puntuali, non il profilo di un sensore continuo: leggile come una tendenza, non come un verdetto clinico.",
+ "labsLink": "Risultati di laboratorio? Registra una HbA1c o la glicemia a digiuno da un prelievo in Laboratorio.",
"learningTitle": "Stiamo ancora imparando la tua glicemia",
"learningBody": "Per ora sono {count} su {days}. Con un po’ più di storico il quadro si chiarisce: continua a registrare o lascia che lo completi un dispositivo connesso.",
"learningBodyEmpty": "Appena arrivano alcune misurazioni — registrate da te o sincronizzate da un dispositivo — il tempo nel range, la media e la variabilità prendono forma qui.",
@@ -3623,7 +3628,10 @@
"learning": "Alcune analisi — glicemia, ritmo del sonno, tendenze della pressione, ciclo — si affinano nella prima settimana o due man mano che i tuoi dati si accumulano. Le caselle vuote ora indicano solo che stiamo ancora imparando il tuo riferimento.",
"connectCta": "Collega un dispositivo",
"logCta": "Registra la tua prima misurazione",
- "importCta": "Importa la cronologia esistente"
+ "importCta": "Importa la cronologia esistente",
+ "aiTitle": "Attiva gli Insight IA (facoltativo)",
+ "aiBody": "Il coach, il briefing giornaliero e le valutazioni trasformano le tue misurazioni in linguaggio chiaro. Per farlo colleghi la tua chiave IA (OpenAI, Anthropic) o un modello locale: i tuoi dati restano tuoi.",
+ "aiCta": "Collega un provider IA"
},
"welcomeBack": {
"title": "Bentornato",
diff --git a/messages/pl.json b/messages/pl.json
index f09755820..0f4eb78f3 100644
--- a/messages/pl.json
+++ b/messages/pl.json
@@ -414,7 +414,7 @@
},
"measurements": {
"title": "Pomiary",
- "subtitle": "Waga, ciśnienie tętnicze, tętno i więcej",
+ "subtitle": "Zapisuj pomiary i przeglądaj pełną historię. Trendy i analizy znajdziesz w Analizach.",
"addMeasurement": "Dodaj",
"editMeasurement": "Edytuj pomiar",
"type": "Typ",
@@ -1892,7 +1892,8 @@
"bmiEmptyTitle": "Dodaj wzrost, aby obliczyć BMI",
"bmiEmptyDescription": "BMI wymaga twojego wzrostu. Zapisz go w Ustawieniach → Konto.",
"bmiEmptyAction": "Otwórz ustawienia konta",
- "noProviderConfigured": "Ocena niedostępna.",
+ "noProviderConfigured": "Połącz dostawcę AI, aby zobaczyć tutaj ocenę swoich pomiarów.",
+ "noProviderAction": "Otwórz ustawienia AI",
"startAnalysis": "Uruchom ocenę",
"refreshAnalysis": "Odśwież ocenę",
"keyTakeaway": "Najważniejsze",
@@ -1965,6 +1966,7 @@
"progressAria": "Postęp wyniku zdrowia",
"deltaVsLastWeek": "{delta} w porównaniu z ubiegłym tygodniem",
"deltaUnavailable": "Brak historii — zajrzyj w przyszłym tygodniu.",
+ "restModePaused": "Wstrzymane podczas choroby — Twój wynik nie jest dziś oceniany.",
"componentBp": "RR",
"componentWeight": "Waga",
"componentMood": "Nastrój",
@@ -2007,7 +2009,7 @@
"emptyAction": "Wygeneruj briefing",
"loadingLabel": "Przygotowanie briefingu",
"noProviderTitle": "Brak połączonego dostawcy AI",
- "noProviderDescription": "Połącz dostawcę AI w ustawieniach, aby otrzymywać codzienne briefingi.",
+ "noProviderDescription": "Codzienny briefing podsumowuje Twoje pomiary prostym językiem. HealthLog potrzebuje do tego dostawcy AI — użyj własnego klucza (OpenAI, Anthropic) lub modelu lokalnego. Twoje dane pozostają u Ciebie.",
"noProviderAction": "Otwórz ustawienia AI"
},
"trendsRow": {
@@ -2072,6 +2074,8 @@
"refusalInjection": "Twoja wiadomość zawiera instrukcje, które próbują nadpisać moje wytyczne. Jestem coachem HealthLog i podsumowuję wyłącznie twoje własne dane zdrowotne — przeformułuj pytanie w tych ramach.",
"errorBudget": "Osiągnięto dzienny limit coacha. Limit resetuje się o północy (UTC).",
"errorProvider": "Coach nie mógł teraz skontaktować się z dostawcą analizy. Spróbuj za chwilę.",
+ "errorNoProvider": "Coach czyta Twoje zapisane dane zdrowotne i odpowiada na pytania o nie (to nie porada medyczna). HealthLog potrzebuje do tego dostawcy AI — połącz własny klucz lub model lokalny.",
+ "errorNoProviderAction": "Połącz dostawcę AI",
"errorNetwork": "Brak połączenia z Internetem — spróbuj ponownie, gdy wrócisz online.",
"prefsSaveError": "Nie udało się zapisać ustawień Coacha. Spróbuj ponownie.",
"errorCredentialExpired": "Połączenie z dostawcą AI wygasło. Połącz je ponownie w Ustawieniach, aby Coach działał dalej.",
@@ -2709,6 +2713,7 @@
"title": "Przegląd glukozy",
"subtitle": "Spojrzenie na ostatnie {days} dni, na podstawie {count} pomiarów.",
"spotCaveat": "To szacunki z pojedynczych pomiarów punktowych, a nie profil z czujnika ciągłego — traktuj je jako kierunek, nie jako diagnozę.",
+ "labsLink": "Wyniki laboratoryjne? HbA1c lub glukozę na czczo z pobrania krwi zapisz w sekcji Laboratorium.",
"learningTitle": "Wciąż poznajemy Twoją glukozę",
"learningBody": "Na razie to {count} w ciągu {days}. Przy nieco dłuższej historii obraz się wyostrza — rejestruj dalej lub pozwól uzupełnić to podłączonemu urządzeniu.",
"learningBodyEmpty": "Gdy pojawi się kilka pomiarów — wpisanych przez Ciebie lub zsynchronizowanych z urządzenia — czas w zakresie, średnia i zmienność nabiorą tu kształtu.",
@@ -3623,7 +3628,10 @@
"learning": "Niektóre wnioski — glukoza, rytm snu, trendy ciśnienia, cykl — wyostrzają się w ciągu pierwszego tygodnia lub dwóch, w miarę gromadzenia danych. Puste kafelki oznaczają teraz tylko, że wciąż poznajemy Twój punkt odniesienia.",
"connectCta": "Połącz urządzenie",
"logCta": "Zapisz pierwszy pomiar",
- "importCta": "Importuj istniejącą historię"
+ "importCta": "Importuj istniejącą historię",
+ "aiTitle": "Włącz Insighty AI (opcjonalnie)",
+ "aiBody": "Coach, codzienny briefing i oceny zamieniają Twoje pomiary w prosty język. W tym celu łączysz własny klucz AI (OpenAI, Anthropic) lub model lokalny — Twoje dane pozostają u Ciebie.",
+ "aiCta": "Połącz dostawcę AI"
},
"welcomeBack": {
"title": "Witaj ponownie",
diff --git a/src/app/page.tsx b/src/app/page.tsx
index c7a111143..8de115c44 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -106,18 +106,7 @@ import { isDashboardSnapshotEnabled } from "@/lib/dashboard/snapshot-flag";
import type { DataSummary } from "@/lib/analytics/trends";
import { mergeSlimAndThickAnalytics } from "@/lib/analytics/merge-slim-thick";
import { isWindowSufficient } from "@/lib/analytics/window-confidence";
-import { getBpTargets } from "@/lib/analytics/bp-targets";
-import {
- buildTrafficLightBands,
- buildTrafficRange,
- buildWeightBandsFromHeight,
- buildWeightRangeFromHeight,
- getBodyFatTargetRange,
-} from "@/lib/analytics/value-bands";
-import {
- getAgeFromDateOfBirth,
- getPersonalizedPulseTarget,
-} from "@/lib/analytics/pulse-targets";
+import { buildDashboardBands } from "@/lib/dashboard/bands";
import { apiGet } from "@/lib/api/api-fetch";
// v1.18.6 — the pure first-paint / skeleton-reservation gates moved to
@@ -537,34 +526,22 @@ export default function DashboardPage() {
const serverBands = snapshotEnabled
? snapshotQuery.data?.targetBands
: undefined;
- const bpTargets = snapshotEnabled
- ? (serverBands?.bpTargets ?? null)
- : user?.dateOfBirth != null
- ? getBpTargets(new Date(user.dateOfBirth))
- : null;
- const bodyFatRange = snapshotEnabled
- ? (serverBands?.bodyFatRange ?? getBodyFatTargetRange(user?.gender))
- : getBodyFatTargetRange(user?.gender);
- const weightRange = snapshotEnabled
- ? (serverBands?.weightRange ?? null)
- : user?.heightCm
- ? buildWeightRangeFromHeight(user.heightCm)
- : null;
- const weightBands = snapshotEnabled
- ? (serverBands?.weightBands ?? undefined)
- : user?.heightCm
- ? buildWeightBandsFromHeight(user.heightCm, {
- lowerBound: 30,
- upperBound: 250,
- })
- : undefined;
- // The pulse target range / bands carry no i18n — read them server-side
- // under snapshot mode; recompute from the profile otherwise.
- const pulseAge = getAgeFromDateOfBirth(user?.dateOfBirth ?? null);
- const pulseTarget = getPersonalizedPulseTarget(
- pulseAge,
- (user?.gender as "MALE" | "FEMALE" | null | undefined) ?? null,
- );
+ // Resolve the bands through the SAME `buildDashboardBands` helper the
+ // server snapshot uses, so the snapshot path and the client fallback
+ // are byte-identical by construction (no second inline copy of the
+ // band math to drift). Always compute the client copy from the profile
+ // — it is pure + cheap — so a still-loading snapshot frame keeps real
+ // bands rather than blank charts; `serverBands ?? clientBands` then
+ // prefers the authoritative server numbers the moment they arrive.
+ const clientBands = buildDashboardBands({
+ dateOfBirth: user?.dateOfBirth ? new Date(user.dateOfBirth) : null,
+ gender: (user?.gender as "MALE" | "FEMALE" | null | undefined) ?? null,
+ heightCm: user?.heightCm ?? null,
+ });
+ const bands = serverBands ?? clientBands;
+ const bpTargets = bands.bpTargets;
+ const weightRange = bands.weightRange;
+ const weightBands = bands.weightBands ?? undefined;
const bpTargetZones = bpTargets
? [
{
@@ -587,50 +564,11 @@ export default function DashboardPage() {
},
]
: undefined;
- const bpSysRange = snapshotEnabled
- ? (serverBands?.bpSysRange ?? null)
- : bpTargets
- ? buildTrafficRange(bpTargets.sysLow, bpTargets.sysHigh)
- : null;
- const bpDiaRange = snapshotEnabled
- ? (serverBands?.bpDiaRange ?? null)
- : bpTargets
- ? buildTrafficRange(bpTargets.diaLow, bpTargets.diaHigh)
- : null;
- const pulseDisplayRange = serverBands?.pulseDisplayRange ?? {
- greenMin: pulseTarget.greenMin,
- greenMax: pulseTarget.greenMax,
- orangeMin: pulseTarget.orangeMin,
- orangeMax: pulseTarget.orangeMax,
- };
- const pulseBands = serverBands?.pulseBands ?? [
- { min: 30, max: pulseTarget.orangeMin, color: "#ff5555", opacity: 0.16 },
- {
- min: pulseTarget.orangeMin,
- max: pulseTarget.greenMin,
- color: "#ffb86c",
- opacity: 0.18,
- },
- {
- min: pulseTarget.greenMin,
- max: pulseTarget.greenMax,
- color: "#50fa7b",
- opacity: 0.2,
- },
- {
- min: pulseTarget.greenMax,
- max: pulseTarget.orangeMax,
- color: "#ffb86c",
- opacity: 0.18,
- },
- { min: pulseTarget.orangeMax, max: 220, color: "#ff5555", opacity: 0.16 },
- ].filter((band) => band.max > band.min);
- const bodyFatBands =
- serverBands?.bodyFatBands ??
- buildTrafficLightBands(bodyFatRange.min, bodyFatRange.max, {
- lowerBound: 2,
- upperBound: 55,
- });
+ const bpSysRange = bands.bpSysRange;
+ const bpDiaRange = bands.bpDiaRange;
+ const pulseDisplayRange = bands.pulseDisplayRange;
+ const pulseBands = bands.pulseBands;
+ const bodyFatBands = bands.bodyFatBands;
// v1.18.6 — ONE batched fetch for every visible non-sleep chart series
// (audit finding #2). Pre-fix, each ` {safeError}
+ + {t("insights.healthScore.restModePaused")} +
+ )} +{t("insights.noProviderConfigured")}
+ + {t("insights.noProviderAction")} ++ {t("onboarding.done.aiTitle")} +
++ {t("onboarding.done.aiBody")} +
+ + {t("onboarding.done.aiCta")} + +