Skip to content

Localize the automation peers' control types and row names - #449

Open
SolidRockProgrammer wants to merge 2 commits into
w-ahmad:mainfrom
Datgel:feat/localize-automation-peers
Open

SolidRockProgrammer wants to merge 2 commits into
w-ahmad:mainfrom
Datgel:feat/localize-automation-peers

Conversation

@SolidRockProgrammer

Copy link
Copy Markdown

Summary

The automation peers return English literals that reach the UI Automation tree:

  • Localized control types: "table view", "column header", "row header" and "cell".
  • Row names: the row, row-header and cell peers compose "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 .resw files:

  • New keys TableViewControlType, ColumnHeaderControlType, RowHeaderControlType, CellControlType, RowNumber ("Row {0}") and Row, in all eleven shipped languages. Each has a <comment> explaining it.
  • TableViewLocalizedStrings.FormatRowNumber(int) formats RowNumber with CultureInfo.CurrentCulture.
  • The five peers read the resources. No behaviour changes in en-US.

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 in TableViewAutomationPeerTests. Each *_ComesFromResources test 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.

  • With this change: 371/371 passed.
  • As a negative control, I ran the same tests with the peers restored to their literals: 366 passed, 5 failed. The five that failed were exactly the five *_ComesFromResources tests.

Note on CI

While doing this I noticed that ci-build.yml's Run Tests step runs the .appxrecipe path as a command, with no vstest.console.exe in front of it. The step exits green without running any tests. The numbers above come from the same workflow with that one line changed to vstest.console.exe tests\bin\x64\Release\net10.0-windows10.0.26100.0\WinUI.TableView.Tests.build.appxrecipe ... (darenm/Setup-VSTest already puts it on PATH). I have left the workflow alone in this PR; happy to send that as a separate one.

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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Peer-level tests do not cover the three indexed row-name integrations.

Review effort: Balanced
Findings: 1 Medium severity

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()

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SolidRockProgrammer do you think copilot is right here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_ComesFromResources
  • TableViewRowHeaderAutomationPeer_IndexedName_ComesFromResources
  • TableViewCellAutomationPeer_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.
SolidRockProgrammer added a commit to Datgel/WinUI.TableView that referenced this pull request Oct 3, 2026
…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>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants