Repository navigation
Localize the automation peers' control types and row names - #449
SolidRockProgrammer wants to merge 2 commits into
Conversation
The automation peers returned English literals for their localized
control types ("table view", "column header", "row header", "cell")
and composed row names as "Row {n}", so a screen reader on a German,
Japanese or Chinese UI announced those in English while every other
TableView string already went through TableViewLocalizedStrings.
Move them to the .resw files in every shipped language, with
FormatRowNumber composing "Row {0}" through the current culture.
The tests swap each resource for a sentinel, so they fail on a peer
that returns a literal even under en-US.
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Peer-level tests do not cover the three indexed row-name integrations.
Review effort: Balanced
Findings: 1
Open (1)
What changed in this PR
Localizes UI Automation control types and row names for screen-reader users.
Changes:
- Adds six automation resources across eleven languages.
- Updates five automation peers to consume localized values.
- Adds localization-focused UI tests.
| File | Description |
|---|---|
tests/TableViewAutomationPeerTests.cs |
Tests localized automation resources. |
src/TableViewLocalizedStrings.cs |
Loads and formats new resources. |
src/AutomationPeers/TableViewAutomationPeer.cs |
Localizes the table control type. |
src/AutomationPeers/TableViewColumnHeaderAutomationPeer.cs |
Localizes the column-header type. |
src/AutomationPeers/TableViewRowHeaderAutomationPeer.cs |
Localizes row-header type and number. |
src/AutomationPeers/TableViewRowAutomationPeer.cs |
Localizes row names. |
src/AutomationPeers/TableViewCellAutomationPeer.cs |
Localizes cell type and row number. |
src/Strings/en-US/WinUI.TableView.resw |
Adds English resources. |
src/Strings/de-DE/WinUI.TableView.resw |
Adds German resources. |
src/Strings/es-ES/WinUI.TableView.resw |
Adds Spanish resources. |
src/Strings/fa-IR/WinUI.TableView.resw |
Adds Persian resources. |
src/Strings/ja-JP/WinUI.TableView.resw |
Adds Japanese resources. |
src/Strings/pl-PL/WinUI.TableView.resw |
Adds Polish resources. |
src/Strings/pt-BR/WinUI.TableView.resw |
Adds Portuguese resources. |
src/Strings/ru-RU/WinUI.TableView.resw |
Adds Russian resources. |
src/Strings/sk-SK/WinUI.TableView.resw |
Adds Slovak resources. |
src/Strings/zh-CN/WinUI.TableView.resw |
Adds Simplified Chinese resources. |
src/Strings/zh-TW/WinUI.TableView.resw |
Adds Traditional Chinese resources. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| } | ||
|
|
||
| [UITestMethod] | ||
| public void FormatRowNumber_UsesTheRowNumberResource() |
There was a problem hiding this comment.
@SolidRockProgrammer do you think copilot is right here?
There was a problem hiding this comment.
Yes, Copilot is right. The peers were already calling FormatRowNumber for the indexed name, so the code was fine. But nothing tested those three call sites. FormatRowNumber_UsesTheRowNumberResource only proves the formatter reads RowNumber, so you could put the "Row {n}" literal back in any of the three peers and every test would still pass.
I've pushed ad90bff, which adds three tests:
TableViewRowAutomationPeer_IndexedName_ComesFromResourcesTableViewRowHeaderAutomationPeer_IndexedName_ComesFromResourcesTableViewCellAutomationPeer_IndexedName_ComesFromResources
The name only gets a row number when the row has an index, so each test realizes the second row of a loaded two-row TableView. It then sets RowNumber to a sentinel ("R#{0}") and reads the peer's name. The row and row-header tests compare the whole name. The cell test checks the name contains the row part, since the rest of a cell's name is the column header and the value.
Results:
- With this change: 374/374 passed.
- As a negative control, I put the three peers back to
$"Row {n + 1}"literals: 371 passed, 3 failed, and the three were exactly the new tests.
The FormatRowNumber test proved the formatter reads RowNumber, but not that the three peers call it: restoring their English literals left every test green. Each new test realizes a row in a loaded TableView, swaps RowNumber for a sentinel and reads the peer's name.
…header peer (#6) * Localize the automation peers' control types and row names The automation peers returned English literals for their localized control types ("table view", "column header", "row header", "cell") and composed row names as "Row {n}", so a screen reader on a German, Japanese or Chinese UI announced those in English while every other TableView string already went through TableViewLocalizedStrings. Move them to the .resw files in every shipped language, with FormatRowNumber composing "Row {0}" through the current culture. The tests swap each resource for a sentinel, so they fail on a peer that returns a literal even under en-US. * Test the indexed row names through the row, row-header and cell peers The FormatRowNumber test proved the formatter reads RowNumber, but not that the three peers call it: restoring their English literals left every test green. Each new test realizes a row in a loaded TableView, swaps RowNumber for a sentinel and reads the peer's name. * DH-1944 Let the host supply TableView strings; create v1.5.0's header peer TableViewLocalization.StringResolver lets an application supply any TableView string by its resource key, read on every access so a language change at run time is followed. A null or empty answer, or a resolver that throws, keeps the library's own value; a malformed RowNumber format falls back to the library's. The control types and row names were moved onto TableViewLocalizedStrings (the two preceding commits, upstream PR w-ahmad#449), so v1.5.0's column-header peer no longer carries English literals. It replaces the DH-1445 header peer, which it is a superset of (Invoke cycles the sort; sort/filter hints) with the same name rule. Cell, table, row and row-header peers are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>

Summary
The automation peers return English literals that reach the UI Automation tree:
"table view","column header","row header"and"cell"."Row {n}", and the row name falls back to"Row".Every other TableView string already goes through
TableViewLocalizedStrings. On a German, Japanese or Chinese UI, a screen reader therefore announces these few words in English.This PR moves them to the
.reswfiles:TableViewControlType,ColumnHeaderControlType,RowHeaderControlType,CellControlType,RowNumber("Row {0}") andRow, in all eleven shipped languages. Each has a<comment>explaining it.TableViewLocalizedStrings.FormatRowNumber(int)formatsRowNumberwithCultureInfo.CurrentCulture.The non-English values are my best translations. They use the terms Windows itself uses where one exists ("Spaltenkopf", "列ヘッダー", "列標題" and so on). Corrections from native speakers are very welcome.
Tests
There are seven new
[UITestMethod]s inTableViewAutomationPeerTests. Each*_ComesFromResourcestest swaps the resource for a sentinel and asserts the peer returns it. That means the tests fail on a peer that returns a literal even under en-US, where the literal and the resource agree.*_ComesFromResourcestests.Note on CI
While doing this I noticed that
ci-build.yml's Run Tests step runs the.appxrecipepath as a command, with novstest.console.exein front of it. The step exits green without running any tests. The numbers above come from the same workflow with that one line changed tovstest.console.exe tests\bin\x64\Release\net10.0-windows10.0.26100.0\WinUI.TableView.Tests.build.appxrecipe ...(darenm/Setup-VSTestalready puts it on PATH). I have left the workflow alone in this PR; happy to send that as a separate one.