[ios] Fix and expand accessibility feature transliteration
- Fixed pre-iOS 11 compatibility. - Expanded transliteration to all supported languages.
This commit is contained in:
@@ -6,6 +6,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@property (nonatomic, readonly, getter=isMapboxStreets) BOOL mapboxStreets;
|
||||
|
||||
+ (NS_SET_OF(NSString *) *)mapboxStreetsLanguages;
|
||||
|
||||
+ (NSString *)preferredMapboxStreetsLanguage;
|
||||
|
||||
- (NS_DICTIONARY_OF(NSString *, NSString *) *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage;
|
||||
|
||||
@@ -20,6 +20,20 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
- (NSString *)mgl_titleCasedStringWithLocale:(NSLocale *)locale;
|
||||
|
||||
/**
|
||||
Returns a transliterated representation of the receiver using the specified
|
||||
script. If transliteration fails, the receiver will be returned.
|
||||
|
||||
Only supports scripts for languages used by Mapbox Streets.
|
||||
|
||||
On iOS 8 or older, this will method will always return the untransliterated
|
||||
receiver.
|
||||
|
||||
@param script The four-letter code representing the name of the script, as
|
||||
specified by ISO 15924.
|
||||
*/
|
||||
- (NSString *)mgl_stringByTransliteratingIntoScript:(NSString *)script;
|
||||
|
||||
@end
|
||||
|
||||
@interface NSAttributedString (MGLAdditions)
|
||||
|
||||
@@ -41,6 +41,25 @@
|
||||
return string;
|
||||
}
|
||||
|
||||
- (NSString *)mgl_stringByTransliteratingIntoScript:(NSString *)script {
|
||||
if (@available(iOS 9.0, *)) {
|
||||
NSMutableString *string = self.mutableCopy;
|
||||
NSStringTransform transform;
|
||||
if ([script isEqualToString:@"Latn"]) {
|
||||
transform = NSStringTransformToLatin;
|
||||
} else if ([script isEqualToString:@"Hans"]) {
|
||||
// No transform available.
|
||||
} else if ([script isEqualToString:@"Cyrl"]) {
|
||||
transform = @"Any-Latin; Latin-Cyrillic";
|
||||
} else if ([script isEqualToString:@"Arab"]) {
|
||||
transform = @"Any-Latin; Latin-Arabic";
|
||||
}
|
||||
return transform ? [string stringByApplyingTransform:transform reverse:NO] : string;
|
||||
} else {
|
||||
return self;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSAttributedString (MGLAdditions)
|
||||
|
||||
@@ -39,4 +39,38 @@
|
||||
XCTAssertEqualObjects([@"Improve This iPhone" mgl_titleCasedStringWithLocale:locale], @"Improve This iPhone");
|
||||
}
|
||||
|
||||
- (void)testTransliteratedString {
|
||||
if (@available(iOS 9.0, *)) {
|
||||
XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Latn"], @"Portland");
|
||||
XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Hans"], @"Portland");
|
||||
XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"Портланд");
|
||||
XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Arab"], @"پُرتلَند");
|
||||
XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Fake"], @"Portland");
|
||||
|
||||
XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Latn"], @"běi jīng");
|
||||
XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Hans"], @"北京");
|
||||
XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"бе̌и йӣнг");
|
||||
XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Arab"], @"بِِ̌ جِينگ");
|
||||
XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Fake"], @"北京");
|
||||
|
||||
XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Latn"], @"Moskva");
|
||||
XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Hans"], @"Mосква");
|
||||
XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"Москва");
|
||||
XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Arab"], @"مُسكڤَ");
|
||||
XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Fake"], @"Mосква");
|
||||
|
||||
XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Latn"], @"rondon");
|
||||
XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Hans"], @"ロンドン");
|
||||
XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"рондон");
|
||||
XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Arab"], @"رُندُن");
|
||||
XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Fake"], @"ロンドン");
|
||||
} else {
|
||||
XCTAssertEqualObjects([@"Made-up Place" mgl_stringByTransliteratingIntoScript:@"Latn"], @"Made-up Place");
|
||||
XCTAssertEqualObjects([@"Made-up Place" mgl_stringByTransliteratingIntoScript:@"Hans"], @"Made-up Place");
|
||||
XCTAssertEqualObjects([@"Made-up Place" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"Made-up Place");
|
||||
XCTAssertEqualObjects([@"Made-up Place" mgl_stringByTransliteratingIntoScript:@"Arab"], @"Made-up Place");
|
||||
XCTAssertEqualObjects([@"Made-up Place" mgl_stringByTransliteratingIntoScript:@"Fake"], @"Made-up Place");
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -227,6 +227,11 @@
|
||||
927FBD021F4DB05500F8BF1F /* MGLMapSnapshotter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 927FBCFE1F4DB05500F8BF1F /* MGLMapSnapshotter.mm */; };
|
||||
929EFFAB1F56DCD4003A77D5 /* MGLAnnotationView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4018B1C41CDC277F00F666AF /* MGLAnnotationView.mm */; };
|
||||
92F2C3ED1F0E3C3A00268EC0 /* MGLRendererFrontend.h in Headers */ = {isa = PBXBuildFile; fileRef = 92F2C3EC1F0E3C3A00268EC0 /* MGLRendererFrontend.h */; };
|
||||
96036A01200565C700510F3D /* NSOrthography+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 960369FF200565C700510F3D /* NSOrthography+MGLAdditions.h */; };
|
||||
96036A02200565C700510F3D /* NSOrthography+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 960369FF200565C700510F3D /* NSOrthography+MGLAdditions.h */; };
|
||||
96036A03200565C700510F3D /* NSOrthography+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 96036A00200565C700510F3D /* NSOrthography+MGLAdditions.m */; };
|
||||
96036A04200565C700510F3D /* NSOrthography+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 96036A00200565C700510F3D /* NSOrthography+MGLAdditions.m */; };
|
||||
96036A0620059BBA00510F3D /* MGLNSOrthographyAdditionsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 96036A0520059BBA00510F3D /* MGLNSOrthographyAdditionsTests.m */; };
|
||||
960D0C361ECF5AAF008E151F /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 960D0C351ECF5AAF008E151F /* Images.xcassets */; };
|
||||
960D0C371ECF5AAF008E151F /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 960D0C351ECF5AAF008E151F /* Images.xcassets */; };
|
||||
9620BB381E69FE1700705A1D /* MGLSDKUpdateChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = 9620BB361E69FE1700705A1D /* MGLSDKUpdateChecker.h */; };
|
||||
@@ -764,6 +769,9 @@
|
||||
927FBCFD1F4DB05500F8BF1F /* MGLMapSnapshotter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMapSnapshotter.h; sourceTree = "<group>"; };
|
||||
927FBCFE1F4DB05500F8BF1F /* MGLMapSnapshotter.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLMapSnapshotter.mm; sourceTree = "<group>"; };
|
||||
92F2C3EC1F0E3C3A00268EC0 /* MGLRendererFrontend.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRendererFrontend.h; sourceTree = "<group>"; };
|
||||
960369FF200565C700510F3D /* NSOrthography+MGLAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSOrthography+MGLAdditions.h"; sourceTree = "<group>"; };
|
||||
96036A00200565C700510F3D /* NSOrthography+MGLAdditions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSOrthography+MGLAdditions.m"; sourceTree = "<group>"; };
|
||||
96036A0520059BBA00510F3D /* MGLNSOrthographyAdditionsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLNSOrthographyAdditionsTests.m; sourceTree = "<group>"; };
|
||||
960D0C351ECF5AAF008E151F /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
|
||||
9620BB361E69FE1700705A1D /* MGLSDKUpdateChecker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLSDKUpdateChecker.h; sourceTree = "<group>"; };
|
||||
9620BB371E69FE1700705A1D /* MGLSDKUpdateChecker.mm */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = MGLSDKUpdateChecker.mm; sourceTree = "<group>"; };
|
||||
@@ -1231,6 +1239,8 @@
|
||||
children = (
|
||||
357FE2DB1E02D2B20068B753 /* NSCoder+MGLAdditions.h */,
|
||||
357FE2DC1E02D2B20068B753 /* NSCoder+MGLAdditions.mm */,
|
||||
960369FF200565C700510F3D /* NSOrthography+MGLAdditions.h */,
|
||||
96036A00200565C700510F3D /* NSOrthography+MGLAdditions.m */,
|
||||
35CE61801D4165D9004F2359 /* UIColor+MGLAdditions.h */,
|
||||
35CE61811D4165D9004F2359 /* UIColor+MGLAdditions.mm */,
|
||||
30E578111DAA7D690050F07E /* UIImage+MGLAdditions.h */,
|
||||
@@ -1413,6 +1423,7 @@
|
||||
35E208A61D24210F00EC9A46 /* MGLNSDataAdditionsTests.m */,
|
||||
1F95931C1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm */,
|
||||
DAE7DEC11E245455007505A6 /* MGLNSStringAdditionsTests.m */,
|
||||
96036A0520059BBA00510F3D /* MGLNSOrthographyAdditionsTests.m */,
|
||||
DA2E885D1CC0382C00F24E7B /* MGLOfflinePackTests.m */,
|
||||
DA2E885E1CC0382C00F24E7B /* MGLOfflineRegionTests.m */,
|
||||
55E2AD121E5B125400E8C587 /* MGLOfflineStorageTests.mm */,
|
||||
@@ -1871,6 +1882,7 @@
|
||||
353933F21D3FB753003F57D7 /* MGLCircleStyleLayer.h in Headers */,
|
||||
DA8847F31CBAFA5100AB86E3 /* MGLMultiPoint.h in Headers */,
|
||||
30E578171DAA85520050F07E /* UIImage+MGLAdditions.h in Headers */,
|
||||
96036A01200565C700510F3D /* NSOrthography+MGLAdditions.h in Headers */,
|
||||
1F7454961ECD450D00021D39 /* MGLLight_Private.h in Headers */,
|
||||
DAD1656C1CF41981001FF4B9 /* MGLFeature.h in Headers */,
|
||||
40EDA1C01CFE0E0200D9EA68 /* MGLAnnotationContainerView.h in Headers */,
|
||||
@@ -1925,6 +1937,7 @@
|
||||
DABFB8641CBE99E500D62B32 /* MGLOfflineStorage.h in Headers */,
|
||||
96E516E32000552A00A02306 /* MGLAccountManager_Private.h in Headers */,
|
||||
96E5170420005A6B00A02306 /* SMCalloutView.h in Headers */,
|
||||
96036A02200565C700510F3D /* NSOrthography+MGLAdditions.h in Headers */,
|
||||
DAD165791CF4CDFF001FF4B9 /* MGLShapeCollection.h in Headers */,
|
||||
4049C29E1DB6CD6C00B3F799 /* MGLPointCollection.h in Headers */,
|
||||
3566C7671D4A77BA008152BC /* MGLShapeSource.h in Headers */,
|
||||
@@ -2378,6 +2391,7 @@
|
||||
40CFA6511D7875BB008103BD /* MGLShapeSourceTests.mm in Sources */,
|
||||
DA35A2C51CCA9F8300E826B2 /* MGLClockDirectionFormatterTests.m in Sources */,
|
||||
35B8E08C1D6C8B5100E768D2 /* MGLPredicateTests.mm in Sources */,
|
||||
96036A0620059BBA00510F3D /* MGLNSOrthographyAdditionsTests.m in Sources */,
|
||||
1F95931D1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm in Sources */,
|
||||
DD58A4C61D822BD000E1F038 /* MGLExpressionTests.mm in Sources */,
|
||||
3575798B1D502B0C000B822E /* MGLBackgroundStyleLayerTests.mm in Sources */,
|
||||
@@ -2420,6 +2434,7 @@
|
||||
DA6408DD1DA4E7D300908C90 /* MGLVectorStyleLayer.m in Sources */,
|
||||
3566C7681D4A77BA008152BC /* MGLShapeSource.mm in Sources */,
|
||||
400533021DB0862B0069F638 /* NSArray+MGLAdditions.mm in Sources */,
|
||||
96036A03200565C700510F3D /* NSOrthography+MGLAdditions.m in Sources */,
|
||||
35136D421D42274500C20EFD /* MGLRasterStyleLayer.mm in Sources */,
|
||||
3538AA1F1D542239008EC33D /* MGLForegroundStyleLayer.mm in Sources */,
|
||||
DA00FC901D5EEB0D009AABC8 /* MGLAttributionInfo.mm in Sources */,
|
||||
@@ -2508,6 +2523,7 @@
|
||||
3566C7691D4A77BA008152BC /* MGLShapeSource.mm in Sources */,
|
||||
400533031DB086490069F638 /* NSArray+MGLAdditions.mm in Sources */,
|
||||
35136D431D42274500C20EFD /* MGLRasterStyleLayer.mm in Sources */,
|
||||
96036A04200565C700510F3D /* NSOrthography+MGLAdditions.m in Sources */,
|
||||
3538AA201D542239008EC33D /* MGLForegroundStyleLayer.mm in Sources */,
|
||||
DA00FC911D5EEB0D009AABC8 /* MGLAttributionInfo.mm in Sources */,
|
||||
DAA4E4201CBB730400178DFB /* MGLOfflinePack.mm in Sources */,
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#import "MGLVectorSource_Private.h"
|
||||
|
||||
#import "NSBundle+MGLAdditions.h"
|
||||
#import "NSOrthography+MGLAdditions.h"
|
||||
#import "NSString+MGLAdditions.h"
|
||||
|
||||
@implementation MGLMapAccessibilityElement
|
||||
|
||||
@@ -49,24 +51,16 @@
|
||||
NSString *languageCode = [MGLVectorSource preferredMapboxStreetsLanguage];
|
||||
NSString *nameAttribute = [NSString stringWithFormat:@"name_%@", languageCode];
|
||||
NSString *name = [feature attributeForKey:nameAttribute];
|
||||
|
||||
|
||||
// If a feature hasn’t been translated into the preferred language, it
|
||||
// may be in the local language, which may be written in another script.
|
||||
// Romanize it.
|
||||
NSLocale *locale = [NSLocale localeWithLocaleIdentifier:languageCode];
|
||||
NSOrthography *orthography;
|
||||
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunguarded-availability-new"
|
||||
if ([NSOrthography respondsToSelector:@selector(defaultOrthographyForLanguage:)]) {
|
||||
orthography = [NSOrthography defaultOrthographyForLanguage:locale.localeIdentifier];
|
||||
// Attempt to transform to the script of the preferred language, keeping
|
||||
// the original string if no transform exists or if transformation fails.
|
||||
if (@available(iOS 9.0, *)) {
|
||||
NSString *dominantScript = [NSOrthography mgl_dominantScriptForMapboxStreetsLanguage:languageCode];
|
||||
name = [name mgl_stringByTransliteratingIntoScript:dominantScript];
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
if ([orthography.dominantScript isEqualToString:@"Latn"]) {
|
||||
name = [name stringByApplyingTransform:NSStringTransformToLatin reverse:NO];
|
||||
}
|
||||
|
||||
|
||||
self.accessibilityLabel = name;
|
||||
}
|
||||
return self;
|
||||
|
||||
18
platform/ios/src/NSOrthography+MGLAdditions.h
Normal file
18
platform/ios/src/NSOrthography+MGLAdditions.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface NSOrthography (NSOrthography_MGLAdditions)
|
||||
|
||||
/**
|
||||
Returns a four-letter ISO 15924 code representing the name of the dominant
|
||||
script for a given language.
|
||||
|
||||
On iOS 11 or newer, this method wraps
|
||||
`+[NSOrthography defaultOrthographyForLanguage:]` and supports any language.
|
||||
On iOS 10 and older, this method only returns values for Mapbox
|
||||
Streets-supported languages.
|
||||
|
||||
@param language The ISO-639 code representing a language.
|
||||
*/
|
||||
+ (NSString *)mgl_dominantScriptForMapboxStreetsLanguage:(NSString *)language;
|
||||
|
||||
@end
|
||||
31
platform/ios/src/NSOrthography+MGLAdditions.m
Normal file
31
platform/ios/src/NSOrthography+MGLAdditions.m
Normal file
@@ -0,0 +1,31 @@
|
||||
#import "NSOrthography+MGLAdditions.h"
|
||||
|
||||
@implementation NSOrthography (MGLAdditions)
|
||||
|
||||
+ (NSString *)mgl_dominantScriptForMapboxStreetsLanguage:(NSString *)language {
|
||||
if (@available(iOS 11.0, *)) {
|
||||
NSLocale *locale = [NSLocale localeWithLocaleIdentifier:language];
|
||||
NSOrthography *orthography = [NSOrthography defaultOrthographyForLanguage:locale.localeIdentifier];
|
||||
|
||||
return orthography.dominantScript;
|
||||
}
|
||||
|
||||
// Manually map Mapbox Streets languages to ISO 15924 script codes.
|
||||
NSSet *latinLanguages = [NSSet setWithObjects:@"de", @"en", @"es", @"fr", @"pt", nil];
|
||||
NSSet *hansLanguages = [NSSet setWithObjects:@"zh", @"zh-Hans", nil];
|
||||
|
||||
if ([latinLanguages containsObject:language]) {
|
||||
return @"Latn";
|
||||
} else if ([hansLanguages containsObject:language]) {
|
||||
return @"Hans";
|
||||
} else if ([language isEqualToString:@"ru"]) {
|
||||
return @"Cyrl";
|
||||
} else if ([language isEqualToString:@"ar"]) {
|
||||
return @"Arab";
|
||||
} else {
|
||||
// Code for undetermined script
|
||||
return @"Zyyy";
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -19,13 +19,17 @@
|
||||
};
|
||||
MGLFeatureAccessibilityElement *element = [[MGLFeatureAccessibilityElement alloc] initWithAccessibilityContainer:self feature:feature];
|
||||
XCTAssertEqualObjects(element.accessibilityLabel, @"English", @"Accessibility label should be localized.");
|
||||
|
||||
|
||||
feature.attributes = @{
|
||||
@"name": @"Цинциннати",
|
||||
@"name_en": @"Цинциннати",
|
||||
};
|
||||
element = [[MGLFeatureAccessibilityElement alloc] initWithAccessibilityContainer:self feature:feature];
|
||||
XCTAssertEqualObjects(element.accessibilityLabel, @"Cincinnati", @"Accessibility label should be romanized.");
|
||||
if (@available(iOS 9.0, *)) {
|
||||
XCTAssertEqualObjects(element.accessibilityLabel, @"Cincinnati", @"Accessibility label should be romanized.");
|
||||
} else {
|
||||
XCTAssertEqualObjects(element.accessibilityLabel, @"Цинциннати", @"Accessibility label should not be romanized.");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testPlaceFeatureValues {
|
||||
|
||||
19
platform/ios/test/MGLNSOrthographyAdditionsTests.m
Normal file
19
platform/ios/test/MGLNSOrthographyAdditionsTests.m
Normal file
@@ -0,0 +1,19 @@
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "NSOrthography+MGLAdditions.h"
|
||||
#import "MGLVectorSource_Private.h"
|
||||
|
||||
@interface MGLNSOrthographyAdditionsTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation MGLNSOrthographyAdditionsTests
|
||||
|
||||
- (void)testStreetsLanguages {
|
||||
for (NSString *language in [MGLVectorSource mapboxStreetsLanguages]) {
|
||||
NSString *dominantScript = [NSOrthography mgl_dominantScriptForMapboxStreetsLanguage:language];
|
||||
XCTAssertNotEqualObjects(dominantScript, @"Zyyy", @"Mapbox Streets languages should have dominant script");
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user