Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Commit

Permalink
[macos] Refactored label localization
Browse files Browse the repository at this point in the history
Localize only Mapbox Streets v6–v7, and do so more systematically than before.
  • Loading branch information
1ec5 committed Dec 21, 2016
1 parent f390b59 commit e7fa769
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 32 deletions.
15 changes: 15 additions & 0 deletions platform/macos/app/MGLVectorSource+MBXAdditions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#import <Mapbox/Mapbox.h>

NS_ASSUME_NONNULL_BEGIN

@interface MGLVectorSource (MBXAdditions)

+ (nullable NSString *)preferredMapboxStreetsLanguage;

- (NS_DICTIONARY_OF(NSString *, NSString *) *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage;

@property (nonatomic, readonly, getter=isMapboxStreets) BOOL mapboxStreets;

@end

NS_ASSUME_NONNULL_END
49 changes: 49 additions & 0 deletions platform/macos/app/MGLVectorSource+MBXAdditions.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#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, ^{
mapboxStreetsLanguages = [NSSet setWithObjects:@"en", @"es", @"fr", @"de", @"ru", @"zh", nil];
});
return mapboxStreetsLanguages;
}

+ (nullable NSString *)preferredMapboxStreetsLanguage {
for (NSString *language in [NSLocale preferredLanguages]) {
NSString *languageCode = [[NSLocale localeWithLocaleIdentifier:language] objectForKey:NSLocaleLanguageCode];
if ([[MGLVectorSource mapboxStreetsLanguages] containsObject:languageCode]) {
return languageCode;
}
}
return nil;
}

