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;
}