From a444b71bcddc484ba8442556e281b33743ee12e3 Mon Sep 17 00:00:00 2001 From: gnezim Date: Sat, 18 Apr 2026 00:15:46 +0300 Subject: [PATCH] Translate remaining English strings and color statuses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - FlightList empty-state, Operated-by label, details/schedule error and not-found messages now route through i18n instead of hardcoded English. Added BOARD.FLIGHT-NOT-FOUND, BOARD.LOAD-FAILED, BOARD.OPERATED-BY, SHARED.RETRY to all nine locales. - FlightStatus label now picks up the same colour as the plane icon (red for Cancelled, green for Arrived/Landed, blue for In Flight, orange for Delayed) — matches Angular's flight-status text treatment so 'Отменен' reads at a glance. - Tests updated to expect the translation keys under the mocked `t`. --- .../components/OnlineBoardDetailsPage.tsx | 6 ++-- .../components/OnlineBoardSearchPage.test.tsx | 4 ++- .../components/ScheduleDetailsPage.tsx | 4 +-- .../components/ScheduleSearchPage.tsx | 6 ++-- src/i18n/locales/de/common.json | 8 +++-- src/i18n/locales/en/common.json | 8 +++-- src/i18n/locales/es/common.json | 8 +++-- src/i18n/locales/fr/common.json | 8 +++-- src/i18n/locales/it/common.json | 8 +++-- src/i18n/locales/ja/common.json | 8 +++-- src/i18n/locales/ko/common.json | 8 +++-- src/i18n/locales/ru/common.json | 32 +++++++++++-------- src/i18n/locales/zh/common.json | 8 +++-- src/ui/flights/FlightList.tsx | 4 ++- src/ui/flights/FlightStatus.scss | 9 ++++++ .../online-board/flight-details.test.tsx | 2 +- .../online-board/flight-search.test.tsx | 2 +- 17 files changed, 92 insertions(+), 41 deletions(-) diff --git a/src/features/online-board/components/OnlineBoardDetailsPage.tsx b/src/features/online-board/components/OnlineBoardDetailsPage.tsx index d4925ca7..7d1a2048 100644 --- a/src/features/online-board/components/OnlineBoardDetailsPage.tsx +++ b/src/features/online-board/components/OnlineBoardDetailsPage.tsx @@ -272,7 +272,7 @@ export const OnlineBoardDetailsPage: FC = ({ return (
-

Failed to load flight details. Please try again.

+

{t("BOARD.LOAD-FAILED")}

); @@ -282,7 +282,7 @@ export const OnlineBoardDetailsPage: FC = ({ return (
-

Flight not found.

+

{t("BOARD.FLIGHT-NOT-FOUND")}

); @@ -357,7 +357,7 @@ export const OnlineBoardDetailsPage: FC = ({ {/* Operating carrier */} {displayFlight.operatingBy.carrier && (
- Operated by: {displayFlight.operatingBy.carrier} + {t("BOARD.OPERATED-BY")}: {displayFlight.operatingBy.carrier} {displayFlight.operatingBy.flightNumber ? ` ${displayFlight.operatingBy.flightNumber}` : ""} diff --git a/src/features/online-board/components/OnlineBoardSearchPage.test.tsx b/src/features/online-board/components/OnlineBoardSearchPage.test.tsx index 6fc9a9c1..5c7fb2a1 100644 --- a/src/features/online-board/components/OnlineBoardSearchPage.test.tsx +++ b/src/features/online-board/components/OnlineBoardSearchPage.test.tsx @@ -89,7 +89,9 @@ describe("OnlineBoardSearchPage", () => { it("renders empty flight list when no results", () => { render(); - expect(screen.getByText("No flights found")).toBeTruthy(); + // Empty-state text is now translated via SHARED.FLIGHTS-NOT-FOUND. + // Mocked `t` returns the key unchanged. + expect(screen.getByText("SHARED.FLIGHTS-NOT-FOUND")).toBeTruthy(); }); it("renders for flight search type", () => { diff --git a/src/features/schedule/components/ScheduleDetailsPage.tsx b/src/features/schedule/components/ScheduleDetailsPage.tsx index b8491156..c5df0426 100644 --- a/src/features/schedule/components/ScheduleDetailsPage.tsx +++ b/src/features/schedule/components/ScheduleDetailsPage.tsx @@ -76,7 +76,7 @@ export const ScheduleDetailsPage: FC = ({ if (error) { return (
-

Failed to load schedule details. Please try again.

+

{t("BOARD.LOAD-FAILED")}

); } @@ -84,7 +84,7 @@ export const ScheduleDetailsPage: FC = ({ if (flights.length === 0) { return (
-

Schedule details not found.

+

{t("BOARD.FLIGHT-NOT-FOUND")}

); } diff --git a/src/features/schedule/components/ScheduleSearchPage.tsx b/src/features/schedule/components/ScheduleSearchPage.tsx index d70412da..6bbd6c32 100644 --- a/src/features/schedule/components/ScheduleSearchPage.tsx +++ b/src/features/schedule/components/ScheduleSearchPage.tsx @@ -11,6 +11,7 @@ import type { FC } from "react"; import { useCallback } from "react"; import { useNavigate, useParams } from "@modern-js/runtime/router"; +import { useTranslation } from "@/i18n/provider.js"; import { FlightList } from "@/ui/flights/FlightList.js"; import "./ScheduleSearchPage.scss"; import { JsonLdRenderer } from "@/shared/seo/json-ld.js"; @@ -67,6 +68,7 @@ function extractSimpleFlights(flights: Array<{ routeType: string }>): ISimpleFli export const ScheduleSearchPage: FC = ({ params }) => { const navigate = useNavigate(); + const { t } = useTranslation(); const routeParams = useParams<{ lang: string }>(); const lang = routeParams.lang ?? "ru"; @@ -143,8 +145,8 @@ export const ScheduleSearchPage: FC = ({ params }) => { {/* Error state */} {outboundError && (
-

Failed to load schedule. Please try again.

- +

{t("BOARD.LOAD-FAILED")}

+
)} diff --git a/src/i18n/locales/de/common.json b/src/i18n/locales/de/common.json index 5483f4e1..81ae6780 100644 --- a/src/i18n/locales/de/common.json +++ b/src/i18n/locales/de/common.json @@ -41,7 +41,10 @@ "DETAILS-TITLE": "Flight details", "FLIGHT-INFO": "Flight information", "LOCAL-TIME-NOTE": "Times shown are LOCAL.", - "ESTIMATED-TIME-NOTE": "Arrival times and distances are estimated. Times may change depending on weather and airport load." + "ESTIMATED-TIME-NOTE": "Arrival times and distances are estimated. Times may change depending on weather and airport load.", + "FLIGHT-NOT-FOUND": "Flight not found.", + "LOAD-FAILED": "Failed to load data. Please try again.", + "OPERATED-BY": "Operated by" }, "BOARDING-STATUSES": { "Expected": "Erwartet", @@ -368,7 +371,8 @@ "TRANSFER": "Transfer", "TRAVEL-TIME": "Reisezeit", "WEEK": "Woche", - "WEEK_FORMAT-WRONG": "" + "WEEK_FORMAT-WRONG": "", + "RETRY": "Retry" }, "WARNING": { "IFLY_HIGHLIGHT": "Bitte beachten Sie:", diff --git a/src/i18n/locales/en/common.json b/src/i18n/locales/en/common.json index b7760bf6..60044ff0 100644 --- a/src/i18n/locales/en/common.json +++ b/src/i18n/locales/en/common.json @@ -41,7 +41,10 @@ "DETAILS-TITLE": "Flight details", "FLIGHT-INFO": "Flight information", "LOCAL-TIME-NOTE": "Times shown are LOCAL.", - "ESTIMATED-TIME-NOTE": "Arrival times and distances are estimated. Times may change depending on weather and airport load." + "ESTIMATED-TIME-NOTE": "Arrival times and distances are estimated. Times may change depending on weather and airport load.", + "FLIGHT-NOT-FOUND": "Flight not found.", + "LOAD-FAILED": "Failed to load data. Please try again.", + "OPERATED-BY": "Operated by" }, "BREADCRUMBS": { "ONLINEBOARD": "Online Board" @@ -395,7 +398,8 @@ "TRANSFER": "Connection", "TRAVEL-TIME": "Travel time", "WEEK": "Week", - "WEEK_FORMAT-WRONG": "" + "WEEK_FORMAT-WRONG": "", + "RETRY": "Retry" }, "WARNING": { "IFLY_HIGHLIGHT": "Please note:", diff --git a/src/i18n/locales/es/common.json b/src/i18n/locales/es/common.json index 315f578f..267a8155 100644 --- a/src/i18n/locales/es/common.json +++ b/src/i18n/locales/es/common.json @@ -41,7 +41,10 @@ "DETAILS-TITLE": "Flight details", "FLIGHT-INFO": "Flight information", "LOCAL-TIME-NOTE": "Times shown are LOCAL.", - "ESTIMATED-TIME-NOTE": "Arrival times and distances are estimated. Times may change depending on weather and airport load." + "ESTIMATED-TIME-NOTE": "Arrival times and distances are estimated. Times may change depending on weather and airport load.", + "FLIGHT-NOT-FOUND": "Flight not found.", + "LOAD-FAILED": "Failed to load data. Please try again.", + "OPERATED-BY": "Operated by" }, "BOARDING-STATUSES": { "Expected": "Prevista", @@ -368,7 +371,8 @@ "TRANSFER": "Enlace", "TRAVEL-TIME": "Duración del viaje", "WEEK": "Semana", - "WEEK_FORMAT-WRONG": "" + "WEEK_FORMAT-WRONG": "", + "RETRY": "Retry" }, "WARNING": { "IFLY_HIGHLIGHT": "Nota:", diff --git a/src/i18n/locales/fr/common.json b/src/i18n/locales/fr/common.json index 2bc1d699..aac5b4a9 100644 --- a/src/i18n/locales/fr/common.json +++ b/src/i18n/locales/fr/common.json @@ -41,7 +41,10 @@ "DETAILS-TITLE": "Flight details", "FLIGHT-INFO": "Flight information", "LOCAL-TIME-NOTE": "Times shown are LOCAL.", - "ESTIMATED-TIME-NOTE": "Arrival times and distances are estimated. Times may change depending on weather and airport load." + "ESTIMATED-TIME-NOTE": "Arrival times and distances are estimated. Times may change depending on weather and airport load.", + "FLIGHT-NOT-FOUND": "Flight not found.", + "LOAD-FAILED": "Failed to load data. Please try again.", + "OPERATED-BY": "Operated by" }, "BOARDING-STATUSES": { "Expected": "Prévu", @@ -368,7 +371,8 @@ "TRANSFER": "Correspondance", "TRAVEL-TIME": "Durée du trajet", "WEEK": "Semaine", - "WEEK_FORMAT-WRONG": "" + "WEEK_FORMAT-WRONG": "", + "RETRY": "Retry" }, "WARNING": { "IFLY_HIGHLIGHT": "Remarque:", diff --git a/src/i18n/locales/it/common.json b/src/i18n/locales/it/common.json index bd2aee34..1278e07b 100644 --- a/src/i18n/locales/it/common.json +++ b/src/i18n/locales/it/common.json @@ -41,7 +41,10 @@ "DETAILS-TITLE": "Flight details", "FLIGHT-INFO": "Flight information", "LOCAL-TIME-NOTE": "Times shown are LOCAL.", - "ESTIMATED-TIME-NOTE": "Arrival times and distances are estimated. Times may change depending on weather and airport load." + "ESTIMATED-TIME-NOTE": "Arrival times and distances are estimated. Times may change depending on weather and airport load.", + "FLIGHT-NOT-FOUND": "Flight not found.", + "LOAD-FAILED": "Failed to load data. Please try again.", + "OPERATED-BY": "Operated by" }, "BOARDING-STATUSES": { "Expected": "Previsto", @@ -368,7 +371,8 @@ "TRANSFER": "Scalo", "TRAVEL-TIME": "Durata del viaggio", "WEEK": "Settimana", - "WEEK_FORMAT-WRONG": "" + "WEEK_FORMAT-WRONG": "", + "RETRY": "Retry" }, "WARNING": { "IFLY_HIGHLIGHT": "Attenzione:", diff --git a/src/i18n/locales/ja/common.json b/src/i18n/locales/ja/common.json index 267ec0f0..6eb766ca 100644 --- a/src/i18n/locales/ja/common.json +++ b/src/i18n/locales/ja/common.json @@ -41,7 +41,10 @@ "DETAILS-TITLE": "Flight details", "FLIGHT-INFO": "Flight information", "LOCAL-TIME-NOTE": "Times shown are LOCAL.", - "ESTIMATED-TIME-NOTE": "Arrival times and distances are estimated. Times may change depending on weather and airport load." + "ESTIMATED-TIME-NOTE": "Arrival times and distances are estimated. Times may change depending on weather and airport load.", + "FLIGHT-NOT-FOUND": "Flight not found.", + "LOAD-FAILED": "Failed to load data. Please try again.", + "OPERATED-BY": "Operated by" }, "BOARDING-STATUSES": { "Expected": "予測", @@ -368,7 +371,8 @@ "TRANSFER": "乗り換え", "TRAVEL-TIME": "移動時間", "WEEK": "週", - "WEEK_FORMAT-WRONG": "" + "WEEK_FORMAT-WRONG": "", + "RETRY": "Retry" }, "WARNING": { "IFLY_HIGHLIGHT": "ご注意:", diff --git a/src/i18n/locales/ko/common.json b/src/i18n/locales/ko/common.json index 47279181..c0838186 100644 --- a/src/i18n/locales/ko/common.json +++ b/src/i18n/locales/ko/common.json @@ -41,7 +41,10 @@ "DETAILS-TITLE": "Flight details", "FLIGHT-INFO": "Flight information", "LOCAL-TIME-NOTE": "Times shown are LOCAL.", - "ESTIMATED-TIME-NOTE": "Arrival times and distances are estimated. Times may change depending on weather and airport load." + "ESTIMATED-TIME-NOTE": "Arrival times and distances are estimated. Times may change depending on weather and airport load.", + "FLIGHT-NOT-FOUND": "Flight not found.", + "LOAD-FAILED": "Failed to load data. Please try again.", + "OPERATED-BY": "Operated by" }, "BOARDING-STATUSES": { "Expected": "예상 ", @@ -368,7 +371,8 @@ "TRANSFER": "연결편 항공", "TRAVEL-TIME": "여행 시간", "WEEK": "주", - "WEEK_FORMAT-WRONG": "" + "WEEK_FORMAT-WRONG": "", + "RETRY": "Retry" }, "WARNING": { "IFLY_HIGHLIGHT": "참고:", diff --git a/src/i18n/locales/ru/common.json b/src/i18n/locales/ru/common.json index 169efde8..bd4a9047 100644 --- a/src/i18n/locales/ru/common.json +++ b/src/i18n/locales/ru/common.json @@ -41,7 +41,10 @@ "DETAILS-TITLE": "Детали рейса", "FLIGHT-INFO": "Информация о рейсе", "LOCAL-TIME-NOTE": "Время в системе - МЕСТНОЕ.", - "ESTIMATED-TIME-NOTE": "Время прилета и расстояния являются расчетными и примерными. Время может изменяться в зависимости от погодных условий и загрузки аэропорта." + "ESTIMATED-TIME-NOTE": "Время прилета и расстояния являются расчетными и примерными. Время может изменяться в зависимости от погодных условий и загрузки аэропорта.", + "FLIGHT-NOT-FOUND": "Рейс не найден.", + "LOAD-FAILED": "Не удалось загрузить данные. Попробуйте снова.", + "OPERATED-BY": "Выполняет рейс" }, "BREADCRUMBS": { "ONLINEBOARD": "Онлайн-табло" @@ -208,7 +211,7 @@ "TITLE": "Расписание рейсов", "TITLE-TAB": "Расписание", "SCHEDULE-BOTTOM-DESCRIPTION": "Расписание рейсов Аэрофлота", - "SCHEDULE-BOTTOM-DESCRIPTION-TEXT" : "

На странице расписания рейсов Аэрофлота представлена вся необходимая информация о времени отправления и прибытия наших рейсов.
Выбирайте дату и планируйте свое путешествие заранее: прямым рейсом или с пересадками.

Мы предлагаем билеты на самолет по конкурентным ценам с удобным онлайн-сервисом для оформления заказа.

Путешествуйте с Аэрофлотом, где комфорт и надежность всегда на высоте!

" + "SCHEDULE-BOTTOM-DESCRIPTION-TEXT": "

На странице расписания рейсов Аэрофлота представлена вся необходимая информация о времени отправления и прибытия наших рейсов.
Выбирайте дату и планируйте свое путешествие заранее: прямым рейсом или с пересадками.

Мы предлагаем билеты на самолет по конкурентным ценам с удобным онлайн-сервисом для оформления заказа.

Путешествуйте с Аэрофлотом, где комфорт и надежность всегда на высоте!

" }, "SEO": { "BOARD": { @@ -251,7 +254,7 @@ "TITLE": "Расписание рейсов {{ departureCity }} - {{ arrivalCity }} | Аэрофлот" } }, - "FLIGHTS-MAP":{ + "FLIGHTS-MAP": { "MAIN": { "DESCRIPTION": "Карта полетов авиакомпании 'Аэрофлот'. Информация о направлениях рейсов.", "TITLE": "Карта полетов авиакомпании Аэрофлот" @@ -395,26 +398,27 @@ "TRANSFER": "Пересадка", "TRAVEL-TIME": "В пути", "WEEK": "Неделя", - "WEEK_FORMAT-WRONG": "Не соответствует формату ДД.ММ.ГГГГ - ДД.ММ.ГГГГ" + "WEEK_FORMAT-WRONG": "Не соответствует формату ДД.ММ.ГГГГ - ДД.ММ.ГГГГ", + "RETRY": "Повторить" }, "SMOKE": { "HEADING": "Страница проверки" }, - "WARNING":{ - "IFLY_HIGHLIGHT" : "Обратите внимание!", - "IFLY_BODY" : "Рейсы Аэрофлота с нумерацией SU5800-SU5949 выполняются на самолётах и экипажем авиакомпании iFly.", - "IFLY_INFO" : "Информация о порядке обслуживания на борту здесь." + "WARNING": { + "IFLY_HIGHLIGHT": "Обратите внимание!", + "IFLY_BODY": "Рейсы Аэрофлота с нумерацией SU5800-SU5949 выполняются на самолётах и экипажем авиакомпании iFly.", + "IFLY_INFO": "Информация о порядке обслуживания на борту здесь." }, - "FLIGHTS-MAP":{ + "FLIGHTS-MAP": { "TITLE": "Карта полетов", "ROUTE": "Найдите свой маршрут", "FILTER_DEPARTURE_PLACEHOLDER": "Откуда", "FILTER_ARRIVAL_PLACEHOLDER": "Куда", "DOMESTIC_FLIGHTS": "Внутренние рейсы", "INTERNATIONAL_FLIGHTS": "Международные регулярные рейсы", - "FILTER_INFO" : "Нажмите на «Купить билет», чтобы получить полный список рейсов по выбранному направлению", - "BUY_TICKET_BTN" : "Купить билет", - "CONNECTING_FLIGHTS" : "Показать только рейсы с пересадкой", - "NO_DIRECTIONS_INFO":"Маршрутов не найдено, измените параметры поиска" + "FILTER_INFO": "Нажмите на «Купить билет», чтобы получить полный список рейсов по выбранному направлению", + "BUY_TICKET_BTN": "Купить билет", + "CONNECTING_FLIGHTS": "Показать только рейсы с пересадкой", + "NO_DIRECTIONS_INFO": "Маршрутов не найдено, измените параметры поиска" } -} \ No newline at end of file +} diff --git a/src/i18n/locales/zh/common.json b/src/i18n/locales/zh/common.json index 38c12fdf..1f7c23a0 100644 --- a/src/i18n/locales/zh/common.json +++ b/src/i18n/locales/zh/common.json @@ -41,7 +41,10 @@ "DETAILS-TITLE": "Flight details", "FLIGHT-INFO": "Flight information", "LOCAL-TIME-NOTE": "Times shown are LOCAL.", - "ESTIMATED-TIME-NOTE": "Arrival times and distances are estimated. Times may change depending on weather and airport load." + "ESTIMATED-TIME-NOTE": "Arrival times and distances are estimated. Times may change depending on weather and airport load.", + "FLIGHT-NOT-FOUND": "Flight not found.", + "LOAD-FAILED": "Failed to load data. Please try again.", + "OPERATED-BY": "Operated by" }, "BOARDING-STATUSES": { "Expected": "预计", @@ -368,7 +371,8 @@ "TRANSFER": "联程", "TRAVEL-TIME": "旅行时间", "WEEK": "周", - "WEEK_FORMAT-WRONG": "" + "WEEK_FORMAT-WRONG": "", + "RETRY": "Retry" }, "WARNING": { "IFLY_HIGHLIGHT": "请注意:", diff --git a/src/ui/flights/FlightList.tsx b/src/ui/flights/FlightList.tsx index 724b1292..294d2b49 100644 --- a/src/ui/flights/FlightList.tsx +++ b/src/ui/flights/FlightList.tsx @@ -1,4 +1,5 @@ import type { FC } from "react"; +import { useTranslation } from "@/i18n/provider.js"; import type { ISimpleFlight } from "@/features/online-board/types.js"; import { FlightCard } from "./FlightCard.js"; import { FlightListSkeleton } from "./FlightListSkeleton.js"; @@ -27,6 +28,7 @@ export const FlightList: FC = ({ skeletonCount = 5, onFlightClick, }) => { + const { t } = useTranslation(); if (loading) { return ; } @@ -34,7 +36,7 @@ export const FlightList: FC = ({ if (flights.length === 0) { return (
-

No flights found

+

{t("SHARED.FLIGHTS-NOT-FOUND")}

); } diff --git a/src/ui/flights/FlightStatus.scss b/src/ui/flights/FlightStatus.scss index bcae491c..7989d2fd 100644 --- a/src/ui/flights/FlightStatus.scss +++ b/src/ui/flights/FlightStatus.scss @@ -21,6 +21,15 @@ white-space: nowrap; } + // Mirror the icon colour on the label so "Отменен"/"Прибыл"/"В полете" + // read at a glance (Angular does the same). + &--cancelled &__label { color: #e55353; } + &--arrived &__label, + &--landed &__label { color: #6da244; } + &--in-flight &__label, + &--departed &__label { color: #2457ff; } + &--delayed &__label { color: #f29f3a; } + &__content { display: flex; align-items: center; diff --git a/tests/integration/online-board/flight-details.test.tsx b/tests/integration/online-board/flight-details.test.tsx index ef47b223..1d55bad4 100644 --- a/tests/integration/online-board/flight-details.test.tsx +++ b/tests/integration/online-board/flight-details.test.tsx @@ -206,7 +206,7 @@ describe("Flight details page integration", () => { />, ); expect(screen.getByTestId("operating-carrier")).toBeTruthy(); - expect(screen.getByText(/Operated by: SU/)).toBeTruthy(); + expect(screen.getByText(/BOARD\.OPERATED-BY: SU/)).toBeTruthy(); }); it("renders flying time", () => { diff --git a/tests/integration/online-board/flight-search.test.tsx b/tests/integration/online-board/flight-search.test.tsx index d236d05a..22d45ae0 100644 --- a/tests/integration/online-board/flight-search.test.tsx +++ b/tests/integration/online-board/flight-search.test.tsx @@ -157,7 +157,7 @@ describe("Flight search page integration", () => { it("renders empty state when no flights returned", () => { setupMocksEmpty(); render(); - expect(screen.getByText("No flights found")).toBeTruthy(); + expect(screen.getByText("SHARED.FLIGHTS-NOT-FOUND")).toBeTruthy(); }); it("renders calendar strip (DayTabs) with pagination arrows", () => {