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]

Reply via email to