ce2ca4a689
- URL surface now matches Angular: `/ru-ru/`, `/en-us/`, `/zh-cn/`, …
(BCP-47). Bare short codes still work — the [lang]/layout auto-
promotes them with a replace navigation. Internally everything that
needs the short language (i18n file lookup, API path segment,
Accept-Language header, dictionary `title[lang]` key, Intl
formatters) reads it through the new `useLocale()` hook, which
returns both `locale` (BCP-47) and `language` (short).
- ApiClient.locale is now mutable and is updated from the [lang]
layout whenever the URL locale changes — was hard-coded to "ru" in
the root layout before, so backend responses for /en/... still came
back in Russian. Cities / airports / flight statuses now arrive in
the active language.
- All 21 empty EN translation keys filled in (AIRPLANE.*, BOARD.
PREVIOUS-FLIGHT, SCHEDULE.FILE-NAME, SEO.SCHEDULE.*, SEO.FLIGHTS-
MAP.*, SHARED.FLIGHT-TRANSFER-PLURAL-*, SHARED.WEEK_FORMAT-WRONG)
so /en-us renders without falling back to raw keys.
- Added BOARD.LOAD-FAILED-TITLE / -MESSAGE keys (RU + EN) and removed
the three hardcoded Russian error strings from the search-page
error card.
- FlightStatus now reads `FLIGHT-STATUSES.{Status}` from i18n instead
of hardcoding the Russian labels.
- FlightCard's OperatorLogo now picks the en/ru carrier-logo variant
from `useLocale().language` instead of always passing "ru" — the
Aeroflot/Rossiya logos display in the active language where
variants exist.
- registerPrimeLocales(): all 9 supported languages get a PrimeReact
`addLocale` entry at module load (RU + EN hand-curated, others built
from Intl). Calendar/AutoComplete widgets switch with the URL.
- ErrorBoundary catches outside the i18n provider, so it now ships
its own minimal localised string table keyed off the URL locale —
no more "Something went wrong" leaking on the Russian site.
- Hreflang URLs now emit BCP-47 (`/en-us/...`) while `hreflang="en"`
stays the short Google-friendly form.
- Datetime helpers accept either short or BCP-47 locale (`isRussianLocale`)
so callers can pass through whatever the route hands them.
64 lines
1.8 KiB
TypeScript
64 lines
1.8 KiB
TypeScript
/**
|
|
* Page navigation tabs matching the Angular `flights-page-tabs` component.
|
|
*
|
|
* Renders "Online Timetable" / "Schedule" / "Flights Map" tab buttons
|
|
* using the same `.tabs` class names as the Angular version.
|
|
*/
|
|
|
|
import type { FC } from "react";
|
|
import { Link } from "@modern-js/runtime/router";
|
|
import { useTranslation } from "@/i18n/provider.js";
|
|
import { useLocale } from "@/i18n/useLocale.js";
|
|
import { useFeatureFlag } from "@/features/flights-map/hooks/useFeatureFlag.js";
|
|
import "./PageTabs.scss";
|
|
|
|
export type ViewType = "onlineboard" | "schedule" | "flights-map";
|
|
|
|
export interface PageTabsProps {
|
|
viewType: ViewType;
|
|
showFlightsMap?: boolean;
|
|
}
|
|
|
|
export const PageTabs: FC<PageTabsProps> = ({
|
|
viewType,
|
|
showFlightsMap,
|
|
}) => {
|
|
const flightsMapEnabled = useFeatureFlag("flightsMap");
|
|
const showMap = showFlightsMap ?? flightsMapEnabled;
|
|
const { t } = useTranslation();
|
|
const { locale } = useLocale();
|
|
|
|
return (
|
|
<div className="tabs">
|
|
<div className="tabs__row">
|
|
<Link
|
|
className={`tabs__tab${viewType === "onlineboard" ? " active" : ""}`}
|
|
to={`/${locale}/onlineboard`}
|
|
data-testid="onlineboard-tab"
|
|
>
|
|
{t("BOARD.TITLE")}
|
|
</Link>
|
|
<Link
|
|
className={`tabs__tab${viewType === "schedule" ? " active" : ""}`}
|
|
to={`/${locale}/schedule`}
|
|
data-testid="schedule-tab"
|
|
>
|
|
{t("SCHEDULE.TITLE-TAB")}
|
|
</Link>
|
|
</div>
|
|
|
|
{showMap && (
|
|
<div className="tabs__row">
|
|
<Link
|
|
className={`tabs__tab tabs__tab--full${viewType === "flights-map" ? " active" : ""}`}
|
|
to={`/${locale}/flights-map`}
|
|
data-testid="flights-map-tab"
|
|
>
|
|
{t("FLIGHTS-MAP.TITLE")}
|
|
</Link>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|