diff --git a/frontend/components/dashboard/scan-terminal/TemperatureStatsBars.tsx b/frontend/components/dashboard/scan-terminal/TemperatureStatsBars.tsx index 153f287d..37f3e4f7 100644 --- a/frontend/components/dashboard/scan-terminal/TemperatureStatsBars.tsx +++ b/frontend/components/dashboard/scan-terminal/TemperatureStatsBars.tsx @@ -3,6 +3,68 @@ import clsx from "clsx"; import { temp } from "@/components/dashboard/scan-terminal/utils"; +const OBSERVATION_LABEL_EN: Record = { + "参考站点 (1分钟)": "Reference Station (1m)", + "天文台实测 (10分钟)": "HKO Live (10m)", + "机场气象站 (10分钟)": "Airport Weather Station (10m)", + "航站楼温度": "Terminal Temperature", + "官方机场观测 (15分钟)": "Official Airport Obs (15m)", + "CWA (10分钟)": "CWA (10m)", + "气象站实测": "Weather Station Live", + "跑道实测 (1分钟)": "Runway Live (1m)", + "机场报文": "Airport METAR", + "METAR 结算 (30分钟)": "METAR Settlement (30m)", +}; + +const HIGH_LABEL_EN: Record = { + "参考站点": "Reference Station", + "天文台实测": "HKO Live", + "天文台": "HKO", + "机场气象站": "Airport Weather Station", + "航站楼": "Terminal", + "官方机场观测": "Official Airport Obs", + "气象站": "Weather Station", + "跑道实测": "Runway", + "机场报文": "Airport METAR", + "METAR 官方": "Official METAR", +}; + +function observationLabel(label: string, isEn: boolean) { + return isEn ? (OBSERVATION_LABEL_EN[label] || label) : label; +} + +function highLabel(label: string, isEn: boolean) { + return isEn ? (HIGH_LABEL_EN[label] || label) : label; +} + +function buildStatsLabels({ + isEn, + isShenzhen, + runwayHeaderLabel, + metarHeaderLabel, + runwayHighLabel, + metarHighLabel, +}: { + isEn: boolean; + isShenzhen: boolean; + runwayHeaderLabel: string; + metarHeaderLabel: string; + runwayHighLabel: string; + metarHighLabel: string; +}) { + const primary = observationLabel(runwayHeaderLabel, isEn); + const secondaryObservation = observationLabel(metarHeaderLabel, isEn); + const dailyHigh = isEn ? "Daily High" : "当日最高"; + return { + primary, + compactSecondary: isShenzhen ? dailyHigh : secondaryObservation, + expandedSecondary: `${secondaryObservation} · ${dailyHigh}`, + dailyPeakTitle: isEn ? "Daily Peak" : "当日最高气温", + runwayHigh: highLabel(runwayHighLabel, isEn), + metarHigh: highLabel(metarHighLabel, isEn), + }; +} + export function TemperatureStatsBars({ isEn, compact, @@ -46,18 +108,27 @@ export function TemperatureStatsBars({ spreadLabelEn: string; formattedUpdateTime: string; }) { + const labels = buildStatsLabels({ + isEn, + isShenzhen, + runwayHeaderLabel, + metarHeaderLabel, + runwayHighLabel, + metarHighLabel, + }); + if (compact) { return (
{timeframe === "1D" ? (
- {isEn ? "Runway" : runwayHeaderLabel}:{" "} + {labels.primary}:{" "} {temp(displayRunwayTemp, tempSymbol)} | - {isEn ? "METAR" : (isShenzhen ? "当日最高" : metarHeaderLabel)}:{" "} + {labels.compactSecondary}:{" "} {temp(observedHighMetar, tempSymbol)}
@@ -93,7 +164,7 @@ export function TemperatureStatsBars({
- {isEn ? "Runway Live (1m)" : `${runwayHeaderLabel}`} + {labels.primary} {temp(displayRunwayTemp, tempSymbol)} @@ -101,7 +172,7 @@ export function TemperatureStatsBars({
- {isEn ? "METAR Settlement · Daily High" : `${metarHeaderLabel} · 当日最高`} + {labels.expandedSecondary} {temp(observedHighMetar, tempSymbol)} @@ -131,12 +202,12 @@ export function TemperatureStatsBars({
- {isEn ? "Daily Peak" : "当日最高气温"} + {labels.dailyPeakTitle}
- {isEn ? "Runway" : runwayHighLabel}: {temp(observedHighRunway, tempSymbol)} + {labels.runwayHigh}: {temp(observedHighRunway, tempSymbol)} | - {isEn ? "METAR" : metarHighLabel}: {temp(observedHighMetar, tempSymbol)} + {labels.metarHigh}: {temp(observedHighMetar, tempSymbol)} {wundergroundDailyHigh !== null && ( <> | @@ -187,3 +258,5 @@ export function TemperatureStatsBars({
); } + +export const __buildTemperatureStatsLabelsForTest = buildStatsLabels; diff --git a/frontend/components/dashboard/scan-terminal/__tests__/temperatureStatsLabels.test.ts b/frontend/components/dashboard/scan-terminal/__tests__/temperatureStatsLabels.test.ts new file mode 100644 index 00000000..8e4653d5 --- /dev/null +++ b/frontend/components/dashboard/scan-terminal/__tests__/temperatureStatsLabels.test.ts @@ -0,0 +1,49 @@ +import { __buildTemperatureStatsLabelsForTest } from "@/components/dashboard/scan-terminal/TemperatureStatsBars"; + +function assert(condition: unknown, message: string) { + if (!condition) throw new Error(message); +} + +export function runTests() { + const hongKong = __buildTemperatureStatsLabelsForTest({ + isEn: true, + isShenzhen: false, + runwayHeaderLabel: "参考站点 (1分钟)", + metarHeaderLabel: "天文台实测 (10分钟)", + runwayHighLabel: "参考站点", + metarHighLabel: "天文台", + }); + + assert(hongKong.primary === "Reference Station (1m)", "Hong Kong English primary label should match 参考站点 (1分钟)"); + assert(hongKong.compactSecondary === "HKO Live (10m)", "Hong Kong compact secondary label should match 天文台实测 (10分钟)"); + assert(hongKong.expandedSecondary === "HKO Live (10m) · Daily High", "Hong Kong expanded secondary label should include HKO plus Daily High"); + assert(hongKong.runwayHigh === "Reference Station", "Hong Kong high summary should translate 参考站点"); + assert(hongKong.metarHigh === "HKO", "Hong Kong high summary should translate 天文台"); + + const shenzhen = __buildTemperatureStatsLabelsForTest({ + isEn: true, + isShenzhen: true, + runwayHeaderLabel: "天文台实测 (10分钟)", + metarHeaderLabel: "天文台实测 (10分钟)", + runwayHighLabel: "天文台实测", + metarHighLabel: "天文台", + }); + + assert(shenzhen.primary === "HKO Live (10m)", "Shenzhen English primary label should match 天文台实测 (10分钟)"); + assert(shenzhen.compactSecondary === "Daily High", "Shenzhen compact secondary label should match 当日最高"); + assert(shenzhen.expandedSecondary === "HKO Live (10m) · Daily High", "Shenzhen expanded secondary label should match 天文台实测 + 当日最高"); + assert(shenzhen.runwayHigh === "HKO Live", "Shenzhen high summary should translate 天文台实测"); + assert(shenzhen.metarHigh === "HKO", "Shenzhen high summary should translate 天文台"); + + const zh = __buildTemperatureStatsLabelsForTest({ + isEn: false, + isShenzhen: true, + runwayHeaderLabel: "天文台实测 (10分钟)", + metarHeaderLabel: "天文台实测 (10分钟)", + runwayHighLabel: "天文台实测", + metarHighLabel: "天文台", + }); + + assert(zh.primary === "天文台实测 (10分钟)", "Chinese primary label should remain unchanged"); + assert(zh.compactSecondary === "当日最高", "Chinese Shenzhen compact secondary label should remain 当日最高"); +}