Add ECMA402 datetime format to package:intl4x (#680)
* Adding Datetime format
* Add tests
* Rename Datetime -> DateTime
* Add readme check
* Add changelog entry
* Fix tests
* Revert name change
* Switch to stable health workflow
* Allow number in package names
* Add checkmark
* Fix merge problems
* Rev pubspec
* Add platforms to pubspec
* Renaming options
* Fixes
* Export Options from all formatters
* fix readme example
diff --git a/pkgs/intl4x/CHANGELOG.md b/pkgs/intl4x/CHANGELOG.md
index b88d09b..8290fd8 100644
--- a/pkgs/intl4x/CHANGELOG.md
+++ b/pkgs/intl4x/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.5.0
+
+- Add `DateTime` formatting.
+
## 0.4.0
- Add a `Locale` class.
diff --git a/pkgs/intl4x/README.md b/pkgs/intl4x/README.md
index e6c6751..b94a3d0 100644
--- a/pkgs/intl4x/README.md
+++ b/pkgs/intl4x/README.md
@@ -17,7 +17,7 @@
| | Number format | List format | Date format | Collation | Display names |
|---|:---:|:---:|:---:|:---:|:---:|
-| **ECMA402 (web)** | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | :heavy_check_mark: |
+| **ECMA402 (web)** | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| **ICU4X (web/native)** | | | | | |
## Implementation and Goals
@@ -36,10 +36,12 @@
import 'package:intl4x/intl4x.dart';
import 'package:intl4x/number_format.dart';
-final numberFormat = Intl(
- ecmaPolicy: const AlwaysEcma(),
- defaultLocale: Locale(language: 'en', country: 'US'),
-).numberFormat(NumberFormatOptions.percent());
+void main() {
+ final numberFormat = Intl(
+ ecmaPolicy: const AlwaysEcma(),
+ locale: const Locale(language: 'en', region: 'US'),
+ ).numberFormat(NumberFormatOptions.percent());
-print(numberFormat.format(0.5)); // prints 50%
+ print(numberFormat.format(0.5)); // prints 50%
+}
```
diff --git a/pkgs/intl4x/lib/collation.dart b/pkgs/intl4x/lib/collation.dart
index b2446dd..cc987ac 100644
--- a/pkgs/intl4x/lib/collation.dart
+++ b/pkgs/intl4x/lib/collation.dart
@@ -4,3 +4,4 @@
export 'src/collation/collation.dart';
export 'src/collation/collation_options.dart';
+export 'src/options.dart';
diff --git a/pkgs/intl4x/lib/datetime_format.dart b/pkgs/intl4x/lib/datetime_format.dart
new file mode 100644
index 0000000..6735df8
--- /dev/null
+++ b/pkgs/intl4x/lib/datetime_format.dart
@@ -0,0 +1,7 @@
+// 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.
+
+export 'src/datetime_format/datetime_format.dart';
+export 'src/datetime_format/datetime_format_options.dart';
+export 'src/options.dart';
diff --git a/pkgs/intl4x/lib/display_names.dart b/pkgs/intl4x/lib/display_names.dart
index 2acd7de..4c1ba3f 100644
--- a/pkgs/intl4x/lib/display_names.dart
+++ b/pkgs/intl4x/lib/display_names.dart
@@ -4,3 +4,4 @@
export 'src/display_names/display_names.dart';
export 'src/display_names/display_names_options.dart';
+export 'src/options.dart';
diff --git a/pkgs/intl4x/lib/intl4x.dart b/pkgs/intl4x/lib/intl4x.dart
index 6e1a9ff..35bd87f 100644
--- a/pkgs/intl4x/lib/intl4x.dart
+++ b/pkgs/intl4x/lib/intl4x.dart
@@ -7,6 +7,9 @@
import 'number_format.dart';
import 'src/collation/collation_impl.dart';
import 'src/data.dart';
+import 'src/datetime_format/datetime_format.dart';
+import 'src/datetime_format/datetime_format_impl.dart';
+import 'src/datetime_format/datetime_format_options.dart';
import 'src/display_names/display_names_impl.dart';
import 'src/ecma/ecma_policy.dart';
import 'src/ecma/ecma_stub.dart' if (dart.library.js) 'src/ecma/ecma_web.dart';
@@ -16,7 +19,6 @@
import 'src/list_format/list_format_options.dart';
import 'src/locale.dart';
import 'src/number_format/number_format_impl.dart';
-import 'src/options.dart';
export 'src/locale.dart';
@@ -66,6 +68,12 @@
DisplayNamesImpl.build(locale, localeMatcher, ecmaPolicy),
);
+ DateTimeFormat datetimeFormat([DateTimeFormatOptions? options]) =>
+ DateTimeFormat(
+ options ?? const DateTimeFormatOptions(),
+ DateTimeFormatImpl.build(locale, localeMatcher, ecmaPolicy),
+ );
+
/// Construct an [Intl] instance providing the current [locale] and the
/// [ecmaPolicy] defining which locales should fall back to the browser
/// provided functions.
diff --git a/pkgs/intl4x/lib/list_format.dart b/pkgs/intl4x/lib/list_format.dart
index f67fa01..a22f8a7 100644
--- a/pkgs/intl4x/lib/list_format.dart
+++ b/pkgs/intl4x/lib/list_format.dart
@@ -4,3 +4,4 @@
export 'src/list_format/list_format.dart';
export 'src/list_format/list_format_options.dart';
+export 'src/options.dart';
diff --git a/pkgs/intl4x/lib/number_format.dart b/pkgs/intl4x/lib/number_format.dart
index e977cfc..facd400 100644
--- a/pkgs/intl4x/lib/number_format.dart
+++ b/pkgs/intl4x/lib/number_format.dart
@@ -4,3 +4,4 @@
export 'src/number_format/number_format.dart';
export 'src/number_format/number_format_options.dart';
+export 'src/options.dart';
diff --git a/pkgs/intl4x/lib/src/datetime_format/datetime_format.dart b/pkgs/intl4x/lib/src/datetime_format/datetime_format.dart
new file mode 100644
index 0000000..0110cea
--- /dev/null
+++ b/pkgs/intl4x/lib/src/datetime_format/datetime_format.dart
@@ -0,0 +1,35 @@
+// 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 '../test_checker.dart';
+import 'datetime_format_impl.dart';
+import 'datetime_format_options.dart';
+
+/// `DateTime` formatting, for example:
+///
+/// ```dart
+/// final date = DateTime.utc(2021, 12, 17, 4, 0, 42);
+/// Intl(locale: const Locale(language: 'fr'))
+/// .datetimeFormat(const DateTimeFormatOptions(
+/// hour: TimeRepresentation.numeric,
+/// hourCycle: HourCycle.h12,
+/// dayPeriod: DayPeriod.narrow,
+/// timeZone: 'UTC',
+/// ))
+/// .format(date); // Output: '4 mat.'
+/// ```
+class DateTimeFormat {
+ final DateTimeFormatOptions _options;
+ final DateTimeFormatImpl impl;
+
+ DateTimeFormat(this._options, this.impl);
+
+ String format(DateTime datetime) {
+ if (isInTest) {
+ return '$datetime//${impl.locale}';
+ } else {
+ return impl.formatImpl(datetime, _options);
+ }
+ }
+}
diff --git a/pkgs/intl4x/lib/src/datetime_format/datetime_format_4x.dart b/pkgs/intl4x/lib/src/datetime_format/datetime_format_4x.dart
new file mode 100644
index 0000000..4c136c3
--- /dev/null
+++ b/pkgs/intl4x/lib/src/datetime_format/datetime_format_4x.dart
@@ -0,0 +1,19 @@
+// 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 '../locale.dart';
+import 'datetime_format_impl.dart';
+import 'datetime_format_options.dart';
+
+DateTimeFormatImpl getDateTimeFormatter4X(Locale locale) =>
+ DateTimeFormat4X(locale);
+
+class DateTimeFormat4X extends DateTimeFormatImpl {
+ DateTimeFormat4X(super.locale);
+
+ @override
+ String formatImpl(DateTime datetime, DateTimeFormatOptions options) {
+ throw UnimplementedError('Insert diplomat bindings here');
+ }
+}
diff --git a/pkgs/intl4x/lib/src/datetime_format/datetime_format_ecma.dart b/pkgs/intl4x/lib/src/datetime_format/datetime_format_ecma.dart
new file mode 100644
index 0000000..4df5494
--- /dev/null
+++ b/pkgs/intl4x/lib/src/datetime_format/datetime_format_ecma.dart
@@ -0,0 +1,160 @@
+// 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:js/js.dart';
+import 'package:js/js_util.dart';
+
+import '../locale.dart';
+import '../options.dart';
+import 'datetime_format_impl.dart';
+import 'datetime_format_options.dart';
+
+DateTimeFormatImpl? getDateTimeFormatterECMA(
+ Locale locale,
+ LocaleMatcher localeMatcher,
+) =>
+ _DateTimeFormatECMA.tryToBuild(locale, localeMatcher);
+
+@JS('Intl.DateTimeFormat')
+class _DateTimeFormatJS {
+ external factory _DateTimeFormatJS([List<String> locale, Object options]);
+ external String format(Object num);
+}
+
+@JS('Intl.DateTimeFormat.supportedLocalesOf')
+external List<String> _supportedLocalesOfJS(
+ List<String> listOfLocales, [
+ Object options,
+]);
+
+@JS('Date')
+class DateJS {
+ external factory DateJS(
+ int year,
+ int monthIndex,
+ int day,
+ int hours,
+ int minutes,
+ int seconds,
+ int milliseconds,
+ );
+
+ external factory DateJS.fromTimeStamp(int timeStamp);
+}
+
+@JS('Date.UTC')
+// ignore: non_constant_identifier_names
+external int UTC(
+ int year,
+ int monthIndex,
+ int day,
+ int hours,
+ int minutes,
+ int seconds,
+ int milliseconds,
+);
+
+class _DateTimeFormatECMA extends DateTimeFormatImpl {
+ _DateTimeFormatECMA(super.locale);
+
+ static DateTimeFormatImpl? tryToBuild(
+ Locale locale,
+ LocaleMatcher localeMatcher,
+ ) {
+ final supportedLocales = supportedLocalesOf(localeMatcher, locale);
+ return supportedLocales.isNotEmpty
+ ? _DateTimeFormatECMA(supportedLocales.first)
+ : null; //TODO: Add support to force return an instance instead of null.
+ }
+
+ static List<Locale> supportedLocalesOf(
+ LocaleMatcher localeMatcher,
+ Locale locale,
+ ) {
+ final o = newObject<Object>();
+ setProperty(o, 'localeMatcher', localeMatcher.jsName);
+ return List.from(_supportedLocalesOfJS([locale.toLanguageTag()], o))
+ .whereType<String>()
+ .map(Locale.parse)
+ .toList();
+ }
+
+ @override
+ String formatImpl(DateTime datetime, DateTimeFormatOptions options) {
+ final datetimeFormatJS = _DateTimeFormatJS(
+ [locale.toLanguageTag()],
+ options.toJsOptions(),
+ );
+ return datetimeFormatJS.format(datetime.toJs());
+ }
+}
+
+extension on DateTime {
+ DateJS toJs() {
+ if (isUtc) {
+ return DateJS.fromTimeStamp(
+ UTC(year, month - 1, day, hour, minute, second, millisecond));
+ } else {
+ return DateJS(year, month - 1, day, hour, minute, second, millisecond);
+ }
+ }
+}
+
+extension on DateTimeFormatOptions {
+ Object toJsOptions() {
+ final o = newObject<Object>();
+ setProperty(o, 'localeMatcher', localeMatcher.jsName);
+ if (dateFormatStyle != null) {
+ setProperty(o, 'dateStyle', dateFormatStyle!.name);
+ }
+ if (timeFormatStyle != null) {
+ setProperty(o, 'timeStyle', timeFormatStyle!.name);
+ }
+ if (calendar != null) setProperty(o, 'calendar', calendar!.jsName);
+ if (dayPeriod != null) setProperty(o, 'dayPeriod', dayPeriod!.name);
+ if (numberingSystem != null) {
+ setProperty(o, 'numberingSystem', numberingSystem!.name);
+ }
+ if (timeZone != null) setProperty(o, 'timeZone', timeZone!);
+ if (clockstyle != null) {
+ setProperty(o, 'hour12', clockstyle!.is12Hour);
+ if (clockstyle!.startAtZero != null) {
+ setProperty(o, 'hourCycle', clockstyle!.hourStyleJsString());
+ }
+ }
+ if (weekday != null) setProperty(o, 'weekday', weekday!.name);
+ if (era != null) setProperty(o, 'era', era!.name);
+ if (year != null) setProperty(o, 'year', year!.jsName);
+ if (month != null) setProperty(o, 'month', month!.jsName);
+ if (day != null) setProperty(o, 'day', day!.jsName);
+ if (hour != null) setProperty(o, 'hour', hour!.jsName);
+ if (minute != null) setProperty(o, 'minute', minute!.jsName);
+ if (second != null) setProperty(o, 'second', second!.jsName);
+ if (fractionalSecondDigits != null) {
+ setProperty(o, 'fractionalSecondDigits', fractionalSecondDigits!);
+ }
+ if (timeZoneName != null) {
+ setProperty(o, 'timeZoneName', timeZoneName!.name);
+ }
+ setProperty(o, 'formatMatcher', formatMatcher.jsName);
+ return o;
+ }
+}
+
+extension on ClockStyle {
+ String hourStyleJsString() {
+ // The four possible values are h11, h12, h23, h24.
+ final firstDigit = is12Hour ? 1 : 2;
+
+ final subtrahend = startAtZero! ? 1 : 0;
+ final secondDigit = firstDigit * 2 - subtrahend;
+
+ /// The cases are
+ /// * firstDigit == 1 && subtrahend == 1 --> h11
+ /// * firstDigit == 1 && subtrahend == 0 --> h12
+ /// * firstDigit == 2 && subtrahend == 1 --> h23
+ /// * firstDigit == 2 && subtrahend == 0 --> h24
+ return 'h$firstDigit$secondDigit';
+ }
+}
diff --git a/pkgs/intl4x/lib/src/datetime_format/datetime_format_impl.dart b/pkgs/intl4x/lib/src/datetime_format/datetime_format_impl.dart
new file mode 100644
index 0000000..8d85adc
--- /dev/null
+++ b/pkgs/intl4x/lib/src/datetime_format/datetime_format_impl.dart
@@ -0,0 +1,35 @@
+// 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 '../ecma/ecma_policy.dart';
+import '../locale.dart';
+import '../options.dart';
+import '../utils.dart';
+import 'datetime_format_4x.dart';
+import 'datetime_format_options.dart';
+import 'datetime_format_stub.dart'
+ if (dart.library.js) 'datetime_format_ecma.dart';
+
+/// This is an intermediate to defer to the actual implementations of
+/// datetime formatting.
+abstract class DateTimeFormatImpl {
+ final Locale locale;
+
+ DateTimeFormatImpl(this.locale);
+
+ String formatImpl(DateTime datetime, DateTimeFormatOptions options);
+
+ factory DateTimeFormatImpl.build(
+ Locale locale,
+ LocaleMatcher localeMatcher,
+ EcmaPolicy ecmaPolicy,
+ ) =>
+ buildFormatter(
+ locale,
+ localeMatcher,
+ ecmaPolicy,
+ getDateTimeFormatterECMA,
+ getDateTimeFormatter4X,
+ );
+}
diff --git a/pkgs/intl4x/lib/src/datetime_format/datetime_format_options.dart b/pkgs/intl4x/lib/src/datetime_format/datetime_format_options.dart
new file mode 100644
index 0000000..24c0de4
--- /dev/null
+++ b/pkgs/intl4x/lib/src/datetime_format/datetime_format_options.dart
@@ -0,0 +1,170 @@
+// 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 '../options.dart';
+
+typedef WeekDayStyle = Style;
+typedef DayPeriod = Style;
+typedef EraStyle = Style;
+typedef DateFormatStyle = TimeFormatStyle;
+
+/// 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
+ /// [clockstyle] parameter is true.
+ final DayPeriod? dayPeriod;
+ final NumberingSystem? numberingSystem;
+ final String? timeZone;
+
+ /// Whether to use a 12- or 24-hour style clock.
+ final ClockStyle? clockstyle;
+ final WeekDayStyle? weekday;
+ final EraStyle? era;
+ final TimeStyle? year;
+ final MonthStyle? month;
+ final TimeStyle? day;
+ final TimeStyle? hour;
+ final TimeStyle? minute;
+ final TimeStyle? second;
+
+ /// The number of digits used to represent fractions of a second.
+ final int? fractionalSecondDigits;
+
+ /// The localized representation of the time zone name.
+ final TimeZoneName? timeZoneName;
+ final FormatMatcher formatMatcher;
+ final LocaleMatcher localeMatcher;
+
+ const DateTimeFormatOptions({
+ this.dateFormatStyle,
+ this.timeFormatStyle,
+ this.calendar,
+ this.dayPeriod,
+ this.numberingSystem,
+ this.timeZone,
+ this.clockstyle,
+ this.weekday,
+ this.era,
+ this.year,
+ this.month,
+ this.day,
+ this.hour,
+ this.minute,
+ this.second,
+ this.fractionalSecondDigits,
+ this.timeZoneName,
+ this.formatMatcher = FormatMatcher.bestfit,
+ this.localeMatcher = LocaleMatcher.bestfit,
+ });
+}
+
+class ClockStyle {
+ final bool is12Hour;
+ final bool? startAtZero;
+
+ const ClockStyle({required this.is12Hour, this.startAtZero});
+}
+
+enum TimeFormatStyle {
+ full,
+ long,
+ medium,
+ short,
+}
+
+enum NumberingSystem {
+ arab,
+ arabext,
+ bali,
+ beng,
+ deva,
+ fullwide,
+ gujr,
+ guru,
+ hanidec,
+ khmr,
+ knda,
+ laoo,
+ latn,
+ limb,
+ mlym,
+ mong,
+ mymr,
+ orya,
+ tamldec,
+ telu,
+ thai,
+ tibt;
+}
+
+enum HourCycle {
+ h11,
+ h12,
+ h23,
+ h24;
+}
+
+enum FormatMatcher {
+ basic,
+ bestfit('best fit');
+
+ final String? _jsName;
+
+ String? get jsName => _jsName ?? name;
+
+ const FormatMatcher([this._jsName]);
+}
+
+enum MonthStyle {
+ numeric,
+ twodigit('2-digit'),
+ long,
+ short,
+ narrow;
+
+ String get jsName => _jsName ?? name;
+
+ final String? _jsName;
+
+ const MonthStyle([this._jsName]);
+}
+
+enum TimeStyle {
+ numeric,
+ twodigit('2-digit');
+
+ String get jsName => _jsName ?? name;
+
+ final String? _jsName;
+
+ const TimeStyle([this._jsName]);
+}
+
+enum TimeZoneName {
+ /// Example: `Pacific Standard Time`
+ long,
+
+ /// Example: `PST`
+ short,
+
+ /// Example: `GMT-8`
+ shortOffset,
+
+ /// Example: `GMT-0800`
+ longOffset,
+
+ /// Example: `PT`
+ shortGeneric,
+
+ /// Example: `Pacific Time`
+ longGeneric;
+}
diff --git a/pkgs/intl4x/lib/src/datetime_format/datetime_format_stub.dart b/pkgs/intl4x/lib/src/datetime_format/datetime_format_stub.dart
new file mode 100644
index 0000000..470952c
--- /dev/null
+++ b/pkgs/intl4x/lib/src/datetime_format/datetime_format_stub.dart
@@ -0,0 +1,13 @@
+// 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 '../locale.dart';
+import '../options.dart';
+import 'datetime_format_impl.dart';
+
+DateTimeFormatImpl? getDateTimeFormatterECMA(
+ Locale locales,
+ LocaleMatcher localeMatcher,
+) =>
+ throw UnimplementedError('Cannot use ECMA outside of web environments.');
diff --git a/pkgs/intl4x/lib/src/display_names/display_names_4x.dart b/pkgs/intl4x/lib/src/display_names/display_names_4x.dart
index 03fd666..8f419d3 100644
--- a/pkgs/intl4x/lib/src/display_names/display_names_4x.dart
+++ b/pkgs/intl4x/lib/src/display_names/display_names_4x.dart
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import '../locale.dart';
+import '../options.dart';
import 'display_names_impl.dart';
import 'display_names_options.dart';
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 7a15512..26dd398 100644
--- a/pkgs/intl4x/lib/src/display_names/display_names_options.dart
+++ b/pkgs/intl4x/lib/src/display_names/display_names_options.dart
@@ -19,12 +19,6 @@
});
}
-enum Style {
- narrow,
- short,
- long,
-}
-
enum DisplayType {
calendar,
currency,
@@ -57,30 +51,3 @@
minute,
second,
}
-
-enum Calendar {
- buddhist,
- chinese,
- coptic,
- dangi,
- ethioaa,
- ethiopic,
- gregory,
- hebrew,
- indian,
- islamic,
- islamicUmalqura('islamic-umalqura'),
- islamicTbla('islamic-tbla'),
- islamicCivil('islamic-civil'),
- islamicRgsa('islamic-rgsa'),
- iso8601,
- japanese,
- persian,
- roc;
-
- String get jsName => _jsName ?? name;
-
- final String? _jsName;
-
- const Calendar([this._jsName]);
-}
diff --git a/pkgs/intl4x/lib/src/list_format/list_format.dart b/pkgs/intl4x/lib/src/list_format/list_format.dart
index 786d6e6..aaadadf 100644
--- a/pkgs/intl4x/lib/src/list_format/list_format.dart
+++ b/pkgs/intl4x/lib/src/list_format/list_format.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 '../options.dart';
import '../test_checker.dart';
import 'list_format_impl.dart';
import 'list_format_options.dart';
@@ -17,12 +16,7 @@
/// ```dart
/// format(['A', 'B', 'C']) == 'A, B, and C'
/// ```
- String format(
- List<String> list, {
- LocaleMatcher localeMatcher = LocaleMatcher.bestfit,
- Type type = Type.conjunction,
- ListStyle style = ListStyle.long,
- }) {
+ String format(List<String> list) {
if (isInTest) {
return '${list.join(', ')}//${_listFormatImpl.locale}';
} else {
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 f8faa3e..aa9381c 100644
--- a/pkgs/intl4x/lib/src/list_format/list_format_options.dart
+++ b/pkgs/intl4x/lib/src/list_format/list_format_options.dart
@@ -4,8 +4,16 @@
import '../options.dart';
+typedef ListStyle = Style;
+
class ListFormatOptions {
final Type type;
+
+ /// Indicates the grouping style (for example, whether list separators and
+ /// conjunctions are included).
+ /// * long: "A, B, and C".
+ /// * short: "A, B, C".
+ /// * narrow: "A B C".
final ListStyle style;
final LocaleMatcher localeMatcher;
@@ -27,16 +35,3 @@
/// Grouping the list items as a unit: "A, B, C".
unit;
}
-
-/// Indicates the grouping style (for example, whether list separators and
-/// conjunctions are included).
-enum ListStyle {
- /// Example: "A, B, and C".
- long,
-
- /// Example: "A, B, C".
- short,
-
- /// Example: "A B C".
- narrow;
-}
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 24e6de7..8fc5a57 100644
--- a/pkgs/intl4x/lib/src/number_format/number_format_options.dart
+++ b/pkgs/intl4x/lib/src/number_format/number_format_options.dart
@@ -6,9 +6,11 @@
import '../options.dart';
+typedef UnitDisplay = Style;
+
/// Number formatting functionality of the browser.
class NumberFormatOptions {
- final Style style;
+ final FormatStyle style;
final String? currency;
final CurrencyDisplay? currencyDisplay;
final Unit? unit;
@@ -131,7 +133,7 @@
factory NumberFormatOptions.compact({
CompactDisplay compactDisplay = CompactDisplay.short,
//General options
- Style style = const DecimalStyle(),
+ FormatStyle style = const DecimalStyle(),
LocaleMatcher localeMatcher = LocaleMatcher.bestfit,
SignDisplay signDisplay = SignDisplay.auto,
Grouping useGrouping = Grouping.auto,
@@ -155,7 +157,7 @@
);
}
- static Digits? getDigits(Style style, Digits? digits) {
+ static Digits? getDigits(FormatStyle style, Digits? digits) {
final fractionDigits = digits?.fractionDigits;
if (fractionDigits != null) {
final int newMin;
@@ -324,12 +326,6 @@
accounting;
}
-enum UnitDisplay {
- long,
- short,
- narrow;
-}
-
enum SignDisplay {
auto,
always,
@@ -419,20 +415,20 @@
String get name => 'engineering';
}
-sealed class Style {
+sealed class FormatStyle {
String get name;
- const Style();
+ const FormatStyle();
}
-final class DecimalStyle extends Style {
+final class DecimalStyle extends FormatStyle {
const DecimalStyle();
@override
String get name => 'decimal';
}
-final class CurrencyStyle extends Style {
+final class CurrencyStyle extends FormatStyle {
final String currency;
final CurrencySign sign;
final CurrencyDisplay display;
@@ -446,13 +442,13 @@
String get name => 'currency';
}
-final class PercentStyle extends Style {
+final class PercentStyle extends FormatStyle {
const PercentStyle();
@override
String get name => 'percent';
}
-final class UnitStyle extends Style {
+final class UnitStyle extends FormatStyle {
final Unit unit;
final UnitDisplay unitDisplay;
diff --git a/pkgs/intl4x/lib/src/options.dart b/pkgs/intl4x/lib/src/options.dart
index 090f3a4..93622a9 100644
--- a/pkgs/intl4x/lib/src/options.dart
+++ b/pkgs/intl4x/lib/src/options.dart
@@ -26,3 +26,36 @@
const LocaleMatcher([this._jsName]);
}
+
+enum Calendar {
+ buddhist,
+ chinese,
+ coptic,
+ dangi,
+ ethioaa,
+ ethiopic,
+ gregory,
+ hebrew,
+ indian,
+ islamic,
+ islamicUmalqura('islamic-umalqura'),
+ islamicTbla('islamic-tbla'),
+ islamicCivil('islamic-civil'),
+ islamicRgsa('islamic-rgsa'),
+ iso8601,
+ japanese,
+ persian,
+ roc;
+
+ String get jsName => _jsName ?? name;
+
+ final String? _jsName;
+
+ const Calendar([this._jsName]);
+}
+
+enum Style {
+ narrow,
+ short,
+ long,
+}
diff --git a/pkgs/intl4x/pubspec.yaml b/pkgs/intl4x/pubspec.yaml
index 5696874..c5e6f88 100644
--- a/pkgs/intl4x/pubspec.yaml
+++ b/pkgs/intl4x/pubspec.yaml
@@ -1,8 +1,11 @@
name: intl4x
description: >-
A lightweight modular library for internationalization (i18n) functionality.
-version: 0.4.0
+version: 0.5.0
repository: https://github.com/dart-lang/i18n/tree/main/pkgs/intl4x
+platforms: ## TODO: Add native platforms once ICU4X is integrated.
+ web:
+
environment:
sdk: '>=3.0.0 <4.0.0'
diff --git a/pkgs/intl4x/test/ecma/datetime_format_test.dart b/pkgs/intl4x/test/ecma/datetime_format_test.dart
new file mode 100644
index 0000000..c1be29b
--- /dev/null
+++ b/pkgs/intl4x/test/ecma/datetime_format_test.dart
@@ -0,0 +1,156 @@
+// 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.
+
+@TestOn('browser')
+library;
+
+import 'package:intl4x/datetime_format.dart';
+import 'package:intl4x/intl4x.dart';
+import 'package:test/test.dart';
+
+import '../utils.dart';
+
+void main() {
+ testWithFormatting('Basic', () {
+ expect(
+ Intl(locale: const Locale(language: 'en', region: 'US'))
+ .datetimeFormat()
+ .format(DateTime.utc(2012, 12, 20, 3, 0, 0)),
+ '12/20/2012');
+ });
+
+ testWithFormatting('timezone', () {
+ final date = DateTime.utc(2021, 12, 17, 3, 0, 42);
+ final intl = Intl(locale: const Locale(language: 'en', region: 'US'));
+ final timeZone = 'America/Los_Angeles';
+ expect(
+ intl
+ .datetimeFormat(DateTimeFormatOptions(
+ timeZone: timeZone,
+ timeZoneName: TimeZoneName.short,
+ ))
+ .format(date),
+ '12/16/2021, PST',
+ );
+ expect(
+ intl
+ .datetimeFormat(DateTimeFormatOptions(
+ timeZone: timeZone,
+ timeZoneName: TimeZoneName.long,
+ ))
+ .format(date),
+ '12/16/2021, Pacific Standard Time',
+ );
+ expect(
+ intl
+ .datetimeFormat(DateTimeFormatOptions(
+ timeZone: timeZone,
+ timeZoneName: TimeZoneName.shortOffset,
+ ))
+ .format(date),
+ '12/16/2021, GMT-8',
+ );
+ expect(
+ intl
+ .datetimeFormat(DateTimeFormatOptions(
+ timeZone: timeZone,
+ timeZoneName: TimeZoneName.longOffset,
+ ))
+ .format(date),
+ '12/16/2021, GMT-08:00',
+ );
+ expect(
+ intl
+ .datetimeFormat(DateTimeFormatOptions(
+ timeZone: timeZone,
+ timeZoneName: TimeZoneName.shortGeneric,
+ ))
+ .format(date),
+ '12/16/2021, PT',
+ );
+ expect(
+ intl
+ .datetimeFormat(DateTimeFormatOptions(
+ timeZone: timeZone,
+ timeZoneName: TimeZoneName.longGeneric,
+ ))
+ .format(date),
+ '12/16/2021, Pacific Time',
+ );
+ });
+
+ testWithFormatting('day period', () {
+ final date = DateTime.utc(2021, 12, 17, 4, 0, 42);
+ expect(
+ Intl(locale: const Locale(language: 'en', region: 'GB'))
+ .datetimeFormat(const DateTimeFormatOptions(
+ hour: TimeStyle.numeric,
+ clockstyle: ClockStyle(
+ is12Hour: true,
+ startAtZero: false,
+ ),
+ dayPeriod: DayPeriod.short,
+ timeZone: 'UTC',
+ ))
+ .format(date),
+ '4 at night');
+
+ expect(
+ Intl(locale: const Locale(language: 'fr'))
+ .datetimeFormat(const DateTimeFormatOptions(
+ hour: TimeStyle.numeric,
+ clockstyle: ClockStyle(
+ is12Hour: true,
+ startAtZero: false,
+ ),
+ dayPeriod: DayPeriod.narrow,
+ timeZone: 'UTC',
+ ))
+ .format(date),
+ '4 mat.');
+
+ expect(
+ Intl(locale: const Locale(language: 'fr'))
+ .datetimeFormat(const DateTimeFormatOptions(
+ hour: TimeStyle.numeric,
+ clockstyle: ClockStyle(
+ is12Hour: true,
+ startAtZero: false,
+ ),
+ dayPeriod: DayPeriod.long,
+ timeZone: 'UTC',
+ ))
+ .format(date),
+ '4 du matin');
+ });
+
+ testWithFormatting('style', () {
+ final date = DateTime.utc(2021, 12, 17, 4, 0, 42);
+ expect(
+ Intl(locale: const Locale(language: 'en'))
+ .datetimeFormat(const DateTimeFormatOptions(
+ timeFormatStyle: TimeFormatStyle.short,
+ timeZone: 'UTC',
+ ))
+ .format(date),
+ '4:00 AM');
+ expect(
+ Intl(locale: const Locale(language: 'en'))
+ .datetimeFormat(const DateTimeFormatOptions(
+ dateFormatStyle: DateFormatStyle.short,
+ timeZone: 'UTC',
+ ))
+ .format(date),
+ '12/17/21');
+ expect(
+ Intl(locale: const Locale(language: 'en'))
+ .datetimeFormat(const DateTimeFormatOptions(
+ timeFormatStyle: TimeFormatStyle.medium,
+ dateFormatStyle: DateFormatStyle.short,
+ timeZone: 'UTC',
+ ))
+ .format(date),
+ '12/17/21, 4:00:42 AM');
+ });
+}