Migrate `package:intl` `Locale` and parser to null safety.

The only field on `Locale` which can never be null is `languageCode`.

PiperOrigin-RevId: 327772496
diff --git a/lib/src/locale.dart b/lib/src/locale.dart
index d3d1d3b..9cd756c 100644
--- a/lib/src/locale.dart
+++ b/lib/src/locale.dart
@@ -1,7 +1,6 @@
 // Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-// @dart=2.9
 
 import 'locale/locale_implementation.dart';
 import 'locale/locale_parser.dart' show LocaleParser;
@@ -21,7 +20,9 @@
   ///
   /// Throws a [FormatException] if any subtag is syntactically invalid.
   static Locale fromSubtags(
-          {String languageCode, String scriptCode, String countryCode}) =>
+          {required String languageCode,
+          String? scriptCode,
+          String? countryCode}) =>
       LocaleImplementation.fromSubtags(
           languageCode: languageCode,
           scriptCode: scriptCode,
@@ -35,7 +36,7 @@
   ///
   /// Throws a [FormatException] if [localeIdentifier] is syntactically invalid.
   static Locale parse(String localeIdentifier) {
-    assert(localeIdentifier != null);
+    ArgumentError.checkNotNull(localeIdentifier);
     var parser = LocaleParser(localeIdentifier);
     var locale = parser.toLocale();
     if (locale == null) {
@@ -52,8 +53,8 @@
   /// https://www.unicode.org/reports/tr35/#Unicode_locale_identifier
   ///
   /// Returns `null` if [localeIdentifier] is syntactically invalid.
-  static Locale tryParse(String localeIdentifier) {
-    assert(localeIdentifier != null);
+  static Locale? tryParse(String localeIdentifier) {
+    ArgumentError.checkNotNull(localeIdentifier);
     var parser = LocaleParser(localeIdentifier);
     return parser.toLocale();
   }
@@ -71,7 +72,7 @@
   /// It is syntactically valid and normalized (has correct case), but not
   /// necessarily valid (the script might not exist) because the list of valid
   /// scripts changes with time.
-  String get scriptCode;
+  String? get scriptCode;
 
   /// The region subtag of the Locale Identifier, null if absent.
   ///
@@ -79,7 +80,7 @@
   /// (deprecated tags have been replaced), but not necessarily valid (the
   /// region might not exist) because the list of valid regions changes with
   /// time.
-  String get countryCode;
+  String? get countryCode;
 
   /// Iterable of variant subtags.
   ///
diff --git a/lib/src/locale/locale_deprecations.dart b/lib/src/locale/locale_deprecations.dart
index f43b6b7..5dd0922 100644
--- a/lib/src/locale/locale_deprecations.dart
+++ b/lib/src/locale/locale_deprecations.dart
@@ -101,7 +101,7 @@
 /// The subtag must already be uppercase.
 ///
 /// TODO(b/127689510): write a new script for updating this list from CLDR data.
-String? replaceDeprecatedRegionSubtag(String? regionCode) {
+String replaceDeprecatedRegionSubtag(String regionCode) {
   return _deprecatedRegionTagReplacements[regionCode] ?? regionCode;
 }
 
diff --git a/lib/src/locale/locale_implementation.dart b/lib/src/locale/locale_implementation.dart
index bd397be..230eb80 100644
--- a/lib/src/locale/locale_implementation.dart
+++ b/lib/src/locale/locale_implementation.dart
@@ -1,7 +1,6 @@
 // Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-// @dart=2.9
 
 import 'package:intl/src/locale.dart' show Locale;
 
@@ -13,18 +12,19 @@
   /// Simple private constructor with asserts to check invariants.
   LocaleImplementation._(this.languageCode, this.scriptCode, this.countryCode,
       this.variants, this._extensions) {
+    ArgumentError.notNull(languageCode);
     // Debug-mode asserts to ensure all parameters are normalized and UTS #35
     // compliant.
     assert(
-        languageCode != null && _normalizedLanguageRE.hasMatch(languageCode),
+        _normalizedLanguageRE.hasMatch(languageCode),
         'languageCode must match RegExp/${_normalizedLanguageRE.pattern}/ '
         'but is "$languageCode".');
     assert(
-        scriptCode == null || _normalizedScriptRE.hasMatch(scriptCode),
+        scriptCode == null || _normalizedScriptRE.hasMatch(scriptCode!),
         'scriptCode must match RegExp/${_normalizedScriptRE.pattern}/ '
         'but is "$scriptCode".');
     assert(
-        countryCode == null || _normalizedRegionRE.hasMatch(countryCode),
+        countryCode == null || _normalizedRegionRE.hasMatch(countryCode!),
         'countryCode must match RegExp/${_normalizedRegionRE.pattern}/ '
         'but is "$countryCode".');
     assert(
@@ -67,10 +67,10 @@
   /// For public APIs, see [Locale.fromSubtags] and [Locale.parse].
   factory LocaleImplementation.unsafe(
     String languageCode, {
-    String scriptCode,
-    String countryCode,
-    Iterable<String> variants,
-    LocaleExtensions extensions,
+    String? scriptCode,
+    String? countryCode,
+    Iterable<String>? variants,
+    LocaleExtensions? extensions,
   }) {
     variants = (variants != null && variants.isNotEmpty)
         ? List.unmodifiable(variants.toList()..sort())
@@ -84,11 +84,13 @@
   ///
   /// Throws a [FormatException] if any subtag is syntactically invalid.
   static LocaleImplementation fromSubtags(
-      {String languageCode, String scriptCode, String countryCode}) {
+      {required String languageCode, String? scriptCode, String? countryCode}) {
     return LocaleImplementation._(
         replaceDeprecatedLanguageSubtag(_normalizeLanguageCode(languageCode)),
-        _normalizeScriptCode(scriptCode),
-        replaceDeprecatedRegionSubtag(_normalizeCountryCode(countryCode)),
+        scriptCode == null ? null : _normalizeScriptCode(scriptCode),
+        countryCode == null
+            ? null
+            : replaceDeprecatedRegionSubtag(_normalizeCountryCode(countryCode)),
         const [],
         null);
   }
@@ -109,7 +111,6 @@
   ///
   /// Throws a [FormatException] if it is syntactically invalid.
   static String _normalizeScriptCode(String scriptCode) {
-    if (scriptCode == null) return null;
     if (!_scriptRegExp.hasMatch(scriptCode)) {
       throw FormatException('Invalid script "$scriptCode"');
     }
@@ -122,7 +123,6 @@
   ///
   /// Throws a [FormatException] if it is syntactically invalid.
   static String _normalizeCountryCode(String countryCode) {
-    if (countryCode == null) return null;
     if (!_regionRegExp.hasMatch(countryCode)) {
       throw FormatException('Invalid region "$countryCode"');
     }
@@ -145,7 +145,7 @@
   /// (deprecated tags have been replaced), but not necessarily valid (the
   /// script might not exist) because the list of valid scripts changes with
   /// time.
-  final String scriptCode;
+  final String? scriptCode;
 
   /// The region subtag of the Locale Identifier, null if absent.
   ///
@@ -153,7 +153,7 @@
   /// (deprecated tags have been replaced), but not necessarily valid (the
   /// region might not exist) because the list of valid regions changes with
   /// time.
-  final String countryCode;
+  final String? countryCode;
 
   /// Iterable of variant subtags, zero-length iterable if variants are absent.
   ///
@@ -165,22 +165,22 @@
 
   /// Locale extensions, null if the locale has no extensions.
   // TODO(hugovdm): Not yet supported: getters for extensions.
-  final LocaleExtensions _extensions;
+  final LocaleExtensions? _extensions;
 
   /// Cache of the value returned by [toLanguageTag].
-  String _languageTag;
+  String? _languageTag;
 
   /// Returns the canonical Unicode BCP47 Locale Identifier for this locale.
   String toLanguageTag() {
     if (_languageTag == null) {
       final out = [languageCode];
-      if (scriptCode != null) out.add(scriptCode);
-      if (countryCode != null) out.add(countryCode);
+      if (scriptCode != null) out.add(scriptCode!);
+      if (countryCode != null) out.add(countryCode!);
       out.addAll(variants);
-      if (_extensions != null) out.addAll(_extensions.subtags);
+      if (_extensions != null) out.addAll(_extensions!.subtags);
       _languageTag = out.join('-');
     }
-    return _languageTag;
+    return _languageTag!;
   }
 }
 
diff --git a/lib/src/locale/locale_parser.dart b/lib/src/locale/locale_parser.dart
index 1390d16..49f98fd 100644
--- a/lib/src/locale/locale_parser.dart
+++ b/lib/src/locale/locale_parser.dart
@@ -1,7 +1,6 @@
 // Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-// @dart=2.9
 
 import 'locale_deprecations.dart';
 import 'locale_extensions.dart';
@@ -14,25 +13,25 @@
   String _languageCode = 'und';
 
   /// Script subtag of Unicode Language Identifier.
-  String _scriptCode;
+  String? _scriptCode;
 
   /// Region subtag of Unicode Language Identifier.
-  String _countryCode;
+  String? _countryCode;
 
   /// Variant subtags of Unicode Language Identifier.
-  List<String> _variants;
+  List<String>? _variants;
 
   /// Unicode Locale Extensions, also known as "U Extension".
-  Map<String, String> _uExtensions;
+  Map<String, String>? _uExtensions;
 
   /// Transformed Extensions, also known as "T Extension".
-  Map<String, String> _tExtensions;
+  Map<String, String>? _tExtensions;
 
   /// Private-Use Extensions.
-  String _xExtensions;
+  String? _xExtensions;
 
   /// Other Extensions.
-  Map<String, String> _otherExtensions;
+  Map<String, String>? _otherExtensions;
 
   /// List of problems with the localeId the parser tried to parse.
   ///
@@ -42,9 +41,9 @@
   /// Produces a Locale instance for the parser's current state.
   ///
   /// Returns null if the Locale would be syntactically invalid.
-  LocaleImplementation toLocale() {
+  LocaleImplementation? toLocale() {
     if (problems.isNotEmpty) return null;
-    LocaleExtensions extensions;
+    LocaleExtensions? extensions;
     if (_uExtensions != null ||
         _tExtensions != null ||
         _otherExtensions != null ||
@@ -62,31 +61,22 @@
   }
 
   /// Subtags of the Locale Identifier, as split by [separators].
-  List<String> _subtags;
+  late List<String> _subtags;
 
   /// RegExp that matches Unicode Locale Identifier subtag separators.
   static final separators = RegExp('[-_]');
 
   /// Last accepted subtag.
-  String _accepted;
-
-  /// Last accepted subtag.
-  String accepted() => _accepted;
+  String _accepted = '';
 
   /// Last accepted list of subtags (for variants).
-  List<String> _acceptedList;
-
-  /// Last accepted list of subtags (for variants).
-  List<String> acceptedList() => _acceptedList;
+  List<String>? _acceptedList;
 
   /// Current subtag pending acceptance.
-  String _current;
-
-  /// Current subtag pending acceptance.
-  String current() => _current;
+  String _current = '';
 
   /// Index of the current subtag.
-  int _currentIndex;
+  int _currentIndex = 0;
 
   /// Advance to the next subtag (see [current] and [accepted]).
   void advance() {
@@ -95,7 +85,7 @@
     if (_currentIndex < _subtags.length) {
       _current = _subtags[_currentIndex];
     } else {
-      _current = null;
+      // Guarded by `atEnd`.
     }
   }
 
@@ -115,7 +105,7 @@
   ///
   /// Parsing failed if there are any entries in [problems].
   LocaleParser(String localeId) {
-    assert(localeId != null);
+    ArgumentError.notNull(localeId);
 
     // Calling toLowerCase unconditionally should be efficient if
     // string_patch.dart is in use:
@@ -126,12 +116,11 @@
     }
 
     _subtags = localeId.split(separators);
-    _currentIndex = 0;
     _current = _subtags[0];
 
     var scriptFound = false;
     if (acceptLanguage()) {
-      _languageCode = replaceDeprecatedLanguageSubtag(accepted());
+      _languageCode = replaceDeprecatedLanguageSubtag(_accepted);
       scriptFound = acceptScript();
     } else {
       scriptFound = acceptScript();
@@ -140,18 +129,18 @@
       }
     }
     if (scriptFound) {
-      _scriptCode = toCapCase(accepted());
+      _scriptCode = toCapCase(_accepted);
     }
     if (acceptRegion()) {
-      _countryCode = replaceDeprecatedRegionSubtag(accepted().toUpperCase());
+      _countryCode = replaceDeprecatedRegionSubtag(_accepted.toUpperCase());
     }
     acceptVariants();
-    _variants = acceptedList();
+    _variants = _acceptedList;
 
     processExtensions();
 
     if (!atEnd()) {
-      problems.add('bad subtag "${current()}"');
+      problems.add('bad subtag "$_current"');
     }
   }
 
@@ -161,7 +150,7 @@
   /// empty.
   void processExtensions() {
     while (acceptSingleton()) {
-      var singleton = accepted();
+      var singleton = _accepted;
       if (singleton == 'u') {
         processUExtensions();
       } else if (singleton == 't') {
@@ -189,26 +178,26 @@
     var empty = true;
     final attributes = <String>[];
     while (acceptLowAlphaNumeric3to8()) {
-      attributes.add(accepted());
+      attributes.add(_accepted);
     }
     if (attributes.isNotEmpty) {
       empty = false;
       attributes.sort();
-      _uExtensions[''] = attributes.join('-');
+      _uExtensions![''] = attributes.join('-');
     }
     // unicode_locale_extensions: collect "(sep keyword)*".
     while (acceptUExtensionKey()) {
       empty = false;
-      var key = accepted();
+      var key = _accepted;
       final typeParts = <String>[];
       while (acceptLowAlphaNumeric3to8()) {
-        typeParts.add(accepted());
+        typeParts.add(_accepted);
       }
-      if (!_uExtensions.containsKey(key)) {
+      if (!_uExtensions!.containsKey(key)) {
         if (typeParts.length == 1 && typeParts[0] == 'true') {
-          _uExtensions[key] = '';
+          _uExtensions![key] = '';
         } else {
-          _uExtensions[key] = typeParts.join('-');
+          _uExtensions![key] = typeParts.join('-');
         }
       } else {
         problems.add('duplicate "$key"');
@@ -234,29 +223,29 @@
     final tlang = <String>[];
     if (acceptLanguage()) {
       empty = false;
-      tlang.add(replaceDeprecatedLanguageSubtag(accepted()));
+      tlang.add(replaceDeprecatedLanguageSubtag(_accepted));
       if (acceptScript()) {
-        tlang.add(accepted());
+        tlang.add(_accepted);
       }
       if (acceptRegion()) {
-        tlang.add(replaceDeprecatedRegionSubtag(accepted().toUpperCase())
+        tlang.add(replaceDeprecatedRegionSubtag(_accepted.toUpperCase())
             .toLowerCase());
       }
       acceptVariants();
-      tlang.addAll(acceptedList());
-      _tExtensions[''] = tlang.join('-');
+      tlang.addAll(_acceptedList!);
+      _tExtensions![''] = tlang.join('-');
     }
     // transformed_extensions: collect "(sep tfield)*".
     while (acceptTExtensionKey()) {
-      var tkey = accepted();
+      var tkey = _accepted;
       final tvalueParts = <String>[];
       while (acceptLowAlphaNumeric3to8()) {
-        tvalueParts.add(accepted());
+        tvalueParts.add(_accepted);
       }
       if (tvalueParts.isNotEmpty) {
         empty = false;
-        if (!_tExtensions.containsKey(tkey)) {
-          _tExtensions[tkey] = tvalueParts.join('-');
+        if (!_tExtensions!.containsKey(tkey)) {
+          _tExtensions![tkey] = tvalueParts.join('-');
         } else {
           problems.add('duplicate "$tkey"');
         }
@@ -277,7 +266,7 @@
   void processPrivateUseExtensions() {
     final values = <String>[];
     while (acceptLowAlphaNumeric1to8()) {
-      values.add(accepted());
+      values.add(_accepted);
     }
     if (values.isNotEmpty) {
       _xExtensions = values.join('-');
@@ -293,22 +282,22 @@
   void processOtherExtensions(String singleton) {
     final values = <String>[];
     while (acceptLowAlphaNumeric2to8()) {
-      values.add(accepted());
+      values.add(_accepted);
     }
     if (values.isEmpty) return;
     if (_otherExtensions == null) {
       _otherExtensions = <String, String>{};
-    } else if (_otherExtensions.containsKey(singleton)) {
+    } else if (_otherExtensions!.containsKey(singleton)) {
       problems.add('duplicate "$singleton"');
       return;
     }
-    _otherExtensions[singleton] = values.join('-');
+    _otherExtensions![singleton] = values.join('-');
   }
 
   /// Advances and returns true if current subtag is a language subtag.
   bool acceptLanguage() {
     if (atEnd()) return false;
-    if (!_languageRegExp.hasMatch(current())) return false;
+    if (!_languageRegExp.hasMatch(_current)) return false;
     advance();
     return true;
   }
@@ -318,7 +307,7 @@
   /// Advances and returns true if current subtag is a script subtag.
   bool acceptScript() {
     if (atEnd()) return false;
-    if (!_scriptRegExp.hasMatch(current())) return false;
+    if (!_scriptRegExp.hasMatch(_current)) return false;
     advance();
     return true;
   }
@@ -328,7 +317,7 @@
   /// Advances and returns true if current subtag is a region subtag.
   bool acceptRegion() {
     if (atEnd()) return false;
-    if (!_regionRegExp.hasMatch(current())) return false;
+    if (!_regionRegExp.hasMatch(_current)) return false;
     advance();
     return true;
   }
@@ -342,8 +331,8 @@
   /// collected subtags.
   void acceptVariants() {
     _acceptedList = [];
-    while (!atEnd() && _variantRegExp.hasMatch(current())) {
-      _acceptedList.add(current());
+    while (!atEnd() && _variantRegExp.hasMatch(_current)) {
+      _acceptedList!.add(_current);
       advance();
     }
   }
@@ -353,7 +342,7 @@
   /// Advances and returns true if current subtag is a singleton.
   bool acceptSingleton() {
     if (atEnd()) return false;
-    if (!_singletonRegExp.hasMatch(current())) return false;
+    if (!_singletonRegExp.hasMatch(_current)) return false;
     advance();
     return true;
   }
@@ -364,7 +353,7 @@
   /// ranging from 1 to 8.
   bool acceptLowAlphaNumeric1to8() {
     if (atEnd()) return false;
-    if (!_alphaNumeric1to8RegExp.hasMatch(current())) return false;
+    if (!_alphaNumeric1to8RegExp.hasMatch(_current)) return false;
     advance();
     return true;
   }
@@ -375,7 +364,7 @@
   /// ranging from 2 to 8.
   bool acceptLowAlphaNumeric2to8() {
     if (atEnd()) return false;
-    if (!_alphaNumeric1to8RegExp.hasMatch(current()) || current().length < 2) {
+    if (!_alphaNumeric1to8RegExp.hasMatch(_current) || _current.length < 2) {
       return false;
     }
     advance();
@@ -386,7 +375,7 @@
   /// ranging from 3 to 8.
   bool acceptLowAlphaNumeric3to8() {
     if (atEnd()) return false;
-    if (!_alphaNumeric1to8RegExp.hasMatch(current()) || current().length < 3) {
+    if (!_alphaNumeric1to8RegExp.hasMatch(_current) || _current.length < 3) {
       return false;
     }
     advance();
@@ -396,7 +385,7 @@
   /// Advances and returns true if current subtag is a valid U Extension key.
   bool acceptUExtensionKey() {
     if (atEnd()) return false;
-    if (!_uExtensionKeyRegExp.hasMatch(current())) return false;
+    if (!_uExtensionKeyRegExp.hasMatch(_current)) return false;
     advance();
     return true;
   }
@@ -407,7 +396,7 @@
   /// (`tkey` in the specification).
   bool acceptTExtensionKey() {
     if (atEnd()) return false;
-    if (!_tExtensionKeyRegExp.hasMatch(current())) return false;
+    if (!_tExtensionKeyRegExp.hasMatch(_current)) return false;
     advance();
     return true;
   }