[in_app_purchase] Expose the NSLocale object (#3897)

* Expose remaining "simple" fields from NSLocale on SKProductWrapper.

* Fix tests

* Fix serialization

* Format code

* Updated version and changelog

* Revert "Updated version and changelog"

This reverts commit 07fe2f50db37a081847ac0299c809a47a11c2224.

* Updated version and changelog
diff --git a/packages/in_app_purchase/in_app_purchase_ios/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_ios/CHANGELOG.md
index d46c124..d9fdfec 100644
--- a/packages/in_app_purchase/in_app_purchase_ios/CHANGELOG.md
+++ b/packages/in_app_purchase/in_app_purchase_ios/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.1.1
+
+* Exposed most of the NSLocale object via the SKProductWrapper class.
+
 ## 0.1.0
 
 * Initial open-source release.
\ No newline at end of file
diff --git a/packages/in_app_purchase/in_app_purchase_ios/ios/Classes/FIAObjectTranslator.m b/packages/in_app_purchase/in_app_purchase_ios/ios/Classes/FIAObjectTranslator.m
index 5d6e0a2..e03a9a7 100644
--- a/packages/in_app_purchase/in_app_purchase_ios/ios/Classes/FIAObjectTranslator.m
+++ b/packages/in_app_purchase/in_app_purchase_ios/ios/Classes/FIAObjectTranslator.m
@@ -107,10 +107,38 @@
     return nil;
   }
   NSMutableDictionary *map = [[NSMutableDictionary alloc] init];
+
+  [map setObject:[locale objectForKey:NSLocaleIdentifier] ?: [NSNull null]
+          forKey:@"localeIdentifier"];
+  [map setObject:[locale objectForKey:NSLocaleCountryCode] ?: [NSNull null] forKey:@"countryCode"];
+  [map setObject:[locale objectForKey:NSLocaleLanguageCode] ?: [NSNull null]
+          forKey:@"languageCode"];
+  [map setObject:[locale objectForKey:NSLocaleScriptCode] ?: [NSNull null] forKey:@"scriptCode"];
+  [map setObject:[locale objectForKey:NSLocaleVariantCode] ?: [NSNull null] forKey:@"variantCode"];
+  [map setObject:[locale objectForKey:NSLocaleCollationIdentifier] ?: [NSNull null]
+          forKey:@"collationIdentifier"];
+  [map setObject:[locale objectForKey:NSLocaleCollatorIdentifier] ?: [NSNull null]
+          forKey:@"collatorIdentifier"];
+  [map setObject:[locale objectForKey:NSLocaleUsesMetricSystem] ?: [NSNull null]
+          forKey:@"usesMetricSystem"];
+  [map setObject:[locale objectForKey:NSLocaleMeasurementSystem] ?: [NSNull null]
+          forKey:@"measurementSystem"];
+  [map setObject:[locale objectForKey:NSLocaleDecimalSeparator] ?: [NSNull null]
+          forKey:@"decimalSeparator"];
+  [map setObject:[locale objectForKey:NSLocaleGroupingSeparator] ?: [NSNull null]
+          forKey:@"groupingSeparator"];
   [map setObject:[locale objectForKey:NSLocaleCurrencySymbol] ?: [NSNull null]
           forKey:@"currencySymbol"];
   [map setObject:[locale objectForKey:NSLocaleCurrencyCode] ?: [NSNull null]
           forKey:@"currencyCode"];
+  [map setObject:[locale objectForKey:NSLocaleQuotationEndDelimiterKey] ?: [NSNull null]
+          forKey:@"endDelimiterKey"];
+  [map setObject:[locale objectForKey:NSLocaleQuotationBeginDelimiterKey] ?: [NSNull null]
+          forKey:@"beginDelimiterKey"];
+  [map setObject:[locale objectForKey:NSLocaleAlternateQuotationEndDelimiterKey] ?: [NSNull null]
+          forKey:@"alternateQuotationEndDelimiterKey"];
+  [map setObject:[locale objectForKey:NSLocaleAlternateQuotationBeginDelimiterKey] ?: [NSNull null]
+          forKey:@"alternateQuotationBeginDelimiterKey"];
   return map;
 }
 
diff --git a/packages/in_app_purchase/in_app_purchase_ios/lib/src/store_kit_wrappers/sk_product_wrapper.dart b/packages/in_app_purchase/in_app_purchase_ios/lib/src/store_kit_wrappers/sk_product_wrapper.dart
index ef0e667..8518cc0 100644
--- a/packages/in_app_purchase/in_app_purchase_ios/lib/src/store_kit_wrappers/sk_product_wrapper.dart
+++ b/packages/in_app_purchase/in_app_purchase_ios/lib/src/store_kit_wrappers/sk_product_wrapper.dart
@@ -329,25 +329,102 @@
 /// Object that indicates the locale of the price
 ///
 /// It is a thin wrapper of [NSLocale](https://developer.apple.com/documentation/foundation/nslocale?language=objc).
-// TODO(cyanglaz): NSLocale is a complex object, want to see the actual need of getting this expanded.
-//                 Matching android to only get the currencySymbol for now.
-//                 https://github.com/flutter/flutter/issues/26610
 @JsonSerializable()
 class SKPriceLocaleWrapper {
   /// Creates a new price locale for `currencySymbol` and `currencyCode`.
-  SKPriceLocaleWrapper(
-      {required this.currencySymbol, required this.currencyCode});
+  SKPriceLocaleWrapper({
+    required this.localeIdentifier,
+    required this.countryCode,
+    required this.languageCode,
+    required this.scriptCode,
+    required this.variantCode,
+    required this.collationIdentifier,
+    required this.collatorIdentifier,
+    required this.usesMetricSystem,
+    required this.measurementSystem,
+    required this.decimalSeparator,
+    required this.groupingSeparator,
+    required this.currencySymbol,
+    required this.currencyCode,
+    required this.endDelimiterKey,
+    required this.beginDelimiterKey,
+    required this.alternateQuotationEndDelimiterKey,
+    required this.alternateQuotationBeginDelimiterKey,
+  });
 
   /// Constructing an instance from a map from the Objective-C layer.
   ///
   /// This method should only be used with `map` values returned by [SKProductWrapper.fromJson] and [SKProductDiscountWrapper.fromJson].
   factory SKPriceLocaleWrapper.fromJson(Map<String, dynamic>? map) {
     if (map == null) {
-      return SKPriceLocaleWrapper(currencyCode: '', currencySymbol: '');
+      return SKPriceLocaleWrapper(
+        localeIdentifier: '',
+        countryCode: '',
+        languageCode: '',
+        scriptCode: '',
+        variantCode: '',
+        collationIdentifier: '',
+        collatorIdentifier: '',
+        usesMetricSystem: true,
+        measurementSystem: '',
+        decimalSeparator: '',
+        groupingSeparator: '',
+        currencySymbol: '',
+        currencyCode: '',
+        endDelimiterKey: '',
+        beginDelimiterKey: '',
+        alternateQuotationEndDelimiterKey: '',
+        alternateQuotationBeginDelimiterKey: '',
+      );
     }
     return _$SKPriceLocaleWrapperFromJson(map);
   }
 
+  ///The identifier for the locale, e.g. "en_US" for US locale.
+  @JsonKey(defaultValue: '')
+  final String localeIdentifier;
+
+  ///The country or region code for the locale, e.g. "US" for en_US locale.
+  @JsonKey(defaultValue: '')
+  final String countryCode;
+
+  ///The language code for the locale, e.g. "en" for en_US locale.
+  @JsonKey(defaultValue: '')
+  final String languageCode;
+
+  ///The script code for the locale, e.g. "Latn" for en_US locale.
+  @JsonKey(defaultValue: '')
+  final String scriptCode;
+
+  ///The variant code for the locale, e.g. "POSIX".
+  @JsonKey(defaultValue: '')
+  final String variantCode;
+
+  ///The collation associated with the locale, e.g. "pinyin".
+  @JsonKey(defaultValue: '')
+  final String collationIdentifier;
+
+  ///The collation identifier for the locale, e.g. "en".
+  @JsonKey(defaultValue: '')
+  final String collatorIdentifier;
+
+  ///A flag whether the locale uses the metric system.
+  ///If the value is false, you can typically assume American measurement units (e.g. miles).
+  @JsonKey(defaultValue: true)
+  final bool usesMetricSystem;
+
+  ///The measurement associated with the locale, e.g. "Metric" or "U.S.".
+  @JsonKey(defaultValue: '')
+  final String measurementSystem;
+
+  ///The decimal separator associated with the locale, e.g. "." or ",".
+  @JsonKey(defaultValue: '')
+  final String decimalSeparator;
+
+  ///The numeric grouping separator associated with the locale, e.g. "," or " ".
+  @JsonKey(defaultValue: '')
+  final String groupingSeparator;
+
   ///The currency symbol for the locale, e.g. $ for US locale.
   @JsonKey(defaultValue: '')
   final String currencySymbol;
@@ -356,19 +433,64 @@
   @JsonKey(defaultValue: '')
   final String currencyCode;
 
-  @override
-  bool operator ==(Object other) {
-    if (identical(other, this)) {
-      return true;
-    }
-    if (other.runtimeType != runtimeType) {
-      return false;
-    }
-    final SKPriceLocaleWrapper typedOther = other as SKPriceLocaleWrapper;
-    return typedOther.currencySymbol == currencySymbol &&
-        typedOther.currencyCode == currencyCode;
-  }
+  ///The end quotation symbol associated with the locale, e.g. "”", "“", "»", or "」".
+  @JsonKey(defaultValue: '')
+  final String endDelimiterKey;
+
+  ///The begin quotation symbol associated with the locale, e.g. "“", "„", "«", or "「".
+  @JsonKey(defaultValue: '')
+  final String beginDelimiterKey;
+
+  ///The alternate end quotation symbol associated with the locale, e.g. "“", "„", "«", or "「".
+  @JsonKey(defaultValue: '')
+  final String alternateQuotationEndDelimiterKey;
+
+  ///The alternating begin quotation symbol associated with the locale, e.g. "“", "„", "«", or "「".
+  @JsonKey(defaultValue: '')
+  final String alternateQuotationBeginDelimiterKey;
 
   @override
-  int get hashCode => hashValues(this.currencySymbol, this.currencyCode);
+  bool operator ==(Object other) =>
+      identical(this, other) ||
+      other is SKPriceLocaleWrapper &&
+          runtimeType == other.runtimeType &&
+          localeIdentifier == other.localeIdentifier &&
+          countryCode == other.countryCode &&
+          languageCode == other.languageCode &&
+          scriptCode == other.scriptCode &&
+          variantCode == other.variantCode &&
+          collationIdentifier == other.collationIdentifier &&
+          collatorIdentifier == other.collatorIdentifier &&
+          usesMetricSystem == other.usesMetricSystem &&
+          measurementSystem == other.measurementSystem &&
+          decimalSeparator == other.decimalSeparator &&
+          groupingSeparator == other.groupingSeparator &&
+          currencySymbol == other.currencySymbol &&
+          currencyCode == other.currencyCode &&
+          endDelimiterKey == other.endDelimiterKey &&
+          beginDelimiterKey == other.beginDelimiterKey &&
+          alternateQuotationEndDelimiterKey ==
+              other.alternateQuotationEndDelimiterKey &&
+          alternateQuotationBeginDelimiterKey ==
+              other.alternateQuotationBeginDelimiterKey;
+
+  @override
+  int get hashCode =>
+      localeIdentifier.hashCode ^
+      countryCode.hashCode ^
+      languageCode.hashCode ^
+      scriptCode.hashCode ^
+      variantCode.hashCode ^
+      collationIdentifier.hashCode ^
+      collatorIdentifier.hashCode ^
+      usesMetricSystem.hashCode ^
+      measurementSystem.hashCode ^
+      decimalSeparator.hashCode ^
+      groupingSeparator.hashCode ^
+      currencySymbol.hashCode ^
+      currencyCode.hashCode ^
+      endDelimiterKey.hashCode ^
+      beginDelimiterKey.hashCode ^
+      alternateQuotationEndDelimiterKey.hashCode ^
+      alternateQuotationBeginDelimiterKey.hashCode;
 }
diff --git a/packages/in_app_purchase/in_app_purchase_ios/lib/src/store_kit_wrappers/sk_product_wrapper.g.dart b/packages/in_app_purchase/in_app_purchase_ios/lib/src/store_kit_wrappers/sk_product_wrapper.g.dart
index 8c2eed3..fd6ab37 100644
--- a/packages/in_app_purchase/in_app_purchase_ios/lib/src/store_kit_wrappers/sk_product_wrapper.g.dart
+++ b/packages/in_app_purchase/in_app_purchase_ios/lib/src/store_kit_wrappers/sk_product_wrapper.g.dart
@@ -110,14 +110,48 @@
 
 SKPriceLocaleWrapper _$SKPriceLocaleWrapperFromJson(Map json) {
   return SKPriceLocaleWrapper(
+    localeIdentifier: json['localeIdentifier'] as String? ?? '',
+    countryCode: json['countryCode'] as String? ?? '',
+    languageCode: json['languageCode'] as String? ?? '',
+    scriptCode: json['scriptCode'] as String? ?? '',
+    variantCode: json['variantCode'] as String? ?? '',
+    collationIdentifier: json['collationIdentifier'] as String? ?? '',
+    collatorIdentifier: json['collatorIdentifier'] as String? ?? '',
+    usesMetricSystem: json['usesMetricSystem'] as bool? ?? true,
+    measurementSystem: json['measurementSystem'] as String? ?? '',
+    decimalSeparator: json['decimalSeparator'] as String? ?? '',
+    groupingSeparator: json['groupingSeparator'] as String? ?? '',
     currencySymbol: json['currencySymbol'] as String? ?? '',
     currencyCode: json['currencyCode'] as String? ?? '',
+    endDelimiterKey: json['endDelimiterKey'] as String? ?? '',
+    beginDelimiterKey: json['beginDelimiterKey'] as String? ?? '',
+    alternateQuotationEndDelimiterKey:
+        json['alternateQuotationEndDelimiterKey'] as String? ?? '',
+    alternateQuotationBeginDelimiterKey:
+        json['alternateQuotationBeginDelimiterKey'] as String? ?? '',
   );
 }
 
 Map<String, dynamic> _$SKPriceLocaleWrapperToJson(
         SKPriceLocaleWrapper instance) =>
     <String, dynamic>{
+      'localeIdentifier': instance.localeIdentifier,
+      'countryCode': instance.countryCode,
+      'languageCode': instance.languageCode,
+      'scriptCode': instance.scriptCode,
+      'variantCode': instance.variantCode,
+      'collationIdentifier': instance.collationIdentifier,
+      'collatorIdentifier': instance.collatorIdentifier,
+      'usesMetricSystem': instance.usesMetricSystem,
+      'measurementSystem': instance.measurementSystem,
+      'decimalSeparator': instance.decimalSeparator,
+      'groupingSeparator': instance.groupingSeparator,
       'currencySymbol': instance.currencySymbol,
       'currencyCode': instance.currencyCode,
+      'endDelimiterKey': instance.endDelimiterKey,
+      'beginDelimiterKey': instance.beginDelimiterKey,
+      'alternateQuotationEndDelimiterKey':
+          instance.alternateQuotationEndDelimiterKey,
+      'alternateQuotationBeginDelimiterKey':
+          instance.alternateQuotationBeginDelimiterKey,
     };
