From afd4df9dbcdff08654a09e95fdae236061aaa4bd Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Tue, 1 Aug 2017 16:14:41 -0400 Subject: =?UTF-8?q?[ios]=20adapt=20Mapbox=20Streets=E2=80=93sourced=20laye?= =?UTF-8?q?rs=20for=20user=20preferred=20language=20(#9582)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [ios] Update label localization * [ios, macos] Move MGLVectorSource+MBXAdditions.h to darwin. * [ios, macos] Adapt Mapbox Streets to the user preferred language. * [ios, macos] Clarify style localization documentation. * [ios, macos] Update localization examples * [ios, macos] Add style language reset to original. * [ios, macos] Update changelogs. * [ios, macos] Rename Vector Source Additions to MGL standard. * [ios, macos] Add suport for stop localization. --- platform/darwin/src/MGLStyle.h | 15 +++ platform/darwin/src/MGLStyle.mm | 136 +++++++++++++++++++++ platform/darwin/src/MGLVectorSource+MGLAdditions.h | 15 +++ platform/darwin/src/MGLVectorSource+MGLAdditions.m | 53 ++++++++ platform/ios/CHANGELOG.md | 6 +- platform/ios/app/MBXViewController.m | 43 +------ platform/ios/ios.xcodeproj/project.pbxproj | 12 ++ platform/macos/CHANGELOG.md | 4 + platform/macos/app/MGLVectorSource+MBXAdditions.h | 15 --- platform/macos/app/MGLVectorSource+MBXAdditions.m | 53 -------- platform/macos/app/MapDocument.m | 49 +------- platform/macos/macos.xcodeproj/project.pbxproj | 14 ++- 12 files changed, 253 insertions(+), 162 deletions(-) create mode 100644 platform/darwin/src/MGLVectorSource+MGLAdditions.h create mode 100644 platform/darwin/src/MGLVectorSource+MGLAdditions.m delete mode 100644 platform/macos/app/MGLVectorSource+MBXAdditions.h delete mode 100644 platform/macos/app/MGLVectorSource+MBXAdditions.m diff --git a/platform/darwin/src/MGLStyle.h b/platform/darwin/src/MGLStyle.h index 8ffd7db2b2..8c009d22c6 100644 --- a/platform/darwin/src/MGLStyle.h +++ b/platform/darwin/src/MGLStyle.h @@ -610,6 +610,21 @@ MGL_EXPORT */ @property (nonatomic, strong) MGLLight *light; +#pragma mark Localizing Map Content + +/** + A Boolean value that determines whether the style attempts to localize labels in + the style into the system’s preferred language. + + When this property is enabled, the style automatically modifies the text property + of any symbol style layer whose source is the + Mapbox + Streets source. On iOS, the user can set the system’s preferred language in + Settings, General Settings, Language & Region. On macOS, the user can set the + system’s preferred language in the Language & Region pane of System Preferences. + */ +@property (nonatomic) BOOL localizesLabels; + @end NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm index eb838085d7..1e0a2e02b7 100644 --- a/platform/darwin/src/MGLStyle.mm +++ b/platform/darwin/src/MGLStyle.mm @@ -21,6 +21,7 @@ #import "MGLSource.h" #import "MGLTileSource_Private.h" #import "MGLVectorSource.h" +#import "MGLVectorSource+MGLAdditions.h" #import "MGLRasterSource.h" #import "MGLShapeSource.h" @@ -48,11 +49,34 @@ #import "NSImage+MGLAdditions.h" #endif +/** + Model class for localization changes. + */ +@interface MGLTextLanguage: NSObject +@property (strong, nonatomic) NSString *originalTextField; +@property (strong, nonatomic) NSString *updatedTextField; + +- (instancetype)initWithTextLanguage:(NSString *)originalTextField updatedTextField:(NSString *)updatedTextField; + +@end + +@implementation MGLTextLanguage +- (instancetype)initWithTextLanguage:(NSString *)originalTextField updatedTextField:(NSString *)updatedTextField +{ + if (self = [super init]) { + _originalTextField = originalTextField; + _updatedTextField = updatedTextField; + } + return self; +} +@end + @interface MGLStyle() @property (nonatomic, readwrite, weak) MGLMapView *mapView; @property (readonly, copy, nullable) NSURL *URL; @property (nonatomic, readwrite, strong) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLOpenGLStyleLayer *) *openGLLayers; +@property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, NS_DICTIONARY_OF(NSObject *, MGLTextLanguage *) *) *localizedLayersByIdentifier; @end @@ -116,6 +140,7 @@ static NSURL *MGLStyleURL_emerald; if (self = [super init]) { _mapView = mapView; _openGLLayers = [NSMutableDictionary dictionary]; + _localizedLayersByIdentifier = [NSMutableDictionary dictionary]; } return self; } @@ -609,4 +634,115 @@ static NSURL *MGLStyleURL_emerald; self.URL ? [NSString stringWithFormat:@"\"%@\"", self.URL] : self.URL]; } +#pragma mark Style language preferences + +- (void)setLocalizesLabels:(BOOL)localizesLabels +{ + if (_localizesLabels != localizesLabels) { + _localizesLabels = localizesLabels; + } else { + return; + } + + if (_localizesLabels) { + NSString *preferredLanguage = [MGLVectorSource preferredMapboxStreetsLanguage]; + NSMutableDictionary *localizedKeysByKeyBySourceIdentifier = [NSMutableDictionary dictionary]; + for (MGLSymbolStyleLayer *layer in self.layers) { + if (![layer isKindOfClass:[MGLSymbolStyleLayer class]]) { + continue; + } + + MGLVectorSource *source = (MGLVectorSource *)[self sourceWithIdentifier:layer.sourceIdentifier]; + if (![source isKindOfClass:[MGLVectorSource class]] || !source.mapboxStreets) { + continue; + } + + NSDictionary *localizedKeysByKey = localizedKeysByKeyBySourceIdentifier[layer.sourceIdentifier]; + if (!localizedKeysByKey) { + localizedKeysByKey = localizedKeysByKeyBySourceIdentifier[layer.sourceIdentifier] = [source localizedKeysByKeyForPreferredLanguage:preferredLanguage]; + } + + NSString *(^stringByLocalizingString)(NSString *) = ^ NSString * (NSString *string) { + NSMutableString *localizedString = string.mutableCopy; + [localizedKeysByKey enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull localizedKey, BOOL * _Nonnull stop) { + NSAssert([key isKindOfClass:[NSString class]], @"key is not a string"); + NSAssert([localizedKey isKindOfClass:[NSString class]], @"localizedKey is not a string"); + [localizedString replaceOccurrencesOfString:[NSString stringWithFormat:@"{%@}", key] + withString:[NSString stringWithFormat:@"{%@}", localizedKey] + options:0 + range:NSMakeRange(0, localizedString.length)]; + }]; + return localizedString; + }; + + if ([layer.text isKindOfClass:[MGLConstantStyleValue class]]) { + NSString *textField = [(MGLConstantStyleValue *)layer.text rawValue]; + NSString *localizingString = stringByLocalizingString(textField); + if (![textField isEqualToString:localizingString]) { + MGLTextLanguage *textLanguage = [[MGLTextLanguage alloc] initWithTextLanguage:textField + updatedTextField:localizingString]; + [self.localizedLayersByIdentifier setObject:@{ textField : textLanguage } forKey:layer.identifier]; + layer.text = [MGLStyleValue valueWithRawValue:localizingString]; + } + } + else if ([layer.text isKindOfClass:[MGLCameraStyleFunction class]]) { + MGLCameraStyleFunction *function = (MGLCameraStyleFunction *)layer.text; + NSMutableDictionary *stops = function.stops.mutableCopy; + NSMutableDictionary *cameraStops = [NSMutableDictionary dictionary]; + [stops enumerateKeysAndObjectsUsingBlock:^(NSNumber *zoomLevel, MGLConstantStyleValue *stop, BOOL *done) { + NSString *textField = stop.rawValue; + NSString *localizingString = stringByLocalizingString(textField); + if (![textField isEqualToString:localizingString]) { + MGLTextLanguage *textLanguage = [[MGLTextLanguage alloc] initWithTextLanguage:textField + updatedTextField:localizingString]; + [cameraStops setObject:textLanguage forKey:zoomLevel]; + stops[zoomLevel] = [MGLStyleValue valueWithRawValue:localizingString]; + } + + }]; + if (cameraStops.count > 0) { + [self.localizedLayersByIdentifier setObject:cameraStops forKey:layer.identifier]; + } + function.stops = stops; + layer.text = function; + } + } + } else { + + [self.localizedLayersByIdentifier enumerateKeysAndObjectsUsingBlock:^(NSString *identifier, NSDictionary *textFields, BOOL *done) { + MGLSymbolStyleLayer *layer = (MGLSymbolStyleLayer *)[self.mapView.style layerWithIdentifier:identifier]; + + if ([layer.text isKindOfClass:[MGLConstantStyleValue class]]) { + NSString *textField = [(MGLConstantStyleValue *)layer.text rawValue]; + [textFields enumerateKeysAndObjectsUsingBlock:^(NSObject *originalLanguage, MGLTextLanguage *textLanguage, BOOL *done) { + if ([textLanguage.updatedTextField isEqualToString:textField]) { + layer.text = [MGLStyleValue valueWithRawValue:textLanguage.originalTextField]; + } + }]; + + } + else if ([layer.text isKindOfClass:[MGLCameraStyleFunction class]]) { + MGLCameraStyleFunction *function = (MGLCameraStyleFunction *)layer.text; + NSMutableDictionary *stops = function.stops.mutableCopy; + [textFields enumerateKeysAndObjectsUsingBlock:^(NSObject *zoomKey, MGLTextLanguage *textLanguage, BOOL *done) { + if ([zoomKey isKindOfClass:[NSNumber class]]) { + NSNumber *zoomLevel = (NSNumber*)zoomKey; + MGLConstantStyleValue *stop = [stops objectForKey:zoomLevel]; + NSString *textField = stop.rawValue; + if ([textLanguage.updatedTextField isEqualToString:textField]) { + stops[zoomLevel] = [MGLStyleValue valueWithRawValue:textLanguage.originalTextField]; + } + } + }]; + + function.stops = stops; + layer.text = function; + } + + }]; + + self.localizedLayersByIdentifier = [NSMutableDictionary dictionary]; + } +} + @end diff --git a/platform/darwin/src/MGLVectorSource+MGLAdditions.h b/platform/darwin/src/MGLVectorSource+MGLAdditions.h new file mode 100644 index 0000000000..43b0aba747 --- /dev/null +++ b/platform/darwin/src/MGLVectorSource+MGLAdditions.h @@ -0,0 +1,15 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface MGLVectorSource (MGLAdditions) + ++ (NSString *)preferredMapboxStreetsLanguage; + +- (NS_DICTIONARY_OF(NSString *, NSString *) *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage; + +@property (nonatomic, readonly, getter=isMapboxStreets) BOOL mapboxStreets; + +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLVectorSource+MGLAdditions.m b/platform/darwin/src/MGLVectorSource+MGLAdditions.m new file mode 100644 index 0000000000..a305388117 --- /dev/null +++ b/platform/darwin/src/MGLVectorSource+MGLAdditions.m @@ -0,0 +1,53 @@ +#import "MGLVectorSource+MGLAdditions.h" + +@implementation MGLVectorSource (MGLAdditions) + ++ (NS_SET_OF(NSString *) *)mapboxStreetsLanguages { + // https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview + static dispatch_once_t onceToken; + static NS_SET_OF(NSString *) *mapboxStreetsLanguages; + dispatch_once(&onceToken, ^{ + // https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview + mapboxStreetsLanguages = [NSSet setWithObjects:@"ar", @"de", @"en", @"es", @"fr", @"pt", @"ru", @"zh", @"zh-Hans", nil]; + }); + return mapboxStreetsLanguages; +} + ++ (NSString *)preferredMapboxStreetsLanguage { + NSArray *supportedLanguages = [MGLVectorSource mapboxStreetsLanguages].allObjects; + NSArray *preferredLanguages = [NSBundle preferredLocalizationsFromArray:supportedLanguages + forPreferences:[NSLocale preferredLanguages]]; + NSString *mostSpecificLanguage; + for (NSString *language in preferredLanguages) { + if (language.length > mostSpecificLanguage.length) { + mostSpecificLanguage = language; + } + } + return mostSpecificLanguage ?: @"en"; +} + +- (BOOL)isMapboxStreets { + NSURL *url = self.configurationURL; + if (![url.scheme isEqualToString:@"mapbox"]) { + return NO; + } + NSArray *identifiers = [url.host componentsSeparatedByString:@","]; + return [identifiers containsObject:@"mapbox.mapbox-streets-v7"] || [identifiers containsObject:@"mapbox.mapbox-streets-v6"]; +} + +- (NS_DICTIONARY_OF(NSString *, NSString *) *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage { + if (!self.mapboxStreets) { + return @{}; + } + + // Replace {name} and {name_*} with the matching localized name tag. + NSString *localizedKey = preferredLanguage ? [NSString stringWithFormat:@"name_%@", preferredLanguage] : @"name"; + NSMutableDictionary *localizedKeysByKey = [NSMutableDictionary dictionaryWithObject:localizedKey forKey:@"name"]; + for (NSString *languageCode in [MGLVectorSource mapboxStreetsLanguages]) { + NSString *key = [NSString stringWithFormat:@"name_%@", languageCode]; + localizedKeysByKey[key] = localizedKey; + } + return localizedKeysByKey; +} + +@end diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 43df49a9ac..55790577cd 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -2,7 +2,11 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started. -## 3.6.1 +## 3.6.2 + +* Added an MGLStyle.localizesLabels property, off by default, that localizes any Mapbox Streets–sourced symbol layer into the user’s preferred language. ([#9582](https://github.com/mapbox/mapbox-gl-native/pull/9582)) + +## 3.6.1 - July 28, 2017 * Reduced the size of the dynamic framework by optimizing symbol visibility. ([#7604](https://github.com/mapbox/mapbox-gl-native/pull/7604)) * Fixed an issue where the attribution button would have its custom tint color reset when the map view received a tint color change notification, such as when an alert controller was presented. ([#9598](https://github.com/mapbox/mapbox-gl-native/pull/9598)) diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m index d3927374a7..ebbc218017 100644 --- a/platform/ios/app/MBXViewController.m +++ b/platform/ios/app/MBXViewController.m @@ -343,7 +343,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { @"Update Shape Source: Features", @"Style Vector Source", @"Style Raster Source", - [NSString stringWithFormat:@"Label Countries in %@", (_usingLocaleBasedCountryLabels ? @"Local Language" : [[NSLocale currentLocale] displayNameForKey:NSLocaleIdentifier value:[self bestLanguageForUser]])], + [NSString stringWithFormat:@"Show Labels in %@", (_usingLocaleBasedCountryLabels ? @"Local Language" : [[NSLocale currentLocale] displayNameForKey:NSLocaleIdentifier value:[self bestLanguageForUser]])], @"Add Route Line", @"Dynamically Style Polygon", ]]; @@ -1274,12 +1274,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { -(void)styleCountryLabelsLanguage { - NSArray *labelLayers = @[ - @"country-label-lg", - @"country-label-md", - @"country-label-sm", - ]; - [self styleLabelLanguageForLayersNamed:labelLayers]; + _usingLocaleBasedCountryLabels = !_usingLocaleBasedCountryLabels; + self.mapView.style.localizesLabels = _usingLocaleBasedCountryLabels; } - (void)styleRouteLine @@ -1362,39 +1358,6 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { [self.mapView.style addLayer:fillStyleLayer]; } -- (void)styleLabelLanguageForLayersNamed:(NSArray *)layers -{ - _usingLocaleBasedCountryLabels = !_usingLocaleBasedCountryLabels; - NSString *bestLanguageForUser = [NSString stringWithFormat:@"{name_%@}", [self bestLanguageForUser]]; - NSString *language = _usingLocaleBasedCountryLabels ? bestLanguageForUser : @"{name}"; - - for (NSString *layerName in layers) { - MGLSymbolStyleLayer *layer = (MGLSymbolStyleLayer *)[self.mapView.style layerWithIdentifier:layerName]; - - if ([layer isKindOfClass:[MGLSymbolStyleLayer class]]) { - if ([layer.text isKindOfClass:[MGLConstantStyleValue class]]) { - MGLConstantStyleValue *label = (MGLConstantStyleValue *)layer.text; - if ([label.rawValue hasPrefix:@"{name"]) { - layer.text = [MGLStyleValue valueWithRawValue:language]; - } - } - else if ([layer.text isKindOfClass:[MGLCameraStyleFunction class]]) { - MGLCameraStyleFunction *function = (MGLCameraStyleFunction *)layer.text; - NSMutableDictionary *stops = function.stops.mutableCopy; - [stops enumerateKeysAndObjectsUsingBlock:^(NSNumber *zoomLevel, MGLConstantStyleValue *stop, BOOL *done) { - if ([stop.rawValue hasPrefix:@"{name"]) { - stops[zoomLevel] = [MGLStyleValue valueWithRawValue:language]; - } - }]; - function.stops = stops; - layer.text = function; - } - } else { - NSLog(@"%@ is not a symbol style layer", layerName); - } - } -} - - (NSString *)bestLanguageForUser { // https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index 8ecbe0c6ab..83b267df43 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -17,6 +17,10 @@ 1F7454971ECD450D00021D39 /* MGLLight_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F7454941ECD450D00021D39 /* MGLLight_Private.h */; }; 1F7454A91ED08AB400021D39 /* MGLLightTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F7454A61ED08AB400021D39 /* MGLLightTest.mm */; }; 1F95931D1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F95931C1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm */; }; + 1FB7DAAF1F2A4DBD00410606 /* MGLVectorSource+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FDD9D6D1F26936400252B09 /* MGLVectorSource+MGLAdditions.h */; }; + 1FB7DAB01F2A4DC200410606 /* MGLVectorSource+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FDD9D6D1F26936400252B09 /* MGLVectorSource+MGLAdditions.h */; }; + 1FB7DAB11F2A4DC800410606 /* MGLVectorSource+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FDD9D6E1F26936400252B09 /* MGLVectorSource+MGLAdditions.m */; }; + 1FB7DAB21F2A4DC900410606 /* MGLVectorSource+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FDD9D6E1F26936400252B09 /* MGLVectorSource+MGLAdditions.m */; }; 30E578171DAA85520050F07E /* UIImage+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E578111DAA7D690050F07E /* UIImage+MGLAdditions.h */; }; 30E578181DAA85520050F07E /* UIImage+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E578111DAA7D690050F07E /* UIImage+MGLAdditions.h */; }; 30E578191DAA855E0050F07E /* UIImage+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 30E578121DAA7D690050F07E /* UIImage+MGLAdditions.mm */; }; @@ -547,6 +551,8 @@ 1F7454941ECD450D00021D39 /* MGLLight_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight_Private.h; sourceTree = ""; }; 1F7454A61ED08AB400021D39 /* MGLLightTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLLightTest.mm; path = ../../darwin/test/MGLLightTest.mm; sourceTree = ""; }; 1F95931C1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLNSDateAdditionsTests.mm; path = ../../darwin/test/MGLNSDateAdditionsTests.mm; sourceTree = ""; }; + 1FDD9D6D1F26936400252B09 /* MGLVectorSource+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLVectorSource+MGLAdditions.h"; sourceTree = ""; }; + 1FDD9D6E1F26936400252B09 /* MGLVectorSource+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLVectorSource+MGLAdditions.m"; sourceTree = ""; }; 20DABE861DF78148007AC5FF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Foundation.strings"; sourceTree = ""; }; 20DABE881DF78148007AC5FF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; 20DABE8A1DF78149007AC5FF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Root.strings"; sourceTree = ""; }; @@ -1537,6 +1543,8 @@ DAD165831CF4CFED001FF4B9 /* Categories */ = { isa = PBXGroup; children = ( + 1FDD9D6D1F26936400252B09 /* MGLVectorSource+MGLAdditions.h */, + 1FDD9D6E1F26936400252B09 /* MGLVectorSource+MGLAdditions.m */, 7E016D821D9E890300A29A21 /* MGLPolygon+MGLAdditions.h */, 7E016D831D9E890300A29A21 /* MGLPolygon+MGLAdditions.m */, 7E016D7C1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.h */, @@ -1642,6 +1650,7 @@ 350098DC1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.h in Headers */, DA8848231CBAFA6200AB86E3 /* MGLOfflineStorage_Private.h in Headers */, 404326891D5B9B27007111BD /* MGLAnnotationContainerView_Private.h in Headers */, + 1FB7DAAF1F2A4DBD00410606 /* MGLVectorSource+MGLAdditions.h in Headers */, DA88483B1CBAFB8500AB86E3 /* MGLCalloutView.h in Headers */, 35E0CFE61D3E501500188327 /* MGLStyle_Private.h in Headers */, 3510FFF01D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */, @@ -1819,6 +1828,7 @@ 35136D4D1D4277FC00C20EFD /* MGLSource.h in Headers */, DA35A2BC1CCA9A6900E826B2 /* MGLClockDirectionFormatter.h in Headers */, 35D13AC41D3D19DD00AFB4E0 /* MGLFillStyleLayer.h in Headers */, + 1FB7DAB01F2A4DC200410606 /* MGLVectorSource+MGLAdditions.h in Headers */, DABFB86E1CBE9A0F00D62B32 /* MGLCalloutView.h in Headers */, 1F7454971ECD450D00021D39 /* MGLLight_Private.h in Headers */, DABFB8601CBE99E500D62B32 /* MGLMapCamera.h in Headers */, @@ -2197,6 +2207,7 @@ 9620BB3A1E69FE1700705A1D /* MGLSDKUpdateChecker.mm in Sources */, 354B83981D2E873E005D9406 /* MGLUserLocationAnnotationView.m in Sources */, DA88485D1CBAFB9800AB86E3 /* MGLFaux3DUserLocationAnnotationView.m in Sources */, + 1FB7DAB11F2A4DC800410606 /* MGLVectorSource+MGLAdditions.m in Sources */, DAD165701CF41981001FF4B9 /* MGLFeature.mm in Sources */, 30E578191DAA855E0050F07E /* UIImage+MGLAdditions.mm in Sources */, 40EDA1C11CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */, @@ -2279,6 +2290,7 @@ 9620BB3B1E69FE1700705A1D /* MGLSDKUpdateChecker.mm in Sources */, DAA4E4221CBB730400178DFB /* MGLPointAnnotation.mm in Sources */, DAED38661D62D0FC00D7640F /* NSURL+MGLAdditions.m in Sources */, + 1FB7DAB21F2A4DC900410606 /* MGLVectorSource+MGLAdditions.m in Sources */, DAD165711CF41981001FF4B9 /* MGLFeature.mm in Sources */, 30E5781A1DAA855E0050F07E /* UIImage+MGLAdditions.mm in Sources */, 40EDA1C21CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */, diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index f69f7e1b5d..1af3c03d68 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog for Mapbox macOS SDK +## 0.5.1 + +* Added an MGLStyle.localizesLabels property, off by default, that localizes any Mapbox Streets–sourced symbol layer into the user’s preferred language. ([#9582](https://github.com/mapbox/mapbox-gl-native/pull/9582)) + ## 0.5.0 This version of the Mapbox macOS SDK corresponds to version 3.6.0 of the Mapbox iOS SDK. diff --git a/platform/macos/app/MGLVectorSource+MBXAdditions.h b/platform/macos/app/MGLVectorSource+MBXAdditions.h deleted file mode 100644 index 1e25ee5a60..0000000000 --- a/platform/macos/app/MGLVectorSource+MBXAdditions.h +++ /dev/null @@ -1,15 +0,0 @@ -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface MGLVectorSource (MBXAdditions) - -+ (NSString *)preferredMapboxStreetsLanguage; - -- (NS_DICTIONARY_OF(NSString *, NSString *) *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage; - -@property (nonatomic, readonly, getter=isMapboxStreets) BOOL mapboxStreets; - -@end - -NS_ASSUME_NONNULL_END diff --git a/platform/macos/app/MGLVectorSource+MBXAdditions.m b/platform/macos/app/MGLVectorSource+MBXAdditions.m deleted file mode 100644 index 323bc74366..0000000000 --- a/platform/macos/app/MGLVectorSource+MBXAdditions.m +++ /dev/null @@ -1,53 +0,0 @@ -#import "MGLVectorSource+MBXAdditions.h" - -@implementation MGLVectorSource (MBXAdditions) - -+ (NS_SET_OF(NSString *) *)mapboxStreetsLanguages { - // https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview - static dispatch_once_t onceToken; - static NS_SET_OF(NSString *) *mapboxStreetsLanguages; - dispatch_once(&onceToken, ^{ - // https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview - mapboxStreetsLanguages = [NSSet setWithObjects:@"ar", @"de", @"en", @"es", @"fr", @"pt", @"ru", @"zh", @"zh-Hans", nil]; - }); - return mapboxStreetsLanguages; -} - -+ (NSString *)preferredMapboxStreetsLanguage { - NSArray *supportedLanguages = [MGLVectorSource mapboxStreetsLanguages].allObjects; - NSArray *preferredLanguages = [NSBundle preferredLocalizationsFromArray:supportedLanguages - forPreferences:[NSLocale preferredLanguages]]; - NSString *mostSpecificLanguage; - for (NSString *language in preferredLanguages) { - if (language.length > mostSpecificLanguage.length) { - mostSpecificLanguage = language; - } - } - return mostSpecificLanguage ?: @"en"; -} - -- (BOOL)isMapboxStreets { - NSURL *url = self.configurationURL; - if (![url.scheme isEqualToString:@"mapbox"]) { - return NO; - } - NSArray *identifiers = [url.host componentsSeparatedByString:@","]; - return [identifiers containsObject:@"mapbox.mapbox-streets-v7"] || [identifiers containsObject:@"mapbox.mapbox-streets-v6"]; -} - -- (NS_DICTIONARY_OF(NSString *, NSString *) *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage { - if (!self.mapboxStreets) { - return @{}; - } - - // Replace {name} and {name_*} with the matching localized name tag. - NSString *localizedKey = preferredLanguage ? [NSString stringWithFormat:@"name_%@", preferredLanguage] : @"name"; - NSMutableDictionary *localizedKeysByKey = [NSMutableDictionary dictionaryWithObject:localizedKey forKey:@"name"]; - for (NSString *languageCode in [MGLVectorSource mapboxStreetsLanguages]) { - NSString *key = [NSString stringWithFormat:@"name_%@", languageCode]; - localizedKeysByKey[key] = localizedKey; - } - return localizedKeysByKey; -} - -@end diff --git a/platform/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m index 59844d363e..1d22295f50 100644 --- a/platform/macos/app/MapDocument.m +++ b/platform/macos/app/MapDocument.m @@ -5,7 +5,7 @@ #import "DroppedPinAnnotation.h" #import "MGLStyle+MBXAdditions.h" -#import "MGLVectorSource+MBXAdditions.h" +#import "MGLVectorSource+MGLAdditions.h" #import @@ -344,52 +344,7 @@ NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id *)layer.text rawValue]; - layer.text = [MGLStyleValue valueWithRawValue:stringByLocalizingString(textField)]; - } - else if ([layer.text isKindOfClass:[MGLCameraStyleFunction class]]) { - MGLCameraStyleFunction *function = (MGLCameraStyleFunction *)layer.text; - NSMutableDictionary *stops = function.stops.mutableCopy; - [stops enumerateKeysAndObjectsUsingBlock:^(NSNumber *zoomLevel, MGLConstantStyleValue *stop, BOOL *done) { - NSString *textField = stop.rawValue; - stops[zoomLevel] = [MGLStyleValue valueWithRawValue:stringByLocalizingString(textField)]; - }]; - function.stops = stops; - layer.text = function; - } - } + self.mapView.style.localizesLabels = _isLocalizingLabels; } - (void)applyPendingState { diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj index be33b822f2..00cb6ce975 100644 --- a/platform/macos/macos.xcodeproj/project.pbxproj +++ b/platform/macos/macos.xcodeproj/project.pbxproj @@ -13,6 +13,8 @@ 1F7454A51ECFB00300021D39 /* MGLLight.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F7454A21ECFB00300021D39 /* MGLLight.mm */; }; 1F7454AB1ED1DDBD00021D39 /* MGLLightTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F7454AA1ED1DDBD00021D39 /* MGLLightTest.mm */; }; 1F95931B1E6DE2B600D5B294 /* MGLNSDateAdditionsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F95931A1E6DE2B600D5B294 /* MGLNSDateAdditionsTests.mm */; }; + 1FCDF1421F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FCDF1401F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.h */; }; + 1FCDF1431F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FCDF1411F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.m */; }; 30E5781B1DAA857E0050F07E /* NSImage+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E578141DAA7D920050F07E /* NSImage+MGLAdditions.h */; }; 3508EC641D749D39009B0EE4 /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3508EC621D749D39009B0EE4 /* NSExpression+MGLAdditions.h */; }; 3508EC651D749D39009B0EE4 /* NSExpression+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3508EC631D749D39009B0EE4 /* NSExpression+MGLAdditions.mm */; }; @@ -233,7 +235,6 @@ DAEDC4371D606291000224FF /* MGLAttributionButtonTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DAEDC4361D606291000224FF /* MGLAttributionButtonTests.m */; }; DAF0D80E1DFE0E5D00B28378 /* MGLPointCollection_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAF0D80D1DFE0E5D00B28378 /* MGLPointCollection_Private.h */; }; DAF0D8161DFE6B1800B28378 /* MGLAttributionInfo_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAF0D8151DFE6B1800B28378 /* MGLAttributionInfo_Private.h */; }; - DAF0D81C1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DAF0D81B1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.m */; }; DD0902B21DB1AC6400C5BDCE /* MGLNetworkConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = DD0902AF1DB1AC6400C5BDCE /* MGLNetworkConfiguration.m */; }; DD0902B31DB1AC6400C5BDCE /* MGLNetworkConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = DD0902B01DB1AC6400C5BDCE /* MGLNetworkConfiguration.h */; }; DD58A4C91D822C6700E1F038 /* MGLExpressionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DD58A4C71D822C6200E1F038 /* MGLExpressionTests.mm */; }; @@ -277,6 +278,8 @@ 1F7454A21ECFB00300021D39 /* MGLLight.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLLight.mm; sourceTree = ""; }; 1F7454AA1ED1DDBD00021D39 /* MGLLightTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLLightTest.mm; sourceTree = ""; }; 1F95931A1E6DE2B600D5B294 /* MGLNSDateAdditionsTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLNSDateAdditionsTests.mm; path = ../../darwin/test/MGLNSDateAdditionsTests.mm; sourceTree = ""; }; + 1FCDF1401F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLVectorSource+MGLAdditions.h"; sourceTree = ""; }; + 1FCDF1411F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLVectorSource+MGLAdditions.m"; sourceTree = ""; }; 30E578141DAA7D920050F07E /* NSImage+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSImage+MGLAdditions.h"; path = "src/NSImage+MGLAdditions.h"; sourceTree = SOURCE_ROOT; }; 3508EC621D749D39009B0EE4 /* NSExpression+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSExpression+MGLAdditions.h"; sourceTree = ""; }; 3508EC631D749D39009B0EE4 /* NSExpression+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSExpression+MGLAdditions.mm"; sourceTree = ""; }; @@ -568,8 +571,6 @@ DAEDC4361D606291000224FF /* MGLAttributionButtonTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAttributionButtonTests.m; sourceTree = ""; }; DAF0D80D1DFE0E5D00B28378 /* MGLPointCollection_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLPointCollection_Private.h; sourceTree = ""; }; DAF0D8151DFE6B1800B28378 /* MGLAttributionInfo_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAttributionInfo_Private.h; sourceTree = ""; }; - DAF0D81A1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLVectorSource+MBXAdditions.h"; sourceTree = ""; }; - DAF0D81B1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLVectorSource+MBXAdditions.m"; sourceTree = ""; }; DAFBD0D51E3FA969000CD6BF /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = ""; }; DAFBD0D61E3FA983000CD6BF /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Foundation.strings"; sourceTree = ""; }; DD0902AF1DB1AC6400C5BDCE /* MGLNetworkConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLNetworkConfiguration.m; sourceTree = ""; }; @@ -740,8 +741,6 @@ DA839E9E1CC2E3400062CAFB /* MapDocument.xib */, DACB0C371E18DFFD005DDBEA /* MGLStyle+MBXAdditions.h */, DACB0C381E18DFFD005DDBEA /* MGLStyle+MBXAdditions.m */, - DAF0D81A1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.h */, - DAF0D81B1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.m */, DAE6C2E91CC3050F00DB3429 /* OfflinePackNameValueTransformer.h */, DAE6C2EA1CC3050F00DB3429 /* OfflinePackNameValueTransformer.m */, DAA48EFB1D6A4731006A7E36 /* StyleLayerIconTransformer.h */, @@ -920,6 +919,8 @@ DAD1657F1CF4CF50001FF4B9 /* Categories */ = { isa = PBXGroup; children = ( + 1FCDF1401F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.h */, + 1FCDF1411F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.m */, 408AA8601DAEED3300022900 /* MGLPolygon+MGLAdditions.h */, 408AA85C1DAEED3300022900 /* MGLPolygon+MGLAdditions.m */, 408AA8611DAEED3300022900 /* MGLPolyline+MGLAdditions.h */, @@ -1154,6 +1155,7 @@ DAE6C3601CC31E0400DB3429 /* MGLOfflineRegion.h in Headers */, DAE6C3681CC31E0400DB3429 /* MGLTilePyramidOfflineRegion.h in Headers */, DA35A2CF1CCAAED300E826B2 /* NSValue+MGLAdditions.h in Headers */, + 1FCDF1421F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.h in Headers */, DAE6C3A61CC31E9400DB3429 /* MGLMapViewDelegate.h in Headers */, DAE6C38B1CC31E2A00DB3429 /* MGLOfflinePack_Private.h in Headers */, 558DE7A61E56161C00C7916D /* MGLFoundation_Private.h in Headers */, @@ -1378,7 +1380,6 @@ DACB0C391E18DFFD005DDBEA /* MGLStyle+MBXAdditions.m in Sources */, DA839E9A1CC2E3400062CAFB /* main.m in Sources */, DA839E971CC2E3400062CAFB /* AppDelegate.m in Sources */, - DAF0D81C1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.m in Sources */, DAE6C2F01CC3050F00DB3429 /* OfflinePackNameValueTransformer.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1394,6 +1395,7 @@ 40B77E461DB11BCD003DA2FE /* NSArray+MGLAdditions.mm in Sources */, DAE6C38C1CC31E2A00DB3429 /* MGLOfflinePack.mm in Sources */, 35D65C5B1D65AD5500722C23 /* NSDate+MGLAdditions.mm in Sources */, + 1FCDF1431F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.m in Sources */, DD0902B21DB1AC6400C5BDCE /* MGLNetworkConfiguration.m in Sources */, 1F7454A51ECFB00300021D39 /* MGLLight.mm in Sources */, DAE6C3B11CC31EF300DB3429 /* MGLAnnotationImage.m in Sources */, -- cgit v1.2.1