- (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
67 changes: 35 additions & 32 deletions platform/macos/app/MapDocument.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#import "LimeGreenStyleLayer.h"
#import "DroppedPinAnnotation.h"

#import "MGLVectorSource+MBXAdditions.h"

#import <Mapbox/Mapbox.h>

static NSString * const MGLDroppedPinAnnotationImageIdentifier = @"dropped";
Expand Down Expand Up @@ -331,58 +333,57 @@ - (void)deleteStyleLayersAtArrangedObjectIndexes:(NSIndexSet *)indices {

- (IBAction)setLabelLanguage:(NSMenuItem *)sender {
_isLocalizingLabels = sender.tag;
[self updateLabels];
[self reload:sender];
}

- (void)updateLabels {
NSString *preferredLanguageCode = self.preferredLanguageCode;
NSString *preferredNameToken = _isLocalizingLabels ? [NSString stringWithFormat:@"{name_%@}", preferredLanguageCode] : @"{name}";
NSRegularExpression *nameTokenExpression = [NSRegularExpression regularExpressionWithPattern:@"\\{name(?:_\\w{2})?\\}" options:0 error:NULL];

for (MGLSymbolStyleLayer *layer in self.mapView.style.layers) {
MGLStyle *style = self.mapView.style;
NSString *preferredLanguage = _isLocalizingLabels ? ([MGLVectorSource preferredMapboxStreetsLanguage] ?: @"en") : nil;
NSMutableDictionary *localizedKeysByKeyBySourceIdentifier = [NSMutableDictionary dictionary];
for (MGLSymbolStyleLayer *layer in style.layers) {
if (![layer isKindOfClass:[MGLSymbolStyleLayer class]]) {
continue;
}

MGLVectorSource *source = (MGLVectorSource *)[style 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.textField isKindOfClass:[MGLStyleConstantValue class]]) {
NSString *textField = [(MGLStyleConstantValue<NSString *> *)layer.textField rawValue];
textField = [nameTokenExpression stringByReplacingMatchesInString:textField
options:0
range:NSMakeRange(0, textField.length)
withTemplate:preferredNameToken];
layer.textField = [MGLStyleValue<NSString *> valueWithRawValue:textField];
layer.textField = [MGLStyleValue<NSString *> valueWithRawValue:stringByLocalizingString(textField)];
} else if ([layer.textField isKindOfClass:[MGLStyleFunction class]]) {
MGLStyleFunction *function = (MGLStyleFunction<NSString *> *)layer.textField;
NSMutableDictionary *stops = function.stops.mutableCopy;
[stops enumerateKeysAndObjectsUsingBlock:^(NSNumber *zoomLevel, MGLStyleConstantValue<NSString *> *stop, BOOL *done) {
NSString *textField = stop.rawValue;
textField = [nameTokenExpression stringByReplacingMatchesInString:textField
options:0
range:NSMakeRange(0, textField.length)
withTemplate:preferredNameToken];
stops[zoomLevel] = [MGLStyleValue<NSString *> valueWithRawValue:textField];
stops[zoomLevel] = [MGLStyleValue<NSString *> valueWithRawValue:stringByLocalizingString(textField)];
}];
function.stops = stops;
layer.textField = function;
}
}
}

- (NSString *)preferredLanguageCode {
// Languages supported by Mapbox Streets v10.
NSSet *supportedLanguages = [NSSet setWithObjects:@"en", @"es", @"fr", @"de", @"ru", @"zh", nil];
NSArray *preferredLanguages = [NSLocale preferredLanguages];

for (NSString *language in preferredLanguages) {
NSString *languageCode = [[NSLocale localeWithLocaleIdentifier:language] objectForKey:NSLocaleLanguageCode];
if ([supportedLanguages containsObject:languageCode]) {
return languageCode;
}
}

return @"en";
}

- (void)applyPendingState {
if (_inheritedStyleURL) {
self.mapView.styleURL = _inheritedStyleURL;
Expand Down Expand Up @@ -836,7 +837,9 @@ - (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
menuItem.state = menuItem.tag == _isLocalizingLabels ? NSOnState: NSOffState;
if (menuItem.tag) {
NSLocale *locale = [NSLocale localeWithLocaleIdentifier:[NSBundle mainBundle].developmentLocalization];
menuItem.title = [locale displayNameForKey:NSLocaleIdentifier value:self.preferredLanguageCode];
NSString *preferredLanguage = [MGLVectorSource preferredMapboxStreetsLanguage];
menuItem.enabled = !!preferredLanguage;
menuItem.title = [locale displayNameForKey:NSLocaleIdentifier value:preferredLanguage ?: @"Preferred Language"];
}
return YES;
}
Expand Down
6 changes: 6 additions & 0 deletions platform/macos/macos.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@
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 */; };
Expand Down Expand Up @@ -475,6 +476,8 @@
DAEDC4361D606291000224FF /* MGLAttributionButtonTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAttributionButtonTests.m; sourceTree = "<group>"; };
DAF0D80D1DFE0E5D00B28378 /* MGLPointCollection_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLPointCollection_Private.h; sourceTree = "<group>"; };
DAF0D8151DFE6B1800B28378 /* MGLAttributionInfo_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAttributionInfo_Private.h; sourceTree = "<group>"; };
DAF0D81A1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLVectorSource+MBXAdditions.h"; sourceTree = "<group>"; };
DAF0D81B1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLVectorSource+MBXAdditions.m"; sourceTree = "<group>"; };
DD0902AF1DB1AC6400C5BDCE /* MGLNetworkConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLNetworkConfiguration.m; sourceTree = "<group>"; };
DD0902B01DB1AC6400C5BDCE /* MGLNetworkConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLNetworkConfiguration.h; sourceTree = "<group>"; };
DD58A4C71D822C6200E1F038 /* MGLExpressionTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLExpressionTests.mm; path = ../../darwin/test/MGLExpressionTests.mm; sourceTree = "<group>"; };
Expand Down Expand Up @@ -623,6 +626,8 @@
DA839E9B1CC2E3400062CAFB /* MapDocument.h */,
DA839E9C1CC2E3400062CAFB /* MapDocument.m */,
DA839E9E1CC2E3400062CAFB /* MapDocument.xib */,
DAF0D81A1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.h */,
DAF0D81B1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.m */,
DAE6C2E91CC3050F00DB3429 /* OfflinePackNameValueTransformer.h */,
DAE6C2EA1CC3050F00DB3429 /* OfflinePackNameValueTransformer.m */,
DAA48EFB1D6A4731006A7E36 /* StyleLayerIconTransformer.h */,
Expand Down Expand Up @@ -1219,6 +1224,7 @@
DAE6C2F11CC3050F00DB3429 /* TimeIntervalTransformer.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;
Expand Down

0 comments on commit e7fa769

Please sign in to comment.