flutter / flutter

Flutter makes it easy and fast to build beautiful apps for mobile and beyond
https://flutter.dev
BSD 3-Clause "New" or "Revised" License
163.16k stars 26.86k forks source link

Allow setting assistive technology locale via Semantics widget #124889

Open Leffe108 opened 1 year ago

Leffe108 commented 1 year ago

Use case

In a app with mixed language content there can be semantics in a different language than the main app language. For example if the static text of an app is localized to several languages but it also receive content from a server that is user/customer generated and the user/customer has only written in one language. For example: use any social media platform in non-English UI language and follow some English content creators. Soon you will have mixed language content in your feed. If this is not propagated to assistive tech, then a screen reader such as TalkBack or VoiceOver may use the wrong voice engine to read content and that makes it harder to hear what it says.

It is however currently not possible to set the locale to use for a Semantic node created by the Semantics widget (and have that communicated to assistive tech).

Another use case could be to wrap the whole MaterialApp or a Scaffold in a Semantics node to set the assistive tech locale of the app to resolve https://github.com/flutter/flutter/issues/99600 (WCAG Level A)

Resolves:

Proposal

Add a locale parameter to Semantics widget constructor which will set the locale of the provided semantic labels.

I propose that it does propagate the announced locale for assistive tech downwards to children in the tree. Alternatively, a flag could be added to toggle if it should propagate down or not.

Why propagate downwards?

Since apps can have mixed content and that can be both the visible text and hidden semantics node, and Localizations.override does not change the reported locale to assistive tech, so there need to be some other way to do that for assistive tech. Semastics widget seem to be a good place to do that as it can affect both that Semantic node and its children.

Alternatives considered

For text that is displayed visually, it is possible to use a TextSpan in a RichText to set the locale. This will then be carried over to the assistive technology layer (except on web and windows). But TextSpan itself is not a widget nor does it accept widgets as children, so it cannot be used more generally to set the assistive tech locale of other widgets in the app, such as a Semantic node further down in the tree.

Localizations.override can override the locale for a sub tree of the app, but it is only used for I18N purposes. It is not carried over to assistive technology.

Leffe108 commented 10 months ago

I have noticed that https://github.com/flutter/engine/pull/44616 has landed in the engine. Which to my understanding relates to AttributedString on the framework side. And the engine PR basically resolves in allowing platform implementation on Web and Desktop for this, while iOS and Android already supports it somehow?

So from what I understand implementation of this proposal is two fold:

  1. A way to provide the desired semantics locale in the widget tree
  2. At some place in the construction of semantics tree, read/obtain the current semantic locale and apply to attributed strings.

Alternatively, the overhead to attribute every string is deemed to large, and an alternative bridge to the engine is needed to propagates defaults for the entire tree or parts of the tree to the engine side?

Researching/discussing 2.

My limited knowledge says that widgets create render objects which creates a SemanticsConfiguration which does carry AttributedString data that is then translated into SemanticNodes in the semantic tree.

It does seem that render boxes that does more than layout should override describeSemanticsConfiguration() and make it alter a passed SemanticsConfiguration object.

So, either every one of these render boxes has to read a parent semantic locale and add it to the AttributedString (lots of changes trough out the code base and any 3rd party widget that implements render boxes has to implement it too), or at a later step when the SemanticsConfiguration is processed to SemanticsNode, the locale is applied. Which then likely let any locale set by attributed string have priority over locale defaults set from higher up in the widgets tree. Likely with some complications that currently is above my knowledge on how to map from widget tree, via render objects to semantics tree.

Question

Does this makes sense, or any input/ideas?