diff --git a/packages/in_app_purchase/in_app_purchase_ios/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_ios/pubspec.yaml
index fcc7c51..c847fb4 100644
--- a/packages/in_app_purchase/in_app_purchase_ios/pubspec.yaml
+++ b/packages/in_app_purchase/in_app_purchase_ios/pubspec.yaml
@@ -1,7 +1,7 @@
 name: in_app_purchase_ios
 description: An implementation for the iOS platform of the Flutter `in_app_purchase` plugin. This uses the iOS StoreKit Framework.
 repository: https://github.com/flutter/plugins/tree/master/packages/in_app_purchase/in_app_purchase_ios
-version: 0.1.0
+version: 0.1.1
 
 flutter:
   plugin:
diff --git a/packages/in_app_purchase/in_app_purchase_ios/test/store_kit_wrappers/sk_product_test.dart b/packages/in_app_purchase/in_app_purchase_ios/test/store_kit_wrappers/sk_product_test.dart
index 9454a9d..55161dc 100644
--- a/packages/in_app_purchase/in_app_purchase_ios/test/store_kit_wrappers/sk_product_test.dart
+++ b/packages/in_app_purchase/in_app_purchase_ios/test/store_kit_wrappers/sk_product_test.dart
@@ -44,8 +44,27 @@
       final SKProductDiscountWrapper wrapper =
           SKProductDiscountWrapper.fromJson(<String, dynamic>{});
       expect(wrapper.price, '');
