Translate remaining aria-labels on shared widgets

Six more aria-labels that still read in English:
  - CityAutocomplete clear button ('clear')
  - CityAutocomplete picker trigger ('open regional picker')
  - Breadcrumbs nav landmark ('breadcrumb')
  - Timeline prev/next buttons ('Previous legs' / 'Next legs')

Routed through new SHARED.A11Y-* keys (translated into all nine
locales). This is screen-reader-only text but part of the parity
budget.
This commit is contained in:
2026-04-18 13:32:35 +03:00
parent 0c660671ea
commit 58631a3d73
12 changed files with 63 additions and 14 deletions
@@ -1,4 +1,5 @@
import { type FC, useState } from "react";
import { useTranslation } from "@/i18n/provider.js";
import type { IFlightLeg } from "../../types.js";
import { Station } from "./Station.js";
import { StationChange } from "./StationChange.js";
@@ -46,6 +47,7 @@ const Section: FC<SectionProps> = ({ legNumber, duration, specifying }) => (
);
export const Timeline: FC<TimelineProps> = ({ legs, canChange }) => {
const { t } = useTranslation();
const [index, setIndex] = useState(0);
const lastPairStart = Math.max(0, legs.length - 2);
const currentLeg = legs[index];
@@ -61,7 +63,7 @@ export const Timeline: FC<TimelineProps> = ({ legs, canChange }) => {
className="timeline__arrow"
data-testid="timeline-prev"
onClick={() => setIndex((i) => Math.max(0, i - 1))}
aria-label="Previous legs"
aria-label={t("SHARED.A11Y-PREV-LEGS")}
>
{"\u2039"}
</button>
@@ -98,7 +100,7 @@ export const Timeline: FC<TimelineProps> = ({ legs, canChange }) => {
className="timeline__arrow"
data-testid="timeline-next"
onClick={() => setIndex((i) => Math.min(lastPairStart, i + 1))}
aria-label="Next legs"
aria-label={t("SHARED.A11Y-NEXT-LEGS")}
>
{"\u203a"}
</button>
+6 -1
View File
@@ -384,7 +384,12 @@
"A11Y-NEXT-PAGE": "Next page",
"A11Y-LOADING-FLIGHTS": "Loading flights",
"A11Y-SCROLL-TO-TOP": "Scroll to top",
"A11Y-PRINT-BTN": "Print"
"A11Y-PRINT-BTN": "Print",
"A11Y-CLEAR": "Clear",
"A11Y-OPEN-PICKER": "Open city picker",
"A11Y-BREADCRUMB": "Breadcrumb",
"A11Y-PREV-LEGS": "Previous legs",
"A11Y-NEXT-LEGS": "Next legs"
},
"WARNING": {
"IFLY_HIGHLIGHT": "Bitte beachten Sie:",
+6 -1
View File
@@ -411,7 +411,12 @@
"A11Y-NEXT-PAGE": "Next page",
"A11Y-LOADING-FLIGHTS": "Loading flights",
"A11Y-SCROLL-TO-TOP": "Scroll to top",
"A11Y-PRINT-BTN": "Print"
"A11Y-PRINT-BTN": "Print",
"A11Y-CLEAR": "Clear",
"A11Y-OPEN-PICKER": "Open city picker",
"A11Y-BREADCRUMB": "Breadcrumb",
"A11Y-PREV-LEGS": "Previous legs",
"A11Y-NEXT-LEGS": "Next legs"
},
"WARNING": {
"IFLY_HIGHLIGHT": "Please note:",
+6 -1
View File
@@ -384,7 +384,12 @@
"A11Y-NEXT-PAGE": "Next page",
"A11Y-LOADING-FLIGHTS": "Loading flights",
"A11Y-SCROLL-TO-TOP": "Scroll to top",
"A11Y-PRINT-BTN": "Print"
"A11Y-PRINT-BTN": "Print",
"A11Y-CLEAR": "Clear",
"A11Y-OPEN-PICKER": "Open city picker",
"A11Y-BREADCRUMB": "Breadcrumb",
"A11Y-PREV-LEGS": "Previous legs",
"A11Y-NEXT-LEGS": "Next legs"
},
"WARNING": {
"IFLY_HIGHLIGHT": "Nota:",
+6 -1
View File
@@ -384,7 +384,12 @@
"A11Y-NEXT-PAGE": "Next page",
"A11Y-LOADING-FLIGHTS": "Loading flights",
"A11Y-SCROLL-TO-TOP": "Scroll to top",
"A11Y-PRINT-BTN": "Print"
"A11Y-PRINT-BTN": "Print",
"A11Y-CLEAR": "Clear",
"A11Y-OPEN-PICKER": "Open city picker",
"A11Y-BREADCRUMB": "Breadcrumb",
"A11Y-PREV-LEGS": "Previous legs",
"A11Y-NEXT-LEGS": "Next legs"
},
"WARNING": {
"IFLY_HIGHLIGHT": "Remarque:",
+6 -1
View File
@@ -384,7 +384,12 @@
"A11Y-NEXT-PAGE": "Next page",
"A11Y-LOADING-FLIGHTS": "Loading flights",
"A11Y-SCROLL-TO-TOP": "Scroll to top",
"A11Y-PRINT-BTN": "Print"
"A11Y-PRINT-BTN": "Print",
"A11Y-CLEAR": "Clear",
"A11Y-OPEN-PICKER": "Open city picker",
"A11Y-BREADCRUMB": "Breadcrumb",
"A11Y-PREV-LEGS": "Previous legs",
"A11Y-NEXT-LEGS": "Next legs"
},
"WARNING": {
"IFLY_HIGHLIGHT": "Attenzione:",
+6 -1
View File
@@ -384,7 +384,12 @@
"A11Y-NEXT-PAGE": "Next page",
"A11Y-LOADING-FLIGHTS": "Loading flights",
"A11Y-SCROLL-TO-TOP": "Scroll to top",
"A11Y-PRINT-BTN": "Print"
"A11Y-PRINT-BTN": "Print",
"A11Y-CLEAR": "Clear",
"A11Y-OPEN-PICKER": "Open city picker",
"A11Y-BREADCRUMB": "Breadcrumb",
"A11Y-PREV-LEGS": "Previous legs",
"A11Y-NEXT-LEGS": "Next legs"
},
"WARNING": {
"IFLY_HIGHLIGHT": "ご注意:",
+6 -1
View File
@@ -384,7 +384,12 @@
"A11Y-NEXT-PAGE": "Next page",
"A11Y-LOADING-FLIGHTS": "Loading flights",
"A11Y-SCROLL-TO-TOP": "Scroll to top",
"A11Y-PRINT-BTN": "Print"
"A11Y-PRINT-BTN": "Print",
"A11Y-CLEAR": "Clear",
"A11Y-OPEN-PICKER": "Open city picker",
"A11Y-BREADCRUMB": "Breadcrumb",
"A11Y-PREV-LEGS": "Previous legs",
"A11Y-NEXT-LEGS": "Next legs"
},
"WARNING": {
"IFLY_HIGHLIGHT": "참고:",
+6 -1
View File
@@ -411,7 +411,12 @@
"A11Y-NEXT-PAGE": "Следующая страница",
"A11Y-LOADING-FLIGHTS": "Загрузка рейсов",
"A11Y-SCROLL-TO-TOP": "Наверх",
"A11Y-PRINT-BTN": "Печать"
"A11Y-PRINT-BTN": "Печать",
"A11Y-CLEAR": "Очистить",
"A11Y-OPEN-PICKER": "Открыть список городов",
"A11Y-BREADCRUMB": "Навигационная цепочка",
"A11Y-PREV-LEGS": "Предыдущие сегменты",
"A11Y-NEXT-LEGS": "Следующие сегменты"
},
"SMOKE": {
"HEADING": "Страница проверки"
+6 -1
View File
@@ -384,7 +384,12 @@
"A11Y-NEXT-PAGE": "Next page",
"A11Y-LOADING-FLIGHTS": "Loading flights",
"A11Y-SCROLL-TO-TOP": "Scroll to top",
"A11Y-PRINT-BTN": "Print"
"A11Y-PRINT-BTN": "Print",
"A11Y-CLEAR": "Clear",
"A11Y-OPEN-PICKER": "Open city picker",
"A11Y-BREADCRUMB": "Breadcrumb",
"A11Y-PREV-LEGS": "Previous legs",
"A11Y-NEXT-LEGS": "Next legs"
},
"WARNING": {
"IFLY_HIGHLIGHT": "请注意:",
@@ -1,5 +1,6 @@
import { useState, useRef, useCallback, useEffect, type FC } from "react";
import { AutoComplete, type AutoCompleteCompleteEvent } from "primereact/autocomplete";
import { useTranslation } from "@/i18n/provider.js";
import { CityPickerPopup } from "./CityPickerPopup.js";
import { searchCities, type CityAutocompleteItem } from "./searchCities.js";
import type { IDictionaries } from "@/shared/dictionaries/index.js";
@@ -26,6 +27,7 @@ export const CityAutocomplete: FC<CityAutocompleteProps> = ({
error,
testIdPrefix = "city-autocomplete",
}) => {
const { t } = useTranslation();
const [inputValue, setInputValue] = useState<CityAutocompleteItem | string>(value);
const [suggestions, setSuggestions] = useState<CityAutocompleteItem[]>([]);
const [popupOpen, setPopupOpen] = useState(false);
@@ -169,7 +171,7 @@ export const CityAutocomplete: FC<CityAutocompleteProps> = ({
className="button-clear"
onClick={handleClear}
data-testid={`${testIdPrefix}-clear-button`}
aria-label="clear"
aria-label={t("SHARED.A11Y-CLEAR")}
/>
<button
type="button"
@@ -181,7 +183,7 @@ export const CityAutocomplete: FC<CityAutocompleteProps> = ({
setPopupOpen((v) => !v);
}}
data-testid={`${testIdPrefix}-popup-button`}
aria-label="open regional picker"
aria-label={t("SHARED.A11Y-OPEN-PICKER")}
/>
</div>
+1 -1
View File
@@ -29,7 +29,7 @@ export const Breadcrumbs: FC<BreadcrumbsProps> = ({ items = [] }) => {
];
return (
<nav className="breadcrumbs" aria-label="breadcrumb" data-testid="breadcrumbs">
<nav className="breadcrumbs" aria-label={t("SHARED.A11Y-BREADCRUMB")} data-testid="breadcrumbs">
<ol className="breadcrumbs__list">
{allItems.map((item, index) => {
const isLast = index === allItems.length - 1;