Pushkal-Gupta opened a new pull request, #75965:
URL: https://github.com/apache/airflow/pull/75965
Nine `aria-label` values are hardcoded English. Each one is the only name
its control has, because the control is an icon with no visible text, so a
screen reader in any of the other 20 languages reads out English.
| File | Label |
| --- | --- |
| `PresetFiltersMenu.tsx` | `Delete preset filter` |
| `AdvancedSearchToggle.tsx` (x2) | `Toggle match-anywhere search` |
| `MultiSelectPill.tsx`, `TextSearchFilter.tsx` | `match anywhere` |
| `DataTable.tsx` | `Previous page`, `Next page` |
| `LogSearchInput.tsx` | `Previous match`, `Next match` |
These look like slips rather than decisions. The convention is already the
other way round: of the 75 `aria-label`s in `src/`, 41 go through
`translate(...)` and 22 take their value from a variable. Nine quoted literals
and three template literals are what is left.
Two of the files argue the point by themselves. `AdvancedSearchToggle`
builds its tooltip from two `translate(...)` calls and then names the button it
wraps with a literal, and `LogSearchInput` calls
`translate("logs.search.matchCount")` on the line above the two buttons it
labels `Previous match` and `Next match`.
### The fix
Three of the nine reuse a key that already exists. `Delete preset filter` is
exactly `common:presetFilters.deleteTitle`, and both `match anywhere` icons
become `common:search.advanced.title`, whose English value is `Match anywhere`,
so the capitalisation shifts a little to match the tooltip beside them. Those
two keys are filled in 14 and 17 of the 21 locales; the rest fall back to
English, which is what the literal was doing anyway.
Five keys are new, so this adds them to `en` and leaves the rest to
translators as usual: `search.advanced.toggleLabel`, `pagination.previousPage`,
`pagination.nextPage`, `logs.search.previousMatch` and `logs.search.nextMatch`.
`MultiSelectPill` and `TextSearchFilter` had no `useTranslation` yet and now
take one on the `common` namespace. The other four files already had the hook.
### Why lint did not catch it
`rules/i18next.js` configures `i18next/no-literal-string` with `markupOnly:
true`. That option no longer exists in the installed
`[email protected]`. It is in neither the rule source nor the README,
so it is a leftover v5 name and does nothing. The rule runs at its default
`jsx-text-only`, which checks JSX text and no attributes at all, so an
`aria-label` is the one piece of user-facing copy it cannot see.
I left that config alone, because I measured what changing it costs. `mode:
"jsx-only"` reports 759 strings. Narrowing it with `jsx-attributes.include:
["aria-label"]` gets to 36, and only 5 of those 36 are hardcoded `aria-label`s.
The other 31 are not problems: ternaries like `wrap ? "un" : ""`, date formats
like `MMM YYYY`, size tokens like `2xs`, and in three places the
`translate(...)` call the rule exists to encourage. It also misses 4 of the
nine fixed here, both `match anywhere` icons among them. So it would mean 31
suppressions and still would not cover the class. Removing the dead
`markupOnly` option is worth doing on its own, but it is a config change with
its own discussion and does not belong here.
So the guard is a narrow test instead. `src/i18n/ariaLabels.test.ts` scans
every `.tsx` under `src/` and fails on an `aria-label` whose value is a quoted
literal. Against `main` it reports exactly these nine and nothing else.
The three template-literal labels are left for a follow-up: `` `Remove
${filter.config.label} filter` `` in `FilterPill.tsx`, `` `Clear ${field}
${inputType}` `` in `DateInput.tsx` and `` `Version ${dagVersionNumber}
indicator` `` in `VersionIndicator.tsx`. `FilterPill` is the one that stings,
since it passes `label={translate("common:filters.removeFilter")}` on the next
line. Each needs a new key with its own interpolation, and in `DateInput` the
interpolated words need translating too. The test checks only fully quoted
values and says so, rather than carrying an exemption list for these three.
### Test selectors
15 existing tests looked these controls up by their English label, with
`getByLabelText("Delete preset filter")` or `getByRole("button", { name: /next
match/iu })`. Unit tests here do not load the locale bundles, so a label now
renders as its key and those queries stop matching. They are updated to the
keys, which is what this suite already does elsewhere. `DataTable`'s two
buttons needed no change, because they are queried by `data-testid`.
### Checks
- the new check reports 0 after this change and all nine before, verified by
reverting only the source edits and keeping the test
- `eslint`, `prettier` and `tsc` clean, and prek rewrites nothing for these
files
- full suite: 2203 of 2204 pass. The one failure is `DagsFilters.test.tsx`,
which is already broken on `main` and is not touched here
No newsfragment: UI copy routed through the existing i18n layer, no
behaviour change.
---
##### Was generative AI tooling used to co-author this PR?
- [X] Yes - Claude Code (Opus 5)
Generated-by: Claude Code (Opus 5) following [the
guidelines](https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]