-      expect(wrapper.priceLocale,
-          SKPriceLocaleWrapper(currencyCode: '', currencySymbol: ''));
+      expect(
+          wrapper.priceLocale,
+          SKPriceLocaleWrapper(
+            localeIdentifier: '',
+            countryCode: '',
+            languageCode: '',
+            scriptCode: '',
+            variantCode: '',
+            collationIdentifier: '',
+            collatorIdentifier: '',
+            usesMetricSystem: true,
+            measurementSystem: '',
+            decimalSeparator: '',
+            groupingSeparator: '',
+            currencySymbol: '',
+            currencyCode: '',
+            endDelimiterKey: '',
+            beginDelimiterKey: '',
+            alternateQuotationEndDelimiterKey: '',
+            alternateQuotationBeginDelimiterKey: '',
+          ));
       expect(wrapper.numberOfPeriods, 0);
       expect(wrapper.paymentMode, SKProductDiscountPaymentMode.payAsYouGo);
       expect(
@@ -69,8 +88,27 @@
       expect(wrapper.productIdentifier, '');
       expect(wrapper.localizedTitle, '');
       expect(wrapper.localizedDescription, '');
-      expect(wrapper.priceLocale,
-          SKPriceLocaleWrapper(currencyCode: '', currencySymbol: ''));
+      expect(
+          wrapper.priceLocale,
+          SKPriceLocaleWrapper(
+            localeIdentifier: '',
+            countryCode: '',
+            languageCode: '',
+            scriptCode: '',
+            variantCode: '',
+            collationIdentifier: '',
+            collatorIdentifier: '',
+            usesMetricSystem: true,
+            measurementSystem: '',
+            decimalSeparator: '',
+            groupingSeparator: '',
+            currencySymbol: '',
+            currencyCode: '',
+            endDelimiterKey: '',
+            beginDelimiterKey: '',
+            alternateQuotationEndDelimiterKey: '',
+            alternateQuotationBeginDelimiterKey: '',
+          ));
       expect(wrapper.subscriptionGroupIdentifier, null);
       expect(wrapper.price, '');
       expect(wrapper.subscriptionPeriod, null);
diff --git a/packages/in_app_purchase/in_app_purchase_ios/test/store_kit_wrappers/sk_test_stub_objects.dart b/packages/in_app_purchase/in_app_purchase_ios/test/store_kit_wrappers/sk_test_stub_objects.dart
index d6c2446..1298c60 100644
--- a/packages/in_app_purchase/in_app_purchase_ios/test/store_kit_wrappers/sk_test_stub_objects.dart
+++ b/packages/in_app_purchase/in_app_purchase_ios/test/store_kit_wrappers/sk_test_stub_objects.dart
@@ -33,8 +33,25 @@
   error: dummyError,
 );
 
-final SKPriceLocaleWrapper dummyLocale =
-    SKPriceLocaleWrapper(currencySymbol: '\$', currencyCode: 'USD');
+final SKPriceLocaleWrapper dummyLocale = SKPriceLocaleWrapper(
+  localeIdentifier: 'en_US',
+  countryCode: 'US',
+  languageCode: 'en',
+  scriptCode: 'Latn',
+  variantCode: 'POSIX',
+  collationIdentifier: '',
+  collatorIdentifier: 'en',
+  usesMetricSystem: true,
+  measurementSystem: 'Metric',
+  decimalSeparator: '.',
+  groupingSeparator: ',',
+  currencySymbol: '\$',
+  currencyCode: 'USD',
+  endDelimiterKey: '”',
+  beginDelimiterKey: '“',
+  alternateQuotationEndDelimiterKey: '”',
+  alternateQuotationBeginDelimiterKey: '“',
+);
 
 final SKProductSubscriptionPeriodWrapper dummySubscription =
     SKProductSubscriptionPeriodWrapper(
@@ -69,8 +86,25 @@
 
 Map<String, dynamic> buildLocaleMap(SKPriceLocaleWrapper local) {
   return {
+    'localeIdentifier': local.localeIdentifier,
+    'countryCode': local.countryCode,
+    'languageCode': local.languageCode,
+    'scriptCode': local.scriptCode,
+    'variantCode': local.variantCode,
+    'collationIdentifier': local.collationIdentifier,
+    'collatorIdentifier': local.collatorIdentifier,
+    'usesMetricSystem': local.usesMetricSystem,
+    'measurementSystem': local.measurementSystem,
+    'decimalSeparator': local.decimalSeparator,
+    'groupingSeparator': local.groupingSeparator,
     'currencySymbol': local.currencySymbol,
-    'currencyCode': local.currencyCode
+    'currencyCode': local.currencyCode,
+    'endDelimiterKey': local.endDelimiterKey,
+    'beginDelimiterKey': local.beginDelimiterKey,
+    'alternateQuotationEndDelimiterKey':
+        local.alternateQuotationEndDelimiterKey,
+    'alternateQuotationBeginDelimiterKey':
+        local.alternateQuotationBeginDelimiterKey
   };
 }