Skip to content

Commit

Permalink
Add DateTime formatting for ICU4X (#788)
Browse files Browse the repository at this point in the history
* Add `DateTime` formatting for ICU4X

* Adapt to testing

* Adapting tests

* Add skips to test

* Fix `islamic` calendar match

* Update ICU4X

* Address comments

* Refactor

* Refactor the other method too

* Add checks to readme

* Prepare for publish

* Topics to list

* Update SDK dep

* Fix pana issues

* Update example
  • Loading branch information
mosuem authored Mar 8, 2024
1 parent 7710126 commit 17757fc
Show file tree
Hide file tree
Showing 10 changed files with 346 additions and 175 deletions.
3 changes: 2 additions & 1 deletion pkgs/intl4x/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
## 0.8.2-wip
## 0.8.2

- Add ICU4X support for number formatting.
- Add resource identifier annotations.
- Add ICU4X support for plural rules.
- Add ICU4X support for display names.
- Add ICU4X support for list formatting.
- Add ICU4X support for datetime formatting.

## 0.8.1

Expand Down
2 changes: 1 addition & 1 deletion pkgs/intl4x/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ via our [issue tracker](https://github.com/dart-lang/i18n/issues)).
| | Number format | List format | Date format | Collation | Display names | Plural Rules |
|---|:---:|:---:|:---:|:---:|:---:|:---:|
| **ECMA402 (web)** | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| **ICU4X (web/native)** | :heavy_check_mark: | :heavy_check_mark: | | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| **ICU4X (web/native)** | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |

## Implementation and Goals

Expand Down
4 changes: 1 addition & 3 deletions pkgs/intl4x/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@ environment:
dependencies:
intl4x:
path: ../
js: ^0.6.7
js: ^0.7.1

dev_dependencies:
build_runner: ^2.4.0
build_web_compilers: ^3.0.0
dart_flutter_team_lints: ^1.0.0
lints: ^2.0.0
142 changes: 139 additions & 3 deletions pkgs/intl4x/lib/src/datetime_format/datetime_format_4x.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
// 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 '../../datetime_format.dart';
import '../bindings/lib.g.dart' as icu;
import '../data.dart';
import '../data_4x.dart';
import '../locale/locale.dart';
import '../locale/locale_4x.dart';
import 'datetime_format_impl.dart';
import 'datetime_format_options.dart';

DateTimeFormatImpl getDateTimeFormatter4X(
Locale locale,
Expand All @@ -15,10 +18,143 @@ DateTimeFormatImpl getDateTimeFormatter4X(
DateTimeFormat4X(locale, data, options);

class DateTimeFormat4X extends DateTimeFormatImpl {
DateTimeFormat4X(super.locale, Data data, super.options);
final icu.DateTimeFormatter? _dateTimeFormatter;
final icu.DateFormatter? _dateFormatter;
final icu.TimeFormatter? _timeFormatter;
final icu.ZonedDateTimeFormatter? _zonedDateTimeFormatter;
final icu.DataProvider _data;

DateTimeFormat4X(super.locale, Data data, super.options)
: _data = data.to4X(),
_dateTimeFormatter = _setDateTimeFormatter(options, data, locale),
_timeFormatter = options.timeFormatStyle != null
? icu.TimeFormatter.withLength(
data.to4X(),
locale.to4X(),
options.dateFormatStyle?.timeTo4xOptions() ??
icu.TimeLength.short,
)
: null,
_dateFormatter = _setDateFormatter(options, data, locale),
_zonedDateTimeFormatter = options.timeZone != null
? icu.ZonedDateTimeFormatter.withLengths(
data.to4X(),
locale.to4X(),
options.dateFormatStyle?.dateTo4xOptions() ??
icu.DateLength.short, //TODO: Check defaults
options.timeFormatStyle?.timeTo4xOptions() ??
icu.TimeLength.short, //TODO: Check defaults
)
: null;

static icu.DateTimeFormatter? _setDateTimeFormatter(
DateTimeFormatOptions options,
Data data,
Locale locale,
) {
final dateFormatStyle = options.dateFormatStyle;
final timeFormatStyle = options.timeFormatStyle;

if (dateFormatStyle == null || timeFormatStyle == null) {
return null;
}

return icu.DateTimeFormatter.withLengths(
data.to4X(),
locale.to4X(),
dateFormatStyle.dateTo4xOptions(),
timeFormatStyle.timeTo4xOptions(),
);
}

static icu.DateFormatter? _setDateFormatter(
DateTimeFormatOptions options,
Data data,
Locale locale,
) {
final dateFormatStyle = options.dateFormatStyle;
final timeFormatStyle = options.timeFormatStyle;

if (dateFormatStyle == null && timeFormatStyle != null) {
return null;
}

return icu.DateFormatter.withLength(
data.to4X(),
locale.to4X(),
dateFormatStyle?.dateTo4xOptions() ?? icu.DateLength.short,
);
}

@override
String formatImpl(DateTime datetime) {
throw UnimplementedError('Insert diplomat bindings here');
final calendarKind = options.calendar?.to4x() ?? icu.AnyCalendarKind.iso;
final isoDateTime = icu.DateTime.fromIsoInCalendar(
datetime.year,
datetime.month,
datetime.day,
datetime.hour,
datetime.minute,
datetime.second,
datetime.microsecond * 1000,
icu.Calendar.forKind(_data, calendarKind),
);
if (_zonedDateTimeFormatter != null) {
final ianaToBcp47Mapper = icu.IanaToBcp47Mapper(_data);
final timeZone = icu.CustomTimeZone.empty()
..trySetIanaTimeZoneId(ianaToBcp47Mapper, options.timeZone!);
return _zonedDateTimeFormatter.formatDatetimeWithCustomTimeZone(
isoDateTime,
timeZone,
);
} else if (_dateTimeFormatter != null) {
return _dateTimeFormatter.formatDatetime(isoDateTime);
} else if (_dateFormatter != null) {
return _dateFormatter.formatDatetime(isoDateTime);
} else if (_timeFormatter != null) {
return _timeFormatter.formatDatetime(isoDateTime);
} else {
throw UnimplementedError(
'Custom skeletons are not yet supported in ICU4X. '
'Either date or time formatting has to be enabled.');
}
}
}

extension on TimeFormatStyle {
icu.TimeLength timeTo4xOptions() => switch (this) {
TimeFormatStyle.full => icu.TimeLength.full,
TimeFormatStyle.long => icu.TimeLength.long,
TimeFormatStyle.medium => icu.TimeLength.medium,
TimeFormatStyle.short => icu.TimeLength.short,
};
icu.DateLength dateTo4xOptions() => switch (this) {
TimeFormatStyle.full => icu.DateLength.full,
TimeFormatStyle.long => icu.DateLength.long,
TimeFormatStyle.medium => icu.DateLength.medium,
TimeFormatStyle.short => icu.DateLength.short,
};
}

extension on Calendar {
icu.AnyCalendarKind to4x() => switch (this) {
Calendar.buddhist => icu.AnyCalendarKind.buddhist,
Calendar.chinese => icu.AnyCalendarKind.chinese,
Calendar.coptic => icu.AnyCalendarKind.coptic,
Calendar.dangi => icu.AnyCalendarKind.dangi,
Calendar.ethioaa => icu.AnyCalendarKind.ethiopianAmeteAlem,
Calendar.ethiopic => icu.AnyCalendarKind.ethiopian,
Calendar.gregory => icu.AnyCalendarKind.gregorian,
Calendar.hebrew => icu.AnyCalendarKind.hebrew,
Calendar.indian => icu.AnyCalendarKind.indian,
Calendar.islamic => icu.AnyCalendarKind.islamicObservational,
Calendar.islamicUmalqura => icu.AnyCalendarKind.islamicUmmAlQura,
Calendar.islamicTbla => icu.AnyCalendarKind.islamicTabular,
Calendar.islamicCivil => icu.AnyCalendarKind.islamicCivil,
Calendar.islamicRgsa => icu.AnyCalendarKind.islamicObservational,
Calendar.iso8601 => icu.AnyCalendarKind.iso,
Calendar.japanese => icu.AnyCalendarKind.japanese,
Calendar.persian => icu.AnyCalendarKind.persian,
Calendar.roc => icu.AnyCalendarKind.roc,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ import '../ecma/ecma_policy.dart';
import '../locale/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';
import 'datetime_format_stub_4x.dart'
if (dart.library.io) 'datetime_format_4x.dart';

/// This is an intermediate to defer to the actual implementations of
/// datetime formatting.
Expand Down
15 changes: 15 additions & 0 deletions pkgs/intl4x/lib/src/datetime_format/datetime_format_stub_4x.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) 2024, 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 '../data.dart';
import '../locale/locale.dart';
import 'datetime_format_impl.dart';
import 'datetime_format_options.dart';

DateTimeFormatImpl getDateTimeFormatter4X(
Locale locale,
Data data,
DateTimeFormatOptions options,
) =>
throw UnimplementedError('Cannot use ICU4X in web environments.');
25 changes: 15 additions & 10 deletions pkgs/intl4x/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
name: intl4x
description: >-
A lightweight modular library for internationalization (i18n) functionality.
version: 0.8.2-wip
version: 0.8.2
repository: https://github.com/dart-lang/i18n/tree/main/pkgs/intl4x
platforms: ## TODO: Add native platforms once ICU4X is integrated.
platforms:
web:
android:
ios:
linux:
macos:
windows:
topics:
- i18n

environment:
sdk: ">=3.0.0 <4.0.0"
sdk: ">=3.3.0 <4.0.0"

dependencies:
ffi: ^2.1.0
js: ^0.6.5
js: ^0.7.1
meta: ^1.12.0

dev_dependencies:
archive: ^3.4.10
args: ^2.4.2
build_runner: ^2.1.4
build_web_compilers: ^3.2.1
collection: ^1.18.0
dart_flutter_team_lints: ^1.0.0
lints: ^2.0.0
native_assets_cli: ^0.3.2
dart_flutter_team_lints: ^2.1.1
lints: ^3.0.0
native_assets_cli: ^0.4.2
path: ^1.9.0
test: ^1.22.1
test: ^1.22.1
Loading

0 comments on commit 17757fc

Please sign in to comment.