mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 04:50:59 +00:00
* fix(measurements): close issue #109 + sync body composition across UI/PDF/thresholds User-visible: TOTAL_BODY_WATER, BONE_MASS, and BLOOD_GLUCOSE now appear in the measurements filter, badge, mobile icon, and edit dialog (issue #109). Server-rendered doctor report now includes body composition rows (audit C-9). Body water + bone mass have proper effective-range thresholds so severity logic doesn't silently return `nominal` (audit C-15). Structural: extracted the per-type display maps from measurement-list.tsx into measurement-list-meta.ts as a deliberate first step toward the single-source manifest planned for phase P5. Coverage tests now fail-fast if a future enum addition skips any of: list label/icon/color, doctor-PDF label/unit/vital-types, chart label, threshold metric order/label/bounds. Audit-2026-05-07 / phase P0: - closes #109 (filter UI gap, root cause for the German raw-enum display) - closes audit C-9 (server PDF used a stale type map) - closes audit C-15 (no warn/critical bands for body composition) Tests: 282 → 296 (14 new). Typecheck clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: align OpenAPI spec + migration comment with body-composition reality - OpenAPI MeasurementType enum: add TOTAL_BODY_WATER + BONE_MASS (was at v1.3.0 baseline; spec drifted past the v1.3 server release) - Bump openapi version 1.3.0 → 1.3.2 to match package.json - Migration 0022 comment: TOTAL_BODY_WATER stores kg (water content), not percent of body weight. Validators (VALUE_RANGES) and the Withings client (type 77 → kg) treat it as kg already; the comment was the lone outlier and would mislead the next reviewer. Audit-2026-05-07 / phase P1a — closes audit M-B1 (partial; OpenAPI enum) and M-B2 (migration unit comment). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(security): close 3 audit CRITICALs (Withings header / idempotency Bearer / GlitchTip URL strip) C-3 (Withings webhook secret in URL): Now reads X-Withings-Webhook-Secret header in preference; URL ?secret=… fallback retained for legacy Withings configurations and emits a Wide Event warning so operators can spot still-using-the-old-flow integrators. The query-string secret was leaking into Coolify access logs and any GlitchTip event whose request.url got captured. C-4 (idempotency dead for Bearer-token clients): The default userIdResolver only consulted getSession(). Bearer-authed clients (the iOS / n8n / headless-API fleet that PR #117 was built for) fell through, so every retry created duplicate measurements. The default now tries cookie session first, then Bearer token via hashToken+apiToken.findUnique with revoked/expiry checks. All three existing call sites (measurements, mood-entries, medications/intake) get the fix automatically. H-B7 (request.url with secrets shipped to GlitchTip): reportToGlitchtip now strips the query string before forwarding the URL, so Withings legacy ?secret=… and any OAuth ?code=… that errors during the callback don't end up in someone else's incident UI. Audit-2026-05-07 / phase P2. Tests: 296 → 301 (5 new in idempotency.test for the Bearer fallback + revoked/expired/null cases). Deferred to a follow-up (needs DB migration / lock primitive): C-2 moodLog webhook → AES-GCM at rest + HMAC-over-body C-5 idempotency TOCTOU advisory_lock or in-flight sentinel row Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(measurements): add OXYGEN_SATURATION (SpO2) as first-class measurement type — v1.3.3 Closes the OxygenSaturation feature request from #109. - Migration 0024 extends MeasurementType enum (additive, IF NOT EXISTS) - Zod validators: plausibility 50–100% (below 50 is incompatible with sustained life; above 100 is physically impossible) - effective-range thresholds: green 95–100, orange 92–94, red <92. Sources: BTS Guideline 2017 (target 94–98%), ATS clinical practice (≥95% at rest is healthy), AHA/clinical literature (<92% = call provider, <88% = ER). Lower-only concern — upper orange wing collapses to greenMax since saturation cannot exceed 100%. - Personalisation: existing thresholds-override UI lets COPD / chronic-respiratory users with a doctor-set 88–92 baseline tune the bands. METRIC_ORDER + METRIC_LABEL_KEYS extended in thresholds-section.tsx; the exhaustive `Record<ThresholdMetric>` forces TS to catch any missed surface. - Withings ScanWatch type 54 mapping (best-effort — only fires for pulse-ox-capable devices). - Wired through measurement-form, measurement-list-meta (icon: Wind), health-chart, both doctor-report-pdf renderers (core + browser-side near-duplicate), OpenAPI spec (version 1.3.0 → 1.3.3), and i18n in 4 sections × 2 locales. - iOS DTO already declared `OXYGEN_SATURATION` — server enum addition closes the long-standing drift flagged in audit V2. - 4 new tests: numeric green band, upper-wing collapse, user-override (COPD baseline), METRIC_BOUNDS shape. Tests: 301 → 305. Typecheck: clean. Audit-2026-05-07 / v1.3.3 / closes #109 SpO2 feature request. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(analytics): truthfulness pass on medical citations + clamp override orangeMax Doctor-agent + hallucination-hunter audit V3 found six medical citations in effective-range.ts that the code did not actually implement. Plus a latent bug in the user-override path that produced physically-impossible orangeMax values (>100% saturation, <0% body-fat). Citation rewrites (no numeric changes): - ACTIVITY_STEPS: "WHO ≥8000 steps/day" → Saint-Maurice JAMA 2020. WHO publishes activity in min/week, not steps. Step-count was a misattribution that also leaked into AI prompts. - TOTAL_BODY_WATER: "ESPEN Body Composition Guidance 2017" → Watson formula / ICRP Reference Man. ESPEN 2017 is BIA practice guidance, it does not publish kg cut-offs. - BONE_MASS: "DEXA-equivalent bone mineral content" → "Bioimpedance- estimated; not DEXA-comparable". Withings BIA values typically run 5–7% below DEXA — calling them DEXA-equivalent in a doctor-report PDF was a real legal-exposure misattribution. - OXYGEN_SATURATION: "BTS Guideline 2017 (target 94–98%) + ATS clinical practice (≥95% at rest is healthy)" → "consumer pulse- oximeter consensus (≥95% normal at rest); slightly tighter than BTS 2017 (94–98%); NICE NG115 ≤92% escalation, BMJ panel / FDA pulse-oximeter labelling ≤88% ER threshold". Code uses 95-100/92-100 band — that's an ATS/consumer-consensus number, not BTS's literal 94-98 target. Soften the citation to match the code, not vice versa. Bug fix (1 latent, 1 real-today): - effective-range override path now clamps orangeMin/orangeMax to METRIC_BOUNDS.{min,max}. Without the clamp, a COPD user setting SpO2 override to {95,100} emitted orangeMax = 100.75 (impossible saturation), and a BODY_FAT override of {2,80} emitted orangeMin = -9.7 (negative percent). Default-range case 210 already hard-coded orangeMax: 100 for the same reason — the override path was the only branch missing the invariant. Tests: 305 → 306 (1 new: clamps override orangeMax to physiological 100%). Typecheck: clean. Audit-2026-05-07-V3 / closes Doctor + Senior-Dev medical-citation findings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(security): Bearer-scope wildcard handling + GDPR Art.17 account-delete erasure Two CRITICAL fixes from audit V3. NEW-V3-1: Bearer-scope bypass. apiToken.permissions ["*"] is meant to be the wildcard "session-equivalent" scope (the iOS app receives this on login). The previous check permissions.includes(requiredPermission) treated "*" as a literal string — never matched. Today many sensitive routes call requireAuth() *without* a requiredPermission, so a leaked iOS token can act as a full-scope token (account delete, settings wipe). Once those routes adopt requireAuth("scope:name"), this wildcard handling keeps the iOS app working while narrower-scoped tokens (e.g. user-issued ["medication:ingest"]) get correctly 403'd. Same wildcard handling added to the standalone permission checks in src/app/api/ingest/medication/route.ts:65, 119. NEW-V3-2: GDPR Art. 17 account-delete erasure completeness. Feedback + AuditLog rows had `onDelete: SetNull` in the schema, which preserved PII (free-text symptom descriptions, IP addresses, login-city geolocation) after the user was deleted. We now explicitly prisma.feedback.deleteMany + prisma.auditLog.deleteMany before the cascade-delete, making account deletion genuinely complete erasure. Tests 306/306 still pass; typecheck clean. Audit V3. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(pdf): real text-content assertions for body composition + SpO2 rows Replaces bytes-only "renders body composition rows" theatre test with pdf-parse-driven text extraction that asserts the actual rendered labels + values for TOTAL_BODY_WATER, BONE_MASS, OXYGEN_SATURATION (DE + EN). Closes V3 audit finding STILL-V2-NEW-3: the previous test only verified "%PDF-" header + byte-count, so a regression that drops a row would still pass. Now a missing label fails the test. - adds dev dep: pdf-parse@^2.4.5 - 308/308 tests pass (was 306, +2 SpO2 + EN-locale coverage) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(measurements): close enum drift cousins — derive 5 type-arrays from canonical enum + extend 2 contract enums V3 audit finding "enum drift cousins": 7 hardcoded module-level type arrays silently dropped SpO2/TBW/BoneMass/BloodGlucose from their respective domains. Adding a new measurement type required hunting these down. Now all aggregation paths derive from `measurementTypeEnum.options`: - /api/insights/comprehensive (AI insights) - /api/dashboard/summary (iOS dashboard adapter) - /api/analytics (analytics aggregator) - /lib/insights/general-status (AI general-status fetch) - /api/import (Zod schema — round-trip now covers all 11 types) External-contract enums extended additively (no breakage for old clients): - /api/measurements/series kindEnum: + oxygen, totalBodyWater, boneMass - /api/dashboard/widgets widgetIdEnum: + oxygenSaturation - DashboardWidgetId + DEFAULT_DASHBOARD_LAYOUT: + oxygenSaturation row - formatMeasurementsForExport: glucoseContext now round-trips Coverage test (`measurement-type-enum-coverage.test.ts`) asserts the canonical enum stays the source of truth and documents PDF exclusions (BLOOD_GLUCOSE renders via context section, SLEEP/STEPS lifestyle-only). 313/313 tests pass (was 308, +5 new). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(security): close 6 V3-audit HIGH findings (CSP, web-push SSRF, IP geo, AI provider leak, import rate-limit) - CSP: chatgpt.com + api.openai.com only on /settings/ai/** routes (was blanket connect-src on every page including /auth/login → DOM-XSS exfil channel) - web-push: webPushSubscriptionSchema.endpoint requires HTTPS + isPublicUrl refinement; was z.url() only → blind SSRF probe via Push subscription - isPublicUrl: also fixes a regression where DNS labels starting with "fc"/"fd" (fcm.googleapis.com etc.) were falsely classified as IPv6 unique-local. Check is now gated on a colon being present in the host. - /api/ai/test: stop returning provider err.message + bodyExcerpt to the client (leaked provider URLs / partial keys / internal headers). Now returns categorised generic message; full details land in the wide-event via annotate() for the operator. - /lib/geo (auditLog IP geo lookup): switch default provider to ipwho.is over HTTPS, accept ipwho.is + ip-api pro response shapes, add IP_GEO_LOOKUP_DISABLED env, refuse non-HTTPS configured URLs. Previously leaked IP+timestamp over plaintext HTTP every auth event (GDPR Art. 32 + Art. 44). - /api/import: add 5/hour rate-limit (was unlimited; bulk-injection vector) Tests: 325/325 pass (was 313, +12 new — webPushSubscriptionSchema SSRF guards, fc/fd-prefix domain regression, geo HTTPS-only, geo provider shapes, geo opt-out env). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(security,gdpr): trusted-proxy IP, audit-log retention, idempotency cachable filter, Bearer mock tightening V3 audit follow-on bundle covering 4 HIGH findings. (I) getClientIp — TRUST_PROXY_HOPS env (default 1) replaces blind XFF trust. Reads chain right-to-left so client-supplied leftmost entries cannot rotate the per-IP rate-limit bucket. Default 1 matches typical single-proxy self-host (Coolify/Caddy/Cloudflare-Tunnel); 0 forces x-real-ip / null. New 7-test suite covers the rotation attack + malformed entries + zero-hop fallback. (F) Audit-log retention job — daily 03:15 cleanup of audit_logs older than AUDIT_LOG_RETENTION_DAYS (default 365). Closes GDPR Art. 5(1)(e) "storage limitation" gap; previously IPs + city + login events accumulated forever. 6-test suite covers default + override + misconfig guard (rejects <7 days). (G) isCachableStatus — extracted the inline 4xx/5xx filter into a named, exported, tested function. 7 cases cover the full do-not-cache contract (401 / 403 / 408 / 429 / any 5xx) and confirm 2xx + 4xx validation are still cached. (H) Bearer-mock tightening — apiToken.findUnique mocks in require-auth-bearer.test + idempotency.test now assert the where.tokenHash shape uses the hashed value (not the raw bearer). A regression that switched back to raw-token comparison would have been silent without this assertion. Tests: 345/345 pass (was 332, +13 new). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(security): encrypt moodLog webhook secret at rest with AES-256-GCM (V3 audit STILL-V2-C-2) The mood_log_webhook_secret column previously stored a plaintext secret that the webhook handler compared timing-safe against every enabled user. A DB dump or backup snapshot leaked active webhook credentials. New contract — no schema migration required: - Writes go through encryptMoodLogSecret() (AES-256-GCM via existing crypto helper, ENCRYPTION_KEY). - Reads go through readMoodLogSecret() which transparently decrypts envelopes and tolerates legacy plaintext rows during the transition (graceful upgrade path — no breaking change for self-hosted users). - Webhook lookup decrypts each candidate before timing-safe compare. - Status endpoint decrypts for the user's settings page. One-shot startup migration in reminder-worker rotates any leftover plaintext rows on next worker boot. Idempotent; encrypted rows are skipped. Tests: 350/350 pass (was 345, +5 new — round-trip, legacy passthrough, isLegacyPlaintext semantics, rotateLegacy idempotence). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: sync README + AGENTS + CLAUDE + CHANGELOG + OpenAPI for v1.3.3 (V3 audit doc-drift) V3 audit Doku Specialist findings — drift between code and meta-docs. - README §Key Features now mentions pulse oximetry (SpO₂) + Withings ScanWatch sync; Tech Stack model count corrected 23 → 25. - AGENTS.md status promoted v1.3.2 → v1.3.3; vitest config corrected `vitest.config.ts` → `vitest.config.mts`; migration range corrected `0001–0022` → `0001–0024`; model count + list updated to 25 (adds Device, IdempotencyKey). - CLAUDE.md model count updated (2 occurrences). - CHANGELOG.md v1.3.3 expanded with Security + Internal sections that were missing the audit-fix-marathon work (Bearer-scope, account-delete cascade, moodLog encryption, CSP tightening, Web-Push SSRF, IP-geo HTTPS, AI-test leak fix, import rate-limit, trusted-proxy XFF, audit-log retention, idempotency cachable filter, Bearer mock tightening, enum-drift cousins, PDF text-content tests). - OpenAPI Bearer description corrected `SHA-256 hashes` → `HMAC-SHA-256 hashes (keyed with API_TOKEN_HMAC_KEY)` so the docs match the actual server-side implementation. Wildcard scope contract documented. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(audit-review): close 4 HIGH + 2 MEDIUM findings from multi-agent review pass Multi-agent review of the audit-fix branch surfaced four HIGH issues and a couple of MEDIUM hardenings. All addressed before merge. HIGH-1 — OpenAPI MetricKind enum drift (iOS strict-decode crash risk). The dashboard-summary route now emits totalBodyWater/boneMass/ oxygenSaturation MetricCards, but the spec still listed only the original 7 kinds. Generated iOS Codable would have crashed the dash the moment a user logged SpO2. MetricKind extended with the 3 new kinds + comment documenting unknown-value defensive decoding. HIGH-2 — kindEnum naming consistency on /api/measurements/series. The series adapter shipped "oxygen" while the dashboard summary used "oxygenSaturation"; both routes now use "oxygenSaturation" so iOS decoders can share a single MetricKind type across endpoints. HIGH-3 — /api/ai/test no-leak guard had no test. Added 4 tests that mock provider errors with sk-leaked-key + provider URLs in the err fields and assert the response body never echoes them, plus the 3-status-categorised generic messages (401/429/5xx/other). HIGH-4 — /api/import 5/hour rate-limit had no test. Added 2 tests that mock checkRateLimit allowed/disallowed and assert the 429 path uses the right key shape (`import:<userId>`, 5, 1h) and that the within- quota path proceeds. MEDIUM-3 — getClientIp under-length XFF chain regression. With TRUST_PROXY_HOPS=2 and a chain of length 1, the previous clamp returned the leftmost (attacker-controlled) entry, silently re-introducing the rotation attack the helper was meant to close. Now: when chain is shorter than configured hops, refuse XFF entirely (fall back to x-real-ip / null). Added regression test. LOW-1 — TRUST_PROXY_HOPS=garbage now throws at boot instead of silently degrading to hops=0. Added regression test. Tests: 358/358 pass (was 350, +8 new). Typecheck clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1634 lines
69 KiB
JSON
1634 lines
69 KiB
JSON
{
|
||
"common": {
|
||
"save": "Save",
|
||
"cancel": "Cancel",
|
||
"delete": "Delete",
|
||
"edit": "Edit",
|
||
"moreOptions": "More options",
|
||
"create": "Create",
|
||
"close": "Close",
|
||
"loading": "Loading...",
|
||
"error": "Error",
|
||
"success": "Success",
|
||
"saved": "Saved",
|
||
"confirm": "Confirm",
|
||
"back": "Back",
|
||
"next": "Next",
|
||
"yes": "Yes",
|
||
"no": "No",
|
||
"search": "Search",
|
||
"filter": "Filter",
|
||
"noData": "No data available",
|
||
"optional": "Optional",
|
||
"required": "Required",
|
||
"all": "All",
|
||
"active": "Active",
|
||
"inactive": "Inactive",
|
||
"enabled": "Enabled",
|
||
"disabled": "Disabled",
|
||
"networkError": "Network error",
|
||
"unknownError": "Unknown error",
|
||
"notLoggedIn": "Not logged in",
|
||
"noAccess": "Access denied",
|
||
"copied": "Copied!",
|
||
"or": "or",
|
||
"unknown": "Unknown",
|
||
"replace": "Replace",
|
||
"add": "Add",
|
||
"import": "Import",
|
||
"test": "Test",
|
||
"reset": "Reset",
|
||
"copy": "Copy",
|
||
"copyDetails": "Copy details",
|
||
"retry": "Retry",
|
||
"reportIssue": "Report issue"
|
||
},
|
||
"doctorReport": {
|
||
"title": "Health Report",
|
||
"subtitle": "HealthLog — Personal Health Report",
|
||
"patient": "Patient",
|
||
"dateOfBirth": "Date of birth",
|
||
"gender": "Gender",
|
||
"genderMale": "Male",
|
||
"genderFemale": "Female",
|
||
"genderOther": "Non-binary",
|
||
"height": "Height",
|
||
"period": "Reporting period",
|
||
"createdOn": "Created on",
|
||
"vitalsTitle": "Vital signs — Summary",
|
||
"colParameter": "Parameter",
|
||
"colCurrent": "Current",
|
||
"colAvgPeriod": "Avg. period",
|
||
"colMin": "Min",
|
||
"colMax": "Max",
|
||
"colN": "n",
|
||
"bpClassificationTitle": "Blood pressure — ESC/ESH classification (2018)",
|
||
"avgBp": "Average blood pressure",
|
||
"classification": "Classification",
|
||
"bmiTitle": "Body Mass Index (BMI)",
|
||
"bmiRow": "BMI: {bmi} kg/m² — Classification: {class} (WHO)",
|
||
"complianceTitle": "Medication compliance",
|
||
"colMedication": "Medication",
|
||
"colTaken": "Taken",
|
||
"colSkipped": "Skipped",
|
||
"colMissed": "Missed",
|
||
"colTotal": "Total",
|
||
"colComplianceRate": "Compliance rate",
|
||
"moodTitle": "Mood trajectory",
|
||
"moodSummary": "Average: {avg} / 5.0 · Entries: {count} · Range: {min}–{max}",
|
||
"colMood": "Mood",
|
||
"colCount": "Count",
|
||
"colShare": "Share",
|
||
"footerDisclaimer1": "This report was generated automatically from self-tracked data and is intended to support",
|
||
"footerDisclaimer2": "the doctor–patient conversation. It does not replace a medical diagnosis. Correlations do not imply causation.",
|
||
"footerSource": "Source: HealthLog | Timezone: Europe/Berlin | Created: {timestamp}",
|
||
"typeWeight": "Body weight",
|
||
"typeBpSys": "Systolic blood pressure",
|
||
"typeBpDia": "Diastolic blood pressure",
|
||
"typePulse": "Resting pulse",
|
||
"typeBodyFat": "Body fat",
|
||
"typeSleep": "Sleep duration",
|
||
"typeSteps": "Activity (steps)",
|
||
"typeTotalBodyWater": "Total body water",
|
||
"typeBoneMass": "Bone mass",
|
||
"typeOxygenSaturation": "Oxygen saturation",
|
||
"unitSteps": "steps",
|
||
"bmiUnderweight": "Underweight",
|
||
"bmiNormal": "Normal weight",
|
||
"bmiOverweight": "Overweight (pre-obese)",
|
||
"bmiObeseGrade1": "Obesity grade I",
|
||
"bmiObeseGrade2": "Obesity grade II",
|
||
"bmiObeseGrade3": "Obesity grade III",
|
||
"bpOptimal": "Optimal",
|
||
"bpNormal": "Normal",
|
||
"bpHighNormal": "High-normal",
|
||
"bpHypertensionGrade1": "Hypertension grade 1",
|
||
"bpHypertensionGrade2": "Hypertension grade 2",
|
||
"bpHypertensionGrade3": "Hypertension grade 3",
|
||
"moodAwful": "Very bad",
|
||
"moodBad": "Bad",
|
||
"moodNeutral": "Neutral",
|
||
"moodGood": "Good",
|
||
"moodGreat": "Very good",
|
||
"typeGlucoseFasting": "Glucose (fasting)",
|
||
"typeGlucosePostprandial": "Glucose (postprandial)",
|
||
"typeGlucoseRandom": "Glucose (random)",
|
||
"typeGlucoseBedtime": "Glucose (bedtime)",
|
||
"glucoseClassificationTitle": "Blood glucose — reference ranges (ADA 2024 / DDG)",
|
||
"glucoseRow": "{label}: avg {avg} {unit} (target {rangeMin}–{rangeMax}) — {class}",
|
||
"glucoseInTarget": "in target",
|
||
"glucoseBelowTarget": "below target",
|
||
"glucoseAboveTarget": "above target"
|
||
},
|
||
"errorBoundary": {
|
||
"title": "Something went wrong",
|
||
"description": "An unexpected error occurred. You can retry, copy the technical details, or open a bug report."
|
||
},
|
||
"nav": {
|
||
"home": "Home",
|
||
"dashboard": "Dashboard",
|
||
"measurements": "Measurements",
|
||
"medications": "Medications",
|
||
"charts": "History",
|
||
"insights": "Insights",
|
||
"targets": "Targets",
|
||
"achievements": "Achievements",
|
||
"settings": "Settings",
|
||
"bugreport": "Bug Report",
|
||
"admin": "Admin Console",
|
||
"logout": "Sign out",
|
||
"login": "Sign in",
|
||
"notifications": "Notifications",
|
||
"theme": "Appearance",
|
||
"themeSystem": "System",
|
||
"themeDark": "Dark",
|
||
"themeLight": "Light",
|
||
"collapseSidebar": "Collapse",
|
||
"userMenu": "User menu",
|
||
"mood": "Mood",
|
||
"skipToContent": "Skip to content",
|
||
"sidebar": "Sidebar",
|
||
"mainNavigation": "Main navigation",
|
||
"mobileNavigation": "Mobile navigation",
|
||
"loadingScreen": "Loading…"
|
||
},
|
||
"auth": {
|
||
"login": "Sign in",
|
||
"register": "Sign up",
|
||
"loginTitle": "Sign In",
|
||
"registerTitle": "Sign Up",
|
||
"username": "Username",
|
||
"password": "Password",
|
||
"email": "Email address",
|
||
"emailPlaceholder": "name@example.com",
|
||
"emailOrUsername": "Email address or username",
|
||
"emailOrUsernamePlaceholder": "name@example.com or username",
|
||
"loginWithPasskey": "Sign in with Passkey",
|
||
"loginWithPassword": "Sign in with password",
|
||
"noAccount": "Don't have an account?",
|
||
"hasAccount": "Already have an account?",
|
||
"registerDisabled": "Registration is disabled.",
|
||
"loginFailed": "Login failed",
|
||
"passkeyFailed": "Passkey authentication cancelled or failed",
|
||
"registerFailed": "Registration failed",
|
||
"createAccount": "Create account",
|
||
"newAccount": "Create new account",
|
||
"backToPasskey": "Back to Passkey",
|
||
"passkeySetupLater": "You can set up passkeys at any time in Settings.",
|
||
"passwordMinLength": "Min. 12 characters",
|
||
"passwordTooShort": "Password must be at least {minLength} characters long.",
|
||
"passwordStrength": "Password strength",
|
||
"strengthWeak": "Very weak",
|
||
"strengthFair": "Weak",
|
||
"strengthGood": "Fair",
|
||
"strengthStrong": "Strong",
|
||
"strengthVeryStrong": "Very strong"
|
||
},
|
||
"dashboard": {
|
||
"title": "Dashboard",
|
||
"subtitle": "Your health overview",
|
||
"greeting": {
|
||
"morning": "Good morning",
|
||
"day": "Hello",
|
||
"evening": "Good evening"
|
||
},
|
||
"welcomeBack": "{greeting}, welcome back.",
|
||
"welcomeBackWithName": "{greeting} {name}, welcome back.",
|
||
"weight": "Weight",
|
||
"bloodPressure": "Blood Pressure",
|
||
"bloodPressureSys": "Blood Pressure (Sys)",
|
||
"bloodPressureDia": "Blood Pressure (Dia)",
|
||
"pulse": "Pulse",
|
||
"bodyFat": "Body Fat",
|
||
"sleep": "Sleep",
|
||
"steps": "Steps",
|
||
"bpInTarget": "BP in Target",
|
||
"days30Short": "30d",
|
||
"medications": "Medications",
|
||
"lastMeasurement": "Last measurement",
|
||
"noMeasurements": "No measurements yet",
|
||
"trend": "Trend",
|
||
"trendUp": "rising",
|
||
"trendDown": "falling",
|
||
"trendStable": "stable",
|
||
"quickEntry": "Quick entry",
|
||
"addMeasurement": "Add measurement",
|
||
"compliance7d": "Compliance (7 days)",
|
||
"complianceGood": "Good",
|
||
"complianceLow": "Low",
|
||
"mood": "Mood",
|
||
"customizeTitle": "Dashboard layout",
|
||
"customizeSubtitle": "Choose which cards appear on your dashboard and in what order. Defaults work out of the box.",
|
||
"layoutReset": "Reset to defaults",
|
||
"layoutSaveSuccess": "Dashboard layout saved",
|
||
"layoutSaveError": "Could not save dashboard layout",
|
||
"layoutResetSuccess": "Reset to default layout",
|
||
"layoutUsingDefaults": "Using default layout",
|
||
"layoutCustomized": "Custom layout active",
|
||
"moveUp": "Move up",
|
||
"moveDown": "Move down"
|
||
},
|
||
"measurements": {
|
||
"title": "Measurements",
|
||
"subtitle": "Weight, blood pressure, pulse & more",
|
||
"addMeasurement": "Add",
|
||
"editMeasurement": "Edit measurement",
|
||
"type": "Type",
|
||
"value": "Value",
|
||
"unit": "Unit",
|
||
"date": "Date",
|
||
"source": "Source",
|
||
"notes": "Notes",
|
||
"typeWeight": "Weight",
|
||
"typeBloodPressure": "Blood Pressure",
|
||
"typeBpSys": "Sys",
|
||
"typeBpDia": "Dia",
|
||
"typePulse": "Pulse",
|
||
"typeBodyFat": "Body Fat",
|
||
"typeSleep": "Sleep",
|
||
"typeSteps": "Steps",
|
||
"typeBloodGlucose": "Blood glucose",
|
||
"typeTotalBodyWater": "Total body water",
|
||
"typeBoneMass": "Bone mass",
|
||
"typeOxygenSaturation": "Oxygen saturation",
|
||
"glucoseContext": "Context",
|
||
"glucoseContextFasting": "Fasting (≥8h without food)",
|
||
"glucoseContextPostprandial": "Post-meal (2h after eating)",
|
||
"glucoseContextRandom": "Random / casual",
|
||
"glucoseContextBedtime": "Before bed",
|
||
"glucoseUnit": "Glucose display unit",
|
||
"glucoseUnitHint": "Stored canonically in mg/dL; display unit is your preference.",
|
||
"unitHours": "hours",
|
||
"unitSteps": "steps",
|
||
"sourceManual": "Manual",
|
||
"sourceWithings": "Withings",
|
||
"sourceImport": "Import",
|
||
"systolicLabel": "Systolic (mmHg)",
|
||
"diastolicLabel": "Diastolic (mmHg)",
|
||
"pulseLabel": "Pulse (bpm)",
|
||
"valueWithUnit": "Value ({unit})",
|
||
"timestamp": "Time",
|
||
"notesPlaceholder": "e.g. after a meal",
|
||
"formReset": "Reset form",
|
||
"saveError": "Error saving",
|
||
"deleteError": "Error deleting",
|
||
"invalidValue": "Please enter a valid value.",
|
||
"invalidTimestamp": "Please select a valid time.",
|
||
"loginRequired": "Please sign in to view measurements.",
|
||
"allTypes": "All types",
|
||
"measurementCount": "{count} measurements",
|
||
"noMeasurements": "No measurements found",
|
||
"comment": "Comment",
|
||
"actions": "Actions",
|
||
"deleteConfirmTitle": "Delete measurement?",
|
||
"deleteConfirmDescription": "This measurement will be permanently deleted.",
|
||
"pageInfo": "Page {page} of {total}",
|
||
"showing": "Showing",
|
||
"of": "of",
|
||
"entries": "entries"
|
||
},
|
||
"mood": {
|
||
"title": "Mood",
|
||
"subtitle": "Mood tracker & entries",
|
||
"addEntry": "Add",
|
||
"editEntry": "Edit entry",
|
||
"moodLevel": "Mood",
|
||
"tags": "Tags",
|
||
"tagsPlaceholder": "e.g. Exercise, Work, Family",
|
||
"tagsHelp": "Comma-separated",
|
||
"timestamp": "Time",
|
||
"date": "Date",
|
||
"source": "Source",
|
||
"levelSuperGut": "Amazing",
|
||
"levelGut": "Good",
|
||
"levelOkay": "Okay",
|
||
"levelSchlecht": "Bad",
|
||
"levelLausig": "Terrible",
|
||
"sourceManual": "Manual",
|
||
"sourceMoodlog": "moodLog",
|
||
"sourceWeb": "Web",
|
||
"sourceTelegram": "Telegram",
|
||
"sourceDaylio": "Daylio",
|
||
"allMoods": "All moods",
|
||
"entryCount": "{count} entries",
|
||
"noEntries": "No mood entries found",
|
||
"actions": "Actions",
|
||
"formReset": "Reset form",
|
||
"saveError": "Error saving",
|
||
"deleteError": "Error deleting",
|
||
"invalidTimestamp": "Please select a valid time.",
|
||
"deleteConfirmTitle": "Delete entry?",
|
||
"deleteConfirmDescription": "This mood entry will be permanently deleted.",
|
||
"pageInfo": "Page {page} of {total}",
|
||
"loginRequired": "Please sign in to view mood entries."
|
||
},
|
||
"medications": {
|
||
"title": "Medications",
|
||
"subtitle": "Medications, schedules & intake",
|
||
"loginRequired": "Please sign in to manage medications.",
|
||
"addMedication": "Add",
|
||
"editMedication": "Edit medication",
|
||
"newMedication": "New medication",
|
||
"firstMedication": "Add first medication",
|
||
"noMedicationsYet": "No medications added yet",
|
||
"loadFailed": "Could not load medications",
|
||
"retryLoad": "Retry",
|
||
"name": "Name",
|
||
"dose": "Dose",
|
||
"doseAmount": "Amount",
|
||
"doseUnit": "Unit",
|
||
"category": "Category",
|
||
"schedule": "Schedule",
|
||
"scheduleFrom": "From",
|
||
"scheduleTo": "To",
|
||
"scheduleLabel": "Label",
|
||
"scheduleDays": "Days",
|
||
"scheduleDaily": "Daily",
|
||
"active": "Active",
|
||
"inactive": "Paused",
|
||
"intakeHistory": "Intake history",
|
||
"showIntakeHistory": "Show intake history",
|
||
"hideIntakeHistory": "Hide intake history",
|
||
"markTaken": "Taken",
|
||
"markSkipped": "Skipped",
|
||
"deleteConfirm": "Delete medication?",
|
||
"deleteDescription": "The medication and all intake records will be permanently deleted.",
|
||
"noMedications": "No medications found.",
|
||
"compliance": "Compliance",
|
||
"import": "Import",
|
||
"importIntakes": "Import intakes",
|
||
"importDescription": "Paste a JSON array with intake data. Expected format:",
|
||
"importUploadFile": "Upload JSON file",
|
||
"importPaste": "Paste JSON here...",
|
||
"importSelected": "Selected: {name}",
|
||
"importFileLoaded": "File \"{name}\" loaded",
|
||
"importInvalidJson": "File does not contain valid JSON",
|
||
"importNoArray": "No array found",
|
||
"importResult": "{imported} intakes imported",
|
||
"importDuplicatesSkipped": "{count} duplicates skipped",
|
||
"importInvalidSkipped": "{count} invalid entries skipped",
|
||
"importFailed": "Import failed",
|
||
"importInvalidFormat": "Invalid JSON format",
|
||
"apiAccess": "API access",
|
||
"apiEndpointTitle": "API Endpoint: {name}",
|
||
"apiEndpointDescription": "Use this endpoint to record intakes via API (e.g., iPhone Shortcut or cron job).",
|
||
"apiEndpointActive": "API endpoint active",
|
||
"apiEndpointActivated": "API endpoint activated",
|
||
"apiEndpointDeactivated": "API endpoint deactivated",
|
||
"apiTokenCount": "Active ({count} tokens)",
|
||
"apiToken": "API Token",
|
||
"apiTokenActiveHint": "Endpoint is active. Existing tokens remain valid but cannot be shown in plain text again.",
|
||
"apiTokenActivateHint": "Activate the endpoint to create a new token.",
|
||
"apiTokenOnceHint": "The token is only shown in plain text once at creation.",
|
||
"requestExample": "Request example",
|
||
"statusLoadFailed": "Could not load status",
|
||
"changeFailed": "Change failed",
|
||
"categoryBloodPressure": "Blood Pressure",
|
||
"categoryCholesterol": "Cholesterol",
|
||
"categoryDiabetes": "Diabetes",
|
||
"categoryVitamin": "Vitamins",
|
||
"categoryThyroid": "Thyroid",
|
||
"categoryHormone": "Hormones",
|
||
"categorySupplement": "Supplement",
|
||
"categoryPainRelief": "Pain Relief",
|
||
"categoryAllergy": "Allergy",
|
||
"categoryDigestive": "Gastrointestinal",
|
||
"categorySkin": "Skincare",
|
||
"categorySleepAid": "Sleep Aid",
|
||
"categoryOther": "Other",
|
||
"daysSun": "Sun",
|
||
"daysMon": "Mon",
|
||
"daysTue": "Tue",
|
||
"daysWed": "Wed",
|
||
"daysThu": "Thu",
|
||
"daysFri": "Fri",
|
||
"daysSat": "Sat",
|
||
"intakeHistoryTitle": "Intake History",
|
||
"newIntake": "Add",
|
||
"editIntake": "Edit Intake",
|
||
"intakeScheduledFor": "Scheduled",
|
||
"intakeTakenAt": "Taken at",
|
||
"intakeStatus": "Status",
|
||
"intakeSource": "Source",
|
||
"intakeStatusTaken": "Taken",
|
||
"intakeStatusSkipped": "Skipped",
|
||
"intakeStatusMissed": "Missed",
|
||
"intakeDeleteConfirm": "Delete intake record?",
|
||
"intakeDeleteDescription": "This intake record will be permanently deleted.",
|
||
"intakeCount": "{count} intakes",
|
||
"viewHistory": "History",
|
||
"today": "Today",
|
||
"yesterday": "Yesterday",
|
||
"tomorrow": "Tomorrow",
|
||
"withoutNotification": "Without notification",
|
||
"pausedSince": "Paused since",
|
||
"takeNow": "Take now",
|
||
"overdue": "Overdue",
|
||
"veryOverdue": "Very overdue",
|
||
"compliance7d": "7-day compliance",
|
||
"compliance30d": "30-day compliance",
|
||
"lastIntake": "Last intake:",
|
||
"nextIntake": "Next intake:",
|
||
"dayStreak": "day streak",
|
||
"taken": "Taken",
|
||
"skipped": "Skipped",
|
||
"weekdaySunday": "Sunday",
|
||
"weekdayMonday": "Monday",
|
||
"weekdayTuesday": "Tuesday",
|
||
"weekdayWednesday": "Wednesday",
|
||
"weekdayThursday": "Thursday",
|
||
"weekdayFriday": "Friday",
|
||
"weekdaySaturday": "Saturday",
|
||
"weekdaySundayPlural": "Sundays",
|
||
"weekdayMondayPlural": "Mondays",
|
||
"weekdayTuesdayPlural": "Tuesdays",
|
||
"weekdayWednesdayPlural": "Wednesdays",
|
||
"weekdayThursdayPlural": "Thursdays",
|
||
"weekdayFridayPlural": "Fridays",
|
||
"weekdaySaturdayPlural": "Saturdays",
|
||
"formType": "Type",
|
||
"formDose": "Dose",
|
||
"formUnit": "Unit",
|
||
"formSchedule": "Schedule",
|
||
"newSchedule": "New schedule",
|
||
"removeSchedule": "Remove schedule",
|
||
"advancedEditing": "Advanced editing",
|
||
"advancedEditingHide": "Hide advanced editing",
|
||
"scheduleInterval": "Interval",
|
||
"scheduleEveryWeek": "every week",
|
||
"scheduleEveryNWeeks": "every {n} weeks",
|
||
"scheduleIntervalPrefix": "Interval:",
|
||
"nextSchedulePrefix": "Next schedule:",
|
||
"nextScheduleDaily": "daily",
|
||
"importIntakesAction": "Import intakes",
|
||
"apiEndpointAction": "API endpoint",
|
||
"pause": "Pause",
|
||
"activate": "Activate",
|
||
"disableNotifications": "Disable notifications",
|
||
"enableNotifications": "Enable notifications",
|
||
"purgeRecords": "Delete all records",
|
||
"formReset": "Reset form",
|
||
"createMedication": "Create medication",
|
||
"deleteConfirmDescription": "The medication and all intake history will be permanently deleted.",
|
||
"purgeConfirmTitle": "Delete all records?",
|
||
"purgeConfirmDescription": "All intake records for this medication will be permanently deleted. The medication itself will remain and start without history data.",
|
||
"saveError": "Error saving",
|
||
"deleteError": "Error deleting",
|
||
"purgeError": "Error deleting records",
|
||
"invalidTimestamp": "Please select a valid time.",
|
||
"noIntakesYet": "No intakes recorded yet",
|
||
"noIntakesForMedication": "No intakes yet for",
|
||
"deleteIntakeAriaLabel": "Delete intake",
|
||
"actions": "Actions",
|
||
"pageInfo": "Page {page} of {total}",
|
||
"loginRequiredIntakes": "Please sign in to view intakes.",
|
||
"clear": "Clear",
|
||
"phaseConfig": "Reminder phases",
|
||
"phaseConfigDescription": "Configure timing for reminders.",
|
||
"phaseGreen": "Green",
|
||
"phaseYellow": "Yellow",
|
||
"phaseOrange": "Orange",
|
||
"phaseRed": "Red",
|
||
"phaseMinutes": "Min",
|
||
"phasePercent": "%",
|
||
"phaseBeforeEnd": "before end",
|
||
"phaseAfterEnd": "after end",
|
||
"phaseResetDefaults": "Reset to defaults",
|
||
"phaseSaved": "Reminder phases saved",
|
||
"phaseReset": "Reset to defaults",
|
||
"sourceWeb": "Website",
|
||
"sourceApi": "External API",
|
||
"sourceReminder": "Telegram / Reminder",
|
||
"sourceImport": "CSV Import",
|
||
"unitDrops": "Drops",
|
||
"unitTablets": "Tablet(s)",
|
||
"unitCapsules": "Capsule(s)",
|
||
"unitPieces": "Piece(s)",
|
||
"namePlaceholder": "e.g. Ibuprofen",
|
||
"dosePlaceholder": "e.g. 400",
|
||
"labelPlaceholder": "e.g. Morning",
|
||
"defaultDose": "Default",
|
||
"dayDeactivate": "Deactivate {day}",
|
||
"dayActivate": "Activate {day}"
|
||
},
|
||
"charts": {
|
||
"title": "History",
|
||
"subtitle": "Visualization of your health data",
|
||
"timeRange": "Time range",
|
||
"days7": "7 days",
|
||
"days30": "30 days",
|
||
"days90": "90 days",
|
||
"days365": "1 year",
|
||
"daysAll": "All",
|
||
"weight": "Weight",
|
||
"bloodPressure": "Blood Pressure",
|
||
"pulse": "Pulse",
|
||
"bodyFat": "Body Fat",
|
||
"sleep": "Sleep",
|
||
"steps": "Steps",
|
||
"compliance": "Compliance",
|
||
"noData": "No data in selected time range",
|
||
"movingAverage": "Moving average",
|
||
"target": "Target range",
|
||
"points7Label": "7 pts",
|
||
"points7Title": "Last 7 data points",
|
||
"points30Label": "30 pts",
|
||
"points30Title": "Last 30 data points",
|
||
"points90Label": "90 pts",
|
||
"points90Title": "Last 90 data points",
|
||
"pointsAllLabel": "All",
|
||
"pointsAllTitle": "All available data points",
|
||
"systolic": "Systolic",
|
||
"diastolic": "Diastolic",
|
||
"bodyWater": "Body water",
|
||
"boneMass": "Bone mass",
|
||
"spo2": "SpO₂",
|
||
"movingAverage7d": "7d avg",
|
||
"avg7dShort": "7d",
|
||
"avg30dShort": "30d",
|
||
"trend": "Trend",
|
||
"targetRanges": "Target ranges",
|
||
"perWeek": "/week",
|
||
"selectMedication": "Select medication",
|
||
"noComplianceData": "No compliance data available",
|
||
"history": "History",
|
||
"complianceDays90": "Compliance (90 days)",
|
||
"colorGreen": "Green:",
|
||
"colorOrange": "Orange:",
|
||
"colorRed": "Red:",
|
||
"mood": "Mood",
|
||
"moodScore": "Mood Score",
|
||
"moodMA": "7-Day Average",
|
||
"moodLabel1": "Awful",
|
||
"moodLabel2": "Bad",
|
||
"moodLabel3": "Okay",
|
||
"moodLabel4": "Good",
|
||
"moodLabel5": "Great",
|
||
"weekdays": {
|
||
"mon": "Mon",
|
||
"wed": "Wed",
|
||
"fri": "Fri",
|
||
"sun": "Sun"
|
||
},
|
||
"months": {
|
||
"jan": "Jan",
|
||
"feb": "Feb",
|
||
"mar": "Mar",
|
||
"apr": "Apr",
|
||
"may": "May",
|
||
"jun": "Jun",
|
||
"jul": "Jul",
|
||
"aug": "Aug",
|
||
"sep": "Sep",
|
||
"oct": "Oct",
|
||
"nov": "Nov",
|
||
"dec": "Dec"
|
||
},
|
||
"heatmapOnTime": "on time",
|
||
"heatmapLate": "late",
|
||
"heatmapVeryLate": "very late",
|
||
"legendOnTime": "On time",
|
||
"legendLate": "Late",
|
||
"legendVeryLate": "Very late",
|
||
"legendMissed": "Missed"
|
||
},
|
||
"insights": {
|
||
"aiAnalysisTitle": "AI Health Analysis",
|
||
"noAnalysisYet": "No analysis yet.",
|
||
"noProviderConfigured": "AI provider not configured.",
|
||
"startAnalysis": "Start analysis",
|
||
"refreshAnalysis": "Refresh analysis",
|
||
"keyTakeaway": "Key Takeaway",
|
||
"findingsTitle": "Findings",
|
||
"correlationsTitle": "Correlations",
|
||
"recommendationsTitle": "Recommendations",
|
||
"dataFoundation": "Data coverage",
|
||
"dataGaps": "Data gaps:",
|
||
"lastUpdated": "Last updated",
|
||
"confidenceHoch": "High",
|
||
"confidenceMittel": "Medium",
|
||
"confidenceGering": "Low",
|
||
"loadError": "Failed to load insights",
|
||
"generationFailed": "The analysis couldn't be generated. Please try again.",
|
||
"title": "Insights",
|
||
"subtitle": "AI-powered analysis of your health data",
|
||
"overviewSubtitle": "Trends, risks, progress at a glance",
|
||
"navGeneral": "General",
|
||
"navBloodPressure": "Blood Pressure",
|
||
"navWeight": "Weight",
|
||
"navPulse": "Pulse",
|
||
"navMood": "Mood",
|
||
"navMedication": "Medication",
|
||
"navBmi": "BMI",
|
||
"measurementsOverDays": "{count} measurements over {days} days",
|
||
"healthStatus": "Health Status",
|
||
"noWeightData": "No weight data or height not set.",
|
||
"notEnoughBpData": "Not enough blood pressure data.",
|
||
"bpTargetRange": "BP Target Range",
|
||
"inTargetRange": "in target range",
|
||
"bpTarget": "Target: {range}",
|
||
"noDobSet": "Date of birth not set.",
|
||
"avg30dEsc": "30d average · ESC/ESH 2018",
|
||
"whoClassification": "WHO Classification",
|
||
"correlations": "Correlations",
|
||
"weightVsBp": "Weight vs. Blood Pressure (Systolic)",
|
||
"strengthStrong": "Strong correlation",
|
||
"strengthModerate": "Moderate correlation",
|
||
"strengthWeak": "Weak correlation",
|
||
"strengthNone": "No correlation",
|
||
"weightBpStrong": "There is a strong correlation between weight and blood pressure.",
|
||
"weightBpModerate": "There is a moderate correlation between weight and blood pressure.",
|
||
"weightBpWeak": "There is a weak correlation between weight and blood pressure.",
|
||
"weightBpNone": "No discernible correlation between weight and blood pressure.",
|
||
"notEnoughCorrelationData": "Not enough data for correlation analysis.",
|
||
"minCorrelationData": "At least 5 time-matched weight and blood pressure measurements needed.",
|
||
"bpMedContinuity": "BP Medication Continuity vs. Blood Pressure (Systolic)",
|
||
"continuityLabel": "Continuity",
|
||
"continuityBpLabel": "BP Medication Continuity (%)",
|
||
"weightKgLabel": "Weight (kg)",
|
||
"bpMedNegative": "Negative correlation: higher continuity tends to be associated with lower systolic blood pressure.",
|
||
"bpMedPositive": "Positive correlation: higher continuity tends to be associated with higher systolic blood pressure in your data.",
|
||
"bpMedNone": "No discernible correlation in your data.",
|
||
"bpMedCount": "{count} BP medications",
|
||
"notEnoughMedData": "Not enough data for this analysis.",
|
||
"notEnoughMedDataHint": "Tag medications as blood pressure medications and record sufficient intakes + blood pressure measurements.",
|
||
"medicationCompliance": "Medication Compliance",
|
||
"dayStreak": "{count} day streak",
|
||
"compliance7d": "7-day compliance",
|
||
"compliance30d": "30-day compliance",
|
||
"taken": "taken",
|
||
"skipped": "skipped",
|
||
"missed": "missed",
|
||
"complianceTrend": "Compliance Trend",
|
||
"generalStatusTitle": "General Health Status",
|
||
"generalStatusLoading": "Generating general status...",
|
||
"generalStatusNoKey": "The general status cannot be generated because no OpenAI key is configured.",
|
||
"generalStatusUnavailable": "The general status is currently unavailable.",
|
||
"generalStatusBadge": {
|
||
"good": "Good",
|
||
"watch": "Watch",
|
||
"critical": "Critical"
|
||
},
|
||
"assessmentTitle": "Assessment",
|
||
"bloodPressureSectionTitle": "Blood Pressure",
|
||
"bloodPressureMedicationListTitle": "Blood Pressure Medication & Compliance",
|
||
"bloodPressureMedicationListEmpty": "No active blood pressure medication configured.",
|
||
"bloodPressureStatusLoading": "Generating blood pressure summary...",
|
||
"bloodPressureStatusNoKey": "The blood pressure summary cannot be generated because no OpenAI key is configured.",
|
||
"bloodPressureStatusUnavailable": "The blood pressure summary is currently unavailable.",
|
||
"weightSectionTitle": "Weight",
|
||
"weightStatusLoading": "Generating weight summary...",
|
||
"weightStatusNoKey": "The weight summary cannot be generated because no OpenAI key is configured.",
|
||
"weightStatusUnavailable": "The weight summary is currently unavailable.",
|
||
"pulseSectionTitle": "Pulse",
|
||
"pulseStatusLoading": "Generating pulse summary...",
|
||
"pulseStatusNoKey": "The pulse summary cannot be generated because no OpenAI key is configured.",
|
||
"pulseStatusUnavailable": "The pulse summary is currently unavailable.",
|
||
"bmiSectionTitle": "BMI",
|
||
"bmiStatusLoading": "Generating BMI summary...",
|
||
"bmiStatusNoKey": "The BMI summary cannot be generated because no OpenAI key is configured.",
|
||
"bmiStatusUnavailable": "The BMI summary is currently unavailable.",
|
||
"medicationComplianceStatusLoading": "Generating medication compliance text...",
|
||
"medicationComplianceStatusNoKey": "The medication compliance text cannot be generated because no OpenAI key is configured.",
|
||
"medicationComplianceStatusUnavailable": "The medication compliance text is currently unavailable.",
|
||
"complianceNoData": "No compliance data available.",
|
||
"aiInsights": "AI Insights",
|
||
"configureApiKey": "Configure an OpenAI API key in Settings to get AI-powered insights.",
|
||
"alertsTitle": "Notes & Recommendations",
|
||
"showLess": "Show less",
|
||
"showMore": "+{count} more",
|
||
"generate": "Generate new analysis",
|
||
"generating": "Generating analysis...",
|
||
"lastGenerated": "Last analysis",
|
||
"noInsights": "No analysis available yet.",
|
||
"rateLimit": "Please wait before generating a new analysis.",
|
||
"noApiKey": "Please add an OpenAI API key in Settings first.",
|
||
"sendRawData": "Send raw data",
|
||
"minMoodCorrelationData": "At least 5 temporally matched mood and measurement data points required.",
|
||
"moodBpModerate": "There is a moderate correlation between mood and blood pressure.",
|
||
"moodBpNone": "No detectable correlation between mood and blood pressure.",
|
||
"moodBpStrong": "There is a strong correlation between mood and blood pressure.",
|
||
"moodBpWeak": "There is a weak correlation between mood and blood pressure.",
|
||
"moodScoreLabel": "Mood (avg)",
|
||
"moodSectionTitle": "Mood",
|
||
"moodStatusLoading": "Generating mood analysis...",
|
||
"moodStatusNoKey": "Mood summary cannot be generated because no OpenAI API key is configured.",
|
||
"moodStatusUnavailable": "Mood analysis unavailable",
|
||
"moodVsBp": "Mood vs. Blood Pressure (Systolic)",
|
||
"moodVsPulse": "Mood vs. Pulse",
|
||
"moodVsWeight": "Mood vs. Weight",
|
||
"moodWeightModerate": "There is a moderate correlation between mood and weight.",
|
||
"moodWeightNone": "No detectable correlation between mood and weight.",
|
||
"moodWeightStrong": "There is a strong correlation between mood and weight.",
|
||
"moodWeightWeak": "There is a weak correlation between mood and weight.",
|
||
"notEnoughMoodCorrelationData": "Not enough mood data for correlation analysis.",
|
||
"refreshButton": "Refresh",
|
||
"generateButton": "Generate",
|
||
"analyzing": "Analyzing health data...",
|
||
"confidence": "Confidence",
|
||
"confidence_niedrig": "low",
|
||
"confidence_mittel": "medium",
|
||
"confidence_hoch": "high",
|
||
"cached": "Cached",
|
||
"sectionChanged": "What has changed",
|
||
"sectionStable": "What is stable",
|
||
"sectionDrivers": "Possible correlations",
|
||
"sectionNextSteps": "Next steps",
|
||
"generatePrompt": "Click \"Generate\" for AI-powered insights about your health data."
|
||
},
|
||
"thresholds": {
|
||
"title": "Personal thresholds",
|
||
"subtitle": "Override the computed target range for any metric. Defaults stay in effect until you set a value here.",
|
||
"defaultLabel": "Default",
|
||
"overrideLabel": "Your override",
|
||
"autoModeLabel": "Auto",
|
||
"overrideModeLabel": "Override",
|
||
"resetAction": "Reset to default",
|
||
"resetAllAction": "Reset all to default",
|
||
"minLabel": "Min",
|
||
"maxLabel": "Max",
|
||
"unitSuffix": "({unit})",
|
||
"outOfBoundsHint": "Allowed range: {min}–{max} {unit}",
|
||
"overrideWarning": "Overriding with values outside guideline ranges may produce unhealthy targets. Only do this on doctor's advice.",
|
||
"saveSuccess": "Thresholds updated",
|
||
"saveError": "Could not save thresholds",
|
||
"resetSuccess": "Reset to default",
|
||
"sourceOverride": "User override",
|
||
"metricWeight": "Weight",
|
||
"metricBpSys": "Blood pressure (systolic)",
|
||
"metricBpDia": "Blood pressure (diastolic)",
|
||
"metricPulse": "Resting pulse",
|
||
"metricBodyFat": "Body fat",
|
||
"metricSleep": "Sleep duration",
|
||
"metricSteps": "Daily steps",
|
||
"metricGlucoseFasting": "Glucose — fasting",
|
||
"metricGlucosePostprandial": "Glucose — post-meal",
|
||
"metricGlucoseRandom": "Glucose — random",
|
||
"metricGlucoseBedtime": "Glucose — bedtime",
|
||
"metricBodyWater": "Body water",
|
||
"metricBoneMass": "Bone mass",
|
||
"metricOxygenSaturation": "Oxygen saturation (SpO₂)",
|
||
"unsetExplanation": "No override — using computed default.",
|
||
"loadError": "Could not load thresholds"
|
||
},
|
||
"onboarding": {
|
||
"welcome": "Welcome to HealthLog!",
|
||
"setupDescription": "Set up your profile to get started. You can change this later in Settings.",
|
||
"completeSetup": "Complete setup",
|
||
"skip": "Skip",
|
||
"medicationsTitle": "Do you take medications?",
|
||
"medicationsDescription": "Add your medications to enable reminders and compliance tracking.",
|
||
"medNamePlaceholder": "Name",
|
||
"medDosePlaceholder": "Dose",
|
||
"notificationsTitle": "Reminders",
|
||
"notificationsDescription": "HealthLog can remind you about your medications. Configure this later in Settings.",
|
||
"telegramHint": "Reminders via Telegram bot with snooze buttons",
|
||
"webPushHint": "Browser notifications directly to your device",
|
||
"notificationsLater": "You can set this up anytime in Settings.",
|
||
"targetsTitle": "Target Values",
|
||
"targetsDescription": "Set optional target values for blood pressure and weight.",
|
||
"targetSys": "Systolic (mmHg)",
|
||
"targetDia": "Diastolic (mmHg)",
|
||
"targetWeight": "Target weight (kg)",
|
||
"errorProfile": "Could not save your profile. Please try again.",
|
||
"errorMedication": "Could not add the medication \"{name}\". Please try again or add it later in Medications.",
|
||
"errorTargets": "Could not save your target values. Please try again.",
|
||
"errorGeneric": "Something went wrong while finishing setup. Please try again.",
|
||
"medScheduleHint": "We'll add a default reminder window (08:00–09:00 daily). You can adjust it anytime in Medications."
|
||
},
|
||
"settings": {
|
||
"title": "Settings",
|
||
"subtitle": "Profile, Security & Connections",
|
||
"sectionsTitle": "Settings",
|
||
"categoryGeneral": "General",
|
||
"categoryNotifications": "Notifications",
|
||
"categoryAi": "AI",
|
||
"categoryPersonalization": "Personalization",
|
||
"categoryIntegration": "Integration",
|
||
"categoryApi": "API",
|
||
"profile": "Profile",
|
||
"security": "Security",
|
||
"language": "Language",
|
||
"languageDescription": "Choose the display language of the application.",
|
||
"languageSystem": "System (automatic)",
|
||
"username": "Username",
|
||
"height": "Height (cm)",
|
||
"dateOfBirth": "Date of birth",
|
||
"dateOfBirthHint": "Used for automatic blood pressure target calculations.",
|
||
"gender": "Gender",
|
||
"genderNone": "Not specified",
|
||
"genderMale": "Male",
|
||
"genderFemale": "Female",
|
||
"genderHint": "Used for gender-specific target values.",
|
||
"profileSaved": "Profile saved",
|
||
"changePassword": "Change password",
|
||
"changePasswordDescription": "Replace your current password with a new one.",
|
||
"passwordReset": "Password reset",
|
||
"currentPassword": "Current password",
|
||
"newPassword": "New password",
|
||
"confirmNewPassword": "Confirm new password",
|
||
"passwordMismatch": "New passwords do not match",
|
||
"passwordUpdated": "Password updated successfully",
|
||
"passkeys": "Passkeys",
|
||
"registeredPasskeys": "Registered Passkeys",
|
||
"passkeysDescription": "Passkeys allow secure login without a password.",
|
||
"passkeyName": "Name",
|
||
"passkeyDevice": "Device",
|
||
"passkeyBackup": "Backup",
|
||
"passkeyCreated": "Created",
|
||
"passkeyActions": "Actions",
|
||
"noPasskeys": "No passkeys registered.",
|
||
"addPasskey": "Add passkey",
|
||
"passkeyAdded": "Passkey added successfully!",
|
||
"passkeyOptionsError": "Could not load passkey options",
|
||
"passkeyRegistrationFailed": "Passkey registration failed",
|
||
"passkeyRegistrationCancelled": "Passkey registration cancelled",
|
||
"passkeyNotSupported": "Your browser or device doesn't support passkeys yet. Try a different browser or device.",
|
||
"passkeyAlreadyRegistered": "This device is already registered as a passkey for your account.",
|
||
"passkeySecurityBlocked": "Your browser blocked the passkey request for security reasons. Make sure you're on a trusted HTTPS origin and try again.",
|
||
"passkeyTimeout": "The passkey prompt timed out. Please try again.",
|
||
"passkeyUnknownError": "Passkey registration failed: {message}",
|
||
"deletePasskey": "Delete passkey?",
|
||
"deletePasskeyDescription": "The passkey will be permanently deleted.",
|
||
"singleDevice": "Single device",
|
||
"multiDevice": "Multi-device",
|
||
"backedUp": "backed up",
|
||
"telegram": "Telegram Notifications",
|
||
"telegramDescription": "Get reminders for missed medication intake via Telegram.",
|
||
"telegramSaved": "Telegram settings saved",
|
||
"botToken": "Bot Token",
|
||
"chatId": "Chat ID",
|
||
"enableNotifications": "Enable notifications",
|
||
"testMessage": "Test message",
|
||
"testSent": "Test message sent!",
|
||
"telegramStep1": "1. Create a bot via @BotFather in Telegram and copy the token.",
|
||
"telegramStep2": "2. Send /start to your bot to activate the chat.",
|
||
"telegramStep3": "3. Find your Chat ID via @userinfobot or the Bot API.",
|
||
"ntfy": "ntfy",
|
||
"ntfyDescription": "Notifications via ntfy (self-hosted or ntfy.sh).",
|
||
"ntfyEnable": "Enable ntfy",
|
||
"ntfyServer": "Server URL",
|
||
"ntfyTopic": "Topic",
|
||
"ntfyAuthToken": "Auth Token (optional)",
|
||
"ntfyAuthTokenHint": "Only needed for private topics with access control.",
|
||
"webPush": "Browser Push",
|
||
"webPushDescription": "Receive notifications directly in your browser, even when HealthLog is not open.",
|
||
"webPushNotSupported": "Your browser does not support push notifications.",
|
||
"webPushDenied": "Push notifications are blocked. Allow them in your browser settings.",
|
||
"webPushNotConfigured": "Web Push is not configured on the server.",
|
||
"webPushSubscribe": "Enable push",
|
||
"webPushUnsubscribe": "Disable push",
|
||
"webPushSubscribed": "Push notifications enabled!",
|
||
"webPushUnsubscribed": "Push notifications disabled.",
|
||
"webPushSubscribeFailed": "Activation failed",
|
||
"webPushActive": "Active",
|
||
"kiInsights": "AI Insights (OpenAI)",
|
||
"kiInsightsDescription": "Optional: Save your OpenAI key for daily, automatic AI evaluations in Insights.",
|
||
"kiInsightsBenefitsTitle": "Benefits with OpenAI key",
|
||
"kiInsightsBenefit1": "Daily summaries for general status, blood pressure, weight, pulse, BMI, and medication compliance.",
|
||
"kiInsightsBenefit2": "Better trend and correlation interpretation based on your latest 30 daily measurement points per area.",
|
||
"kiInsightsBenefit3": "Automatic background refresh so the Insights page always shows up-to-date assessments.",
|
||
"createApiKeyHint": "Create your API key at:",
|
||
"createApiKeyLinkLabel": "platform.openai.com/api-keys",
|
||
"apiKey": "API Key",
|
||
"apiKeyStored": "Key stored — enter new one to replace",
|
||
"apiKeySaved": "API key saved",
|
||
"apiKeyRemoved": "API key removed",
|
||
"codexConnected": "ChatGPT connected successfully! AI insights are now active.",
|
||
"codexDisconnected": "ChatGPT connection removed.",
|
||
"codexConnectionFailed": "ChatGPT connection failed. Please try again.",
|
||
"rawData": "Send raw data",
|
||
"rawDataOnDescription": "Aggregated metrics plus anonymized raw points from the last 30 days are sent. This includes per-metric measurements (for example weight, blood pressure, pulse) with time context so AI can detect patterns, outliers, and correlations more reliably. Name, email, and direct account identifiers are not transmitted.",
|
||
"rawDataOffDescription": "Only summarized metrics are sent (for example averages, trends, minimum/maximum). No individual points and no exact time context. This is more privacy-preserving, but AI can be less precise for short-term patterns and fluctuations.",
|
||
"rawDataWarning": "Raw mode enabled: AI receives additional anonymized points from the last 30 days for higher analysis accuracy.",
|
||
"regenerateInsights": "Regenerate reports",
|
||
"regenerateSuccess": "Reports regenerated successfully",
|
||
"regenerateRateLimit": "Please wait — max 2 analyses per hour.",
|
||
"lastGeneratedAt": "Last generated",
|
||
"withings": "Withings",
|
||
"withingsDescription": "Connect your Withings scale and blood pressure monitors.",
|
||
"withingsCredentials": "API Credentials",
|
||
"withingsClientId": "Client ID",
|
||
"withingsClientSecret": "Client Secret",
|
||
"withingsCredentialsSaved": "Credentials saved",
|
||
"withingsCredentialsSavedPlaceholder": "Saved — enter new to replace",
|
||
"withingsCredentialsSavedPlaceholderSecret": "Saved — enter new to replace",
|
||
"withingsSaveCredentials": "Save credentials",
|
||
"withingsConnected": "Connected",
|
||
"configured": "Configured",
|
||
"withingsTokenExpired": "Token expired",
|
||
"withingsLastSync": "Last sync",
|
||
"withingsSync": "Sync now",
|
||
"withingsFullSync": "Sync all data",
|
||
"withingsFullSyncTitle": "Full synchronization?",
|
||
"withingsFullSyncDescription": "All available Withings data will be fully synchronized. This may take some time depending on your history.",
|
||
"withingsSyncResult": "{count} measurements synchronized",
|
||
"withingsFullSyncResult": "{count} measurements fully synchronized",
|
||
"withingsSyncFailed": "Sync failed",
|
||
"withingsSynchronize": "Synchronize",
|
||
"withingsDisconnect": "Disconnect",
|
||
"withingsDisconnectTitle": "Disconnect Withings?",
|
||
"withingsDisconnectDescription": "The connection to Withings will be disconnected. Previously synced data will be preserved.",
|
||
"withingsConnect": "Connect with Withings",
|
||
"withingsNoCredentials": "Please enter your API credentials above to connect Withings.",
|
||
"apiTokens": "API Tokens",
|
||
"apiTokensDescription": "Create API tokens for external medication intake (e.g., Shortcuts, automations).",
|
||
"tokenName": "Token name",
|
||
"tokenNamePlaceholder": "Token name (e.g., iPhone Shortcut)",
|
||
"tokenCreated": "Token created — copy it now! It won't be shown again.",
|
||
"tokenRevoke": "Revoke token?",
|
||
"tokenRevokeDescription": "The token will be permanently deactivated. Applications using it will lose access.",
|
||
"tokenRevoked": "Revoked",
|
||
"tokenExpired": "Expired",
|
||
"tokenActive": "Active",
|
||
"activeTokensTitle": "Active tokens",
|
||
"revokedTokensTitle": "Revoked tokens ({count})",
|
||
"tokenTableName": "Name",
|
||
"tokenTablePermissions": "Permissions",
|
||
"tokenTableStatus": "Status",
|
||
"tokenTableCreated": "Created",
|
||
"tokenTableLastUsed": "Last used",
|
||
"tokenTableActions": "Actions",
|
||
"noActiveTokens": "No active tokens available.",
|
||
"tokenNeverUsed": "Never",
|
||
"tokenLastUsed": "Last used:",
|
||
"tokenCreatedAt": "Created:",
|
||
"apiEndpoint": "API endpoint for medication intake:",
|
||
"apiEndpointsTitle": "API endpoints",
|
||
"apiEndpointsDescription": "Overview of currently available external endpoint(s) for token-based ingestion.",
|
||
"apiEndpointMethod": "Method",
|
||
"apiEndpointPath": "Path",
|
||
"apiEndpointAuth": "Authentication",
|
||
"apiEndpointExample": "Body example",
|
||
"export": "Data Export",
|
||
"exportDescription": "Export all your health data.",
|
||
"exportJson": "JSON Export",
|
||
"exportCsv": "CSV Export",
|
||
"doctorReport": "Doctor Report (PDF)",
|
||
"auditLog": "Activity Log",
|
||
"auditLogHidden": "Log hidden.",
|
||
"auditLogShowing": "Showing last {count} of {total} entries",
|
||
"auditNoActivities": "No activities found.",
|
||
"auditAction": "Action",
|
||
"auditCategory": "Category",
|
||
"auditIp": "IP",
|
||
"auditLocation": "Location",
|
||
"auditTimestamp": "Time",
|
||
"auditOther": "Other",
|
||
"auditCategoryAuth": "Auth",
|
||
"auditCategoryData": "Data",
|
||
"auditCategoryIntegration": "Integration",
|
||
"auditCategoryAI": "AI",
|
||
"auditCategoryAdmin": "Admin",
|
||
"auditActionAuthRegister": "Registration",
|
||
"auditActionAuthLogin": "Login",
|
||
"auditActionAuthLoginPasskey": "Passkey login",
|
||
"auditActionAuthLoginPassword": "Password login",
|
||
"auditActionAuthLoginFailed": "Failed login",
|
||
"auditActionAuthLogout": "Logout",
|
||
"auditActionAuthPasskeyRegister": "Passkey added",
|
||
"auditActionAuthPasskeyDelete": "Passkey deleted",
|
||
"auditActionAuthPasswordChange": "Password changed",
|
||
"auditActionMeasurementCreate": "Measurement created",
|
||
"auditActionMeasurementUpdate": "Measurement updated",
|
||
"auditActionMeasurementDelete": "Measurement deleted",
|
||
"auditActionMedicationCreate": "Medication created",
|
||
"auditActionMedicationUpdate": "Medication updated",
|
||
"auditActionMedicationDelete": "Medication deleted",
|
||
"auditActionMedicationIntake": "Intake recorded",
|
||
"auditActionMedicationIngestExternal": "External intake",
|
||
"auditActionWithingsConnect": "Withings connected",
|
||
"auditActionWithingsDisconnect": "Withings disconnected",
|
||
"auditActionExportDownload": "Export downloaded",
|
||
"auditActionInsightsGenerate": "AI insight generated",
|
||
"auditActionAdminUserUpdate": "Admin: User updated",
|
||
"auditActionAdminUserResetPassword": "Admin: Password reset",
|
||
"auditActionAdminSettingsUpdate": "Admin: Settings changed",
|
||
"collapse": "Collapse",
|
||
"expand": "Expand",
|
||
"dangerZoneTitle": "Danger Zone",
|
||
"dangerZone": "Delete All Data",
|
||
"dangerZoneDescription": "Deletes all your health data and integrations. Your user account will be preserved.",
|
||
"dangerZoneConfirm": "Delete all data?",
|
||
"dangerZoneConfirmDescription": "This will permanently delete all your health data and integrations. Your user account will be preserved.",
|
||
"dangerZoneSuccess": "All personal data has been deleted",
|
||
"dangerZoneDeleteFailed": "Deletion failed",
|
||
"finalDelete": "Delete permanently",
|
||
"saved": "Saved",
|
||
"savingError": "Error saving",
|
||
"adminAreaTitle": "Admin area",
|
||
"adminAreaDescription": "System-wide settings and user management are available in the admin console.",
|
||
"openAdminConsole": "Open admin console",
|
||
"moodLogTitle": "moodLog",
|
||
"moodLogDescription": "Import mood data from moodLog",
|
||
"moodLogUrl": "moodLog URL",
|
||
"moodLogUrlPlaceholder": "https://mood.example.com",
|
||
"moodLogApiKey": "API Key",
|
||
"moodLogApiKeyPlaceholder": "ml_...",
|
||
"moodLogWebhookSecret": "Webhook Secret",
|
||
"moodLogWebhookSecretHelp": "Enter this secret in moodLog as webhook secret",
|
||
"moodLogSync": "Start sync",
|
||
"moodLogFullSync": "Full sync",
|
||
"moodLogFullSyncTitle": "Full sync?",
|
||
"moodLogFullSyncDescription": "All available mood data will be fully synchronized.",
|
||
"moodLogDisconnect": "Disconnect",
|
||
"moodLogDisconnectTitle": "Disconnect moodLog?",
|
||
"moodLogDisconnectDescription": "The connection will be removed and all imported mood data will be deleted.",
|
||
"moodLogLastSync": "Last sync",
|
||
"moodLogEntries": "Entries",
|
||
"moodLogSyncResult": "{count} entries synchronized",
|
||
"moodLogSyncFailed": "Sync failed",
|
||
"moodLogSaved": "moodLog connection saved",
|
||
"moodLogDisconnected": "moodLog disconnected"
|
||
},
|
||
"admin": {
|
||
"title": "Administration",
|
||
"subtitle": "System status, users, and settings",
|
||
"settingsSaveError": "Failed to save settings",
|
||
"settingsSaveValidationError": "Please check the highlighted fields",
|
||
"sectionsTitle": "Admin",
|
||
"systemStatus": "System Status",
|
||
"version": "Version",
|
||
"database": "Database",
|
||
"databaseConnected": "Connected",
|
||
"databaseError": "Error",
|
||
"gitCommit": "Git Commit",
|
||
"startedAt": "Started",
|
||
"users": "Users",
|
||
"measurementsCount": "Measurements",
|
||
"activeTokens": "Active API Tokens",
|
||
"activeSessions": "Active Sessions",
|
||
"loadingStatus": "Loading status...",
|
||
"workerStatus": "Job Worker",
|
||
"workerRunning": "Running",
|
||
"workerStopped": "Stopped",
|
||
"lastReminderCheck": "Last Reminder Check",
|
||
"appSettings": "Application Settings",
|
||
"registrationEnabled": "Allow registration",
|
||
"registrationEnabledDescription": "When disabled, new users cannot register.",
|
||
"defaultLanguage": "Default language",
|
||
"defaultLanguageDescription": "Used for new users and as fallback.",
|
||
"servicesGlobal": "Services (global)",
|
||
"servicesGlobalDescription": "Disabled services are not available to any user.",
|
||
"telegramGlobal": "Allow Telegram notifications system-wide.",
|
||
"ntfyGlobal": "Allow ntfy notifications system-wide.",
|
||
"webPushGlobal": "Allow browser push notifications system-wide.",
|
||
"integrationWebPush": "Web Push",
|
||
"integrationBugReport": "Bug Report",
|
||
"apiGlobal": "Allow API endpoints and API tokens system-wide.",
|
||
"moodLogGlobal": "Allow moodLog integration system-wide.",
|
||
"sectionGeneral": "General",
|
||
"sectionIntegrations": "Integrations",
|
||
"sectionMedication": "Medications",
|
||
"sectionManagement": "Management",
|
||
"umamiTitle": "Umami",
|
||
"umamiDescription": "Web analytics with Umami",
|
||
"glitchtipTitle": "GlitchTip",
|
||
"glitchtipDescription": "Error tracking with GlitchTip",
|
||
"webPushVapidTitle": "Web Push VAPID",
|
||
"monitoring": "Monitoring (Umami/Glitchtip)",
|
||
"monitoringDescription": "Configuration for website analytics and error tracking.",
|
||
"umamiEnabled": "Enable Umami tracking.",
|
||
"umamiScriptUrl": "Umami script URL",
|
||
"umamiScriptUrlPlaceholder": "https://analytics.example.com/script.js",
|
||
"umamiWebsiteId": "Umami website ID",
|
||
"umamiWebsiteIdPlaceholder": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
||
"glitchtipEnabled": "Enable Glitchtip error tracking.",
|
||
"glitchtipDsn": "Glitchtip DSN",
|
||
"glitchtipDsnPlaceholder": "https://<public-key>@glitchtip.example.com/<project-id>",
|
||
"glitchtipEnvironment": "Glitchtip environment",
|
||
"glitchtipEnvironmentPlaceholder": "production",
|
||
"webPushVapid": "Web Push (VAPID)",
|
||
"webPushVapidDescription": "Configure VAPID keys and subject here so browser push works without environment variables.",
|
||
"webPushVapidPublicKey": "VAPID public key",
|
||
"webPushVapidPublicKeyPlaceholder": "BElx... (Base64URL)",
|
||
"webPushVapidPrivateKey": "VAPID private key",
|
||
"webPushVapidPrivateKeyPlaceholder": "Optional: enter a new private key",
|
||
"webPushVapidSubject": "VAPID subject",
|
||
"webPushVapidSubjectPlaceholder": "mailto:you@example.com",
|
||
"configured": "Configured",
|
||
"notConfigured": "Not configured",
|
||
"monitoringTestUmami": "Send Umami test",
|
||
"monitoringTestGlitchtip": "Send Glitchtip test",
|
||
"monitoringTestSuccess": "Test event sent",
|
||
"monitoringTestFailed": "Monitoring test failed",
|
||
"monitoringSave": "Save monitoring",
|
||
"bugReportGithub": "Bug report (GitHub Issues)",
|
||
"bugReportGithubDescription": "Configure GitHub issue token and repository for incoming bug reports.",
|
||
"bugReportRepo": "Repository",
|
||
"bugReportRepoPlaceholder": "owner/repo",
|
||
"bugReportToken": "Issue token",
|
||
"bugReportTokenPlaceholder": "ghp_...",
|
||
"bugReportConfigured": "Bug report is configured.",
|
||
"bugReportNotConfigured": "Bug report is not fully configured yet.",
|
||
"bugReportSave": "Save bug report settings",
|
||
"userManagement": "User Management",
|
||
"userEmail": "Email",
|
||
"userRole": "Role",
|
||
"userPasskeys": "Passkeys",
|
||
"userCreated": "Created",
|
||
"userActions": "Actions",
|
||
"editUser": "Edit user",
|
||
"editUserTitle": "Edit user: {name}",
|
||
"demoteToUser": "Demote to user",
|
||
"promoteToAdmin": "Promote to admin",
|
||
"toUser": "→ User",
|
||
"toAdmin": "→ Admin",
|
||
"ownRoleUnchangeable": "Cannot change own role",
|
||
"lastAdminError": "Cannot demote the last admin",
|
||
"resetPassword": "Reset password",
|
||
"resetPasswordTitle": "Reset password: {name}",
|
||
"newPassword": "New password",
|
||
"newPasswordPlaceholder": "At least 12 characters",
|
||
"passwordReset": "Password has been reset",
|
||
"reset": "Reset",
|
||
"loadingUsers": "Loading users...",
|
||
"apiTokens": "API Tokens",
|
||
"tokenUser": "User",
|
||
"tokenName": "Name",
|
||
"tokenPermissions": "Permissions",
|
||
"tokenStatus": "Status",
|
||
"tokenLastUsed": "Last used",
|
||
"tokenCreated": "Created",
|
||
"tokenNeverUsed": "Never",
|
||
"noTokens": "No API tokens found.",
|
||
"loginOverview": "Login Overview",
|
||
"allAuthEvents": "All auth events",
|
||
"failedOnly": "Failed only",
|
||
"noEntries": "No entries found.",
|
||
"showingEntries": "Showing {count} of {total} auth entries",
|
||
"status": "Status",
|
||
"action": "Action",
|
||
"ip": "IP",
|
||
"location": "Location",
|
||
"timestamp": "Time",
|
||
"dangerZone": "Danger Zone",
|
||
"deleteAllData": "Delete all data globally",
|
||
"deleteAllDescription": "Deletes all measurement, medication, integration, and log data for all users. User accounts will be preserved.",
|
||
"deleteAllConfirm": "Really delete all data?",
|
||
"deleteAllConfirmDescription": "This will permanently delete all measurements, medications, intake records, and integration data for all users. User accounts will be preserved.",
|
||
"deleteButton": "Delete all data",
|
||
"finalDelete": "Delete permanently",
|
||
"deletedResult": "Deleted: {measurements} measurements, {medications} medications, {intakeEvents} intakes",
|
||
"authRegister": "Registration",
|
||
"authLogin": "Login",
|
||
"authLoginPasskey": "Passkey login",
|
||
"authLoginPassword": "Password login",
|
||
"authLoginFailed": "Failed",
|
||
"authLogout": "Logout",
|
||
"authPasskeyRegister": "Passkey added",
|
||
"authPasskeyDelete": "Passkey deleted",
|
||
"medicationReminders": "Medication Reminders",
|
||
"medicationRemindersDescription": "Thresholds for reminder badge color transitions on medication cards.",
|
||
"reminderLateMinutes": "Late threshold (minutes)",
|
||
"reminderLateMinutesDescription": "Minutes after window end until the badge changes from yellow to orange.",
|
||
"reminderMissedMinutes": "Missed threshold (minutes)",
|
||
"reminderMissedMinutesDescription": "Minutes after window end until the intake is considered missed.",
|
||
"reminderThresholdsSave": "Save thresholds",
|
||
"notificationTest": "Test Notifications",
|
||
"notificationTestSend": "Send test notification",
|
||
"notificationTestSuccess": "Test notification sent",
|
||
"notificationTestFailed": "Failed to send test notification",
|
||
"reminderCheckRun": "Run reminder check",
|
||
"reminderCheckSuccess": "Reminder check completed",
|
||
"reminderCheckFailed": "Failed to run reminder check",
|
||
"reminderCheckResults": "Results",
|
||
"reminderCheckNotifOn": "Notifications on",
|
||
"reminderCheckNotifOff": "Notifications off",
|
||
"reminderCheckEventsToday": "Events today",
|
||
"reminderCheckNotifSent": "Sent",
|
||
"feedback": {
|
||
"title": "Feedback Inbox",
|
||
"description": "User-submitted feedback, bug reports, and feature requests.",
|
||
"tabOpen": "Open",
|
||
"tabAcknowledged": "Acknowledged",
|
||
"tabResolved": "Resolved",
|
||
"tabArchived": "Archived",
|
||
"subject": "Subject",
|
||
"category": "Category",
|
||
"user": "User",
|
||
"createdAt": "Submitted",
|
||
"actions": "Actions",
|
||
"details": "Details",
|
||
"noEntries": "No feedback in this category.",
|
||
"loading": "Loading feedback...",
|
||
"anonymous": "anonymous",
|
||
"categoryBug": "Bug",
|
||
"categoryFeature": "Feature",
|
||
"categoryQuestion": "Question",
|
||
"categoryOther": "Other",
|
||
"statusOpen": "Open",
|
||
"statusAcknowledged": "Acknowledged",
|
||
"statusResolved": "Resolved",
|
||
"statusArchived": "Archived",
|
||
"dialogTitle": "Feedback details",
|
||
"metadataHeading": "Metadata",
|
||
"metaUrl": "URL",
|
||
"metaLocale": "Locale",
|
||
"metaUserAgent": "User agent",
|
||
"metaAppVersion": "App version",
|
||
"screenshotHeading": "Screenshot",
|
||
"adminNote": "Admin note",
|
||
"adminNotePlaceholder": "Internal note (not visible to user)",
|
||
"saveNote": "Save note",
|
||
"noteSaved": "Note saved",
|
||
"actionAcknowledge": "Acknowledge",
|
||
"actionResolve": "Resolve",
|
||
"actionArchive": "Archive",
|
||
"actionPublishGithub": "Publish to GitHub",
|
||
"publishedToGithub": "Published to GitHub",
|
||
"viewIssue": "View issue",
|
||
"publishSuccess": "Issue created",
|
||
"publishFailed": "GitHub publish failed",
|
||
"updateFailed": "Update failed",
|
||
"archiveConfirm": "Archive this feedback entry?",
|
||
"submittedBy": "Submitted by",
|
||
"sectionTitle": "Feedback"
|
||
}
|
||
},
|
||
"achievements": {
|
||
"title": "Achievements",
|
||
"subtitle": "Unlock achievements from feature rollout onward through consistent health trends and medication habits.",
|
||
"loginRequired": "Please sign in to view achievements.",
|
||
"points": "Points",
|
||
"unlocked": "Unlocked",
|
||
"nextGoal": "Next goal",
|
||
"allCompleted": "All achievements unlocked!",
|
||
"completed": "Completed",
|
||
"inProgress": "In progress",
|
||
"progress": "Progress",
|
||
"goalReached": "Goal reached",
|
||
"remaining": "{count} remaining",
|
||
"remainingUnlocks": "{count} still unlockable",
|
||
"noneUnlockedYet": "No achievements unlocked yet.",
|
||
"pointsValue": "{points} points",
|
||
"metricCount": "{count}",
|
||
"metricDays": "{count} days",
|
||
"metricPercent": "{count}%",
|
||
"badges": {
|
||
"intakeTotal1": {
|
||
"title": "First intake",
|
||
"description": "Record your first taken medication event."
|
||
},
|
||
"intakeTotal10": {
|
||
"title": "Intake starter",
|
||
"description": "Record 10 taken medication events."
|
||
},
|
||
"intakeTotal50": {
|
||
"title": "Intake routine",
|
||
"description": "Record 50 taken medication events."
|
||
},
|
||
"intakeTotal150": {
|
||
"title": "Intake expert",
|
||
"description": "Record 150 taken medication events."
|
||
},
|
||
"intakeTotal300": {
|
||
"title": "Intake legend",
|
||
"description": "Record 300 taken medication events."
|
||
},
|
||
"overIntake1": {
|
||
"title": "Idiot",
|
||
"description": "Take medication at least once more than planned."
|
||
},
|
||
"skippedIntake1": {
|
||
"title": "Lazy Boy",
|
||
"description": "Skip at least one planned intake."
|
||
},
|
||
"passkeyCreated1": {
|
||
"title": "Passkey set up",
|
||
"description": "Create your first passkey."
|
||
},
|
||
"passkeyLogin1": {
|
||
"title": "Passkey login",
|
||
"description": "Sign in at least once with a passkey."
|
||
},
|
||
"passwordLogin1": {
|
||
"title": "Old school",
|
||
"description": "Sign in at least once with username and password."
|
||
},
|
||
"bugReport1": {
|
||
"title": "Bug hunter",
|
||
"description": "Submit a bug report."
|
||
},
|
||
"loginStreak7": {
|
||
"title": "Login streak 7d",
|
||
"description": "Sign in on 7 consecutive days."
|
||
},
|
||
"loginStreak30": {
|
||
"title": "Login streak 30d",
|
||
"description": "Sign in on 30 consecutive days."
|
||
},
|
||
"onTimePerfect1": {
|
||
"title": "On-time 1d",
|
||
"description": "Take all medications on time for 1 consecutive days."
|
||
},
|
||
"compliance801": {
|
||
"title": "80% adherence · 1d",
|
||
"description": "Reach at least 80% 30-day adherence for 1 consecutive days."
|
||
},
|
||
"bmiGreen1": {
|
||
"title": "BMI green · 1d",
|
||
"description": "Keep your BMI in the normal range for 1 consecutive days."
|
||
},
|
||
"bpGreen1": {
|
||
"title": "BP green · 1d",
|
||
"description": "Keep your blood pressure in the normal range for 1 consecutive days."
|
||
},
|
||
"pulseGreen1": {
|
||
"title": "Pulse green · 1d",
|
||
"description": "Keep your resting pulse in the normal range for 1 consecutive days."
|
||
},
|
||
"onTimePerfect7": {
|
||
"title": "On-time 7d",
|
||
"description": "Take all medications on time for 7 consecutive days."
|
||
},
|
||
"compliance807": {
|
||
"title": "80% adherence · 7d",
|
||
"description": "Reach at least 80% 30-day adherence for 7 consecutive days."
|
||
},
|
||
"bmiGreen7": {
|
||
"title": "BMI green · 7d",
|
||
"description": "Keep your BMI in the normal range for 7 consecutive days."
|
||
},
|
||
"bpGreen7": {
|
||
"title": "BP green · 7d",
|
||
"description": "Keep your blood pressure in the normal range for 7 consecutive days."
|
||
},
|
||
"pulseGreen7": {
|
||
"title": "Pulse green · 7d",
|
||
"description": "Keep your resting pulse in the normal range for 7 consecutive days."
|
||
},
|
||
"onTimePerfect30": {
|
||
"title": "On-time 30d",
|
||
"description": "Take all medications on time for 30 consecutive days."
|
||
},
|
||
"compliance8030": {
|
||
"title": "80% adherence · 30d",
|
||
"description": "Reach at least 80% 30-day adherence for 30 consecutive days."
|
||
},
|
||
"bmiGreen30": {
|
||
"title": "BMI green · 30d",
|
||
"description": "Keep your BMI in the normal range for 30 consecutive days."
|
||
},
|
||
"bpGreen30": {
|
||
"title": "BP green · 30d",
|
||
"description": "Keep your blood pressure in the normal range for 30 consecutive days."
|
||
},
|
||
"pulseGreen30": {
|
||
"title": "Pulse green · 30d",
|
||
"description": "Keep your resting pulse in the normal range for 30 consecutive days."
|
||
},
|
||
"onTimePerfect180": {
|
||
"title": "On-time 180d",
|
||
"description": "Take all medications on time for 180 consecutive days."
|
||
},
|
||
"compliance80180": {
|
||
"title": "80% adherence · 180d",
|
||
"description": "Reach at least 80% 30-day adherence for 180 consecutive days."
|
||
},
|
||
"bmiGreen180": {
|
||
"title": "BMI green · 180d",
|
||
"description": "Keep your BMI in the normal range for 180 consecutive days."
|
||
},
|
||
"bpGreen180": {
|
||
"title": "BP green · 180d",
|
||
"description": "Keep your blood pressure in the normal range for 180 consecutive days."
|
||
},
|
||
"pulseGreen180": {
|
||
"title": "Pulse green · 180d",
|
||
"description": "Keep your resting pulse in the normal range for 180 consecutive days."
|
||
},
|
||
"onTimePerfect360": {
|
||
"title": "On-time 360d",
|
||
"description": "Take all medications on time for 360 consecutive days."
|
||
},
|
||
"compliance80360": {
|
||
"title": "80% adherence · 360d",
|
||
"description": "Reach at least 80% 30-day adherence for 360 consecutive days."
|
||
},
|
||
"bmiGreen360": {
|
||
"title": "BMI green · 360d",
|
||
"description": "Keep your BMI in the normal range for 360 consecutive days."
|
||
},
|
||
"bpGreen360": {
|
||
"title": "BP green · 360d",
|
||
"description": "Keep your blood pressure in the normal range for 360 consecutive days."
|
||
},
|
||
"pulseGreen360": {
|
||
"title": "Pulse green · 360d",
|
||
"description": "Keep your resting pulse in the normal range for 360 consecutive days."
|
||
}
|
||
},
|
||
"nextProgressLabel": "Progress to unlock",
|
||
"completedOn": "Completed on {date}"
|
||
},
|
||
"bugreport": {
|
||
"title": "Bug Report",
|
||
"subtitle": "Send a bug report, feature request, or question. The server administrator will see your submission.",
|
||
"loginRequired": "Please sign in to report a bug.",
|
||
"bugTitle": "Title",
|
||
"description": "Description",
|
||
"descriptionPlaceholder": "Describe the bug in detail. What did you do? What happened? What did you expect?",
|
||
"screenshot": "Screenshot (optional)",
|
||
"screenshotPreview": "Screenshot preview",
|
||
"attachScreenshot": "Attach screenshot",
|
||
"imageOnly": "Only image files allowed",
|
||
"tooLarge": "Screenshot is too large (max 5 MB)",
|
||
"submit": "Send",
|
||
"success": "Thank you! Your bug report has been submitted successfully.",
|
||
"viewOnGithub": "View on GitHub",
|
||
"errorCreating": "Error creating report",
|
||
"notConfiguredTitle": "Bug reports not available yet",
|
||
"notConfiguredUser": "Bug reporting is disabled on this server. Please contact your administrator.",
|
||
"notConfiguredAdmin": "Bug reporting requires a GitHub integration (issue token + repository). Configure it in the admin panel under Settings → Bug reports.",
|
||
"openAdminSettings": "Open admin settings",
|
||
"category": "Type",
|
||
"categoryBug": "Bug report",
|
||
"categoryFeature": "Feature request",
|
||
"categoryQuestion": "Question",
|
||
"categoryOther": "Other feedback",
|
||
"bugTitlePlaceholder": "Short summary (optional)",
|
||
"githubEscalationNote": "Your feedback is stored locally. If the admin enables GitHub integration, it can be promoted to a public issue on request.",
|
||
"internalOnlyNote": "Your feedback is stored locally and only visible to the administrator of this server."
|
||
},
|
||
"notifications": {
|
||
"title": "Notifications",
|
||
"subtitle": "Choose which notifications you want to receive through which channel.",
|
||
"noChannels": "Configure a notification channel in Settings first.",
|
||
"goToSettings": "Go to Settings",
|
||
"channelDisabled": "Disabled",
|
||
"eventMedicationReminder": "Medication Reminder",
|
||
"eventMedicationReminderDesc": "Reminder when an intake is due.",
|
||
"eventMeasurementAnomaly": "Anomalous Measurement",
|
||
"eventMeasurementAnomalyDesc": "Warning for unusual measurements (e.g., high blood pressure).",
|
||
"eventComplianceLow": "Low Compliance",
|
||
"eventComplianceLowDesc": "Notification when medication compliance drops below a threshold.",
|
||
"eventWithingsSyncFailed": "Withings Sync Failed",
|
||
"eventWithingsSyncFailedDesc": "Alert when the automatic Withings data sync fails.",
|
||
"eventSystemAlert": "System Alert",
|
||
"eventSystemAlertDesc": "Important system messages and administrative notices."
|
||
},
|
||
"format": {
|
||
"dateShort": "{month}/{day}/{year}",
|
||
"timeShort": "{hours}:{minutes}",
|
||
"dateTime": "{month}/{day}/{year}, {hours}:{minutes}"
|
||
},
|
||
"classifications": {
|
||
"bmi": {
|
||
"Underweight": "Underweight",
|
||
"Normal": "Normal",
|
||
"Overweight": "Overweight",
|
||
"ObesityGradeI": "Obesity Grade I",
|
||
"ObesityGradeII": "Obesity Grade II",
|
||
"ObesityGradeIII": "Obesity Grade III"
|
||
},
|
||
"bp": {
|
||
"Optimal": "Optimal",
|
||
"Normal": "Normal",
|
||
"HighNormal": "High-normal",
|
||
"HypertensionGrade1": "Hypertension Grade 1",
|
||
"HypertensionGrade2": "Hypertension Grade 2",
|
||
"HypertensionGrade3": "Hypertension Grade 3"
|
||
},
|
||
"pulse": {
|
||
"Bradycardia": "Bradycardia",
|
||
"Normal": "Normal",
|
||
"Elevated": "Elevated",
|
||
"Tachycardia": "Tachycardia"
|
||
},
|
||
"pulseTarget": {
|
||
"SignificantlyLow": "Significantly low",
|
||
"SlightlyLow": "Slightly low",
|
||
"OnTarget": "On target",
|
||
"SlightlyElevated": "Slightly elevated",
|
||
"SignificantlyElevated": "Significantly elevated"
|
||
},
|
||
"sleep": {
|
||
"FarTooShort": "Far too short",
|
||
"TooShort": "Too short",
|
||
"OnTarget": "On target",
|
||
"SlightlyLong": "Slightly long",
|
||
"FarTooLong": "Far too long"
|
||
},
|
||
"bodyFat": {
|
||
"Essential": "Essential",
|
||
"Athletic": "Athletic",
|
||
"Fitness": "Fitness",
|
||
"Average": "Average",
|
||
"Obese": "Obese"
|
||
},
|
||
"steps": {
|
||
"VeryLow": "Very low",
|
||
"LowActive": "Low active",
|
||
"ModeratelyActive": "Moderately active",
|
||
"Active": "Active",
|
||
"VeryActive": "Very active"
|
||
},
|
||
"general": {
|
||
"Good": "Good",
|
||
"Moderate": "Moderate",
|
||
"Low": "Low",
|
||
"VeryGood": "Very good",
|
||
"VeryStable": "Very stable",
|
||
"Stable": "Stable",
|
||
"Fluctuating": "Fluctuating"
|
||
},
|
||
"alerts": {
|
||
"bmiBelowNormalTitle": "BMI below normal range",
|
||
"bmiBelowNormalMessage": "Your BMI of {bmi} is below the normal range (18.5–24.9).",
|
||
"bmiOverweightTitle": "BMI in overweight range",
|
||
"bmiOverweightMessage": "Your BMI of {bmi} is in the overweight range. Regular exercise and a balanced diet can help.",
|
||
"bmiObesityTitle": "BMI in obesity range",
|
||
"bmiObesityMessage": "Your BMI of {bmi} is in the obesity range.",
|
||
"bmiNormalTitle": "BMI in normal range",
|
||
"bmiNormalMessage": "Your BMI of {bmi} is in the healthy range.",
|
||
"bpDangerTitle": "Blood pressure significantly elevated",
|
||
"bpDangerMessage": "Your average blood pressure ({sys}/{dia}) is classified as \"{category}\".",
|
||
"bpWarningTitle": "Blood pressure slightly elevated",
|
||
"bpWarningMessage": "Your average blood pressure ({sys}/{dia}) is in the \"{category}\" range.",
|
||
"bpTargetGoodTitle": "Good blood pressure target adherence",
|
||
"bpTargetGoodMessage": "{pct}% of your readings are within the target range.",
|
||
"bpTargetBadTitle": "Blood pressure often outside target range",
|
||
"bpTargetBadMessage": "Only {pct}% of your readings are within the target range.",
|
||
"weightIncreasingTitle": "Weight increasing",
|
||
"weightIncreasingMessage": "Your weight is increasing by approx. {change} kg per week over the last 30 days.",
|
||
"weightDecreasingTitle": "Weight decreasing",
|
||
"weightDecreasingMessage": "Your weight is decreasing by approx. {change} kg per week over the last 30 days.",
|
||
"pulseOutliersTitle": "Pulse outliers detected",
|
||
"pulseOutliersMessage": "{count} unusual pulse readings in the last 30 days.",
|
||
"complianceLowTitle": "Low compliance: {name}",
|
||
"complianceLowMessage": "Your 7-day compliance for {name} is only {pct}%. Regular intake is important.",
|
||
"complianceGoodTitle": "Excellent compliance: {name}",
|
||
"complianceGoodMessage": "{pct}% compliance over the last 7 days. Keep it up!"
|
||
}
|
||
},
|
||
"targets": {
|
||
"title": "Targets",
|
||
"introText": "Your current values compared to recommended target ranges. Guideline sources are linked on each card.",
|
||
"weight": "Weight",
|
||
"bloodPressure": "Blood pressure",
|
||
"bloodPressureOnTarget": "Blood pressure on target",
|
||
"restingPulse": "Resting pulse",
|
||
"sleepDuration": "Sleep duration",
|
||
"bmi": "BMI",
|
||
"bodyFat": "Body fat",
|
||
"stepsPerDay": "Steps/day",
|
||
"medicationCompliance": "Medication compliance",
|
||
"mood": "Mood",
|
||
"moodStability": "Mood stability",
|
||
"steps": "steps",
|
||
"average30d": "30-day average:",
|
||
"currentValue": "Current: {value} {unit}",
|
||
"targetRangeValue": "Target: {min}–{max} {unit}",
|
||
"sourceLabel": "Source: {source}",
|
||
"inTarget": "Within target range",
|
||
"aboveTarget": "{delta} {unit} above target",
|
||
"belowTarget": "{delta} {unit} below target",
|
||
"diastolic": "Diastolic",
|
||
"noMeasurementYet": "No measurements yet",
|
||
"noMeasurementData": "No measurement data available. Add some measurements to see your targets.",
|
||
"profileIncomplete": "Profile incomplete",
|
||
"profileIncompleteHeight": "Add your height in profile settings to enable BMI targets.",
|
||
"profileIncompleteAge": "Add your date of birth in profile settings for age-specific targets.",
|
||
"profileIncompleteHeightAge": "Add your height and date of birth in profile settings for personalized targets.",
|
||
"compliancePerMedication": "Compliance per medication",
|
||
"glucoseFasting": "Glucose — fasting",
|
||
"glucosePostprandial": "Glucose — post-meal",
|
||
"glucoseRandom": "Glucose — random",
|
||
"glucoseBedtime": "Glucose — bedtime",
|
||
"loadError": "Failed to load targets"
|
||
},
|
||
"passwordStrength": {
|
||
"veryWeak": "Very weak",
|
||
"weak": "Weak",
|
||
"acceptable": "Acceptable",
|
||
"strong": "Strong",
|
||
"veryStrong": "Very strong",
|
||
"tooShort": "Too short",
|
||
"characters": "characters",
|
||
"minLength": "At least {count} characters required."
|
||
},
|
||
"telegram": {
|
||
"errorInvalidAction": "Invalid action.",
|
||
"errorMedicationNotFound": "Medication not found.",
|
||
"errorMedicationInactive": "Medication not found or inactive.",
|
||
"errorUnknownAction": "Unknown action.",
|
||
"snoozeOneHour": "1 hour",
|
||
"snoozeThreeHours": "3 hours",
|
||
"snoozedFor": "{name} snoozed for {duration}.",
|
||
"skipped": "{name} skipped.",
|
||
"alreadyRecorded": "{name} was already recorded.",
|
||
"recordedAsTaken": "{name} recorded as taken.",
|
||
"confirmed": "{name} confirmed.",
|
||
"genericConfirmed": "Confirmed.",
|
||
"cancelled": "Cancelled.",
|
||
"noActiveMedications": "No active medications found.",
|
||
"whichMedication": "<b>Which medication did you take?</b>",
|
||
"cancelButton": "Cancel",
|
||
"askMedicationName": "Please specify a medication, e.g. <b>taken Ramipril</b>.",
|
||
"medicationNotFoundExact": "Medication not found. Please send the name exactly as in HealthLog.",
|
||
"helpHeader": "<b>Available commands:</b>",
|
||
"helpBody": "/help — show this help\n/add — record an intake\n/start — start the bot\n\n<b>Text commands:</b>\ntaken <name> — confirm an intake\n\n<b>Buttons in reminders:</b>\n• Taken — confirm intake\n• 🕐 1h / 🕐 3h — postpone reminder\n• ⏭ Skip — skip intake\n• ✓ Confirm — confirm a missed intake"
|
||
}
|
||
}
|