diff --git a/docs/api/openapi.yaml b/docs/api/openapi.yaml index e9dfaa516..aacee6ab9 100644 --- a/docs/api/openapi.yaml +++ b/docs/api/openapi.yaml @@ -51244,922 +51244,7 @@ components: type: object properties: data: - type: object - properties: - steps: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - activeEnergy: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - walkingRunningDistance: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - flightsClimbed: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - sleep: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - weight: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - bloodPressure: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - pulse: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - bodyFat: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - bodyTemperature: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - spo2: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - hrv: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - restingHeartRate: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - vo2Max: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - skinTemperature: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - respiratoryRate: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - recovery: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - stress: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - sleepDebt: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - hrvRmssd: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - metricPriority: - type: object - properties: - steps: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - activeEnergy: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - walkingRunningDistance: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - flightsClimbed: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - sleep: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - weight: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - bloodPressure: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - pulse: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - bodyFat: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - bodyTemperature: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - spo2: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - hrv: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - restingHeartRate: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - vo2Max: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - skinTemperature: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - respiratoryRate: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - recovery: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - stress: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - sleepDebt: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - hrvRmssd: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - additionalProperties: false - deviceTypePriority: - type: object - properties: - default: - maxItems: 8 - type: array - items: - type: string - enum: - - watch - - band - - ring - - phone - - scale - - other - - unknown - additionalProperties: - maxItems: 8 - type: array - items: - type: string - enum: - - watch - - band - - ring - - phone - - scale - - other - - unknown - additionalProperties: false + $ref: "#/components/schemas/ResolvedSourcePriority" error: type: "null" meta: @@ -52172,10 +51257,450 @@ components: - data - error additionalProperties: false - PutSourcePriorityResponse: + ResolvedSourcePriority: type: object properties: - data: + steps: + maxItems: 16 + type: array + items: + type: string + enum: + - MANUAL + - WITHINGS + - IMPORT + - APPLE_HEALTH + - COMPUTED + - WHOOP + - FITBIT + - NIGHTSCOUT + - POLAR + - OURA + - TELEGRAM + - MCP + - GOOGLE_HEALTH + - STRAVA + - EXTERNAL + - HEALTH_CONNECT + activeEnergy: + maxItems: 16 + type: array + items: + type: string + enum: + - MANUAL + - WITHINGS + - IMPORT + - APPLE_HEALTH + - COMPUTED + - WHOOP + - FITBIT + - NIGHTSCOUT + - POLAR + - OURA + - TELEGRAM + - MCP + - GOOGLE_HEALTH + - STRAVA + - EXTERNAL + - HEALTH_CONNECT + walkingRunningDistance: + maxItems: 16 + type: array + items: + type: string + enum: + - MANUAL + - WITHINGS + - IMPORT + - APPLE_HEALTH + - COMPUTED + - WHOOP + - FITBIT + - NIGHTSCOUT + - POLAR + - OURA + - TELEGRAM + - MCP + - GOOGLE_HEALTH + - STRAVA + - EXTERNAL + - HEALTH_CONNECT + flightsClimbed: + maxItems: 16 + type: array + items: + type: string + enum: + - MANUAL + - WITHINGS + - IMPORT + - APPLE_HEALTH + - COMPUTED + - WHOOP + - FITBIT + - NIGHTSCOUT + - POLAR + - OURA + - TELEGRAM + - MCP + - GOOGLE_HEALTH + - STRAVA + - EXTERNAL + - HEALTH_CONNECT + sleep: + maxItems: 16 + type: array + items: + type: string + enum: + - MANUAL + - WITHINGS + - IMPORT + - APPLE_HEALTH + - COMPUTED + - WHOOP + - FITBIT + - NIGHTSCOUT + - POLAR + - OURA + - TELEGRAM + - MCP + - GOOGLE_HEALTH + - STRAVA + - EXTERNAL + - HEALTH_CONNECT + weight: + maxItems: 16 + type: array + items: + type: string + enum: + - MANUAL + - WITHINGS + - IMPORT + - APPLE_HEALTH + - COMPUTED + - WHOOP + - FITBIT + - NIGHTSCOUT + - POLAR + - OURA + - TELEGRAM + - MCP + - GOOGLE_HEALTH + - STRAVA + - EXTERNAL + - HEALTH_CONNECT + bloodPressure: + maxItems: 16 + type: array + items: + type: string + enum: + - MANUAL + - WITHINGS + - IMPORT + - APPLE_HEALTH + - COMPUTED + - WHOOP + - FITBIT + - NIGHTSCOUT + - POLAR + - OURA + - TELEGRAM + - MCP + - GOOGLE_HEALTH + - STRAVA + - EXTERNAL + - HEALTH_CONNECT + pulse: + maxItems: 16 + type: array + items: + type: string + enum: + - MANUAL + - WITHINGS + - IMPORT + - APPLE_HEALTH + - COMPUTED + - WHOOP + - FITBIT + - NIGHTSCOUT + - POLAR + - OURA + - TELEGRAM + - MCP + - GOOGLE_HEALTH + - STRAVA + - EXTERNAL + - HEALTH_CONNECT + bodyFat: + maxItems: 16 + type: array + items: + type: string + enum: + - MANUAL + - WITHINGS + - IMPORT + - APPLE_HEALTH + - COMPUTED + - WHOOP + - FITBIT + - NIGHTSCOUT + - POLAR + - OURA + - TELEGRAM + - MCP + - GOOGLE_HEALTH + - STRAVA + - EXTERNAL + - HEALTH_CONNECT + bodyTemperature: + maxItems: 16 + type: array + items: + type: string + enum: + - MANUAL + - WITHINGS + - IMPORT + - APPLE_HEALTH + - COMPUTED + - WHOOP + - FITBIT + - NIGHTSCOUT + - POLAR + - OURA + - TELEGRAM + - MCP + - GOOGLE_HEALTH + - STRAVA + - EXTERNAL + - HEALTH_CONNECT + spo2: + maxItems: 16 + type: array + items: + type: string + enum: + - MANUAL + - WITHINGS + - IMPORT + - APPLE_HEALTH + - COMPUTED + - WHOOP + - FITBIT + - NIGHTSCOUT + - POLAR + - OURA + - TELEGRAM + - MCP + - GOOGLE_HEALTH + - STRAVA + - EXTERNAL + - HEALTH_CONNECT + hrv: + maxItems: 16 + type: array + items: + type: string + enum: + - MANUAL + - WITHINGS + - IMPORT + - APPLE_HEALTH + - COMPUTED + - WHOOP + - FITBIT + - NIGHTSCOUT + - POLAR + - OURA + - TELEGRAM + - MCP + - GOOGLE_HEALTH + - STRAVA + - EXTERNAL + - HEALTH_CONNECT + restingHeartRate: + maxItems: 16 + type: array + items: + type: string + enum: + - MANUAL + - WITHINGS + - IMPORT + - APPLE_HEALTH + - COMPUTED + - WHOOP + - FITBIT + - NIGHTSCOUT + - POLAR + - OURA + - TELEGRAM + - MCP + - GOOGLE_HEALTH + - STRAVA + - EXTERNAL + - HEALTH_CONNECT + vo2Max: + maxItems: 16 + type: array + items: + type: string + enum: + - MANUAL + - WITHINGS + - IMPORT + - APPLE_HEALTH + - COMPUTED + - WHOOP + - FITBIT + - NIGHTSCOUT + - POLAR + - OURA + - TELEGRAM + - MCP + - GOOGLE_HEALTH + - STRAVA + - EXTERNAL + - HEALTH_CONNECT + skinTemperature: + maxItems: 16 + type: array + items: + type: string + enum: + - MANUAL + - WITHINGS + - IMPORT + - APPLE_HEALTH + - COMPUTED + - WHOOP + - FITBIT + - NIGHTSCOUT + - POLAR + - OURA + - TELEGRAM + - MCP + - GOOGLE_HEALTH + - STRAVA + - EXTERNAL + - HEALTH_CONNECT + respiratoryRate: + maxItems: 16 + type: array + items: + type: string + enum: + - MANUAL + - WITHINGS + - IMPORT + - APPLE_HEALTH + - COMPUTED + - WHOOP + - FITBIT + - NIGHTSCOUT + - POLAR + - OURA + - TELEGRAM + - MCP + - GOOGLE_HEALTH + - STRAVA + - EXTERNAL + - HEALTH_CONNECT + recovery: + maxItems: 16 + type: array + items: + type: string + enum: + - MANUAL + - WITHINGS + - IMPORT + - APPLE_HEALTH + - COMPUTED + - WHOOP + - FITBIT + - NIGHTSCOUT + - POLAR + - OURA + - TELEGRAM + - MCP + - GOOGLE_HEALTH + - STRAVA + - EXTERNAL + - HEALTH_CONNECT + stress: + maxItems: 16 + type: array + items: + type: string + enum: + - MANUAL + - WITHINGS + - IMPORT + - APPLE_HEALTH + - COMPUTED + - WHOOP + - FITBIT + - NIGHTSCOUT + - POLAR + - OURA + - TELEGRAM + - MCP + - GOOGLE_HEALTH + - STRAVA + - EXTERNAL + - HEALTH_CONNECT + sleepDebt: + maxItems: 16 + type: array + items: + type: string + enum: + - MANUAL + - WITHINGS + - IMPORT + - APPLE_HEALTH + - COMPUTED + - WHOOP + - FITBIT + - NIGHTSCOUT + - POLAR + - OURA + - TELEGRAM + - MCP + - GOOGLE_HEALTH + - STRAVA + - EXTERNAL + - HEALTH_CONNECT + hrvRmssd: + maxItems: 16 + type: array + items: + type: string + enum: + - MANUAL + - WITHINGS + - IMPORT + - APPLE_HEALTH + - COMPUTED + - WHOOP + - FITBIT + - NIGHTSCOUT + - POLAR + - OURA + - TELEGRAM + - MCP + - GOOGLE_HEALTH + - STRAVA + - EXTERNAL + - HEALTH_CONNECT + metricPriority: type: object properties: steps: @@ -52618,480 +52143,67 @@ components: - STRAVA - EXTERNAL - HEALTH_CONNECT - metricPriority: - type: object - properties: - steps: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - activeEnergy: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - walkingRunningDistance: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - flightsClimbed: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - sleep: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - weight: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - bloodPressure: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - pulse: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - bodyFat: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - bodyTemperature: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - spo2: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - hrv: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - restingHeartRate: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - vo2Max: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - skinTemperature: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - respiratoryRate: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - recovery: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - stress: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - sleepDebt: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - hrvRmssd: - maxItems: 16 - type: array - items: - type: string - enum: - - MANUAL - - WITHINGS - - IMPORT - - APPLE_HEALTH - - COMPUTED - - WHOOP - - FITBIT - - NIGHTSCOUT - - POLAR - - OURA - - TELEGRAM - - MCP - - GOOGLE_HEALTH - - STRAVA - - EXTERNAL - - HEALTH_CONNECT - additionalProperties: false - deviceTypePriority: - type: object - properties: - default: - maxItems: 8 - type: array - items: - type: string - enum: - - watch - - band - - ring - - phone - - scale - - other - - unknown - additionalProperties: - maxItems: 8 - type: array - items: - type: string - enum: - - watch - - band - - ring - - phone - - scale - - other - - unknown additionalProperties: false + deviceTypePriority: + type: object + properties: + default: + maxItems: 8 + type: array + items: + type: string + enum: + - watch + - band + - ring + - phone + - scale + - other + - unknown + additionalProperties: + maxItems: 8 + type: array + items: + type: string + enum: + - watch + - band + - ring + - phone + - scale + - other + - unknown + inUse: + type: array + items: + type: string + enum: + - MANUAL + - WITHINGS + - IMPORT + - APPLE_HEALTH + - COMPUTED + - WHOOP + - FITBIT + - NIGHTSCOUT + - POLAR + - OURA + - TELEGRAM + - MCP + - GOOGLE_HEALTH + - STRAVA + - EXTERNAL + - HEALTH_CONNECT + description: Ranked sources this account has connected or holds at least one measurement from, in ladder-catalogue + order. Read-only; an editor may list only these while the ladders keep every source. + required: + - inUse + additionalProperties: false + PutSourcePriorityResponse: + type: object + properties: + data: + $ref: "#/components/schemas/ResolvedSourcePriority" error: type: "null" meta: diff --git a/e2e/settings-mobile-consistency.spec.ts b/e2e/settings-mobile-consistency.spec.ts index b9b67519e..7294d69ba 100644 --- a/e2e/settings-mobile-consistency.spec.ts +++ b/e2e/settings-mobile-consistency.spec.ts @@ -149,38 +149,42 @@ test.describe("Settings mobile consistency (Pixel 5)", () => { } }); - test("/settings/account: Sprache select shares one grid row with date-of-birth", async ({ + test("/settings/account: the language select sits in the display card, outside the profile form", async ({ page, }) => { await page.goto("/settings/account", { waitUntil: "networkidle" }); - // Gate on the two elements this test is about, not on the network. An idle - // network says nothing about whether React has rendered the profile form, - // and the single `evaluate` below does not retry — so without this the - // test reads an empty document and reports "language + dob fields must - // exist", which sounds like a missing feature and is really a race. + // Gate on the elements this test is about, not on the network: the + // single `evaluate` below does not retry. await expect(page.locator("#language-select")).toBeAttached(); await expect(page.locator("#dob")).toBeAttached(); - // The v1.4.27 R1 settings audit pairs date-of-birth with language - // in a single `grid sm:grid-cols-2` row so the profile form keeps - // a uniform two-column rhythm and the language field no longer - // sits alone at the bottom with a `sm:max-w-xs` clamp. - const sharedGrid = await page.evaluate(() => { + // The language, unit and format selects save the moment they change, so + // they live in their own card with no save button. Inside the profile + // form they read as covered by its Save. Each card keeps a two-column + // grid: the language shares one with the units, the date of birth one + // with the timezone. + const layout = await page.evaluate(() => { const lang = document.getElementById("language-select"); const dob = document.getElementById("dob"); - if (!lang || !dob) return { found: false, sharedGrid: false }; - const langGrid = lang.closest('[class*="grid"]'); - const dobGrid = dob.closest('[class*="grid"]'); + if (!lang || !dob) return null; + const displayCard = document.querySelector( + '[data-testid="settings-display-card"]', + ); return { - found: true, - sharedGrid: langGrid !== null && langGrid === dobGrid, + langInDisplayCard: !!displayCard && displayCard.contains(lang), + langInForm: lang.closest("form") !== null, + dobInForm: dob.closest("form") !== null, + langHasGrid: lang.closest('[class*="grid"]') !== null, + dobHasGrid: dob.closest('[class*="grid"]') !== null, }; }); - expect(sharedGrid.found, "language + dob fields must exist").toBe(true); - expect(sharedGrid.sharedGrid, "language + dob must share a grid").toBe( - true, - ); + expect(layout, "language + dob fields must exist").not.toBeNull(); + expect(layout!.langInDisplayCard).toBe(true); + expect(layout!.langInForm).toBe(false); + expect(layout!.dobInForm).toBe(true); + expect(layout!.langHasGrid).toBe(true); + expect(layout!.dobHasGrid).toBe(true); }); test("/: Compare-to trigger renders at 44 px", async ({ page }) => { diff --git a/e2e/v137-sharing-managed-profiles.spec.ts b/e2e/v137-sharing-managed-profiles.spec.ts index 6cf1ffbd0..f4e225f74 100644 --- a/e2e/v137-sharing-managed-profiles.spec.ts +++ b/e2e/v137-sharing-managed-profiles.spec.ts @@ -503,13 +503,20 @@ test.describe.serial("scoped sharing browser journeys", () => { await expect(banner).toHaveAttribute("data-record-kind", "shared"); await page.goto("/measurements"); - await expect(page.locator('[data-slot="measurement-add"]')).toBeVisible(); + // An empty list carries its add action in the empty state rather than + // the header, so the write grant shows whichever of the two applies. + const addControl = page + .locator( + '[data-slot="measurement-add"], [data-slot="measurement-add-first"]', + ) + .first(); + await expect(addControl).toBeVisible(); const post = page.waitForResponse( (response) => response.request().method() === "POST" && new URL(response.url()).pathname === "/api/measurements", ); - await page.locator('[data-slot="measurement-add"]').click(); + await addControl.click(); await page.locator("#sys").fill("124"); await page.locator("#dia").fill("78"); await page.getByRole("button", { name: /^save$/i }).click(); diff --git a/messages/de.json b/messages/de.json index 656959442..445c875ee 100644 --- a/messages/de.json +++ b/messages/de.json @@ -5310,12 +5310,10 @@ "sections": { "account": { "title": "Konto", - "description": "Profil, Passwort, Passkeys und deine Onboarding-Tour.", "subtitle": "Dein Profil, deine Identität und deine Anmeldedaten." }, "integrations": { "title": "Integrationen", - "description": "Verbundene Dienste, Zustellkanäle und die Quellen-Priorität an einem Ort.", "subtitle": "Datenquellen und Geräte verbinden und verwalten.", "connectionsHeading": "Verbindungen", "channelsHeading": "Zustellkanäle", @@ -5324,16 +5322,14 @@ }, "notifications": { "title": "Benachrichtigungen", - "description": "Alle Erinnerungen an einem Ort — was wann erinnert wird und wohin es zugestellt wird.", "subtitle": "Wähle, welche Erinnerungen du erhältst und wohin sie zugestellt werden.", "remindersHeading": "Erinnerungen", "matrixLink": "Benachrichtigungen je Kanal", "channelsLink": "Zustellkanäle" }, "layout": { - "title": "Darstellung", - "description": "Lege fest, wie Dashboard, Auswertungen und deine Tracking-Module dargestellt und angeordnet sind.", - "backToHub": "Zurück zu Darstellung", + "title": "Layout", + "backToHub": "Zurück zu Layout", "dashboard": { "title": "Dashboard", "description": "Kachel-Layout und Reihenfolge." @@ -5342,7 +5338,7 @@ "title": "Insights", "description": "Reihenfolge der Übersicht und der Navigations-Pills." }, - "subtitle": "Lege fest, wie Dashboard, Auswertungen und Tracking-Module dargestellt werden.", + "subtitle": "Lege fest, was Dashboard, Auswertungen und Tracking-Module zeigen und in welcher Reihenfolge.", "medications": { "title": "Medikamente", "description": "Listenansicht und manuelle Reihenfolge." @@ -5373,12 +5369,10 @@ }, "thresholds": { "title": "Zielwerte", - "description": "Pro Metrik dein eigener Zielbereich für gesunde Werte.", "subtitle": "Lege deine persönlichen Zielbereiche je Messgröße fest." }, "sources": { "title": "Quellen-Priorität", - "description": "Lege fest, welche Quelle führt, wenn mehrere am selben Tag dieselbe Metrik liefern.", "integrationsHint": "Neue Datenquellen verbindest oder trennst du auf der Integrationen-Seite.", "integrationsHintLink": "Integrationen öffnen", "cardTitle": "Rangfolge je Messgröße", @@ -5460,26 +5454,23 @@ "moduleHintLink": "Module verwalten", "detail": "Nur lesend — die Quell-App besitzt die Werte." }, - "helpDetail": "Die übrigen bleiben für die Audit-Spur gespeichert." + "helpDetail": "Die übrigen bleiben für die Audit-Spur gespeichert. Aufgeführt sind nur Quellen, die du verbunden hast oder von denen Daten vorliegen.", + "noneInUse": "Noch keine Quelle zum Sortieren. Verbinde eine unter Integrationen oder trage einen Wert ein." }, "ai": { "title": "KI-Anbieter", - "description": "Provider, Modell und API-Key für die Auswertungen — dazu dein „Über mich“-Kontext.", "subtitle": "Wähle deinen KI-Anbieter und hinterlege deinen eigenen Schlüssel." }, "api": { "title": "API & Tokens", - "description": "Bearer-Tokens für eigene Skripte und Apps — erfasse Messwerte oder Einnahmen von überall.", "subtitle": "API-Endpunkte und Zugriffstoken verwalten." }, "advanced": { "title": "Erweitert", - "description": "Der Notausgang: alle Gesundheitsdaten unwiderruflich löschen oder das Konto auflösen.", "subtitle": "Daten zurücksetzen und Konto löschen." }, "export": { "title": "Export & Import", - "description": "Lade deine Daten als CSV oder vollständiges JSON-Backup herunter — oder importiere aus Apple Health, JSON oder CSV. Den Arztbericht findest du jetzt unter Gesundheitsakte.", "otherOptionsHeading": "Weitere Export-Optionen", "downloadFailed": "Der Export konnte nicht heruntergeladen werden. Bitte versuche es erneut.", "actions": { @@ -5724,12 +5715,10 @@ }, "about": { "title": "Über diese App", - "description": "Version, Lizenz, Links.", "subtitle": "Version, Lizenz und Projektlinks." }, "sharing": { "title": "Freigabe", - "description": "Erstelle einen schreibgeschützten Link, um deine Gesundheitsakte mit einer Ärztin oder einem Arzt zu teilen.", "subtitle": "Teile schreibgeschützten Zugriff auf deine Daten." }, "insights": { @@ -5764,12 +5753,10 @@ }, "gesundheitsakte": { "title": "Gesundheitsakte", - "description": "Vollständige Gesundheitsakte als PDF, FHIR R4 oder kombiniertes ZIP-Paket exportieren.", "subtitle": "Exportiere eine vollständige, arzttaugliche Gesundheitsakte." }, "coach": { "title": "Coach", - "description": "Coach aktivieren, Ton und Umfang, gespeicherte Erinnerungen.", "subtitle": "Stelle den Coach, seine proaktiven Hinweise und sein Gedächtnis ein." }, "security": { @@ -5778,8 +5765,7 @@ }, "privacy": { "title": "Daten & Datenschutz", - "subtitle": "Deine Daten, Aufbewahrung, Verschlüsselung und Kontosicherheit an einem Ort.", - "description": "Export, Löschung, Aufbewahrung, Verschlüsselungsstatus, Sitzungen und Sicherheitsaktivität." + "subtitle": "Deine Daten, Aufbewahrung, Verschlüsselung und Kontosicherheit an einem Ort." }, "mcp": { "title": "MCP-Connector", @@ -5787,8 +5773,7 @@ }, "anamnesis": { "title": "Anamnese", - "subtitle": "Deine Vorgeschichte, Lebensumstände, Notfalldaten und was die KI lesen darf.", - "description": "Strukturierte Datensätze zu Allergien und Familienanamnese." + "subtitle": "Deine Vorgeschichte, Lebensumstände, Notfalldaten und was die KI lesen darf." }, "environment": { "title": "Standort & Wetter", @@ -6542,30 +6527,32 @@ "fitbitViewData": "Schlaf ansehen", "googleHealthViewData": "Schritte ansehen", "apiTokens": "API-Tokens", - "apiTokensDescription": "API-Tokens für die externe Medikamenten-Einnahme prüfen und widerrufen.", + "apiTokensDescription": "Tokens für Messwerte, Trainings, Dokumente und Medikamenten-Einnahme prüfen und widerrufen.", "measurementsToken": { "title": "Messwert-Tokens", "description": "Erzeuge ein Token, damit eine Waage, eine Uhr oder deine Hausautomatisierung Werte eintragen kann.", "detail": "Das Token wird nur einmal angezeigt.", "scopeNote": "Es kann ausschließlich neue Werte in deine eigene Akte eintragen — deine Daten nicht lesen, nichts ändern oder löschen und kein weiteres Token erstellen. Werte werden als von einem externen Gerät gekennzeichnet, damit du sie später erkennst, und lassen sich jederzeit korrigieren.", - "createFailed": "Das Token konnte nicht erstellt werden. Bitte versuche es erneut." + "createFailed": "Das Token konnte nicht erstellt werden. Bitte versuche es erneut.", + "namePlaceholder": "Name, z. B. Home Assistant" }, "workoutsToken": { "title": "Training-Tokens", "description": "Erzeuge ein Token, damit eine Uhren-Brücke oder ein Sync-Skript deine Trainings eintragen kann.", "detail": "Das Token wird nur einmal angezeigt.", "scopeNote": "Es kann ausschließlich neue Trainings in deine eigene Akte eintragen. Es kann deine Daten nicht lesen, nichts ändern oder löschen und kein weiteres Token erstellen. Trainings, die darüber kommen, werden als von einem externen Gerät gekennzeichnet. Hat dein Telefon dieselbe Einheit aufgezeichnet, siehst du die Aufzeichnung des Telefons.", - "createFailed": "Das Token konnte nicht erstellt werden. Bitte versuche es erneut." + "createFailed": "Das Token konnte nicht erstellt werden. Bitte versuche es erneut.", + "namePlaceholder": "Name, z. B. Garmin" }, "documentsToken": { "title": "Dokument-Tokens", "description": "Erzeuge ein Token, damit Paperless-ngx, Papra oder das Import-Skript Dokumente in deine Ablage legen können.", "detail": "Das Token wird nur einmal angezeigt.", "scopeNote": "Es kann ausschließlich neue Dokumente in deine eigene Ablage legen. Es kann nichts auflisten, öffnen, herunterladen, ändern oder löschen und kein weiteres Token erstellen. Zurück bekommt es nur, ob eine Datei gespeichert wurde, nicht wie du sie abgelegt hast.", - "createFailed": "Das Token konnte nicht erstellt werden. Bitte versuche es erneut." + "createFailed": "Das Token konnte nicht erstellt werden. Bitte versuche es erneut.", + "namePlaceholder": "Name, z. B. Paperless-ngx" }, - "tokenMintMovedDescription": "Tokens werden dort ausgestellt, wo sie gelten: oben für Messwerte und Dokumente, oder im API-Endpoint eines Medikaments für genau dieses Medikament.", - "tokenNamePlaceholder": "Token-Name (z. B. iPhone Shortcut)", + "tokenMintMovedDescription": "Tokens werden dort ausgestellt, wo sie gelten: oben für Messwerte, Trainings und Dokumente, oder im API-Endpoint eines Medikaments für genau dieses Medikament.", "tokenCreated": "Token erstellt — kopiere ihn jetzt! Er wird nicht erneut angezeigt.", "tokenCopy": "Token kopieren", "tokenCopied": "Token in die Zwischenablage kopiert", @@ -6757,7 +6744,8 @@ "statusNeedsReselect": "Inhalt neu wählen", "reselectReason": "Dieser Link lieferte einen Umfang, den niemand gewählt hatte, und wurde deshalb geschlossen. Lege einen neuen an und wähle den Inhalt.", "reselectAction": "Ersatz anlegen", - "createDetail": "Der Token wird nur einmal bei der Erstellung angezeigt — sichere ihn dann; er lässt sich nicht wiederherstellen." + "createDetail": "Der Token wird nur einmal bei der Erstellung angezeigt — sichere ihn dann; er lässt sich nicht wiederherstellen.", + "activeDescription": "Deine geteilten Links, wann sie ablaufen und wie oft sie geöffnet wurden." }, "genderOther": "Divers", "nightscout": "Nightscout", @@ -6983,12 +6971,11 @@ }, "stored": { "title": "Was gespeichert wird", - "measurements": "Messwerte (Gewicht, Blutdruck, Puls, Glukose, Schlaf, Körperzusammensetzung)", - "medications": "Medikamente und Einnahmeverlauf", - "moodLabs": "Stimmungseinträge und Laborwerte", - "coach": "Coach-Unterhaltungen", + "measurements": "Messwerte: Gewicht, Blutdruck, Puls und jeder weitere Wert, den du oder ein Gerät erfasst", "integrations": "Integrations-Tokens (verschlüsselt)", - "security": "Konto- und Sicherheitsaktivität (Sitzungen, Anmeldeprotokoll)" + "security": "Konto- und Sicherheitsaktivität (Sitzungen, Anmeldeprotokoll)", + "profile": "Dein Profil, deine Anamnese und die Angaben für die Gesundheitsakte", + "modules": "Einträge der Module: {list}" }, "retention": { "title": "Aufbewahrung", @@ -7019,8 +7006,6 @@ "mcp": { "enableTitle": "Remote-MCP-Endpunkt", "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.", @@ -7039,7 +7024,8 @@ "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.", "tokensDetail": "Das Token wird nur einmal angezeigt.", - "connectionsDetail": "Das Widerrufen einer Verbindung beendet den Zugriff sofort, einschließlich der Möglichkeit zur Erneuerung." + "connectionsDetail": "Das Widerrufen einer Verbindung beendet den Zugriff sofort, einschließlich der Möglichkeit zur Erneuerung.", + "tokenNamePlaceholder": "Name, z. B. Claude Desktop" }, "strava": "Strava", "stravaDescription": "Verbinde dein Strava-Konto, um deine Aktivitäten in deinen Trainingsverlauf zu übernehmen.", @@ -7169,7 +7155,12 @@ "signInAgain": "Melde dich neu an und versuche es dann noch einmal. Dein Konto hat kein Passwort und keinen Passkey, den du hier bestätigen könntest.", "failed": "Das hat nicht geklappt. Prüfe deine Eingabe und versuche es noch einmal." }, - "passwordTitle": "Passwort" + "passwordTitle": "Passwort", + "tokenNameLabel": "Token-Name", + "displayCard": { + "title": "Darstellung", + "description": "Sprache, Einheiten und Formate, gespeichert, sobald du sie änderst." + } }, "admin": { "title": "Administration", @@ -7377,8 +7368,8 @@ "coachFollowsSwitch": "Folgt dem Coach-Schalter im Bereich Coach." }, "invites": { - "title": "Einladungen", - "description": "Einladungslinks für die Registrierung — funktionieren auch bei geschlossener Registrierung.", + "title": "Einladungslinks", + "description": "Jeder Link lässt eine Registrierung zu, auch wenn sie geschlossen ist.", "create": "Einladung erstellen", "createTitle": "Einladung erstellen", "createDescription": "Der Link erlaubt eine Registrierung auch bei geschlossener Registrierung.", @@ -7424,8 +7415,8 @@ "revokedToast": "Einladung widerrufen.", "revokeError": "Einladung konnte nicht widerrufen werden." }, - "userManagement": "Nutzerverwaltung", - "userManagementDescription": "Konten verwalten: Rollen, Passwort-Resets und erzwungene Abmeldungen.", + "userManagement": "Konten", + "userManagementDescription": "Rollen, Passwort-Resets und erzwungene Abmeldungen für jedes Konto.", "userName": "Name", "userEmail": "E-Mail", "userRole": "Rolle", @@ -7881,7 +7872,12 @@ "backupsNone": "Noch keine Sicherung hat die Schlüssel festgehalten, die sie braucht.", "backupsStillNeed": "Alle Zeilen sind rotiert, aber Sicherungen brauchen noch {keys}", "backupsUnrecordedBadge": "Alle Zeilen sind rotiert, aber ältere Sicherungen können noch einen ausgemusterten Schlüssel brauchen", - "backupsDetail": "Die Rotation verschlüsselt jede Zeile neu, aber eine Sicherung behält den Schlüssel, unter dem ihr Inhalt geschrieben wurde, und ihre Wiederherstellung braucht ihn. Behalte einen ausgemusterten Schlüssel in ENCRYPTION_KEYS, bis er unten nicht mehr aufgeführt ist." + "backupsDetail": "Die Rotation verschlüsselt jede Zeile neu, aber eine Sicherung behält den Schlüssel, unter dem ihr Inhalt geschrieben wurde, und ihre Wiederherstellung braucht ihn. Behalte einen ausgemusterten Schlüssel in ENCRYPTION_KEYS, bis er unten nicht mehr aufgeführt ist.", + "columnsShow": "Tabelle anzeigen ({count} mit Zeilen)", + "columnsHide": "Tabelle ausblenden", + "columnsShowAll": "Alle {count} Spalten anzeigen", + "columnsShowWithRows": "Nur Spalten mit Zeilen", + "columnsNoneWithRows": "Noch keine verschlüsselte Spalte enthält Zeilen." }, "maintenance": { "title": "Pflege der Messwert-Tabelle", diff --git a/messages/en.json b/messages/en.json index c97071d00..c474dd409 100644 --- a/messages/en.json +++ b/messages/en.json @@ -392,7 +392,7 @@ "insights": "Insights", "achievements": "Achievements", "settings": "Settings", - "admin": "Admin Console", + "admin": "Admin console", "logout": "Sign out", "login": "Sign in", "notifications": "Notifications", @@ -3279,7 +3279,7 @@ "relativeDaysAgoFew": "{count} days ago", "navOverview": "Overview", "navCatalog": "All metrics", - "navBloodPressure": "Blood Pressure", + "navBloodPressure": "Blood pressure", "navWeight": "Weight", "navPulse": "Pulse", "navMood": "Mood", @@ -3814,7 +3814,7 @@ "description": "Body-water readings appear here once a connected body-composition scale syncs." } }, - "navTotalBodyWater": "Body Water", + "navTotalBodyWater": "Body water", "boneMass": { "title": "Bone Mass", "description": "Your bone mass over time, in kilograms.", @@ -3824,7 +3824,7 @@ "description": "Bone-mass readings appear here once a connected body-composition scale syncs." } }, - "navBoneMass": "Bone Mass", + "navBoneMass": "Bone mass", "flightsClimbed": { "title": "Flights Climbed", "description": "Flights of stairs you climbed each day.", @@ -3854,7 +3854,7 @@ "description": "Fat-free-mass readings appear here once a connected body-composition scale syncs." } }, - "navFatFreeMass": "Fat-Free Mass", + "navFatFreeMass": "Fat-free mass", "fatMass": { "title": "Fat Mass", "description": "Your fat mass over time, in kilograms.", @@ -3874,8 +3874,8 @@ "cta": "Log body fat" } }, - "navFatMass": "Fat Mass", - "navBodyFat": "Body Fat", + "navFatMass": "Fat mass", + "navBodyFat": "Body fat", "muscleMass": { "title": "Muscle Mass", "description": "Your muscle mass over time, in kilograms.", @@ -3885,7 +3885,7 @@ "description": "Muscle-mass readings appear here once a connected body-composition scale syncs." } }, - "navMuscleMass": "Muscle Mass", + "navMuscleMass": "Muscle mass", "skinTemperature": { "title": "Skin Temperature", "description": "Your wrist skin temperature over time, in degrees Celsius.", @@ -3895,7 +3895,7 @@ "description": "Skin-temperature readings appear here once a wearable syncs overnight wrist temperature." } }, - "navSkinTemperature": "Skin Temp", + "navSkinTemperature": "Skin temp", "pulseWaveVelocity": { "title": "Pulse Wave Velocity", "description": "Your arterial pulse-wave velocity over time, in m/s — higher values suggest stiffer arteries.", @@ -3905,7 +3905,7 @@ "description": "Pulse-wave-velocity readings appear here once a compatible Withings device syncs." } }, - "navPulseWaveVelocity": "Pulse Wave Velocity", + "navPulseWaveVelocity": "Pulse wave velocity", "vascularAge": { "title": "Vascular Age", "description": "Your estimated vascular age over time, in years.", @@ -3915,7 +3915,7 @@ "description": "Vascular-age estimates appear here once a compatible Withings device syncs." } }, - "navVascularAge": "Vascular Age", + "navVascularAge": "Vascular age", "visceralFat": { "title": "Visceral Fat", "description": "Your visceral-fat rating over time.", @@ -3925,7 +3925,7 @@ "description": "Visceral-fat ratings appear here once a compatible body-composition scale syncs." } }, - "navVisceralFat": "Visceral Fat", + "navVisceralFat": "Visceral fat", "audioExposureEnv": { "title": "Environmental Audio", "description": "Your daily average environmental sound exposure, in dBA.", @@ -3935,7 +3935,7 @@ "description": "Environmental sound-level readings appear here once Apple Health syncs." } }, - "navAudioExposureEnv": "Environmental Audio", + "navAudioExposureEnv": "Environmental audio", "audioExposureHeadphone": { "title": "Headphone Audio", "description": "Your daily average headphone audio exposure, in dBA.", @@ -3945,7 +3945,7 @@ "description": "Headphone audio-level readings appear here once Apple Health syncs." } }, - "navAudioExposureHeadphone": "Headphone Audio", + "navAudioExposureHeadphone": "Headphone audio", "timeInDaylight": { "title": "Time in Daylight", "description": "Minutes spent in daylight each day.", @@ -3965,7 +3965,7 @@ "description": "Walking-steadiness readings appear here once Apple Health syncs." } }, - "navWalkingSteadiness": "Walking Steadiness", + "navWalkingSteadiness": "Walking steadiness", "audioExposureEvent": { "title": "Loud Audio Events", "description": "Days with a loud-environment exposure event.", @@ -3975,7 +3975,7 @@ "description": "Loud-environment events appear here once Apple Health syncs." } }, - "navAudioExposureEvent": "Loud Events", + "navAudioExposureEvent": "Loud events", "respiratoryRate": { "title": "Respiratory Rate", "description": "Your respiratory rate over time, in breaths per minute.", @@ -3985,7 +3985,7 @@ "description": "Respiratory-rate readings appear here once Apple Health syncs sleep or workout breathing." } }, - "navRespiratoryRate": "Respiratory Rate", + "navRespiratoryRate": "Respiratory rate", "leanBodyMass": { "title": "Lean Body Mass", "description": "Your lean body mass over time, in kilograms.", @@ -3995,7 +3995,7 @@ "description": "Lean-body-mass readings appear here once a connected body-composition scale syncs." } }, - "navLeanBodyMass": "Lean Body Mass", + "navLeanBodyMass": "Lean body mass", "walkingHeartRateAverage": { "title": "Walking Heart Rate", "description": "Your average heart rate while walking, in bpm.", @@ -4005,7 +4005,7 @@ "description": "Walking-heart-rate readings appear here once Apple Health syncs." } }, - "navWalkingHeartRateAverage": "Walking Heart Rate", + "navWalkingHeartRateAverage": "Walking heart rate", "walkingAsymmetry": { "title": "Walking Asymmetry", "description": "Your walking asymmetry over time, as a percentage.", @@ -4015,7 +4015,7 @@ "description": "Walking-asymmetry readings appear here once Apple Health syncs." } }, - "navWalkingAsymmetry": "Walking Asymmetry", + "navWalkingAsymmetry": "Walking asymmetry", "walkingDoubleSupport": { "title": "Double Support Time", "description": "The percentage of each stride spent with both feet on the ground.", @@ -4025,7 +4025,7 @@ "description": "Double-support readings appear here once Apple Health syncs." } }, - "navWalkingDoubleSupport": "Double Support", + "navWalkingDoubleSupport": "Double support", "walkingStepLength": { "title": "Walking Step Length", "description": "Your average walking step length over time, in metres.", @@ -4035,7 +4035,7 @@ "description": "Step-length readings appear here once Apple Health syncs." } }, - "navWalkingStepLength": "Step Length", + "navWalkingStepLength": "Step length", "walkingSpeed": { "title": "Walking Speed", "description": "Your average walking speed over time, in km/h.", @@ -4045,7 +4045,7 @@ "description": "Walking-speed readings appear here once Apple Health syncs." } }, - "navWalkingSpeed": "Walking Speed", + "navWalkingSpeed": "Walking speed", "mood": { "logLink": "Log a mood entry", "heatmapTitle": "Mood calendar", @@ -4426,7 +4426,7 @@ "description": "VO₂ max readings appear here once Apple Health syncs from your workouts." } }, - "navCardioFitness": "Cardio Fitness", + "navCardioFitness": "Cardio fitness", "cardioRecovery": { "title": "Cardio Recovery", "description": "How far your heart rate drops one minute after peak exercise.", @@ -4436,7 +4436,7 @@ "description": "Cardio-recovery readings appear here once Apple Health syncs from a workout." } }, - "navCardioRecovery": "Cardio Recovery", + "navCardioRecovery": "Cardio recovery", "wristTemperature": { "title": "Wrist Temperature", "description": "Your overnight wrist temperature, tracked against your own baseline.", @@ -4446,7 +4446,7 @@ "description": "Wrist-temperature readings appear here once Apple Health syncs overnight wear." } }, - "navWristTemperature": "Wrist Temp", + "navWristTemperature": "Wrist temp", "falls": { "title": "Falls", "description": "Hard falls your Apple Watch detected, counted per day.", @@ -4466,7 +4466,7 @@ "description": "Six-minute-walk estimates appear here once Apple Health syncs." } }, - "navSixMinuteWalk": "6-Min Walk", + "navSixMinuteWalk": "6-min walk", "stairAscentSpeed": { "title": "Stair Ascent Speed", "description": "How fast you climb stairs, measured by your iPhone or Watch.", @@ -4476,7 +4476,7 @@ "description": "Stair-ascent-speed readings appear here once Apple Health syncs." } }, - "navStairAscentSpeed": "Stair Ascent", + "navStairAscentSpeed": "Stair ascent", "stairDescentSpeed": { "title": "Stair Descent Speed", "description": "How fast you go down stairs, measured by your iPhone or Watch.", @@ -4486,7 +4486,7 @@ "description": "Stair-descent-speed readings appear here once Apple Health syncs." } }, - "navStairDescentSpeed": "Stair Descent", + "navStairDescentSpeed": "Stair descent", "breathingDisturbances": { "title": "Breathing Disturbances", "description": "How often your breathing was disturbed during sleep.", @@ -5310,12 +5310,10 @@ "sections": { "account": { "title": "Account", - "description": "Profile, password, passkeys, and your onboarding tour.", "subtitle": "Your profile, identity, and sign-in details." }, "integrations": { "title": "Integrations", - "description": "Connected services, delivery channels, and source priority in one place.", "subtitle": "Connect and manage your data sources and devices.", "connectionsHeading": "Connections", "channelsHeading": "Delivery channels", @@ -5324,16 +5322,14 @@ }, "notifications": { "title": "Notifications", - "description": "Every reminder in one place — what gets reminded and when, and where it's delivered.", "subtitle": "Choose which reminders you receive and where they're delivered.", "remindersHeading": "Reminders", "matrixLink": "Per-channel notifications", "channelsLink": "Delivery channels" }, "layout": { - "title": "Appearance", - "description": "Choose how the dashboard, insights, and your tracking modules look and are arranged.", - "backToHub": "Back to Appearance", + "title": "Layout", + "backToHub": "Back to Layout", "dashboard": { "title": "Dashboard", "description": "Tile layout and order." @@ -5342,7 +5338,7 @@ "title": "Insights", "description": "Overview sections and navigation-pill order." }, - "subtitle": "Personalize how your dashboard, insights, and tracking modules look.", + "subtitle": "Choose what your dashboard, insights and tracking modules show, and in which order.", "medications": { "title": "Medications", "description": "List view and manual order." @@ -5373,12 +5369,10 @@ }, "thresholds": { "title": "Targets", - "description": "Per metric: your own target range for healthy values.", "subtitle": "Set your personal target ranges per metric." }, "sources": { "title": "Source priority", - "description": "Pick which ingest source wins when more than one records the same metric for the same day.", "integrationsHint": "Connect or disconnect data sources on the Integrations page.", "integrationsHintLink": "Open integrations", "cardTitle": "Ranking per metric", @@ -5460,26 +5454,23 @@ "moduleHintLink": "Manage modules", "detail": "Read-only — the source app owns the numbers." }, - "helpDetail": "The others stay stored for the audit trail." + "helpDetail": "The others stay stored for the audit trail. Only sources you have connected or hold data from are listed.", + "noneInUse": "No source to rank yet. Connect one under Integrations or record a value." }, "ai": { "title": "AI provider", - "description": "Provider, model, and API key for the insights — plus your “about me” context.", "subtitle": "Choose your AI provider and bring your own key." }, "api": { - "title": "API & Tokens", - "description": "Bearer tokens for your own scripts and apps — log measurements or medication intake from anywhere.", + "title": "API & tokens", "subtitle": "Manage API endpoints and access tokens." }, "advanced": { "title": "Advanced", - "description": "The way out: erase every health record for good, or close the account entirely.", "subtitle": "Data reset and account deletion." }, "export": { - "title": "Export & Import", - "description": "Download your data as CSV or a full JSON backup — or import from Apple Health, JSON, or CSV. The doctor report now lives under Health Record.", + "title": "Export & import", "otherOptionsHeading": "Other export options", "downloadFailed": "Couldn't download the export. Please try again.", "actions": { @@ -5724,12 +5715,10 @@ }, "about": { "title": "About this app", - "description": "Version, license, links.", "subtitle": "Version, license, and project links." }, "sharing": { "title": "Sharing", - "description": "Create a read-only link to share your health record with a clinician.", "subtitle": "Share read-only access to your data." }, "insights": { @@ -5764,12 +5753,10 @@ }, "gesundheitsakte": { "title": "Health record", - "description": "Export your full health record as PDF, FHIR R4, or a combined ZIP package.", "subtitle": "Export a complete, clinician-ready health record." }, "coach": { "title": "Coach", - "description": "Enable the Coach, tone and scope, stored memories.", "subtitle": "Tune the Coach, its proactive nudges, and its memory." }, "security": { @@ -5777,18 +5764,16 @@ "subtitle": "Two-factor authentication, recovery codes, and passkeys." }, "privacy": { - "title": "Data & Privacy", - "subtitle": "Your data, retention, encryption, and account security in one place.", - "description": "Export, deletion, retention, encryption status, sessions, and security activity." + "title": "Data & privacy", + "subtitle": "Your data, retention, encryption, and account security in one place." }, "mcp": { - "title": "MCP Connector", + "title": "MCP connector", "subtitle": "Connect an external assistant to your own health data." }, "anamnesis": { "title": "Medical history", - "subtitle": "Your medical background, lifestyle, emergency details and what the AI may read.", - "description": "Structured allergies and family history records." + "subtitle": "Your medical background, lifestyle, emergency details and what the AI may read." }, "environment": { "title": "Location & weather", @@ -5847,7 +5832,7 @@ } }, "score": { - "title": "Health Score", + "title": "Health score", "subtitle": "Choose which pillars count toward your score.", "card": { "title": "What counts toward your score", @@ -6541,31 +6526,33 @@ "whoopViewData": "View your recovery", "fitbitViewData": "View your sleep", "googleHealthViewData": "View your steps", - "apiTokens": "API Tokens", - "apiTokensDescription": "Review and revoke API tokens for external medication intake.", + "apiTokens": "API tokens", + "apiTokensDescription": "Review and revoke tokens for measurements, workouts, documents and medication intake.", "measurementsToken": { "title": "Measurement tokens", "description": "Mint a token so a scale, a watch bridge or your home automation can add readings.", "detail": "The token is shown once.", "scopeNote": "It can only add new readings to your own record — it cannot read your data, change or delete anything, or create another token. Readings are marked as coming from an external device, so you can tell them apart later, and you can still correct them yourself.", - "createFailed": "The token could not be created. Please try again." + "createFailed": "The token could not be created. Please try again.", + "namePlaceholder": "Name, e.g. Home Assistant" }, "workoutsToken": { "title": "Workout tokens", "description": "Mint a token so a watch bridge or a sync script can add your workouts.", "detail": "The token is shown once.", "scopeNote": "It can only add new workouts to your own record. It cannot read your data, change or delete anything, or create another token. Workouts it adds are marked as coming from an external device, and when your phone recorded the same session, you see the phone's copy.", - "createFailed": "The token could not be created. Please try again." + "createFailed": "The token could not be created. Please try again.", + "namePlaceholder": "Name, e.g. Garmin" }, "documentsToken": { "title": "Document tokens", "description": "Mint a token so Paperless-ngx, Papra or the import script can add documents to your vault.", "detail": "The token is shown once.", "scopeNote": "It can only add new documents to your own vault. It cannot list, open, download, change or delete anything, and it cannot create another token. What it gets back is whether a file was stored, not how you filed it.", - "createFailed": "The token could not be created. Please try again." + "createFailed": "The token could not be created. Please try again.", + "namePlaceholder": "Name, e.g. Paperless-ngx" }, - "tokenMintMovedDescription": "Tokens are issued where they apply: above for measurements and documents, or in a medication's own API endpoint for that one medication.", - "tokenNamePlaceholder": "Token name (e.g., iPhone Shortcut)", + "tokenMintMovedDescription": "Tokens are issued where they apply: above for measurements, workouts and documents, or in a medication's own API endpoint for that one medication.", "tokenCreated": "Token created — copy it now! It won't be shown again.", "tokenCopy": "Copy token", "tokenCopied": "Token copied to clipboard", @@ -6757,7 +6744,8 @@ "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", - "createDetail": "The token is shown once on creation — store it then; it cannot be recovered." + "createDetail": "The token is shown once on creation — store it then; it cannot be recovered.", + "activeDescription": "Links you have shared, when they expire and how often they were opened." }, "genderOther": "Other", "nightscout": "Nightscout", @@ -6983,12 +6971,11 @@ }, "stored": { "title": "What's stored", - "measurements": "Measurements (weight, blood pressure, pulse, glucose, sleep, body composition)", - "medications": "Medications and intake history", - "moodLabs": "Mood entries and lab results", - "coach": "Coach conversations", + "measurements": "Measurements: weight, blood pressure, pulse and every other value you or a device record", "integrations": "Integration tokens (encrypted)", - "security": "Account and security activity (sessions, sign-in log)" + "security": "Account and security activity (sessions, sign-in log)", + "profile": "Your profile, medical history and health-record details", + "modules": "Records of each module: {list}" }, "retention": { "title": "Retention", @@ -7019,8 +7006,6 @@ "mcp": { "enableTitle": "Remote MCP endpoint", "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.", @@ -7039,7 +7024,8 @@ "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.", "connectionsDetail": "Revoking a connection ends its access immediately, including its ability to refresh.", - "tokensDetail": "The token is shown once." + "tokensDetail": "The token is shown once.", + "tokenNamePlaceholder": "Name, e.g. Claude Desktop" }, "strava": "Strava", "stravaDescription": "Connect your Strava account to sync your activities into your workout history.", @@ -7169,7 +7155,12 @@ "signInAgain": "Sign in again, then try once more. Your account has no password or passkey to confirm here.", "failed": "That did not work. Check what you entered and try again." }, - "passwordTitle": "Password" + "passwordTitle": "Password", + "tokenNameLabel": "Token name", + "displayCard": { + "title": "Display", + "description": "Language, units and formats, saved as soon as you change them." + } }, "admin": { "title": "Administration", @@ -7377,8 +7368,8 @@ "coachFollowsSwitch": "Follows the Coach switch in the Coach section." }, "invites": { - "title": "Invites", - "description": "Registration invite links — they work even while registration is closed.", + "title": "Invite links", + "description": "Each link admits a signup even while registration is closed.", "create": "Create invite", "createTitle": "Create invite", "createDescription": "The link admits a signup even while registration is closed.", @@ -7424,8 +7415,8 @@ "revokedToast": "Invite revoked.", "revokeError": "Could not revoke the invite." }, - "userManagement": "User Management", - "userManagementDescription": "Manage accounts: roles, password resets, and forced logouts.", + "userManagement": "Accounts", + "userManagementDescription": "Roles, password resets and forced logouts for every account.", "userName": "Name", "userEmail": "Email", "userRole": "Role", @@ -7446,7 +7437,7 @@ "passwordReset": "Password has been reset", "reset": "Reset", "loadingUsers": "Loading users…", - "apiTokens": "API Tokens", + "apiTokens": "API tokens", "apiTokensDescription": "All active and revoked API tokens across all accounts.", "tokenUser": "User", "tokenName": "Name", @@ -7557,7 +7548,7 @@ }, "section": { "system-status": { - "title": "System Status", + "title": "System status", "subtitle": "Worker, database, and integration health at a glance." }, "general": { @@ -7603,7 +7594,7 @@ "subtitle": "Mint, track, and revoke registration invite links." }, "api-tokens": { - "title": "API Tokens", + "title": "API tokens", "subtitle": "Active bearer tokens issued to native clients and integrations." }, "login-overview": { @@ -7823,7 +7814,7 @@ "restoreSkippedVaccinationCiphertext": "Vaccination note sealed with a key this server does not hold (dose kept without it)" }, "danger-zone": { - "title": "Danger Zone", + "title": "Danger zone", "subtitle": "Destructive system-wide operations." }, "about": { @@ -7881,7 +7872,12 @@ "backupsNone": "No backup has recorded the keys it needs yet.", "backupsStillNeed": "Every row is rotated, but backups still need {keys}", "backupsUnrecordedBadge": "Every row is rotated, but older backups may still need a retired key", - "backupsDetail": "Rotation re-encrypts every row, but a backup keeps the key its content was written under, and restoring it needs that key. Keep a retired key in ENCRYPTION_KEYS until nothing below lists it." + "backupsDetail": "Rotation re-encrypts every row, but a backup keeps the key its content was written under, and restoring it needs that key. Keep a retired key in ENCRYPTION_KEYS until nothing below lists it.", + "columnsShow": "Show the table ({count} with rows)", + "columnsHide": "Hide the table", + "columnsShowAll": "Show all {count} columns", + "columnsShowWithRows": "Only columns with rows", + "columnsNoneWithRows": "No encrypted column holds rows yet." }, "maintenance": { "title": "Measurement table maintenance", diff --git a/messages/es.json b/messages/es.json index 02d1d2fca..45dd83155 100644 --- a/messages/es.json +++ b/messages/es.json @@ -5310,12 +5310,10 @@ "sections": { "account": { "title": "Cuenta", - "description": "Perfil, contraseña, claves de acceso y tu recorrido de incorporación.", "subtitle": "Tu perfil, identidad y datos de acceso." }, "integrations": { "title": "Integraciones", - "description": "Servicios conectados, canales de entrega y prioridad de fuentes en un solo lugar.", "subtitle": "Conecta y gestiona tus fuentes de datos y dispositivos.", "connectionsHeading": "Conexiones", "channelsHeading": "Canales de entrega", @@ -5324,16 +5322,14 @@ }, "notifications": { "title": "Notificaciones", - "description": "Todos los recordatorios en un solo lugar: qué se recuerda y cuándo, y dónde se entrega.", "subtitle": "Elige qué recordatorios recibes y dónde se entregan.", "remindersHeading": "Recordatorios", "matrixLink": "Notificaciones por canal", "channelsLink": "Canales de entrega" }, "layout": { - "title": "Apariencia", - "description": "Elige cómo se ven y se organizan el panel, los análisis y tus módulos de seguimiento.", - "backToHub": "Volver a Apariencia", + "title": "Diseño", + "backToHub": "Volver a Diseño", "dashboard": { "title": "Panel", "description": "Diseño y orden de los mosaicos." @@ -5342,7 +5338,7 @@ "title": "Análisis", "description": "Orden de la vista general y de las pestañas de navegación." }, - "subtitle": "Personaliza cómo se ven tu panel, los análisis y los módulos de seguimiento.", + "subtitle": "Elige qué muestran tu panel, los análisis y los módulos de seguimiento, y en qué orden.", "medications": { "title": "Medicamentos", "description": "Vista de lista y orden manual." @@ -5373,12 +5369,10 @@ }, "thresholds": { "title": "Objetivos", - "description": "Por métrica: tu propio rango objetivo de valores saludables.", "subtitle": "Define tus rangos objetivo personales por métrica." }, "sources": { "title": "Prioridad de fuentes", - "description": "Elige qué fuente prevalece cuando varias registran la misma métrica el mismo día.", "integrationsHint": "Conecta o desconecta fuentes de datos en la página de integraciones.", "integrationsHintLink": "Abrir integraciones", "cardTitle": "Orden por métrica", @@ -5460,26 +5454,23 @@ "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." + "helpDetail": "Las demás se conservan para la traza de auditoría. Solo aparecen las fuentes que has conectado o de las que hay datos.", + "noneInUse": "Aún no hay fuentes que ordenar. Conecta una en Integraciones o registra un valor." }, "ai": { "title": "Proveedor de IA", - "description": "Proveedor, modelo y clave API para los análisis, además de tu contexto «sobre mí».", "subtitle": "Elige tu proveedor de IA y usa tu propia clave." }, "api": { - "title": "API & Tokens", - "description": "Tokens Bearer para tus propios scripts y apps — registra mediciones o tomas de medicación desde cualquier lugar.", + "title": "API y tokens", "subtitle": "Gestiona los endpoints de la API y los tokens de acceso." }, "advanced": { "title": "Avanzado", - "description": "La salida: borra todos los registros de salud para siempre o cierra la cuenta.", "subtitle": "Restablecer datos y eliminar cuenta." }, "export": { "title": "Exportar e importar", - "description": "Descarga tus datos como CSV o una copia de seguridad JSON completa, o importa desde Apple Health, JSON o CSV. El informe médico ahora está en Historia de salud.", "otherOptionsHeading": "Otras opciones de exportación", "downloadFailed": "No se pudo descargar la exportación. Inténtalo de nuevo.", "actions": { @@ -5724,12 +5715,10 @@ }, "about": { "title": "Acerca de esta app", - "description": "Versión, licencia, enlaces.", "subtitle": "Versión, licencia y enlaces del proyecto." }, "sharing": { "title": "Compartir", - "description": "Crea un enlace de solo lectura para compartir tu historial de salud con un profesional médico.", "subtitle": "Comparte acceso de solo lectura a tus datos." }, "insights": { @@ -5764,12 +5753,10 @@ }, "gesundheitsakte": { "title": "Historial médico", - "description": "Exporta tu historial médico completo como PDF, FHIR R4 o un paquete ZIP combinado.", "subtitle": "Exporta un historial de salud completo, listo para el médico." }, "coach": { "title": "Coach", - "description": "Activa el Coach, tono y alcance, recuerdos guardados.", "subtitle": "Ajusta el Coach, sus avisos proactivos y su memoria." }, "security": { @@ -5778,8 +5765,7 @@ }, "privacy": { "title": "Datos y privacidad", - "subtitle": "Tus datos, su retención, el cifrado y la seguridad de la cuenta en un solo lugar.", - "description": "Exportación, eliminación, retención, estado de cifrado, sesiones y actividad de seguridad." + "subtitle": "Tus datos, su retención, el cifrado y la seguridad de la cuenta en un solo lugar." }, "mcp": { "title": "Conector MCP", @@ -5787,8 +5773,7 @@ }, "anamnesis": { "title": "Antecedentes médicos", - "subtitle": "Tus antecedentes, tu estilo de vida, tus datos de emergencia y lo que la IA puede leer.", - "description": "Registros estructurados de alergias y antecedentes familiares." + "subtitle": "Tus antecedentes, tu estilo de vida, tus datos de emergencia y lo que la IA puede leer." }, "environment": { "title": "Ubicación y clima", @@ -6542,30 +6527,32 @@ "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.", + "apiTokensDescription": "Revisa y revoca los tokens de mediciones, entrenamientos, documentos y tomas de medicación.", "measurementsToken": { "title": "Tokens de mediciones", "description": "Crea un token para que una báscula, un reloj o tu domótica añadan mediciones.", "detail": "El token se muestra una sola vez.", "scopeNote": "Solo puede añadir mediciones nuevas a tu propio historial: no puede leer tus datos, ni modificar o borrar nada, ni crear otro token. Las mediciones se marcan como procedentes de un dispositivo externo, así las distingues más tarde, y puedes corregirlas igualmente.", - "createFailed": "No se pudo crear el token. Inténtalo de nuevo." + "createFailed": "No se pudo crear el token. Inténtalo de nuevo.", + "namePlaceholder": "Nombre, p. ej. Home Assistant" }, "workoutsToken": { "title": "Tokens de entrenamientos", "description": "Crea un token para que un puente de tu reloj o un script de sincronización añadan tus entrenamientos.", "detail": "El token se muestra una sola vez.", "scopeNote": "Solo puede añadir entrenamientos nuevos a tu propio historial. No puede leer tus datos, ni modificar o borrar nada, ni crear otro token. Los entrenamientos que añade se marcan como procedentes de un dispositivo externo, y si tu teléfono registró la misma sesión, ves la copia del teléfono.", - "createFailed": "No se pudo crear el token. Inténtalo de nuevo." + "createFailed": "No se pudo crear el token. Inténtalo de nuevo.", + "namePlaceholder": "Nombre, p. ej. Garmin" }, "documentsToken": { "title": "Tokens de documentos", "description": "Crea un token para que Paperless-ngx, Papra o el script de importación añadan documentos a tu archivo.", "detail": "El token se muestra una sola vez.", "scopeNote": "Solo puede añadir documentos nuevos a tu propio archivo. No puede listar, abrir, descargar, modificar ni borrar nada, ni crear otro token. Lo único que recibe es si un archivo se guardó, no cómo lo archivaste.", - "createFailed": "No se pudo crear el token. Inténtalo de nuevo." + "createFailed": "No se pudo crear el token. Inténtalo de nuevo.", + "namePlaceholder": "Nombre, p. ej. Paperless-ngx" }, - "tokenMintMovedDescription": "Los tokens se emiten donde se aplican: arriba para las mediciones y los documentos, o en el endpoint de API de un medicamento para ese medicamento concreto.", - "tokenNamePlaceholder": "Nombre del token (p. ej., Atajo de iPhone)", + "tokenMintMovedDescription": "Los tokens se emiten donde se aplican: arriba para las mediciones, los entrenamientos y los documentos, o en el endpoint de API de un medicamento para ese medicamento concreto.", "tokenCreated": "Token creado — ¡cópialo ahora! No se mostrará de nuevo.", "tokenCopy": "Copiar token", "tokenCopied": "Token copiado al portapapeles", @@ -6757,7 +6744,8 @@ "statusNeedsReselect": "Elegir contenido de nuevo", "reselectReason": "Este enlace mostraba un alcance que nadie eligió, por eso se cerró. Crea uno nuevo y elige su contenido.", "reselectAction": "Crear un reemplazo", - "createDetail": "El token se muestra una sola vez al crearlo — guárdalo entonces; no se puede recuperar." + "createDetail": "El token se muestra una sola vez al crearlo — guárdalo entonces; no se puede recuperar.", + "activeDescription": "Los enlaces que has compartido, cuándo caducan y cuántas veces se abrieron." }, "genderOther": "Otro", "nightscout": "Nightscout", @@ -6983,12 +6971,11 @@ }, "stored": { "title": "Qué se almacena", - "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", - "coach": "Conversaciones con el asistente", + "measurements": "Mediciones: peso, presión arterial, pulso y cualquier otro valor que registres tú o un dispositivo", "integrations": "Tokens de integración (cifrados)", - "security": "Actividad de la cuenta y de seguridad (sesiones, registro de inicio de sesión)" + "security": "Actividad de la cuenta y de seguridad (sesiones, registro de inicio de sesión)", + "profile": "Tu perfil, tus antecedentes médicos y los datos del historial de salud", + "modules": "Registros de cada módulo: {list}" }, "retention": { "title": "Retención", @@ -7019,8 +7006,6 @@ "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.", - "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.", @@ -7039,7 +7024,8 @@ "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.", "tokensDetail": "El token se muestra una sola vez.", - "connectionsDetail": "Revocar una conexión finaliza su acceso de inmediato, incluida su capacidad de renovación." + "connectionsDetail": "Revocar una conexión finaliza su acceso de inmediato, incluida su capacidad de renovación.", + "tokenNamePlaceholder": "Nombre, p. ej. Claude Desktop" }, "strava": "Strava", "stravaDescription": "Conecta tu cuenta de Strava para sincronizar tus actividades con tu historial de entrenamientos.", @@ -7169,7 +7155,12 @@ "signInAgain": "Vuelve a iniciar sesión y inténtalo de nuevo. Tu cuenta no tiene contraseña ni llave de acceso que confirmar aquí.", "failed": "No ha funcionado. Revisa lo que has introducido e inténtalo de nuevo." }, - "passwordTitle": "Contraseña" + "passwordTitle": "Contraseña", + "tokenNameLabel": "Nombre del token", + "displayCard": { + "title": "Visualización", + "description": "Idioma, unidades y formatos; se guardan en cuanto los cambias." + } }, "admin": { "title": "Administration", @@ -7377,8 +7368,8 @@ "coachFollowsSwitch": "Sigue el interruptor del Coach en la sección Coach." }, "invites": { - "title": "Invitaciones", - "description": "Enlaces de invitación para el registro: funcionan incluso con el registro cerrado.", + "title": "Enlaces de invitación", + "description": "Cada enlace permite un registro aunque el registro esté cerrado.", "create": "Crear invitación", "createTitle": "Crear invitación", "createDescription": "El enlace permite registrarse incluso con el registro cerrado.", @@ -7424,8 +7415,8 @@ "revokedToast": "Invitación revocada.", "revokeError": "No se pudo revocar la invitación." }, - "userManagement": "Gestión de usuarios", - "userManagementDescription": "Gestiona las cuentas: roles, restablecimientos de contraseña y cierres de sesión forzados.", + "userManagement": "Cuentas", + "userManagementDescription": "Roles, restablecimientos de contraseña y cierres de sesión forzados de cada cuenta.", "userName": "Nombre", "userEmail": "Correo electrónico", "userRole": "Rol", @@ -7881,7 +7872,12 @@ "backupsNone": "Ninguna copia ha registrado todavía las claves que necesita.", "backupsStillNeed": "Todas las filas están rotadas, pero las copias aún necesitan {keys}", "backupsUnrecordedBadge": "Todas las filas están rotadas, pero copias antiguas pueden necesitar aún una clave retirada", - "backupsDetail": "La rotación vuelve a cifrar cada fila, pero una copia conserva la clave con la que se escribió su contenido, y restaurarla necesita esa clave. Mantén una clave retirada en ENCRYPTION_KEYS hasta que no aparezca abajo." + "backupsDetail": "La rotación vuelve a cifrar cada fila, pero una copia conserva la clave con la que se escribió su contenido, y restaurarla necesita esa clave. Mantén una clave retirada en ENCRYPTION_KEYS hasta que no aparezca abajo.", + "columnsShow": "Mostrar la tabla ({count} con filas)", + "columnsHide": "Ocultar la tabla", + "columnsShowAll": "Mostrar las {count} columnas", + "columnsShowWithRows": "Solo columnas con filas", + "columnsNoneWithRows": "Ninguna columna cifrada contiene filas todavía." }, "maintenance": { "title": "Mantenimiento de la tabla de mediciones", diff --git a/messages/fr.json b/messages/fr.json index 0d6b487d1..49cbf7748 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -5310,12 +5310,10 @@ "sections": { "account": { "title": "Compte", - "description": "Profil, mot de passe, clés d’accès et votre visite guidée.", "subtitle": "Votre profil, votre identité et vos identifiants." }, "integrations": { "title": "Intégrations", - "description": "Services connectés, canaux de diffusion et priorité des sources au même endroit.", "subtitle": "Connectez et gérez vos sources de données et appareils.", "connectionsHeading": "Connexions", "channelsHeading": "Canaux de diffusion", @@ -5324,16 +5322,14 @@ }, "notifications": { "title": "Notifications", - "description": "Tous les rappels au même endroit — quoi et quand, et où c'est envoyé.", "subtitle": "Choisissez les rappels que vous recevez et où ils sont envoyés.", "remindersHeading": "Rappels", "matrixLink": "Notifications par canal", "channelsLink": "Canaux de diffusion" }, "layout": { - "title": "Apparence", - "description": "Choisissez l'apparence et l'agencement du tableau de bord, des analyses et de vos modules de suivi.", - "backToHub": "Retour à Apparence", + "title": "Disposition", + "backToHub": "Retour à Disposition", "dashboard": { "title": "Tableau de bord", "description": "Disposition et ordre des tuiles." @@ -5342,7 +5338,7 @@ "title": "Analyses", "description": "Ordre de la vue d'ensemble et des onglets de navigation." }, - "subtitle": "Personnalisez l'apparence du tableau de bord, des analyses et des modules de suivi.", + "subtitle": "Choisissez ce qu'affichent le tableau de bord, les analyses et les modules de suivi, et dans quel ordre.", "medications": { "title": "Médicaments", "description": "Vue en liste et ordre manuel." @@ -5373,12 +5369,10 @@ }, "thresholds": { "title": "Objectifs", - "description": "Par métrique : votre propre plage cible pour des valeurs saines.", "subtitle": "Définissez vos plages cibles personnelles par mesure." }, "sources": { "title": "Priorité des sources", - "description": "Choisissez quelle source prévaut quand plusieurs enregistrent la même métrique le même jour.", "integrationsHint": "Connectez ou déconnectez des sources de données sur la page Intégrations.", "integrationsHintLink": "Ouvrir les intégrations", "cardTitle": "Classement par métrique", @@ -5460,26 +5454,23 @@ "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." + "helpDetail": "Les autres restent enregistrées pour la piste d’audit. Seules les sources connectées ou ayant fourni des données sont listées.", + "noneInUse": "Aucune source à classer pour l'instant. Connectez-en une dans Intégrations ou saisissez une valeur." }, "ai": { "title": "Fournisseur d'IA", - "description": "Fournisseur, modèle et clé API pour les analyses, ainsi que votre contexte « à propos de moi ».", "subtitle": "Choisissez votre fournisseur d'IA et utilisez votre propre clé." }, "api": { - "title": "API & Tokens", - "description": "Jetons Bearer pour vos propres scripts et applis — enregistrez des mesures ou des prises de médicaments depuis n’importe où.", + "title": "API et jetons", "subtitle": "Gérez les points d'accès API et les jetons d'accès." }, "advanced": { "title": "Avancé", - "description": "La sortie : effacer définitivement toutes les données de santé ou fermer le compte.", "subtitle": "Réinitialisation des données et suppression du compte." }, "export": { "title": "Export et import", - "description": "Téléchargez vos données en CSV ou en sauvegarde JSON complète, ou importez depuis Apple Santé, JSON ou CSV. Le compte rendu médical se trouve désormais dans Dossier de santé.", "otherOptionsHeading": "Autres options d'export", "downloadFailed": "Échec du téléchargement de l'export. Veuillez réessayer.", "actions": { @@ -5724,12 +5715,10 @@ }, "about": { "title": "À propos de cette app", - "description": "Version, licence, liens.", "subtitle": "Version, licence et liens du projet." }, "sharing": { "title": "Partage", - "description": "Créez un lien en lecture seule pour partager votre dossier de santé avec un professionnel de santé.", "subtitle": "Partagez un accès en lecture seule à vos données." }, "insights": { @@ -5764,12 +5753,10 @@ }, "gesundheitsakte": { "title": "Dossier médical", - "description": "Exportez votre dossier médical complet au format PDF, FHIR R4 ou en paquet ZIP combiné.", "subtitle": "Exportez un dossier de santé complet, prêt pour le médecin." }, "coach": { "title": "Coach", - "description": "Activez le Coach, le ton et la portée, les souvenirs enregistrés.", "subtitle": "Réglez le Coach, ses suggestions proactives et sa mémoire." }, "security": { @@ -5778,8 +5765,7 @@ }, "privacy": { "title": "Données et confidentialité", - "subtitle": "Vos données, leur conservation, le chiffrement et la sécurité du compte au même endroit.", - "description": "Export, suppression, conservation, état du chiffrement, sessions et activité de sécurité." + "subtitle": "Vos données, leur conservation, le chiffrement et la sécurité du compte au même endroit." }, "mcp": { "title": "Connecteur MCP", @@ -5787,8 +5773,7 @@ }, "anamnesis": { "title": "Antécédents médicaux", - "subtitle": "Vos antécédents, votre mode de vie, vos informations d’urgence et ce que l’IA peut lire.", - "description": "Dossiers structurés des allergies et des antécédents familiaux." + "subtitle": "Vos antécédents, votre mode de vie, vos informations d’urgence et ce que l’IA peut lire." }, "environment": { "title": "Localisation et météo", @@ -6542,30 +6527,32 @@ "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.", + "apiTokensDescription": "Consultez et révoquez les jetons des mesures, entraînements, documents et prises de médicaments.", "measurementsToken": { "title": "Jetons de mesures", "description": "Créez un jeton pour qu'une balance, une montre ou votre domotique ajoutent des mesures.", "detail": "Le jeton n'est affiché qu'une fois.", "scopeNote": "Il peut uniquement ajouter de nouvelles mesures à votre propre dossier : il ne peut pas lire vos données, ni modifier ou supprimer quoi que ce soit, ni créer un autre jeton. Les mesures sont signalées comme provenant d'un appareil externe, ce qui vous permet de les reconnaître plus tard, et vous pouvez toujours les corriger.", - "createFailed": "Le jeton n'a pas pu être créé. Veuillez réessayer." + "createFailed": "Le jeton n'a pas pu être créé. Veuillez réessayer.", + "namePlaceholder": "Nom, p. ex. Home Assistant" }, "workoutsToken": { "title": "Jetons d'entraînements", "description": "Créez un jeton pour qu'une passerelle de montre ou un script de synchronisation ajoutent vos entraînements.", "detail": "Le jeton n'est affiché qu'une fois.", "scopeNote": "Il peut uniquement ajouter de nouveaux entraînements à votre propre dossier. Il ne peut pas lire vos données, ni modifier ou supprimer quoi que ce soit, ni créer un autre jeton. Les entraînements qu'il ajoute sont signalés comme provenant d'un appareil externe, et si votre téléphone a enregistré la même séance, c'est la copie du téléphone que vous voyez.", - "createFailed": "Le jeton n'a pas pu être créé. Veuillez réessayer." + "createFailed": "Le jeton n'a pas pu être créé. Veuillez réessayer.", + "namePlaceholder": "Nom, p. ex. Garmin" }, "documentsToken": { "title": "Jetons de documents", "description": "Créez un jeton pour que Paperless-ngx, Papra ou le script d'import ajoutent des documents à votre coffre.", "detail": "Le jeton n'est affiché qu'une fois.", "scopeNote": "Il peut uniquement ajouter de nouveaux documents à votre propre coffre. Il ne peut rien lister, ouvrir, télécharger, modifier ou supprimer, ni créer un autre jeton. Il apprend seulement si un fichier a été enregistré, pas comment vous l'avez classé.", - "createFailed": "Le jeton n'a pas pu être créé. Veuillez réessayer." + "createFailed": "Le jeton n'a pas pu être créé. Veuillez réessayer.", + "namePlaceholder": "Nom, p. ex. Paperless-ngx" }, - "tokenMintMovedDescription": "Les jetons sont émis là où ils s'appliquent : ci-dessus pour les mesures et les documents, ou dans le point de terminaison API d'un médicament pour ce seul médicament.", - "tokenNamePlaceholder": "Nom du jeton (par exemple Raccourci iPhone)", + "tokenMintMovedDescription": "Les jetons sont émis là où ils s'appliquent : ci-dessus pour les mesures, les entraînements et les documents, ou dans le point de terminaison API d'un médicament pour ce seul médicament.", "tokenCreated": "Jeton créé — copiez-le maintenant ! Il ne sera plus affiché.", "tokenCopy": "Copier le jeton", "tokenCopied": "Jeton copié dans le presse-papiers", @@ -6757,7 +6744,8 @@ "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", - "createDetail": "Le jeton n'est affiché qu'une fois à la création — conservez-le alors ; il ne peut pas être récupéré." + "createDetail": "Le jeton n'est affiché qu'une fois à la création — conservez-le alors ; il ne peut pas être récupéré.", + "activeDescription": "Les liens que vous avez partagés, leur expiration et le nombre d'ouvertures." }, "genderOther": "Autre", "nightscout": "Nightscout", @@ -6983,12 +6971,11 @@ }, "stored": { "title": "Ce qui est stocké", - "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", - "coach": "Conversations avec le coach", + "measurements": "Mesures : poids, tension, pouls et toute autre valeur enregistrée par vous ou un appareil", "integrations": "Jetons d'intégration (chiffrés)", - "security": "Activité du compte et de sécurité (sessions, journal de connexion)" + "security": "Activité du compte et de sécurité (sessions, journal de connexion)", + "profile": "Votre profil, vos antécédents médicaux et les informations du dossier de santé", + "modules": "Données de chaque module : {list}" }, "retention": { "title": "Conservation", @@ -7019,8 +7006,6 @@ "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.", - "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é.", @@ -7039,7 +7024,8 @@ "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.", "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." + "connectionsDetail": "Révoquer une connexion met fin à son accès immédiatement, y compris sa capacité à se renouveler.", + "tokenNamePlaceholder": "Nom, p. ex. Claude Desktop" }, "strava": "Strava", "stravaDescription": "Connecte ton compte Strava pour synchroniser tes activités avec ton historique d'entraînement.", @@ -7169,7 +7155,12 @@ "signInAgain": "Reconnectez-vous, puis réessayez. Votre compte n'a ni mot de passe ni clé d'accès à confirmer ici.", "failed": "Cela n'a pas fonctionné. Vérifiez votre saisie et réessayez." }, - "passwordTitle": "Mot de passe" + "passwordTitle": "Mot de passe", + "tokenNameLabel": "Nom du jeton", + "displayCard": { + "title": "Affichage", + "description": "Langue, unités et formats, enregistrés dès que vous les modifiez." + } }, "admin": { "title": "Administration", @@ -7377,8 +7368,8 @@ "coachFollowsSwitch": "Suit l'interrupteur Coach de la section Coach." }, "invites": { - "title": "Invitations", - "description": "Liens d'invitation pour l'inscription — ils fonctionnent même lorsque les inscriptions sont fermées.", + "title": "Liens d'invitation", + "description": "Chaque lien permet une inscription 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.", @@ -7424,8 +7415,8 @@ "revokedToast": "Invitation révoquée.", "revokeError": "Impossible de révoquer l'invitation." }, - "userManagement": "Gestion des utilisateurs", - "userManagementDescription": "Gérez les comptes : rôles, réinitialisations de mot de passe et déconnexions forcées.", + "userManagement": "Comptes", + "userManagementDescription": "Rôles, réinitialisations de mot de passe et déconnexions forcées pour chaque compte.", "userName": "Nom", "userEmail": "E-mail", "userRole": "Rôle", @@ -7881,7 +7872,12 @@ "backupsNone": "Aucune sauvegarde n'a encore enregistré les clés dont elle a besoin.", "backupsStillNeed": "Toutes les lignes sont renouvelées, mais des sauvegardes ont encore besoin de {keys}", "backupsUnrecordedBadge": "Toutes les lignes sont renouvelées, mais d'anciennes sauvegardes peuvent encore avoir besoin d'une clé retirée", - "backupsDetail": "La rotation rechiffre chaque ligne, mais une sauvegarde garde la clé sous laquelle son contenu a été écrit, et sa restauration en a besoin. Gardez une clé retirée dans ENCRYPTION_KEYS tant qu'elle figure ci-dessous." + "backupsDetail": "La rotation rechiffre chaque ligne, mais une sauvegarde garde la clé sous laquelle son contenu a été écrit, et sa restauration en a besoin. Gardez une clé retirée dans ENCRYPTION_KEYS tant qu'elle figure ci-dessous.", + "columnsShow": "Afficher le tableau ({count} avec des lignes)", + "columnsHide": "Masquer le tableau", + "columnsShowAll": "Afficher les {count} colonnes", + "columnsShowWithRows": "Seulement les colonnes avec des lignes", + "columnsNoneWithRows": "Aucune colonne chiffrée ne contient encore de lignes." }, "maintenance": { "title": "Maintenance de la table des mesures", diff --git a/messages/it.json b/messages/it.json index 7a091f12f..7e31189e0 100644 --- a/messages/it.json +++ b/messages/it.json @@ -5310,12 +5310,10 @@ "sections": { "account": { "title": "Account", - "description": "Profilo, password, passkey e il tour di benvenuto.", "subtitle": "Il tuo profilo, la tua identità e i dati di accesso." }, "integrations": { "title": "Integrazioni", - "description": "Servizi collegati, canali di consegna e priorità delle fonti in un unico posto.", "subtitle": "Collega e gestisci le tue fonti di dati e i dispositivi.", "connectionsHeading": "Connessioni", "channelsHeading": "Canali di recapito", @@ -5324,16 +5322,14 @@ }, "notifications": { "title": "Notifiche", - "description": "Tutti i promemoria in un unico posto — cosa e quando, e dove vengono recapitati.", "subtitle": "Scegli quali promemoria ricevere e dove vengono recapitati.", "remindersHeading": "Promemoria", "matrixLink": "Notifiche per canale", "channelsLink": "Canali di recapito" }, "layout": { - "title": "Aspetto", - "description": "Scegli l'aspetto e la disposizione di dashboard, approfondimenti e dei tuoi moduli di monitoraggio.", - "backToHub": "Torna ad Aspetto", + "title": "Layout", + "backToHub": "Torna a Layout", "dashboard": { "title": "Cruscotto", "description": "Layout e ordine dei riquadri." @@ -5342,7 +5338,7 @@ "title": "Approfondimenti", "description": "Ordine della panoramica e delle schede di navigazione." }, - "subtitle": "Personalizza l'aspetto di dashboard, approfondimenti e moduli di monitoraggio.", + "subtitle": "Scegli cosa mostrano dashboard, approfondimenti e moduli di monitoraggio, e in quale ordine.", "medications": { "title": "Farmaci", "description": "Vista elenco e ordine manuale." @@ -5373,12 +5369,10 @@ }, "thresholds": { "title": "Obiettivi", - "description": "Per metrica: il tuo intervallo target per valori sani.", "subtitle": "Imposta i tuoi intervalli target personali per metrica." }, "sources": { "title": "Priorità delle fonti", - "description": "Scegli quale fonte prevale quando più di una registra la stessa metrica lo stesso giorno.", "integrationsHint": "Collega o scollega le fonti dati nella pagina Integrazioni.", "integrationsHintLink": "Apri le integrazioni", "cardTitle": "Ordine per metrica", @@ -5460,26 +5454,23 @@ "moduleHintLink": "Gestisci moduli", "detail": "Sola lettura: i valori appartengono all'app di origine." }, - "helpDetail": "Le altre restano memorizzate per la traccia di controllo." + "helpDetail": "Le altre restano memorizzate per la traccia di controllo. Sono elencate solo le fonti collegate o con dati.", + "noneInUse": "Ancora nessuna fonte da ordinare. Collegane una in Integrazioni o registra un valore." }, "ai": { "title": "Provider IA", - "description": "Provider, modello e chiave API per gli approfondimenti, oltre al tuo contesto «su di me».", "subtitle": "Scegli il tuo provider IA e usa la tua chiave." }, "api": { - "title": "API & Tokens", - "description": "Token Bearer per i tuoi script e le tue app — registra misurazioni o assunzioni di farmaci da ovunque.", + "title": "API e token", "subtitle": "Gestisci gli endpoint API e i token di accesso." }, "advanced": { "title": "Avanzate", - "description": "La via d'uscita: cancella per sempre tutti i dati di salute o chiudi l'account.", "subtitle": "Ripristino dati ed eliminazione account." }, "export": { "title": "Esporta e importa", - "description": "Scarica i tuoi dati come CSV o backup JSON completo, oppure importa da Apple Salute, JSON o CSV. Il referto medico ora si trova in Cartella sanitaria.", "otherOptionsHeading": "Altre opzioni di esportazione", "downloadFailed": "Impossibile scaricare l'esportazione. Riprova.", "actions": { @@ -5724,12 +5715,10 @@ }, "about": { "title": "Informazioni su questa app", - "description": "Versione, licenza, link.", "subtitle": "Versione, licenza e link al progetto." }, "sharing": { "title": "Condivisione", - "description": "Crea un link di sola lettura per condividere la tua cartella clinica con un medico.", "subtitle": "Condividi l'accesso in sola lettura ai tuoi dati." }, "insights": { @@ -5764,12 +5753,10 @@ }, "gesundheitsakte": { "title": "Cartella clinica", - "description": "Esporta la tua cartella clinica completa in PDF, FHIR R4 o in un pacchetto ZIP combinato.", "subtitle": "Esporta una cartella clinica completa, pronta per il medico." }, "coach": { "title": "Coach", - "description": "Attiva il Coach, tono e ambito, ricordi salvati.", "subtitle": "Regola il Coach, i suoi suggerimenti proattivi e la sua memoria." }, "security": { @@ -5778,8 +5765,7 @@ }, "privacy": { "title": "Dati e privacy", - "subtitle": "I tuoi dati, la conservazione, la cifratura e la sicurezza dell'account in un unico posto.", - "description": "Esportazione, eliminazione, conservazione, stato della cifratura, sessioni e attività di sicurezza." + "subtitle": "I tuoi dati, la conservazione, la cifratura e la sicurezza dell'account in un unico posto." }, "mcp": { "title": "Connettore MCP", @@ -5787,8 +5773,7 @@ }, "anamnesis": { "title": "Anamnesi", - "subtitle": "La tua anamnesi, lo stile di vita, i dati di emergenza e ciò che l’IA può leggere.", - "description": "Record strutturati di allergie e anamnesi familiare." + "subtitle": "La tua anamnesi, lo stile di vita, i dati di emergenza e ciò che l’IA può leggere." }, "environment": { "title": "Posizione e meteo", @@ -6542,30 +6527,32 @@ "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.", + "apiTokensDescription": "Rivedi e revoca i token per misurazioni, allenamenti, documenti e assunzioni di farmaci.", "measurementsToken": { "title": "Token per misurazioni", "description": "Crea un token perché una bilancia, un orologio o la tua domotica aggiungano misurazioni.", "detail": "Il token viene mostrato una sola volta.", "scopeNote": "Può soltanto aggiungere nuove misurazioni alla tua cartella: non può leggere i tuoi dati, modificare o eliminare nulla, né creare un altro token. Le misurazioni vengono contrassegnate come provenienti da un dispositivo esterno, così le riconosci in seguito, e puoi comunque correggerle.", - "createFailed": "Non è stato possibile creare il token. Riprova." + "createFailed": "Non è stato possibile creare il token. Riprova.", + "namePlaceholder": "Nome, ad es. Home Assistant" }, "workoutsToken": { "title": "Token per allenamenti", "description": "Crea un token perché un ponte dell'orologio o uno script di sincronizzazione aggiungano i tuoi allenamenti.", "detail": "Il token viene mostrato una sola volta.", "scopeNote": "Può soltanto aggiungere nuovi allenamenti alla tua cartella. Non può leggere i tuoi dati, modificare o eliminare nulla, né creare un altro token. Gli allenamenti che aggiunge vengono contrassegnati come provenienti da un dispositivo esterno e, se il telefono ha registrato la stessa sessione, vedi la copia del telefono.", - "createFailed": "Non è stato possibile creare il token. Riprova." + "createFailed": "Non è stato possibile creare il token. Riprova.", + "namePlaceholder": "Nome, ad es. Garmin" }, "documentsToken": { "title": "Token per documenti", "description": "Crea un token perché Paperless-ngx, Papra o lo script di importazione aggiungano documenti al tuo archivio.", "detail": "Il token viene mostrato una sola volta.", "scopeNote": "Può soltanto aggiungere nuovi documenti al tuo archivio. Non può elencare, aprire, scaricare, modificare o eliminare nulla, né creare un altro token. Riceve solo l'informazione se un file è stato salvato, non come l'hai archiviato.", - "createFailed": "Non è stato possibile creare il token. Riprova." + "createFailed": "Non è stato possibile creare il token. Riprova.", + "namePlaceholder": "Nome, ad es. Paperless-ngx" }, - "tokenMintMovedDescription": "I token vengono emessi dove si applicano: qui sopra per le misurazioni e i documenti, oppure nell'endpoint API di un farmaco per quel singolo farmaco.", - "tokenNamePlaceholder": "Nome del token (ad es. Comando rapido iPhone)", + "tokenMintMovedDescription": "I token vengono emessi dove si applicano: qui sopra per le misurazioni, gli allenamenti e i documenti, oppure nell'endpoint API di un farmaco per quel singolo farmaco.", "tokenCreated": "Token creato — copialo ora! Non verrà mostrato di nuovo.", "tokenCopy": "Copia token", "tokenCopied": "Token copiato negli appunti", @@ -6757,7 +6744,8 @@ "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", - "createDetail": "Il token viene mostrato una sola volta alla creazione — salvalo subito; non può essere recuperato." + "createDetail": "Il token viene mostrato una sola volta alla creazione — salvalo subito; non può essere recuperato.", + "activeDescription": "I link che hai condiviso, quando scadono e quante volte sono stati aperti." }, "genderOther": "Altro", "nightscout": "Nightscout", @@ -6983,12 +6971,11 @@ }, "stored": { "title": "Cosa viene memorizzato", - "measurements": "Misurazioni (peso, pressione, polso, glicemia, sonno, composizione corporea)", - "medications": "Farmaci e cronologia delle assunzioni", - "moodLabs": "Voci dell'umore e referti di laboratorio", - "coach": "Conversazioni con il coach", + "measurements": "Misurazioni: peso, pressione, battito e ogni altro valore registrato da te o da un dispositivo", "integrations": "Token di integrazione (cifrati)", - "security": "Attività dell'account e di sicurezza (sessioni, registro degli accessi)" + "security": "Attività dell'account e di sicurezza (sessioni, registro degli accessi)", + "profile": "Il tuo profilo, l'anamnesi e i dati della cartella sanitaria", + "modules": "Dati di ogni modulo: {list}" }, "retention": { "title": "Conservazione", @@ -7019,8 +7006,6 @@ "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.", - "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.", @@ -7039,7 +7024,8 @@ "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.", "tokensDetail": "Il token viene mostrato una sola volta.", - "connectionsDetail": "Revocare una connessione ne termina subito l'accesso, inclusa la capacità di rinnovarsi." + "connectionsDetail": "Revocare una connessione ne termina subito l'accesso, inclusa la capacità di rinnovarsi.", + "tokenNamePlaceholder": "Nome, ad es. Claude Desktop" }, "strava": "Strava", "stravaDescription": "Collega il tuo account Strava per sincronizzare le tue attività con la cronologia degli allenamenti.", @@ -7169,7 +7155,12 @@ "signInAgain": "Accedi di nuovo, poi riprova. Il tuo account non ha una password o una passkey da confermare qui.", "failed": "Non ha funzionato. Controlla quanto inserito e riprova." }, - "passwordTitle": "Password" + "passwordTitle": "Password", + "tokenNameLabel": "Nome del token", + "displayCard": { + "title": "Visualizzazione", + "description": "Lingua, unità e formati, salvati appena li modifichi." + } }, "admin": { "title": "Administration", @@ -7377,8 +7368,8 @@ "coachFollowsSwitch": "Segue l'interruttore Coach nella sezione Coach." }, "invites": { - "title": "Inviti", - "description": "Link di invito per la registrazione: funzionano anche quando la registrazione è chiusa.", + "title": "Link di invito", + "description": "Ogni link consente una registrazione anche quando la registrazione è chiusa.", "create": "Crea invito", "createTitle": "Crea invito", "createDescription": "Il link consente una registrazione anche quando la registrazione è chiusa.", @@ -7424,8 +7415,8 @@ "revokedToast": "Invito revocato.", "revokeError": "Impossibile revocare l'invito." }, - "userManagement": "Gestione utenti", - "userManagementDescription": "Gestisci gli account: ruoli, reset delle password e disconnessioni forzate.", + "userManagement": "Account", + "userManagementDescription": "Ruoli, reset delle password e disconnessioni forzate per ogni account.", "userName": "Nome", "userEmail": "E-mail", "userRole": "Ruolo", @@ -7881,7 +7872,12 @@ "backupsNone": "Nessun backup ha ancora registrato le chiavi di cui ha bisogno.", "backupsStillNeed": "Tutte le righe sono ruotate, ma i backup hanno ancora bisogno di {keys}", "backupsUnrecordedBadge": "Tutte le righe sono ruotate, ma backup più vecchi potrebbero ancora richiedere una chiave ritirata", - "backupsDetail": "La rotazione cifra di nuovo ogni riga, ma un backup conserva la chiave con cui è stato scritto il suo contenuto, e ripristinarlo richiede quella chiave. Tieni una chiave ritirata in ENCRYPTION_KEYS finché compare qui sotto." + "backupsDetail": "La rotazione cifra di nuovo ogni riga, ma un backup conserva la chiave con cui è stato scritto il suo contenuto, e ripristinarlo richiede quella chiave. Tieni una chiave ritirata in ENCRYPTION_KEYS finché compare qui sotto.", + "columnsShow": "Mostra la tabella ({count} con righe)", + "columnsHide": "Nascondi la tabella", + "columnsShowAll": "Mostra tutte le {count} colonne", + "columnsShowWithRows": "Solo colonne con righe", + "columnsNoneWithRows": "Nessuna colonna cifrata contiene ancora righe." }, "maintenance": { "title": "Manutenzione della tabella delle misurazioni", diff --git a/messages/ko.json b/messages/ko.json index 5f1896298..32c245fac 100644 --- a/messages/ko.json +++ b/messages/ko.json @@ -5310,12 +5310,10 @@ "sections": { "account": { "title": "계정", - "description": "프로필, 비밀번호, 패스키, 시작 설정 둘러보기.", "subtitle": "프로필과 신원, 로그인 정보예요." }, "integrations": { "title": "연동", - "description": "연결된 서비스, 전송 채널, 소스 우선순위를 한곳에서.", "subtitle": "데이터 소스와 기기를 연결하고 관리해요.", "connectionsHeading": "연결", "channelsHeading": "전송 채널", @@ -5324,7 +5322,6 @@ }, "notifications": { "title": "알림", - "description": "모든 알림을 한곳에서 — 무엇을 언제 알릴지, 어디로 보낼지.", "subtitle": "받을 알림과 전달될 곳을 골라요.", "remindersHeading": "알림", "matrixLink": "채널별 알림", @@ -5332,7 +5329,6 @@ }, "layout": { "title": "화면 구성", - "description": "대시보드, 인사이트, 기록 모듈의 모습과 배치를 정해요.", "backToHub": "화면 구성으로 돌아가기", "dashboard": { "title": "대시보드", @@ -5342,7 +5338,7 @@ "title": "인사이트", "description": "개요 섹션과 내비게이션 칩 순서." }, - "subtitle": "대시보드, 인사이트, 기록 모듈의 모습을 원하는 대로 바꿔요.", + "subtitle": "대시보드, 인사이트, 기록 모듈에 무엇을 어떤 순서로 보여줄지 정해요.", "medications": { "title": "복약", "description": "목록 보기와 수동 순서." @@ -5373,12 +5369,10 @@ }, "thresholds": { "title": "목표", - "description": "지표별로 건강한 값의 목표 범위를 정해요.", "subtitle": "지표별 목표 범위를 직접 정해요." }, "sources": { "title": "소스 우선순위", - "description": "같은 날 같은 지표를 여러 소스가 기록했을 때 어느 소스를 쓸지 골라요.", "integrationsHint": "데이터 소스 연결과 해제는 연동 페이지에서 해요.", "integrationsHintLink": "연동 열기", "cardTitle": "지표별 순위", @@ -5460,26 +5454,23 @@ "moduleHintLink": "모듈 관리", "detail": "읽기 전용 — 숫자는 원본 앱이 관리해요." }, - "helpDetail": "나머지는 감사 기록용으로 그대로 보관해요." + "helpDetail": "나머지는 감사 기록용으로 그대로 보관해요. 연결했거나 데이터가 있는 소스만 보여요.", + "noneInUse": "아직 순위를 정할 소스가 없어요. 연동에서 연결하거나 값을 기록해 보세요." }, "ai": { "title": "AI 제공자", - "description": "인사이트에 쓸 제공자, 모델, API 키 — 그리고 '나에 대해' 컨텍스트.", "subtitle": "AI 제공자를 고르고 직접 키를 등록해요." }, "api": { "title": "API와 토큰", - "description": "내 스크립트와 앱에서 쓸 베어러 토큰 — 어디서든 측정값이나 약 복용을 기록해요.", "subtitle": "API 엔드포인트와 액세스 토큰을 관리해요." }, "advanced": { "title": "고급", - "description": "마지막 출구: 모든 건강 기록을 영구히 지우거나 계정을 완전히 닫아요.", "subtitle": "데이터 초기화와 계정 삭제." }, "export": { "title": "내보내기와 불러오기", - "description": "데이터를 CSV나 전체 JSON 백업으로 내려받고, Apple Health·JSON·CSV에서 불러와요. 진료용 리포트는 이제 건강 기록에 있어요.", "otherOptionsHeading": "다른 내보내기 옵션", "downloadFailed": "내보내기 파일을 내려받지 못했어요. 다시 시도해 주세요.", "actions": { @@ -5724,12 +5715,10 @@ }, "about": { "title": "앱 정보", - "description": "버전, 라이선스, 링크.", "subtitle": "버전, 라이선스, 프로젝트 링크." }, "sharing": { "title": "공유", - "description": "임상의와 건강 기록을 나눌 읽기 전용 링크를 만들어요.", "subtitle": "데이터를 읽기 전용으로 공유해요." }, "insights": { @@ -5764,12 +5753,10 @@ }, "gesundheitsakte": { "title": "건강 기록", - "description": "전체 건강 기록을 PDF, FHIR R4, 또는 하나로 묶은 ZIP으로 내보내요.", "subtitle": "임상의에게 바로 건넬 수 있는 완전한 건강 기록을 내보내요." }, "coach": { "title": "코치", - "description": "코치 켜기, 말투와 범위, 저장된 기억.", "subtitle": "코치의 말투, 먼저 건네는 알림, 기억을 조정해요." }, "security": { @@ -5778,8 +5765,7 @@ }, "privacy": { "title": "데이터와 개인정보", - "subtitle": "데이터, 보관 기간, 암호화, 계정 보안을 한곳에서.", - "description": "내보내기, 삭제, 보관 기간, 암호화 상태, 세션, 보안 활동." + "subtitle": "데이터, 보관 기간, 암호화, 계정 보안을 한곳에서." }, "mcp": { "title": "MCP 커넥터", @@ -5787,8 +5773,7 @@ }, "anamnesis": { "title": "병력", - "subtitle": "병력, 생활 습관, 응급 정보와 AI가 읽을 수 있는 범위를 관리해요.", - "description": "구조화된 알레르기·가족력 기록." + "subtitle": "병력, 생활 습관, 응급 정보와 AI가 읽을 수 있는 범위를 관리해요." }, "environment": { "title": "위치 및 날씨", @@ -6542,30 +6527,32 @@ "fitbitViewData": "수면 보기", "googleHealthViewData": "걸음 수 보기", "apiTokens": "API 토큰", - "apiTokensDescription": "외부에서 복용을 기록할 때 쓰는 API 토큰을 확인하고 폐기해요.", + "apiTokensDescription": "측정값, 운동, 문서, 복용 기록용 토큰을 확인하고 폐기해요.", "measurementsToken": { "title": "측정 토큰", "description": "체중계나 시계, 홈 오토메이션이 측정값을 기록할 수 있도록 토큰을 만들어요.", "detail": "토큰은 한 번만 표시돼요.", "scopeNote": "본인 기록에 새 측정값을 추가하는 것만 할 수 있어요. 데이터를 읽거나, 무언가를 고치거나 지우거나, 다른 토큰을 만들 수는 없어요. 측정값은 외부 기기에서 온 것으로 표시되니 나중에 구분할 수 있고, 언제든 고칠 수 있어요.", - "createFailed": "토큰을 만들지 못했어요. 다시 시도해 주세요." + "createFailed": "토큰을 만들지 못했어요. 다시 시도해 주세요.", + "namePlaceholder": "이름 (예: Home Assistant)" }, "workoutsToken": { "title": "운동 토큰", "description": "시계 연동 브리지나 동기화 스크립트가 운동 기록을 추가할 수 있도록 토큰을 만들어요.", "detail": "토큰은 한 번만 표시돼요.", "scopeNote": "본인 기록에 새 운동을 추가하는 것만 할 수 있어요. 데이터를 읽거나, 무언가를 고치거나 지우거나, 다른 토큰을 만들 수는 없어요. 이 토큰으로 추가된 운동은 외부 기기에서 온 것으로 표시되고, 휴대폰이 같은 운동을 기록했다면 휴대폰의 기록이 보여요.", - "createFailed": "토큰을 만들지 못했어요. 다시 시도해 주세요." + "createFailed": "토큰을 만들지 못했어요. 다시 시도해 주세요.", + "namePlaceholder": "이름 (예: Garmin)" }, "documentsToken": { "title": "문서 토큰", "description": "Paperless-ngx, Papra 또는 가져오기 스크립트가 문서함에 문서를 추가할 수 있도록 토큰을 만들어요.", "detail": "토큰은 한 번만 표시돼요.", "scopeNote": "본인 문서함에 새 문서를 추가하는 것만 할 수 있어요. 목록을 보거나, 열거나, 내려받거나, 고치거나 지울 수 없고, 다른 토큰을 만들 수도 없어요. 파일이 저장됐는지만 알 수 있고, 어떻게 정리했는지는 알 수 없어요.", - "createFailed": "토큰을 만들지 못했어요. 다시 시도해 주세요." + "createFailed": "토큰을 만들지 못했어요. 다시 시도해 주세요.", + "namePlaceholder": "이름 (예: Paperless-ngx)" }, - "tokenMintMovedDescription": "토큰은 쓰이는 곳에서 발급돼요. 측정값과 문서는 위에서, 특정 약은 그 약의 API 엔드포인트에서 만들 수 있어요.", - "tokenNamePlaceholder": "토큰 이름 (예: iPhone 단축어)", + "tokenMintMovedDescription": "토큰은 쓰이는 곳에서 발급돼요. 측정값, 운동, 문서는 위에서, 특정 약은 그 약의 API 엔드포인트에서 만들 수 있어요.", "tokenCreated": "토큰을 만들었어요 — 지금 복사하세요! 다시 보여주지 않아요.", "tokenCopy": "토큰 복사", "tokenCopied": "토큰을 클립보드에 복사했어요", @@ -6757,7 +6744,8 @@ "statusNeedsReselect": "내용 다시 선택", "reselectReason": "이 링크는 선택한 적 없는 범위를 제공하고 있어서 닫았어요. 새로 만들고 담을 내용을 골라 주세요.", "reselectAction": "새 링크 만들기", - "createDetail": "토큰은 만들 때 한 번만 보여줘요 — 그때 저장해 두세요. 복구할 수 없어요." + "createDetail": "토큰은 만들 때 한 번만 보여줘요 — 그때 저장해 두세요. 복구할 수 없어요.", + "activeDescription": "공유한 링크와 만료 시점, 열람 횟수를 보여줘요." }, "genderOther": "기타", "nightscout": "Nightscout", @@ -6983,12 +6971,11 @@ }, "stored": { "title": "저장되는 항목", - "measurements": "측정값 (체중, 혈압, 맥박, 혈당, 수면, 체성분)", - "medications": "약과 복용 기록", - "moodLabs": "기분 기록과 검사 결과", - "coach": "코치 대화", + "measurements": "측정값: 체중, 혈압, 맥박과 직접 또는 기기로 기록한 모든 값", "integrations": "연동 토큰 (암호화됨)", - "security": "계정·보안 활동 (세션, 로그인 기록)" + "security": "계정·보안 활동 (세션, 로그인 기록)", + "profile": "프로필, 병력, 건강 기록 정보", + "modules": "모듈별 기록: {list}" }, "retention": { "title": "보관 기간", @@ -7019,8 +7006,6 @@ "mcp": { "enableTitle": "원격 MCP 엔드포인트", "enableDescription": "MCP를 지원하는 어시스턴트(Claude.ai, ChatGPT)가 범위가 제한된 토큰으로 건강 데이터를 읽게 해요.", - "enabled": "켜짐", - "disabled": "꺼짐", "enableToggleLabel": "MCP 엔드포인트 켜기", "endpointLabel": "커넥터 URL", "connectDescription": "어시스턴트의 커스텀 커넥터 설정에 이 URL을 붙여 넣고, 아래 토큰으로 인증하거나 안내가 나오면 로그인하세요.", @@ -7039,7 +7024,8 @@ "connectionRevokeDescription": "어시스턴트의 접근을 즉시 끊고 갱신도 막아요. 다시 연결하려면 승인을 새로 받아야 해요.", "connectionRevokeFailed": "어시스턴트 연결을 해제하지 못했어요.", "connectionsDetail": "연결을 해지하면 갱신 권한까지 포함해 접근이 즉시 끊겨요.", - "tokensDetail": "토큰은 한 번만 보여줘요." + "tokensDetail": "토큰은 한 번만 보여줘요.", + "tokenNamePlaceholder": "이름 (예: Claude Desktop)" }, "strava": "Strava", "stravaDescription": "Strava 계정을 연결해 활동을 운동 기록으로 동기화해요.", @@ -7169,7 +7155,12 @@ "signInAgain": "다시 로그인한 후 한 번 더 시도하세요. 이 계정에는 여기서 확인할 비밀번호나 패스키가 없습니다.", "failed": "확인하지 못했습니다. 입력한 내용을 확인하고 다시 시도하세요." }, - "passwordTitle": "비밀번호" + "passwordTitle": "비밀번호", + "tokenNameLabel": "토큰 이름", + "displayCard": { + "title": "표시", + "description": "언어, 단위, 형식은 바꾸는 즉시 저장돼요." + } }, "admin": { "title": "관리자", @@ -7353,8 +7344,8 @@ "coachFollowsSwitch": "코치 섹션의 코치 스위치를 따라요." }, "invites": { - "title": "초대", - "description": "가입 초대 링크 — 회원가입이 닫혀 있어도 동작해요.", + "title": "초대 링크", + "description": "링크마다 회원가입이 닫혀 있어도 가입할 수 있어요.", "create": "초대 만들기", "createTitle": "초대 만들기", "createDescription": "회원가입이 닫혀 있어도 이 링크로는 가입할 수 있어요.", @@ -7400,8 +7391,8 @@ "revokedToast": "초대를 취소했어요.", "revokeError": "초대를 취소하지 못했어요." }, - "userManagement": "사용자 관리", - "userManagementDescription": "계정 관리: 역할, 비밀번호 재설정, 강제 로그아웃.", + "userManagement": "계정", + "userManagementDescription": "계정마다 역할, 비밀번호 재설정, 강제 로그아웃을 관리해요.", "userName": "이름", "userEmail": "이메일", "userRole": "역할", @@ -7857,7 +7848,12 @@ "backupsNone": "아직 필요한 키를 기록한 백업이 없습니다.", "backupsStillNeed": "모든 행이 회전되었지만 백업에 아직 {keys}가 필요합니다", "backupsUnrecordedBadge": "모든 행이 회전되었지만 이전 백업에는 아직 폐기한 키가 필요할 수 있습니다", - "backupsDetail": "회전은 모든 행을 다시 암호화하지만, 백업은 내용이 기록된 키를 그대로 유지하며 복원에는 그 키가 필요합니다. 아래에 더 이상 표시되지 않을 때까지 폐기한 키를 ENCRYPTION_KEYS에 유지하세요." + "backupsDetail": "회전은 모든 행을 다시 암호화하지만, 백업은 내용이 기록된 키를 그대로 유지하며 복원에는 그 키가 필요합니다. 아래에 더 이상 표시되지 않을 때까지 폐기한 키를 ENCRYPTION_KEYS에 유지하세요.", + "columnsShow": "표 보기 (행 있음 {count}개)", + "columnsHide": "표 숨기기", + "columnsShowAll": "열 {count}개 모두 보기", + "columnsShowWithRows": "행이 있는 열만", + "columnsNoneWithRows": "아직 행이 있는 암호화 열이 없어요." }, "maintenance": { "title": "측정값 테이블 유지 관리", diff --git a/messages/pl.json b/messages/pl.json index 9dd315df2..286d25498 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -5310,12 +5310,10 @@ "sections": { "account": { "title": "Konto", - "description": "Profil, hasło, klucze dostępu i przewodnik wprowadzający.", "subtitle": "Twój profil, tożsamość i dane logowania." }, "integrations": { "title": "Integracje", - "description": "Połączone usługi, kanały dostarczania i priorytet źródeł w jednym miejscu.", "subtitle": "Połącz i zarządzaj źródłami danych oraz urządzeniami.", "connectionsHeading": "Połączenia", "channelsHeading": "Kanały dostarczania", @@ -5324,16 +5322,14 @@ }, "notifications": { "title": "Powiadomienia", - "description": "Wszystkie przypomnienia w jednym miejscu — co i kiedy oraz gdzie są dostarczane.", "subtitle": "Wybierz, które przypomnienia otrzymujesz i gdzie są dostarczane.", "remindersHeading": "Przypomnienia", "matrixLink": "Powiadomienia wg kanału", "channelsLink": "Kanały dostarczania" }, "layout": { - "title": "Wygląd", - "description": "Ustal, jak wyglądają i są rozłożone pulpit, analizy i Twoje moduły śledzenia.", - "backToHub": "Powrót do Wygląd", + "title": "Układ", + "backToHub": "Powrót do układu", "dashboard": { "title": "Pulpit", "description": "Układ i kolejność kafelków." @@ -5342,7 +5338,7 @@ "title": "Analizy", "description": "Kolejność przeglądu i pigułek nawigacyjnych." }, - "subtitle": "Dostosuj wygląd pulpitu, analiz i modułów śledzenia.", + "subtitle": "Wybierz, co pokazują pulpit, analizy i moduły śledzenia oraz w jakiej kolejności.", "medications": { "title": "Leki", "description": "Widok listy i ręczna kolejność." @@ -5373,12 +5369,10 @@ }, "thresholds": { "title": "Cele", - "description": "Dla każdej metryki: własny zakres docelowy zdrowych wartości.", "subtitle": "Ustaw osobiste zakresy docelowe dla każdej metryki." }, "sources": { "title": "Priorytet źródeł", - "description": "Wybierz, które źródło wygrywa, gdy kilka rejestruje tę samą metrykę tego samego dnia.", "integrationsHint": "Źródła danych podłączysz lub odłączysz na stronie integracji.", "integrationsHintLink": "Otwórz integracje", "cardTitle": "Kolejność według metryki", @@ -5460,26 +5454,23 @@ "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." + "helpDetail": "Pozostałe pozostają zapisane na potrzeby audytu. Widać tylko źródła, które połączono lub z których są dane.", + "noneInUse": "Nie ma jeszcze źródeł do uszeregowania. Połącz jedno w Integracjach albo wpisz wartość." }, "ai": { "title": "Dostawca AI", - "description": "Dostawca, model i klucz API do analiz, a także Twój kontekst „o mnie”.", "subtitle": "Wybierz dostawcę AI i użyj własnego klucza." }, "api": { - "title": "API & Tokens", - "description": "Tokeny Bearer dla własnych skryptów i aplikacji — zapisuj pomiary lub przyjęcia leków z dowolnego miejsca.", + "title": "API i tokeny", "subtitle": "Zarządzaj punktami końcowymi API i tokenami dostępu." }, "advanced": { "title": "Zaawansowane", - "description": "Wyjście awaryjne: trwale usuń wszystkie dane zdrowotne albo zamknij konto.", "subtitle": "Reset danych i usunięcie konta." }, "export": { "title": "Eksport i import", - "description": "Pobierz swoje dane jako CSV lub pełną kopię zapasową JSON albo zaimportuj z Apple Health, JSON lub CSV. Raport dla lekarza znajdziesz teraz w Dokumentacji zdrowotnej.", "otherOptionsHeading": "Inne opcje eksportu", "downloadFailed": "Nie udało się pobrać eksportu. Spróbuj ponownie.", "actions": { @@ -5724,12 +5715,10 @@ }, "about": { "title": "O tej aplikacji", - "description": "Wersja, licencja, linki.", "subtitle": "Wersja, licencja i linki do projektu." }, "sharing": { "title": "Udostępnianie", - "description": "Utwórz link tylko do odczytu, aby udostępnić swoją dokumentację zdrowotną lekarzowi.", "subtitle": "Udostępnij dostęp tylko do odczytu do swoich danych." }, "insights": { @@ -5764,12 +5753,10 @@ }, "gesundheitsakte": { "title": "Dokumentacja zdrowotna", - "description": "Wyeksportuj pełną dokumentację zdrowotną jako PDF, FHIR R4 lub łączony pakiet ZIP.", "subtitle": "Eksportuj kompletną dokumentację zdrowotną gotową dla lekarza." }, "coach": { "title": "Coach", - "description": "Włącz Coacha, ton i zakres, zapisane wspomnienia.", "subtitle": "Dostosuj Coacha, jego proaktywne podpowiedzi i pamięć." }, "security": { @@ -5778,8 +5765,7 @@ }, "privacy": { "title": "Dane i prywatność", - "subtitle": "Twoje dane, retencja, szyfrowanie i bezpieczeństwo konta w jednym miejscu.", - "description": "Eksport, usuwanie, retencja, stan szyfrowania, sesje i aktywność zabezpieczeń." + "subtitle": "Twoje dane, retencja, szyfrowanie i bezpieczeństwo konta w jednym miejscu." }, "mcp": { "title": "Łącznik MCP", @@ -5787,8 +5773,7 @@ }, "anamnesis": { "title": "Wywiad medyczny", - "subtitle": "Twoja historia zdrowia, styl życia, dane alarmowe i to, co może czytać AI.", - "description": "Ustrukturyzowane rekordy alergii i wywiadu rodzinnego." + "subtitle": "Twoja historia zdrowia, styl życia, dane alarmowe i to, co może czytać AI." }, "environment": { "title": "Lokalizacja i pogoda", @@ -6542,30 +6527,32 @@ "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.", + "apiTokensDescription": "Przeglądaj i unieważniaj tokeny do pomiarów, treningów, dokumentów i przyjęć leków.", "measurementsToken": { "title": "Tokeny pomiarów", "description": "Utwórz token, aby waga, zegarek lub automatyka domowa mogły dodawać pomiary.", "detail": "Token pokazujemy tylko raz.", "scopeNote": "Może wyłącznie dodawać nowe pomiary do Twojej dokumentacji — nie odczyta danych, niczego nie zmieni ani nie usunie i nie utworzy kolejnego tokenu. Pomiary są oznaczane jako pochodzące z urządzenia zewnętrznego, dzięki czemu później je rozpoznasz, i nadal możesz je poprawić.", - "createFailed": "Nie udało się utworzyć tokenu. Spróbuj ponownie." + "createFailed": "Nie udało się utworzyć tokenu. Spróbuj ponownie.", + "namePlaceholder": "Nazwa, np. Home Assistant" }, "workoutsToken": { "title": "Tokeny treningów", "description": "Utwórz token, aby mostek zegarka lub skrypt synchronizacji mogły dodawać Twoje treningi.", "detail": "Token pokazujemy tylko raz.", "scopeNote": "Może wyłącznie dodawać nowe treningi do Twojej dokumentacji. Nie odczyta danych, niczego nie zmieni ani nie usunie i nie utworzy kolejnego tokenu. Dodane przez niego treningi są oznaczane jako pochodzące z urządzenia zewnętrznego, a jeśli telefon zapisał tę samą sesję, widzisz zapis z telefonu.", - "createFailed": "Nie udało się utworzyć tokenu. Spróbuj ponownie." + "createFailed": "Nie udało się utworzyć tokenu. Spróbuj ponownie.", + "namePlaceholder": "Nazwa, np. Garmin" }, "documentsToken": { "title": "Tokeny dokumentów", "description": "Utwórz token, aby Paperless-ngx, Papra lub skrypt importu mogły dodawać dokumenty do Twojego archiwum.", "detail": "Token pokazujemy tylko raz.", "scopeNote": "Może wyłącznie dodawać nowe dokumenty do Twojego archiwum. Niczego nie wyświetli, nie otworzy, nie pobierze, nie zmieni ani nie usunie i nie utworzy kolejnego tokenu. Dowiaduje się tylko, czy plik został zapisany, a nie to, jak został opisany.", - "createFailed": "Nie udało się utworzyć tokenu. Spróbuj ponownie." + "createFailed": "Nie udało się utworzyć tokenu. Spróbuj ponownie.", + "namePlaceholder": "Nazwa, np. Paperless-ngx" }, - "tokenMintMovedDescription": "Tokeny wydajemy tam, gdzie działają: powyżej dla pomiarów i dokumentów albo w punkcie końcowym API danego leku dla tego jednego leku.", - "tokenNamePlaceholder": "Nazwa tokena (np. Skrót iPhone)", + "tokenMintMovedDescription": "Tokeny wydajemy tam, gdzie działają: powyżej dla pomiarów, treningów i dokumentów albo w punkcie końcowym API danego leku dla tego jednego leku.", "tokenCreated": "Token utworzony — skopiuj go teraz! Nie zostanie pokazany ponownie.", "tokenCopy": "Kopiuj token", "tokenCopied": "Token skopiowany do schowka", @@ -6757,7 +6744,8 @@ "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", - "createDetail": "Token jest pokazywany tylko raz przy tworzeniu — zapisz go wtedy; nie można go odzyskać." + "createDetail": "Token jest pokazywany tylko raz przy tworzeniu — zapisz go wtedy; nie można go odzyskać.", + "activeDescription": "Udostępnione linki, termin ich wygaśnięcia i liczba otwarć." }, "genderOther": "Inna", "nightscout": "Nightscout", @@ -6983,12 +6971,11 @@ }, "stored": { "title": "Co jest przechowywane", - "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", - "coach": "Rozmowy z asystentem", + "measurements": "Pomiary: waga, ciśnienie, tętno i każda inna wartość zapisana przez ciebie lub urządzenie", "integrations": "Tokeny integracji (szyfrowane)", - "security": "Aktywność konta i zabezpieczeń (sesje, dziennik logowań)" + "security": "Aktywność konta i zabezpieczeń (sesje, dziennik logowań)", + "profile": "Twój profil, wywiad medyczny i dane dokumentacji zdrowotnej", + "modules": "Wpisy poszczególnych modułów: {list}" }, "retention": { "title": "Retencja", @@ -7019,8 +7006,6 @@ "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.", - "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.", @@ -7039,7 +7024,8 @@ "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.", "tokensDetail": "Token jest wyświetlany tylko raz.", - "connectionsDetail": "Cofnięcie połączenia natychmiast kończy dostęp, w tym możliwość odnawiania." + "connectionsDetail": "Cofnięcie połączenia natychmiast kończy dostęp, w tym możliwość odnawiania.", + "tokenNamePlaceholder": "Nazwa, np. Claude Desktop" }, "strava": "Strava", "stravaDescription": "Połącz konto Strava, aby synchronizować aktywności z historią treningów.", @@ -7169,7 +7155,12 @@ "signInAgain": "Zaloguj się ponownie, a potem spróbuj jeszcze raz. Twoje konto nie ma hasła ani klucza dostępu do potwierdzenia tutaj.", "failed": "To się nie udało. Sprawdź wprowadzone dane i spróbuj ponownie." }, - "passwordTitle": "Hasło" + "passwordTitle": "Hasło", + "tokenNameLabel": "Nazwa tokena", + "displayCard": { + "title": "Wyświetlanie", + "description": "Język, jednostki i formaty zapisywane od razu po zmianie." + } }, "admin": { "title": "Administracja", @@ -7377,8 +7368,8 @@ "coachFollowsSwitch": "Podlega przełącznikowi Coach w sekcji Coach." }, "invites": { - "title": "Zaproszenia", - "description": "Linki z zaproszeniem do rejestracji — działają nawet przy zamkniętej rejestracji.", + "title": "Linki z zaproszeniem", + "description": "Każdy link pozwala na rejestrację nawet przy zamkniętej rejestracji.", "create": "Utwórz zaproszenie", "createTitle": "Utwórz zaproszenie", "createDescription": "Link umożliwia rejestrację nawet przy zamkniętej rejestracji.", @@ -7424,8 +7415,8 @@ "revokedToast": "Zaproszenie unieważnione.", "revokeError": "Nie udało się unieważnić zaproszenia." }, - "userManagement": "Zarządzanie użytkownikami", - "userManagementDescription": "Zarządzaj kontami: role, resety haseł i wymuszone wylogowania.", + "userManagement": "Konta", + "userManagementDescription": "Role, resety haseł i wymuszone wylogowania dla każdego konta.", "userName": "Nazwa", "userEmail": "E-mail", "userRole": "Rola", @@ -7881,7 +7872,12 @@ "backupsNone": "Żadna kopia nie zapisała jeszcze kluczy, których potrzebuje.", "backupsStillNeed": "Wszystkie wiersze są zrotowane, ale kopie nadal potrzebują {keys}", "backupsUnrecordedBadge": "Wszystkie wiersze są zrotowane, ale starsze kopie mogą nadal potrzebować wycofanego klucza", - "backupsDetail": "Rotacja ponownie szyfruje każdy wiersz, ale kopia zachowuje klucz, którym zapisano jej zawartość, a jej przywrócenie wymaga tego klucza. Trzymaj wycofany klucz w ENCRYPTION_KEYS, dopóki widnieje poniżej." + "backupsDetail": "Rotacja ponownie szyfruje każdy wiersz, ale kopia zachowuje klucz, którym zapisano jej zawartość, a jej przywrócenie wymaga tego klucza. Trzymaj wycofany klucz w ENCRYPTION_KEYS, dopóki widnieje poniżej.", + "columnsShow": "Pokaż tabelę (z wierszami: {count})", + "columnsHide": "Ukryj tabelę", + "columnsShowAll": "Pokaż wszystkie kolumny ({count})", + "columnsShowWithRows": "Tylko kolumny z wierszami", + "columnsNoneWithRows": "Żadna zaszyfrowana kolumna nie zawiera jeszcze wierszy." }, "maintenance": { "title": "Konserwacja tabeli pomiarów", diff --git a/src/__tests__/success-affordance-guard.test.ts b/src/__tests__/success-affordance-guard.test.ts index b1333d4b0..dffde273e 100644 --- a/src/__tests__/success-affordance-guard.test.ts +++ b/src/__tests__/success-affordance-guard.test.ts @@ -379,7 +379,7 @@ const PINNED_AFFORDANCES: Record< "text-success": 2, }, "src/components/settings/mcp-section.tsx": { - "text-success": 5, + "text-success": 4, "toast.success": 2, }, "src/components/settings/modules-section.tsx": { "toast.success": 1 }, diff --git a/src/app/api/auth/me/source-priority/__tests__/route.test.ts b/src/app/api/auth/me/source-priority/__tests__/route.test.ts index fdc7cc849..4b4801ea5 100644 --- a/src/app/api/auth/me/source-priority/__tests__/route.test.ts +++ b/src/app/api/auth/me/source-priority/__tests__/route.test.ts @@ -11,6 +11,10 @@ vi.mock("@/lib/db", () => ({ vi.mock("@/lib/auth/session", () => ({ getSession: vi.fn() })); +vi.mock("@/lib/integrations/sources-in-use", () => ({ + getSourcesInUse: vi.fn().mockResolvedValue(["WITHINGS", "MANUAL"]), +})); + vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); @@ -73,6 +77,17 @@ describe("GET /api/auth/me/source-priority", () => { expect(env.data).toHaveProperty("metricPriority"); expect(env.data).toHaveProperty("deviceTypePriority"); }); + + it("carries the account's sources in use beside the ladders", async () => { + vi.mocked(getSession).mockResolvedValue(SESSION_OK as never); + vi.mocked(prisma.user.findUnique).mockResolvedValue({ + sourcePriorityJson: null, + } as never); + + const res = await (GET as () => Promise)(); + const env = (await res.json()) as { data: { inUse: string[] } }; + expect(env.data.inUse).toEqual(["WITHINGS", "MANUAL"]); + }); }); describe("PUT /api/auth/me/source-priority", () => { diff --git a/src/app/api/auth/me/source-priority/route.ts b/src/app/api/auth/me/source-priority/route.ts index d5897cd1d..e37a4a528 100644 --- a/src/app/api/auth/me/source-priority/route.ts +++ b/src/app/api/auth/me/source-priority/route.ts @@ -30,6 +30,7 @@ import { annotate } from "@/lib/logging/context"; import { auditLog } from "@/lib/auth/audit"; import { prisma } from "@/lib/db"; import { invalidateUserProfile } from "@/lib/cache/invalidate"; +import { getSourcesInUse } from "@/lib/integrations/sources-in-use"; import { parseSourcePriority, sourcePrioritySchema, @@ -41,11 +42,19 @@ export const GET = apiHandler(async () => { const { user } = await requireAuth(); annotate({ action: { name: "auth.me.source-priority.get" } }); - const row = await prisma.user.findUnique({ - where: { id: user.id }, - select: { sourcePriorityJson: true }, + const [row, inUse] = await Promise.all([ + prisma.user.findUnique({ + where: { id: user.id }, + select: { sourcePriorityJson: true }, + }), + getSourcesInUse(user.id), + ]); + // `inUse` is read-only context for the editor: which ranked sources this + // account has connected or holds data from. It never feeds the ladder. + return apiSuccess({ + ...parseSourcePriority(row?.sourcePriorityJson), + inUse, }); - return apiSuccess(parseSourcePriority(row?.sourcePriorityJson)); }); export const PUT = apiHandler(async (req: Request) => { @@ -118,5 +127,8 @@ export const PUT = apiHandler(async (req: Request) => { action: { name: "auth.me.source-priority.put" }, meta: { keys: Object.keys(parsed.data).length }, }); - return apiSuccess(parseSourcePriority(parsed.data)); + return apiSuccess({ + ...parseSourcePriority(parsed.data), + inUse: await getSourcesInUse(user.id), + }); }); diff --git a/src/components/admin/__tests__/app-log-pagination.test.tsx b/src/components/admin/__tests__/app-log-pagination.test.tsx new file mode 100644 index 000000000..23534725e --- /dev/null +++ b/src/components/admin/__tests__/app-log-pagination.test.tsx @@ -0,0 +1,65 @@ +import { describe, expect, it, vi } from "vitest"; +import { renderToStaticMarkup } from "react-dom/server"; + +/** + * `/admin/app-logs` pages the buffered events fifty at a time, like the + * login overview, instead of painting up to ~500 rows in one table. + */ + +vi.mock("next/navigation", () => ({ + useRouter: () => ({ push: vi.fn(), replace: vi.fn() }), + usePathname: () => "/admin/app-logs", +})); + +const events = Array.from({ length: 120 }, (_, i) => ({ + request_id: `req-${i}`, + trace_id: `trace${String(i).padStart(11, "0")}`, + level: "info" as const, + timestamp: "2026-05-10T09:00:00Z", + duration_ms: 12, + action: { name: `event.number.${i}` }, + kind: "request", +})); + +let served = events; + +vi.mock("@tanstack/react-query", () => ({ + useQuery: () => ({ + data: { events: served, meta: { total: served.length, bufferMax: 500 } }, + isLoading: false, + refetch: vi.fn(), + isFetching: false, + }), +})); + +import { I18nProvider } from "@/lib/i18n/context"; +import { + APP_LOG_PAGE_SIZE, + AppLogPreviewSection, +} from "../app-log-preview-section"; + +function render() { + return renderToStaticMarkup( + + + , + ); +} + +describe("AppLogPreviewSection — pagination", () => { + it("paints one page of fifty rows and a pager", () => { + served = events; + const html = render(); + expect(APP_LOG_PAGE_SIZE).toBe(50); + expect(html.match(/event.number.0<"); + expect(html).not.toContain(">event.number.50<"); + expect(html).toContain('data-testid="app-log-pagination"'); + expect(html).toContain("Page 1 of 3"); + }); + + it("shows no pager when everything fits on one page", () => { + served = events.slice(0, 10); + expect(render()).not.toContain('data-testid="app-log-pagination"'); + }); +}); diff --git a/src/components/admin/__tests__/encryption-legacy-badge.test.tsx b/src/components/admin/__tests__/encryption-legacy-badge.test.tsx index 2043f5049..07c461003 100644 --- a/src/components/admin/__tests__/encryption-legacy-badge.test.tsx +++ b/src/components/admin/__tests__/encryption-legacy-badge.test.tsx @@ -23,7 +23,7 @@ vi.mock("@tanstack/react-query", () => ({ vi.mock("../key-backup-card", () => ({ KeyBackupCard: () => null })); import { I18nProvider } from "@/lib/i18n/context"; -import { EncryptionSection } from "../encryption-section"; +import { ColumnCoverage, EncryptionSection } from "../encryption-section"; function statusWith(configuredKeyCount: number) { return { @@ -77,9 +77,64 @@ describe("encryption coverage — the legacy-key badge", () => { }); it("names the per-column table as a focusable scroll region", () => { - status.current = statusWith(1); - expect(render()).toMatch( + const html = renderToStaticMarkup( + + + , + ); + expect(html).toMatch( /role="region"[^>]*tabindex="0"|tabindex="0"[^>]*role="region"/, ); }); }); + +function column(model: string, field: string, total: number) { + return { + model, + field, + kind: "string" as const, + total, + byKeyId: { v2: total }, + legacy: 0, + }; +} + +describe("encryption coverage — the per-column table", () => { + const columns = [ + column("User", "withRows", 4), + column("User", "empty", 0), + column("Note", "alsoEmpty", 0), + ]; + + function renderCoverage(initiallyOpen: boolean) { + return renderToStaticMarkup( + + + , + ); + } + + it("starts collapsed on the page, naming how many columns hold rows", () => { + status.current = { ...statusWith(1), columns }; + const html = render(); + expect(html).toContain('data-testid="encryption-columns-toggle"'); + expect(html).toContain('aria-expanded="false"'); + expect(html).toContain("Show the table (1 with rows)"); + expect(html).not.toContain('data-slot="encryption-column-rows"'); + }); + + it("lists only columns with rows when opened, with a toggle for all", () => { + const html = renderCoverage(true); + expect(html).toContain("User.withRows"); + expect(html).not.toContain("User.empty"); + expect(html).toContain("Show all 3 columns"); + }); +}); diff --git a/src/components/admin/__tests__/sections.test.tsx b/src/components/admin/__tests__/sections.test.tsx index bc6ee08a0..3d3449673 100644 --- a/src/components/admin/__tests__/sections.test.tsx +++ b/src/components/admin/__tests__/sections.test.tsx @@ -102,7 +102,7 @@ describe("admin sections — SSR smoke", () => { it(" renders with filter pills", () => { const html = render(); - expect(html).toContain("User Management"); + expect(html).toContain("Accounts"); // Filter pills painted from i18n expect(html).toContain("All"); expect(html).toContain("Admins"); @@ -110,7 +110,7 @@ describe("admin sections — SSR smoke", () => { it(" renders", () => { const html = render(); - expect(html).toContain("API Tokens"); + expect(html).toContain("API tokens"); }); it(" renders", () => { diff --git a/src/components/admin/app-log-preview-section.tsx b/src/components/admin/app-log-preview-section.tsx index 8f23a5bc4..b043500c3 100644 --- a/src/components/admin/app-log-preview-section.tsx +++ b/src/components/admin/app-log-preview-section.tsx @@ -55,6 +55,13 @@ import { apiGet } from "@/lib/api/api-fetch"; type RangePreset = "15m" | "1h" | "6h" | "all"; +/** + * Rows per page. The buffer answers with up to ~500 events at once; fifty a + * page matches the login overview's default and keeps the table one screen + * of scrolling instead of ten. + */ +export const APP_LOG_PAGE_SIZE = 50; + interface AppLogsResponse { events: WideEvent[]; meta: { @@ -92,6 +99,7 @@ export function AppLogPreviewSection() { const [level, setLevel] = useState("__all__"); const [range, setRange] = useState("1h"); const [selected, setSelected] = useState(null); + const [page, setPage] = useState(1); const params = useMemo(() => { const p = new URLSearchParams(); @@ -114,6 +122,14 @@ export function AppLogPreviewSection() { }); const events = data?.events ?? []; + // The buffer refetches every 30 s and can shrink under a narrower filter, + // so the page is clamped on read rather than trusted. + const lastPage = Math.max(1, Math.ceil(events.length / APP_LOG_PAGE_SIZE)); + const currentPage = Math.min(page, lastPage); + const pageEvents = events.slice( + (currentPage - 1) * APP_LOG_PAGE_SIZE, + currentPage * APP_LOG_PAGE_SIZE, + ); return ( @@ -133,19 +149,28 @@ export function AppLogPreviewSection() { type="search" placeholder={t("admin.section.app-logs.filterTraceId")} value={traceId} - onChange={(e) => setTraceId(e.target.value)} + onChange={(e) => { + setTraceId(e.target.value); + setPage(1); + }} aria-label={t("admin.section.app-logs.filterTraceId")} /> setActionFilter(e.target.value)} + onChange={(e) => { + setActionFilter(e.target.value); + setPage(1); + }} aria-label={t("admin.section.app-logs.filterAction")} /> - { + setRange(v as RangePreset); + setPage(1); + }} + > - {events.map((event, i) => { + {pageEvents.map((event, i) => { const actionLabel = event.action?.name ?? (`${event.http?.method ?? ""} ${event.http?.path ?? ""}`.trim() || @@ -290,7 +321,7 @@ export function AppLogPreviewSection() {
@@ -299,6 +330,38 @@ export function AppLogPreviewSection() { bufferMax: data?.meta.bufferMax ?? 500, })} + {/* Same pager as the login overview. */} + {lastPage > 1 ? ( +
+ + + {t("admin.section.auditLog.pageOf", { + page: currentPage, + total: lastPage, + })} + + +
+ ) : null}
)} diff --git a/src/components/admin/encryption-section.tsx b/src/components/admin/encryption-section.tsx index e2755e3af..8879cea70 100644 --- a/src/components/admin/encryption-section.tsx +++ b/src/components/admin/encryption-section.tsx @@ -12,9 +12,11 @@ * which case the documented CLI is the path). */ +import { useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { Archive, + ChevronDown, KeyRound, Loader2, RotateCw, @@ -360,63 +362,139 @@ export function EncryptionSection() { title={t("admin.section.encryption.columnsTitle")} description={t("admin.section.encryption.columnsDescription")} /> - {/* Focusable, named scroll region: on a phone the table scrolls - sideways, and a keyboard user has to be able to reach it - (axe scrollable-region-focusable). */} -
- - - - - - - - - - - {columns.map((c) => { - const active = c.byKeyId[s.activeKeyId] ?? 0; - const stale = c.total - active; - return ( - - - - - - - ); - })} - -
- {t("admin.section.encryption.colColumn")} - - {t("admin.section.encryption.colTotal")} - - {t("admin.section.encryption.colActive")} - - {t("admin.section.encryption.colStale")} -
- {c.model}.{c.field} - {c.total.toLocaleString()}{active.toLocaleString()} - {stale > 0 ? ( - - {stale.toLocaleString()} - - ) : ( - 0 - )} -
-
+
); } +/** + * The per-column table, collapsed by default. A fresh install lists well over + * a hundred encrypted columns, nearly all of them empty, and the table pushed + * every other card off the page. Opened, it shows the columns that hold rows + * (the ones a rotation has to touch), with a toggle for the full list. + */ +export function ColumnCoverage({ + columns, + activeKeyId, + initiallyOpen = false, +}: { + columns: ColumnScan[]; + activeKeyId: string; + /** Start expanded; the page always starts collapsed. */ + initiallyOpen?: boolean; +}) { + const { t } = useTranslations(); + const [open, setOpen] = useState(initiallyOpen); + const [showAll, setShowAll] = useState(false); + const withRows = columns.filter((c) => c.total > 0); + const shown = showAll ? columns : withRows; + + return ( +
+ + {open ? ( +
+ {shown.length === 0 ? ( +

+ {t("admin.section.encryption.columnsNoneWithRows")} +

+ ) : ( + /* Focusable, named scroll region: on a phone the table scrolls + sideways, and a keyboard user has to be able to reach it + (axe scrollable-region-focusable). */ +
+ + + + + + + + + + + {shown.map((c) => { + const active = c.byKeyId[activeKeyId] ?? 0; + const stale = c.total - active; + return ( + + + + + + + ); + })} + +
+ {t("admin.section.encryption.colColumn")} + + {t("admin.section.encryption.colTotal")} + + {t("admin.section.encryption.colActive")} + + {t("admin.section.encryption.colStale")} +
+ {c.model}.{c.field} + + {c.total.toLocaleString()} + {active.toLocaleString()} + {stale > 0 ? ( + + {stale.toLocaleString()} + + ) : ( + 0 + )} +
+
+ )} + {withRows.length < columns.length ? ( + + ) : null} +
+ ) : null} +
+ ); +} + function Stat({ label, value }: { label: string; value: string }) { return ; } diff --git a/src/components/admin/user-management-section.tsx b/src/components/admin/user-management-section.tsx index 8c2f746c0..e6b0ff13a 100644 --- a/src/components/admin/user-management-section.tsx +++ b/src/components/admin/user-management-section.tsx @@ -231,45 +231,50 @@ export function UserManagementSection() {