fix: unsupported locale for date format (#32267)

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Santo Shakil <shakil.mezbah@gmail.com>
This commit is contained in:
shenlong
2026-10-19 19:08:30 +06:00
committed by GitHub
co-authored by shenlong-tanwen Santo Shakil
parent 76ae1aa4ba
commit e6fdb56bcd
14 changed files with 49 additions and 25 deletions
+6
View File
@@ -127,6 +127,12 @@ dart_code_metrics:
severity: perf
- name: tr
description: 'Use context.t.<key> or StaticTranslations.instance.<key> instead of tr().'
- type: Locale
entries:
- name: toLanguageTag
description: 'Pass resolvedDateTimeLocale() to DateFormat, not the raw locale. DateFormat throws for locales intl has no date data for (e.g. mi).'
exclude-paths:
- 'lib/main.dart'
- type: String
entries:
- name: tr
@@ -1,7 +1,6 @@
import 'dart:async';
import 'package:drift/drift.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:immich_mobile/data/db/main/database.dart';
import 'package:immich_mobile/data/db/main/table/local/asset.dart';
import 'package:immich_mobile/data/db/main/table/remote/asset.dart';
@@ -13,6 +12,7 @@ import 'package:immich_mobile/domain/models/timeline.model.dart';
import 'package:immich_mobile/domain/services/timeline.service.dart';
import 'package:immich_mobile/infrastructure/repositories/map.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/timeline.repository.drift.dart';
import 'package:intl/intl.dart';
import 'package:stream_transform/stream_transform.dart';
@DriftAccessor()
+2 -1
View File
@@ -6,6 +6,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:immich_mobile/domain/models/log.model.dart';
import 'package:immich_mobile/domain/services/log.service.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/datetime_extensions.dart';
import 'package:immich_mobile/extensions/theme_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/routing/router.dart';
@@ -105,7 +106,7 @@ class AppLogPage extends HookWidget {
style: TextStyle(fontSize: 14.0, color: context.colorScheme.onSurface, fontFamily: "GoogleSansCode"),
),
subtitle: Text(
"at ${DateFormat("HH:mm:ss.SSS").format(logMessage.createdAt)} in ${logMessage.logger}",
"at ${DateFormat("HH:mm:ss.SSS", resolvedDateTimeLocale()).format(logMessage.createdAt)} in ${logMessage.logger}",
style: TextStyle(fontSize: 12.0, color: context.colorScheme.onSurfaceSecondary),
),
leading: buildLeadingIcon(logMessage.level),
@@ -8,6 +8,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/services/timeline.service.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/datetime_extensions.dart';
import 'package:immich_mobile/extensions/theme_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/models/folder/recursive_folder.model.dart';
@@ -201,7 +202,7 @@ class FolderContent extends HookConsumerWidget {
style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600),
),
subtitle: Text(
"${asset.exifInfo.fileSize != null ? "${formatBytes(asset.exifInfo.fileSize ?? 0)} • " : ""}${DateFormat.yMMMd().format(asset.createdAt.toLocal())}",
"${asset.exifInfo.fileSize != null ? "${formatBytes(asset.exifInfo.fileSize ?? 0)} • " : ""}${DateFormat.yMMMd(resolvedDateTimeLocale()).format(asset.createdAt.toLocal())}",
style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary),
),
),
@@ -6,6 +6,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/datetime_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/models/shared_link/shared_link.model.dart';
import 'package:immich_mobile/providers/server_info.provider.dart';
@@ -179,7 +180,7 @@ class SharedLinkEditPage extends HookConsumerWidget {
);
}
String formatDateTime(DateTime dateTime) => DateFormat.yMMMd(context.locale.toString()).add_Hm().format(dateTime);
String formatDateTime(DateTime dateTime) => DateFormat.yMMMd(resolvedDateTimeLocale()).add_Hm().format(dateTime);
DateTime? getExpiresAtFromPreset(Duration preset) => preset == Duration.zero ? null : DateTime.now().add(preset);
@@ -4,6 +4,7 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/events.model.dart';
import 'package:immich_mobile/domain/models/memory.model.dart';
import 'package:immich_mobile/domain/utils/event_stream.dart';
import 'package:immich_mobile/extensions/datetime_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/widgets/memory/memory_title.widget.dart';
import 'package:immich_mobile/routing/router.dart';
@@ -16,7 +17,7 @@ class MemoryBottomInfo extends StatelessWidget {
@override
Widget build(BuildContext context) {
final df = DateFormat.yMMMMd();
final df = DateFormat.yMMMMd(resolvedDateTimeLocale());
final fileCreatedDate = asset.createdAt;
return Padding(
padding: const EdgeInsets.all(16.0),
@@ -5,6 +5,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/data/store.dart';
import 'package:immich_mobile/domain/models/person.model.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/datetime_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/utils/debug_print.dart';
import 'package:immich_mobile/widgets/common/immich_toast.dart';
@@ -61,7 +62,7 @@ class _PersonBirthdayEditFormState extends ConsumerState<PersonBirthdayEditForm>
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(16.0)),
child: ScrollDatePicker(
viewType: datePickerColumnOrder(DateFormat.yMd(context.locale.toLanguageTag()).pattern),
viewType: datePickerColumnOrder(DateFormat.yMd(resolvedDateTimeLocale()).pattern),
options: DatePickerOptions(
backgroundColor: context.colorScheme.surfaceContainerHigh,
itemExtent: 50,
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:immich_mobile/extensions/datetime_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:intl/intl.dart';
@@ -11,11 +12,11 @@ sealed class DateFilterInputModel {
// If date range is less than 24 hours, set the end date to the end of the day
final date = asDateTimeRange();
if (date.end.difference(date.start).inHours < 24) {
return DateFormat.yMMMd().format(date.start.toLocal());
return DateFormat.yMMMd(resolvedDateTimeLocale()).format(date.start.toLocal());
} else {
return context.t.search_filter_date_interval(
start: DateFormat.yMMMd().format(date.start.toLocal()),
end: DateFormat.yMMMd().format(date.end.toLocal()),
start: DateFormat.yMMMd(resolvedDateTimeLocale()).format(date.start.toLocal()),
end: DateFormat.yMMMd(resolvedDateTimeLocale()).format(date.end.toLocal()),
);
}
}
@@ -7,6 +7,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/timeline.model.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/datetime_extensions.dart';
import 'package:immich_mobile/extensions/theme_extensions.dart';
import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
@@ -27,15 +28,15 @@ class TimelineHeader extends HookWidget {
required this.assetOffset,
});
String _formatMonth(BuildContext context, DateTime date) {
String _formatMonth(DateTime date) {
final formatter = date.year == DateTime.now().year
? DateFormat.MMMM(context.locale.toLanguageTag())
: DateFormat.yMMMM(context.locale.toLanguageTag());
? DateFormat.MMMM(resolvedDateTimeLocale())
: DateFormat.yMMMM(resolvedDateTimeLocale());
return formatter.format(date);
}
String _formatDay(BuildContext context, DateTime date) {
final formatter = DateFormat.yMMMEd(context.locale.toLanguageTag());
String _formatDay(DateTime date) {
final formatter = DateFormat.yMMMEd(resolvedDateTimeLocale());
return formatter.format(date);
}
@@ -61,7 +62,7 @@ class TimelineHeader extends HookWidget {
Row(
children: [
Text(
toBeginningOfSentenceCase(_formatMonth(context, date)),
toBeginningOfSentenceCase(_formatMonth(date)),
style: context.textTheme.labelLarge?.copyWith(fontSize: 24),
),
const Spacer(),
@@ -72,7 +73,7 @@ class TimelineHeader extends HookWidget {
Row(
children: [
Text(
toBeginningOfSentenceCase(_formatDay(context, date)),
toBeginningOfSentenceCase(_formatDay(date)),
style: context.textTheme.labelLarge?.copyWith(fontSize: 15),
),
const Spacer(),
@@ -7,6 +7,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/models/timeline.model.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/datetime_extensions.dart';
import 'package:immich_mobile/extensions/theme_extensions.dart';
import 'package:immich_mobile/presentation/widgets/timeline/constants.dart';
import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart';
@@ -62,7 +63,7 @@ List<_Segment> _buildSegments({required List<Segment> layoutSegments, required d
return [];
}
final formatter = DateFormat.yMMM();
final formatter = DateFormat.yMMM(resolvedDateTimeLocale());
DateTime? lastDate;
double lastOffset = -offsetThreshold;
for (final layoutSegment in layoutSegments) {
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/datetime_extensions.dart';
import 'package:immich_mobile/extensions/duration_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/widgets/common/dropdown_search_menu.dart';
@@ -108,7 +109,7 @@ class _DateTimePicker extends HookWidget {
}
void popWithDateTime() {
final formattedDateTime = DateFormat("yyyy-MM-dd'T'HH:mm:ss").format(date.value);
final formattedDateTime = DateFormat("yyyy-MM-dd'T'HH:mm:ss", 'en').format(date.value);
final dtWithOffset =
formattedDateTime + Duration(milliseconds: tzOffset.value.offsetInMilliseconds).formatAsOffset();
context.pop(dtWithOffset);
@@ -149,7 +150,10 @@ class _DateTimePicker extends HookWidget {
1,
),
trailing: Icon(Icons.edit_outlined, size: 18, color: context.primaryColor),
title: Text(DateFormat("dd-MM-yyyy hh:mm a").format(date.value), style: context.textTheme.bodyMedium),
title: Text(
DateFormat("dd-MM-yyyy hh:mm a", resolvedDateTimeLocale()).format(date.value),
style: context.textTheme.bodyMedium,
),
onTap: pickDate,
),
const SizedBox(height: 24),
@@ -7,6 +7,7 @@ import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/person.model.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/datetime_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/widgets/images/remote_image_provider.dart';
import 'package:immich_mobile/presentation/widgets/sliver_app_bar/item_count_text.widget.dart';
@@ -290,7 +291,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S
if (widget.person.birthDate != null)
Text(
"${DateFormat.yMMMd(context.locale.toString()).format(widget.person.birthDate!)} (${formatAge(widget.person.birthDate!, DateTime.now())})",
"${DateFormat.yMMMd(resolvedDateTimeLocale()).format(widget.person.birthDate!)} (${formatAge(widget.person.birthDate!, DateTime.now())})",
style: context.textTheme.labelLarge?.copyWith(
color: Colors.white,
height: 1.2,
@@ -1,6 +1,7 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:immich_mobile/domain/models/time_range.model.dart';
import 'package:immich_mobile/extensions/datetime_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/utils/option.dart';
@@ -19,7 +20,7 @@ class MapTimeRange extends StatelessWidget {
title: Text(context.t.date_after),
subtitle: Text(
timeRange.from != null
? DateFormat.yMMMd(context.locale.toLanguageTag()).add_jm().format(timeRange.from!)
? DateFormat.yMMMd(resolvedDateTimeLocale()).add_jm().format(timeRange.from!)
: context.t.not_set,
),
trailing: timeRange.from != null
@@ -45,7 +46,7 @@ class MapTimeRange extends StatelessWidget {
title: Text(context.t.date_before),
subtitle: Text(
timeRange.to != null
? DateFormat.yMMMd(context.locale.toLanguageTag()).add_jm().format(timeRange.to!)
? DateFormat.yMMMd(resolvedDateTimeLocale()).add_jm().format(timeRange.to!)
: context.t.not_set,
),
trailing: timeRange.to != null
@@ -7,6 +7,7 @@ import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/datetime_extensions.dart';
import 'package:immich_mobile/extensions/platform_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/providers/cleanup.provider.dart';
@@ -423,7 +424,7 @@ class _FreeUpSpaceSettingsState extends ConsumerState<FreeUpSpaceSettings> {
),
subtitle: hasDate
? Text(
DateFormat.yMMMd().format(state.selectedDate!),
DateFormat.yMMMd(resolvedDateTimeLocale()).format(state.selectedDate!),
style: context.textTheme.bodyMedium?.copyWith(
color: context.colorScheme.primary,
fontWeight: FontWeight.w500,
@@ -628,7 +629,7 @@ class _FreeUpSpaceSettingsState extends ConsumerState<FreeUpSpaceSettings> {
Text(
context.t.cleanup_step4_summary(
count: state.assetsToDelete.length,
date: DateFormat.yMMMd().format(state.selectedDate!),
date: DateFormat.yMMMd(resolvedDateTimeLocale()).format(state.selectedDate!),
),
style: context.textTheme.labelLarge?.copyWith(fontSize: 15),
),
@@ -687,7 +688,10 @@ class _DeleteConfirmationDialog extends StatelessWidget {
return AlertDialog(
title: Text(context.t.cleanup_confirm_prompt_title),
content: Text(
context.t.cleanup_confirm_description(count: assetCount, date: DateFormat.yMMMd().format(cutoffDate)),
context.t.cleanup_confirm_description(
count: assetCount,
date: DateFormat.yMMMd(resolvedDateTimeLocale()).format(cutoffDate),
),
style: context.textTheme.labelLarge?.copyWith(fontSize: 15),
),
actions: [