mirror of
https://github.com/immich-app/immich.git
synced 2026-10-26 04:19:42 +08:00
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:
co-authored by
shenlong-tanwen
Santo Shakil
parent
76ae1aa4ba
commit
e6fdb56bcd
@@ -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()
|
||||
|
||||
@@ -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: [
|
||||
|
||||
Reference in New Issue
Block a user