refactor(insights): migrate route slugs to english with redirects

Rename every routed Insights sub-page slug from German to English
(`/insights/blutdruck` -> `/insights/blood-pressure`, `gewicht` ->
`weight`, `medikamente` -> `medications`, and the full v1.7.0 metric
tail). UI copy stays localised through `t()` keys; only the internal
slugs change. The slug registry in `src/lib/insights/sub-page-metric.ts`
is the single source of truth, so the tab strip, target summary, and
daily-briefing routing targets follow it.

`next.config.ts` `redirects()` emits a permanent redirect from every
legacy German slug to its English target, including a `:path*` variant
so nested routes survive. Bookmarks and cached PWA navigation keep
resolving.
This commit is contained in:
Marc-André Bombeck committed 2026-05-31 22:12:25 +02:00
1 parent b416cabbf1
commit c4ccd7e297
46 files changed
+244 -180

No files matched your search

+1 -1
View File
@@ -4,7 +4,7 @@
* Returns a chronological merge of every GLP-1-relevant event the user
* has logged: dose changes, injections (with site if recorded),
* inventory events, and side-effect-tagged mood entries from the
* trailing 90 days. Used by /insights/medikamente's TherapyTimeline
* trailing 90 days. Used by /insights/medications' TherapyTimeline
* component. Web-only users without a GLP-1 medication get
* `hasGlp1: false` so the component hides cleanly.
*/
@@ -5,7 +5,7 @@ import { Flame } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.4.32 — `/insights/aktive-energie`.
* v1.4.32 — `/insights/active-energy`.
*
* Active-energy-burned sub-page. The MeasurementType is cumulative
* (kcal per day, per R-A Option A); the v1.4.30 server-side daily-
@@ -5,7 +5,7 @@ import { Volume2 } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.7.0 — `/insights/laermereignisse`.
* v1.7.0 — `/insights/audio-events`.
*
* AUDIO_EXPOSURE_EVENT sub-page. Reuses the generic HealthKitMetricPage scaffold;
* the chart daily-aggregates through the rollup read path. Empty-state
@@ -5,7 +5,7 @@ import { Droplet } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.7.0 — `/insights/blutzucker`.
* v1.7.0 — `/insights/blood-glucose`.
*
* BLOOD_GLUCOSE sub-page. Reuses the generic HealthKitMetricPage scaffold;
* the chart daily-aggregates through the rollup read path. Empty-state
@@ -18,7 +18,7 @@ import { SubPageShell } from "@/components/insights/sub-page-shell";
import { getBpTargets } from "@/lib/analytics/bp-targets";
/**
* v1.4.25 W4 — `/insights/blutdruck`.
* v1.4.25 W4 — `/insights/blood-pressure`.
*
* Routed Blood-Pressure sub-page. The mother page hosts the overview
* (hero + briefing + advisor); this page concentrates on BP-only depth:
@@ -114,7 +114,7 @@ export default function InsightsBlutdruckPage() {
userTimezone={user?.timezone}
/>
<MetricTargetSummary slug="blutdruck" />
<MetricTargetSummary slug="blood-pressure" />
<InsightStatusCard
title={t("insights.assessmentTitle")}
@@ -5,7 +5,7 @@ import { Thermometer } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.4.32 — `/insights/koerpertemperatur`.
* v1.4.32 — `/insights/body-temperature`.
*
* Body-temperature sub-page. Canonical storage is Celsius. Distinct
* from skin temperature (Withings ScanWatch dermal reading) — surface
@@ -5,7 +5,7 @@ import { Droplet } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.7.0 — `/insights/koerperwasser`.
* v1.7.0 — `/insights/body-water`.
*
* TOTAL_BODY_WATER sub-page. Reuses the generic HealthKitMetricPage scaffold;
* the chart daily-aggregates through the rollup read path. Empty-state
@@ -5,7 +5,7 @@ import { Bone } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.7.0 — `/insights/knochenmasse`.
* v1.7.0 — `/insights/bone-mass`.
*
* BONE_MASS sub-page. Reuses the generic HealthKitMetricPage scaffold;
* the chart daily-aggregates through the rollup read path. Empty-state
@@ -5,7 +5,7 @@ import { Sun } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.7.0 — `/insights/tageslicht`.
* v1.7.0 — `/insights/daylight`.
*
* TIME_IN_DAYLIGHT sub-page. Reuses the generic HealthKitMetricPage scaffold;
* the chart daily-aggregates through the rollup read path. Empty-state
@@ -5,7 +5,7 @@ import { Footprints } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.7.0 — `/insights/doppelstandphase`.
* v1.7.0 — `/insights/double-support-time`.
*
* WALKING_DOUBLE_SUPPORT sub-page. Reuses the generic HealthKitMetricPage scaffold;
* the chart daily-aggregates through the rollup read path. Empty-state
@@ -5,7 +5,7 @@ import { Volume2 } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.7.0 — `/insights/laermbelastung`.
* v1.7.0 — `/insights/environmental-audio`.
*
* AUDIO_EXPOSURE_ENV sub-page. Reuses the generic HealthKitMetricPage scaffold;
* the chart daily-aggregates through the rollup read path. Empty-state
@@ -5,7 +5,7 @@ import { Scale } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.7.0 — `/insights/fettfreie-masse`.
* v1.7.0 — `/insights/fat-free-mass`.
*
* FAT_FREE_MASS sub-page. Reuses the generic HealthKitMetricPage scaffold;
* the chart daily-aggregates through the rollup read path. Empty-state
@@ -5,7 +5,7 @@ import { Droplets } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.7.0 — `/insights/fettmasse`.
* v1.7.0 — `/insights/fat-mass`.
*
* FAT_MASS sub-page. Reuses the generic HealthKitMetricPage scaffold;
* the chart daily-aggregates through the rollup read path. Empty-state
@@ -5,7 +5,7 @@ import { TrendingUp } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.7.0 — `/insights/stockwerke`.
* v1.7.0 — `/insights/flights-climbed`.
*
* FLIGHTS_CLIMBED sub-page. Reuses the generic HealthKitMetricPage scaffold;
* the chart daily-aggregates through the rollup read path. Empty-state
@@ -5,7 +5,7 @@ import { Headphones } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.7.0 — `/insights/kopfhoererpegel`.
* v1.7.0 — `/insights/headphone-audio`.
*
* AUDIO_EXPOSURE_HEADPHONE sub-page. Reuses the generic HealthKitMetricPage scaffold;
* the chart daily-aggregates through the rollup read path. Empty-state
@@ -5,7 +5,7 @@ import { Scale } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.7.0 — `/insights/magermasse`.
* v1.7.0 — `/insights/lean-body-mass`.
*
* LEAN_BODY_MASS sub-page. Reuses the generic HealthKitMetricPage scaffold;
* the chart daily-aggregates through the rollup read path. Empty-state
@@ -21,7 +21,7 @@ import { TherapyTimeline } from "@/components/insights/therapy-timeline";
import { MedicationCardHeader } from "@/components/medications/MedicationCardHeader";
/**
* v1.4.25 W4 — `/insights/medikamente`.
* v1.4.25 W4 — `/insights/medications`.
*
* Routed Medication-Compliance sub-page. Mirrors the per-medication
* grid from the v1.4.24 mother page (compliance bars + heatmap +
@@ -166,7 +166,7 @@ export default function InsightsMedikamentePage() {
{medications.map((med) => {
const medicationSummary = medicationSummaryById.get(med.id);
// UI-H3 — route every medication-list row (medications page
// + insights/medikamente) through `<MedicationCardHeader>`
// + insights/medications) through `<MedicationCardHeader>`
// so the surface stays one shape: `{name} {dose}` on line 1
// + outline category badge on line 2. The streak chip rides
// the `stateBadges` slot so a strong streak still surfaces.
@@ -243,7 +243,7 @@ export default function InsightsMedikamentePage() {
})}
</div>
<MetricTargetSummary slug="medikamente" />
<MetricTargetSummary slug="medications" />
<InsightStatusCard
title={t("insights.assessmentTitle")}
@@ -19,7 +19,7 @@ import { MetricTargetSummary } from "@/components/insights/metric-target-summary
import { SubPageShell } from "@/components/insights/sub-page-shell";
/**
* v1.4.25 W4 — `/insights/stimmung`.
* v1.4.25 W4 — `/insights/mood`.
*
* Routed Mood sub-page. Unlike the mother page (which hid the mood
* section entirely when no mood data existed), a dedicated sub-page
@@ -105,7 +105,7 @@ export default function InsightsStimmungPage() {
userTimezone={user?.timezone}
/>
<MetricTargetSummary slug="stimmung" />
<MetricTargetSummary slug="mood" />
<InsightStatusCard
title={t("insights.assessmentTitle")}
@@ -5,7 +5,7 @@ import { Dumbbell } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.7.0 — `/insights/muskelmasse`.
* v1.7.0 — `/insights/muscle-mass`.
*
* MUSCLE_MASS sub-page. Reuses the generic HealthKitMetricPage scaffold;
* the chart daily-aggregates through the rollup read path. Empty-state
@@ -5,7 +5,7 @@ import { Wind } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.4.32 — `/insights/sauerstoff`.
* v1.4.32 — `/insights/oxygen`.
*
* Oxygen-saturation (SpO₂) sub-page. Canonical storage is percent
* (0..100); the chart axis runs 85..100 because clinical normal is
@@ -5,7 +5,7 @@ import { Activity } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.7.0 — `/insights/pulswellengeschwindigkeit`.
* v1.7.0 — `/insights/pulse-wave-velocity`.
*
* PULSE_WAVE_VELOCITY sub-page. Reuses the generic HealthKitMetricPage scaffold;
* the chart daily-aggregates through the rollup read path. Empty-state
@@ -22,7 +22,7 @@ import {
} from "@/lib/analytics/pulse-targets";
/**
* v1.4.25 W4 — `/insights/puls`.
* v1.4.25 W4 — `/insights/pulse`.
*
* Routed Pulse sub-page. Renders the pulse chart with the personalized
* Karvonen-derived target band plus the per-section AI assessment.
@@ -126,7 +126,7 @@ export default function InsightsPulsPage() {
userTimezone={user?.timezone}
/>
<MetricTargetSummary slug="puls" />
<MetricTargetSummary slug="pulse" />
<InsightStatusCard
title={t("insights.assessmentTitle")}
@@ -5,7 +5,7 @@ import { Wind } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.7.0 — `/insights/atemfrequenz`.
* v1.7.0 — `/insights/respiratory-rate`.
*
* RESPIRATORY_RATE sub-page. Reuses the generic HealthKitMetricPage scaffold;
* the chart daily-aggregates through the rollup read path. Empty-state
@@ -5,9 +5,9 @@ import { Heart } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.4.32 — `/insights/ruhepuls`.
* v1.4.32 — `/insights/resting-pulse`.
*
* Resting-heart-rate sub-page. Distinct from `/insights/puls` — Apple
* Resting-heart-rate sub-page. Distinct from `/insights/pulse` — Apple
* Health splits the steady-state daily `restingHeartRate` value from
* the spot `PULSE` samples logged manually or by Withings. The split
* here mirrors that distinction so the user has a "daily floor"
@@ -5,7 +5,7 @@ import { Thermometer } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.7.0 — `/insights/hauttemperatur`.
* v1.7.0 — `/insights/skin-temperature`.
*
* SKIN_TEMPERATURE sub-page. Reuses the generic HealthKitMetricPage scaffold;
* the chart daily-aggregates through the rollup read path. Empty-state
@@ -13,7 +13,7 @@ import { SleepOverview } from "@/components/insights/sleep-overview";
import { SubPageShell } from "@/components/insights/sub-page-shell";
/**
* v1.4.25 W4c — `/insights/schlaf`.
* v1.4.25 W4c — `/insights/sleep`.
*
* The Sleep sub-page surfaces the per-stage breakdown + duration trend
* the v1.4.23 schema gained but never rendered. All charts live inside
@@ -65,7 +65,7 @@ export default function InsightsSchlafPage() {
>
<SleepOverview />
<MetricTargetSummary slug="schlaf" />
<MetricTargetSummary slug="sleep" />
{/*
v1.4.28 (BK-UI-StatusSchlaf) — the six sibling sub-pages all
@@ -5,7 +5,7 @@ import { Footprints } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.7.0 — `/insights/schrittlaenge`.
* v1.7.0 — `/insights/step-length`.
*
* WALKING_STEP_LENGTH sub-page. Reuses the generic HealthKitMetricPage scaffold;
* the chart daily-aggregates through the rollup read path. Empty-state
@@ -5,7 +5,7 @@ import { HeartPulse } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.7.0 — `/insights/gefaessalter`.
* v1.7.0 — `/insights/vascular-age`.
*
* VASCULAR_AGE sub-page. Reuses the generic HealthKitMetricPage scaffold;
* the chart daily-aggregates through the rollup read path. Empty-state
@@ -5,7 +5,7 @@ import { Droplets } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.7.0 — `/insights/viszeralfett`.
* v1.7.0 — `/insights/visceral-fat`.
*
* VISCERAL_FAT sub-page. Reuses the generic HealthKitMetricPage scaffold;
* the chart daily-aggregates through the rollup read path. Empty-state
@@ -5,7 +5,7 @@ import { Footprints } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.7.0 — `/insights/gangasymmetrie`.
* v1.7.0 — `/insights/walking-asymmetry`.
*
* WALKING_ASYMMETRY sub-page. Reuses the generic HealthKitMetricPage scaffold;
* the chart daily-aggregates through the rollup read path. Empty-state
@@ -5,7 +5,7 @@ import { Footprints } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.7.0 — `/insights/gehstrecke`.
* v1.7.0 — `/insights/walking-distance`.
*
* WALKING_RUNNING_DISTANCE sub-page. Reuses the generic HealthKitMetricPage scaffold;
* the chart daily-aggregates through the rollup read path. Empty-state
@@ -5,7 +5,7 @@ import { HeartPulse } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.7.0 — `/insights/gehpuls`.
* v1.7.0 — `/insights/walking-heart-rate`.
*
* WALKING_HEART_RATE_AVERAGE sub-page. Reuses the generic HealthKitMetricPage scaffold;
* the chart daily-aggregates through the rollup read path. Empty-state
@@ -5,7 +5,7 @@ import { Gauge } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.7.0 — `/insights/gehgeschwindigkeit`.
* v1.7.0 — `/insights/walking-speed`.
*
* WALKING_SPEED sub-page. Reuses the generic HealthKitMetricPage scaffold;
* the chart daily-aggregates through the rollup read path. Empty-state
@@ -5,7 +5,7 @@ import { Gauge } from "lucide-react";
import { HealthKitMetricPage } from "@/components/insights/healthkit-metric-page";
/**
* v1.7.0 — `/insights/gangstabilitaet`.
* v1.7.0 — `/insights/walking-steadiness`.
*
* WALKING_STEADINESS sub-page. Reuses the generic HealthKitMetricPage scaffold;
* the chart daily-aggregates through the rollup read path. Empty-state
@@ -18,7 +18,7 @@ import { SubPageShell } from "@/components/insights/sub-page-shell";
import { buildWeightBandsFromHeight } from "@/lib/analytics/value-bands";
/**
* v1.4.25 W4 — `/insights/gewicht`.
* v1.4.25 W4 — `/insights/weight`.
*
* Routed Weight sub-page. Renders the weight chart with the user's
* height-derived green/orange/red bands plus the per-section AI
@@ -87,7 +87,7 @@ export default function InsightsGewichtPage() {
userTimezone={user?.timezone}
/>
<MetricTargetSummary slug="gewicht" />
<MetricTargetSummary slug="weight" />
<InsightStatusCard
title={t("insights.assessmentTitle")}