Skip to content
Open
Show file tree
Hide file tree
Changes from all 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