diff --git a/Sources/MapboxMaps/Style/Style+Localization.swift b/Sources/MapboxMaps/Style/Style+Localization.swift index 0dd6c0064220..d4ed7d92ba26 100644 --- a/Sources/MapboxMaps/Style/Style+Localization.swift +++ b/Sources/MapboxMaps/Style/Style+Localization.swift @@ -85,7 +85,7 @@ extension Style { if var stringExpression = String(data: try JSONEncoder().encode(symbolLayer.textField), encoding: .utf8), stringExpression != "null" { - stringExpression.updateExpression(replacement: replacement, regex: expressionCoalesce) + stringExpression.updateOnceExpression(replacement: replacement, regex: expressionCoalesce) stringExpression.updateExpression(replacement: replacement, regex: expressionAbbr) // Turn the new json string back into an Expression @@ -113,4 +113,20 @@ extension String { range: range, withTemplate: replacement) } + + /// Updates string once using the first occurrence of a regex + /// - Parameters: + /// - replacement: New string to replace the matched pattern + /// - regex: The regex pattern that will be matched for replacement + internal mutating func updateOnceExpression(replacement: String, regex: NSRegularExpression) { + var range = NSRange(location: 0, length: NSString(string: self).length) + range = regex.rangeOfFirstMatch(in: self, options: [], range: range) + if range.lowerBound == NSNotFound { + return + } + self = regex.stringByReplacingMatches(in: self, + options: [], + range: range, + withTemplate: replacement) + } } diff --git a/Tests/MapboxMapsTests/Style/Style+LocalizationTests.swift b/Tests/MapboxMapsTests/Style/Style+LocalizationTests.swift index 088f1c0c2518..c6a6f233c769 100644 --- a/Tests/MapboxMapsTests/Style/Style+LocalizationTests.swift +++ b/Tests/MapboxMapsTests/Style/Style+LocalizationTests.swift @@ -33,4 +33,52 @@ final class StyleLocalizationTests: MapViewIntegrationTestCase { XCTAssertThrowsError(try style.localizeLabels(into: Locale(identifier: "tlh"))) } + + func testOnlyLocalizesFirstLocalization() throws { + var source = GeoJSONSource() + source.data = .feature(Feature(geometry: Point(CLLocationCoordinate2D(latitude: 0, longitude: 0)))) + try style.addSource(source, id: "a") + + var symbolLayer = SymbolLayer(id: "a") + symbolLayer.source = "a" + symbolLayer.textField = .expression( + Exp(.format) { + Exp(.coalesce) { + Exp(.get) { + "name_en" + } + Exp(.get) { + "name_fr" + } + Exp(.get) { + "name" + } + } + FormatOptions() + } + ) + + try style.addLayer(symbolLayer) + + try style.localizeLabels(into: Locale(identifier: "de")) + + let updatedLayer = try style.layer(withId: "a", type: SymbolLayer.self) + + XCTAssertEqual(updatedLayer.textField, .expression( + Exp(.format) { + Exp(.coalesce) { + Exp(.get) { + "name_de" + } + Exp(.get) { + "name_fr" + } + Exp(.get) { + "name" + } + } + FormatOptions() + } + )) + } }