{locale === "en-US" ? "Model" : "模型"}
diff --git a/frontend/components/dashboard/ScanTerminalDashboard.tsx b/frontend/components/dashboard/ScanTerminalDashboard.tsx
index 8b0da846..759694dc 100644
--- a/frontend/components/dashboard/ScanTerminalDashboard.tsx
+++ b/frontend/components/dashboard/ScanTerminalDashboard.tsx
@@ -43,6 +43,10 @@ import {
getLocalizedAirportName,
getLocalizedCityName,
} from "@/lib/dashboard-home-copy";
+import {
+ formatTemperatureValue,
+ normalizeTemperatureLabel,
+} from "@/lib/dashboard-utils";
const DEFAULT_FILTERS: FilterState = {
scan_mode: "tradable",
@@ -222,6 +226,7 @@ function DetailPanel({
detailSignal && detailSignal.market_slug === row.market_slug
? detailSignal
: row;
+ const tempSymbol = row.temp_symbol || displayRow.temp_symbol || "°C";
const yesRow = getSideRow(marketScan, row, "yes");
const noRow = getSideRow(marketScan, row, "no");
const comparisonBuckets = buildComparisonBuckets(marketScan, row);
@@ -276,26 +281,33 @@ function DetailPanel({
{isEn ? "Current Temp" : "当前温度"}
- {row.current_temp != null ? `${row.current_temp}${row.temp_symbol || ""}` : "--"}
+ {row.current_temp != null
+ ? formatTemperatureValue(row.current_temp, tempSymbol)
+ : "--"}
{isEn ? "Day High (So Far)" : "今日最高(至今)"}
{row.current_max_so_far != null
- ? `${row.current_max_so_far}${row.temp_symbol || ""}`
+ ? formatTemperatureValue(row.current_max_so_far, tempSymbol)
: "--"}
{isEn ? "Target" : "目标温度"}
- {displayRow.target_label || "--"}
+
+ {normalizeTemperatureLabel(displayRow.target_label, tempSymbol) || "--"}
+
{isEn ? "Gap To Target" : "距离目标"}
{displayRow.gap_to_target != null
- ? `${displayRow.gap_to_target >= 0 ? "+" : ""}${displayRow.gap_to_target.toFixed(1)}${row.temp_symbol || ""}`
+ ? formatTemperatureValue(displayRow.gap_to_target, tempSymbol, {
+ signed: true,
+ digits: 1,
+ })
: "--"}
diff --git a/frontend/lib/dashboard-utils.ts b/frontend/lib/dashboard-utils.ts
index 8848b95e..535730ce 100644
--- a/frontend/lib/dashboard-utils.ts
+++ b/frontend/lib/dashboard-utils.ts
@@ -45,6 +45,42 @@ function containsCjk(text: string) {
return /[\u3400-\u9fff]/.test(text);
}
+export function normalizeTemperatureSymbol(value?: string | null) {
+ return String(value || "").toUpperCase().includes("F") ? "°F" : "°C";
+}
+
+export function formatTemperatureValue(
+ value: number | null | undefined,
+ symbol?: string | null,
+ options?: { signed?: boolean; digits?: number },
+) {
+ const numeric = Number(value);
+ if (!Number.isFinite(numeric)) return "--";
+ const digits = options?.digits ?? 0;
+ const sign = options?.signed && numeric >= 0 ? "+" : "";
+ return `${sign}${numeric.toFixed(digits)}${normalizeTemperatureSymbol(symbol)}`;
+}
+
+export function normalizeTemperatureLabel(
+ label?: string | null,
+ fallbackSymbol?: string | null,
+) {
+ const raw = String(label || "").trim();
+ if (!raw) return "";
+ const normalizedSymbol = normalizeTemperatureSymbol(fallbackSymbol);
+ let next = raw.replace(/℃/gi, "°C");
+ next = next.replace(/°?([CF])\b/gi, (_, unit: string) => `°${unit.toUpperCase()}`);
+ if (!/[°][CF]/.test(next) && /\d/.test(next)) {
+ next = `${next}${normalizedSymbol}`;
+ }
+ if (normalizedSymbol === "°F") {
+ next = next.replace(/°C/g, "°F");
+ } else {
+ next = next.replace(/°F/g, "°C");
+ }
+ return next;
+}
+
function getLocalizedDynamicCommentary(
detail: CityDetail,
locale: Locale,