summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--platform/darwin/src/MGLBackgroundStyleLayer.mm3
-rw-r--r--platform/darwin/src/MGLFillExtrusionStyleLayer.mm3
-rw-r--r--platform/darwin/src/MGLFillStyleLayer.mm3
-rw-r--r--platform/darwin/src/MGLLineStyleLayer.mm3
-rw-r--r--platform/darwin/src/MGLStyle.h23
-rw-r--r--platform/darwin/src/MGLStyle.mm82
-rw-r--r--platform/darwin/src/MGLStyleLayer.mm.ejs10
-rw-r--r--platform/darwin/src/MGLSymbolStyleLayer.mm6
-rw-r--r--platform/darwin/src/MGLVectorTileSource.mm41
-rw-r--r--platform/darwin/src/MGLVectorTileSource_Private.h3
-rw-r--r--platform/darwin/src/NSExpression+MGLAdditions.h16
-rw-r--r--platform/darwin/src/NSExpression+MGLAdditions.mm229
-rw-r--r--platform/darwin/src/NSExpression+MGLPrivateAdditions.h6
-rw-r--r--platform/darwin/test/MGLExpressionTests.mm140
-rw-r--r--platform/darwin/test/MGLStyleTests.mm42
-rw-r--r--platform/ios/CHANGELOG.md4
-rw-r--r--platform/ios/app/MBXViewController.m15
-rw-r--r--platform/macos/CHANGELOG.md1
-rw-r--r--platform/macos/app/MapDocument.m4
-rw-r--r--platform/macos/macos.xcodeproj/xcshareddata/xcschemes/macosapp.xcscheme3
20 files changed, 535 insertions, 102 deletions
diff --git a/platform/darwin/src/MGLBackgroundStyleLayer.mm b/platform/darwin/src/MGLBackgroundStyleLayer.mm
index 7c4cf79709..993645d3a8 100644
--- a/platform/darwin/src/MGLBackgroundStyleLayer.mm
+++ b/platform/darwin/src/MGLBackgroundStyleLayer.mm
@@ -116,7 +116,8 @@
if (propertyValue.isUndefined()) {
propertyValue = self.rawLayer->getDefaultBackgroundPattern();
}
- return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ NSExpression *expression = MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ return expression.mgl_expressionByReplacingTokensWithKeyPaths;
}
- (void)setBackgroundPatternTransition:(MGLTransition )transition {
diff --git a/platform/darwin/src/MGLFillExtrusionStyleLayer.mm b/platform/darwin/src/MGLFillExtrusionStyleLayer.mm
index 1baa264689..688ce4c1ac 100644
--- a/platform/darwin/src/MGLFillExtrusionStyleLayer.mm
+++ b/platform/darwin/src/MGLFillExtrusionStyleLayer.mm
@@ -231,7 +231,8 @@ namespace mbgl {
if (propertyValue.isUndefined()) {
propertyValue = self.rawLayer->getDefaultFillExtrusionPattern();
}
- return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ NSExpression *expression = MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ return expression.mgl_expressionByReplacingTokensWithKeyPaths;
}
- (void)setFillExtrusionPatternTransition:(MGLTransition )transition {
diff --git a/platform/darwin/src/MGLFillStyleLayer.mm b/platform/darwin/src/MGLFillStyleLayer.mm
index ab28b414b5..c975e28d6b 100644
--- a/platform/darwin/src/MGLFillStyleLayer.mm
+++ b/platform/darwin/src/MGLFillStyleLayer.mm
@@ -220,7 +220,8 @@ namespace mbgl {
if (propertyValue.isUndefined()) {
propertyValue = self.rawLayer->getDefaultFillPattern();
}
- return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ NSExpression *expression = MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ return expression.mgl_expressionByReplacingTokensWithKeyPaths;
}
- (void)setFillPatternTransition:(MGLTransition )transition {
diff --git a/platform/darwin/src/MGLLineStyleLayer.mm b/platform/darwin/src/MGLLineStyleLayer.mm
index 4e6ff27b39..619cc70afe 100644
--- a/platform/darwin/src/MGLLineStyleLayer.mm
+++ b/platform/darwin/src/MGLLineStyleLayer.mm
@@ -390,7 +390,8 @@ namespace mbgl {
if (propertyValue.isUndefined()) {
propertyValue = self.rawLayer->getDefaultLinePattern();
}
- return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ NSExpression *expression = MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ return expression.mgl_expressionByReplacingTokensWithKeyPaths;
}
- (void)setLinePatternTransition:(MGLTransition )transition {
diff --git a/platform/darwin/src/MGLStyle.h b/platform/darwin/src/MGLStyle.h
index eb7554534f..cb0cdbac71 100644
--- a/platform/darwin/src/MGLStyle.h
+++ b/platform/darwin/src/MGLStyle.h
@@ -493,17 +493,20 @@ MGL_EXPORT
#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.
+ Attempts to localize labels in the style into the given locale.
- When this property is enabled, the style automatically modifies the text property
- of any symbol style layer whose source is the
- <a href="https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview">Mapbox
- Streets source</a>. 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;
+ This method automatically modifies the text property of any symbol style layer
+ in the style whose source is the
+ <a href="https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview">Mapbox Streets source</a>.
+ 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.
+
+ @param locale The locale into which labels should be localized. To use the
+ system’s preferred language, if supported, specify `nil`. To use the local
+ language, specify a locale with the identifier `mul`.
+ */
+- (void)localizeLabelsIntoLocale:(nullable NSLocale *)locale;
@end
diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm
index 0162dbd354..867ac6c451 100644
--- a/platform/darwin/src/MGLStyle.mm
+++ b/platform/darwin/src/MGLStyle.mm
@@ -570,73 +570,27 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles,
#pragma mark Mapbox Streets source introspection
-- (void)setLocalizesLabels:(BOOL)localizesLabels
-{
- if (_localizesLabels != localizesLabels) {
- _localizesLabels = localizesLabels;
- } else {
- return;
- }
+- (void)localizeLabelsIntoLocale:(nullable NSLocale *)locale {
+ NSSet<MGLVectorTileSource *> *streetsSources =
+ [self.sources filteredSetUsingPredicate:
+ [NSPredicate predicateWithBlock:^BOOL(MGLVectorTileSource * _Nullable source, NSDictionary<NSString *, id> * _Nullable bindings) {
+ return [source isKindOfClass:[MGLVectorTileSource class]] && [source isMapboxStreets];
+ }]];
+ NSSet<NSString *> *streetsSourceIdentifiers = [streetsSources valueForKey:@"identifier"];
- if (_localizesLabels) {
- NSString *preferredLanguage = [MGLVectorTileSource preferredMapboxStreetsLanguage];
- NSMutableDictionary *localizedKeysByKeyBySourceIdentifier = [NSMutableDictionary dictionary];
- for (MGLSymbolStyleLayer *layer in self.layers) {
- if (![layer isKindOfClass:[MGLSymbolStyleLayer class]]) {
- continue;
- }
-
- MGLVectorTileSource *source = (MGLVectorTileSource *)[self sourceWithIdentifier:layer.sourceIdentifier];
- if (![source isKindOfClass:[MGLVectorTileSource 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.expressionType == NSConstantValueExpressionType) {
- NSString *textField = layer.text.constantValue;
- 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 = [NSExpression expressionForConstantValue:localizingString];
- }
- }
+ for (MGLSymbolStyleLayer *layer in self.layers) {
+ if (![layer isKindOfClass:[MGLSymbolStyleLayer class]]) {
+ continue;
+ }
+ if (![streetsSourceIdentifiers containsObject:layer.sourceIdentifier]) {
+ continue;
}
- } else {
- [self.localizedLayersByIdentifier enumerateKeysAndObjectsUsingBlock:^(NSString *identifier, NSDictionary<NSObject *, MGLTextLanguage *> *textFields, BOOL *done) {
- MGLSymbolStyleLayer *layer = (MGLSymbolStyleLayer *)[self.mapView.style layerWithIdentifier:identifier];
-
- if (layer.text.expressionType == NSConstantValueExpressionType) {
- NSString *textField = layer.text.constantValue;
- [textFields enumerateKeysAndObjectsUsingBlock:^(NSObject *originalLanguage, MGLTextLanguage *textLanguage, BOOL *done) {
- if ([textLanguage.updatedTextField isEqualToString:textField]) {
- layer.text = [NSExpression expressionForConstantValue:textLanguage.originalTextField];
- }
- }];
-
- }
- }];
-
- self.localizedLayersByIdentifier = [NSMutableDictionary dictionary];
+ NSExpression *text = layer.text;
+ NSExpression *localizedText = [text mgl_expressionLocalizedIntoLocale:locale];
+ if (![localizedText isEqual:text]) {
+ layer.text = localizedText;
+ }
}
}
diff --git a/platform/darwin/src/MGLStyleLayer.mm.ejs b/platform/darwin/src/MGLStyleLayer.mm.ejs
index ac7676a1cc..42940083b5 100644
--- a/platform/darwin/src/MGLStyleLayer.mm.ejs
+++ b/platform/darwin/src/MGLStyleLayer.mm.ejs
@@ -136,7 +136,12 @@ namespace mbgl {
if (propertyValue.isUndefined()) {
propertyValue = self.rawLayer->getDefault<%- camelize(originalPropertyName(property)) %>();
}
+<% if (property.type === 'string') { -%>
+ NSExpression *expression = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toExpression(propertyValue);
+ return expression.mgl_expressionByReplacingTokensWithKeyPaths;
+<% } else { -%>
return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toExpression(propertyValue);
+<% } -%>
}
<% if (property.original) { -%>
@@ -174,7 +179,12 @@ namespace mbgl {
if (propertyValue.isUndefined()) {
propertyValue = self.rawLayer->getDefault<%- camelize(originalPropertyName(property)) %>();
}
+<% if (property.type === 'string') { -%>
+ NSExpression *expression = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toExpression(propertyValue);
+ return expression.mgl_expressionByReplacingTokensWithKeyPaths;
+<% } else { -%>
return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toExpression(propertyValue);
+<% } -%>
}
<% if (property["transition"]) { -%>
diff --git a/platform/darwin/src/MGLSymbolStyleLayer.mm b/platform/darwin/src/MGLSymbolStyleLayer.mm
index 24d6643e6c..0d9fac4808 100644
--- a/platform/darwin/src/MGLSymbolStyleLayer.mm
+++ b/platform/darwin/src/MGLSymbolStyleLayer.mm
@@ -233,7 +233,8 @@ namespace mbgl {
if (propertyValue.isUndefined()) {
propertyValue = self.rawLayer->getDefaultIconImage();
}
- return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ NSExpression *expression = MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ return expression.mgl_expressionByReplacingTokensWithKeyPaths;
}
- (void)setIconImage:(NSExpression *)iconImage {
@@ -578,7 +579,8 @@ namespace mbgl {
if (propertyValue.isUndefined()) {
propertyValue = self.rawLayer->getDefaultTextField();
}
- return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ NSExpression *expression = MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ return expression.mgl_expressionByReplacingTokensWithKeyPaths;
}
- (void)setTextField:(NSExpression *)textField {
diff --git a/platform/darwin/src/MGLVectorTileSource.mm b/platform/darwin/src/MGLVectorTileSource.mm
index 5678d9c0bb..c1f7267e4a 100644
--- a/platform/darwin/src/MGLVectorTileSource.mm
+++ b/platform/darwin/src/MGLVectorTileSource.mm
@@ -75,28 +75,55 @@
@implementation MGLVectorTileSource (Private)
+/**
+ An array of locale codes with dedicated name fields in the Mapbox Streets
+ source.
+
+ https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview
+ */
+static NSArray * const MGLMapboxStreetsLanguages = @[
+ @"ar", @"de", @"en", @"es", @"fr", @"pt", @"ru", @"zh", @"zh-Hans",
+];
+
+/**
+ Like `MGLMapboxStreetsLanguages`, but deanglicized for use with
+ `+[NSBundle preferredLocalizationsFromArray:forPreferences:]`.
+ */
+static NSArray * const MGLMapboxStreetsAlternativeLanguages = @[
+ @"mul", @"ar", @"de", @"es", @"fr", @"pt", @"ru", @"zh", @"zh-Hans",
+];
+
+ (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];
+ mapboxStreetsLanguages = [NSSet setWithArray:MGLMapboxStreetsLanguages];
});
return mapboxStreetsLanguages;
}
+ (NSString *)preferredMapboxStreetsLanguage {
- NSArray<NSString *> *supportedLanguages = [MGLVectorTileSource mapboxStreetsLanguages].allObjects;
- NSArray<NSString *> *preferredLanguages = [NSBundle preferredLocalizationsFromArray:supportedLanguages
- forPreferences:[NSLocale preferredLanguages]];
+ return [self preferredMapboxStreetsLanguageForPreferences:[NSLocale preferredLanguages]];
+}
+
++ (NSString *)preferredMapboxStreetsLanguageForPreferences:(NSArray<NSString *> *)preferencesArray {
+ BOOL acceptsEnglish = [preferencesArray filteredArrayUsingPredicate:
+ [NSPredicate predicateWithBlock:^BOOL(NSString * _Nullable language, NSDictionary<NSString *,id> * _Nullable bindings) {
+ return [[NSLocale localeWithLocaleIdentifier:language].languageCode isEqualToString:@"en"];
+ }]].count;
+
+ NSArray<NSString *> *preferredLanguages = [NSBundle preferredLocalizationsFromArray:MGLMapboxStreetsAlternativeLanguages
+ forPreferences:preferencesArray];
NSString *mostSpecificLanguage;
for (NSString *language in preferredLanguages) {
if (language.length > mostSpecificLanguage.length) {
mostSpecificLanguage = language;
}
}
- return mostSpecificLanguage ?: @"en";
+ if ([mostSpecificLanguage isEqualToString:@"mul"]) {
+ return acceptsEnglish ? @"en" : nil;
+ }
+ return mostSpecificLanguage;
}
- (BOOL)isMapboxStreets {
diff --git a/platform/darwin/src/MGLVectorTileSource_Private.h b/platform/darwin/src/MGLVectorTileSource_Private.h
index 41873b4ff6..77521869f1 100644
--- a/platform/darwin/src/MGLVectorTileSource_Private.h
+++ b/platform/darwin/src/MGLVectorTileSource_Private.h
@@ -8,7 +8,8 @@ NS_ASSUME_NONNULL_BEGIN
+ (NS_SET_OF(NSString *) *)mapboxStreetsLanguages;
-+ (NSString *)preferredMapboxStreetsLanguage;
++ (nullable NSString *)preferredMapboxStreetsLanguage;
++ (nullable NSString *)preferredMapboxStreetsLanguageForPreferences:(NSArray<NSString *> *)preferencesArray;
- (NS_DICTIONARY_OF(NSString *, NSString *) *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage;
diff --git a/platform/darwin/src/NSExpression+MGLAdditions.h b/platform/darwin/src/NSExpression+MGLAdditions.h
index 66d306ad06..a0081cb428 100644
--- a/platform/darwin/src/NSExpression+MGLAdditions.h
+++ b/platform/darwin/src/NSExpression+MGLAdditions.h
@@ -44,6 +44,22 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, readonly) id mgl_jsonExpressionObject;
+/**
+ Returns a copy of the receiver localized into the given locale.
+
+ This method assumes the receiver refers to the feature attributes that are
+ available in vector tiles supplied by the
+ <a href="https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview">Mapbox Streets source</a>.
+ 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.
+
+ @param locale The locale into which labels should be localized. To use the
+ system’s preferred language, if supported, specify `nil`. To use the local
+ language, specify a locale with the identifier `mul`.
+ */
+- (NSExpression *)mgl_expressionLocalizedIntoLocale:(nullable NSLocale *)locale;
+
@end
NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/NSExpression+MGLAdditions.mm b/platform/darwin/src/NSExpression+MGLAdditions.mm
index c175868dae..be6eef7f47 100644
--- a/platform/darwin/src/NSExpression+MGLAdditions.mm
+++ b/platform/darwin/src/NSExpression+MGLAdditions.mm
@@ -10,6 +10,7 @@
#endif
#import "NSPredicate+MGLAdditions.h"
#import "NSValue+MGLStyleAttributeAdditions.h"
+#import "MGLVectorTileSource_Private.h"
#import <objc/runtime.h>
@@ -328,6 +329,112 @@
return {};
}
+// Selectors of functions that can contain tokens in arguments.
+static NSArray * const MGLTokenizedFunctions = @[
+ @"mgl_interpolateWithCurveType:parameters:stops:",
+ @"mgl_interpolate:withCurveType:parameters:stops:",
+ @"mgl_stepWithMinimum:stops:",
+ @"mgl_step:from:stops:",
+];
+
+/**
+ Returns a copy of the given collection with tokens replaced by key path
+ expressions.
+
+ If no replacements take place, this method returns the original collection.
+ */
+NS_ARRAY_OF(NSExpression *) *MGLCollectionByReplacingTokensWithKeyPaths(NS_ARRAY_OF(NSExpression *) *collection) {
+ __block NSMutableArray *upgradedCollection;
+ [collection enumerateObjectsUsingBlock:^(NSExpression * _Nonnull item, NSUInteger idx, BOOL * _Nonnull stop) {
+ NSExpression *upgradedItem = item.mgl_expressionByReplacingTokensWithKeyPaths;
+ if (upgradedItem != item) {
+ if (!upgradedCollection) {
+ upgradedCollection = [collection mutableCopy];
+ }
+ upgradedCollection[idx] = upgradedItem;
+ }
+ }];
+ return upgradedCollection ?: collection;
+};
+
+/**
+ Returns a copy of the given stop dictionary with tokens replaced by key path
+ expressions.
+
+ If no replacements take place, this method returns the original stop
+ dictionary.
+ */
+NS_DICTIONARY_OF(NSNumber *, NSExpression *) *MGLStopDictionaryByReplacingTokensWithKeyPaths(NS_DICTIONARY_OF(NSNumber *, NSExpression *) *stops) {
+ __block NSMutableDictionary *upgradedStops;
+ [stops enumerateKeysAndObjectsUsingBlock:^(id _Nonnull zoomLevel, NSExpression * _Nonnull value, BOOL * _Nonnull stop) {
+ if (![value isKindOfClass:[NSExpression class]]) {
+ value = [NSExpression expressionForConstantValue:value];
+ }
+ NSExpression *upgradedValue = value.mgl_expressionByReplacingTokensWithKeyPaths;
+ if (upgradedValue != value) {
+ if (!upgradedStops) {
+ upgradedStops = [stops mutableCopy];
+ }
+ upgradedStops[zoomLevel] = upgradedValue;
+ }
+ }];
+ return upgradedStops ?: stops;
+};
+
+- (NSExpression *)mgl_expressionByReplacingTokensWithKeyPaths {
+ switch (self.expressionType) {
+ case NSConstantValueExpressionType: {
+ NSString *constantValue = self.constantValue;
+ if ([constantValue isKindOfClass:[NSString class]] &&
+ [constantValue containsString:@"{"] && [constantValue containsString:@"}"]) {
+ NSMutableArray *components = [NSMutableArray array];
+ NSScanner *scanner = [NSScanner scannerWithString:constantValue];
+ scanner.charactersToBeSkipped = nil;
+ while (!scanner.isAtEnd) {
+ NSString *string;
+ if ([scanner scanUpToString:@"{" intoString:&string]) {
+ [components addObject:[NSExpression expressionForConstantValue:string]];
+ }
+
+ NSString *token;
+ if ([scanner scanString:@"{" intoString:NULL]
+ && [scanner scanUpToString:@"}" intoString:&token]
+ && [scanner scanString:@"}" intoString:NULL]) {
+ [components addObject:[NSExpression expressionForKeyPath:token]];
+ }
+ }
+ if (components.count == 1) {
+ return components.firstObject;
+ }
+ return [NSExpression expressionForFunction:@"mgl_join:"
+ arguments:@[[NSExpression expressionForAggregate:components]]];
+ }
+ NSDictionary *stops = self.constantValue;
+ if ([stops isKindOfClass:[NSDictionary class]]) {
+ NSDictionary *localizedStops = MGLStopDictionaryByReplacingTokensWithKeyPaths(stops);
+ if (localizedStops != stops) {
+ return [NSExpression expressionForConstantValue:localizedStops];
+ }
+ }
+ return self;
+ }
+
+ case NSFunctionExpressionType: {
+ if ([MGLTokenizedFunctions containsObject:self.function]) {
+ NSArray *arguments = self.arguments;
+ NSArray *localizedArguments = MGLCollectionByReplacingTokensWithKeyPaths(arguments);
+ if (localizedArguments != arguments) {
+ return [NSExpression expressionForFunction:self.operand selectorName:self.function arguments:localizedArguments];
+ }
+ }
+ return self;
+ }
+
+ default:
+ return self;
+ }
+}
+
@end
@implementation NSObject (MGLExpressionAdditions)
@@ -1108,4 +1215,126 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
return expressionObject;
}
+#pragma mark Localization
+
+/**
+ Returns a localized copy of the given collection.
+
+ If no localization takes place, this method returns the original collection.
+ */
+NS_ARRAY_OF(NSExpression *) *MGLLocalizedCollection(NS_ARRAY_OF(NSExpression *) *collection, NSLocale * _Nullable locale) {
+ __block NSMutableArray *localizedCollection;
+ [collection enumerateObjectsUsingBlock:^(NSExpression * _Nonnull item, NSUInteger idx, BOOL * _Nonnull stop) {
+ NSExpression *localizedItem = [item mgl_expressionLocalizedIntoLocale:locale];
+ if (localizedItem != item) {
+ if (!localizedCollection) {
+ localizedCollection = [collection mutableCopy];
+ }
+ localizedCollection[idx] = localizedItem;
+ }
+ }];
+ return localizedCollection ?: collection;
+};
+
+/**
+ Returns a localized copy of the given stop dictionary.
+
+ If no localization takes place, this method returns the original stop
+ dictionary.
+ */
+NS_DICTIONARY_OF(NSNumber *, NSExpression *) *MGLLocalizedStopDictionary(NS_DICTIONARY_OF(NSNumber *, NSExpression *) *stops, NSLocale * _Nullable locale) {
+ __block NSMutableDictionary *localizedStops;
+ [stops enumerateKeysAndObjectsUsingBlock:^(id _Nonnull zoomLevel, NSExpression * _Nonnull value, BOOL * _Nonnull stop) {
+ if (![value isKindOfClass:[NSExpression class]]) {
+ value = [NSExpression expressionForConstantValue:value];
+ }
+ NSExpression *localizedValue = [value mgl_expressionLocalizedIntoLocale:locale];
+ if (localizedValue != value) {
+ if (!localizedStops) {
+ localizedStops = [stops mutableCopy];
+ }
+ localizedStops[zoomLevel] = localizedValue;
+ }
+ }];
+ return localizedStops ?: stops;
+};
+
+- (NSExpression *)mgl_expressionLocalizedIntoLocale:(nullable NSLocale *)locale {
+ switch (self.expressionType) {
+ case NSConstantValueExpressionType: {
+ NSDictionary *stops = self.constantValue;
+ if ([stops isKindOfClass:[NSDictionary class]]) {
+ NSDictionary *localizedStops = MGLLocalizedStopDictionary(stops, locale);
+ if (localizedStops != stops) {
+ return [NSExpression expressionForConstantValue:localizedStops];
+ }
+ }
+ return self;
+ }
+
+ case NSKeyPathExpressionType: {
+ if ([self.keyPath isEqualToString:@"name"] || [self.keyPath hasPrefix:@"name_"]) {
+ NSString *localizedKeyPath = @"name";
+ if (![locale.localeIdentifier isEqualToString:@"mul"]) {
+ NSArray *preferences = locale ? @[locale.localeIdentifier] : [NSLocale preferredLanguages];
+ NSString *preferredLanguage = [MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences];
+ if (preferredLanguage) {
+ localizedKeyPath = [NSString stringWithFormat:@"name_%@", preferredLanguage];
+ }
+ }
+ return [NSExpression expressionForKeyPath:localizedKeyPath];
+ }
+ return self;
+ }
+
+ case NSFunctionExpressionType: {
+ NSExpression *operand = self.operand;
+ NSExpression *localizedOperand = [operand mgl_expressionLocalizedIntoLocale:locale];
+
+ NSArray *arguments = self.arguments;
+ NSArray *localizedArguments = MGLLocalizedCollection(arguments, locale);
+ if (localizedArguments != arguments) {
+ return [NSExpression expressionForFunction:localizedOperand
+ selectorName:self.function
+ arguments:localizedArguments];
+ }
+ if (localizedOperand != operand) {
+ return [NSExpression expressionForFunction:localizedOperand
+ selectorName:self.function
+ arguments:self.arguments];
+ }
+ return self;
+ }
+
+ case NSConditionalExpressionType: {
+ if (@available(iOS 9.0, *)) {
+ NSExpression *trueExpression = self.trueExpression;
+ NSExpression *localizedTrueExpression = [trueExpression mgl_expressionLocalizedIntoLocale:locale];
+ NSExpression *falseExpression = self.falseExpression;
+ NSExpression *localizedFalseExpression = [falseExpression mgl_expressionLocalizedIntoLocale:locale];
+ if (localizedTrueExpression != trueExpression || localizedFalseExpression != falseExpression) {
+ return [NSExpression expressionForConditional:self.predicate
+ trueExpression:localizedTrueExpression
+ falseExpression:localizedFalseExpression];
+ }
+ }
+ return self;
+ }
+
+ case NSAggregateExpressionType: {
+ NSArray *collection = self.collection;
+ if ([collection isKindOfClass:[NSArray class]]) {
+ NSArray *localizedCollection = MGLLocalizedCollection(collection, locale);
+ if (localizedCollection != collection) {
+ return [NSExpression expressionForAggregate:localizedCollection];
+ }
+ }
+ return self;
+ }
+
+ default:
+ return self;
+ }
+}
+
@end
diff --git a/platform/darwin/src/NSExpression+MGLPrivateAdditions.h b/platform/darwin/src/NSExpression+MGLPrivateAdditions.h
index d255cfdb0f..a1948f9e45 100644
--- a/platform/darwin/src/NSExpression+MGLPrivateAdditions.h
+++ b/platform/darwin/src/NSExpression+MGLPrivateAdditions.h
@@ -27,6 +27,11 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) mbgl::FeatureIdentifier mgl_featureIdentifier;
@property (nonatomic, readonly) std::vector<mbgl::FeatureIdentifier> mgl_aggregateFeatureIdentifier;
+/**
+ Returns a copy of the receiver with tokens replaced by key path expressions.
+ */
+- (NSExpression *)mgl_expressionByReplacingTokensWithKeyPaths;
+
@end
@interface NSNull (MGLExpressionAdditions)
@@ -74,6 +79,7 @@ NS_ASSUME_NONNULL_BEGIN
- (NSExpression *)mgl_expressionWithContext:(NSDictionary<NSString *, NSExpression *> *)context;
+
- (id)mgl_has:(id)element;
@end
diff --git a/platform/darwin/test/MGLExpressionTests.mm b/platform/darwin/test/MGLExpressionTests.mm
index b050c5f652..c91cf36d1a 100644
--- a/platform/darwin/test/MGLExpressionTests.mm
+++ b/platform/darwin/test/MGLExpressionTests.mm
@@ -866,4 +866,144 @@ using namespace std::string_literals;
}
}
+#pragma mark - Localization tests
+
+- (void)testTokenReplacement {
+ {
+ NSExpression *original = MGLConstantExpression(@"");
+ NSExpression *expected = original;
+ XCTAssertEqualObjects(original.mgl_expressionByReplacingTokensWithKeyPaths, expected);
+ }
+ {
+ NSExpression *original = MGLConstantExpression(@"{");
+ NSExpression *expected = original;
+ XCTAssertEqualObjects(original.mgl_expressionByReplacingTokensWithKeyPaths, expected);
+ }
+ {
+ NSExpression *original = MGLConstantExpression(@"{token");
+ NSExpression *expected = original;
+ XCTAssertEqualObjects(original.mgl_expressionByReplacingTokensWithKeyPaths, expected);
+ }
+ {
+ NSExpression *original = MGLConstantExpression(@"{token}");
+ NSExpression *expected = [NSExpression expressionForKeyPath:@"token"];
+ XCTAssertEqualObjects(original.mgl_expressionByReplacingTokensWithKeyPaths, expected);
+ }
+ {
+ NSExpression *original = MGLConstantExpression(@"{token} {token}");
+ NSExpression *expected = [NSExpression expressionWithFormat:@"mgl_join({token, ' ', token})"];
+ XCTAssertEqualObjects(original.mgl_expressionByReplacingTokensWithKeyPaths, expected);
+ }
+ {
+ NSExpression *original = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, '{short}', %@)", @{
+ @1: MGLConstantExpression(@"{short}"),
+ @2: @"…",
+ @3: @"{long}",
+ }];
+ NSExpression *expected = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, short, %@)", @{
+ @1: [NSExpression expressionForKeyPath:@"short"],
+ @2: @"…",
+ @3: [NSExpression expressionForKeyPath:@"long"],
+ }];
+ XCTAssertEqualObjects(original.mgl_expressionByReplacingTokensWithKeyPaths, expected);
+ }
+}
+
+- (void)testLocalization {
+ {
+ NSExpression *original = MGLConstantExpression(@"");
+ NSExpression *expected = original;
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:nil], expected);
+ }
+ {
+ NSExpression *original = MGLConstantExpression(@"Old MacDonald");
+ NSExpression *expected = original;
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:nil], expected);
+ }
+ {
+ NSExpression *original = MGLConstantExpression(@"{name_en}");
+ NSExpression *expected = original;
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:nil], expected);
+ }
+ {
+ NSExpression *original = [NSExpression expressionForKeyPath:@"name_en"];
+ NSExpression *expected = original;
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:nil], expected);
+ }
+ {
+ NSExpression *original = [NSExpression expressionForKeyPath:@"name_en"];
+ NSExpression *expected = [NSExpression expressionForKeyPath:@"name"];
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:[NSLocale localeWithLocaleIdentifier:@"mul"]], expected);
+ }
+ {
+ NSExpression *original = [NSExpression expressionForKeyPath:@"name_en"];
+ NSExpression *expected = [NSExpression expressionForKeyPath:@"name_fr"];
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:[NSLocale localeWithLocaleIdentifier:@"fr-CA"]], expected);
+ }
+ {
+ NSExpression *original = [NSExpression expressionForKeyPath:@"name_en"];
+ NSExpression *expected = [NSExpression expressionForKeyPath:@"name_zh-Hans"];
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:[NSLocale localeWithLocaleIdentifier:@"zh-Hans"]], expected);
+ }
+ {
+ NSExpression *original = [NSExpression expressionForKeyPath:@"name_en"];
+ NSExpression *expected = [NSExpression expressionForKeyPath:@"name"];
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:[NSLocale localeWithLocaleIdentifier:@"tlh"]], expected);
+ }
+ {
+ NSExpression *original = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, short, %@)", @{
+ @1: [NSExpression expressionForKeyPath:@"abbr"],
+ @2: @"…",
+ @3: [NSExpression expressionForKeyPath:@"name_fr"],
+ }];
+ NSExpression *expected = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, short, %@)", @{
+ @1: [NSExpression expressionForKeyPath:@"abbr"],
+ @2: @"…",
+ @3: [NSExpression expressionForKeyPath:@"name_es"],
+ }];
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:[NSLocale localeWithLocaleIdentifier:@"es-PR"]], expected);
+ }
+ {
+ NSArray *jsonExpression = @[
+ @"step",
+ @[@"zoom"],
+ @[
+ @"case",
+ @[
+ @"<",
+ @[
+ @"to-number",
+ @[@"get", @"area"]
+ ],
+ @80000
+ ],
+ @[@"get", @"abbr"],
+ @[@"get", @"name_en"]
+ ],
+ @5, @[@"get", @"name_en"]
+ ];
+ NSArray *localizedJSONExpression = @[
+ @"step",
+ @[@"zoom"],
+ @[
+ @"case",
+ @[
+ @"<",
+ @[
+ @"to-number",
+ @[@"get", @"area"]
+ ],
+ @80000
+ ],
+ @[@"get", @"abbr"],
+ @[@"get", @"name"]
+ ],
+ @5, @[@"get", @"name"]
+ ];
+ NSExpression *expression = [NSExpression expressionWithMGLJSONObject:jsonExpression];
+ NSExpression *localizedExpression = [expression mgl_expressionLocalizedIntoLocale:[NSLocale localeWithLocaleIdentifier:@"mul"]];
+ XCTAssertEqualObjects(localizedExpression.mgl_jsonExpressionObject, localizedJSONExpression);
+ }
+}
+
@end
diff --git a/platform/darwin/test/MGLStyleTests.mm b/platform/darwin/test/MGLStyleTests.mm
index 7b1cc56ba4..6048f39ea3 100644
--- a/platform/darwin/test/MGLStyleTests.mm
+++ b/platform/darwin/test/MGLStyleTests.mm
@@ -1,6 +1,7 @@
#import <Mapbox/Mapbox.h>
#import "NSBundle+MGLAdditions.h"
+#import "MGLVectorTileSource_Private.h"
#import <mbgl/util/default_styles.hpp>
@@ -419,4 +420,45 @@
XCTAssertEqualObjects(layers[startIndex++].identifier, layer4.identifier);
}
+#pragma mark Localization tests
+
+- (void)testLanguageMatching {
+ {
+ NSArray *preferences = @[@"en"];
+ XCTAssertEqualObjects([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"en");
+ }
+ {
+ NSArray *preferences = @[@"en-US"];
+ XCTAssertEqualObjects([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"en");
+ }
+ {
+ NSArray *preferences = @[@"fr"];
+ XCTAssertEqualObjects([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"fr");
+ }
+ {
+ NSArray *preferences = @[@"zh-Hans"];
+ XCTAssertEqualObjects([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"zh-Hans");
+ }
+ {
+ NSArray *preferences = @[@"zh-Hans", @"en"];
+ XCTAssertEqualObjects([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"zh-Hans");
+ }
+ {
+ NSArray *preferences = @[@"zh-Hant"];
+ XCTAssertNil([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences]);
+ }
+ {
+ NSArray *preferences = @[@"tlh"];
+ XCTAssertNil([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences]);
+ }
+ {
+ NSArray *preferences = @[@"tlh", @"en"];
+ XCTAssertEqualObjects([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"en");
+ }
+ {
+ NSArray *preferences = @[@"mul"];
+ XCTAssertNil([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences]);
+ }
+}
+
@end
diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md
index 6eec7c0e48..f9644e8817 100644
--- a/platform/ios/CHANGELOG.md
+++ b/platform/ios/CHANGELOG.md
@@ -12,16 +12,16 @@ The 4.0._x_ series of releases will be the last to support iOS 8. The minimum iO
* Added Danish and Hebrew localizations. ([#10967](https://github.com/mapbox/mapbox-gl-native/pull/10967), [#11136](https://github.com/mapbox/mapbox-gl-native/pull/11134))
* Removed methods, properties, and constants that had been deprecated as of v3.7.4. ([#11205](https://github.com/mapbox/mapbox-gl-native/pull/11205))
-### Styles and rendering
+### Styles
* Added support for a new layer type: `MGLHeatmapStyleLayer`, a powerful way to visualize point data distributions using heatmaps, fully customizable through runtime styling. [#11046](https://github.com/mapbox/mapbox-gl-native/pull/11046)
* The layout and paint properties on subclasses of `MGLStyleLayer` are now of type `NSExpression` instead of `MGLStyleValue`. A new “Predicates and Expressions” guide provides an overview of the supported operators. ([#10726](https://github.com/mapbox/mapbox-gl-native/pull/10726))
* Renamed `MGLRasterSource` to `MGLRasterTileSource` and `MGLVectorSource` to `MGLVectorTileSource`. ([#11568](https://github.com/mapbox/mapbox-gl-native/pull/11568))
* Added an `MGLComputedShapeSource` class that allows applications to supply vector data to a style layer on a per-tile basis. ([#9983](https://github.com/mapbox/mapbox-gl-native/pull/9983))
* A style can now display smooth hillshading and customize its appearance at runtime using the `MGLHillshadeStyleLayer` class. Hillshading is based on a rasterized digital elevation model supplied by the `MGLRasterDEMSource` class. ([#10642](https://github.com/mapbox/mapbox-gl-native/pull/10642))
+* Replaced the `MGLStyle.localizesLabels` property with an `-[MGLStyle localizeLabelsIntoLocale:]` method that allows you to specify the language to localize into. Also added an `-[NSExpression(MGLAdditions) mgl_expressionLocalizedIntoLocale:]` method for localizing an individual value used with `MGLSymbolStyleLayer.text`. ([#11651](https://github.com/mapbox/mapbox-gl-native/pull/11651))
* The `MGLSymbolStyleLayer.textFontNames` property can now depend on a feature’s attributes. ([#10850](https://github.com/mapbox/mapbox-gl-native/pull/10850))
* Added `MGLShapeSourceOptionWrapsCoordinates`, to specify whether the shape of an `MGLComputedShapeSource` should be wrapped to accommodate coordinates with longitudes beyond −180 and 180; and `MGLShapeSourceOptionClipsCoordinates`, to specify whether the shape of an `MGLComputedShapeSource` should be clipped at the edge of each tile. ([#11041](https://github.com/mapbox/mapbox-gl-native/pull/11041))
-* Added support for Mapzen Terrarium DEM encoding. ([#11274](https://github.com/mapbox/mapbox-gl-native/pull/11274))
* The layer filtering predicate's format strings now can contain arithmetic and calls to built-in `NSExpression` functions. ([#11587](https://github.com/mapbox/mapbox-gl-native/pull/11587))
* The layer filtering predicate's key paths now may need to be cast to `NSString` or `NSNumber`. ([#11587](https://github.com/mapbox/mapbox-gl-native/pull/11587))
diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m
index 07d684cd64..0a76f8c0c5 100644
--- a/platform/ios/app/MBXViewController.m
+++ b/platform/ios/app/MBXViewController.m
@@ -92,7 +92,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
MBXSettingsMiscellaneousShowZoomLevel,
MBXSettingsMiscellaneousScrollView,
MBXSettingsMiscellaneousToggleTwoMaps,
- MBXSettingsMiscellaneousCountryLabels,
+ MBXSettingsMiscellaneousLocalizeLabels,
MBXSettingsMiscellaneousShowSnapshots,
MBXSettingsMiscellaneousShouldLimitCameraChanges,
MBXSettingsMiscellaneousPrintLogFile,
@@ -130,7 +130,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
@property (nonatomic) NSInteger styleIndex;
@property (nonatomic) BOOL debugLoggingEnabled;
@property (nonatomic) BOOL customUserLocationAnnnotationEnabled;
-@property (nonatomic) BOOL usingLocaleBasedCountryLabels;
+@property (nonatomic, getter=isLocalizingLabels) BOOL localizingLabels;
@property (nonatomic) BOOL reuseQueueStatsEnabled;
@property (nonatomic) BOOL showZoomLevelEnabled;
@property (nonatomic) BOOL shouldLimitCameraChanges;
@@ -139,7 +139,6 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
@interface MGLMapView (MBXViewController)
-@property (nonatomic) BOOL usingLocaleBasedCountryLabels;
@property (nonatomic) NSDictionary *annotationViewReuseQueueByIdentifier;
@end
@@ -383,7 +382,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[NSString stringWithFormat:@"%@ Zoom/Pitch/Direction Label", (_showZoomLevelEnabled ? @"Hide" :@"Show")],
@"Embedded Map View",
[NSString stringWithFormat:@"%@ Second Map", ([self.view viewWithTag:2] == nil ? @"Show" : @"Hide")],
- [NSString stringWithFormat:@"Show Labels in %@", (_usingLocaleBasedCountryLabels ? @"Default Language" : [[NSLocale currentLocale] displayNameForKey:NSLocaleIdentifier value:[self bestLanguageForUser]])],
+ [NSString stringWithFormat:@"Show Labels in %@", (_localizingLabels ? @"Default Language" : [[NSLocale currentLocale] displayNameForKey:NSLocaleIdentifier value:[self bestLanguageForUser]])],
@"Show Snapshots",
[NSString stringWithFormat:@"%@ Camera Changes", (_shouldLimitCameraChanges ? @"Unlimit" : @"Limit")],
]];
@@ -574,7 +573,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
case MBXSettingsMiscellaneous:
switch (indexPath.row)
{
- case MBXSettingsMiscellaneousCountryLabels:
+ case MBXSettingsMiscellaneousLocalizeLabels:
[self styleCountryLabelsLanguage];
break;
case MBXSettingsMiscellaneousWorldTour:
@@ -1400,8 +1399,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
-(void)styleCountryLabelsLanguage
{
- _usingLocaleBasedCountryLabels = !_usingLocaleBasedCountryLabels;
- self.mapView.style.localizesLabels = _usingLocaleBasedCountryLabels;
+ _localizingLabels = !_localizingLabels;
+ [self.mapView.style localizeLabelsIntoLocale:_localizingLabels ? [NSLocale localeWithLocaleIdentifier:@"mul"] : nil];
}
- (void)styleRouteLine
@@ -1984,7 +1983,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
// Default Mapbox styles use {name_en} as their label language, which means
// that a device with an English-language locale is already effectively
// using locale-based country labels.
- _usingLocaleBasedCountryLabels = [[self bestLanguageForUser] isEqualToString:@"en"];
+ _localizingLabels = [[self bestLanguageForUser] isEqualToString:@"en"];
}
- (BOOL)mapView:(MGLMapView *)mapView shouldChangeFromCamera:(MGLMapCamera *)oldCamera toCamera:(MGLMapCamera *)newCamera {
diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md
index 9df33211af..0cfba09455 100644
--- a/platform/macos/CHANGELOG.md
+++ b/platform/macos/CHANGELOG.md
@@ -9,6 +9,7 @@
* Renamed `MGLRasterSource` to `MGLRasterTileSource` and `MGLVectorSource` to `MGLVectorTileSource`. ([#11568](https://github.com/mapbox/mapbox-gl-native/pull/11568))
* Added an `MGLComputedShapeSource` class that allows applications to supply vector data to a style layer on a per-tile basis. ([#9983](https://github.com/mapbox/mapbox-gl-native/pull/9983))
* A style can now display smooth hillshading and customize its appearance at runtime using the `MGLHillshadeStyleLayer` class. Hillshading is based on a rasterized digital elevation model supplied by the `MGLRasterDEMSource` class. ([#10642](https://github.com/mapbox/mapbox-gl-native/pull/10642))
+* Replaced the `MGLStyle.localizesLabels` property with an `-[MGLStyle localizeLabelsIntoLocale:]` method that allows you to specify the language to localize into. Note that this method does not automatically update the style when the system’s preferred language changes. Also added an `-[NSExpression(MGLAdditions) mgl_expressionLocalizedIntoLocale:]` method for localizing an individual value used with `MGLSymbolStyleLayer.text`. ([#11651](https://github.com/mapbox/mapbox-gl-native/pull/11651))
* Fixed incorrect color calibration on macOS 10.13 High Sierra when using color-related methods of `MGLStyleLayer` subclasses, as well as when displaying an `MGLAttributionInfo`. It is no longer necessary to explicitly convert an `NSColor` to the sRGB color space before using these classes on High Sierra. ([#11391](https://github.com/mapbox/mapbox-gl-native/pull/11391))
* The `MGLSymbolStyleLayer.textFontNames` property can now depend on a feature’s attributes. ([#10850](https://github.com/mapbox/mapbox-gl-native/pull/10850))
* Properties such as `MGLSymbolStyleLayer.iconAllowsOverlap` and `MGLSymbolStyleLayer.iconIgnoresPlacement` now account for symbols in other sources. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436))
diff --git a/platform/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m
index 2a65ee144c..9b18dbd761 100644
--- a/platform/macos/app/MapDocument.m
+++ b/platform/macos/app/MapDocument.m
@@ -421,7 +421,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
}
- (void)updateLabels {
- self.mapView.style.localizesLabels = _isLocalizingLabels;
+ [self.mapView.style localizeLabelsIntoLocale:_isLocalizingLabels ? nil : [NSLocale localeWithLocaleIdentifier:@"mul"]];
}
- (void)applyPendingState {
@@ -1067,7 +1067,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
menuItem.state = menuItem.tag == _isLocalizingLabels ? NSOnState: NSOffState;
if (menuItem.tag) {
NSLocale *locale = [NSLocale localeWithLocaleIdentifier:[NSBundle mainBundle].developmentLocalization];
- NSString *preferredLanguage = [MGLVectorTileSource preferredMapboxStreetsLanguage];
+ NSString *preferredLanguage = [MGLVectorTileSource preferredMapboxStreetsLanguage] ?: @"en";
menuItem.title = [locale displayNameForKey:NSLocaleIdentifier value:preferredLanguage];
}
return YES;
diff --git a/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/macosapp.xcscheme b/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/macosapp.xcscheme
index b0ce01fbf0..aab7486fbe 100644
--- a/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/macosapp.xcscheme
+++ b/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/macosapp.xcscheme
@@ -26,7 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
- language = ""
+ language = "en"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
@@ -56,7 +56,6 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
- language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"