diff --git a/pkgs/intl4x/CHANGELOG.md b/pkgs/intl4x/CHANGELOG.md index 0600a786..092b6279 100644 --- a/pkgs/intl4x/CHANGELOG.md +++ b/pkgs/intl4x/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.0-alpha.3-wip + +- Add `calendar.dart` entrypoint exposing `Calendar` and `Weekday` with `Weekday.firstDayOfWeek` API. + ## 1.0.0-alpha.2 - Add `locale.dart` entrypoint exposing only `Locale`. diff --git a/pkgs/intl4x/lib/calendar.dart b/pkgs/intl4x/lib/calendar.dart new file mode 100644 index 00000000..fe3e806f --- /dev/null +++ b/pkgs/intl4x/lib/calendar.dart @@ -0,0 +1,13 @@ +// Copyright (c) 2026, 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. + +/// Calendar types and weekday information. +/// +/// Provides [Calendar] and [Weekday] enums for working with calendar systems +/// and week-start information. +/// @docImport 'src/options.dart'; +library; + +export 'src/locale/locale.dart' show Locale; +export 'src/options.dart' show Calendar, Weekday; diff --git a/pkgs/intl4x/lib/src/first_day_of_week/first_day_of_week.dart b/pkgs/intl4x/lib/src/first_day_of_week/first_day_of_week.dart new file mode 100644 index 00000000..2dd7e21f --- /dev/null +++ b/pkgs/intl4x/lib/src/first_day_of_week/first_day_of_week.dart @@ -0,0 +1,15 @@ +// Copyright (c) 2026, 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_native.dart' + if (dart.library.js_interop) '../ecma/ecma_web.dart'; +import '../locale/locale.dart'; +import '../options.dart'; +import 'first_day_of_week_stub.dart' + if (dart.library.js_interop) 'first_day_of_week_ecma.dart'; +import 'first_day_of_week_stub_4x.dart' + if (dart.library.ffi) 'first_day_of_week_4x.dart'; + +Weekday getFirstDayOfWeek(Locale locale) => + useBrowser ? getFirstDayOfWeekECMA(locale) : getFirstDayOfWeek4X(locale); diff --git a/pkgs/intl4x/lib/src/first_day_of_week/first_day_of_week_4x.dart b/pkgs/intl4x/lib/src/first_day_of_week/first_day_of_week_4x.dart new file mode 100644 index 00000000..146e60ce --- /dev/null +++ b/pkgs/intl4x/lib/src/first_day_of_week/first_day_of_week_4x.dart @@ -0,0 +1,22 @@ +// Copyright (c) 2026, 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 '../locale/locale.dart'; +import '../locale/locale_4x.dart'; +import '../options.dart'; + +Weekday getFirstDayOfWeek4X(Locale locale) { + final weekInfo = icu.WeekInformation((locale as Locale4x).get4X); + return switch (weekInfo.firstWeekday) { + icu.Weekday.monday => Weekday.monday, + icu.Weekday.tuesday => Weekday.tuesday, + icu.Weekday.wednesday => Weekday.wednesday, + icu.Weekday.thursday => Weekday.thursday, + icu.Weekday.friday => Weekday.friday, + icu.Weekday.saturday => Weekday.saturday, + icu.Weekday.sunday => Weekday.sunday, + }; +} diff --git a/pkgs/intl4x/lib/src/first_day_of_week/first_day_of_week_ecma.dart b/pkgs/intl4x/lib/src/first_day_of_week/first_day_of_week_ecma.dart new file mode 100644 index 00000000..2af3eb40 --- /dev/null +++ b/pkgs/intl4x/lib/src/first_day_of_week/first_day_of_week_ecma.dart @@ -0,0 +1,36 @@ +// Copyright (c) 2026, 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:js_interop'; +import 'dart:js_interop_unsafe'; + +import '../locale/locale.dart'; +import '../options.dart'; + +@JS('Intl.Locale') +extension type _LocaleJS._(JSObject _) implements JSObject { + external factory _LocaleJS(String s); + external JSObject? get weekInfo; + external JSObject? getWeekInfo(); +} + +Weekday getFirstDayOfWeekECMA(Locale locale) { + final jsLocale = _LocaleJS(locale.toLanguageTag()); + JSObject? weekInfo; + if (jsLocale.hasProperty('getWeekInfo'.toJS).toDart) { + weekInfo = jsLocale.getWeekInfo(); + } else if (jsLocale.hasProperty('weekInfo'.toJS).toDart) { + weekInfo = jsLocale.weekInfo; + } + if (weekInfo != null) { + final firstDayJS = weekInfo.getProperty('firstDay'.toJS); + if (firstDayJS != null && firstDayJS.isA()) { + final firstDay = (firstDayJS as JSNumber).toDartInt; + if (firstDay >= 1 && firstDay <= 7) { + return Weekday.fromIsoIndex(firstDay); + } + } + } + return Weekday.monday; +} diff --git a/pkgs/intl4x/lib/src/first_day_of_week/first_day_of_week_stub.dart b/pkgs/intl4x/lib/src/first_day_of_week/first_day_of_week_stub.dart new file mode 100644 index 00000000..6658a3e1 --- /dev/null +++ b/pkgs/intl4x/lib/src/first_day_of_week/first_day_of_week_stub.dart @@ -0,0 +1,9 @@ +// Copyright (c) 2026, 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/locale.dart'; +import '../options.dart'; + +Weekday getFirstDayOfWeekECMA(Locale locale) => + throw UnimplementedError('Cannot use ECMA outside of web environments.'); diff --git a/pkgs/intl4x/lib/src/first_day_of_week/first_day_of_week_stub_4x.dart b/pkgs/intl4x/lib/src/first_day_of_week/first_day_of_week_stub_4x.dart new file mode 100644 index 00000000..21d5d236 --- /dev/null +++ b/pkgs/intl4x/lib/src/first_day_of_week/first_day_of_week_stub_4x.dart @@ -0,0 +1,9 @@ +// Copyright (c) 2026, 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/locale.dart'; +import '../options.dart'; + +Weekday getFirstDayOfWeek4X(Locale locale) => + throw UnimplementedError('Cannot use ICU4X in web environments.'); diff --git a/pkgs/intl4x/lib/src/options.dart b/pkgs/intl4x/lib/src/options.dart index 6a70a01b..705cbd1a 100644 --- a/pkgs/intl4x/lib/src/options.dart +++ b/pkgs/intl4x/lib/src/options.dart @@ -2,8 +2,45 @@ // 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. -/// Common options used across intl4x libraries. -library; +import 'first_day_of_week/first_day_of_week.dart'; +import 'locale/locale.dart' show Locale; + +/// Weekdays. +enum Weekday { + monday(1), + tuesday(2), + wednesday(3), + thursday(4), + friday(5), + saturday(6), + sunday(7); + + /// The ISO 8601 standard index, where Monday is 1 and Sunday is 7. + final int isoIndex; + + const Weekday(this.isoIndex); + + /// Returns the first day of the week for the given [locale]. + factory Weekday.firstDayOfWeek([Locale? locale]) => + getFirstDayOfWeek(locale ?? Locale.system); + + /// Returns the [Weekday] corresponding to the given ISO 8601 index (1 for + /// Monday, 7 for Sunday). + factory Weekday.fromIsoIndex(int isoIndex) => switch (isoIndex) { + 1 => Weekday.monday, + 2 => Weekday.tuesday, + 3 => Weekday.wednesday, + 4 => Weekday.thursday, + 5 => Weekday.friday, + 6 => Weekday.saturday, + 7 => Weekday.sunday, + _ => throw ArgumentError.value( + isoIndex, + 'isoIndex', + 'Must be between 1 and 7', + ), + }; +} /// Calendar types for date and time formatting. enum Calendar { diff --git a/pkgs/intl4x/pubspec.yaml b/pkgs/intl4x/pubspec.yaml index ba500c25..7e62605d 100644 --- a/pkgs/intl4x/pubspec.yaml +++ b/pkgs/intl4x/pubspec.yaml @@ -1,7 +1,7 @@ name: intl4x description: >- A lightweight modular library for internationalization (i18n) functionality. -version: 1.0.0-alpha.2 +version: 1.0.0-alpha.3-wip repository: https://github.com/dart-lang/i18n/tree/main/pkgs/intl4x issue_tracker: https://github.com/dart-lang/i18n/issues?q=is%3Aissue+is%3Aopen+label%3Apackage%3Aintl4x diff --git a/pkgs/intl4x/test/calendar_test.dart b/pkgs/intl4x/test/calendar_test.dart new file mode 100644 index 00000000..1130e3ed --- /dev/null +++ b/pkgs/intl4x/test/calendar_test.dart @@ -0,0 +1,46 @@ +// Copyright (c) 2026, 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/calendar.dart'; +import 'package:test/test.dart'; + +import 'utils.dart'; + +void main() { + group('firstDayOfWeek', () { + testWithFormatting('en-US', () { + final locale = Locale.parse('en-US'); + expect(Weekday.firstDayOfWeek(locale), Weekday.sunday); + }); + + testWithFormatting('fr-FR', () { + final locale = Locale.parse('fr-FR'); + expect(Weekday.firstDayOfWeek(locale), Weekday.monday); + }); + + testWithFormatting('ar-EG', () { + final locale = Locale.parse('ar-EG'); + expect(Weekday.firstDayOfWeek(locale), Weekday.saturday); + }); + }); + + group('Weekday ISO enum', () { + test('values', () { + expect(Weekday.monday.isoIndex, 1); + expect(Weekday.tuesday.isoIndex, 2); + expect(Weekday.wednesday.isoIndex, 3); + expect(Weekday.thursday.isoIndex, 4); + expect(Weekday.friday.isoIndex, 5); + expect(Weekday.saturday.isoIndex, 6); + expect(Weekday.sunday.isoIndex, 7); + }); + + test('fromIsoIndex', () { + expect(Weekday.fromIsoIndex(1), Weekday.monday); + expect(Weekday.fromIsoIndex(7), Weekday.sunday); + expect(() => Weekday.fromIsoIndex(0), throwsArgumentError); + expect(() => Weekday.fromIsoIndex(8), throwsArgumentError); + }); + }); +} diff --git a/pkgs/intl4x/test/locale_test.dart b/pkgs/intl4x/test/locale_test.dart new file mode 100644 index 00000000..6c7bb76d --- /dev/null +++ b/pkgs/intl4x/test/locale_test.dart @@ -0,0 +1,40 @@ +// Copyright (c) 2026, 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:test/test.dart'; + +void main() { + group('Locale', () { + test('parse and toLanguageTag', () { + final locale = Locale.parse('en-US'); + expect(locale.toLanguageTag(), 'en-US'); + expect(locale.toString(), 'en-US'); + }); + + test('toLanguageTag', () { + final locale = Locale.parse('de-DE'); + expect(locale.toLanguageTag(), 'de-DE'); + }); + + test('withCalendar', () { + final locale = Locale.parse('en-US').withCalendar(Calendar.buddhist); + expect(locale.toLanguageTag(), 'en-US-u-ca-buddhist'); + }); + + test('withNumberingSystem', () { + final locale = Locale.parse( + 'en-US', + ).withNumberingSystem(NumberingSystem.arabic); + expect(locale.toLanguageTag(), 'en-US-u-nu-arab'); + }); + + test('withClockStyle', () { + final locale = Locale.parse( + 'en-US', + ).withClockStyle(ClockStyle.oneToTwelve); + expect(locale.toLanguageTag(), 'en-US-u-hc-h12'); + }); + }); +}