diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fe44ecf20..3e84e2f71d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Fixes +- Fixes setting the correct locale to contexts with navigatorKey ([#1724](https://github.com/getsentry/sentry-dart/pull/1724)) + - If you have a selected locale in e.g MaterialApp, this fix will retrieve the correct locale for the event context. - Flutter renderer information was removed on dart:io platforms since it didn't add the correct value ([#1723](https://github.com/getsentry/sentry-dart/pull/1723)) - Unsupported types with Expando ([#1690](https://github.com/getsentry/sentry-dart/pull/1690)) diff --git a/flutter/lib/src/event_processor/flutter_enricher_event_processor.dart b/flutter/lib/src/event_processor/flutter_enricher_event_processor.dart index f8003248ec..de6b6bab9f 100644 --- a/flutter/lib/src/event_processor/flutter_enricher_event_processor.dart +++ b/flutter/lib/src/event_processor/flutter_enricher_event_processor.dart @@ -107,7 +107,9 @@ class FlutterEnricherEventProcessor implements EventProcessor { } SentryCulture _getCulture(SentryCulture? culture) { - final languageTag = _window?.locale.toLanguageTag(); + final windowLanguageTag = _window?.locale.toLanguageTag(); + final screenLocale = _retrieveWidgetLocale(_options.navigatorKey); + final languageTag = screenLocale?.toLanguageTag() ?? windowLanguageTag; // Future enhancement: // _window?.locales @@ -256,4 +258,12 @@ class FlutterEnricherEventProcessor implements EventProcessor { return app; } } + + Locale? _retrieveWidgetLocale(GlobalKey? navigatorKey) { + final BuildContext? context = navigatorKey?.currentContext; + if (context != null) { + return Localizations.maybeLocaleOf(context); + } + return null; + } } diff --git a/flutter/lib/src/sentry_flutter_options.dart b/flutter/lib/src/sentry_flutter_options.dart index 7a82f3b53f..1a71a0d37e 100644 --- a/flutter/lib/src/sentry_flutter_options.dart +++ b/flutter/lib/src/sentry_flutter_options.dart @@ -286,4 +286,7 @@ class SentryFlutterOptions extends SentryOptions { // ignore: invalid_use_of_internal_member super.profilesSampleRate = value; } + + /// The [navigatorKey] is used to add information of the currently used locale to the contexts. + GlobalKey? navigatorKey; } diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index ded2470f09..15b69ec73a 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -29,6 +29,8 @@ dev_dependencies: flutter_lints: ^2.0.0 collection: ^1.16.0 remove_from_coverage: ^2.0.0 + flutter_localizations: + sdk: flutter flutter: plugin: diff --git a/flutter/test/event_processor/flutter_enricher_event_processor_test.dart b/flutter/test/event_processor/flutter_enricher_event_processor_test.dart index 6b95793ad7..5fbc1d7034 100644 --- a/flutter/test/event_processor/flutter_enricher_event_processor_test.dart +++ b/flutter/test/event_processor/flutter_enricher_event_processor_test.dart @@ -1,9 +1,11 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; + // backcompatibility for Flutter < 3.3 // ignore: unnecessary_import import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; @@ -104,6 +106,38 @@ void main() { expect(culture?.timezone, isNotNull); }); + testWidgets( + 'GIVEN MaterialApp WHEN setting locale and sentryNavigatorKey THEN enrich event culture with selected locale', + (WidgetTester tester) async { + GlobalKey navigatorKey = GlobalKey(); + + await tester.pumpWidget(MaterialApp( + navigatorKey: navigatorKey, + home: Material(), + localizationsDelegates: const [ + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: const [ + Locale('en', 'US'), + Locale('de', 'DE'), + ], + locale: const Locale('de', 'DE'), + )); + + final enricher = fixture.getSut( + binding: () => tester.binding, + optionsBuilder: (options) { + options.navigatorKey = navigatorKey; + return options; + }, + ); + + final event = await enricher.apply(SentryEvent()); + + expect(event?.contexts.culture?.locale, 'de-DE'); + }); + testWidgets('app context in foreground', (WidgetTester tester) async { final enricher = fixture.getSut( binding: () => tester.binding, @@ -380,16 +414,19 @@ class Fixture { PlatformChecker? checker, bool hasNativeIntegration = false, bool reportPackages = true, + SentryFlutterOptions Function(SentryFlutterOptions)? optionsBuilder, }) { final platformChecker = checker ?? MockPlatformChecker( hasNativeIntegration: hasNativeIntegration, ); + final options = SentryFlutterOptions( dsn: fakeDsn, checker: platformChecker, )..reportPackages = reportPackages; - return FlutterEnricherEventProcessor(options); + final customizedOptions = optionsBuilder?.call(options) ?? options; + return FlutterEnricherEventProcessor(customizedOptions); } PageRoute route(RouteSettings? settings) => PageRouteBuilder(