Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion executors/dart/bin/executor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ void printVersion() {
final dartVersion = version.substring(0, version.indexOf(' '));
final versionInfo = {
'platform': 'Dart Native',
'icuVersion': '73', //TODO: get from ICU4X somehow
'icuVersion': '77', //TODO: get from ICU4X somehow
'platformVersion': dartVersion,
'intlVersion': intl4xVersion,
};
Expand Down
37 changes: 27 additions & 10 deletions executors/dart/lib/collator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,22 @@ import 'package:intl4x/collation.dart';
import 'package:intl4x/intl4x.dart';

String testCollation(String jsonEncoded) {
final json =
jsonDecode(jsonEncoded)
as Map<
String,
dynamic
>; // For the moment, use strings for easier interop
final json = jsonDecode(jsonEncoded) as Map<String, dynamic>;
// Global default locale
final testLocale = json['locale'] as String? ?? 'en';
final outputLine = <String, dynamic>{'label': json['label']};

if (json.containsKey('rules')) {
outputLine['error_type'] = 'unsupported';
outputLine['unsupported'] = 'unsupported_options';
outputLine['error_message'] = 'Rules are not supported';
return jsonEncode(outputLine);
}
final localeString = json['locale'] as String? ?? 'en';
if (localeString == 'root') {
outputLine['error_type'] = 'unsupported';
outputLine['unsupported'] = 'unsupported_options';
outputLine['error_message'] = 'Locale `root` is unsupported';
return jsonEncode(outputLine);
}
// Set up collator object with optional locale and testOptions.
final s1 = json['s1'];
final s2 = json['s2'];
Expand All @@ -36,6 +42,7 @@ String testCollation(String jsonEncoded) {
.where((value) => value.jsName == json['case_first'])
.firstOrNull ??
CaseFirst.localeDependent;
final compareType = json['compare_type'] as String?;

if (s1 == null || s2 == null) {
outputLine.addAll({
Expand All @@ -45,7 +52,7 @@ String testCollation(String jsonEncoded) {
});
} else {
try {
final coll = Intl(locale: Locale.parse(testLocale));
final coll = Intl(locale: Locale.parse(localeString));

final collationOptions = CollationOptions(
ignorePunctuation: ignorePunctuation,
Expand All @@ -55,7 +62,17 @@ String testCollation(String jsonEncoded) {
);

final compared = coll.collation(collationOptions).compare(s1, s2);
final result = compared <= 0;

bool result;
if (compareType == '=') {
// Check for strict equality comparison
result = compared == 0;
} else if (compareType != null && compareType.startsWith('<')) {
// Check results with different compare types
result = compared < 0;
} else {
result = compared <= 0;
}
outputLine['result'] = result;

if (result != true) {
Expand Down
166 changes: 141 additions & 25 deletions executors/dart/lib/datetime_format.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dart:convert';

import 'package:collection/collection.dart';
import 'package:intl4x/datetime_format.dart';
import 'package:intl4x/intl4x.dart';
Expand All @@ -10,29 +11,34 @@ import 'package:intl4x/intl4x.dart';
String testDateTimeFmt(String jsonEncoded) {
final json = jsonDecode(jsonEncoded) as Map<String, dynamic>;

final label = json['label'];
var localeString = json['locale'] as String?; // locale can be null from JSON
if (localeString == null || localeString.isEmpty) {
localeString = 'und'; // Default to 'und' if locale is null or empty
}

final returnJson = <String, dynamic>{'label': json['label']};

// Parse Locale string
Locale locale;
try {
locale = Locale.parse(localeString.replaceAll('_', '-'));
} catch (e) {
return jsonEncode({
'label': label,
'error': 'Invalid locale format: ${e.toString()}',
'locale': localeString,
});
returnJson['error'] = 'Invalid locale format: ${e.toString()}';
returnJson['locale'] = localeString;
return jsonEncode(returnJson);
}

var testOptionsJson = <String, dynamic>{};
if (json['options'] != null) {
testOptionsJson = json['options'] as Map<String, dynamic>;
}

if (testOptionsJson['dateTimeFormatType'] == 'atTime') {
returnJson['error_type'] = 'unsupported';
returnJson['unsupported'] = '`at` not supported';
return jsonEncode(returnJson);
}

// Initialize DateTimeFormatOptions
var dateTimeFormatOptions = DateTimeFormatOptions();

Expand All @@ -53,11 +59,22 @@ String testDateTimeFmt(String jsonEncoded) {
}

// ignore: unused_local_variable - to be used with the timezoneformatter
String? timezone;
if (testOptionsJson.containsKey('time_zone')) {
timezone = testOptionsJson['time_zone'] as String;
String? timeZoneName;
int? offsetSeconds;
if (testOptionsJson.containsKey('timeZone') &&
json.containsKey('tz_offset_secs')) {
timeZoneName = testOptionsJson['timeZone'] as String;
offsetSeconds = (json['tz_offset_secs'] as num).toInt();
}

final semanticSkeleton = testOptionsJson['semanticSkeleton'] as String?;
final semanticSkeletonLength =
testOptionsJson['semanticSkeletonLength'] as String?;

final dateStyle = testOptionsJson['dateStyle'] as String?;
final timeStyle = testOptionsJson['timeStyle'] as String?;
final yearStyle = testOptionsJson['yearStyle'] as String?;

DateTime? testDate;
if (json['input_string'] != null) {
final isoDateString = json['input_string'] as String;
Expand All @@ -72,34 +89,133 @@ String testDateTimeFmt(String jsonEncoded) {
try {
testDate = DateTime.parse(testDateString);
} catch (e) {
return jsonEncode({
'label': label,
'error': 'Invalid input_string date format: ${e.toString()}',
'input_string': isoDateString,
});
returnJson['error'] = 'Invalid input_string date format: ${e.toString()}';
returnJson['input_string'] = isoDateString;
return jsonEncode(returnJson);
}
} else {
testDate = DateTime.now();
}

final returnJson = <String, dynamic>{'label': label};
final dtFormatter = Intl(
locale: locale,
).dateTimeFormat(dateTimeFormatOptions);

try {} catch (error) {
returnJson['error'] = 'DateTimeFormat Constructor: ${error.toString()}';
returnJson['options'] = testOptionsJson;
return jsonEncode(returnJson);
}

try {
final formattedDt = dtFormatter.ymd(testDate);
final formatter = semanticSkeleton != null
? getFormatterForSkeleton(
semanticSkeleton,
semanticSkeletonLength,
dtFormatter,
)
: getFormatterForStyle(dateStyle, timeStyle, yearStyle, dtFormatter);
String formattedDt;
if (timeStyle == 'full' && timeZoneName != null) {
final offset = Duration(seconds: offsetSeconds!);
final timeZone = TimeZone(name: timeZoneName, offset: offset);
final timeZoneStyle = 'long';
final zonedFormatter = getZonedFormatter(timeZoneStyle, formatter);
formattedDt = zonedFormatter.format(testDate.add(offset), timeZone);
} else {
formattedDt = formatter.format(testDate);
}
returnJson['result'] = formattedDt;
returnJson['actual_options'] = dateTimeFormatOptions.toString();
} catch (error) {
returnJson['unsupported'] = ': ${error.toString()}';
} on Exception catch (e) {
returnJson['error_type'] = 'unsupported';
returnJson['unsupported'] = ': ${e.toString()}';
return jsonEncode(returnJson);
}
returnJson['actual_options'] = dateTimeFormatOptions.humanReadable;
returnJson['options'] = testOptionsJson;

return jsonEncode(returnJson);
}

DateTimeFormatter getFormatterForSkeleton(
String semanticSkeleton,
String? semanticSkeletonLength,
DateTimeFormatBuilder dtFormatter,
) {
// The provided Rust code implies a more complex logic, but here we'll map the known skeletons.
// The Rust code's `None => None` and `None => Ok(...)` branches aren't directly translatable
// to a Dart function that must return a Formatter. We'll handle the valid cases and throw for others.

final semanticDateStyle = switch (semanticSkeletonLength) {
'short' => DateFormatStyle.short,
'medium' => DateFormatStyle.medium,
'long' => DateFormatStyle.long,
_ => throw Exception(),
};
return switch (semanticSkeleton) {
'D' || 'DT' || 'DTZ' => dtFormatter.d(),
'MD' => dtFormatter.md(),
'MDT' || 'MDTZ' => dtFormatter.mdt(dateStyle: semanticDateStyle),
'YMD' || 'YMDT' || 'YMDTZ' => dtFormatter.ymd(dateStyle: semanticDateStyle),
'YMDE' => dtFormatter.ymde(dateStyle: semanticDateStyle),
'YMDET' || 'YMDETZ' => dtFormatter.ymdet(dateStyle: semanticDateStyle),
'M' => dtFormatter.m(),
'Y' => dtFormatter.y(),
'T' || 'Z' || 'TZ' => dtFormatter.t(),
_ => throw Exception('Unknown skeleton: $semanticSkeleton'),
};
}

ZonedDateTimeFormatter getZonedFormatter(
String timeZoneStyle,
DateTimeFormatter formatter,
) => switch (timeZoneStyle) {
'short' => formatter.withTimeZoneShort(),
'long' => formatter.withTimeZoneLong(),
'full' => formatter.withTimeZoneLongGeneric(),
String() => throw Exception('Unknown time zone style `$timeZoneStyle`'),
};

DateTimeFormatter getFormatterForStyle(
String? dateStyle,
String? timeStyle,
String? yearStyle,
DateTimeFormatBuilder dtFormatter,
) => switch ((dateStyle, timeStyle, yearStyle)) {
('medium', null, _) => dtFormatter.ymd(dateStyle: DateFormatStyle.medium),
(null, 'short', _) => dtFormatter.t(style: TimeFormatStyle.short),
('full', 'short', null) => dtFormatter.ymdet(
dateStyle: DateFormatStyle.full,
timeStyle: TimeFormatStyle.short,
),
('full', 'full', null) => dtFormatter.ymdet(
dateStyle: DateFormatStyle.full,
timeStyle: TimeFormatStyle.full,
),
('short', 'full', null) => dtFormatter.ymdt(
dateStyle: DateFormatStyle.short,
timeStyle: TimeFormatStyle.full,
),
('short', 'full', 'with_era') => dtFormatter.ymdet(
dateStyle: DateFormatStyle.short,
timeStyle: TimeFormatStyle.full,
),
(_, _, 'with_era') => dtFormatter.ymde(),
(_, _, _) => throw Exception(
'Unknown combination of date style `$dateStyle`, time style `$timeStyle`, and year style `$yearStyle`',
),
};

extension on DateTimeFormatOptions {
String get humanReadable {
final fields = <String, dynamic>{
if (calendar != null) 'calendar': calendar,
if (dayPeriod != null) 'dayPeriod': dayPeriod,
if (numberingSystem != null) 'numberingSystem': numberingSystem,
if (clockstyle != null) 'clockstyle': clockstyle,
if (era != null) 'era': era,
if (timestyle != null) 'timestyle': timestyle,
if (fractionalSecondDigits != null)
'fractionalSecondDigits': fractionalSecondDigits,
'formatMatcher': formatMatcher,
};
final entries = fields.entries
.map((e) => '${e.key}: ${e.value}')
.join(', ');
return 'DateTimeFormatOptions($entries)';
}
}
9 changes: 8 additions & 1 deletion executors/dart/lib/lang_names.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ String testLangNames(String jsonEncoded) {
}

final languageLabel = json['language_label'] as String;
if (languageLabel.contains('u-kr')) {
outputLine.addAll({
'unsupported': 'u-kr extension not supported',
'error_retry': false, // Do not repeat
});
return jsonEncode(outputLine);
}

Locale languageLabelLocale;
try {
languageLabelLocale = Locale.parse(languageLabel);
Expand All @@ -49,7 +57,6 @@ String testLangNames(String jsonEncoded) {
outputLine['result'] = resultLangName;
} catch (error) {
outputLine.addAll({
'error_type': 'unsupported',
'error_detail': error.toString(),
'actual_options': options.toJson(),
'error_retry': false, // Do not repeat
Expand Down
10 changes: 3 additions & 7 deletions executors/dart/lib/numberformat.dart
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,8 @@ NumberFormatOptions _decimalPatternToOptions(
);
return numberFormatOptions.copyWith(roundingMode: roundingMode);
} else {
return numberFormatOptions;
//TODO: remove this halfEven default override, as soon as it is always passed in the numberformat args.
return numberFormatOptions.copyWith(roundingMode: RoundingMode.halfEven);
}
}

Expand Down Expand Up @@ -324,9 +325,6 @@ NumberFormatOptions _fromJson(Map<String, dynamic> options) {
final signDisplay = SignDisplay.values
.where((element) => element.name == options['signDisplay'])
.firstOrNull;
final localeMatcher = LocaleMatcher.values
.where((element) => element.jsName == options['localeMatcher'])
.firstOrNull;
final useGrouping = Grouping.values
.where((element) => element.jsName == options['useGrouping'])
.firstOrNull;
Expand Down Expand Up @@ -374,7 +372,6 @@ NumberFormatOptions _fromJson(Map<String, dynamic> options) {
return NumberFormatOptions.custom().copyWith(
style: style,
currency: currency,
localeMatcher: localeMatcher,
signDisplay: signDisplay,
notation: notation,
useGrouping: useGrouping,
Expand All @@ -391,11 +388,10 @@ extension on NumberFormatOptions {
return {
'style': style.name,
'currency': currency,
'localeMatcher': localeMatcher.jsName,
'signDisplay': signDisplay.name,
'notation': notation.name,
'useGrouping': useGrouping.jsName,
'numberingSystem': numberingSystem?.toString(),
'numberingSystem': numberingSystem,
'roundingMode': roundingMode.name,
'trailingZeroDisplay': trailingZeroDisplay.name,
'minimumIntegerDigits': minimumIntegerDigits,
Expand Down
2 changes: 1 addition & 1 deletion executors/dart/lib/version.dart
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
/// This file is autogenerated by bin/set_version.dart, do not modify manually.
const intl4xVersion = '0.12.2';
const intl4xVersion = '0.13.0';
2 changes: 1 addition & 1 deletion executors/dart/out/version.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
const dartVersion = "0.12.2";
const dartVersion = "0.13.0";
module.exports = { dartVersion };
Loading
Loading