Skip to content

Commit 7b05b09

Browse files
dgopsqfacebook-github-bot
authored andcommitted
Integrated iOS-only accessibilityLanguage prop (#33090)
Summary: This PR fixes #30891 This PR is going to add an `accessibilityLanguage` prop to all the available components. This props is currently working only on iOS and should follow the [guidelines of the relative configuration](https://developer.apple.com/documentation/objectivec/nsobject/1615192-accessibilitylanguage). I'm in no way an expert on native programming (especially Objective-C) so I'm open to changes / improvements 🙂 ## Changelog <!-- Help reviewers and the release process by writing your own changelog entry. For an example, see: https://github.com/facebook/react-native/wiki/Changelog --> [iOS] [Added] - Integrated the `accessibilityLanguage` prop to all the available components. The prop is available for any platform but it will work only on iOS. Pull Request resolved: #33090 Test Plan: This has been tested using both the Simulator, checking for the `Language` attribute, and using a physical device with the Voice Over enabled. <img width="1083" alt="Screenshot 2022-02-11 at 13 17 32" src="https://user-images.githubusercontent.com/5963683/153590415-65fcb4ff-8f31-4a0f-90e5-8eb1aae6aa3d.png"> Reviewed By: philIip Differential Revision: D34523608 Pulled By: rh389 fbshipit-source-id: b5d77fc0b3d76ea8ed8f30c8385459ba98122ff6
1 parent ff76952 commit 7b05b09

19 files changed

+53
-0
lines changed

Libraries/Components/Button.js

+3
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ type ButtonProps = $ReadOnly<{|
146146
onAccessibilityAction?: ?(event: AccessibilityActionEvent) => mixed,
147147
accessibilityState?: ?AccessibilityState,
148148
accessibilityHint?: ?string,
149+
accessibilityLanguage?: ?Stringish,
149150
|}>;
150151

151152
/**
@@ -277,6 +278,7 @@ class Button extends React.Component<ButtonProps> {
277278
accessible,
278279
accessibilityActions,
279280
accessibilityHint,
281+
accessibilityLanguage,
280282
onAccessibilityAction,
281283
} = this.props;
282284
const buttonStyles = [styles.button];
@@ -320,6 +322,7 @@ class Button extends React.Component<ButtonProps> {
320322
onAccessibilityAction={onAccessibilityAction}
321323
accessibilityLabel={accessibilityLabel}
322324
accessibilityHint={accessibilityHint}
325+
accessibilityLanguage={accessibilityLanguage}
323326
accessibilityRole="button"
324327
accessibilityState={accessibilityState}
325328
hasTVPreferredFocus={hasTVPreferredFocus}

Libraries/Components/Pressable/Pressable.js

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ type Props = $ReadOnly<{|
4343
accessibilityActions?: ?$ReadOnlyArray<AccessibilityActionInfo>,
4444
accessibilityElementsHidden?: ?boolean,
4545
accessibilityHint?: ?Stringish,
46+
accessibilityLanguage?: ?Stringish,
4647
accessibilityIgnoresInvertColors?: ?boolean,
4748
accessibilityLabel?: ?Stringish,
4849
accessibilityLiveRegion?: ?('none' | 'polite' | 'assertive'),

Libraries/Components/Touchable/TouchableBounce.js

+1
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ class TouchableBounce extends React.Component<Props, State> {
137137
accessible={this.props.accessible !== false}
138138
accessibilityLabel={this.props.accessibilityLabel}
139139
accessibilityHint={this.props.accessibilityHint}
140+
accessibilityLanguage={this.props.accessibilityLanguage}
140141
accessibilityRole={this.props.accessibilityRole}
141142
accessibilityState={this.props.accessibilityState}
142143
accessibilityActions={this.props.accessibilityActions}

Libraries/Components/Touchable/TouchableHighlight.js

+1
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ class TouchableHighlight extends React.Component<Props, State> {
296296
accessible={this.props.accessible !== false}
297297
accessibilityLabel={this.props.accessibilityLabel}
298298
accessibilityHint={this.props.accessibilityHint}
299+
accessibilityLanguage={this.props.accessibilityLanguage}
299300
accessibilityRole={this.props.accessibilityRole}
300301
accessibilityState={accessibilityState}
301302
accessibilityValue={this.props.accessibilityValue}

Libraries/Components/Touchable/TouchableNativeFeedback.js

+1
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ class TouchableNativeFeedback extends React.Component<Props, State> {
271271
),
272272
accessible: this.props.accessible !== false,
273273
accessibilityHint: this.props.accessibilityHint,
274+
accessibilityLanguage: this.props.accessibilityLanguage,
274275
accessibilityLabel: this.props.accessibilityLabel,
275276
accessibilityRole: this.props.accessibilityRole,
276277
accessibilityState: accessibilityState,

Libraries/Components/Touchable/TouchableOpacity.js

+1
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ class TouchableOpacity extends React.Component<Props, State> {
225225
accessible={this.props.accessible !== false}
226226
accessibilityLabel={this.props.accessibilityLabel}
227227
accessibilityHint={this.props.accessibilityHint}
228+
accessibilityLanguage={this.props.accessibilityLanguage}
228229
accessibilityRole={this.props.accessibilityRole}
229230
accessibilityState={accessibilityState}
230231
accessibilityActions={this.props.accessibilityActions}

Libraries/Components/Touchable/TouchableWithoutFeedback.js

+2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type Props = $ReadOnly<{|
3333
accessibilityActions?: ?$ReadOnlyArray<AccessibilityActionInfo>,
3434
accessibilityElementsHidden?: ?boolean,
3535
accessibilityHint?: ?Stringish,
36+
accessibilityLanguage?: ?Stringish,
3637
accessibilityIgnoresInvertColors?: ?boolean,
3738
accessibilityLabel?: ?Stringish,
3839
accessibilityLiveRegion?: ?('none' | 'polite' | 'assertive'),
@@ -72,6 +73,7 @@ const PASSTHROUGH_PROPS = [
7273
'accessibilityActions',
7374
'accessibilityElementsHidden',
7475
'accessibilityHint',
76+
'accessibilityLanguage',
7577
'accessibilityIgnoresInvertColors',
7678
'accessibilityLabel',
7779
'accessibilityLiveRegion',

Libraries/Components/View/ReactNativeViewAttributes.js

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const UIView = {
2121
accessibilityState: true,
2222
accessibilityValue: true,
2323
accessibilityHint: true,
24+
accessibilityLanguage: true,
2425
importantForAccessibility: true,
2526
nativeID: true,
2627
testID: true,

Libraries/Components/View/ViewPropTypes.js

+9
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,15 @@ export type ViewProps = $ReadOnly<{|
415415
*/
416416
accessibilityHint?: ?Stringish,
417417

418+
/**
419+
* Indicates to the accessibility services that the UI component is in
420+
* a specific language. The provided string should be formatted following
421+
* the BCP 47 specification (https://www.rfc-editor.org/info/bcp47).
422+
*
423+
* @platform ios
424+
*/
425+
accessibilityLanguage?: ?Stringish,
426+
418427
/**
419428
* Indicates to accessibility services to treat UI component like a specific role.
420429
*/

Libraries/NativeComponent/PlatformBaseViewConfig.js

+2
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ const PlatformBaseViewConfig: PartialViewConfigWithoutName =
147147
accessibilityLabelledBy: true,
148148
accessibilityLabel: true,
149149
accessibilityHint: true,
150+
accessibilityLanguage: true,
150151
accessibilityRole: true,
151152
accessibilityState: true,
152153
accessibilityActions: true,
@@ -354,6 +355,7 @@ const PlatformBaseViewConfig: PartialViewConfigWithoutName =
354355
accessibilityActions: true,
355356
accessibilityLabel: true,
356357
accessibilityHint: true,
358+
accessibilityLanguage: true,
357359
accessibilityValue: true,
358360
accessibilityViewIsModal: true,
359361
accessibilityElementsHidden: true,

Libraries/Text/TextProps.js

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export type TextProps = $ReadOnly<{|
4444
accessibilityActions?: ?$ReadOnlyArray<AccessibilityActionInfo>,
4545
onAccessibilityAction?: ?(event: AccessibilityActionEvent) => mixed,
4646
accessibilityHint?: ?Stringish,
47+
accessibilityLanguage?: ?Stringish,
4748
accessibilityLabel?: ?Stringish,
4849
accessibilityRole?: ?AccessibilityRole,
4950
accessibilityState?: ?AccessibilityState,

React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentAccessibilityProvider.mm

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ - (instancetype)initWithString:(facebook::react::AttributedString)attributedStri
6767
firstElement.isAccessibilityElement = YES;
6868
firstElement.accessibilityTraits = _view.accessibilityTraits;
6969
firstElement.accessibilityLabel = accessibilityLabel;
70+
firstElement.accessibilityLanguage = _view.accessibilityLanguage;
7071
firstElement.accessibilityFrame = UIAccessibilityConvertFrameToScreenCoordinates(_view.bounds, _view);
7172
[firstElement setAccessibilityActivationPoint:CGPointMake(
7273
firstElement.accessibilityFrame.origin.x + 1.0,

React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm

+6
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,12 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &
296296
self.accessibilityElement.accessibilityLabel = RCTNSStringFromStringNilIfEmpty(newViewProps.accessibilityLabel);
297297
}
298298

299+
// `accessibilityLanguage`
300+
if (oldViewProps.accessibilityLanguage != newViewProps.accessibilityLanguage) {
301+
self.accessibilityElement.accessibilityLanguage =
302+
RCTNSStringFromStringNilIfEmpty(newViewProps.accessibilityLanguage);
303+
}
304+
299305
// `accessibilityHint`
300306
if (oldViewProps.accessibilityHint != newViewProps.accessibilityHint) {
301307
self.accessibilityElement.accessibilityHint = RCTNSStringFromStringNilIfEmpty(newViewProps.accessibilityHint);

React/Views/RCTViewManager.m

+1
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ - (RCTShadowView *)shadowView
124124
RCT_REMAP_VIEW_PROPERTY(accessibilityActions, reactAccessibilityElement.accessibilityActions, NSDictionaryArray)
125125
RCT_REMAP_VIEW_PROPERTY(accessibilityLabel, reactAccessibilityElement.accessibilityLabel, NSString)
126126
RCT_REMAP_VIEW_PROPERTY(accessibilityHint, reactAccessibilityElement.accessibilityHint, NSString)
127+
RCT_REMAP_VIEW_PROPERTY(accessibilityLanguage, reactAccessibilityElement.accessibilityLanguage, NSString)
127128
RCT_REMAP_VIEW_PROPERTY(accessibilityValue, reactAccessibilityElement.accessibilityValueInternal, NSDictionary)
128129
RCT_REMAP_VIEW_PROPERTY(accessibilityViewIsModal, reactAccessibilityElement.accessibilityViewIsModal, BOOL)
129130
RCT_REMAP_VIEW_PROPERTY(accessibilityElementsHidden, reactAccessibilityElement.accessibilityElementsHidden, BOOL)

React/Views/UIView+React.h

+1
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@
120120
@property (nonatomic, copy) NSDictionary<NSString *, id> *accessibilityState;
121121
@property (nonatomic, copy) NSArray<NSDictionary *> *accessibilityActions;
122122
@property (nonatomic, copy) NSDictionary *accessibilityValueInternal;
123+
@property (nonatomic, copy) NSString *accessibilityLanguage;
123124

124125
/**
125126
* Used in debugging to get a description of the view hierarchy rooted at

React/Views/UIView+React.m

+11
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,17 @@ - (void)setAccessibilityActions:(NSArray<NSDictionary *> *)accessibilityActions
315315
self, @selector(accessibilityActions), accessibilityActions, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
316316
}
317317

318+
- (NSString *)accessibilityLanguage
319+
{
320+
return objc_getAssociatedObject(self, _cmd);
321+
}
322+
323+
- (void)setAccessibilityLanguage:(NSString *)accessibilityLanguage
324+
{
325+
objc_setAssociatedObject(
326+
self, @selector(accessibilityLanguage), accessibilityLanguage, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
327+
}
328+
318329
- (NSString *)accessibilityRole
319330
{
320331
return objc_getAssociatedObject(self, _cmd);

ReactCommon/react/renderer/components/view/AccessibilityProps.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ AccessibilityProps::AccessibilityProps(
6767
"accessibilityHint",
6868
sourceProps.accessibilityHint,
6969
"")),
70+
accessibilityLanguage(convertRawProp(
71+
context,
72+
rawProps,
73+
"accessibilityLanguage",
74+
sourceProps.accessibilityLanguage,
75+
"")),
7076
accessibilityValue(convertRawProp(
7177
context,
7278
rawProps,

ReactCommon/react/renderer/components/view/AccessibilityProps.h

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class AccessibilityProps {
3535
AccessibilityLiveRegion::None};
3636
std::string accessibilityRole{""};
3737
std::string accessibilityHint{""};
38+
std::string accessibilityLanguage{""};
3839
AccessibilityValue accessibilityValue;
3940
std::vector<AccessibilityAction> accessibilityActions{};
4041
bool accessibilityViewIsModal{false};

packages/rn-tester/js/examples/Accessibility/AccessibilityIOSExample.js

+3
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ class AccessibilityIOSExample extends React.Component<Props> {
5555
This view's children are hidden from the accessibility tree
5656
</Text>
5757
</View>
58+
<View accessible={true} accessibilityLanguage="it-IT">
59+
<Text>This view's language should be `it-IT`</Text>
60+
</View>
5861
</RNTesterBlock>
5962
);
6063
}

0 commit comments

Comments
 (0)