From c6055fd18ccf505d673321f92288c82965563485 Mon Sep 17 00:00:00 2001 From: Stefan Schaller Date: Mon, 14 Aug 2023 14:29:50 +0200 Subject: [PATCH] 250-change-language-fixes (#275) Co-authored-by: Julian Bissekkou --- CHANGELOG.md | 4 +- android/build.gradle | 1 - .../mapbox/mapboxgl/MapboxMapController.java | 12 +-- .../com/mapbox/mapboxgl/setMapLanguage.kt | 31 ++++++++ example/ios/Podfile | 1 - example/ios/Podfile.lock | 10 +-- example/lib/localized_map.dart | 78 +++++++++++++++++++ example/lib/main.dart | 2 + ios/Classes/Constants.swift | 1 - ios/Classes/Convert.swift | 1 - ios/Classes/LayerPropertyConverter.swift | 1 - ios/Classes/MGLMapView+setLanguage.swift | 55 +++++++++++++ ios/Classes/MapboxMapController.swift | 20 +++-- ios/maplibre_gl.podspec | 1 - maplibre_gl_platform_interface/pubspec.yaml | 2 +- .../lib/src/mapbox_web_gl_platform.dart | 32 ++++++-- 16 files changed, 214 insertions(+), 38 deletions(-) create mode 100644 android/src/main/java/com/mapbox/mapboxgl/setMapLanguage.kt create mode 100644 example/lib/localized_map.dart create mode 100644 ios/Classes/MGLMapView+setLanguage.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 8dfb0a4ac..4a5d1ee13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,9 @@ MaplibreMap( ... ) ``` - * The old api `registerWith` was removed from the MapboxMapsPlugin.java, since there is no need for that. -* The `minSdkVersion` was bumped to at least 21 now, since the native android sdk constraint expect that. +* The `minSdkVersion` was bumped to at least 21 now, since the native android sdk constraint expect that. +* Changed the minimum Dart version from sdk: `2.12.0` to `2.14.0` in `maplibre_gl_platform_interface/pubspec.yaml`. ## 0.16.0, Jun 28, 2022 * cherry-picked all commits from upstream up to [https://github.com/flutter-mapbox-gl/maps/commit/3496907955cd4b442e4eb905d67e8d46692174f1), including up to release 0.16.0 from upstream diff --git a/android/build.gradle b/android/build.gradle index 6e8c7b5ba..5acdf869a 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -42,7 +42,6 @@ android { dependencies { implementation 'org.maplibre.gl:android-sdk:10.2.0' implementation 'org.maplibre.gl:android-plugin-annotation-v9:2.0.0' - implementation 'org.maplibre.gl:android-plugin-localization-v9:2.0.0' implementation 'org.maplibre.gl:android-plugin-offline-v9:2.0.0' implementation 'com.squareup.okhttp3:okhttp:4.10.0' } diff --git a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java index 2c70b7d25..66fdabe8a 100644 --- a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java +++ b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java @@ -57,7 +57,6 @@ import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; import com.mapbox.mapboxsdk.maps.Style; import com.mapbox.mapboxsdk.offline.OfflineManager; -import com.mapbox.mapboxsdk.plugins.localization.LocalizationPlugin; import com.mapbox.mapboxsdk.style.expressions.Expression; import com.mapbox.mapboxsdk.style.layers.CircleLayer; import com.mapbox.mapboxsdk.style.layers.FillExtrusionLayer; @@ -76,6 +75,7 @@ import com.mapbox.mapboxsdk.style.sources.ImageSource; import com.mapbox.mapboxsdk.style.sources.Source; import com.mapbox.mapboxsdk.style.sources.VectorSource; +import com.mapbox.mapboxsdk.style.types.Formatted; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.MethodCall; @@ -89,6 +89,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; @@ -131,7 +132,6 @@ final class MapboxMapController private MethodChannel.Result mapReadyResult; private LocationComponent locationComponent = null; private LocationEngineCallback locationEngineCallback = null; - private LocalizationPlugin localizationPlugin; private Style style; private Feature draggedFeature; private AndroidGesturesManager androidGesturesManager; @@ -163,7 +163,6 @@ public void onStyleLoaded(@NonNull Style style) { mapboxMap.addOnMapClickListener(MapboxMapController.this); mapboxMap.addOnMapLongClickListener(MapboxMapController.this); - localizationPlugin = new LocalizationPlugin(mapView, mapboxMap, style); methodChannel.invokeMethod("map#onStyleLoaded", null); } @@ -644,7 +643,9 @@ public void onMethodCall(MethodCall call, MethodChannel.Result result) { case "map#matchMapLanguageWithDeviceDefault": { try { - localizationPlugin.matchMapLanguageWithDeviceDefault(); + final Locale deviceLocale = Locale.getDefault(); + MapboxMapUtils.setMapLanguage(mapboxMap, deviceLocale.getLanguage()); + result.success(null); } catch (RuntimeException exception) { Log.d(TAG, exception.toString()); @@ -673,7 +674,8 @@ public void onMethodCall(MethodCall call, MethodChannel.Result result) { { final String language = call.argument("language"); try { - localizationPlugin.setMapLanguage(language); + MapboxMapUtils.setMapLanguage(mapboxMap, language); + result.success(null); } catch (RuntimeException exception) { Log.d(TAG, exception.toString()); diff --git a/android/src/main/java/com/mapbox/mapboxgl/setMapLanguage.kt b/android/src/main/java/com/mapbox/mapboxgl/setMapLanguage.kt new file mode 100644 index 000000000..870967478 --- /dev/null +++ b/android/src/main/java/com/mapbox/mapboxgl/setMapLanguage.kt @@ -0,0 +1,31 @@ +@file:JvmName("MapboxMapUtils") + +package com.mapbox.mapboxgl + +import com.mapbox.mapboxsdk.maps.MapboxMap +import com.mapbox.mapboxsdk.style.expressions.Expression +import com.mapbox.mapboxsdk.style.layers.PropertyFactory +import com.mapbox.mapboxsdk.style.layers.SymbolLayer + +fun MapboxMap.setMapLanguage(language: String) { + val layers = this.style?.layers ?: emptyList() + + val languageRegex = Regex("(name:[a-z]+)") + + val symbolLayers = layers.filterIsInstance() + + for (layer in symbolLayers) { + // continue when there is no current expression + val expression = layer.textField.expression ?: continue + + // We could skip the current iteration, whenever there is not current language. + if (!expression.toString().contains(languageRegex)) { + continue + } + + val properties = + "[\"coalesce\", [\"get\",\"name:$language\"],[\"get\",\"name:latin\"],[\"get\",\"name\"]]" + + layer.setProperties(PropertyFactory.textField(Expression.raw(properties))) + } +} diff --git a/example/ios/Podfile b/example/ios/Podfile index ece43299c..62b8d6813 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -5,7 +5,6 @@ source 'https://github.com/m0nac0/flutter-maplibre-podspecs.git' platform :ios, '11.0' pod 'MapLibre' -pod 'MapLibreAnnotationExtension' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 225d685bd..f6a6cba77 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -8,9 +8,6 @@ PODS: - maplibre_gl (0.0.1): - Flutter - MapLibre (~> 5.12.2) - - MapLibreAnnotationExtension (~> 0.0.1-beta.2) - - MapLibreAnnotationExtension (0.0.1-beta.2): - - MapLibre (~> 5.12.0) - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS @@ -21,13 +18,11 @@ DEPENDENCIES: - location (from `.symlinks/plugins/location/ios`) - MapLibre - maplibre_gl (from `.symlinks/plugins/maplibre_gl/ios`) - - MapLibreAnnotationExtension - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) SPEC REPOS: https://github.com/m0nac0/flutter-maplibre-podspecs.git: - MapLibre - - MapLibreAnnotationExtension EXTERNAL SOURCES: device_info_plus: @@ -46,10 +41,9 @@ SPEC CHECKSUMS: Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 location: 3a2eed4dd2fab25e7b7baf2a9efefe82b512d740 MapLibre: 5aa820115d91293a4d09e681e7470f88bd34b672 - maplibre_gl: 2a0ce99f40b3a133b0e8b40b5ef37e0562165399 - MapLibreAnnotationExtension: bc7f1041206218d7d24ba0908e3145cd54b86664 + maplibre_gl: f48aee1349b694fc372a509ec7bae88d9f9527c4 path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 -PODFILE CHECKSUM: a908f6081c2697d922d17bcdb852cad068049f2e +PODFILE CHECKSUM: 583ff63e051d4a697972e0fb94f90560347dabb0 COCOAPODS: 1.12.1 diff --git a/example/lib/localized_map.dart b/example/lib/localized_map.dart new file mode 100644 index 000000000..83457157a --- /dev/null +++ b/example/lib/localized_map.dart @@ -0,0 +1,78 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:maplibre_gl/mapbox_gl.dart'; + +import 'page.dart'; + +class LocalizedMapPage extends ExamplePage { + const LocalizedMapPage({super.key}) + : super(const Icon(Icons.map), 'Localized screen map'); + + @override + Widget build(BuildContext context) { + return const LocalizedMap(); + } +} + +class LocalizedMap extends StatefulWidget { + const LocalizedMap({super.key}); + + @override + State createState() => LocalizedMapState(); +} + +class LocalizedMapState extends State { + final _mapReadyCompleter = Completer(); + + var _mapLanguage = "en"; + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Column( + children: [ + DropdownButton( + value: _mapLanguage, + icon: const Icon(Icons.arrow_drop_down), + elevation: 16, + onChanged: (value) { + if (value == null) return; + + setState(() => _mapLanguage = value); + _setMapLanguage(); + }, + items: ["en", "de", "es", "pl"] + .map>((String value) { + return DropdownMenuItem( + value: value, + child: Text(value), + ); + }).toList(), + ), + Expanded( + child: MaplibreMap( + onMapCreated: _onMapCreated, + initialCameraPosition: + const CameraPosition(target: LatLng(0.0, 0.0)), + onStyleLoadedCallback: _onStyleLoadedCallback, + ), + ), + ], + ), + ); + } + + void _onMapCreated(MaplibreMapController controller) { + _mapReadyCompleter.complete(controller); + } + + void _onStyleLoadedCallback() { + _setMapLanguage(); + } + + Future _setMapLanguage() async { + final controller = await _mapReadyCompleter.future; + controller.setMapLanguage(_mapLanguage); + } +} diff --git a/example/lib/main.dart b/example/lib/main.dart index c67afa62f..f3bc96f30 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -10,6 +10,7 @@ import 'package:location/location.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:maplibre_gl_example/get_map_informations.dart'; import 'package:maplibre_gl_example/given_bounds.dart'; +import 'package:maplibre_gl_example/localized_map.dart'; import 'package:maplibre_gl_example/no_location_permission_page.dart'; import 'animate_camera.dart'; @@ -37,6 +38,7 @@ import 'package:maplibre_gl/mapbox_gl.dart'; final List _allPages = [ const MapUiPage(), const FullMapPage(), + const LocalizedMapPage(), const AnimateCameraPage(), const MoveCameraPage(), const PlaceSymbolPage(), diff --git a/ios/Classes/Constants.swift b/ios/Classes/Constants.swift index a19e35082..224e7f999 100644 --- a/ios/Classes/Constants.swift +++ b/ios/Classes/Constants.swift @@ -1,5 +1,4 @@ import Mapbox -import MapLibreAnnotationExtension /* * The mapping is based on the values defined here: diff --git a/ios/Classes/Convert.swift b/ios/Classes/Convert.swift index c8d05eef7..631fa103c 100644 --- a/ios/Classes/Convert.swift +++ b/ios/Classes/Convert.swift @@ -1,5 +1,4 @@ import Mapbox -import MapLibreAnnotationExtension class Convert { class func interpretMapboxMapOptions(options: Any?, delegate: MapboxMapOptionsSink) { diff --git a/ios/Classes/LayerPropertyConverter.swift b/ios/Classes/LayerPropertyConverter.swift index 00c451ea4..90571f9be 100644 --- a/ios/Classes/LayerPropertyConverter.swift +++ b/ios/Classes/LayerPropertyConverter.swift @@ -2,7 +2,6 @@ // ./scripts/lib/generate.dart import Mapbox -import MapLibreAnnotationExtension class LayerPropertyConverter { class func addSymbolProperties(symbolLayer: MGLSymbolStyleLayer, properties: [String: String]) { diff --git a/ios/Classes/MGLMapView+setLanguage.swift b/ios/Classes/MGLMapView+setLanguage.swift new file mode 100644 index 000000000..c3cf6a41e --- /dev/null +++ b/ios/Classes/MGLMapView+setLanguage.swift @@ -0,0 +1,55 @@ +// +// MLNMapView+setLanguage.swift +// maplibre_gl +// +// Created by Julian Bissekkou on 09.08.23. +// + +import Foundation +import Mapbox + +extension MGLMapView { + func setMapLanguage(_ language: String) { + guard let style = style else { return } + + let layers = style.layers + + for layer in layers { + if let symbolLayer = layer as? MGLSymbolStyleLayer { + if symbolLayer.text == nil { + continue + } + + // We could skip the current iteration, whenever there is not current language. + if !symbolLayer.text.description.containsLanguage() { + continue + } + + let properties = ["text-field": "[\"coalesce\",[\"get\",\"name:\(language)\"],[\"get\",\"name:latin\"],[\"get\",\"name\"]]"] + + LayerPropertyConverter.addSymbolProperties( + symbolLayer: symbolLayer, + properties: properties + ) + } + } + } +} + + +private extension String { + func containsLanguage() -> Bool { + do { + let regex = try NSRegularExpression(pattern: "(name:[a-z]+)") + let range = NSRange(location: 0, length: self.utf16.count) + + if let _ = regex.firstMatch(in: self, options: [], range: range) { + return true + } else { + return false + } + } catch { + return false + } + } +} diff --git a/ios/Classes/MapboxMapController.swift b/ios/Classes/MapboxMapController.swift index 7b68ac8ca..0f3816a68 100644 --- a/ios/Classes/MapboxMapController.swift +++ b/ios/Classes/MapboxMapController.swift @@ -1,6 +1,5 @@ import Flutter import Mapbox -import MapLibreAnnotationExtension class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, MapboxMapOptionsSink, UIGestureRecognizerDelegate @@ -106,11 +105,6 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma mapView.addGestureRecognizer(pan) } } - func removeAllForController(controller: MGLAnnotationController, ids: [String]){ - let idSet = Set(ids) - let annotations = controller.styleAnnotations() - controller.removeStyleAnnotations(annotations.filter { idSet.contains($0.identifier) }) - } func gestureRecognizer( _: UIGestureRecognizer, @@ -161,9 +155,10 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma } result(nil) case "map#matchMapLanguageWithDeviceDefault": - if let style = mapView.style { - style.localizeLabels(into: nil) + if let langStr = Locale.current.languageCode { + setMapLanguage(language: langStr) } + result(nil) case "map#updateContentInsets": guard let arguments = methodCall.arguments as? [String: Any] else { return } @@ -195,9 +190,8 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma } case "map#setMapLanguage": guard let arguments = methodCall.arguments as? [String: Any] else { return } - if let localIdentifier = arguments["language"] as? String, let style = mapView.style { - let locale = Locale(identifier: localIdentifier) - style.localizeLabels(into: locale) + if let localIdentifier = arguments["language"] as? String { + setMapLanguage(language: localIdentifier) } result(nil) case "map#queryRenderedFeatures": @@ -841,6 +835,10 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma private func getCamera() -> MGLMapCamera? { return trackCameraPosition ? mapView.camera : nil } + + private func setMapLanguage(language: String) { + self.mapView.setMapLanguage(language) + } /* * Scan layers from top to bottom and return the first matching feature diff --git a/ios/maplibre_gl.podspec b/ios/maplibre_gl.podspec index fe0ed3de6..66bb6f93d 100644 --- a/ios/maplibre_gl.podspec +++ b/ios/maplibre_gl.podspec @@ -15,7 +15,6 @@ A new Flutter plugin. s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' s.dependency 'Flutter' - s.dependency 'MapLibreAnnotationExtension', '~> 0.0.1-beta.2' s.dependency 'MapLibre', '~> 5.12.2' s.swift_version = '4.2' s.ios.deployment_target = '9.0' diff --git a/maplibre_gl_platform_interface/pubspec.yaml b/maplibre_gl_platform_interface/pubspec.yaml index 6385a1ca5..e44997fd8 100644 --- a/maplibre_gl_platform_interface/pubspec.yaml +++ b/maplibre_gl_platform_interface/pubspec.yaml @@ -9,5 +9,5 @@ dependencies: meta: ^1.0.5 environment: - sdk: '>=2.12.0 <3.0.0' + sdk: '>=2.14.0 <3.0.0' flutter: '>=2.10.0' diff --git a/maplibre_gl_web/lib/src/mapbox_web_gl_platform.dart b/maplibre_gl_web/lib/src/mapbox_web_gl_platform.dart index cac10d9f6..c6703a693 100644 --- a/maplibre_gl_web/lib/src/mapbox_web_gl_platform.dart +++ b/maplibre_gl_web/lib/src/mapbox_web_gl_platform.dart @@ -236,11 +236,33 @@ class MaplibreMapController extends MapLibreGlPlatform @override Future setMapLanguage(String language) async { - _map.setLayoutProperty( - 'country-label', - 'text-field', - ['get', 'name_' + language], - ); + final List layers = _map.getStyle()?.layers ?? []; + + final languageRegex = RegExp("(name:[a-z]+)"); + + final symbolLayers = layers.where((layer) => layer.type == "symbol"); + + for (final layer in symbolLayers) { + final dynamic properties = _map.getLayoutProperty(layer.id, 'text-field'); + + if (properties == null) { + continue; + } + + // We could skip the current iteration, whenever there is not current language. + if (!languageRegex.hasMatch(properties.toString())) { + continue; + } + + final newProperties = [ + "coalesce", + ["get", "name:$language"], + ["get", "name:latin"], + ["get", "name"], + ]; + + _map.setLayoutProperty(layer.id, 'text-field', newProperties); + } } @override