diff --git a/src/features/online-board/components/OnlineBoardSearchPage.tsx b/src/features/online-board/components/OnlineBoardSearchPage.tsx index 1c7d7c74..8784d092 100644 --- a/src/features/online-board/components/OnlineBoardSearchPage.tsx +++ b/src/features/online-board/components/OnlineBoardSearchPage.tsx @@ -185,27 +185,40 @@ export const OnlineBoardSearchPage: FC = ({ const lang = routeParams.lang ?? "ru"; const { dictionaries } = useDictionaries(lang); - // Human-readable title/breadcrumb. Angular derives these from the - // station dictionary — e.g. "Маршрут: Шереметьево - Санкт-Петербург". + // Human-readable title/breadcrumb. Angular prefers the city name when a + // code resolves to a city (LED → 'Санкт-Петербург'); falls back to the + // airport name only for codes that aren't city codes (SVO → 'Шереметьево'). const describeStation = (code?: string): string => { if (!code || !dictionaries) return code ?? ""; const upper = code.toUpperCase(); - return ( - dictionaries.airportByCode.get(upper)?.name ?? - dictionaries.cityByCode.get(upper)?.name ?? - code - ); + const city = dictionaries.cityByCode.get(upper); + if (city) return city.name; + const airport = dictionaries.airportByCode.get(upper); + if (airport) return airport.name; + return code; }; + // Today's date gets rendered as 'Сегодня', matching Angular's heading. + const dateLabel = ((): string => { + if (!params.date || params.date.length !== 8) return ""; + const now = new Date(); + const todayYyyymmdd = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, "0")}${String(now.getDate()).padStart(2, "0")}`; + if (params.date === todayYyyymmdd) return t("SHARED.TODAY"); + // Otherwise format as 'DD.MM.YYYY'. + return `${params.date.slice(6, 8)}.${params.date.slice(4, 6)}.${params.date.slice(0, 4)}`; + })(); let searchHeading: string; switch (params.type) { case "route": searchHeading = `${t("BOARD.ROUTE-TEXT")}${describeStation(params.departure)} - ${describeStation(params.arrival)}`; + if (dateLabel) searchHeading += `, ${dateLabel}`; break; case "departure": searchHeading = `${t("BOARD.DEPARTURE")}: ${describeStation(params.station)}`; + if (dateLabel) searchHeading += `, ${dateLabel}`; break; case "arrival": searchHeading = `${t("BOARD.ARRIVAL")}: ${describeStation(params.station)}`; + if (dateLabel) searchHeading += `, ${dateLabel}`; break; case "flight": searchHeading = `${t("BOARD.FLIGHT_NUMBER")}: ${params.carrier}${params.flightNumber}`; diff --git a/src/features/schedule/components/ScheduleSearchPage.tsx b/src/features/schedule/components/ScheduleSearchPage.tsx index 484f96ea..d15dff4d 100644 --- a/src/features/schedule/components/ScheduleSearchPage.tsx +++ b/src/features/schedule/components/ScheduleSearchPage.tsx @@ -84,14 +84,16 @@ export const ScheduleSearchPage: FC = ({ params }) => { // Resolve IATA codes to human city/airport names so the heading reads // 'Маршрут: Шереметьево - Санкт-Петербург' instead of 'SVO - LED'. + // City wins over airport when the code resolves to both (Angular + // parity — LED is both codes; arrivals render the city name). const describeStation = (code?: string): string => { if (!code || !dictionaries) return code ?? ""; const upper = code.toUpperCase(); - return ( - dictionaries.airportByCode.get(upper)?.name ?? - dictionaries.cityByCode.get(upper)?.name ?? - code - ); + const city = dictionaries.cityByCode.get(upper); + if (city) return city.name; + const airport = dictionaries.airportByCode.get(upper); + if (airport) return airport.name; + return code; }; const depName = describeStation(outbound.departure); const arrName = describeStation(outbound.arrival); diff --git a/src/routes/[lang]/layout.tsx b/src/routes/[lang]/layout.tsx index 2ce29b10..04d9c898 100644 --- a/src/routes/[lang]/layout.tsx +++ b/src/routes/[lang]/layout.tsx @@ -1,11 +1,39 @@ import { useState, useEffect } from "react"; import { useParams } from "@modern-js/runtime/router"; import { Outlet } from "@modern-js/runtime/router"; +import { addLocale, locale as setPrimeLocale } from "primereact/api"; import { isLanguage, type Language } from "@/i18n/resolver"; import { createI18nInstance } from "@/i18n/config"; import { I18nProvider } from "@/i18n/provider"; import type i18next from "i18next"; +// Register PrimeReact locales once at module load so the Calendar / +// AutoComplete widgets render with localized labels (e.g. 'Выбрать дату' +// instead of 'Choose Date'). Only the keys PrimeReact actually reads +// are listed here; the rest fall back to defaults. +addLocale("ru", { + dayNames: ["воскресенье", "понедельник", "вторник", "среда", "четверг", "пятница", "суббота"], + dayNamesShort: ["вс", "пн", "вт", "ср", "чт", "пт", "сб"], + dayNamesMin: ["вс", "пн", "вт", "ср", "чт", "пт", "сб"], + monthNames: ["январь", "февраль", "март", "апрель", "май", "июнь", "июль", "август", "сентябрь", "октябрь", "ноябрь", "декабрь"], + monthNamesShort: ["янв", "фев", "мар", "апр", "май", "июн", "июл", "авг", "сен", "окт", "ноя", "дек"], + today: "Сегодня", + clear: "Очистить", + chooseDate: "Выбрать дату", + prevDecade: "Предыдущее десятилетие", + nextDecade: "Следующее десятилетие", + prevYear: "Предыдущий год", + nextYear: "Следующий год", + prevMonth: "Предыдущий месяц", + nextMonth: "Следующий месяц", + chooseYear: "Выбрать год", + chooseMonth: "Выбрать месяц", + weekHeader: "Нед", + firstDayOfWeek: 1, + emptyMessage: "Совпадений не найдено", + emptyFilterMessage: "Совпадений не найдено", +}); + /** * Locale-scoped layout. Validates the `lang` URL segment, * creates the i18n instance, and wraps children via . @@ -22,6 +50,9 @@ export default function LangLayout(): JSX.Element { useEffect(() => { if (!locale) return; let cancelled = false; + // PrimeReact reads the active locale via its module-level state; set it + // whenever our URL locale changes so widgets pick up the new labels. + setPrimeLocale(locale === "ru" ? "ru" : "en"); void createI18nInstance({ locale }).then((instance) => { if (!cancelled) { setI18n(instance); diff --git a/src/ui/flights/FlightCard.tsx b/src/ui/flights/FlightCard.tsx index ec58b67b..0c6f582c 100644 --- a/src/ui/flights/FlightCard.tsx +++ b/src/ui/flights/FlightCard.tsx @@ -77,7 +77,13 @@ export const FlightCard: FC = ({ flight, onClick }) => { @@ -99,7 +105,10 @@ export const FlightCard: FC = ({ flight, onClick }) => {