diff --git a/React-Core.podspec b/React-Core.podspec index bc4dfb468ab..b85e969e349 100644 --- a/React-Core.podspec +++ b/React-Core.podspec @@ -44,6 +44,7 @@ Pod::Spec.new do |s| s.author = "Facebook, Inc. and its affiliates" s.platforms = { :ios => "10.0", :tvos => "10.0" } s.source = source + s.resource_bundle = { "AccessibilityResources" => ["React/AccessibilityResources/*.lproj"]} s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags s.header_dir = "React" s.framework = "JavaScriptCore" diff --git a/React/AccessibilityResources/en.lproj/Localizable.strings b/React/AccessibilityResources/en.lproj/Localizable.strings new file mode 100644 index 00000000000..866bd0d8838 --- /dev/null +++ b/React/AccessibilityResources/en.lproj/Localizable.strings @@ -0,0 +1,26 @@ +/* + Localizable.strings + React +*/ +"alert"="alert"; +"checkbox"="checkbox"; +"combobox"="combo box"; +"menu"="menu"; +"menubar"="menu bar"; +"menuitem"="menu item"; +"progressbar"="progress bar"; +"radio"="radio button"; +"radiogroup"="radio group"; +"scrollbar"="scroll bar"; +"spinbutton"="spin button"; +"switch"="switch"; +"tab"="tab description"; +"tablist"="tab list"; +"timer"="timer"; +"toolbar"="tool bar"; +"checked"="checked"; +"unchecked"="not checked"; +"busy"="busy"; +"expanded"="expanded"; +"collapsed"="collapsed"; +"mixed"="mixed"; diff --git a/React/Views/RCTView.m b/React/Views/RCTView.m index 831a1b52fc5..6936549fa6e 100644 --- a/React/Views/RCTView.m +++ b/React/Views/RCTView.m @@ -202,6 +202,51 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:unused) - (NSString *)accessibilityValue { + static dispatch_once_t onceToken; + static NSDictionary *rolesAndStatesDescription = nil; + + dispatch_once(&onceToken, ^{ + NSString *bundlePath = [[NSBundle mainBundle]pathForResource:@"AccessibilityResources" ofType:@"bundle"]; + NSBundle *bundle = [NSBundle bundleWithPath:bundlePath]; + + if (bundle) { + NSURL *url = [bundle URLForResource:@"Localizable" withExtension:@"strings"]; + if (@available(iOS 11.0, *)) { + rolesAndStatesDescription = [NSDictionary dictionaryWithContentsOfURL:url error:nil]; + } else { + // Fallback on earlier versions + rolesAndStatesDescription = [NSDictionary dictionaryWithContentsOfURL:url]; + } + } + if (rolesAndStatesDescription == nil) { + NSLog(@"Cannot load localized accessibility strings."); + rolesAndStatesDescription = @{ + @"alert" : @"alert", + @"checkbox" : @"checkbox", + @"combobox" : @"combo box", + @"menu" : @"menu", + @"menubar" : @"menu bar", + @"menuitem" : @"menu item", + @"progressbar" : @"progress bar", + @"radio" : @"radio button", + @"radiogroup" : @"radio group", + @"scrollbar" : @"scroll bar", + @"spinbutton" : @"spin button", + @"switch" : @"switch", + @"tab" : @"tab", + @"tablist" : @"tab list", + @"timer" : @"timer", + @"toolbar" : @"tool bar", + @"checked" : @"checked", + @"unchecked" : @"not checked", + @"busy" : @"busy", + @"expanded" : @"expanded", + @"collapsed" : @"collapsed", + @"mixed": @"mixed", + }; + } + }); + if ((self.accessibilityTraits & SwitchAccessibilityTrait) == SwitchAccessibilityTrait) { for (NSString *state in self.accessibilityState) { id val = self.accessibilityState[state]; @@ -214,41 +259,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:unused) } } NSMutableArray *valueComponents = [NSMutableArray new]; - static NSDictionary *roleDescriptions = nil; - static dispatch_once_t onceToken1; - dispatch_once(&onceToken1, ^{ - roleDescriptions = @{ - @"alert" : @"alert", - @"checkbox" : @"checkbox", - @"combobox" : @"combo box", - @"menu" : @"menu", - @"menubar" : @"menu bar", - @"menuitem" : @"menu item", - @"progressbar" : @"progress bar", - @"radio" : @"radio button", - @"radiogroup" : @"radio group", - @"scrollbar" : @"scroll bar", - @"spinbutton" : @"spin button", - @"switch" : @"switch", - @"tab" : @"tab", - @"tablist" : @"tab list", - @"timer" : @"timer", - @"toolbar" : @"tool bar", - }; - }); - static NSDictionary *stateDescriptions = nil; - static dispatch_once_t onceToken2; - dispatch_once(&onceToken2, ^{ - stateDescriptions = @{ - @"checked" : @"checked", - @"unchecked" : @"not checked", - @"busy" : @"busy", - @"expanded" : @"expanded", - @"collapsed" : @"collapsed", - @"mixed": @"mixed", - }; - }); - NSString *roleDescription = self.accessibilityRole ? roleDescriptions[self.accessibilityRole]: nil; + NSString *roleDescription = self.accessibilityRole ? rolesAndStatesDescription[self.accessibilityRole]: nil; if (roleDescription) { [valueComponents addObject:roleDescription]; } @@ -259,16 +270,16 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:unused) } if ([state isEqualToString:@"checked"]) { if ([val isKindOfClass:[NSNumber class]]) { - [valueComponents addObject:stateDescriptions[[val boolValue] ? @"checked" : @"unchecked"]]; + [valueComponents addObject:rolesAndStatesDescription[[val boolValue] ? @"checked" : @"unchecked"]]; } else if ([val isKindOfClass:[NSString class]] && [val isEqualToString:@"mixed"]) { - [valueComponents addObject:stateDescriptions[@"mixed"]]; + [valueComponents addObject:rolesAndStatesDescription[@"mixed"]]; } } if ([state isEqualToString:@"expanded"] && [val isKindOfClass:[NSNumber class]]) { - [valueComponents addObject:stateDescriptions[[val boolValue] ? @"expanded" : @"collapsed"]]; + [valueComponents addObject:rolesAndStatesDescription[[val boolValue] ? @"expanded" : @"collapsed"]]; } if ([state isEqualToString:@"busy"] && [val isKindOfClass:[NSNumber class]] && [val boolValue]) { - [valueComponents addObject:stateDescriptions[@"busy"]]; + [valueComponents addObject:rolesAndStatesDescription[@"busy"]]; } }