Skip to content
This repository was archived by the owner on Aug 8, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions platform/darwin/src/MGLStyle.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
<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;

@end

NS_ASSUME_NONNULL_END
136 changes: 136 additions & 0 deletions platform/darwin/src/MGLStyle.mm
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#import "MGLSource.h"
#import "MGLTileSource_Private.h"
#import "MGLVectorSource.h"
#import "MGLVectorSource+MGLAdditions.h"
#import "MGLRasterSource.h"
#import "MGLShapeSource.h"

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -116,6 +140,7 @@ - (instancetype)initWithMapView:(MGLMapView *)mapView {
if (self = [super init]) {
_mapView = mapView;
_openGLLayers = [NSMutableDictionary dictionary];
_localizedLayersByIdentifier = [NSMutableDictionary dictionary];
}
return self;
}
Expand Down Expand Up @@ -609,4 +634,115 @@ - (NSString *)description
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<NSString *> *)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<NSString *> valueWithRawValue:localizingString];
}
}
else if ([layer.text isKindOfClass:[MGLCameraStyleFunction class]]) {
MGLCameraStyleFunction *function = (MGLCameraStyleFunction<NSString *> *)layer.text;
NSMutableDictionary *stops = function.stops.mutableCopy;
NSMutableDictionary *cameraStops = [NSMutableDictionary dictionary];
[stops enumerateKeysAndObjectsUsingBlock:^(NSNumber *zoomLevel, MGLConstantStyleValue<NSString *> *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<NSString *> 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<NSObject *, MGLTextLanguage *> *textFields, BOOL *done) {
MGLSymbolStyleLayer *layer = (MGLSymbolStyleLayer *)[self.mapView.style layerWithIdentifier:identifier];

if ([layer.text isKindOfClass:[MGLConstantStyleValue class]]) {
NSString *textField = [(MGLConstantStyleValue<NSString *> *)layer.text rawValue];
[textFields enumerateKeysAndObjectsUsingBlock:^(NSObject *originalLanguage, MGLTextLanguage *textLanguage, BOOL *done) {
if ([textLanguage.updatedTextField isEqualToString:textField]) {
layer.text = [MGLStyleValue<NSString *> valueWithRawValue:textLanguage.originalTextField];
}
}];

}
else if ([layer.text isKindOfClass:[MGLCameraStyleFunction class]]) {
MGLCameraStyleFunction *function = (MGLCameraStyleFunction<NSString *> *)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<NSString *> *stop = [stops objectForKey:zoomLevel];
NSString *textField = stop.rawValue;
if ([textLanguage.updatedTextField isEqualToString:textField]) {
stops[zoomLevel] = [MGLStyleValue<NSString *> valueWithRawValue:textLanguage.originalTextField];
}
}
}];

function.stops = stops;
layer.text = function;
}

}];

self.localizedLayersByIdentifier = [NSMutableDictionary dictionary];
}
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

NS_ASSUME_NONNULL_BEGIN

@interface MGLVectorSource (MBXAdditions)
@interface MGLVectorSource (MGLAdditions)

+ (NSString *)preferredMapboxStreetsLanguage;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#import "MGLVectorSource+MBXAdditions.h"
#import "MGLVectorSource+MGLAdditions.h"

@implementation MGLVectorSource (MBXAdditions)
@implementation MGLVectorSource (MGLAdditions)

+ (NS_SET_OF(NSString *) *)mapboxStreetsLanguages {
// https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview
Expand Down
6 changes: 5 additions & 1 deletion platform/ios/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
43 changes: 3 additions & 40 deletions platform/ios/app/MBXViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ - (void)dismissSettings:(__unused id)sender
@"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",
]];
Expand Down Expand Up @@ -1274,12 +1274,8 @@ - (void)styleRasterSource

-(void)styleCountryLabelsLanguage
{
NSArray<NSString *> *labelLayers = @[
@"country-label-lg",
@"country-label-md",
@"country-label-sm",
];
[self styleLabelLanguageForLayersNamed:labelLayers];
_usingLocaleBasedCountryLabels = !_usingLocaleBasedCountryLabels;
self.mapView.style.localizesLabels = _usingLocaleBasedCountryLabels;
}

- (void)styleRouteLine
Expand Down Expand Up @@ -1362,39 +1358,6 @@ - (void)stylePolygonWithDDS {
[self.mapView.style addLayer:fillStyleLayer];
}

- (void)styleLabelLanguageForLayersNamed:(NSArray<NSString *> *)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<NSString *> *)layer.text;
if ([label.rawValue hasPrefix:@"{name"]) {
layer.text = [MGLStyleValue valueWithRawValue:language];
}
}
else if ([layer.text isKindOfClass:[MGLCameraStyleFunction class]]) {
MGLCameraStyleFunction *function = (MGLCameraStyleFunction<NSString *> *)layer.text;
NSMutableDictionary *stops = function.stops.mutableCopy;
[stops enumerateKeysAndObjectsUsingBlock:^(NSNumber *zoomLevel, MGLConstantStyleValue<NSString *> *stop, BOOL *done) {
if ([stop.rawValue hasPrefix:@"{name"]) {
stops[zoomLevel] = [MGLStyleValue<NSString *> valueWithRawValue:language];
}
}];
function.stops = stops;
layer.text = function;
}
} else {
NSLog(@"%@ is not a symbol style layer", layerName);
}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also replace/remove -bestLanguageForUser?

Copy link
Copy Markdown
Contributor Author

@fabian-guerra fabian-guerra Jul 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@friedbunny We could but in the MBXViewController we would have to explicitly import "MGLVectorSource+MBXAdditions.h" is that ok?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as -bestLanguageForUser matches the implementation of MGLVectorSource.preferredMapboxStreetsLanguage, there should be no need to make that header public to import it. Displaying the language that would be used is a pretty narrow use case that I wouldn’t expect any other application to need.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@friedbunny @1ec5 it does matches the implementation so I don't think we need to get rid of this.

- (NSString *)bestLanguageForUser
{
// https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview
Expand Down
12 changes: 12 additions & 0 deletions platform/ios/ios.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -547,6 +551,8 @@
1F7454941ECD450D00021D39 /* MGLLight_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight_Private.h; sourceTree = "<group>"; };
1F7454A61ED08AB400021D39 /* MGLLightTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLLightTest.mm; path = ../../darwin/test/MGLLightTest.mm; sourceTree = "<group>"; };
1F95931C1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLNSDateAdditionsTests.mm; path = ../../darwin/test/MGLNSDateAdditionsTests.mm; sourceTree = "<group>"; };
1FDD9D6D1F26936400252B09 /* MGLVectorSource+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLVectorSource+MGLAdditions.h"; sourceTree = "<group>"; };
1FDD9D6E1F26936400252B09 /* MGLVectorSource+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLVectorSource+MGLAdditions.m"; sourceTree = "<group>"; };
20DABE861DF78148007AC5FF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Foundation.strings"; sourceTree = "<group>"; };
20DABE881DF78148007AC5FF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
20DABE8A1DF78149007AC5FF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Root.strings"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -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 */,
Expand Down
4 changes: 4 additions & 0 deletions platform/macos/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
Loading