diff --git a/.github/workflows/intl4x.yml b/.github/workflows/intl4x.yml index 83c394606..7f8611761 100644 --- a/.github/workflows/intl4x.yml +++ b/.github/workflows/intl4x.yml @@ -35,7 +35,7 @@ jobs: with: sdk: main - - run: dart pub get + - run: dart pub get --example - run: dart analyze --fatal-infos @@ -45,22 +45,19 @@ jobs: - run: dart test -p chrome - - run: dart pub get - working-directory: pkgs/intl4x/example_native - - - run: dart --enable-experiment=record-use build cli bin/example_native.dart - working-directory: pkgs/intl4x/example_native + - run: dart --enable-experiment=record-use build cli bin/example.dart + working-directory: pkgs/intl4x/example - run: tree . -a if: matrix.os == 'ubuntu-latest' - - name: Run example_native (Linux) - working-directory: pkgs/intl4x/example_native + - name: Run example (Linux) + working-directory: pkgs/intl4x/example if: matrix.os == 'ubuntu-latest' - run: ./build/cli/linux_x64/bundle/bin/example_native + run: ./build/cli/linux_x64/bundle/bin/example - - name: Print file size and check limit for example_native (Linux) - working-directory: pkgs/intl4x/example_native + - name: Print file size and check limit for example (Linux) + working-directory: pkgs/intl4x/example if: matrix.os == 'ubuntu-latest' run: | FILE_PATH="build/cli/linux_x64/bundle/lib/libicu4x.so" @@ -71,13 +68,13 @@ jobs: exit 1 fi - - name: Run example_native (Mac) - working-directory: pkgs/intl4x/example_native + - name: Run example (Mac) + working-directory: pkgs/intl4x/example if: matrix.os == 'macos-latest' - run: ./build/cli/macos_arm64/bundle/bin/example_native + run: ./build/cli/macos_arm64/bundle/bin/example - - name: Print file size and check limit for example_native (Mac) - working-directory: pkgs/intl4x/example_native + - name: Print file size and check limit for example (Mac) + working-directory: pkgs/intl4x/example if: matrix.os == 'macos-latest' run: | FILE_PATH="build/cli/macos_arm64/bundle/lib/libicu4x.dylib" @@ -88,12 +85,12 @@ jobs: exit 1 fi - - name: Run example_native (Windows) - working-directory: pkgs/intl4x/example_native + - name: Run example (Windows) + working-directory: pkgs/intl4x/example if: matrix.os == 'windows-latest' - run: .\build\cli\windows_x64\bundle\bin\example_native.exe - - name: Print file size and check limit for example_native (Windows) - working-directory: pkgs/intl4x/example_native + run: .\build\cli\windows_x64\bundle\bin\example.exe + - name: Print file size and check limit for example (Windows) + working-directory: pkgs/intl4x/example if: matrix.os == 'windows-latest' run: | $filePath = ".\build\cli\windows_x64\bundle\lib\icu4x.dll" diff --git a/pkgs/intl4x/CHANGELOG.md b/pkgs/intl4x/CHANGELOG.md index b7e93ca7f..7b62f0eb4 100644 --- a/pkgs/intl4x/CHANGELOG.md +++ b/pkgs/intl4x/CHANGELOG.md @@ -6,6 +6,7 @@ - Make package compatible to run on Windows again. - Update to `native_toolchain_c` v0.17.1. - Move logic to package:icu4x. +- Switch `DateTime` formatting API and more clean-ups. ## 0.12.2 diff --git a/pkgs/intl4x/example/.gitignore b/pkgs/intl4x/example/.gitignore index 3a8579040..4dda46f04 100644 --- a/pkgs/intl4x/example/.gitignore +++ b/pkgs/intl4x/example/.gitignore @@ -1,3 +1,4 @@ # https://dart.dev/guides/libraries/private-files # Created by `dart pub` .dart_tool/ +build/ diff --git a/pkgs/intl4x/example/README.md b/pkgs/intl4x/example/README.md deleted file mode 100644 index 857d3d530..000000000 --- a/pkgs/intl4x/example/README.md +++ /dev/null @@ -1 +0,0 @@ -An example on how to use `package:intl4x` in a browser context. \ No newline at end of file diff --git a/pkgs/intl4x/example/bin/example.dart b/pkgs/intl4x/example/bin/example.dart new file mode 100644 index 000000000..b85890ceb --- /dev/null +++ b/pkgs/intl4x/example/bin/example.dart @@ -0,0 +1,21 @@ +// Copyright (c) 2025, 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. + +import 'package:intl4x/datetime_format.dart'; +import 'package:intl4x/intl4x.dart'; + +void main(List arguments) { + const timeZone = TimeZone(name: 'Europe/Paris', offset: Duration(hours: 2)); + final dateTime = DateTime.parse('2024-07-01T08:50:07Z'); + + print(Intl().locale.toString()); + + print(Intl().dateTimeFormat().d().format(DateTime.now())); + + final withTimeZoneLong = Intl(locale: Locale.parse('en')) + .dateTimeFormat() + .ymdt(dateStyle: DateFormatStyle.full, timeStyle: TimeFormatStyle.short) + .withTimeZoneLong(); + print(withTimeZoneLong.format(dateTime, timeZone)); +} diff --git a/pkgs/intl4x/example/pubspec.yaml b/pkgs/intl4x/example/pubspec.yaml index 8faa98965..c656fb0d5 100644 --- a/pkgs/intl4x/example/pubspec.yaml +++ b/pkgs/intl4x/example/pubspec.yaml @@ -1,15 +1,14 @@ -name: example_web -description: An example on how to use the intl4x package. -version: 1.0.0 -publish_to: none +name: example +description: A sample application using intl4x. environment: - sdk: '>=3.0.0 <4.0.0' + sdk: ^3.8.0 + +publish_to: none dependencies: - intl4x: + intl4x: path: ../ - js: ^0.7.1 dev_dependencies: dart_flutter_team_lints: ^3.0.0 diff --git a/pkgs/intl4x/example/web/index.html b/pkgs/intl4x/example/web/index.html deleted file mode 100644 index 65ea647bc..000000000 --- a/pkgs/intl4x/example/web/index.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - example - - - - - - -
- - - diff --git a/pkgs/intl4x/example/web/main.dart b/pkgs/intl4x/example/web/main.dart deleted file mode 100644 index a4e621c92..000000000 --- a/pkgs/intl4x/example/web/main.dart +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2023, 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. - -import 'package:intl4x/intl4x.dart'; -import 'package:intl4x/number_format.dart'; - -void main() { - String nf(num number) => Intl() - .numberFormat(NumberFormatOptions.custom( - style: CurrencyStyle(currency: 'USD'), - digits: const Digits.withFractionDigits(minimum: 0, maximum: 2), - roundingMode: RoundingMode.halfCeil, - )) - .format(number); - print(nf(11.21)); // "$11.20" - print(nf(11.22)); // "$11.20" - print(nf(11.224)); // "$11.20" - print(nf(11.225)); // "$11.25"s - print(nf(11.23)); - //TODO: Add examples for formatting. -} diff --git a/pkgs/intl4x/example/web/styles.css b/pkgs/intl4x/example/web/styles.css deleted file mode 100644 index cc035c95c..000000000 --- a/pkgs/intl4x/example/web/styles.css +++ /dev/null @@ -1,14 +0,0 @@ -@import url(https://fonts.googleapis.com/css?family=Roboto); - -html, body { - width: 100%; - height: 100%; - margin: 0; - padding: 0; - font-family: 'Roboto', sans-serif; -} - -#output { - padding: 20px; - text-align: center; -} diff --git a/pkgs/intl4x/example_native/.gitignore b/pkgs/intl4x/example_native/.gitignore deleted file mode 100644 index 4dda46f04..000000000 --- a/pkgs/intl4x/example_native/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# https://dart.dev/guides/libraries/private-files -# Created by `dart pub` -.dart_tool/ -build/ diff --git a/pkgs/intl4x/example_native/README.md b/pkgs/intl4x/example_native/README.md deleted file mode 100644 index 3816eca3a..000000000 --- a/pkgs/intl4x/example_native/README.md +++ /dev/null @@ -1,2 +0,0 @@ -A sample command-line application with an entrypoint in `bin/`, library code -in `lib/`, and example unit test in `test/`. diff --git a/pkgs/intl4x/example_native/analysis_options.yaml b/pkgs/intl4x/example_native/analysis_options.yaml deleted file mode 100644 index dee8927aa..000000000 --- a/pkgs/intl4x/example_native/analysis_options.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# This file configures the static analysis results for your project (errors, -# warnings, and lints). -# -# This enables the 'recommended' set of lints from `package:lints`. -# This set helps identify many issues that may lead to problems when running -# or consuming Dart code, and enforces writing Dart using a single, idiomatic -# style and format. -# -# If you want a smaller set of lints you can change this to specify -# 'package:lints/core.yaml'. These are just the most critical lints -# (the recommended set includes the core lints). -# The core lints are also what is used by pub.dev for scoring packages. - -include: package:lints/recommended.yaml - -# Uncomment the following section to specify additional rules. - -# linter: -# rules: -# - camel_case_types - -# analyzer: -# exclude: -# - path/to/excluded/files/** - -# For more information about the core and recommended set of lints, see -# https://dart.dev/go/core-lints - -# For additional information about configuring this file, see -# https://dart.dev/guides/language/analysis-options diff --git a/pkgs/intl4x/example_native/bin/example_native.dart b/pkgs/intl4x/example_native/bin/example_native.dart deleted file mode 100644 index 06a4001ca..000000000 --- a/pkgs/intl4x/example_native/bin/example_native.dart +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2025, 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. - -import 'package:intl4x/datetime_format.dart'; -import 'package:intl4x/intl4x.dart'; - -void main(List arguments) { - const timeZone = TimeZone.long( - name: 'Europe/Paris', - offset: Duration(hours: 2), - ); - print(Intl().locale.toString()); - print(Intl().dateTimeFormat().time(DateTime.now(), timeZone: timeZone)); -} diff --git a/pkgs/intl4x/example_native/pubspec.yaml b/pkgs/intl4x/example_native/pubspec.yaml deleted file mode 100644 index fa40fa06c..000000000 --- a/pkgs/intl4x/example_native/pubspec.yaml +++ /dev/null @@ -1,15 +0,0 @@ -name: example_native -description: A sample application using intl4x. - -environment: - sdk: ^3.6.0 - -publish_to: none - -dependencies: - intl4x: - path: ../ - -dev_dependencies: - lints: ^6.0.0 - diff --git a/pkgs/intl4x/hook/identifiers.g.dart b/pkgs/intl4x/hook/identifiers.g.dart deleted file mode 100644 index a50ac73ba..000000000 --- a/pkgs/intl4x/hook/identifiers.g.dart +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2025, 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. - -import 'package:record_use/record_use.dart' as record_use; - -const ymdtIdentifier = record_use.Identifier( - importUri: 'package:intl4x/src/datetime_format/datetime_format.dart', - name: 'DatetimeFormatExt|ymdt', -); -const ymdIdentifier = record_use.Identifier( - importUri: 'package:intl4x/src/datetime_format/datetime_format.dart', - name: 'DatetimeFormatExt|ymd', -); -const timeIdentifier = record_use.Identifier( - importUri: 'package:intl4x/src/datetime_format/datetime_format.dart', - name: 'DatetimeFormatExt|time', -); -const timeZoneIdentifier = record_use.Identifier( - importUri: 'package:intl4x/src/datetime_format/datetime_format_options.dart', - name: 'TimeZone', -); diff --git a/pkgs/intl4x/hook/link.dart b/pkgs/intl4x/hook/link.dart deleted file mode 100644 index a78b8cd25..000000000 --- a/pkgs/intl4x/hook/link.dart +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) 2025, 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. - -import 'dart:convert'; -import 'dart:io'; - -import 'package:hooks/hooks.dart' show LinkInput, link; -import 'package:icu4x/hook.dart' show SymbolKeeper; -import 'package:intl4x/datetime_format.dart'; -import 'package:record_use/record_use.dart' as record_use; - -import 'identifiers.g.dart'; - -/// Run the linker to turn a static into a treeshaken dynamic library. -Future main(List args) async { - await link(args, (input, output) async { - // Collect the timezone symbols, as the API does a switch so that by - // default, all timezone symbols would be included. - - final usages = input.usages; - final timeZonesTimeFormat = usages?.symbolsFor( - timeIdentifier, - 'String time(DateTime datetime, {TimeZone timeZone})', - 'ZonedTimeFormatter', - ); - final timeZonesDateFormat = usages?.symbolsFor( - ymdIdentifier, - 'String ymd(DateTime datetime, {TimeZone timeZone})', - 'ZonedDateFormatter', - ); - final timeZonesDateTimeFormat = usages?.symbolsFor( - ymdtIdentifier, - 'String ymdt(DateTime datetime, {TimeZone timeZone})', - 'ZonedDateTimeFormatter', - ); - - output.registerSymbolsToBeKept(input.packageName, { - if (timeZonesTimeFormat != null) - 'icu4x_ZonedTimeFormatter_create_': timeZonesTimeFormat, - if (timeZonesDateFormat != null) - 'icu4x_ZonedDateFormatter_create_': timeZonesDateFormat, - if (timeZonesDateTimeFormat != null) - 'icu4x_ZonedDateTimeFormatter_create_': timeZonesDateTimeFormat, - }); - }); -} - -extension on record_use.RecordedUsages { - Set? symbolsFor( - record_use.Identifier id, - String signature, - String formatterName, - ) => - constArgumentsFor(id, signature) - .map( - (argument) => - ((argument.named['timeZone'] as Map)['type'] as Map)['index'] - as int, - ) - .map((index) => TimeZoneType.values[index]) - .map((timeZoneType) => timeZoneType.icuSymbol(formatterName)) - .toSet(); -} - -extension on LinkInput { - record_use.RecordedUsages? get usages { - if (recordedUsagesFile == null) { - return null; - } - final usagesContent = File.fromUri(recordedUsagesFile!).readAsStringSync(); - final usagesJson = jsonDecode(usagesContent) as Map; - return record_use.RecordedUsages.fromJson(usagesJson); - } -} - -extension on TimeZoneType { - String icuSymbol(String formatterName) => switch (this) { - TimeZoneType.long => 'icu4x_${formatterName}_create_specific_long_mv1', - TimeZoneType.short => 'icu4x_${formatterName}_create_specific_short_mv1', - TimeZoneType.shortOffset => - 'icu4x_${formatterName}_create_localized_offset_short_mv1', - TimeZoneType.longOffset => - 'icu4x_${formatterName}_create_localized_offset_long_mv1', - TimeZoneType.shortGeneric => - 'icu4x_${formatterName}_create_generic_short_mv1', - TimeZoneType.longGeneric => - 'icu4x_${formatterName}_create_generic_long_mv1', - }; -} diff --git a/pkgs/intl4x/lib/datetime_format.dart b/pkgs/intl4x/lib/datetime_format.dart index 16b76dd2f..316c5d5e6 100644 --- a/pkgs/intl4x/lib/datetime_format.dart +++ b/pkgs/intl4x/lib/datetime_format.dart @@ -2,7 +2,8 @@ // 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. -export 'src/datetime_format/datetime_format.dart' - show DateTimeFormat, DatetimeFormatExt; +export 'src/datetime_format/datetime_format.dart' show DateTimeFormatBuilder; +export 'src/datetime_format/datetime_format_impl.dart' + show DateTimeFormatter, ZonedDateTimeFormatter; export 'src/datetime_format/datetime_format_options.dart'; export 'src/options.dart'; diff --git a/pkgs/intl4x/lib/intl4x.dart b/pkgs/intl4x/lib/intl4x.dart index 4ff9d37a1..6f3b74837 100644 --- a/pkgs/intl4x/lib/intl4x.dart +++ b/pkgs/intl4x/lib/intl4x.dart @@ -42,49 +42,35 @@ typedef Icu4xKey = String; /// print(numberFormat.percent().format(0.5)); //prints 50% /// ``` class Intl { - final LocaleMatcher localeMatcher; - Locale locale; /// Construct an [Intl] instance providing the current [locale]. - Intl({Locale? locale, this.localeMatcher = LocaleMatcher.lookup}) - : locale = locale ?? findSystemLocale(); + Intl({Locale? locale}) : locale = locale ?? findSystemLocale(); CaseMapping get caseMapping => - buildCaseMapping(CaseMappingImpl.build(locale, localeMatcher)); + buildCaseMapping(CaseMappingImpl.build(locale)); Collation collation([CollationOptions options = const CollationOptions()]) => - buildCollation(CollationImpl.build(locale, options, localeMatcher)); + buildCollation(CollationImpl.build(locale, options)); - DateTimeFormat dateTimeFormat([ + DateTimeFormatBuilder dateTimeFormat([ DateTimeFormatOptions options = const DateTimeFormatOptions(), - ]) => buildDateTimeFormat( - DateTimeFormatImpl.build(locale, options, localeMatcher), - ); + ]) => buildDateTimeFormat(DateTimeFormatImpl.build(locale, options)); DisplayNames displayNames([ DisplayNamesOptions options = const DisplayNamesOptions(), - ]) => - buildDisplayNames(DisplayNamesImpl.build(locale, options, localeMatcher)); + ]) => buildDisplayNames(DisplayNamesImpl.build(locale, options)); ListFormat listFormat([ ListFormatOptions options = const ListFormatOptions(), - ]) => buildListFormat(ListFormatImpl.build(locale, options, localeMatcher)); + ]) => buildListFormat(ListFormatImpl.build(locale, options)); NumberFormat numberFormat([NumberFormatOptions? options]) => buildNumberFormat( - NumberFormatImpl.build( - locale, - options ?? NumberFormatOptions.custom(), - localeMatcher, - ), + NumberFormatImpl.build(locale, options ?? NumberFormatOptions.custom()), ); PluralRules plural([PluralRulesOptions? options]) => buildPluralRules( - PluralRulesImpl.build( - locale, - options ?? PluralRulesOptions(), - localeMatcher, - ), + PluralRulesImpl.build(locale, options ?? PluralRulesOptions()), ); } diff --git a/pkgs/intl4x/lib/src/case_mapping/case_mapping_ecma.dart b/pkgs/intl4x/lib/src/case_mapping/case_mapping_ecma.dart index d036c6001..f6b6dc81c 100644 --- a/pkgs/intl4x/lib/src/case_mapping/case_mapping_ecma.dart +++ b/pkgs/intl4x/lib/src/case_mapping/case_mapping_ecma.dart @@ -5,10 +5,9 @@ import 'dart:js_interop'; import '../locale/locale.dart'; -import '../options.dart'; import 'case_mapping_impl.dart'; -CaseMappingImpl getCaseMappingECMA(Locale locale, Null _, LocaleMatcher _) => +CaseMappingImpl getCaseMappingECMA(Locale locale, Null _) => _CaseMappingECMA.tryToBuild(locale); extension on JSString { diff --git a/pkgs/intl4x/lib/src/case_mapping/case_mapping_impl.dart b/pkgs/intl4x/lib/src/case_mapping/case_mapping_impl.dart index a62defe77..dc8bf4c46 100644 --- a/pkgs/intl4x/lib/src/case_mapping/case_mapping_impl.dart +++ b/pkgs/intl4x/lib/src/case_mapping/case_mapping_impl.dart @@ -3,7 +3,6 @@ // BSD-style license that can be found in the LICENSE file. import '../locale/locale.dart'; -import '../options.dart'; import '../utils.dart'; import 'case_mapping_stub.dart' if (dart.library.js_interop) 'case_mapping_ecma.dart'; @@ -17,12 +16,6 @@ abstract class CaseMappingImpl { String toLowerCase(String input); String toUpperCase(String input); - static CaseMappingImpl build(Locale locales, LocaleMatcher localeMatcher) => - buildFormatter( - locales, - null, - localeMatcher, - getCaseMappingECMA, - getCaseMapping4X, - ); + static CaseMappingImpl build(Locale locales) => + buildFormatter(locales, null, getCaseMappingECMA, getCaseMapping4X); } diff --git a/pkgs/intl4x/lib/src/case_mapping/case_mapping_stub.dart b/pkgs/intl4x/lib/src/case_mapping/case_mapping_stub.dart index 3cee53c07..4585f26cd 100644 --- a/pkgs/intl4x/lib/src/case_mapping/case_mapping_stub.dart +++ b/pkgs/intl4x/lib/src/case_mapping/case_mapping_stub.dart @@ -3,8 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import '../locale/locale.dart'; -import '../options.dart'; import 'case_mapping_impl.dart'; -CaseMappingImpl getCaseMappingECMA(Locale locale, Null _, LocaleMatcher _) => +CaseMappingImpl getCaseMappingECMA(Locale locale, Null _) => throw UnimplementedError('Cannot use ECMA outside of web environments.'); diff --git a/pkgs/intl4x/lib/src/collation/collation_4x.dart b/pkgs/intl4x/lib/src/collation/collation_4x.dart index afecb6159..d7119d88d 100644 --- a/pkgs/intl4x/lib/src/collation/collation_4x.dart +++ b/pkgs/intl4x/lib/src/collation/collation_4x.dart @@ -61,18 +61,16 @@ extension on CollationOptions { null => icu.CollatorStrength.tertiary, }; - final icuCaseLevel = - sensitivity == Sensitivity.caseSensitivity - ? icu.CollatorCaseLevel.on - : icu.CollatorCaseLevel.off; + final icuCaseLevel = sensitivity == Sensitivity.caseSensitivity + ? icu.CollatorCaseLevel.on + : icu.CollatorCaseLevel.off; return icu.CollatorOptions( strength: icuStrength, caseLevel: icuCaseLevel, - alternateHandling: - ignorePunctuation - ? icu.CollatorAlternateHandling.shifted - : icu.CollatorAlternateHandling.nonIgnorable, + alternateHandling: ignorePunctuation + ? icu.CollatorAlternateHandling.shifted + : icu.CollatorAlternateHandling.nonIgnorable, //TODO(mosum): maxVariable: Not supported in ECMA402 ); } diff --git a/pkgs/intl4x/lib/src/collation/collation_ecma.dart b/pkgs/intl4x/lib/src/collation/collation_ecma.dart index a0c5fd3eb..0b8ec8647 100644 --- a/pkgs/intl4x/lib/src/collation/collation_ecma.dart +++ b/pkgs/intl4x/lib/src/collation/collation_ecma.dart @@ -8,15 +8,11 @@ library; import 'dart:js_interop'; import '../locale/locale.dart'; -import '../options.dart'; import 'collation_impl.dart'; import 'collation_options.dart'; -CollationImpl getCollatorECMA( - Locale locale, - CollationOptions options, - LocaleMatcher localeMatcher, -) => CollationECMA.tryToBuild(locale, options, localeMatcher); +CollationImpl getCollatorECMA(Locale locale, CollationOptions options) => + CollationECMA.tryToBuild(locale, options); extension type Collator._(JSObject _) implements JSObject { external Collator([JSArray locales, JSAny options]); @@ -31,26 +27,17 @@ extension type Collator._(JSObject _) implements JSObject { class CollationECMA extends CollationImpl { CollationECMA(super.locale, super.options); - static CollationImpl tryToBuild( - Locale locale, - CollationOptions options, - LocaleMatcher localeMatcher, - ) { - final supportedLocales = supportedLocalesOf(localeMatcher, locale); + static CollationImpl tryToBuild(Locale locale, CollationOptions options) { + final supportedLocales = supportedLocalesOf(locale); return CollationECMA( supportedLocales.firstOrNull ?? Locale.parse('und'), options, ); } - static List supportedLocalesOf( - LocaleMatcher localeMatcher, - Locale locale, - ) { - final o = {'localeMatcher': localeMatcher.jsName}.jsify()!; + static List supportedLocalesOf(Locale locale) { return Collator.supportedLocalesOf( [locale.toLanguageTag().toJS].toJS, - o, ).toDart.whereType().map(Locale.parse).toList(); } @@ -65,14 +52,12 @@ class CollationECMA extends CollationImpl { } extension on CollationOptions { - JSAny toJsOptions() => - { - 'localeMatcher': localeMatcher.jsName, - 'usage': usage.name, - if (sensitivity != null) 'sensitivity': sensitivity!.jsName, - 'ignorePunctuation': ignorePunctuation, - 'numeric': numeric, - if (caseFirst != null) 'caseFirst': caseFirst!.jsName, - if (collation != null) 'collation': collation, - }.jsify()!; + JSAny toJsOptions() => { + 'usage': usage.name, + if (sensitivity != null) 'sensitivity': sensitivity!.jsName, + 'ignorePunctuation': ignorePunctuation, + 'numeric': numeric, + if (caseFirst != null) 'caseFirst': caseFirst!.jsName, + if (collation != null) 'collation': collation, + }.jsify()!; } diff --git a/pkgs/intl4x/lib/src/collation/collation_impl.dart b/pkgs/intl4x/lib/src/collation/collation_impl.dart index 177195ef1..37d3cb3ba 100644 --- a/pkgs/intl4x/lib/src/collation/collation_impl.dart +++ b/pkgs/intl4x/lib/src/collation/collation_impl.dart @@ -3,7 +3,6 @@ // BSD-style license that can be found in the LICENSE file. import '../locale/locale.dart'; -import '../options.dart'; import '../utils.dart'; import 'collation.dart'; import 'collation_options.dart'; @@ -13,7 +12,6 @@ import 'collation_stub_4x.dart' if (dart.library.ffi) 'collation_4x.dart'; /// Separated into a class to not clutter the public API with implementation /// details. abstract class CollationImpl { - /// The current locale, selected by the localematcher final Locale locale; final CollationOptions options; @@ -21,17 +19,8 @@ abstract class CollationImpl { /// Factory to get the correct implementation, either calling on ICU4X or the /// in-built browser implementation. - static CollationImpl build( - Locale locale, - CollationOptions options, - LocaleMatcher localeMatcher, - ) => buildFormatter( - locale, - options, - localeMatcher, - getCollatorECMA, - getCollator4X, - ); + static CollationImpl build(Locale locale, CollationOptions options) => + buildFormatter(locale, options, getCollatorECMA, getCollator4X); /// Actual implementation of the [Collation.compare] method. int compareImpl(String a, String b); diff --git a/pkgs/intl4x/lib/src/collation/collation_options.dart b/pkgs/intl4x/lib/src/collation/collation_options.dart index 33220a256..d0cd6a4b7 100644 --- a/pkgs/intl4x/lib/src/collation/collation_options.dart +++ b/pkgs/intl4x/lib/src/collation/collation_options.dart @@ -2,8 +2,6 @@ // 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. -import '../options.dart'; - class CollationOptions { final Usage usage; final Sensitivity? sensitivity; @@ -11,10 +9,8 @@ class CollationOptions { final bool? numeric; final CaseFirst? caseFirst; final String? collation; - final LocaleMatcher localeMatcher; const CollationOptions({ - this.localeMatcher = LocaleMatcher.bestfit, this.usage = Usage.sort, this.sensitivity, this.ignorePunctuation = false, @@ -30,7 +26,6 @@ class CollationOptions { bool? numeric, CaseFirst? caseFirst, String? collation, - LocaleMatcher? localeMatcher, }) { return CollationOptions( usage: usage ?? this.usage, @@ -39,7 +34,6 @@ class CollationOptions { numeric: numeric ?? this.numeric, caseFirst: caseFirst ?? this.caseFirst, collation: collation ?? this.collation, - localeMatcher: localeMatcher ?? this.localeMatcher, ); } } diff --git a/pkgs/intl4x/lib/src/collation/collation_stub.dart b/pkgs/intl4x/lib/src/collation/collation_stub.dart index 80c93257f..5d9f9f64c 100644 --- a/pkgs/intl4x/lib/src/collation/collation_stub.dart +++ b/pkgs/intl4x/lib/src/collation/collation_stub.dart @@ -7,8 +7,5 @@ import '../locale/locale.dart'; import 'collation_impl.dart'; /// Stub for the conditional import -CollationImpl getCollatorECMA( - Locale locale, - CollationOptions options, - LocaleMatcher localeMatcher, -) => throw UnimplementedError('Cannot use ECMA outside of web environments.'); +CollationImpl getCollatorECMA(Locale locale, CollationOptions options) => + throw UnimplementedError('Cannot use ECMA outside of web environments.'); diff --git a/pkgs/intl4x/lib/src/datetime_format/datetime_format.dart b/pkgs/intl4x/lib/src/datetime_format/datetime_format.dart index 2cc5ae68f..0de3c2483 100644 --- a/pkgs/intl4x/lib/src/datetime_format/datetime_format.dart +++ b/pkgs/intl4x/lib/src/datetime_format/datetime_format.dart @@ -2,10 +2,7 @@ // 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. -import 'package:meta/meta.dart'; - import '../../datetime_format.dart'; -import '../test_checker.dart'; import 'datetime_format_impl.dart'; /// `DateTime` formatting, for example: @@ -21,58 +18,46 @@ import 'datetime_format_impl.dart'; /// )) /// .format(date); // Output: '4 mat.' /// ``` -class DateTimeFormat { +class DateTimeFormatBuilder { final DateTimeFormatImpl _impl; - DateTimeFormat._(this._impl); + DateTimeFormatBuilder._(this._impl); - String d(DateTime datetime) => _format(_impl.d, datetime, _impl); - String m(DateTime datetime) => _format(_impl.m, datetime, _impl); - String y(DateTime datetime) => _format(_impl.y, datetime, _impl); - String md(DateTime datetime) => _format(_impl.md, datetime, _impl); + DateTimeFormatter d({DateFormatStyle? dateStyle}) => + _impl.d(dateStyle: dateStyle); - String ymde(DateTime datetime) => _format(_impl.ymde, datetime, _impl); + DateTimeFormatter m({DateFormatStyle? dateStyle}) => + _impl.m(dateStyle: dateStyle); - String ymdet(DateTime datetime) => _format(_impl.ymdet, datetime, _impl); + DateTimeFormatter y({DateFormatStyle? dateStyle}) => + _impl.y(dateStyle: dateStyle); - static String _format( - String Function(DateTime datetime) format, - DateTime datetime, - DateTimeFormatImpl impl, - ) { - if (isInTest) { - return '$datetime//${impl.locale}'; - } else { - return format(datetime); - } - } -} + DateTimeFormatter md({DateFormatStyle? dateStyle}) => + _impl.md(dateStyle: dateStyle); + + DateTimeFormatter ymd({DateFormatStyle? dateStyle}) => + _impl.ymd(dateStyle: dateStyle); + + DateTimeFormatter ymde({DateFormatStyle? dateStyle}) => + _impl.ymde(dateStyle: dateStyle); + + DateTimeFormatter mdt({ + DateFormatStyle? dateStyle, + TimeFormatStyle? timeStyle, + }) => _impl.mdt(timeStyle: timeStyle, dateStyle: dateStyle); -extension DatetimeFormatExt on DateTimeFormat { - @RecordUse() - String ymdt(DateTime datetime, {@mustBeConst TimeZone? timeZone}) => - DateTimeFormat._format( - (datetime) => _impl.ymdt(datetime, timeZone: timeZone), - datetime, - _impl, - ); + DateTimeFormatter ymdt({ + DateFormatStyle? dateStyle, + TimeFormatStyle? timeStyle, + }) => _impl.ymdt(timeStyle: timeStyle, dateStyle: dateStyle); - @RecordUse() - String ymd(DateTime datetime, {@mustBeConst TimeZone? timeZone}) => - DateTimeFormat._format( - (datetime) => _impl.ymd(datetime, timeZone: timeZone), - datetime, - _impl, - ); + DateTimeFormatter ymdet({ + DateFormatStyle? dateStyle, + TimeFormatStyle? timeStyle, + }) => _impl.ymdet(timeStyle: timeStyle, dateStyle: dateStyle); - @RecordUse() - String time(DateTime datetime, {@mustBeConst TimeZone? timeZone}) => - DateTimeFormat._format( - (datetime) => _impl.time(datetime, timeZone: timeZone), - datetime, - _impl, - ); + DateTimeFormatter t({TimeFormatStyle? style}) => _impl.t(style: style); } -DateTimeFormat buildDateTimeFormat(DateTimeFormatImpl impl) => - DateTimeFormat._(impl); +DateTimeFormatBuilder buildDateTimeFormat(DateTimeFormatImpl impl) => + DateTimeFormatBuilder._(impl); diff --git a/pkgs/intl4x/lib/src/datetime_format/datetime_format_4x.dart b/pkgs/intl4x/lib/src/datetime_format/datetime_format_4x.dart deleted file mode 100644 index 1e2133ec2..000000000 --- a/pkgs/intl4x/lib/src/datetime_format/datetime_format_4x.dart +++ /dev/null @@ -1,355 +0,0 @@ -// Copyright (c) 2023, 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. - -import 'package:icu4x/icu4x.dart' as icu; - -import '../../datetime_format.dart'; -import '../locale/locale.dart'; -import '../locale/locale_4x.dart'; -import '../utils.dart'; -import 'datetime_format_impl.dart'; - -DateTimeFormatImpl getDateTimeFormatter4X( - Locale locale, - DateTimeFormatOptions options, -) => DateTimeFormat4X(locale as Locale4x, options); - -class DateTimeFormat4X extends DateTimeFormatImpl { - DateTimeFormat4X(Locale4x super.locale, super.options); - - icu.Locale get localeX => (super.locale as Locale4x).get4X; - - static icu.Locale setLocaleExtensions( - icu.Locale locale, - DateTimeFormatOptions options, - ) { - final l = locale.clone(); - final calendar = options.calendar; - if (calendar != null) { - l.setUnicodeExtension('ca', calendar.jsName); - } - final clockStyle = options.clockstyle; - if (clockStyle != null) { - l.setUnicodeExtension('hc', clockStyle.hourStyleExtensionString); - } - final numberingSystem = options.numberingSystem; - if (numberingSystem != null) { - l.setUnicodeExtension('nu', numberingSystem.name); - } - return l; - } - - String _dateFormatter( - icu.DateFormatter Function( - icu.Locale locale, { - icu.DateTimeAlignment? alignment, - icu.DateTimeLength? length, - icu.YearStyle? yearStyle, - }) - f, - icu.DateTimeLength length, - DateTime datetime, - TimeZone? timeZone, - ) { - final (alignment, yearStyle, _, optionLength) = options.toX(); - final localeXwithExtensions = setLocaleExtensions(localeX, options); - final dateFormatter = f( - localeXwithExtensions, - alignment: alignment, - length: optionLength ?? length, - yearStyle: yearStyle, - ); - final (isoDate, time) = datetime.toX; - if (timeZone != null) { - final utcOffset = icu.UtcOffset.fromSeconds(timeZone.offset.inSeconds); - final timeZoneX = icu.IanaParser() - .parse(timeZone.name) - .withOffset(utcOffset) - .atDateTimeIso(isoDate, time); - - timeZoneX.setVariant(timeZone); - - final zonedDateFormatter = timeZone - .map( - (timeZone) => switch (timeZone.type) { - TimeZoneType.long => icu.ZonedDateFormatter.specificLong, - TimeZoneType.short => icu.ZonedDateFormatter.specificShort, - TimeZoneType.shortOffset => - icu.ZonedDateFormatter.localizedOffsetShort, - TimeZoneType.longOffset => - icu.ZonedDateFormatter.localizedOffsetLong, - TimeZoneType.shortGeneric => icu.ZonedDateFormatter.genericShort, - TimeZoneType.longGeneric => icu.ZonedDateFormatter.genericLong, - }, - ) - .map((constr) => constr(localeXwithExtensions, dateFormatter)); - return zonedDateFormatter.formatIso(isoDate, timeZoneX); - } else { - return dateFormatter.formatIso(isoDate); - } - } - - String _dateTimeFormatter( - icu.DateTimeFormatter Function( - icu.Locale locale, { - icu.DateTimeAlignment? alignment, - icu.TimePrecision? timePrecision, - icu.DateTimeLength? length, - icu.YearStyle? yearStyle, - }) - f, - icu.DateTimeLength length, - DateTime datetime, - TimeZone? timeZone, - ) { - final (alignment, yearStyle, timePrecision, optionLength) = options.toX(); - final dateTimeFormatter = f( - setLocaleExtensions(localeX, options), - alignment: alignment, - length: optionLength ?? length, - timePrecision: timePrecision, - yearStyle: yearStyle, - ); - final (isoDate, time) = datetime.toX; - if (timeZone != null) { - final utcOffset = icu.UtcOffset.fromSeconds(timeZone.offset.inSeconds); - final timeZoneX = icu.IanaParser() - .parse(timeZone.name) - .withOffset(utcOffset) - .atDateTimeIso(isoDate, time); - - timeZoneX.setVariant(timeZone); - - final zonedDateFormatter = switch (timeZone.type) { - TimeZoneType.long => icu.ZonedDateTimeFormatter.specificLong, - TimeZoneType.short => icu.ZonedDateTimeFormatter.specificShort, - TimeZoneType.shortOffset => - icu.ZonedDateTimeFormatter.localizedOffsetShort, - TimeZoneType.longOffset => - icu.ZonedDateTimeFormatter.localizedOffsetLong, - TimeZoneType.shortGeneric => icu.ZonedDateTimeFormatter.genericShort, - TimeZoneType.longGeneric => icu.ZonedDateTimeFormatter.genericLong, - }(localeX, dateTimeFormatter); - return zonedDateFormatter.formatIso(isoDate, time, timeZoneX); - } else { - return dateTimeFormatter.formatIso(isoDate, time); - } - } - - @override - String d(DateTime datetime) { - final (alignment, _, _, optionLength) = options.toX(); - return icu.DateFormatter.d( - setLocaleExtensions(localeX, options), - alignment: alignment, - length: optionLength ?? icu.DateTimeLength.short, - ).formatIso(datetime.toX.$1); - } - - @override - String m(DateTime datetime) { - final (alignment, _, _, optionLength) = options.toX(); - return icu.DateFormatter.m( - setLocaleExtensions(localeX, options), - alignment: alignment, - length: optionLength ?? icu.DateTimeLength.short, - ).formatIso(datetime.toX.$1); - } - - @override - String md(DateTime datetime) { - final (alignment, _, _, optionLength) = options.toX(); - return icu.DateFormatter.md( - setLocaleExtensions(localeX, options), - alignment: alignment, - length: optionLength ?? icu.DateTimeLength.short, - ).formatIso(datetime.toX.$1); - } - - @override - String y(DateTime datetime, {TimeZone? timeZone}) => _dateFormatter( - icu.DateFormatter.y, - icu.DateTimeLength.long, - datetime, - timeZone, - ); - - @override - String ymd(DateTime datetime, {TimeZone? timeZone}) => _dateFormatter( - icu.DateFormatter.ymd, - icu.DateTimeLength.short, - datetime, - timeZone, - ); - - @override - String ymde(DateTime datetime, {TimeZone? timeZone}) => _dateFormatter( - icu.DateFormatter.ymde, - icu.DateTimeLength.short, - datetime, - timeZone, - ); - - @override - String ymdt(DateTime datetime, {TimeZone? timeZone}) => _dateTimeFormatter( - icu.DateTimeFormatter.ymdt, - icu.DateTimeLength.short, - datetime, - timeZone, - ); - - @override - String time(DateTime datetime, {TimeZone? timeZone}) { - final (alignment, yearStyle, timePrecision, length) = options.toX( - timePrecisionDefault: - options.timestyle == TimeStyle.twodigit - ? icu.TimePrecision.minute - : null, - ); - final localeXwithExtensions = setLocaleExtensions(localeX, options); - final (_, time) = datetime.toX; - if (timeZone != null) { - final utcOffset = icu.UtcOffset.fromSeconds(timeZone.offset.inSeconds); - final correctedDateTime = datetime.add( - Duration(seconds: utcOffset.seconds), - ); - final (isoDate, time) = correctedDateTime.toX; - final timeZoneX = icu.IanaParser() - .parse(timeZone.name) - .withOffset(utcOffset) - .atDateTimeIso(isoDate, time); - - timeZoneX.setVariant(timeZone); - - final zonedTimeFormatter = switch (timeZone.type) { - TimeZoneType.long => icu.ZonedTimeFormatter.specificLong, - TimeZoneType.short => icu.ZonedTimeFormatter.specificShort, - TimeZoneType.shortOffset => icu.ZonedTimeFormatter.localizedOffsetShort, - TimeZoneType.longOffset => icu.ZonedTimeFormatter.localizedOffsetLong, - TimeZoneType.shortGeneric => icu.ZonedTimeFormatter.genericShort, - TimeZoneType.longGeneric => icu.ZonedTimeFormatter.genericLong, - }(localeXwithExtensions); - return zonedTimeFormatter.format(time, timeZoneX); - } else { - return icu.TimeFormatter( - localeXwithExtensions, - alignment: alignment, - length: length ?? icu.DateTimeLength.short, - timePrecision: timePrecision, - ).format(time); - } - } - - @override - String ymdet(DateTime datetime, {TimeZone? timeZone}) { - final (alignment, yearStyle, timePrecision, length) = options.toX(); - final localeXwithExtensions = setLocaleExtensions(localeX, options); - final (isoDate, time) = datetime.toX; - final dateTimeFormatter = icu.DateTimeFormatter.ymdet( - localeXwithExtensions, - alignment: alignment, - timePrecision: timePrecision, - yearStyle: yearStyle, - length: length ?? icu.DateTimeLength.short, - ); - if (timeZone != null) { - final utcOffset = icu.UtcOffset.fromSeconds(timeZone.offset.inSeconds); - final correctedDateTime = datetime.add( - Duration(seconds: utcOffset.seconds), - ); - final (isoDate, time) = correctedDateTime.toX; - final timeZoneX = icu.IanaParser() - .parse(timeZone.name) - .withOffset(utcOffset) - .atDateTimeIso(isoDate, time); - - timeZoneX.setVariant(timeZone); - - final zonedDateTimeFormatter = switch (timeZone.type) { - TimeZoneType.long => icu.ZonedDateTimeFormatter.specificLong, - TimeZoneType.short => icu.ZonedDateTimeFormatter.specificShort, - TimeZoneType.shortOffset => - icu.ZonedDateTimeFormatter.localizedOffsetShort, - TimeZoneType.longOffset => - icu.ZonedDateTimeFormatter.localizedOffsetLong, - TimeZoneType.shortGeneric => icu.ZonedDateTimeFormatter.genericShort, - TimeZoneType.longGeneric => icu.ZonedDateTimeFormatter.genericLong, - }(localeXwithExtensions, dateTimeFormatter); - return zonedDateTimeFormatter.formatIso(isoDate, time, timeZoneX); - } else { - return dateTimeFormatter.formatIso(isoDate, time); - } - } -} - -extension on icu.TimeZoneInfo { - void setVariant(TimeZone timeZone) { - final success = inferVariant(icu.VariantOffsetsCalculator()); - if (!success) { - throw ArgumentError( - ''' -The variant of ${timeZone.name} with offset ${timeZone.offset} could not be inferred''', - ); - } - } -} - -extension on DateTime { - (icu.IsoDate, icu.Time) get toX { - final isoDate = icu.IsoDate(year, month, day); - final time = icu.Time( - hour, - minute, - second, - millisecond * 1_000_000 + microsecond * 1_000, - ); - return (isoDate, time); - } -} - -extension on DateTimeFormatOptions { - ( - icu.DateTimeAlignment?, - icu.YearStyle?, - icu.TimePrecision?, - icu.DateTimeLength?, - ) - toX({icu.TimePrecision? timePrecisionDefault}) { - icu.TimePrecision? timePrecision; - if (fractionalSecondDigits != null) { - timePrecision = icu.TimePrecision.fromSubsecondDigits( - fractionalSecondDigits!, - ); - } else { - timePrecision = switch (timeFormatStyle) { - null => timePrecisionDefault ?? icu.TimePrecision.hour, - TimeFormatStyle.full => icu.TimePrecision.second, - TimeFormatStyle.medium => icu.TimePrecision.second, - TimeFormatStyle.short => icu.TimePrecision.minute, - }; - } - final dateTimeAlignment = - timestyle == TimeStyle.twodigit - ? icu.DateTimeAlignment.column - : icu.DateTimeAlignment.auto; - return ( - dateTimeAlignment, - switch (dateFormatStyle) { - null => icu.YearStyle.full, - DateFormatStyle.full => icu.YearStyle.auto, - DateFormatStyle.long => icu.YearStyle.auto, - DateFormatStyle.medium => icu.YearStyle.auto, - DateFormatStyle.short => icu.YearStyle.auto, - }, - timePrecision, - switch (dateFormatStyle) { - DateFormatStyle.full => icu.DateTimeLength.long, - DateFormatStyle.long => icu.DateTimeLength.long, - DateFormatStyle.medium => icu.DateTimeLength.medium, - DateFormatStyle.short => icu.DateTimeLength.short, - null => null, - }, - ); - } -} diff --git a/pkgs/intl4x/lib/src/datetime_format/datetime_format_ecma.dart b/pkgs/intl4x/lib/src/datetime_format/datetime_format_ecma.dart index 400b8bf68..48aad102b 100644 --- a/pkgs/intl4x/lib/src/datetime_format/datetime_format_ecma.dart +++ b/pkgs/intl4x/lib/src/datetime_format/datetime_format_ecma.dart @@ -12,224 +12,395 @@ import 'datetime_format_options.dart'; DateTimeFormatImpl getDateTimeFormatterECMA( Locale locale, DateTimeFormatOptions options, - LocaleMatcher localeMatcher, -) => _DateTimeFormatECMA.tryToBuild(locale, options, localeMatcher); +) => _DateTimeFormatECMA.tryToBuild(locale, options); -@JS('Intl.DateTimeFormat') -extension type DateTimeFormat._(JSObject _) implements JSObject { - external factory DateTimeFormat([JSArray locale, JSAny options]); - external String format(JSAny num); +class DateTimeJSOptions { + final TimeStyle? year; + final TimeStyle? month; + final TimeStyle? day; + final TimeStyle? hour; + final TimeStyle? minute; + final TimeStyle? second; + final TimeZone? timeZone; + final TimeZoneType? timeZoneType; + final Style? weekday; - external static JSArray supportedLocalesOf( - JSArray listOfLocales, [ - JSAny options, - ]); -} + DateTimeJSOptions({ + this.year, + this.month, + this.day, + this.hour, + this.minute, + this.second, + this.timeZone, + this.timeZoneType, + this.weekday, + }); -extension type Date._(JSObject _) implements JSObject { - external factory Date( - int year, - int monthIndex, - int day, - int hours, - int minutes, - int seconds, - int milliseconds, + DateTimeJSOptions copyWith({ + TimeStyle? year, + TimeStyle? month, + TimeStyle? day, + TimeStyle? hour, + TimeStyle? minute, + TimeStyle? second, + TimeZone? timeZone, + TimeZoneType? timeZoneType, + Style? weekday, + }) => DateTimeJSOptions( + day: day ?? this.day, + hour: hour ?? this.hour, + minute: minute ?? this.minute, + month: month ?? this.month, + second: second ?? this.second, + timeZone: timeZone ?? this.timeZone, + timeZoneType: timeZoneType ?? this.timeZoneType, + weekday: weekday ?? this.weekday, + year: year ?? this.year, ); +} - external factory Date.fromTimeStamp(int timeStamp); +class FormatterECMA extends FormatterImpl { + final Locale locale; + final DateTimeFormatOptions options; + final DateTimeJSOptions optionsJS; + final DateTimeFormatImpl impl; + final DateTimeFormat dateTimeFormat; + final TimeFormatStyle? timeStyle; + final DateFormatStyle? dateStyle; - // ignore: non_constant_identifier_names - external static int UTC( - int year, - int monthIndex, - int day, - int hours, - int minutes, - int seconds, - int milliseconds, - ); + FormatterECMA( + this.impl, + this.optionsJS, + this.locale, + this.options, { + required this.timeStyle, + required this.dateStyle, + }) : dateTimeFormat = DateTimeFormat( + [locale.toLanguageTag().toJS].toJS, + options.toJsMap(optionsJS, timeStyle, dateStyle), + ), + super(impl); + + @override + String formatInternal(DateTime datetime) => + dateTimeFormat.format(datetime.js); + + @override + ZonedDateTimeFormatter withTimeZoneLong() => + FormatterZonedECMA(TimeZoneType.long, this); + + @override + ZonedDateTimeFormatter withTimeZoneLongGeneric() => + FormatterZonedECMA(TimeZoneType.longGeneric, this); + + @override + ZonedDateTimeFormatter withTimeZoneLongOffset() => + FormatterZonedECMA(TimeZoneType.longOffset, this); + + @override + ZonedDateTimeFormatter withTimeZoneShort() => + FormatterZonedECMA(TimeZoneType.short, this); + + @override + ZonedDateTimeFormatter withTimeZoneShortGeneric() => + FormatterZonedECMA(TimeZoneType.shortGeneric, this); + + @override + ZonedDateTimeFormatter withTimeZoneShortOffset() => + FormatterZonedECMA(TimeZoneType.shortOffset, this); } -class _DateTimeFormatECMA extends DateTimeFormatImpl { - _DateTimeFormatECMA(super.locale, super.options); +class FormatterZonedECMA extends FormatterZonedImpl { + final FormatterECMA formatter; - static DateTimeFormatImpl tryToBuild( - Locale locale, - DateTimeFormatOptions options, - LocaleMatcher localeMatcher, - ) { - final supportedLocales = supportedLocalesOf(localeMatcher, locale); - return _DateTimeFormatECMA( - supportedLocales.firstOrNull ?? Locale.parse('und'), - options, - ); - } + FormatterZonedECMA(TimeZoneType timeZoneType, this.formatter) + : super(formatter.impl, timeZoneType); - static List supportedLocalesOf( - LocaleMatcher localeMatcher, - Locale locale, + static DateTimeFormat createDateTimeFormat( + FormatterECMA formatter, + TimeZoneType timeZoneType, + TimeZone timeZone, ) { - final o = {'localeMatcher': localeMatcher.jsName}.jsify()!; - return DateTimeFormat.supportedLocalesOf( - [locale.toLanguageTag().toJS].toJS, - o, - ).toDart.whereType().map(Locale.parse).toList(); + final localeJS = [formatter.locale.toLanguageTag().toJS].toJS; + return DateTimeFormat( + localeJS, + formatter.options.toJsMap( + formatter.optionsJS.copyWith( + timeZone: timeZone, + timeZoneType: timeZoneType, + ), + formatter.timeStyle, + formatter.dateStyle, + ), + ); } @override - String d(DateTime datetime) => - _format(datetime: datetime, year: null, day: _timeStyle, month: null); + String formatInternal(DateTime datetime, TimeZone timeZone) => + createDateTimeFormat( + formatter, + timeZoneType, + timeZone, + ).format(datetime.subtract(timeZone.offset).jsUtc); +} + +class _DateTimeFormatECMA extends DateTimeFormatImpl { + _DateTimeFormatECMA(super.locale, super.options); + + TimeStyle? _timeStyle( + TimeFormatStyle? timeStyle, + DateFormatStyle? dateStyle, + ) => dateStyle != null || timeStyle != null + ? null + : (options.timestyle ?? TimeStyle.numeric); + + TimeStyle? _timeStyleOrNull( + TimeFormatStyle? timeStyle, + DateFormatStyle? dateStyle, + ) => dateStyle != null || timeStyle != null ? null : options.timestyle; @override - String m(DateTime datetime) => - _format(datetime: datetime, year: null, day: null, month: _timeStyle); + FormatterImpl d({DateFormatStyle? dateStyle}) => FormatterECMA( + this, + DateTimeJSOptions( + year: null, + day: _timeStyle(null, dateStyle), + month: null, + ), + locale, + options, + timeStyle: null, + dateStyle: dateStyle, + ); @override - String md(DateTime datetime) => _format( - datetime: datetime, - year: null, - day: _timeStyle, - month: _timeStyle, + FormatterImpl m({DateFormatStyle? dateStyle}) => FormatterECMA( + this, + DateTimeJSOptions( + year: null, + day: null, + month: _timeStyle(null, dateStyle), + ), + locale, + options, + timeStyle: null, + dateStyle: dateStyle, ); @override - String y(DateTime datetime) => _format(datetime: datetime, year: _timeStyle); + FormatterImpl md({DateFormatStyle? dateStyle}) => FormatterECMA( + this, + DateTimeJSOptions( + year: null, + day: _timeStyle(null, dateStyle), + month: _timeStyle(null, dateStyle), + ), + locale, + options, + timeStyle: null, + dateStyle: dateStyle, + ); @override - String ymd(DateTime datetime, {TimeZone? timeZone}) => _format( - datetime: datetime, - year: _timeStyle, - month: _timeStyle, - day: _timeStyle, - timeZone: timeZone, + FormatterImpl t({TimeFormatStyle? style}) => FormatterECMA( + this, + DateTimeJSOptions( + hour: _timeStyle(style, null), + minute: _timeStyleOrNull(style, null), + ), + locale, + options, + timeStyle: style, + dateStyle: null, ); @override - String ymde(DateTime datetime) => _format(datetime: datetime); + FormatterImpl y({DateFormatStyle? dateStyle}) => FormatterECMA( + this, + DateTimeJSOptions(year: _timeStyle(null, dateStyle)), + locale, + options, + timeStyle: null, + dateStyle: dateStyle, + ); @override - String ymdt(DateTime datetime, {TimeZone? timeZone}) => _format( - datetime: datetime, - hour: _timeStyle, - minute: _timeStyleOrNull, - second: null, - year: _timeStyle, - month: _timeStyle, - day: _timeStyle, - timeZone: timeZone, + FormatterImpl ymd({DateFormatStyle? dateStyle}) => FormatterECMA( + this, + DateTimeJSOptions( + year: _timeStyle(null, dateStyle), + month: _timeStyle(null, dateStyle), + day: _timeStyle(null, dateStyle), + ), + locale, + options, + timeStyle: null, + dateStyle: dateStyle, ); @override - String time(DateTime datetime, {TimeZone? timeZone}) => _format( - datetime: datetime, - year: null, - hour: _timeStyle, - minute: _timeStyleOrNull, - second: null, - timeZone: timeZone, + FormatterImpl ymde({DateFormatStyle? dateStyle}) => FormatterECMA( + this, + DateTimeJSOptions(), + locale, + options, + timeStyle: null, + dateStyle: dateStyle, ); - TimeStyle? get _timeStyle => - options.dateFormatStyle != null || options.timeFormatStyle != null - ? null - : (options.timestyle ?? TimeStyle.numeric); - - TimeStyle? get _timeStyleOrNull => - options.dateFormatStyle != null || options.timeFormatStyle != null - ? null - : options.timestyle; - - @override - String ymdet(DateTime datetime) => _format( - datetime: datetime, - hour: _timeStyle, - minute: _timeStyleOrNull, - second: null, - year: _timeStyle, - month: _timeStyle, - day: _timeStyle, - weekday: Style.short, + @override + FormatterImpl ymdet({ + DateFormatStyle? dateStyle, + TimeFormatStyle? timeStyle, + }) => FormatterECMA( + this, + DateTimeJSOptions( + hour: _timeStyle(timeStyle, dateStyle), + minute: _timeStyleOrNull(timeStyle, dateStyle), + year: _timeStyle(timeStyle, dateStyle), + month: _timeStyle(timeStyle, dateStyle), + day: _timeStyle(timeStyle, dateStyle), + weekday: Style.short, + ), + locale, + options, + timeStyle: timeStyle, + dateStyle: dateStyle, ); - String _format({ - TimeStyle? year, - TimeStyle? month, - TimeStyle? day, - TimeStyle? hour, - TimeStyle? minute, - TimeStyle? second, - TimeZone? timeZone, - Style? weekday, - required DateTime datetime, - }) { - final correctedDatetime = - timeZone == null - ? datetime.toJs() - : datetime.subtract(timeZone.offset).toJsUtc(); + @override + FormatterImpl mdt({DateFormatStyle? dateStyle, TimeFormatStyle? timeStyle}) => + FormatterECMA( + this, + DateTimeJSOptions( + hour: _timeStyle(timeStyle, dateStyle), + minute: _timeStyleOrNull(timeStyle, dateStyle), + month: _timeStyle(timeStyle, dateStyle), + day: _timeStyle(timeStyle, dateStyle), + ), + locale, + options, + timeStyle: timeStyle, + dateStyle: dateStyle, + ); - return DateTimeFormat( + @override + FormatterImpl ymdt({ + DateFormatStyle? dateStyle, + TimeFormatStyle? timeStyle, + }) => FormatterECMA( + this, + DateTimeJSOptions( + hour: _timeStyle(timeStyle, dateStyle), + minute: _timeStyleOrNull(timeStyle, dateStyle), + year: _timeStyle(timeStyle, dateStyle), + month: _timeStyle(timeStyle, dateStyle), + day: _timeStyle(timeStyle, dateStyle), + ), + locale, + options, + timeStyle: timeStyle, + dateStyle: dateStyle, + ); + + static List supportedLocalesOf(Locale locale) { + return DateTimeFormat.supportedLocalesOf( [locale.toLanguageTag().toJS].toJS, - options.toJsOptions( - year: year, - month: month, - day: day, - hour: hour, - minute: minute, - second: second, - timeZone: timeZone, - weekday: weekday, - ), - ).format(correctedDatetime); + ).toDart.whereType().map(Locale.parse).toList(); + } + + static DateTimeFormatImpl tryToBuild( + Locale locale, + DateTimeFormatOptions options, + ) { + final supportedLocales = supportedLocalesOf(locale); + return _DateTimeFormatECMA( + supportedLocales.firstOrNull ?? Locale.parse('und'), + options, + ); } } +extension type Date._(JSObject _) implements JSObject { + external factory Date( + int year, + int monthIndex, + int day, + int hours, + int minutes, + int seconds, + int milliseconds, + ); + + external factory Date.fromTimeStamp(int timeStamp); + + // ignore: non_constant_identifier_names + external static int UTC( + int year, + int monthIndex, + int day, + int hours, + int minutes, + int seconds, + int milliseconds, + ); +} + +@JS('Intl.DateTimeFormat') +extension type DateTimeFormat._(JSObject _) implements JSObject { + external factory DateTimeFormat([JSArray locale, JSAny options]); + external String format(JSAny num); + + external static JSArray supportedLocalesOf( + JSArray listOfLocales, [ + JSAny options, + ]); +} + extension on DateTime { - Date toJs() => Date(year, month - 1, day, hour, minute, second, millisecond); + Date get js => Date(year, month - 1, day, hour, minute, second, millisecond); - Date toJsUtc() => Date.fromTimeStamp( + Date get jsUtc => Date.fromTimeStamp( Date.UTC(year, month - 1, day, hour, minute, second, millisecond), ); } extension on DateTimeFormatOptions { - JSAny toJsOptions({ - TimeStyle? year, - TimeStyle? month, - TimeStyle? day, - TimeStyle? hour, - TimeStyle? minute, - TimeStyle? second, - TimeZone? timeZone, - Style? weekday, - }) => - { - 'localeMatcher': localeMatcher.jsName, - if (dateFormatStyle != null) 'dateStyle': dateFormatStyle!.name, - if (timeFormatStyle != null) 'timeStyle': timeFormatStyle!.name, - if (calendar != null) 'calendar': calendar!.jsName, - if (dayPeriod != null) 'dayPeriod': dayPeriod!.name, - if (numberingSystem != null) 'numberingSystem': numberingSystem!.name, - if (timeZone != null) ...{ - 'timeZone': timeZone.name, - 'timeZoneName': timeZone.type.name, - }, - if (clockstyle != null) ...{ - 'hour12': clockstyle!.is12Hour, - 'hourCycle': clockstyle!.hourStyleExtensionString, - }, - if (weekday != null && dateFormatStyle == null) 'weekday': weekday.name, - if (era != null && dateFormatStyle == null) 'era': era!.name, - if (year != null && dateFormatStyle == null) 'year': year.jsName, - if (month != null && dateFormatStyle == null) 'month': month.jsName, - if (day != null && dateFormatStyle == null) 'day': day.jsName, - if (hour != null && timeFormatStyle == null) 'hour': hour.jsName, - if (minute != null && timeFormatStyle == null) 'minute': minute.jsName, - if (second != null && timeFormatStyle == null) 'second': second.jsName, - if (fractionalSecondDigits != null) - 'fractionalSecondDigits': fractionalSecondDigits!, - 'formatMatcher': formatMatcher.jsName, - }.jsify()!; + JSAny toJsMap( + DateTimeJSOptions options, + TimeFormatStyle? timeStyle, + DateFormatStyle? dateStyle, + ) => { + if (dateStyle != null) 'dateStyle': dateStyle.name, + if (timeStyle != null) 'timeStyle': timeStyle.name, + if (calendar != null) 'calendar': calendar!.jsName, + if (dayPeriod != null) 'dayPeriod': dayPeriod!.name, + if (numberingSystem != null) 'numberingSystem': numberingSystem!.name, + if (options.timeZone != null) ...{ + 'timeZone': options.timeZone!.name, + 'timeZoneName': options.timeZoneType!.name, + }, + if (clockstyle != null) ...{ + 'hour12': clockstyle!.is12Hour, + 'hourCycle': clockstyle!.hourStyleExtensionString, + }, + if (options.weekday != null && dateStyle == null) + 'weekday': options.weekday!.name, + if (era != null && dateStyle == null) 'era': era!.name, + if (options.year != null && dateStyle == null) 'year': options.year!.jsName, + if (options.month != null && dateStyle == null) + 'month': options.month!.jsName, + if (options.day != null && dateStyle == null) 'day': options.day!.jsName, + if (options.hour != null && timeStyle == null) 'hour': options.hour!.jsName, + if (options.minute != null && timeStyle == null) + 'minute': options.minute!.jsName, + if (options.second != null && timeStyle == null) + 'second': options.second!.jsName, + if (fractionalSecondDigits != null) + 'fractionalSecondDigits': fractionalSecondDigits!, + 'formatMatcher': formatMatcher.jsName, + }.jsify()!; } extension on ClockStyle { diff --git a/pkgs/intl4x/lib/src/datetime_format/datetime_format_impl.dart b/pkgs/intl4x/lib/src/datetime_format/datetime_format_impl.dart index 1fbba4448..496925a4c 100644 --- a/pkgs/intl4x/lib/src/datetime_format/datetime_format_impl.dart +++ b/pkgs/intl4x/lib/src/datetime_format/datetime_format_impl.dart @@ -3,13 +3,13 @@ // BSD-style license that can be found in the LICENSE file. import '../locale/locale.dart'; -import '../options.dart'; +import '../test_checker.dart' show isInTest; import '../utils.dart'; import 'datetime_format_options.dart'; import 'datetime_format_stub.dart' if (dart.library.js_interop) 'datetime_format_ecma.dart'; import 'datetime_format_stub_4x.dart' - if (dart.library.ffi) 'datetime_format_4x.dart'; + if (dart.library.ffi) 'icu4x/datetime_format_4x.dart'; /// This is an intermediate to defer to the actual implementations of /// datetime formatting. @@ -22,22 +22,74 @@ abstract class DateTimeFormatImpl { static DateTimeFormatImpl build( Locale locale, DateTimeFormatOptions options, - LocaleMatcher localeMatcher, ) => buildFormatter( locale, options, - localeMatcher, getDateTimeFormatterECMA, getDateTimeFormatter4X, ); - String d(DateTime datetime); - String m(DateTime datetime); - String y(DateTime datetime); - String md(DateTime datetime); - String ymd(DateTime datetime, {TimeZone? timeZone}); - String ymde(DateTime datetime); - String ymdt(DateTime datetime, {TimeZone? timeZone}); - String ymdet(DateTime datetime); - String time(DateTime datetime, {TimeZone? timeZone}); + FormatterImpl d({DateFormatStyle? dateStyle}); + FormatterImpl m({DateFormatStyle? dateStyle}); + FormatterImpl y({DateFormatStyle? dateStyle}); + FormatterImpl md({DateFormatStyle? dateStyle}); + FormatterImpl ymd({DateFormatStyle? dateStyle}); + FormatterImpl ymde({DateFormatStyle? dateStyle}); + FormatterImpl mdt({DateFormatStyle? dateStyle, TimeFormatStyle? timeStyle}); + FormatterImpl ymdt({DateFormatStyle? dateStyle, TimeFormatStyle? timeStyle}); + FormatterImpl ymdet({DateFormatStyle? dateStyle, TimeFormatStyle? timeStyle}); + FormatterImpl t({TimeFormatStyle? style}); +} + +abstract class FormatterImpl extends DateTimeFormatter { + final DateTimeFormatImpl _impl; + + FormatterImpl(this._impl); + + String formatInternal(DateTime datetime); + + @override + String format(DateTime datetime) { + if (isInTest) { + return '$datetime//${_impl.locale}'; + } else { + return formatInternal(datetime); + } + } +} + +abstract class FormatterZonedImpl extends ZonedDateTimeFormatter { + final DateTimeFormatImpl _impl; + final TimeZoneType timeZoneType; + + String formatInternal(DateTime datetime, TimeZone timeZone); + + FormatterZonedImpl(this._impl, this.timeZoneType); + + @override + String format(DateTime datetime, TimeZone timeZone) { + if (isInTest) { + return '$datetime//${_impl.locale}'; + } else { + return formatInternal(datetime, timeZone); + } + } +} + +/// A base class for formatters that can format a [DateTime] into a string. +sealed class DateTimeFormatter { + String format(DateTime datetime); + + ZonedDateTimeFormatter withTimeZoneShort(); + ZonedDateTimeFormatter withTimeZoneLong(); + ZonedDateTimeFormatter withTimeZoneShortOffset(); + ZonedDateTimeFormatter withTimeZoneLongOffset(); + ZonedDateTimeFormatter withTimeZoneShortGeneric(); + ZonedDateTimeFormatter withTimeZoneLongGeneric(); +} + +/// A base class for formatters that can format a [DateTime] and [TimeZone] into +/// a string. +sealed class ZonedDateTimeFormatter { + String format(DateTime datetime, TimeZone timeZone); } diff --git a/pkgs/intl4x/lib/src/datetime_format/datetime_format_options.dart b/pkgs/intl4x/lib/src/datetime_format/datetime_format_options.dart index e55cc223e..2db639d03 100644 --- a/pkgs/intl4x/lib/src/datetime_format/datetime_format_options.dart +++ b/pkgs/intl4x/lib/src/datetime_format/datetime_format_options.dart @@ -2,8 +2,6 @@ // 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. -import 'package:meta/meta.dart' show RecordUse; - import '../options.dart'; typedef WeekDayStyle = Style; @@ -12,12 +10,6 @@ typedef EraStyle = Style; /// DateTime formatting functionality of the browser. class DateTimeFormatOptions { - /// The date formatting style. - final DateFormatStyle? dateFormatStyle; - - /// The time formatting style. - final TimeFormatStyle? timeFormatStyle; - final Calendar? calendar; /// The formatting style used for day periods - only used when the @@ -35,11 +27,8 @@ class DateTimeFormatOptions { /// The localized representation of the time zone name. final FormatMatcher formatMatcher; - final LocaleMatcher localeMatcher; const DateTimeFormatOptions({ - this.dateFormatStyle, - this.timeFormatStyle, this.calendar, this.dayPeriod, this.numberingSystem, @@ -48,12 +37,9 @@ class DateTimeFormatOptions { this.timestyle, this.fractionalSecondDigits, this.formatMatcher = FormatMatcher.bestfit, - this.localeMatcher = LocaleMatcher.bestfit, }); DateTimeFormatOptions copyWith({ - DateFormatStyle? dateFormatStyle, - TimeFormatStyle? timeFormatStyle, Calendar? calendar, DayPeriod? dayPeriod, NumberingSystem? numberingSystem, @@ -63,11 +49,8 @@ class DateTimeFormatOptions { TimeStyle? timestyle, int? fractionalSecondDigits, FormatMatcher? formatMatcher, - LocaleMatcher? localeMatcher, }) { return DateTimeFormatOptions( - dateFormatStyle: dateFormatStyle ?? this.dateFormatStyle, - timeFormatStyle: timeFormatStyle ?? this.timeFormatStyle, calendar: calendar ?? this.calendar, dayPeriod: dayPeriod ?? this.dayPeriod, numberingSystem: numberingSystem ?? this.numberingSystem, @@ -77,7 +60,6 @@ class DateTimeFormatOptions { fractionalSecondDigits: fractionalSecondDigits ?? this.fractionalSecondDigits, formatMatcher: formatMatcher ?? this.formatMatcher, - localeMatcher: localeMatcher ?? this.localeMatcher, ); } } @@ -97,7 +79,7 @@ enum ClockStyle { } } -enum TimeFormatStyle { full, medium, short } +enum TimeFormatStyle { full, long, medium, short } enum DateFormatStyle { full, long, medium, short } @@ -148,37 +130,11 @@ enum TimeStyle { const TimeStyle([this._jsName]); } -@RecordUse() final class TimeZone { final String name; - final TimeZoneType type; final Duration offset; - final bool inferVariant; - - const TimeZone.short({required this.name, required this.offset}) - : type = TimeZoneType.short, - inferVariant = true; - - const TimeZone.long({required this.name, required this.offset}) - : type = TimeZoneType.long, - inferVariant = true; - - const TimeZone.shortOffset({required this.name, required this.offset}) - : type = TimeZoneType.shortOffset, - inferVariant = true; - - const TimeZone.longOffset({required this.name, required this.offset}) - : type = TimeZoneType.longOffset, - inferVariant = true; - - const TimeZone.shortGeneric({required this.name, required this.offset}) - : type = TimeZoneType.shortGeneric, - inferVariant = false; - - const TimeZone.longGeneric({required this.name, required this.offset}) - : type = TimeZoneType.longGeneric, - inferVariant = false; + const TimeZone({required this.name, required this.offset}); } enum TimeZoneType { @@ -191,7 +147,7 @@ enum TimeZoneType { /// Example: `GMT-8` shortOffset, - /// Example: `GMT-0800` + /// Example: `GMT-08:00` longOffset, /// Example: `PT` diff --git a/pkgs/intl4x/lib/src/datetime_format/datetime_format_stub.dart b/pkgs/intl4x/lib/src/datetime_format/datetime_format_stub.dart index 47b1a7854..7b5b30610 100644 --- a/pkgs/intl4x/lib/src/datetime_format/datetime_format_stub.dart +++ b/pkgs/intl4x/lib/src/datetime_format/datetime_format_stub.dart @@ -9,5 +9,4 @@ import 'datetime_format_impl.dart'; DateTimeFormatImpl getDateTimeFormatterECMA( Locale locales, DateTimeFormatOptions options, - LocaleMatcher localeMatcher, ) => throw UnimplementedError('Cannot use ECMA outside of web environments.'); diff --git a/pkgs/intl4x/lib/src/datetime_format/icu4x/date_formatter.dart b/pkgs/intl4x/lib/src/datetime_format/icu4x/date_formatter.dart new file mode 100644 index 000000000..a41e92499 --- /dev/null +++ b/pkgs/intl4x/lib/src/datetime_format/icu4x/date_formatter.dart @@ -0,0 +1,179 @@ +// Copyright (c) 2023, 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. + +import 'package:icu4x/icu4x.dart' as icu; + +import '../../../datetime_format.dart'; +import '../datetime_format_impl.dart'; +import 'datetime_format_4x.dart'; + +class DateFormatterX extends FormatterImpl { + final icu.DateFormatter formatter; + final DateTimeFormatImpl impl; + final icu.Locale localeX; + + DateFormatterX.d( + this.impl, + this.localeX, + icu.DateTimeAlignment? alignment, + icu.DateTimeLength? length, + ) : formatter = icu.DateFormatter.d( + localeX, + alignment: alignment, + length: length ?? icu.DateTimeLength.short, + ), + super(impl); + + DateFormatterX.m( + this.impl, + this.localeX, + icu.DateTimeAlignment? alignment, + icu.DateTimeLength? length, + ) : formatter = icu.DateFormatter.m( + localeX, + alignment: alignment, + length: length ?? icu.DateTimeLength.short, + ), + super(impl); + + DateFormatterX.md( + this.impl, + this.localeX, + icu.DateTimeAlignment? alignment, + icu.DateTimeLength? length, + ) : formatter = icu.DateFormatter.md( + localeX, + alignment: alignment, + length: length ?? icu.DateTimeLength.short, + ), + super(impl); + + DateFormatterX.y( + this.impl, + this.localeX, + icu.DateTimeAlignment? alignment, + icu.DateTimeLength? length, + icu.YearStyle? yearStyle, + ) : formatter = icu.DateFormatter.y( + localeX, + alignment: alignment, + length: length ?? icu.DateTimeLength.short, + yearStyle: yearStyle, + ), + super(impl); + + DateFormatterX.ymd( + this.impl, + this.localeX, + icu.DateTimeAlignment? alignment, + icu.DateTimeLength? length, + icu.YearStyle? yearStyle, + ) : formatter = icu.DateFormatter.ymd( + localeX, + alignment: alignment, + length: length ?? icu.DateTimeLength.short, + yearStyle: yearStyle, + ), + super(impl); + + DateFormatterX.ymde( + this.impl, + this.localeX, + icu.DateTimeAlignment? alignment, + icu.DateTimeLength? length, + icu.YearStyle? yearStyle, + ) : formatter = icu.DateFormatter.ymde( + localeX, + alignment: alignment, + length: length ?? icu.DateTimeLength.short, + yearStyle: yearStyle, + ), + super(impl); + + @override + String formatInternal(DateTime datetime) => + formatter.formatIso(datetime.toX.$1); + + @override + ZonedDateTimeFormatter withTimeZoneShort() => DateFormatterZonedX.short(this); + + @override + ZonedDateTimeFormatter withTimeZoneLong() => DateFormatterZonedX.long(this); + + @override + ZonedDateTimeFormatter withTimeZoneShortOffset() => + DateFormatterZonedX.shortOffset(this); + + @override + ZonedDateTimeFormatter withTimeZoneLongOffset() => + DateFormatterZonedX.longOffset(this); + + @override + ZonedDateTimeFormatter withTimeZoneShortGeneric() => + DateFormatterZonedX.shortGeneric(this); + + @override + ZonedDateTimeFormatter withTimeZoneLongGeneric() => + DateFormatterZonedX.longGeneric(this); +} + +class DateFormatterZonedX extends FormatterZonedImpl { + final DateFormatterX dateFormatter; + final icu.ZonedDateFormatter formatter; + + DateFormatterZonedX.short(this.dateFormatter) + : formatter = icu.ZonedDateFormatter.specificShort( + dateFormatter.localeX, + dateFormatter.formatter, + ), + super(dateFormatter.impl, TimeZoneType.short); + + DateFormatterZonedX.long(this.dateFormatter) + : formatter = icu.ZonedDateFormatter.specificLong( + dateFormatter.localeX, + dateFormatter.formatter, + ), + + super(dateFormatter.impl, TimeZoneType.long); + + DateFormatterZonedX.shortOffset(this.dateFormatter) + : formatter = icu.ZonedDateFormatter.localizedOffsetShort( + dateFormatter.localeX, + dateFormatter.formatter, + ), + super(dateFormatter.impl, TimeZoneType.shortOffset); + + DateFormatterZonedX.longOffset(this.dateFormatter) + : formatter = icu.ZonedDateFormatter.localizedOffsetLong( + dateFormatter.localeX, + dateFormatter.formatter, + ), + super(dateFormatter.impl, TimeZoneType.longOffset); + + DateFormatterZonedX.shortGeneric(this.dateFormatter) + : formatter = icu.ZonedDateFormatter.genericShort( + dateFormatter.localeX, + dateFormatter.formatter, + ), + super(dateFormatter.impl, TimeZoneType.shortGeneric); + + DateFormatterZonedX.longGeneric(this.dateFormatter) + : formatter = icu.ZonedDateFormatter.genericLong( + dateFormatter.localeX, + dateFormatter.formatter, + ), + super(dateFormatter.impl, TimeZoneType.longGeneric); + + @override + String formatInternal(DateTime datetime, TimeZone timeZone) { + final utcOffset = icu.UtcOffset.fromSeconds(timeZone.offset.inSeconds); + final (isoDate, time) = datetime.toX; + final timeZoneX = icu.IanaParser() + .parse(timeZone.name) + .withOffset(utcOffset) + .atDateTimeIso(isoDate, time); + + return formatter.formatIso(isoDate, timeZoneX); + } +} diff --git a/pkgs/intl4x/lib/src/datetime_format/icu4x/date_time_formatter.dart b/pkgs/intl4x/lib/src/datetime_format/icu4x/date_time_formatter.dart new file mode 100644 index 000000000..10ea4933c --- /dev/null +++ b/pkgs/intl4x/lib/src/datetime_format/icu4x/date_time_formatter.dart @@ -0,0 +1,150 @@ +// Copyright (c) 2023, 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. + +import 'package:icu4x/icu4x.dart' as icu; + +import '../../../datetime_format.dart'; +import '../datetime_format_impl.dart'; +import 'datetime_format_4x.dart'; + +class DateTimeFormatterX extends FormatterImpl { + final icu.DateTimeFormatter formatter; + final DateTimeFormatImpl impl; + final icu.Locale localeX; + + DateTimeFormatterX.mdt( + this.impl, + this.localeX, + icu.DateTimeAlignment? alignment, + icu.DateTimeLength? length, + icu.TimePrecision? timePrecision, + ) : formatter = icu.DateTimeFormatter.mdt( + localeX, + alignment: alignment, + length: length ?? icu.DateTimeLength.short, + timePrecision: timePrecision, + ), + super(impl); + + DateTimeFormatterX.ymdt( + this.impl, + this.localeX, + icu.DateTimeAlignment? alignment, + icu.DateTimeLength? length, + icu.TimePrecision? timePrecision, + icu.YearStyle? yearStyle, + ) : formatter = icu.DateTimeFormatter.ymdt( + localeX, + alignment: alignment, + length: length ?? icu.DateTimeLength.short, + timePrecision: timePrecision, + yearStyle: yearStyle, + ), + super(impl); + + DateTimeFormatterX.ymdet( + this.impl, + this.localeX, + icu.DateTimeAlignment? alignment, + icu.DateTimeLength? length, + icu.TimePrecision? timePrecision, + icu.YearStyle? yearStyle, + ) : formatter = icu.DateTimeFormatter.ymdet( + localeX, + alignment: alignment, + length: length ?? icu.DateTimeLength.short, + timePrecision: timePrecision, + yearStyle: yearStyle, + ), + super(impl); + + @override + String formatInternal(DateTime datetime) { + final (isoDate, time) = datetime.toX; + return formatter.formatIso(isoDate, time); + } + + @override + ZonedDateTimeFormatter withTimeZoneShort() => + DateTimeFormatterZonedX.short(this); + + @override + ZonedDateTimeFormatter withTimeZoneLong() => + DateTimeFormatterZonedX.long(this); + + @override + ZonedDateTimeFormatter withTimeZoneShortOffset() => + DateTimeFormatterZonedX.shortOffset(this); + + @override + ZonedDateTimeFormatter withTimeZoneLongOffset() => + DateTimeFormatterZonedX.longOffset(this); + + @override + ZonedDateTimeFormatter withTimeZoneShortGeneric() => + DateTimeFormatterZonedX.shortGeneric(this); + + @override + ZonedDateTimeFormatter withTimeZoneLongGeneric() => + DateTimeFormatterZonedX.longGeneric(this); +} + +class DateTimeFormatterZonedX extends FormatterZonedImpl { + final DateTimeFormatterX dateFormatter; + final icu.ZonedDateTimeFormatter formatter; + + DateTimeFormatterZonedX.short(this.dateFormatter) + : formatter = icu.ZonedDateTimeFormatter.specificShort( + dateFormatter.localeX, + dateFormatter.formatter, + ), + super(dateFormatter.impl, TimeZoneType.short); + + DateTimeFormatterZonedX.long(this.dateFormatter) + : formatter = icu.ZonedDateTimeFormatter.specificLong( + dateFormatter.localeX, + dateFormatter.formatter, + ), + super(dateFormatter.impl, TimeZoneType.long); + + DateTimeFormatterZonedX.shortOffset(this.dateFormatter) + : formatter = icu.ZonedDateTimeFormatter.localizedOffsetShort( + dateFormatter.localeX, + dateFormatter.formatter, + ), + super(dateFormatter.impl, TimeZoneType.shortOffset); + + DateTimeFormatterZonedX.longOffset(this.dateFormatter) + : formatter = icu.ZonedDateTimeFormatter.localizedOffsetLong( + dateFormatter.localeX, + dateFormatter.formatter, + ), + super(dateFormatter.impl, TimeZoneType.longOffset); + + DateTimeFormatterZonedX.shortGeneric(this.dateFormatter) + : formatter = icu.ZonedDateTimeFormatter.genericShort( + dateFormatter.localeX, + dateFormatter.formatter, + ), + super(dateFormatter.impl, TimeZoneType.shortGeneric); + + DateTimeFormatterZonedX.longGeneric(this.dateFormatter) + : formatter = icu.ZonedDateTimeFormatter.genericLong( + dateFormatter.localeX, + dateFormatter.formatter, + ), + super(dateFormatter.impl, TimeZoneType.longGeneric); + + @override + String formatInternal(DateTime datetime, TimeZone timeZone) { + final utcOffset = icu.UtcOffset.fromSeconds(timeZone.offset.inSeconds); + final (isoDate, time) = datetime.toX; + final timeZoneX = icu.IanaParser() + .parse(timeZone.name) + .withOffset(utcOffset) + .atDateTimeIso(isoDate, time); + + return formatter.formatIso(isoDate, time, timeZoneX); + } +} diff --git a/pkgs/intl4x/lib/src/datetime_format/icu4x/datetime_format_4x.dart b/pkgs/intl4x/lib/src/datetime_format/icu4x/datetime_format_4x.dart new file mode 100644 index 000000000..993045124 --- /dev/null +++ b/pkgs/intl4x/lib/src/datetime_format/icu4x/datetime_format_4x.dart @@ -0,0 +1,214 @@ +// Copyright (c) 2023, 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. + +import 'package:icu4x/icu4x.dart' as icu; + +import '../../../datetime_format.dart'; +import '../../locale/locale.dart'; +import '../../locale/locale_4x.dart'; +import '../datetime_format_impl.dart'; +import 'date_formatter.dart'; +import 'date_time_formatter.dart'; +import 'time_formatter.dart'; + +DateTimeFormatImpl getDateTimeFormatter4X( + Locale locale, + DateTimeFormatOptions options, +) => DateTimeFormat4X(locale as Locale4x, options); + +class DateTimeFormat4X extends DateTimeFormatImpl { + DateTimeFormat4X(Locale4x super.locale, super.options); + + icu.Locale get localeX => (super.locale as Locale4x).get4X; + + @override + FormatterImpl d({DateFormatStyle? dateStyle}) { + final (alignment, _, _, length) = options.toX(dateStyle: dateStyle); + final locale = setLocaleExtensions(localeX, options); + return DateFormatterX.d(this, locale, alignment, length); + } + + @override + FormatterImpl m({DateFormatStyle? dateStyle}) { + final (alignment, _, _, length) = options.toX(dateStyle: dateStyle); + final locale = setLocaleExtensions(localeX, options); + return DateFormatterX.m(this, locale, alignment, length); + } + + @override + FormatterImpl md({DateFormatStyle? dateStyle}) { + final (alignment, _, _, length) = options.toX(dateStyle: dateStyle); + final locale = setLocaleExtensions(localeX, options); + return DateFormatterX.md(this, locale, alignment, length); + } + + @override + FormatterImpl y({DateFormatStyle? dateStyle}) { + final (alignment, yearStyle, _, length) = options.toX(dateStyle: dateStyle); + final locale = setLocaleExtensions(localeX, options); + return DateFormatterX.y(this, locale, alignment, length, yearStyle); + } + + @override + FormatterImpl ymd({DateFormatStyle? dateStyle}) { + final (alignment, yearStyle, _, length) = options.toX(dateStyle: dateStyle); + final locale = setLocaleExtensions(localeX, options); + return DateFormatterX.ymd(this, locale, alignment, length, yearStyle); + } + + @override + FormatterImpl ymde({DateFormatStyle? dateStyle}) { + final (alignment, yearStyle, _, length) = options.toX(dateStyle: dateStyle); + final locale = setLocaleExtensions(localeX, options); + return DateFormatterX.ymde(this, locale, alignment, length, yearStyle); + } + + @override + FormatterImpl mdt({DateFormatStyle? dateStyle, TimeFormatStyle? timeStyle}) { + final (alignment, _, timePrecision, length) = options.toX( + timeStyle: timeStyle, + dateStyle: dateStyle, + ); + final locale = setLocaleExtensions(localeX, options); + return DateTimeFormatterX.mdt( + this, + locale, + alignment, + length, + timePrecision, + ); + } + + @override + FormatterImpl ymdt({DateFormatStyle? dateStyle, TimeFormatStyle? timeStyle}) { + final (alignment, yearStyle, timePrecision, length) = options.toX( + timeStyle: timeStyle, + dateStyle: dateStyle, + ); + final locale = setLocaleExtensions(localeX, options); + return DateTimeFormatterX.ymdt( + this, + locale, + alignment, + length, + timePrecision, + yearStyle, + ); + } + + @override + FormatterImpl ymdet({ + DateFormatStyle? dateStyle, + TimeFormatStyle? timeStyle, + }) { + final (alignment, yearStyle, timePrecision, length) = options.toX( + timeStyle: timeStyle, + dateStyle: dateStyle, + ); + final locale = setLocaleExtensions(localeX, options); + return DateTimeFormatterX.ymdet( + this, + locale, + alignment, + length, + timePrecision, + yearStyle, + ); + } + + @override + FormatterImpl t({TimeFormatStyle? style}) { + final (alignment, _, timePrecision, length) = options.toX( + timePrecisionDefault: options.timestyle == TimeStyle.twodigit + ? icu.TimePrecision.minute + : null, + timeStyle: style, + ); + final locale = setLocaleExtensions(localeX, options); + return TimeFormatterX.t(this, locale, timePrecision, alignment, length); + } +} + +extension DateToICU4X on DateTime { + (icu.IsoDate, icu.Time) get toX { + final isoDate = icu.IsoDate(year, month, day); + final time = icu.Time( + hour, + minute, + second, + millisecond * 1_000_000 + microsecond * 1_000, + ); + return (isoDate, time); + } +} + +extension on DateTimeFormatOptions { + ( + icu.DateTimeAlignment?, + icu.YearStyle?, + icu.TimePrecision?, + icu.DateTimeLength?, + ) + toX({ + icu.TimePrecision? timePrecisionDefault, + TimeFormatStyle? timeStyle, + DateFormatStyle? dateStyle, + }) { + icu.TimePrecision? timePrecision; + if (fractionalSecondDigits != null) { + timePrecision = icu.TimePrecision.fromSubsecondDigits( + fractionalSecondDigits!, + ); + } else { + timePrecision = switch (timeStyle) { + null => timePrecisionDefault ?? icu.TimePrecision.hour, + TimeFormatStyle.full => icu.TimePrecision.second, + TimeFormatStyle.long => icu.TimePrecision.second, + TimeFormatStyle.medium => icu.TimePrecision.second, + TimeFormatStyle.short => icu.TimePrecision.minute, + }; + } + final dateTimeAlignment = timestyle == TimeStyle.twodigit + ? icu.DateTimeAlignment.column + : icu.DateTimeAlignment.auto; + return ( + dateTimeAlignment, + switch (dateStyle) { + null => icu.YearStyle.full, + DateFormatStyle.full => icu.YearStyle.auto, + DateFormatStyle.long => icu.YearStyle.auto, + DateFormatStyle.medium => icu.YearStyle.auto, + DateFormatStyle.short => icu.YearStyle.auto, + }, + timePrecision, + switch (dateStyle) { + DateFormatStyle.full => icu.DateTimeLength.long, + DateFormatStyle.long => icu.DateTimeLength.long, + DateFormatStyle.medium => icu.DateTimeLength.medium, + DateFormatStyle.short => icu.DateTimeLength.short, + null => null, + }, + ); + } +} + +icu.Locale setLocaleExtensions( + icu.Locale locale, + DateTimeFormatOptions options, +) { + final l = locale.clone(); + final calendar = options.calendar; + if (calendar != null) { + l.setUnicodeExtension('ca', calendar.jsName); + } + final clockStyle = options.clockstyle; + if (clockStyle != null) { + l.setUnicodeExtension('hc', clockStyle.hourStyleExtensionString); + } + final numberingSystem = options.numberingSystem; + if (numberingSystem != null) { + l.setUnicodeExtension('nu', numberingSystem.name); + } + return l; +} diff --git a/pkgs/intl4x/lib/src/datetime_format/icu4x/time_formatter.dart b/pkgs/intl4x/lib/src/datetime_format/icu4x/time_formatter.dart new file mode 100644 index 000000000..e02fe705d --- /dev/null +++ b/pkgs/intl4x/lib/src/datetime_format/icu4x/time_formatter.dart @@ -0,0 +1,131 @@ +// Copyright (c) 2023, 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. + +import 'package:icu4x/icu4x.dart' as icu; + +import '../../../datetime_format.dart'; +import '../datetime_format_impl.dart'; +import 'datetime_format_4x.dart'; + +class TimeFormatterX extends FormatterImpl { + final icu.TimeFormatter formatter; + final DateTimeFormatImpl impl; + final icu.Locale localeX; + + final icu.TimePrecision? timePrecision; + final icu.DateTimeAlignment? alignment; + final icu.DateTimeLength? length; + + TimeFormatterX.t( + this.impl, + this.localeX, + this.timePrecision, + this.alignment, + this.length, + ) : formatter = icu.TimeFormatter( + localeX, + alignment: alignment, + length: length ?? icu.DateTimeLength.short, + timePrecision: timePrecision, + ), + super(impl); + + @override + String formatInternal(DateTime datetime) { + final (_, time) = datetime.toX; + return formatter.format(time); + } + + @override + TimeFormatterZonedX withTimeZoneShort() => TimeFormatterZonedX.short(this); + + @override + ZonedDateTimeFormatter withTimeZoneLong() => TimeFormatterZonedX.long(this); + + @override + ZonedDateTimeFormatter withTimeZoneShortOffset() => + TimeFormatterZonedX.shortOffset(this); + + @override + ZonedDateTimeFormatter withTimeZoneLongOffset() => + TimeFormatterZonedX.longOffset(this); + + @override + ZonedDateTimeFormatter withTimeZoneShortGeneric() => + TimeFormatterZonedX.shortGeneric(this); + + @override + ZonedDateTimeFormatter withTimeZoneLongGeneric() => + TimeFormatterZonedX.longGeneric(this); +} + +class TimeFormatterZonedX extends FormatterZonedImpl { + final TimeFormatterX timeFormatter; + final icu.ZonedTimeFormatter formatter; + + TimeFormatterZonedX.short(this.timeFormatter) + : formatter = icu.ZonedTimeFormatter.specificShort( + timeFormatter.localeX, + alignment: timeFormatter.alignment, + length: timeFormatter.length, + timePrecision: timeFormatter.timePrecision, + ), + super(timeFormatter.impl, TimeZoneType.short); + + TimeFormatterZonedX.long(this.timeFormatter) + : formatter = icu.ZonedTimeFormatter.specificLong( + timeFormatter.localeX, + alignment: timeFormatter.alignment, + length: timeFormatter.length, + timePrecision: timeFormatter.timePrecision, + ), + super(timeFormatter.impl, TimeZoneType.long); + + TimeFormatterZonedX.shortOffset(this.timeFormatter) + : formatter = icu.ZonedTimeFormatter.localizedOffsetShort( + timeFormatter.localeX, + alignment: timeFormatter.alignment, + length: timeFormatter.length, + timePrecision: timeFormatter.timePrecision, + ), + super(timeFormatter.impl, TimeZoneType.shortOffset); + + TimeFormatterZonedX.longOffset(this.timeFormatter) + : formatter = icu.ZonedTimeFormatter.localizedOffsetLong( + timeFormatter.localeX, + alignment: timeFormatter.alignment, + length: timeFormatter.length, + timePrecision: timeFormatter.timePrecision, + ), + super(timeFormatter.impl, TimeZoneType.longOffset); + + TimeFormatterZonedX.shortGeneric(this.timeFormatter) + : formatter = icu.ZonedTimeFormatter.genericShort( + timeFormatter.localeX, + alignment: timeFormatter.alignment, + length: timeFormatter.length, + timePrecision: timeFormatter.timePrecision, + ), + super(timeFormatter.impl, TimeZoneType.shortGeneric); + + TimeFormatterZonedX.longGeneric(this.timeFormatter) + : formatter = icu.ZonedTimeFormatter.genericLong( + timeFormatter.localeX, + alignment: timeFormatter.alignment, + length: timeFormatter.length, + timePrecision: timeFormatter.timePrecision, + ), + super(timeFormatter.impl, TimeZoneType.longGeneric); + + @override + String formatInternal(DateTime datetime, TimeZone timeZone) { + final utcOffset = icu.UtcOffset.fromSeconds(timeZone.offset.inSeconds); + final (isoDate, time) = datetime.toX; + final timeZoneX = icu.IanaParser() + .parse(timeZone.name) + .withOffset(utcOffset) + .atDateTimeIso(isoDate, time); + return formatter.format(time, timeZoneX); + } +} diff --git a/pkgs/intl4x/lib/src/display_names/display_names_ecma.dart b/pkgs/intl4x/lib/src/display_names/display_names_ecma.dart index 7dbedc413..411f1d3c9 100644 --- a/pkgs/intl4x/lib/src/display_names/display_names_ecma.dart +++ b/pkgs/intl4x/lib/src/display_names/display_names_ecma.dart @@ -12,8 +12,7 @@ import 'display_names_options.dart'; DisplayNamesImpl getDisplayNamesECMA( Locale locale, DisplayNamesOptions options, - LocaleMatcher localeMatcher, -) => _DisplayNamesECMA.tryToBuild(locale, options, localeMatcher); +) => _DisplayNamesECMA.tryToBuild(locale, options); @JS('Intl.DisplayNames') extension type DisplayNames._(JSObject _) implements JSObject { @@ -32,23 +31,17 @@ class _DisplayNamesECMA extends DisplayNamesImpl { static DisplayNamesImpl tryToBuild( Locale locale, DisplayNamesOptions options, - LocaleMatcher localeMatcher, ) { - final supportedLocales = supportedLocalesOf(localeMatcher, locale); + final supportedLocales = supportedLocalesOf(locale); return _DisplayNamesECMA( supportedLocales.firstOrNull ?? Locale.parse('und'), options, ); } - static List supportedLocalesOf( - LocaleMatcher localeMatcher, - Locale locale, - ) { - final o = {'localeMatcher': localeMatcher.jsName}.jsify()!; + static List supportedLocalesOf(Locale locale) { return DisplayNames.supportedLocalesOf( [locale.toLanguageTag().toJS].toJS, - o, ).toDart.whereType().map(Locale.parse).toList(); } @@ -84,12 +77,10 @@ class _DisplayNamesECMA extends DisplayNamesImpl { } extension on DisplayNamesOptions { - JSAny toJsOptions(DisplayType type) => - { - 'localeMatcher': localeMatcher.jsName, - 'style': style.name, - 'type': type.name, - 'languageDisplay': languageDisplay.name, - 'fallback': fallback.name, - }.jsify()!; + JSAny toJsOptions(DisplayType type) => { + 'style': style.name, + 'type': type.name, + 'languageDisplay': languageDisplay.name, + 'fallback': fallback.name, + }.jsify()!; } diff --git a/pkgs/intl4x/lib/src/display_names/display_names_impl.dart b/pkgs/intl4x/lib/src/display_names/display_names_impl.dart index 78e480e76..d1061dad0 100644 --- a/pkgs/intl4x/lib/src/display_names/display_names_impl.dart +++ b/pkgs/intl4x/lib/src/display_names/display_names_impl.dart @@ -31,15 +31,6 @@ abstract class DisplayNamesImpl { String ofCalendar(Calendar calendar); - static DisplayNamesImpl build( - Locale locale, - DisplayNamesOptions options, - LocaleMatcher localeMatcher, - ) => buildFormatter( - locale, - options, - localeMatcher, - getDisplayNamesECMA, - getDisplayNames4X, - ); + static DisplayNamesImpl build(Locale locale, DisplayNamesOptions options) => + buildFormatter(locale, options, getDisplayNamesECMA, getDisplayNames4X); } diff --git a/pkgs/intl4x/lib/src/display_names/display_names_options.dart b/pkgs/intl4x/lib/src/display_names/display_names_options.dart index 9b4a3d2c1..1d41c9fe0 100644 --- a/pkgs/intl4x/lib/src/display_names/display_names_options.dart +++ b/pkgs/intl4x/lib/src/display_names/display_names_options.dart @@ -9,26 +9,22 @@ class DisplayNamesOptions { final Style style; final LanguageDisplay languageDisplay; final Fallback fallback; - final LocaleMatcher localeMatcher; const DisplayNamesOptions({ this.style = Style.long, this.languageDisplay = LanguageDisplay.dialect, this.fallback = Fallback.code, - this.localeMatcher = LocaleMatcher.bestfit, }); DisplayNamesOptions copyWith({ Style? style, LanguageDisplay? languageDisplay, Fallback? fallback, - LocaleMatcher? localeMatcher, }) { return DisplayNamesOptions( style: style ?? this.style, languageDisplay: languageDisplay ?? this.languageDisplay, fallback: fallback ?? this.fallback, - localeMatcher: localeMatcher ?? this.localeMatcher, ); } } diff --git a/pkgs/intl4x/lib/src/display_names/display_names_stub.dart b/pkgs/intl4x/lib/src/display_names/display_names_stub.dart index 59661bc66..d2c70c5de 100644 --- a/pkgs/intl4x/lib/src/display_names/display_names_stub.dart +++ b/pkgs/intl4x/lib/src/display_names/display_names_stub.dart @@ -9,5 +9,4 @@ import 'display_names_impl.dart'; DisplayNamesImpl getDisplayNamesECMA( Locale locales, DisplayNamesOptions options, - LocaleMatcher localeMatcher, ) => throw UnimplementedError('Cannot use ECMA outside of web environments.'); diff --git a/pkgs/intl4x/lib/src/list_format/list_format_ecma.dart b/pkgs/intl4x/lib/src/list_format/list_format_ecma.dart index f4aa1038c..99a4195ca 100644 --- a/pkgs/intl4x/lib/src/list_format/list_format_ecma.dart +++ b/pkgs/intl4x/lib/src/list_format/list_format_ecma.dart @@ -5,15 +5,11 @@ import 'dart:js_interop'; import '../locale/locale.dart'; -import '../options.dart'; import 'list_format_impl.dart'; import 'list_format_options.dart'; -ListFormatImpl getListFormatterECMA( - Locale locale, - ListFormatOptions options, - LocaleMatcher localeMatcher, -) => _ListFormatECMA.tryToBuild(locale, options, localeMatcher); +ListFormatImpl getListFormatterECMA(Locale locale, ListFormatOptions options) => + _ListFormatECMA.tryToBuild(locale, options); @JS('Intl.ListFormat') extension type ListFormat._(JSObject _) implements JSObject { @@ -29,26 +25,17 @@ extension type ListFormat._(JSObject _) implements JSObject { class _ListFormatECMA extends ListFormatImpl { _ListFormatECMA(super.locale, super.options); - static ListFormatImpl tryToBuild( - Locale locale, - ListFormatOptions options, - LocaleMatcher localeMatcher, - ) { - final supportedLocales = supportedLocalesOf(locale, localeMatcher); + static ListFormatImpl tryToBuild(Locale locale, ListFormatOptions options) { + final supportedLocales = supportedLocalesOf(locale); return _ListFormatECMA( supportedLocales.firstOrNull ?? Locale.parse('und'), options, ); } - static List supportedLocalesOf( - Locale locale, - LocaleMatcher localeMatcher, - ) { - final o = {'localeMatcher': localeMatcher.jsName}.jsify()!; + static List supportedLocalesOf(Locale locale) { return ListFormat.supportedLocalesOf( [locale.toLanguageTag().toJS].toJS, - o, ).toDart.whereType().map(Locale.parse).toList(); } @@ -62,10 +49,5 @@ class _ListFormatECMA extends ListFormatImpl { } extension on ListFormatOptions { - JSAny toJsOptions() => - { - 'localeMatcher': localeMatcher.jsName, - 'type': type.jsName, - 'style': style.name, - }.jsify()!; + JSAny toJsOptions() => {'type': type.jsName, 'style': style.name}.jsify()!; } diff --git a/pkgs/intl4x/lib/src/list_format/list_format_impl.dart b/pkgs/intl4x/lib/src/list_format/list_format_impl.dart index 0811c4bff..937842ace 100644 --- a/pkgs/intl4x/lib/src/list_format/list_format_impl.dart +++ b/pkgs/intl4x/lib/src/list_format/list_format_impl.dart @@ -3,7 +3,6 @@ // BSD-style license that can be found in the LICENSE file. import '../locale/locale.dart'; -import '../options.dart'; import '../utils.dart'; import 'list_format_options.dart'; import 'list_format_stub.dart' @@ -18,15 +17,11 @@ abstract class ListFormatImpl { String formatImpl(List list); - static ListFormatImpl build( - Locale locales, - ListFormatOptions options, - LocaleMatcher localeMatcher, - ) => buildFormatter( - locales, - options, - localeMatcher, - getListFormatterECMA, - getListFormatter4X, - ); + static ListFormatImpl build(Locale locales, ListFormatOptions options) => + buildFormatter( + locales, + options, + getListFormatterECMA, + getListFormatter4X, + ); } diff --git a/pkgs/intl4x/lib/src/list_format/list_format_options.dart b/pkgs/intl4x/lib/src/list_format/list_format_options.dart index f988c49d7..83c7f10c8 100644 --- a/pkgs/intl4x/lib/src/list_format/list_format_options.dart +++ b/pkgs/intl4x/lib/src/list_format/list_format_options.dart @@ -15,23 +15,13 @@ class ListFormatOptions { /// * short: "A, B, C". /// * narrow: "A B C". final ListStyle style; - final LocaleMatcher localeMatcher; - - const ListFormatOptions({ - this.type = Type.and, - this.style = ListStyle.long, - this.localeMatcher = LocaleMatcher.bestfit, - }); - - ListFormatOptions copyWith({ - Type? type, - ListStyle? style, - LocaleMatcher? localeMatcher, - }) { + + const ListFormatOptions({this.type = Type.and, this.style = ListStyle.long}); + + ListFormatOptions copyWith({Type? type, ListStyle? style}) { return ListFormatOptions( type: type ?? this.type, style: style ?? this.style, - localeMatcher: localeMatcher ?? this.localeMatcher, ); } } diff --git a/pkgs/intl4x/lib/src/list_format/list_format_stub.dart b/pkgs/intl4x/lib/src/list_format/list_format_stub.dart index 409525f55..b0ccb2df3 100644 --- a/pkgs/intl4x/lib/src/list_format/list_format_stub.dart +++ b/pkgs/intl4x/lib/src/list_format/list_format_stub.dart @@ -6,8 +6,5 @@ import '../../list_format.dart'; import '../locale/locale.dart'; import 'list_format_impl.dart'; -ListFormatImpl getListFormatterECMA( - Locale locale, - ListFormatOptions options, - LocaleMatcher localeMatcher, -) => throw UnimplementedError('Cannot use ECMA outside of web environments.'); +ListFormatImpl getListFormatterECMA(Locale locale, ListFormatOptions options) => + throw UnimplementedError('Cannot use ECMA outside of web environments.'); diff --git a/pkgs/intl4x/lib/src/number_format/number_format_4x.dart b/pkgs/intl4x/lib/src/number_format/number_format_4x.dart index 420fd4958..bf59b854c 100644 --- a/pkgs/intl4x/lib/src/number_format/number_format_4x.dart +++ b/pkgs/intl4x/lib/src/number_format/number_format_4x.dart @@ -38,6 +38,7 @@ class NumberFormat4X extends NumberFormatImpl { final String s => icu.Decimal.fromString(s), Object() => icu.Decimal.fromString(number.toString()), }; + fixedDecimal.applySignDisplay(options.signDisplay.toX); return _constructDouble(fixedDecimal); } @@ -112,6 +113,16 @@ class NumberFormat4X extends NumberFormatImpl { } } +extension on SignDisplay { + icu.DecimalSignDisplay get toX => switch (this) { + SignDisplay.always => icu.DecimalSignDisplay.always, + SignDisplay.never => icu.DecimalSignDisplay.never, + SignDisplay.auto => icu.DecimalSignDisplay.auto, + SignDisplay.exceptZero => icu.DecimalSignDisplay.exceptZero, + SignDisplay.negative => icu.DecimalSignDisplay.negative, + }; +} + extension on NumberFormatOptions { icu.DecimalGroupingStrategy get toX => switch (useGrouping) { Grouping.always => icu.DecimalGroupingStrategy.always, diff --git a/pkgs/intl4x/lib/src/number_format/number_format_ecma.dart b/pkgs/intl4x/lib/src/number_format/number_format_ecma.dart index fe43d0f7e..5ee6c9620 100644 --- a/pkgs/intl4x/lib/src/number_format/number_format_ecma.dart +++ b/pkgs/intl4x/lib/src/number_format/number_format_ecma.dart @@ -5,15 +5,13 @@ import 'dart:js_interop'; import '../locale/locale.dart'; -import '../options.dart'; import 'number_format_impl.dart'; import 'number_format_options.dart'; NumberFormatImpl getNumberFormatterECMA( Locale locale, NumberFormatOptions options, - LocaleMatcher localeMatcher, -) => _NumberFormatECMA.tryToBuild(locale, options, localeMatcher); +) => _NumberFormatECMA.tryToBuild(locale, options); @JS('Intl.NumberFormat') extension type NumberFormat._(JSObject _) implements JSObject { @@ -32,23 +30,17 @@ class _NumberFormatECMA extends NumberFormatImpl { static NumberFormatImpl tryToBuild( Locale locale, NumberFormatOptions options, - LocaleMatcher localeMatcher, ) { - final supportedLocales = supportedLocalesOf(localeMatcher, locale); + final supportedLocales = supportedLocalesOf(locale); return _NumberFormatECMA( supportedLocales.firstOrNull ?? Locale.parse('und'), options, ); } - static List supportedLocalesOf( - LocaleMatcher localeMatcher, - Locale locale, - ) { - final o = {'localeMatcher': localeMatcher.jsName}.jsify()!; + static List supportedLocalesOf(Locale locale) { return NumberFormat.supportedLocalesOf( [locale.toLanguageTag().toJS].toJS, - o, ).toDart.whereType().map(Locale.parse).toList(); } @@ -86,7 +78,6 @@ extension on NumberFormatOptions { 'sign': signDisplay.name, if (notation is CompactNotation) 'compactDisplay': (notation as CompactNotation).compactDisplay.name, - 'localeMatcher': localeMatcher.jsName, 'notation': notation.name, if (numberingSystem != null) 'numberingSystem': numberingSystem, 'signDisplay': signDisplay.name, diff --git a/pkgs/intl4x/lib/src/number_format/number_format_impl.dart b/pkgs/intl4x/lib/src/number_format/number_format_impl.dart index 191c80eec..ba0d0f737 100644 --- a/pkgs/intl4x/lib/src/number_format/number_format_impl.dart +++ b/pkgs/intl4x/lib/src/number_format/number_format_impl.dart @@ -3,7 +3,6 @@ // BSD-style license that can be found in the LICENSE file. import '../locale/locale.dart'; -import '../options.dart'; import '../utils.dart'; import 'number_format_options.dart'; import 'number_format_stub.dart' @@ -21,15 +20,11 @@ abstract class NumberFormatImpl { String formatImpl(Object number); - static NumberFormatImpl build( - Locale locale, - NumberFormatOptions options, - LocaleMatcher localeMatcher, - ) => buildFormatter( - locale, - options, - localeMatcher, - getNumberFormatterECMA, - getNumberFormatter4X, - ); + static NumberFormatImpl build(Locale locale, NumberFormatOptions options) => + buildFormatter( + locale, + options, + getNumberFormatterECMA, + getNumberFormatter4X, + ); } diff --git a/pkgs/intl4x/lib/src/number_format/number_format_options.dart b/pkgs/intl4x/lib/src/number_format/number_format_options.dart index 0b8eaa3b5..95d281107 100644 --- a/pkgs/intl4x/lib/src/number_format/number_format_options.dart +++ b/pkgs/intl4x/lib/src/number_format/number_format_options.dart @@ -12,8 +12,8 @@ typedef UnitDisplay = Style; class NumberFormatOptions { final FormatStyle style; final String? currency; + //General options - final LocaleMatcher localeMatcher; final SignDisplay signDisplay; final Notation notation; final Grouping useGrouping; @@ -23,12 +23,9 @@ class NumberFormatOptions { final int minimumIntegerDigits; final Digits? digits; - NumberFormatOptions.custom( - //General options - { + NumberFormatOptions.custom({ this.style = const DecimalStyle(), this.currency, - this.localeMatcher = LocaleMatcher.bestfit, this.signDisplay = SignDisplay.auto, this.notation = const StandardNotation(), this.useGrouping = Grouping.auto, @@ -40,8 +37,6 @@ class NumberFormatOptions { }) : digits = getDigits(style, digits); factory NumberFormatOptions.percent({ - //General options - LocaleMatcher localeMatcher = LocaleMatcher.bestfit, SignDisplay signDisplay = SignDisplay.auto, Notation notation = const StandardNotation(), Grouping useGrouping = Grouping.auto, @@ -53,7 +48,6 @@ class NumberFormatOptions { }) { return NumberFormatOptions.custom( style: const PercentStyle(), - localeMatcher: localeMatcher, signDisplay: signDisplay, notation: notation, useGrouping: useGrouping, @@ -68,7 +62,6 @@ class NumberFormatOptions { required Unit unit, UnitDisplay unitDisplay = UnitDisplay.short, //General options - LocaleMatcher localeMatcher = LocaleMatcher.bestfit, SignDisplay signDisplay = SignDisplay.auto, Notation notation = const StandardNotation(), Grouping useGrouping = Grouping.auto, @@ -80,7 +73,6 @@ class NumberFormatOptions { }) { return NumberFormatOptions.custom( style: UnitStyle(unit: unit, unitDisplay: unitDisplay), - localeMatcher: localeMatcher, signDisplay: signDisplay, notation: notation, useGrouping: useGrouping, @@ -97,7 +89,6 @@ class NumberFormatOptions { CurrencyDisplay currencyDisplay = CurrencyDisplay.symbol, CurrencySign currencySign = CurrencySign.standard, //General options - LocaleMatcher localeMatcher = LocaleMatcher.bestfit, SignDisplay signDisplay = SignDisplay.auto, Notation notation = const StandardNotation(), Grouping useGrouping = Grouping.auto, @@ -114,7 +105,6 @@ class NumberFormatOptions { display: currencyDisplay, sign: currencySign, ), - localeMatcher: localeMatcher, signDisplay: signDisplay, notation: notation, useGrouping: useGrouping, @@ -130,7 +120,6 @@ class NumberFormatOptions { CompactDisplay compactDisplay = CompactDisplay.short, //General options FormatStyle style = const DecimalStyle(), - LocaleMatcher localeMatcher = LocaleMatcher.bestfit, SignDisplay signDisplay = SignDisplay.auto, Grouping useGrouping = Grouping.auto, String? numberingSystem, @@ -141,7 +130,6 @@ class NumberFormatOptions { }) { return NumberFormatOptions.custom( style: style, - localeMatcher: localeMatcher, signDisplay: signDisplay, notation: CompactNotation(compactDisplay: compactDisplay), useGrouping: useGrouping, @@ -193,7 +181,6 @@ class NumberFormatOptions { NumberFormatOptions copyWith({ FormatStyle? style, String? currency, - LocaleMatcher? localeMatcher, SignDisplay? signDisplay, Notation? notation, Grouping? useGrouping, @@ -206,7 +193,6 @@ class NumberFormatOptions { return NumberFormatOptions.custom( style: style ?? this.style, currency: currency ?? this.currency, - localeMatcher: localeMatcher ?? this.localeMatcher, signDisplay: signDisplay ?? this.signDisplay, notation: notation ?? this.notation, useGrouping: useGrouping ?? this.useGrouping, diff --git a/pkgs/intl4x/lib/src/number_format/number_format_stub.dart b/pkgs/intl4x/lib/src/number_format/number_format_stub.dart index 307fcd919..c2c246011 100644 --- a/pkgs/intl4x/lib/src/number_format/number_format_stub.dart +++ b/pkgs/intl4x/lib/src/number_format/number_format_stub.dart @@ -2,7 +2,6 @@ // 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. -import '../../list_format.dart'; import '../locale/locale.dart'; import 'number_format_impl.dart'; import 'number_format_options.dart'; @@ -10,5 +9,4 @@ import 'number_format_options.dart'; NumberFormatImpl getNumberFormatterECMA( Locale locale, NumberFormatOptions options, - LocaleMatcher localeMatcher, ) => throw UnimplementedError('Cannot use ECMA outside of web environments.'); diff --git a/pkgs/intl4x/lib/src/options.dart b/pkgs/intl4x/lib/src/options.dart index 89b78563a..c12aaa6a1 100644 --- a/pkgs/intl4x/lib/src/options.dart +++ b/pkgs/intl4x/lib/src/options.dart @@ -2,31 +2,6 @@ // 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. -/// Used to find the best match between a user's desired locales and an -/// application's supported locales. -/// -/// When receiving a list of locales for which there is no perfect match in the -/// list of supported locales, it is probably not the best solution to return -/// null or an empty string. -/// Instead, the application "falls back" until it finds a matching language tag -/// associated with a suitable piece of content to insert. The exact fallback -/// algorithm is determined by this enum. -enum LocaleMatcher { - /// See the algorithm in - /// https://datatracker.ietf.org/doc/html/rfc4647#section-3.4. - lookup, - - /// A matcher lets the runtime provide a locale that's at least, but possibly - /// more, suited for the request than the result of the [lookup] algorithm. - bestfit('best fit'); - - final String? _jsName; - - String? get jsName => _jsName ?? name; - - const LocaleMatcher([this._jsName]); -} - enum Calendar { buddhist, chinese, diff --git a/pkgs/intl4x/lib/src/plural_rules/plural_rules_ecma.dart b/pkgs/intl4x/lib/src/plural_rules/plural_rules_ecma.dart index 9c40d671a..eeaff8050 100644 --- a/pkgs/intl4x/lib/src/plural_rules/plural_rules_ecma.dart +++ b/pkgs/intl4x/lib/src/plural_rules/plural_rules_ecma.dart @@ -5,7 +5,6 @@ import 'dart:js_interop'; import '../locale/locale.dart'; -import '../options.dart'; import 'plural_rules.dart'; import 'plural_rules_impl.dart'; import 'plural_rules_options.dart'; @@ -13,8 +12,7 @@ import 'plural_rules_options.dart'; PluralRulesImpl getPluralSelectECMA( Locale locale, PluralRulesOptions options, - LocaleMatcher localeMatcher, -) => _PluralRulesECMA.tryToBuild(locale, options, localeMatcher); +) => _PluralRulesECMA.tryToBuild(locale, options); @JS('Intl.PluralRules') extension type PluralRules._(JSObject _) implements JSObject { @@ -30,26 +28,17 @@ extension type PluralRules._(JSObject _) implements JSObject { class _PluralRulesECMA extends PluralRulesImpl { _PluralRulesECMA(super.locale, super.options); - static PluralRulesImpl tryToBuild( - Locale locale, - PluralRulesOptions options, - LocaleMatcher localeMatcher, - ) { - final supportedLocales = supportedLocalesOf(locale, localeMatcher); + static PluralRulesImpl tryToBuild(Locale locale, PluralRulesOptions options) { + final supportedLocales = supportedLocalesOf(locale); return _PluralRulesECMA( supportedLocales.firstOrNull ?? Locale.parse('und'), options, ); } - static List supportedLocalesOf( - Locale locale, - LocaleMatcher localeMatcher, - ) { - final o = {'localeMatcher': localeMatcher.jsName}.jsify()!; + static List supportedLocalesOf(Locale locale) { return PluralRules.supportedLocalesOf( [locale.toLanguageTag().toJS].toJS, - o, ).toDart.whereType().map(Locale.parse).toList(); } @@ -68,7 +57,6 @@ class _PluralRulesECMA extends PluralRulesImpl { extension on PluralRulesOptions { JSAny toJsOptions() { return { - 'localeMatcher': localeMatcher.jsName, 'type': type.name, 'roundingMode': roundingMode.name, if (digits?.roundingPriority != null) diff --git a/pkgs/intl4x/lib/src/plural_rules/plural_rules_impl.dart b/pkgs/intl4x/lib/src/plural_rules/plural_rules_impl.dart index 549737ddf..01cf5a78d 100644 --- a/pkgs/intl4x/lib/src/plural_rules/plural_rules_impl.dart +++ b/pkgs/intl4x/lib/src/plural_rules/plural_rules_impl.dart @@ -3,7 +3,6 @@ // BSD-style license that can be found in the LICENSE file. import '../locale/locale.dart'; -import '../options.dart'; import '../utils.dart'; import 'plural_rules.dart'; import 'plural_rules_options.dart'; @@ -19,15 +18,6 @@ abstract class PluralRulesImpl { PluralCategory selectImpl(num number); - static PluralRulesImpl build( - Locale locales, - PluralRulesOptions options, - LocaleMatcher localeMatcher, - ) => buildFormatter( - locales, - options, - localeMatcher, - getPluralSelectECMA, - getPluralSelect4X, - ); + static PluralRulesImpl build(Locale locales, PluralRulesOptions options) => + buildFormatter(locales, options, getPluralSelectECMA, getPluralSelect4X); } diff --git a/pkgs/intl4x/lib/src/plural_rules/plural_rules_options.dart b/pkgs/intl4x/lib/src/plural_rules/plural_rules_options.dart index 350af5205..0f63e0015 100644 --- a/pkgs/intl4x/lib/src/plural_rules/plural_rules_options.dart +++ b/pkgs/intl4x/lib/src/plural_rules/plural_rules_options.dart @@ -13,15 +13,12 @@ class PluralRulesOptions { final int minimumIntegerDigits; final TrailingZeroDisplay trailingZeroDisplay; - final LocaleMatcher localeMatcher; - PluralRulesOptions({ this.type = Type.cardinal, Digits? digits, this.roundingMode = RoundingMode.halfExpand, this.minimumIntegerDigits = 1, this.trailingZeroDisplay = TrailingZeroDisplay.auto, - this.localeMatcher = LocaleMatcher.bestfit, }) : digits = NumberFormatOptions.getDigits(const DecimalStyle(), digits); PluralRulesOptions copyWith({ @@ -30,7 +27,6 @@ class PluralRulesOptions { RoundingMode? roundingMode, int? minimumIntegerDigits, TrailingZeroDisplay? trailingZeroDisplay, - LocaleMatcher? localeMatcher, }) { return PluralRulesOptions( type: type ?? this.type, @@ -38,7 +34,6 @@ class PluralRulesOptions { roundingMode: roundingMode ?? this.roundingMode, minimumIntegerDigits: minimumIntegerDigits ?? this.minimumIntegerDigits, trailingZeroDisplay: trailingZeroDisplay ?? this.trailingZeroDisplay, - localeMatcher: localeMatcher ?? this.localeMatcher, ); } } diff --git a/pkgs/intl4x/lib/src/plural_rules/plural_rules_stub.dart b/pkgs/intl4x/lib/src/plural_rules/plural_rules_stub.dart index 8ff9eb613..bf2ab97a0 100644 --- a/pkgs/intl4x/lib/src/plural_rules/plural_rules_stub.dart +++ b/pkgs/intl4x/lib/src/plural_rules/plural_rules_stub.dart @@ -9,5 +9,4 @@ import 'plural_rules_impl.dart'; PluralRulesImpl getPluralSelectECMA( Locale locale, PluralRulesOptions options, - LocaleMatcher localeMatcher, ) => throw UnimplementedError('Cannot use ECMA outside of web environments.'); diff --git a/pkgs/intl4x/lib/src/utils.dart b/pkgs/intl4x/lib/src/utils.dart index ea1207bcc..a78c60983 100644 --- a/pkgs/intl4x/lib/src/utils.dart +++ b/pkgs/intl4x/lib/src/utils.dart @@ -5,19 +5,14 @@ import 'ecma/ecma_native.dart' if (dart.library.js_interop) 'ecma/ecma_web.dart'; import 'locale/locale.dart'; -import 'options.dart'; T buildFormatter( Locale locale, Options options, - LocaleMatcher localeMatcher, - T Function(Locale locales, Options options, LocaleMatcher localeMatcher) - builderECMA, + T Function(Locale locales, Options options) builderECMA, T Function(Locale locales, Options options) builder4X, ) { - return useBrowser - ? builderECMA(locale, options, localeMatcher) - : builder4X(locale, options); + return useBrowser ? builderECMA(locale, options) : builder4X(locale, options); } extension Mapper on T { diff --git a/pkgs/intl4x/pubspec.yaml b/pkgs/intl4x/pubspec.yaml index 72eeed23f..5a288656b 100644 --- a/pkgs/intl4x/pubspec.yaml +++ b/pkgs/intl4x/pubspec.yaml @@ -17,7 +17,7 @@ topics: - i18n environment: - sdk: ^3.7.0 + sdk: ^3.8.0 dependencies: hooks: ^0.20.0 diff --git a/pkgs/intl4x/test/datetime_format_test.dart b/pkgs/intl4x/test/datetime_format_test.dart index a9f29d26a..d3859b03f 100644 --- a/pkgs/intl4x/test/datetime_format_test.dart +++ b/pkgs/intl4x/test/datetime_format_test.dart @@ -14,65 +14,67 @@ void main() { final dateTime = DateTime(2012, 12, 20, 3, 0, 0); testWithFormatting( 'd', - () => expect(intl.dateTimeFormat().d(dateTime), '20'), + () => expect(intl.dateTimeFormat().d().format(dateTime), '20'), ); testWithFormatting( 'm', - () => expect(intl.dateTimeFormat().m(dateTime), '12'), + () => expect(intl.dateTimeFormat().m().format(dateTime), '12'), ); testWithFormatting( 'y', - () => expect(intl.dateTimeFormat().y(dateTime), '2012'), + () => expect(intl.dateTimeFormat().y().format(dateTime), '2012'), ); testWithFormatting( 'md', - () => expect(intl.dateTimeFormat().md(dateTime), '12/20'), + () => expect(intl.dateTimeFormat().md().format(dateTime), '12/20'), ); testWithFormatting( 'ymd', - () => expect(intl.dateTimeFormat().ymd(dateTime), '12/20/2012'), + () => expect(intl.dateTimeFormat().ymd().format(dateTime), '12/20/2012'), ); testWithFormatting( 'ymdt', () => expect( - intl.dateTimeFormat().ymdt(dateTime), + intl.dateTimeFormat().ymdt().format(dateTime), matches(r'12/20/2012[,]? 3\sAM'), ), ); testWithFormatting( 'ymdet', () => expect( - intl.dateTimeFormat().ymdet(dateTime), + intl.dateTimeFormat().ymdet().format(dateTime), matches(r'Thu, 12/20/2012[,]? 3\sAM'), ), ); testWithFormatting( 'time', - () => expect(intl.dateTimeFormat().time(dateTime), matches(r'3\sAM')), + () => + expect(intl.dateTimeFormat().t().format(dateTime), matches(r'3\sAM')), ); }); group('timezone', () { - final date = DateTime(2021, 12, 17, 3, 0, 42); + final dateTime = DateTime(2021, 12, 17, 3, 0, 42); final intl = Intl(locale: Locale.parse('en-US')); - const timeZone = 'America/Los_Angeles'; - const offset = Duration(hours: -8); - testWithFormatting( - 'short', - () => expect( - intl.dateTimeFormat().ymd( - date, - timeZone: const TimeZone.short(name: timeZone, offset: offset), + const timeZone = TimeZone( + name: 'America/Los_Angeles', + offset: Duration(hours: -8), + ); + testWithFormatting('short', () { + return expect( + intl.dateTimeFormat().ymd().withTimeZoneShort().format( + dateTime, + timeZone, ), matches(r'12/17/2021[,]? PST'), - ), - ); + ); + }); testWithFormatting( 'long', () => expect( - intl.dateTimeFormat().ymd( - date, - timeZone: const TimeZone.long(name: timeZone, offset: offset), + intl.dateTimeFormat().ymd().withTimeZoneLong().format( + dateTime, + timeZone, ), matches(r'12/17/2021[,]? Pacific Standard Time'), ), @@ -82,22 +84,18 @@ void main() { () => expect( intl .dateTimeFormat(const DateTimeFormatOptions()) - .ymd( - date, - timeZone: const TimeZone.shortOffset( - name: timeZone, - offset: offset, - ), - ), + .ymd() + .withTimeZoneShortOffset() + .format(dateTime, timeZone), matches(r'12/17/2021[,]? GMT-8'), ), ); testWithFormatting( 'longOffset', () => expect( - intl.dateTimeFormat().ymd( - date, - timeZone: const TimeZone.longOffset(name: timeZone, offset: offset), + intl.dateTimeFormat().ymd().withTimeZoneLongOffset().format( + dateTime, + timeZone, ), matches(r'12/17/2021[,]? GMT-08:00'), ), @@ -105,9 +103,9 @@ void main() { testWithFormatting( 'shortGeneric', () => expect( - intl.dateTimeFormat().ymd( - date, - timeZone: const TimeZone.shortGeneric(name: timeZone, offset: offset), + intl.dateTimeFormat().ymd().withTimeZoneShortGeneric().format( + dateTime, + timeZone, ), matches(r'12/17/2021[,]? PT'), ), @@ -115,9 +113,9 @@ void main() { testWithFormatting( 'longGeneric', () => expect( - intl.dateTimeFormat().ymd( - date, - timeZone: const TimeZone.longGeneric(name: timeZone, offset: offset), + intl.dateTimeFormat().ymd().withTimeZoneLongGeneric().format( + dateTime, + timeZone, ), matches(r'12/17/2021[,]? Pacific Time'), ), @@ -125,16 +123,18 @@ void main() { }); group('timezone', () { - final date = DateTime(2021, 12, 17, 3, 0, 42); + final dateTime = DateTime(2021, 12, 17, 3, 0, 42); final intl = Intl(locale: Locale.parse('en-US')); - const timeZone = 'America/Los_Angeles'; - const offset = Duration(hours: -8); + const timeZone = TimeZone( + name: 'America/Los_Angeles', + offset: Duration(hours: -8), + ); testWithFormatting( 'short', () => expect( - intl.dateTimeFormat().ymdt( - date, - timeZone: const TimeZone.short(name: timeZone, offset: offset), + intl.dateTimeFormat().ymdt().withTimeZoneShort().format( + dateTime, + timeZone, ), matches(r'12/17/2021[,]? 3\sAM PST'), ), @@ -142,9 +142,9 @@ void main() { testWithFormatting( 'long', () => expect( - intl.dateTimeFormat().ymdt( - date, - timeZone: const TimeZone.long(name: timeZone, offset: offset), + intl.dateTimeFormat().ymdt().withTimeZoneLong().format( + dateTime, + timeZone, ), matches(r'12/17/2021[,]? 3\sAM Pacific Standard Time'), ), @@ -154,22 +154,18 @@ void main() { () => expect( intl .dateTimeFormat(const DateTimeFormatOptions()) - .ymdt( - date, - timeZone: const TimeZone.shortOffset( - name: timeZone, - offset: offset, - ), - ), + .ymdt() + .withTimeZoneShortOffset() + .format(dateTime, timeZone), matches(r'12/17/2021[,]? 3\sAM GMT-8'), ), ); testWithFormatting( 'longOffset', () => expect( - intl.dateTimeFormat().ymdt( - date, - timeZone: const TimeZone.longOffset(name: timeZone, offset: offset), + intl.dateTimeFormat().ymdt().withTimeZoneLongOffset().format( + dateTime, + timeZone, ), matches(r'12/17/2021[,]? 3\sAM GMT-08:00'), ), @@ -177,9 +173,9 @@ void main() { testWithFormatting( 'shortGeneric', () => expect( - intl.dateTimeFormat().ymdt( - date, - timeZone: const TimeZone.shortGeneric(name: timeZone, offset: offset), + intl.dateTimeFormat().ymdt().withTimeZoneShortGeneric().format( + dateTime, + timeZone, ), matches(r'12/17/2021[,]? 3\sAM PT'), ), @@ -187,9 +183,9 @@ void main() { testWithFormatting( 'longGeneric', () => expect( - intl.dateTimeFormat().ymdt( - date, - timeZone: const TimeZone.longGeneric(name: timeZone, offset: offset), + intl.dateTimeFormat().ymdt().withTimeZoneLongGeneric().format( + dateTime, + timeZone, ), matches(r'12/17/2021[,]? 3\sAM Pacific Time'), ), @@ -197,7 +193,7 @@ void main() { }); group('day period', () { - final date = DateTime(2021, 12, 17, 4, 0, 42); + final dateTime = DateTime(2021, 12, 17, 4, 0, 42); testWithFormatting( 'short', () => expect( @@ -208,7 +204,8 @@ void main() { dayPeriod: DayPeriod.short, ), ) - .time(date), + .t() + .format(dateTime), '4 at night', ), ); @@ -223,7 +220,8 @@ void main() { dayPeriod: DayPeriod.narrow, ), ) - .time(date), + .t() + .format(dateTime), '4 mat.', ), ); @@ -238,24 +236,22 @@ void main() { dayPeriod: DayPeriod.long, ), ) - .time(date), + .t() + .format(dateTime), '4 du matin', ), ); }, tags: ['icu4xUnimplemented']); group('date style', () { - final date = DateTime(2021, 12, 17, 4, 0, 42); + final dateTime = DateTime(2021, 12, 17, 4, 0, 42); testWithFormatting( 'short', () => expect( Intl(locale: Locale.parse('en')) - .dateTimeFormat( - const DateTimeFormatOptions( - dateFormatStyle: DateFormatStyle.short, - ), - ) - .ymd(date), + .dateTimeFormat() + .ymd(dateStyle: DateFormatStyle.short) + .format(dateTime), '12/17/21', ), ); @@ -263,12 +259,9 @@ void main() { 'medium', () => expect( Intl(locale: Locale.parse('en')) - .dateTimeFormat( - const DateTimeFormatOptions( - dateFormatStyle: DateFormatStyle.medium, - ), - ) - .ymd(date), + .dateTimeFormat() + .ymd(dateStyle: DateFormatStyle.medium) + .format(dateTime), 'Dec 17, 2021', ), ); @@ -276,70 +269,54 @@ void main() { 'long', () => expect( Intl(locale: Locale.parse('en')) - .dateTimeFormat( - const DateTimeFormatOptions( - dateFormatStyle: DateFormatStyle.long, - ), - ) - .ymd(date), + .dateTimeFormat() + .ymd(dateStyle: DateFormatStyle.long) + .format(dateTime), 'December 17, 2021', ), ); }); group('time style', () { - final date = DateTime(2021, 12, 17, 4, 0, 0); + final dateTime = DateTime(2021, 12, 17, 4, 0, 0); final intl = Intl(locale: Locale.parse('en')); testWithFormatting( 'short', () => expect( - intl - .dateTimeFormat( - const DateTimeFormatOptions( - timeFormatStyle: TimeFormatStyle.short, - ), - ) - .time(date), + intl.dateTimeFormat().t(style: TimeFormatStyle.short).format(dateTime), matches(r'^4:00\sAM$'), ), ); testWithFormatting( 'medium', () => expect( - intl - .dateTimeFormat( - const DateTimeFormatOptions( - timeFormatStyle: TimeFormatStyle.medium, - ), - ) - .time(date), + intl.dateTimeFormat().t(style: TimeFormatStyle.medium).format(dateTime), matches(r'^4:00:00\sAM$'), ), ); }); group('datetime style', () { - final date = DateTime(2021, 12, 17, 4, 0, 42); + final dateTime = DateTime(2021, 12, 17, 4, 0, 42); final intl = Intl(locale: Locale.parse('en')); testWithFormatting( 'medium short', () => expect( intl - .dateTimeFormat( - const DateTimeFormatOptions( - timeFormatStyle: TimeFormatStyle.medium, - dateFormatStyle: DateFormatStyle.short, - ), + .dateTimeFormat() + .ymdt( + dateStyle: DateFormatStyle.short, + timeStyle: TimeFormatStyle.medium, ) - .ymdt(date), + .format(dateTime), matches(r'^12/17/21, 4:00:42\sAM$'), ), ); }); group('individual options', () { - final date = DateTime(2025, 6, 18, 10, 30, 45, 123); + final dateTime = DateTime(2025, 6, 18, 10, 30, 45, 123); group('calendar', () { testWithFormatting( @@ -347,12 +324,10 @@ void main() { () => expect( Intl(locale: Locale.parse('en-US')) .dateTimeFormat( - const DateTimeFormatOptions( - calendar: Calendar.chinese, - dateFormatStyle: DateFormatStyle.short, - ), + const DateTimeFormatOptions(calendar: Calendar.chinese), ) - .ymd(date), + .ymd(dateStyle: DateFormatStyle.short) + .format(dateTime), '5/23/2025', ), ); @@ -362,12 +337,10 @@ void main() { () => expect( Intl(locale: Locale.parse('en-US')) .dateTimeFormat( - const DateTimeFormatOptions( - calendar: Calendar.japanese, - dateFormatStyle: DateFormatStyle.short, - ), + const DateTimeFormatOptions(calendar: Calendar.japanese), ) - .ymd(date), + .ymd(dateStyle: DateFormatStyle.short) + .format(dateTime), '6/18/7 R', ), ); @@ -377,12 +350,10 @@ void main() { () => expect( Intl(locale: Locale.parse('ar')) .dateTimeFormat( - const DateTimeFormatOptions( - calendar: Calendar.islamicCivil, - dateFormatStyle: DateFormatStyle.short, - ), + const DateTimeFormatOptions(calendar: Calendar.islamicCivil), ) - .ymd(date), + .ymd(dateStyle: DateFormatStyle.short) + .format(dateTime), // Dhu al-Hijjah 12, 1446 AH '21‏/12‏/1446 هـ', // 12/11/1446 AH ), @@ -397,10 +368,10 @@ void main() { .dateTimeFormat( const DateTimeFormatOptions( numberingSystem: NumberingSystem.arab, - dateFormatStyle: DateFormatStyle.short, ), ) - .ymd(DateTime(2025, 6, 18)), + .ymd(dateStyle: DateFormatStyle.short) + .format(DateTime(2025, 6, 18)), '٦/١٨/٢٥', // 18/6/25 in Arabic numerals ), ); @@ -412,10 +383,10 @@ void main() { .dateTimeFormat( const DateTimeFormatOptions( numberingSystem: NumberingSystem.deva, - dateFormatStyle: DateFormatStyle.short, ), ) - .ymd(DateTime(2025, 6, 18)), + .ymd(dateStyle: DateFormatStyle.short) + .format(DateTime(2025, 6, 18)), '६/१८/२५', // 18/6/25 in Devanagari numerals ), ); @@ -424,12 +395,9 @@ void main() { 'numberingSystem - devanagari in locale', () => expect( Intl(locale: Locale.parse('en-US-u-nu-deva')) - .dateTimeFormat( - const DateTimeFormatOptions( - dateFormatStyle: DateFormatStyle.short, - ), - ) - .ymd(DateTime(2025, 6, 18)), + .dateTimeFormat() + .ymd(dateStyle: DateFormatStyle.short) + .format(DateTime(2025, 6, 18)), '६/१८/२५', // 18/6/25 in Devanagari numerals ), ); @@ -441,10 +409,10 @@ void main() { .dateTimeFormat( const DateTimeFormatOptions( numberingSystem: NumberingSystem.thai, - dateFormatStyle: DateFormatStyle.short, ), ) - .ymd(DateTime(2025, 6, 18)), + .ymd(dateStyle: DateFormatStyle.short) + .format(DateTime(2025, 6, 18)), '๖/๑๘/๒๕', // 18/6/25 in Thai numerals ), ); @@ -461,7 +429,8 @@ void main() { clockstyle: ClockStyle.zeroToTwentyThree, ), ) - .time(DateTime(2025, 6, 18, 15, 30, 0)), + .t() + .format(DateTime(2025, 6, 18, 15, 30, 0)), '15:30', ), ); @@ -476,7 +445,8 @@ void main() { clockstyle: ClockStyle.zeroToEleven, ), ) - .time(DateTime(2025, 6, 18, 15, 30, 0)), + .t() + .format(DateTime(2025, 6, 18, 15, 30, 0)), matches(r'03:30\sPM'), ), ); @@ -492,7 +462,8 @@ void main() { dayPeriod: DayPeriod.short, ), ) - .time(DateTime(2025, 6, 18, 0, 30, 0)), + .t() + .format(DateTime(2025, 6, 18, 0, 30, 0)), '12:30 at night', ), tags: ['icu4xUnimplemented'], @@ -509,7 +480,8 @@ void main() { dayPeriod: DayPeriod.short, ), ) - .time(DateTime(2025, 6, 18, 0, 30, 0)), + .t() + .format(DateTime(2025, 6, 18, 0, 30, 0)), '12:30 at night', ), tags: ['icu4xUnimplemented'], @@ -524,7 +496,8 @@ void main() { .dateTimeFormat( const DateTimeFormatOptions(timestyle: TimeStyle.numeric), ) - .y(date), + .y() + .format(dateTime), '2025', ), ); @@ -534,7 +507,8 @@ void main() { () => expect( Intl(locale: Locale.parse('en-US')) .dateTimeFormat(const DateTimeFormatOptions()) - .md(DateTime(2025, 6, 18)), + .md() + .format(DateTime(2025, 6, 18)), '6/18', ), ); @@ -546,7 +520,8 @@ void main() { .dateTimeFormat( const DateTimeFormatOptions(timestyle: TimeStyle.numeric), ) - .md(DateTime(2025, 1, 18)), // January + .md() + .format(DateTime(2025, 1, 18)), // January '1/18', ), ); @@ -558,7 +533,8 @@ void main() { .dateTimeFormat( const DateTimeFormatOptions(timestyle: TimeStyle.twodigit), ) - .md(DateTime(2025, 6, 8)), + .md() + .format(DateTime(2025, 6, 8)), '06/08', ), ); @@ -570,7 +546,8 @@ void main() { .dateTimeFormat( const DateTimeFormatOptions(timestyle: TimeStyle.twodigit), ) - .time(DateTime(2025, 6, 18, 7, 30, 0)), + .t() + .format(DateTime(2025, 6, 18, 7, 30, 0)), matches(r'7:30\sAM'), ), ); @@ -582,7 +559,8 @@ void main() { .dateTimeFormat( const DateTimeFormatOptions(timestyle: TimeStyle.twodigit), ) - .time(DateTime(2025, 6, 18, 7, 5, 0)), + .t() + .format(DateTime(2025, 6, 18, 7, 5, 0)), matches(r'7:05\sAM'), ), ); @@ -599,7 +577,8 @@ void main() { fractionalSecondDigits: 1, ), ) - .time(DateTime(2025, 6, 18, 10, 30, 45, 123)), + .t() + .format(DateTime(2025, 6, 18, 10, 30, 45, 123)), matches(r'10:30:45.1\sAM'), ), ); @@ -614,39 +593,23 @@ void main() { fractionalSecondDigits: 3, ), ) - .time(DateTime(2025, 6, 18, 10, 30, 45, 123)), + .t() + .format(DateTime(2025, 6, 18, 10, 30, 45, 123)), matches(r'10:30:45.123\sAM'), ), ); }); - group('formatMatcher and localeMatcher', () { + group('formatMatcher', () { testWithFormatting( 'formatMatcher - basic', () => expect( Intl(locale: Locale.parse('en-US')) .dateTimeFormat( - const DateTimeFormatOptions( - formatMatcher: FormatMatcher.basic, - dateFormatStyle: DateFormatStyle.short, - ), + const DateTimeFormatOptions(formatMatcher: FormatMatcher.basic), ) - .ymd(DateTime(2025, 6, 18)), - '6/18/25', - ), - ); - - testWithFormatting( - 'localeMatcher - lookup', - () => expect( - Intl(locale: Locale.parse('en-US')) - .dateTimeFormat( - const DateTimeFormatOptions( - localeMatcher: LocaleMatcher.lookup, - dateFormatStyle: DateFormatStyle.short, - ), - ) - .ymd(DateTime(2025, 6, 18)), + .ymd(dateStyle: DateFormatStyle.short) + .format(DateTime(2025, 6, 18)), '6/18/25', ), ); @@ -654,7 +617,7 @@ void main() { }); group('combinations of options', () { - final date = DateTime(2025, 6, 18, 10, 30, 45, 123); + final dateTime = DateTime(2025, 6, 18, 10, 30, 45, 123); final intlEnUS = Intl(locale: Locale.parse('en-US')); group('Time Zone + Date/Time Components', () { @@ -665,9 +628,11 @@ void main() { .dateTimeFormat( const DateTimeFormatOptions(timestyle: TimeStyle.numeric), ) - .time( + .t() + .withTimeZoneLong() + .format( DateTime(2025, 6, 18, 10, 30, 0), - timeZone: const TimeZone.long( + const TimeZone( name: 'America/New_York', offset: Duration(hours: -4), ), @@ -682,13 +647,12 @@ void main() { 'French locale - long date, short time', () => expect( Intl(locale: Locale.parse('fr-FR')) - .dateTimeFormat( - const DateTimeFormatOptions( - dateFormatStyle: DateFormatStyle.long, - timeFormatStyle: TimeFormatStyle.short, - ), + .dateTimeFormat() + .ymdt( + dateStyle: DateFormatStyle.long, + timeStyle: TimeFormatStyle.short, ) - .ymdt(date), + .format(dateTime), // Example: 18 juin 2025 à 10:30 matches(r'^18 juin 2025 à 10:30$'), ), @@ -701,15 +665,17 @@ void main() { Intl(locale: Locale.parse('de-DE')) .dateTimeFormat( const DateTimeFormatOptions( - dateFormatStyle: DateFormatStyle.full, - timeFormatStyle: TimeFormatStyle.medium, clockstyle: ClockStyle.zeroToTwentyThree, ), ) - .ymdet(date), + .ymdet( + dateStyle: DateFormatStyle.full, + timeStyle: TimeFormatStyle.medium, + ) + .format(dateTime), 'Mittwoch, 18. Juni 2025 um 10:30:45', ), - testOn: 'chrome', + tags: ['icu4xUnimplemented'], ); testWithFormatting( @@ -718,12 +684,14 @@ void main() { Intl(locale: Locale.parse('de-DE')) .dateTimeFormat( const DateTimeFormatOptions( - dateFormatStyle: DateFormatStyle.full, - timeFormatStyle: TimeFormatStyle.medium, clockstyle: ClockStyle.zeroToTwentyThree, ), ) - .ymdet(date), + .ymdet( + dateStyle: DateFormatStyle.full, + timeStyle: TimeFormatStyle.medium, + ) + .format(dateTime), 'Mittwoch, 18. Juni 2025, 10:30:45', ), testOn: 'vm', diff --git a/pkgs/intl4x/test/numberformat_test.dart b/pkgs/intl4x/test/numberformat_test.dart index 22aa92726..6b5f2cf4b 100644 --- a/pkgs/intl4x/test/numberformat_test.dart +++ b/pkgs/intl4x/test/numberformat_test.dart @@ -122,18 +122,17 @@ void main() { testWithFormatting('RoundingMode', () { for (final roundingMode in RoundingMode.values) { - final expectation = - switch (roundingMode) { - RoundingMode.ceil => [2.3, 2.3, 2.3, -2.2, -2.2, -2.2], - RoundingMode.floor => [2.2, 2.2, 2.2, -2.3, -2.3, -2.3], - RoundingMode.expand => [2.3, 2.3, 2.3, -2.3, -2.3, -2.3], - RoundingMode.trunc => [2.2, 2.2, 2.2, -2.2, -2.2, -2.2], - RoundingMode.halfCeil => [2.2, 2.3, 2.3, -2.2, -2.2, -2.3], - RoundingMode.halfFloor => [2.2, 2.2, 2.3, -2.2, -2.3, -2.3], - RoundingMode.halfExpand => [2.2, 2.3, 2.3, -2.2, -2.3, -2.3], - RoundingMode.halfTrunc => [2.2, 2.2, 2.3, -2.2, -2.2, -2.3], - RoundingMode.halfEven => [2.2, 2.2, 2.3, -2.2, -2.2, -2.3], - }.map((e) => e.toString()).toList(); + final expectation = switch (roundingMode) { + RoundingMode.ceil => [2.3, 2.3, 2.3, -2.2, -2.2, -2.2], + RoundingMode.floor => [2.2, 2.2, 2.2, -2.3, -2.3, -2.3], + RoundingMode.expand => [2.3, 2.3, 2.3, -2.3, -2.3, -2.3], + RoundingMode.trunc => [2.2, 2.2, 2.2, -2.2, -2.2, -2.2], + RoundingMode.halfCeil => [2.2, 2.3, 2.3, -2.2, -2.2, -2.3], + RoundingMode.halfFloor => [2.2, 2.2, 2.3, -2.2, -2.3, -2.3], + RoundingMode.halfExpand => [2.2, 2.3, 2.3, -2.2, -2.3, -2.3], + RoundingMode.halfTrunc => [2.2, 2.2, 2.3, -2.2, -2.2, -2.3], + RoundingMode.halfEven => [2.2, 2.2, 2.3, -2.2, -2.2, -2.3], + }.map((e) => e.toString()).toList(); String formatter(Object number) => intl .numberFormat( NumberFormatOptions.custom( diff --git a/pkgs/intl4x/test/tools/conformance_parser.dart b/pkgs/intl4x/test/tools/conformance_parser.dart index a8d25a4a3..082d184b5 100644 --- a/pkgs/intl4x/test/tools/conformance_parser.dart +++ b/pkgs/intl4x/test/tools/conformance_parser.dart @@ -59,14 +59,15 @@ String? compareToReference( } String? shouldFail(Info info, Info referenceInfo) { - final moreErrors = - info.error > referenceInfo.error ? 'Too many new errors' : null; - final moreFailing = - info.failing > referenceInfo.failing ? 'Too many new failing' : null; - final moreUnsupported = - info.unsupported > referenceInfo.unsupported - ? 'Too many new unsupported' - : null; + final moreErrors = info.error > referenceInfo.error + ? 'Too many new errors' + : null; + final moreFailing = info.failing > referenceInfo.failing + ? 'Too many new failing' + : null; + final moreUnsupported = info.unsupported > referenceInfo.unsupported + ? 'Too many new unsupported' + : null; return moreErrors ?? moreFailing ?? moreUnsupported; } @@ -96,10 +97,9 @@ Map getJson(String pathToCurrent, String exec) { final decoded = jsonDecode(currentStr) as Map; return decoded.map((key, value) { - final list = - (value as List) - .where((element) => (element as Map)['exec'] == exec) - .toList(); + final list = (value as List) + .where((element) => (element as Map)['exec'] == exec) + .toList(); return MapEntry(key, list); }); }