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

Localize expressions more thoroughly #11651

Merged
merged 14 commits into from
Apr 16, 2018
Merged

Localize expressions more thoroughly #11651

merged 14 commits into from
Apr 16, 2018

Conversation

1ec5
Copy link
Contributor

@1ec5 1ec5 commented Apr 10, 2018

Replaced the MGLStyle.localizesLabels property with a -localizeLabelsIntoLocale: method that allows the caller to specify the locale to localize into. This method relies on a new -[NSExpression(MGLAdditions) mgl_expressionLocalizedIntoLocale:] method, for developers who want to vary behavior from layer to layer. (This will simplify mapbox/mapbox-navigation-ios#1076 considerably.)

Due to #11659, this PR also adds some logic to automatically upgrade the legacy {token} syntax to key path expressions for string-typed property getters.

More to come:

spanish

Fixes #10713.

/cc @fabian-guerra @anandthakker @bsudekum @nickidlugash @langsmith

@1ec5 1ec5 added iOS Mapbox Maps SDK for iOS refactor macOS Mapbox Maps SDK for macOS localization Human language support and internationalization labels Apr 10, 2018
@1ec5 1ec5 added this to the ios-v4.0.0 milestone Apr 10, 2018
@1ec5 1ec5 self-assigned this Apr 10, 2018
@1ec5 1ec5 requested a review from fabian-guerra April 10, 2018 20:41
Copy link
Contributor

@fabian-guerra fabian-guerra left a comment

Choose a reason for hiding this comment

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

This PR is looking good.

}
}
if (!locale) {
locale = [NSLocale localeWithLocaleIdentifier:@"mul"];
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice!


case NSFunctionExpressionType: {
NSExpression *operand = self.operand;
NSExpression *localizedOperand = [operand mgl_expressionLocalizedIntoLocale:locale replacingTokens:NO];
Copy link
Contributor

Choose a reason for hiding this comment

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

According to this:

@param replacesTokens YES to localize {token} syntax in constant value subexpressions; NO otherwise.

NSExpression *localizedOperand = [operand mgl_expressionLocalizedIntoLocale:locale replacingTokens:NO]; overrides the value passed in replacesTokens. What would happen if the function expression contains a constant value? should not this be behave accordingly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, this deserves clarification: the replacesTokens parameter is only meant to replace the legacy token syntax, but that’s a subset of the syntax that we want to localize. The only reason that the replacesTokens functionality is supported at all in functions is that three functions in particular may represent style functions, which can contain tokens. Nothing else in the expression language can contain tokens.

Having said all that, #11659 means that tokens are only supported for the MGLSymbolStyleLayer.text and iconImageName properties’ getters, not their setters. So as long as we come up with an expression to set, we need to upgrade all the tokens within the expression to key path expressions. 023624360aa37c4d984b3f49484c8d3552b2b414 does that, but only as a post-localization step on individual function calls.

We may end up needing to unconditionally upgrade the entire expression upfront, before even looking at whether to localize anything. If we do that, then the separate replacesTokens functionality will no longer be necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

f3efae542788ff0978a3ba833a0b52a8e20e5af1 removes the replacesTokens parameter outright. Instead, token upgrading happens as part of the property’s getter, which limits the scope and makes it easy to remove later on.

@@ -96,7 +96,7 @@ + (NSString *)preferredMapboxStreetsLanguage {
mostSpecificLanguage = language;
}
}
return mostSpecificLanguage ?: @"en";
return mostSpecificLanguage;
Copy link
Contributor Author

@1ec5 1ec5 Apr 13, 2018

Choose a reason for hiding this comment

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

This fallback turned out to be redundant: preferredLanguages is never empty, so mostSpecificLanguage is never nil.

+[NSBundle preferredLocalizationsFromArray:forPreferences:] never returns an empty array of languages, because it always falls back to English if none of the available locales matches one of the preferred locales. To maximize the flexibility of the label localization functionality, I’ve been thinking it would be better if we fall back to name rather than name_en. That way, for instance, if a user prefers Hebrew – which is supported by the map SDK’s UI – they can at least get Hebrew labels in Israel rather than English everywhere.

The trick will be to distinguish between the case where +[NSBundle preferredLocalizationsFromArray:forPreferences:] returns English because the locale really does match English (en, en-US, eng-AU, etc.) from the case where it chose English as a last resort. I’m open to doing this as a followup task, potentially in a patch release, unless a solution comes to mind.

/cc @friedbunny @nickidlugash @langsmith

Copy link
Contributor

@fabian-guerra fabian-guerra left a comment

Choose a reason for hiding this comment

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

Tests LGTM 👍

}
}
return self;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Note that {Camera,Source,Composite}Function now track whether or not they were constructed directly from an expression (as opposed to being converted from a stop function):

It's likely not worth the extra complexity, but just noting that if needed, we could try to propagate that metadata to the NSExpression representation for an expression so that you could only do this replacement for those that originally came from stop functions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think fixing #11659 would be more effective than trying to make the workaround for it more robust, but it’s good to know, for future reference, that mbgl is capable of persisting these distinctions.

@1ec5
Copy link
Contributor Author

1ec5 commented Apr 13, 2018

This PR drops the ability to “delocalize” a style, which was previously possible by setting MGLStyle.localizesLabels back to NO. The reason is twofold:

If the developer needs to delocalize a style, they can take either of the following approaches:

  • Call -[MGLStyle localizeLabelsIntoLocale:] again, this time passing in the original locale.
  • Call -[MGLMapView reloadStyle:] (which blows away any runtime styling changes).

Copy link
Contributor

@fabian-guerra fabian-guerra left a comment

Choose a reason for hiding this comment

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

👍🏼

1ec5 added 14 commits April 16, 2018 14:46
The broader feature is new to v4.0.0 as well.
Replaced the MGLStyle.localizesLabels property with a -localizeLabelsIntoLocale: method that allows the caller to specify the locale to localize into. Also exposed a per-expression localization method for developers who want to vary behavior from layer to layer.
Separated token upgrading into a separate process that only happens as part of the MGLSymbolStyleLayer.text and MGLSymbolStyleLayer.iconImageName properties’ getters, so that it’s easy to remove later when mbgl changes obviate this workaround. Removed the replacesTokens parameter to the expression localization methods.
For consistency, replace tokens with key paths in all string-typed style paint and layout properties.
Added tests for replacement of tokens with key paths in expressions. Fixed token replacement for raw strings in stop dictionaries. Avoid sticking a single string inside an mgl_join: call.
Added unit tests of token replacement and localization of expressions. Only NSExpression is responsible for resolving the preferred language now, since NSLocale tends to tack a region code onto the locale identifier and the NSExpression method can be called independently anyways. Added a private variation of +[MGLVectorTileSource preferredMapboxStreetsLanguage] that takes an array of preferred languages. Fixed localization of non-expressions in stop dictionaries.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
iOS Mapbox Maps SDK for iOS localization Human language support and internationalization macOS Mapbox Maps SDK for macOS refactor
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants