mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
fix(insights): localise the Coach scope pill and clarify the return-to-baseline copy
The "the Coach is already on …" pill resolved its metric label through the brand-free English domain vocabulary, so it read in English on every non-English locale. It now prefers the existing localised measurements.type* name for each measurement-backed source, keeping the English phrase only for the gait/audio long tail that rarely becomes a scoped launch. Also rewords the English return-to-baseline line so the day count reads as the in-range run length rather than the time since.
This commit is contained in:
1 parent
388c8b23dd
commit
1de72d045e
3 files changed
+49
-5
No files matched your search
+1
-1
@@ -3600,7 +3600,7 @@
|
||||
}
|
||||
},
|
||||
"streak": {
|
||||
"returnLabel": "Your {metric} is back inside your usual range after {count} days — whatever that was, it's passed.",
|
||||
"returnLabel": "Your {metric} has been back inside your usual range for {count} days now — whatever that was, it's passed.",
|
||||
"daysInside": "{count} days inside",
|
||||
"daysOutside": "{count} days outside"
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ export function scopeSourceFromMetricKey(
|
||||
}
|
||||
|
||||
/**
|
||||
* v1.22.0 (A2) — the human label for a launch scope's primary metric, for
|
||||
* v1.21.2 (A2) — the human label for a launch scope's primary metric, for
|
||||
* the VISIBLE "the Coach is already on …" pill. The caller resolves the
|
||||
* i18n string via `t("insights.coach.scope.metric.<source>")`; this helper
|
||||
* supplies the English domain phrase as the deterministic fallback for any
|
||||
@@ -182,3 +182,42 @@ export function metricScopeLabelFallback(
|
||||
if (!metric) return null;
|
||||
return COACH_SOURCE_DOMAIN_LABEL[metric] ?? (metric as string);
|
||||
}
|
||||
|
||||
/**
|
||||
* v1.21.2 (A2) — map a launch-scope source onto the EXISTING localised
|
||||
* `measurements.type*` metric-name key. The scope pill reads in the user's
|
||||
* own language for every measurement-backed source, instead of the English
|
||||
* `COACH_SOURCE_DOMAIN_LABEL` fallback that only carries the brand-free
|
||||
* domain phrase. Sources without a measurement name (mood, the gait / audio
|
||||
* long tail) keep that English fallback — they almost never become a scoped
|
||||
* launch. Returns the key, or null when the source has no localised name.
|
||||
*/
|
||||
const SCOPE_SOURCE_METRIC_LABEL_KEY: Partial<Record<CoachScopeSource, string>> =
|
||||
{
|
||||
bp: "measurements.typeBloodPressure",
|
||||
weight: "measurements.typeWeight",
|
||||
pulse: "measurements.typePulse",
|
||||
hrv: "measurements.typeHeartRateVariability",
|
||||
resting_hr: "measurements.typeRestingHeartRate",
|
||||
respiratory_rate: "measurements.typeRespiratoryRate",
|
||||
spo2: "measurements.typeOxygenSaturation",
|
||||
bmi: "measurements.typeBodyMassIndex",
|
||||
body_temp: "measurements.typeBodyTemperature",
|
||||
vo2_max: "measurements.typeVo2Max",
|
||||
sleep: "measurements.typeSleep",
|
||||
body_fat: "measurements.typeBodyFat",
|
||||
fat_mass: "measurements.typeFatMass",
|
||||
fat_free_mass: "measurements.typeFatFreeMass",
|
||||
muscle_mass: "measurements.typeMuscleMass",
|
||||
lean_body_mass: "measurements.typeLeanBodyMass",
|
||||
bone_mass: "measurements.typeBoneMass",
|
||||
active_energy: "measurements.typeActiveEnergyBurned",
|
||||
flights: "measurements.typeFlightsClimbed",
|
||||
};
|
||||
|
||||
export function scopeSourceMetricLabelKey(
|
||||
metric: CoachScopeSource | undefined | null,
|
||||
): string | null {
|
||||
if (!metric) return null;
|
||||
return SCOPE_SOURCE_METRIC_LABEL_KEY[metric] ?? null;
|
||||
}
|
||||
@@ -20,7 +20,10 @@ import { apiDelete, apiGet } from "@/lib/api/api-fetch";
|
||||
import type { CoachScope } from "@/lib/ai/coach/types";
|
||||
import type { CoachLaunchScope } from "@/lib/insights/coach-launch-context";
|
||||
import type { CoachSeededQuestionDTO } from "@/app/api/insights/coach/seeded-question/route";
|
||||
import { metricScopeLabelFallback } from "@/components/insights/coach-metric-scope";
|
||||
import {
|
||||
metricScopeLabelFallback,
|
||||
scopeSourceMetricLabelKey,
|
||||
} from "@/components/insights/coach-metric-scope";
|
||||
|
||||
import { CoachDrawerBody } from "./coach-drawer-body";
|
||||
import { CoachHero } from "./coach-hero";
|
||||
@@ -444,7 +447,7 @@ export function CoachConversation({
|
||||
pendingQuestions.length === 0 &&
|
||||
!pendingAdopt;
|
||||
|
||||
// v1.22.0 (A2 + A3) — the visible scope/opener affordance for the hero.
|
||||
// v1.21.2 (A2 + A3) — the visible scope/opener affordance for the hero.
|
||||
//
|
||||
// A2 (scoped launch): the Coach was opened narrowed to a metric. Make it
|
||||
// visible — a "the Coach is already on <metric>" pill plus the data-aware
|
||||
@@ -482,8 +485,10 @@ export function CoachConversation({
|
||||
// to the brand-free English domain phrase so every source has a label.
|
||||
// Hoisted so both the hero hint (page) and the sources rail (drawer) read
|
||||
// the same string. Null when the launch carried no metric.
|
||||
const a2LabelKey = scopeSourceMetricLabelKey(a2Metric);
|
||||
const a2MetricLabel = a2Metric
|
||||
? (tOrNull(`insights.coach.scope.metric.${a2Metric}`) ??
|
||||
(a2LabelKey ? tOrNull(a2LabelKey) : null) ??
|
||||
metricScopeLabelFallback(a2Metric) ??
|
||||
a2Metric)
|
||||
: null;
|
||||
@@ -787,7 +792,7 @@ export function CoachConversation({
|
||||
onSourcesOpenChange={setSourcesTrayOpen}
|
||||
sourcesRail={
|
||||
<SourcesRail
|
||||
// v1.22.0 (A2) — surface the launch scope on the drawer surface;
|
||||
// v1.21.2 (A2) — surface the launch scope on the drawer surface;
|
||||
// only set for a fresh, scoped, not-yet-sent conversation so a
|
||||
// continued thread (its own established scope) shows no stale line.
|
||||
activeScopeLabel={
|
||||
|
||||
Reference in new issue
Block a user