mirror of
https://github.com/encounter/engine.git
synced 2026-03-30 11:09:55 -07:00
Add Locale.fromSubtags and support for scriptCode. (#6518)
* Add Locale.fromComponents. * Change toString from underscores to dashes. Expand the unit tests. * Rename 'fromComponents' to 'create'. Change variants from String to List<String>. * Use default for language parameter. Use hashCode/hashList. * Have toString() stick with old (underscore) behaviour. * Demonstrate empty-list bug in assert code. * Fix empty-list assert bug. * Add ignores for lint issues. Unsure about 71340 though. * Fix operator== via _listEquals. * Remove length-checking asserts: we're anyway not checking characters in fields. * Documentation update. * Change reasoning for ignore:prefer_initializing_formals. * Try 'fromSubtags' as new constructor name. * Documentation improvements based on Pull Request review. * Assert-fail for invalid-length subtags and drop bad subtags in production code. * Revert "Assert-fail for invalid-length subtags and drop bad subtags in production code." This reverts commit d6f06f5e7b3537d60000c47641580475ef16abbe. * Re-fix Locale.toString() for variants=[]. * Tear out variants, in case we want to have one fewer pointer in the future. * Make named parameters' names consistent with member names. * Also remove _listEquals: no longer in use. * Lint fix. * Fix code review nits. * Lint fix for assert, and a couple more not-zero-length-string asserts. * Code Review: two of three nits addressed... * Review fix: change 'should' to 'must' for subtag prescriptions. * Assert-check that countryCode is never ''.
This commit is contained in:
+95
-25
@@ -116,9 +116,11 @@ class WindowPadding {
|
||||
}
|
||||
}
|
||||
|
||||
/// An identifier used to select a user's language and formatting preferences,
|
||||
/// consisting of a language and a country. This is a subset of locale
|
||||
/// identifiers as defined by BCP 47.
|
||||
/// An identifier used to select a user's language and formatting preferences.
|
||||
///
|
||||
/// This represents a [Unicode Language
|
||||
/// Identifier](https://www.unicode.org/reports/tr35/#Unicode_language_identifier)
|
||||
/// (i.e. without Locale extensions), except variants are not supported.
|
||||
///
|
||||
/// Locales are canonicalized according to the "preferred value" entries in the
|
||||
/// [IANA Language Subtag
|
||||
@@ -145,16 +147,58 @@ class Locale {
|
||||
/// The primary language subtag must not be null. The region subtag is
|
||||
/// optional.
|
||||
///
|
||||
/// The values are _case sensitive_, and should match the case of the relevant
|
||||
/// subtags in the [IANA Language Subtag
|
||||
/// Registry](https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry).
|
||||
/// Typically this means the primary language subtag should be lowercase and
|
||||
/// the region subtag should be uppercase.
|
||||
const Locale(this._languageCode, [ this._countryCode ]) : assert(_languageCode != null), assert(_languageCode != '');
|
||||
/// The subtag values are _case sensitive_ and must be one of the valid
|
||||
/// subtags according to CLDR supplemental data:
|
||||
/// [language](http://unicode.org/cldr/latest/common/validity/language.xml),
|
||||
/// [region](http://unicode.org/cldr/latest/common/validity/region.xml). The
|
||||
/// primary language subtag must be at least two and at most eight lowercase
|
||||
/// letters, but not four letters. The region region subtag must be two
|
||||
/// uppercase letters or three digits. See the [Unicode Language
|
||||
/// Identifier](https://www.unicode.org/reports/tr35/#Unicode_language_identifier)
|
||||
/// specification.
|
||||
///
|
||||
/// Validity is not checked by default, but some methods may throw away
|
||||
/// invalid data.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [new Locale.fromSubtags], which also allows a [scriptCode] to be
|
||||
/// specified.
|
||||
const Locale(
|
||||
this._languageCode, [
|
||||
this._countryCode,
|
||||
]) : assert(_languageCode != null),
|
||||
assert(_languageCode != ''),
|
||||
scriptCode = null,
|
||||
assert(_countryCode != '');
|
||||
|
||||
/// Creates a new Locale object.
|
||||
///
|
||||
/// The keyword arguments specify the subtags of the Locale.
|
||||
///
|
||||
/// The subtag values are _case sensitive_ and must be valid subtags according
|
||||
/// to CLDR supplemental data:
|
||||
/// [language](http://unicode.org/cldr/latest/common/validity/language.xml),
|
||||
/// [script](http://unicode.org/cldr/latest/common/validity/script.xml) and
|
||||
/// [region](http://unicode.org/cldr/latest/common/validity/region.xml) for
|
||||
/// each of languageCode, scriptCode and countryCode respectively.
|
||||
///
|
||||
/// Validity is not checked by default, but some methods may throw away
|
||||
/// invalid data.
|
||||
const Locale.fromSubtags({
|
||||
String languageCode = 'und',
|
||||
this.scriptCode,
|
||||
String countryCode,
|
||||
}) : assert(languageCode != null),
|
||||
assert(languageCode != ''),
|
||||
_languageCode = languageCode,
|
||||
assert(scriptCode != ''),
|
||||
assert(countryCode != ''),
|
||||
_countryCode = countryCode;
|
||||
|
||||
/// The primary language subtag for the locale.
|
||||
///
|
||||
/// This must not be null.
|
||||
/// This must not be null. It may be 'und', representing 'undefined'.
|
||||
///
|
||||
/// This is expected to be string registered in the [IANA Language Subtag
|
||||
/// Registry](https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry)
|
||||
@@ -166,10 +210,19 @@ class Locale {
|
||||
/// Locale('he')` and `const Locale('iw')` are equal, and both have the
|
||||
/// [languageCode] `he`, because `iw` is a deprecated language subtag that was
|
||||
/// replaced by the subtag `he`.
|
||||
String get languageCode => _canonicalizeLanguageCode(_languageCode);
|
||||
///
|
||||
/// This must be a valid Unicode Language subtag as listed in [Unicode CLDR
|
||||
/// supplemental
|
||||
/// data](http://unicode.org/cldr/latest/common/validity/language.xml).
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [new Locale.fromSubtags], which describes the conventions for creating
|
||||
/// [Locale] objects.
|
||||
String get languageCode => _replaceDeprecatedLanguageSubtag(_languageCode);
|
||||
final String _languageCode;
|
||||
|
||||
static String _canonicalizeLanguageCode(String languageCode) {
|
||||
static String _replaceDeprecatedLanguageSubtag(String languageCode) {
|
||||
// This switch statement is generated by //flutter/tools/gen_locale.dart
|
||||
// Mappings generated for language subtag registry as of 2018-08-08.
|
||||
switch (languageCode) {
|
||||
@@ -255,9 +308,23 @@ class Locale {
|
||||
}
|
||||
}
|
||||
|
||||
/// The script subtag for the locale.
|
||||
///
|
||||
/// This may be null, indicating that there is no specified script subtag.
|
||||
///
|
||||
/// This must be a valid Unicode Language Identifier script subtag as listed
|
||||
/// in [Unicode CLDR supplemental
|
||||
/// data](http://unicode.org/cldr/latest/common/validity/script.xml).
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [new Locale.fromSubtags], which describes the conventions for creating
|
||||
/// [Locale] objects.
|
||||
final String scriptCode;
|
||||
|
||||
/// The region subtag for the locale.
|
||||
///
|
||||
/// This can be null.
|
||||
/// This may be null, indicating that there is no specified region subtag.
|
||||
///
|
||||
/// This is expected to be string registered in the [IANA Language Subtag
|
||||
/// Registry](https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry)
|
||||
@@ -269,10 +336,15 @@ class Locale {
|
||||
/// 'DE')` and `const Locale('de', 'DD')` are equal, and both have the
|
||||
/// [countryCode] `DE`, because `DD` is a deprecated language subtag that was
|
||||
/// replaced by the subtag `DE`.
|
||||
String get countryCode => _canonicalizeRegionCode(_countryCode);
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [new Locale.fromSubtags], which describes the conventions for creating
|
||||
/// [Locale] objects.
|
||||
String get countryCode => _replaceDeprecatedRegionSubtag(_countryCode);
|
||||
final String _countryCode;
|
||||
|
||||
static String _canonicalizeRegionCode(String regionCode) {
|
||||
static String _replaceDeprecatedRegionSubtag(String regionCode) {
|
||||
// This switch statement is generated by //flutter/tools/gen_locale.dart
|
||||
// Mappings generated for language subtag registry as of 2018-08-08.
|
||||
switch (regionCode) {
|
||||
@@ -294,23 +366,21 @@ class Locale {
|
||||
return false;
|
||||
final Locale typedOther = other;
|
||||
return languageCode == typedOther.languageCode
|
||||
&& scriptCode == typedOther.scriptCode
|
||||
&& countryCode == typedOther.countryCode;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
int result = 373;
|
||||
result = 37 * result + languageCode.hashCode;
|
||||
if (_countryCode != null)
|
||||
result = 37 * result + countryCode.hashCode;
|
||||
return result;
|
||||
}
|
||||
int get hashCode => hashValues(languageCode, scriptCode, countryCode);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
if (_countryCode == null)
|
||||
return languageCode;
|
||||
return '${languageCode}_$countryCode';
|
||||
final StringBuffer out = StringBuffer(languageCode);
|
||||
if (scriptCode != null)
|
||||
out.write('_$scriptCode');
|
||||
if (_countryCode != null)
|
||||
out.write('_$countryCode');
|
||||
return out.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,11 +12,35 @@ void main() {
|
||||
expect(const Locale('en').toString(), 'en');
|
||||
expect(const Locale('en'), new Locale('en', $null));
|
||||
expect(const Locale('en').hashCode, new Locale('en', $null).hashCode);
|
||||
expect(const Locale('en'), isNot(new Locale('en', '')));
|
||||
expect(const Locale('en').hashCode, isNot(new Locale('en', '').hashCode));
|
||||
expect(const Locale('en', 'US').toString(), 'en_US');
|
||||
expect(const Locale('iw').toString(), 'he');
|
||||
expect(const Locale('iw', 'DD').toString(), 'he_DE');
|
||||
expect(const Locale('iw', 'DD'), const Locale('he', 'DE'));
|
||||
});
|
||||
|
||||
test('Locale.fromSubtags', () {
|
||||
expect(const Locale.fromSubtags().languageCode, 'und');
|
||||
expect(const Locale.fromSubtags().scriptCode, null);
|
||||
expect(const Locale.fromSubtags().countryCode, null);
|
||||
|
||||
expect(const Locale.fromSubtags(languageCode: 'en').toString(), 'en');
|
||||
expect(const Locale.fromSubtags(languageCode: 'en').languageCode, 'en');
|
||||
expect(const Locale.fromSubtags(scriptCode: 'Latn').toString(), 'und_Latn');
|
||||
expect(const Locale.fromSubtags(scriptCode: 'Latn').scriptCode, 'Latn');
|
||||
expect(const Locale.fromSubtags(countryCode: 'US').toString(), 'und_US');
|
||||
expect(const Locale.fromSubtags(countryCode: 'US').countryCode, 'US');
|
||||
|
||||
expect(Locale.fromSubtags(languageCode: 'es', countryCode: '419').toString(), 'es_419');
|
||||
expect(Locale.fromSubtags(languageCode: 'es', countryCode: '419').languageCode, 'es');
|
||||
expect(Locale.fromSubtags(languageCode: 'es', countryCode: '419').countryCode, '419');
|
||||
|
||||
expect(Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN').toString(), 'zh_Hans_CN');
|
||||
});
|
||||
|
||||
test('Locale equality', () {
|
||||
expect(Locale.fromSubtags(languageCode: 'en'),
|
||||
isNot(Locale.fromSubtags(languageCode: 'en', scriptCode: 'Latn')));
|
||||
expect(Locale.fromSubtags(languageCode: 'en').hashCode,
|
||||
isNot(Locale.fromSubtags(languageCode: 'en', scriptCode: 'Latn').hashCode));
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user