diff --git a/Libraries/Components/Button.js b/Libraries/Components/Button.js index 4ae6a7271e3af1..89ec384fe2c1b7 100644 --- a/Libraries/Components/Button.js +++ b/Libraries/Components/Button.js @@ -146,6 +146,7 @@ type ButtonProps = $ReadOnly<{| onAccessibilityAction?: ?(event: AccessibilityActionEvent) => mixed, accessibilityState?: ?AccessibilityState, accessibilityHint?: ?string, + accessibilityLanguage?: ?Stringish, |}>; /** @@ -277,6 +278,7 @@ class Button extends React.Component { accessible, accessibilityActions, accessibilityHint, + accessibilityLanguage, onAccessibilityAction, } = this.props; const buttonStyles = [styles.button]; @@ -320,6 +322,7 @@ class Button extends React.Component { onAccessibilityAction={onAccessibilityAction} accessibilityLabel={accessibilityLabel} accessibilityHint={accessibilityHint} + accessibilityLanguage={accessibilityLanguage} accessibilityRole="button" accessibilityState={accessibilityState} hasTVPreferredFocus={hasTVPreferredFocus} diff --git a/Libraries/Components/Pressable/Pressable.js b/Libraries/Components/Pressable/Pressable.js index 6aca0169847022..6db069675b8cf0 100644 --- a/Libraries/Components/Pressable/Pressable.js +++ b/Libraries/Components/Pressable/Pressable.js @@ -43,6 +43,7 @@ type Props = $ReadOnly<{| accessibilityActions?: ?$ReadOnlyArray, accessibilityElementsHidden?: ?boolean, accessibilityHint?: ?Stringish, + accessibilityLanguage?: ?Stringish, accessibilityIgnoresInvertColors?: ?boolean, accessibilityLabel?: ?Stringish, accessibilityLiveRegion?: ?('none' | 'polite' | 'assertive'), diff --git a/Libraries/Components/Touchable/TouchableBounce.js b/Libraries/Components/Touchable/TouchableBounce.js index 60f4050e77982e..f77ab0f5738fcf 100644 --- a/Libraries/Components/Touchable/TouchableBounce.js +++ b/Libraries/Components/Touchable/TouchableBounce.js @@ -137,6 +137,7 @@ class TouchableBounce extends React.Component { accessible={this.props.accessible !== false} accessibilityLabel={this.props.accessibilityLabel} accessibilityHint={this.props.accessibilityHint} + accessibilityLanguage={this.props.accessibilityLanguage} accessibilityRole={this.props.accessibilityRole} accessibilityState={this.props.accessibilityState} accessibilityActions={this.props.accessibilityActions} diff --git a/Libraries/Components/Touchable/TouchableHighlight.js b/Libraries/Components/Touchable/TouchableHighlight.js index 3c6a3fe2bde23a..d41513b189b55d 100644 --- a/Libraries/Components/Touchable/TouchableHighlight.js +++ b/Libraries/Components/Touchable/TouchableHighlight.js @@ -296,6 +296,7 @@ class TouchableHighlight extends React.Component { accessible={this.props.accessible !== false} accessibilityLabel={this.props.accessibilityLabel} accessibilityHint={this.props.accessibilityHint} + accessibilityLanguage={this.props.accessibilityLanguage} accessibilityRole={this.props.accessibilityRole} accessibilityState={accessibilityState} accessibilityValue={this.props.accessibilityValue} diff --git a/Libraries/Components/Touchable/TouchableNativeFeedback.js b/Libraries/Components/Touchable/TouchableNativeFeedback.js index 1ae3119ebaf704..910a51c5e30994 100644 --- a/Libraries/Components/Touchable/TouchableNativeFeedback.js +++ b/Libraries/Components/Touchable/TouchableNativeFeedback.js @@ -271,6 +271,7 @@ class TouchableNativeFeedback extends React.Component { ), accessible: this.props.accessible !== false, accessibilityHint: this.props.accessibilityHint, + accessibilityLanguage: this.props.accessibilityLanguage, accessibilityLabel: this.props.accessibilityLabel, accessibilityRole: this.props.accessibilityRole, accessibilityState: accessibilityState, diff --git a/Libraries/Components/Touchable/TouchableOpacity.js b/Libraries/Components/Touchable/TouchableOpacity.js index 6d78af53a9cb45..67e18c06d54c83 100644 --- a/Libraries/Components/Touchable/TouchableOpacity.js +++ b/Libraries/Components/Touchable/TouchableOpacity.js @@ -225,6 +225,7 @@ class TouchableOpacity extends React.Component { accessible={this.props.accessible !== false} accessibilityLabel={this.props.accessibilityLabel} accessibilityHint={this.props.accessibilityHint} + accessibilityLanguage={this.props.accessibilityLanguage} accessibilityRole={this.props.accessibilityRole} accessibilityState={accessibilityState} accessibilityActions={this.props.accessibilityActions} diff --git a/Libraries/Components/Touchable/TouchableWithoutFeedback.js b/Libraries/Components/Touchable/TouchableWithoutFeedback.js index b1bb99c28a9ce8..2b3bd4b930e14b 100755 --- a/Libraries/Components/Touchable/TouchableWithoutFeedback.js +++ b/Libraries/Components/Touchable/TouchableWithoutFeedback.js @@ -33,6 +33,7 @@ type Props = $ReadOnly<{| accessibilityActions?: ?$ReadOnlyArray, accessibilityElementsHidden?: ?boolean, accessibilityHint?: ?Stringish, + accessibilityLanguage?: ?Stringish, accessibilityIgnoresInvertColors?: ?boolean, accessibilityLabel?: ?Stringish, accessibilityLiveRegion?: ?('none' | 'polite' | 'assertive'), @@ -72,6 +73,7 @@ const PASSTHROUGH_PROPS = [ 'accessibilityActions', 'accessibilityElementsHidden', 'accessibilityHint', + 'accessibilityLanguage', 'accessibilityIgnoresInvertColors', 'accessibilityLabel', 'accessibilityLiveRegion', diff --git a/Libraries/Components/View/ReactNativeViewAttributes.js b/Libraries/Components/View/ReactNativeViewAttributes.js index be31fae1ae2b2a..f4ea0d2c1f5219 100644 --- a/Libraries/Components/View/ReactNativeViewAttributes.js +++ b/Libraries/Components/View/ReactNativeViewAttributes.js @@ -21,6 +21,7 @@ const UIView = { accessibilityState: true, accessibilityValue: true, accessibilityHint: true, + accessibilityLanguage: true, importantForAccessibility: true, nativeID: true, testID: true, diff --git a/Libraries/Components/View/ViewPropTypes.js b/Libraries/Components/View/ViewPropTypes.js index 4e22c3b3b53b22..b151095d696f95 100644 --- a/Libraries/Components/View/ViewPropTypes.js +++ b/Libraries/Components/View/ViewPropTypes.js @@ -415,6 +415,15 @@ export type ViewProps = $ReadOnly<{| */ accessibilityHint?: ?Stringish, + /** + * Indicates to the accessibility services that the UI component is in + * a specific language. The provided string should be formatted following + * the BCP 47 specification (https://www.rfc-editor.org/info/bcp47). + * + * @platform ios + */ + accessibilityLanguage?: ?Stringish, + /** * Indicates to accessibility services to treat UI component like a specific role. */ diff --git a/Libraries/NativeComponent/PlatformBaseViewConfig.js b/Libraries/NativeComponent/PlatformBaseViewConfig.js index c3454a3eecc315..a34da238262391 100644 --- a/Libraries/NativeComponent/PlatformBaseViewConfig.js +++ b/Libraries/NativeComponent/PlatformBaseViewConfig.js @@ -147,6 +147,7 @@ const PlatformBaseViewConfig: PartialViewConfigWithoutName = accessibilityLabelledBy: true, accessibilityLabel: true, accessibilityHint: true, + accessibilityLanguage: true, accessibilityRole: true, accessibilityState: true, accessibilityActions: true, @@ -354,6 +355,7 @@ const PlatformBaseViewConfig: PartialViewConfigWithoutName = accessibilityActions: true, accessibilityLabel: true, accessibilityHint: true, + accessibilityLanguage: true, accessibilityValue: true, accessibilityViewIsModal: true, accessibilityElementsHidden: true, diff --git a/Libraries/Text/TextProps.js b/Libraries/Text/TextProps.js index f83602692a2997..e07f315e5399d8 100644 --- a/Libraries/Text/TextProps.js +++ b/Libraries/Text/TextProps.js @@ -44,6 +44,7 @@ export type TextProps = $ReadOnly<{| accessibilityActions?: ?$ReadOnlyArray, onAccessibilityAction?: ?(event: AccessibilityActionEvent) => mixed, accessibilityHint?: ?Stringish, + accessibilityLanguage?: ?Stringish, accessibilityLabel?: ?Stringish, accessibilityRole?: ?AccessibilityRole, accessibilityState?: ?AccessibilityState, diff --git a/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentAccessibilityProvider.mm b/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentAccessibilityProvider.mm index 13723e178bc6e8..b3676564857049 100644 --- a/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentAccessibilityProvider.mm +++ b/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentAccessibilityProvider.mm @@ -67,6 +67,7 @@ - (instancetype)initWithString:(facebook::react::AttributedString)attributedStri firstElement.isAccessibilityElement = YES; firstElement.accessibilityTraits = _view.accessibilityTraits; firstElement.accessibilityLabel = accessibilityLabel; + firstElement.accessibilityLanguage = _view.accessibilityLanguage; firstElement.accessibilityFrame = UIAccessibilityConvertFrameToScreenCoordinates(_view.bounds, _view); [firstElement setAccessibilityActivationPoint:CGPointMake( firstElement.accessibilityFrame.origin.x + 1.0, diff --git a/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm b/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm index 7f7e54d7995947..172020a7e5b638 100644 --- a/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm @@ -296,6 +296,12 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & self.accessibilityElement.accessibilityLabel = RCTNSStringFromStringNilIfEmpty(newViewProps.accessibilityLabel); } + // `accessibilityLanguage` + if (oldViewProps.accessibilityLanguage != newViewProps.accessibilityLanguage) { + self.accessibilityElement.accessibilityLanguage = + RCTNSStringFromStringNilIfEmpty(newViewProps.accessibilityLanguage); + } + // `accessibilityHint` if (oldViewProps.accessibilityHint != newViewProps.accessibilityHint) { self.accessibilityElement.accessibilityHint = RCTNSStringFromStringNilIfEmpty(newViewProps.accessibilityHint); diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m index b62381f2d39e4e..4069b05af3dd2a 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -124,6 +124,7 @@ - (RCTShadowView *)shadowView RCT_REMAP_VIEW_PROPERTY(accessibilityActions, reactAccessibilityElement.accessibilityActions, NSDictionaryArray) RCT_REMAP_VIEW_PROPERTY(accessibilityLabel, reactAccessibilityElement.accessibilityLabel, NSString) RCT_REMAP_VIEW_PROPERTY(accessibilityHint, reactAccessibilityElement.accessibilityHint, NSString) +RCT_REMAP_VIEW_PROPERTY(accessibilityLanguage, reactAccessibilityElement.accessibilityLanguage, NSString) RCT_REMAP_VIEW_PROPERTY(accessibilityValue, reactAccessibilityElement.accessibilityValueInternal, NSDictionary) RCT_REMAP_VIEW_PROPERTY(accessibilityViewIsModal, reactAccessibilityElement.accessibilityViewIsModal, BOOL) RCT_REMAP_VIEW_PROPERTY(accessibilityElementsHidden, reactAccessibilityElement.accessibilityElementsHidden, BOOL) diff --git a/React/Views/UIView+React.h b/React/Views/UIView+React.h index 23246cfb2257a2..21a70337d7ac9c 100644 --- a/React/Views/UIView+React.h +++ b/React/Views/UIView+React.h @@ -120,6 +120,7 @@ @property (nonatomic, copy) NSDictionary *accessibilityState; @property (nonatomic, copy) NSArray *accessibilityActions; @property (nonatomic, copy) NSDictionary *accessibilityValueInternal; +@property (nonatomic, copy) NSString *accessibilityLanguage; /** * Used in debugging to get a description of the view hierarchy rooted at diff --git a/React/Views/UIView+React.m b/React/Views/UIView+React.m index 16169c62f246a9..94ad951e7179d7 100644 --- a/React/Views/UIView+React.m +++ b/React/Views/UIView+React.m @@ -315,6 +315,17 @@ - (void)setAccessibilityActions:(NSArray *)accessibilityActions self, @selector(accessibilityActions), accessibilityActions, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } +- (NSString *)accessibilityLanguage +{ + return objc_getAssociatedObject(self, _cmd); +} + +- (void)setAccessibilityLanguage:(NSString *)accessibilityLanguage +{ + objc_setAssociatedObject( + self, @selector(accessibilityLanguage), accessibilityLanguage, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + - (NSString *)accessibilityRole { return objc_getAssociatedObject(self, _cmd); diff --git a/ReactCommon/react/renderer/components/view/AccessibilityProps.cpp b/ReactCommon/react/renderer/components/view/AccessibilityProps.cpp index 98311ae553bb79..21f7bec8c37c23 100644 --- a/ReactCommon/react/renderer/components/view/AccessibilityProps.cpp +++ b/ReactCommon/react/renderer/components/view/AccessibilityProps.cpp @@ -67,6 +67,12 @@ AccessibilityProps::AccessibilityProps( "accessibilityHint", sourceProps.accessibilityHint, "")), + accessibilityLanguage(convertRawProp( + context, + rawProps, + "accessibilityLanguage", + sourceProps.accessibilityLanguage, + "")), accessibilityValue(convertRawProp( context, rawProps, diff --git a/ReactCommon/react/renderer/components/view/AccessibilityProps.h b/ReactCommon/react/renderer/components/view/AccessibilityProps.h index 53b85874a53555..d0df2cc21122a4 100644 --- a/ReactCommon/react/renderer/components/view/AccessibilityProps.h +++ b/ReactCommon/react/renderer/components/view/AccessibilityProps.h @@ -35,6 +35,7 @@ class AccessibilityProps { AccessibilityLiveRegion::None}; std::string accessibilityRole{""}; std::string accessibilityHint{""}; + std::string accessibilityLanguage{""}; AccessibilityValue accessibilityValue; std::vector accessibilityActions{}; bool accessibilityViewIsModal{false}; diff --git a/packages/rn-tester/js/examples/Accessibility/AccessibilityIOSExample.js b/packages/rn-tester/js/examples/Accessibility/AccessibilityIOSExample.js index 2328d62c146fa8..b8572b3fb98db2 100644 --- a/packages/rn-tester/js/examples/Accessibility/AccessibilityIOSExample.js +++ b/packages/rn-tester/js/examples/Accessibility/AccessibilityIOSExample.js @@ -55,6 +55,9 @@ class AccessibilityIOSExample extends React.Component { This view's children are hidden from the accessibility tree + + This view's language should be `it-IT` + ); }