Send platformResolvedLocale from iOS embedder (#18519)

diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm
index ad00946..59eae77 100644
--- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm
+++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm
@@ -759,27 +759,46 @@
 #pragma mark - Locale updates
 
 - (void)onLocaleUpdated:(NSNotification*)notification {
-  NSArray<NSString*>* preferredLocales = [NSLocale preferredLanguages];
-  NSMutableArray<NSString*>* data = [[NSMutableArray new] autorelease];
+  // [NSLocale currentLocale] provides an iOS resolved locale if the
+  // supported locales are exposed to the iOS embedder. Here, we get
+  // currentLocale and pass it to dart:ui
+  NSMutableArray<NSString*>* localeData = [[NSMutableArray new] autorelease];
+  NSLocale* platformResolvedLocale = [NSLocale currentLocale];
+  NSString* languageCode = [platformResolvedLocale objectForKey:NSLocaleLanguageCode];
+  NSString* countryCode = [platformResolvedLocale objectForKey:NSLocaleCountryCode];
+  NSString* scriptCode = [platformResolvedLocale objectForKey:NSLocaleScriptCode];
+  NSString* variantCode = [platformResolvedLocale objectForKey:NSLocaleVariantCode];
+  if (languageCode) {
+    [localeData addObject:languageCode];
+    [localeData addObject:(countryCode ? countryCode : @"")];
+    [localeData addObject:(scriptCode ? scriptCode : @"")];
+    [localeData addObject:(variantCode ? variantCode : @"")];
+  }
+  if (localeData.count != 0) {
+    [self.localizationChannel invokeMethod:@"setPlatformResolvedLocale" arguments:localeData];
+  }
 
+  // Get and pass the user's preferred locale list to dart:ui
+  localeData = [[NSMutableArray new] autorelease];
+  NSArray<NSString*>* preferredLocales = [NSLocale preferredLanguages];
   for (NSString* localeID in preferredLocales) {
-    NSLocale* currentLocale = [[[NSLocale alloc] initWithLocaleIdentifier:localeID] autorelease];
-    NSString* languageCode = [currentLocale objectForKey:NSLocaleLanguageCode];
-    NSString* countryCode = [currentLocale objectForKey:NSLocaleCountryCode];
-    NSString* scriptCode = [currentLocale objectForKey:NSLocaleScriptCode];
-    NSString* variantCode = [currentLocale objectForKey:NSLocaleVariantCode];
+    NSLocale* locale = [[[NSLocale alloc] initWithLocaleIdentifier:localeID] autorelease];
+    NSString* languageCode = [locale objectForKey:NSLocaleLanguageCode];
+    NSString* countryCode = [locale objectForKey:NSLocaleCountryCode];
+    NSString* scriptCode = [locale objectForKey:NSLocaleScriptCode];
+    NSString* variantCode = [locale objectForKey:NSLocaleVariantCode];
     if (!languageCode) {
       continue;
     }
-    [data addObject:languageCode];
-    [data addObject:(countryCode ? countryCode : @"")];
-    [data addObject:(scriptCode ? scriptCode : @"")];
-    [data addObject:(variantCode ? variantCode : @"")];
+    [localeData addObject:languageCode];
+    [localeData addObject:(countryCode ? countryCode : @"")];
+    [localeData addObject:(scriptCode ? scriptCode : @"")];
+    [localeData addObject:(variantCode ? variantCode : @"")];
   }
-  if (data.count == 0) {
+  if (localeData.count == 0) {
     return;
   }
-  [self.localizationChannel invokeMethod:@"setLocale" arguments:data];
+  [self.localizationChannel invokeMethod:@"setLocale" arguments:localeData];
 }
 
 @end
diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/LocalizationInitializationTest.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/LocalizationInitializationTest.m
index 974022e..3648eb9 100644
--- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/LocalizationInitializationTest.m
+++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/LocalizationInitializationTest.m
@@ -25,11 +25,17 @@
 - (void)testNoLocalePrepend {
   NSTimeInterval timeout = 10.0;
 
+  // The locales received by dart:ui are exposed onBeginFrame via semantics label.
+  // There should only be one locale, since the default iOS app only has en_US as
+  // the locale. The list should consist of just the en locale.
   XCUIElement* textInputSemanticsObject =
       [self.application.textFields matchingIdentifier:@"[en]"].element;
+  XCTAssertTrue([textInputSemanticsObject waitForExistenceWithTimeout:timeout]);
 
-  // The locales recieved by dart:ui are exposed onBeginFrame via semantics label.
-  // There should only be one locale, as we have removed the locale prepend on iOS.
+  [textInputSemanticsObject tap];
+
+  // [NSLocale currentLocale] always includes a country code.
+  textInputSemanticsObject = [self.application.textFields matchingIdentifier:@"en_US"].element;
   XCTAssertTrue([textInputSemanticsObject waitForExistenceWithTimeout:timeout]);
 }
 
diff --git a/testing/scenario_app/lib/src/locale_initialization.dart b/testing/scenario_app/lib/src/locale_initialization.dart
index f20985e..e19bc41 100644
--- a/testing/scenario_app/lib/src/locale_initialization.dart
+++ b/testing/scenario_app/lib/src/locale_initialization.dart
@@ -16,6 +16,9 @@
       : assert(window != null),
         super(window);
 
+  int _tapCount = 0;
+
+  /// Start off by sending the supported locales list via semantics.
   @override
   void onBeginFrame(Duration duration) {
     // Doesn't matter what we draw. Just paint white.
@@ -66,4 +69,46 @@
       )).build()
     );
   }
+
+  /// Handle taps.
+  ///
+  /// Send changing information via semantics on each successive tap.
+  @override
+  void onPointerDataPacket(PointerDataPacket packet) {
+    String label;
+    switch(_tapCount) {
+      case 1: {
+        label = window.platformResolvedLocale.toString();
+        break;
+      }
+      // Expand for other test cases.
+    }
+
+    window.updateSemantics((SemanticsUpdateBuilder()
+      ..updateNode(
+        id: 0,
+        // SemanticsFlag.isTextField.
+        flags: 16,
+        // SemanticsAction.tap.
+        actions: 1,
+        rect: const Rect.fromLTRB(0.0, 0.0, 414.0, 48.0),
+        label: label,
+        textDirection: TextDirection.ltr,
+        textSelectionBase: 0,
+        textSelectionExtent: 0,
+        platformViewId: -1,
+        maxValueLength: -1,
+        currentValueLength: 0,
+        scrollChildren: 0,
+        scrollIndex: 0,
+        transform: Matrix4.identity().storage,
+        elevation: 0.0,
+        thickness: 0.0,
+        childrenInTraversalOrder: Int32List(0),
+        childrenInHitTestOrder: Int32List(0),
+        additionalActions: Int32List(0),
+      )).build()
+    );
+    _tapCount++;
+  }
 }