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);
}
Comment on lines +12 to +24

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There's some code duplication in how unsupported options are handled for 'rules' and the 'root' locale. You could extract this logic into a local helper function within testCollation to improve code clarity and maintainability. For example:

String unsupported(String message) {
  outputLine['error_type'] = 'unsupported';
  outputLine['unsupported'] = 'unsupported_options';
  outputLine['error_message'] = message;
  return jsonEncode(outputLine);
}

if (json.containsKey('rules')) {
  return unsupported('Rules are not supported');
}
// ...
if (localeString == 'root') {
  return unsupported('Locale `root` is unsupported');
}

// 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
154 changes: 140 additions & 14 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 @@ -16,6 +17,8 @@ String testDateTimeFmt(String jsonEncoded) {
localeString = 'und'; // Default to 'und' if locale is null or empty
}

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

// Parse Locale string
Locale locale;
try {
Expand All @@ -33,6 +36,12 @@ String testDateTimeFmt(String jsonEncoded) {
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 +62,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 @@ -82,24 +102,130 @@ String testDateTimeFmt(String jsonEncoded) {
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()}';
}
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,
) {
final zonedFormatter = switch (timeZoneStyle) {
'short' => formatter.withTimeZoneShort(),
'long' => formatter.withTimeZoneLong(),
'full' => formatter.withTimeZoneLongGeneric(),
String() => throw Exception('Unknown time zone style `$timeZoneStyle`'),
};
return zonedFormatter;
}

DateTimeFormatter getFormatterForStyle(
String? dateStyle,
String? timeStyle,
String? yearStyle,
DateTimeFormatBuilder dtFormatter,
) {
final formatter = 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`',
),
};
return formatter;
}

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