mirror of
https://github.com/immich-app/immich.git
synced 2025-07-07 10:14:08 -04:00
fix(mobile): add translate extension (#18942)
* re-write localization service and add translation extension * Revert "re-write localization service and add translation extension" This reverts commit fdd7386020f638b92ad4f4691667d67e8c2935fc. * fix can't use context for easy_localization * fix lint * update new translate context * handle context null * revert main file * Revert "revert main file" This reverts commit 16faca46d0a36abafe41a19bb46b38fffa4940f1. * remove fix nested MaterialApp * change use t extenstion and remove translation utils * update function call similar for consistently --------- Co-authored-by: dvbthien <dvbthien@gmail.com>
This commit is contained in:
parent
16fcb657b7
commit
3d0c851636
50
mobile/lib/extensions/translate_extensions.dart
Normal file
50
mobile/lib/extensions/translate_extensions.dart
Normal file
@ -0,0 +1,50 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:intl/message_format.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
extension StringTranslateExtension on String {
|
||||
String t({BuildContext? context, Map<String, Object>? args}) {
|
||||
return _translateHelper(context, this, args);
|
||||
}
|
||||
}
|
||||
|
||||
extension TextTranslateExtension on Text {
|
||||
Text t({BuildContext? context, Map<String, Object>? args}) {
|
||||
return Text(
|
||||
_translateHelper(context, data ?? '', args),
|
||||
key: key,
|
||||
style: style,
|
||||
strutStyle: strutStyle,
|
||||
textAlign: textAlign,
|
||||
textDirection: textDirection,
|
||||
locale: locale,
|
||||
softWrap: softWrap,
|
||||
overflow: overflow,
|
||||
textScaler: textScaler,
|
||||
maxLines: maxLines,
|
||||
semanticsLabel: semanticsLabel,
|
||||
textWidthBasis: textWidthBasis,
|
||||
textHeightBehavior: textHeightBehavior,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _translateHelper(
|
||||
BuildContext? context,
|
||||
String key, [
|
||||
Map<String, Object>? args,
|
||||
]) {
|
||||
if (key.isEmpty) {
|
||||
return '';
|
||||
}
|
||||
try {
|
||||
final translatedMessage = key.tr(context: context);
|
||||
return args != null
|
||||
? MessageFormat(translatedMessage, locale: Intl.defaultLocale ?? 'en')
|
||||
.format(args)
|
||||
: translatedMessage;
|
||||
} catch (e) {
|
||||
debugPrint('Translation failed for key "$key". Error: $e');
|
||||
return key;
|
||||
}
|
||||
}
|
@ -8,13 +8,13 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||
import 'package:immich_mobile/models/albums/album_search.model.dart';
|
||||
import 'package:immich_mobile/pages/common/large_leading_tile.dart';
|
||||
import 'package:immich_mobile/providers/album/album.provider.dart';
|
||||
import 'package:immich_mobile/providers/album/album_sort_by_options.provider.dart';
|
||||
import 'package:immich_mobile/providers/user.provider.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/utils/translation.dart';
|
||||
import 'package:immich_mobile/widgets/album/album_thumbnail_card.dart';
|
||||
import 'package:immich_mobile/widgets/common/immich_app_bar.dart';
|
||||
import 'package:immich_mobile/widgets/common/immich_thumbnail.dart';
|
||||
@ -230,11 +230,17 @@ class AlbumsPage extends HookConsumerWidget {
|
||||
),
|
||||
subtitle: sorted[index].ownerId != null
|
||||
? Text(
|
||||
'${t('items_count', {
|
||||
'count': sorted[index].assetCount,
|
||||
})} • ${sorted[index].ownerId != userId ? t('shared_by_user', {
|
||||
'user': sorted[index].ownerName!,
|
||||
}) : 'owned'.tr()}',
|
||||
'${'items_count'.t(
|
||||
context: context,
|
||||
args: {
|
||||
'count': sorted[index].assetCount,
|
||||
},
|
||||
)} • ${sorted[index].ownerId != userId ? 'shared_by_user'.t(
|
||||
context: context,
|
||||
args: {
|
||||
'user': sorted[index].ownerName!,
|
||||
},
|
||||
) : 'owned'.t(context: context)}',
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style:
|
||||
context.textTheme.bodyMedium?.copyWith(
|
||||
|
@ -2,9 +2,9 @@ import 'package:auto_route/auto_route.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/utils/translation.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||
import 'package:immich_mobile/pages/common/large_leading_tile.dart';
|
||||
import 'package:immich_mobile/providers/album/album.provider.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
@ -46,7 +46,10 @@ class LocalAlbumsPage extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
t('items_count', {'count': albums[index].assetCount}),
|
||||
'items_count'.t(
|
||||
context: context,
|
||||
args: {'count': albums[index].assetCount},
|
||||
),
|
||||
style: context.textTheme.bodyMedium?.copyWith(
|
||||
color: context.colorScheme.onSurfaceSecondary,
|
||||
),
|
||||
|
@ -1,10 +1,10 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/models/memories/memory.model.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/repositories/asset.repository.dart';
|
||||
import 'package:immich_mobile/services/api.service.dart';
|
||||
import 'package:immich_mobile/utils/translation.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
final memoryServiceProvider = StateProvider<MemoryService>((ref) {
|
||||
@ -40,7 +40,11 @@ class MemoryService {
|
||||
.getAllByRemoteId(memory.assets.map((e) => e.id));
|
||||
final yearsAgo = now.year - memory.data.year;
|
||||
if (dbAssets.isNotEmpty) {
|
||||
final String title = t('years_ago', {'years': yearsAgo.toString()});
|
||||
final String title = 'years_ago'.t(
|
||||
args: {
|
||||
'years': yearsAgo.toString(),
|
||||
},
|
||||
);
|
||||
memories.add(
|
||||
Memory(
|
||||
title: title,
|
||||
|
@ -6,10 +6,10 @@ import 'package:immich_mobile/constants/enums.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/extensions/asset_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||
import 'package:immich_mobile/providers/asset.provider.dart';
|
||||
import 'package:immich_mobile/services/asset.service.dart';
|
||||
import 'package:immich_mobile/services/share.service.dart';
|
||||
import 'package:immich_mobile/utils/translation.dart';
|
||||
import 'package:immich_mobile/widgets/common/date_time_picker.dart';
|
||||
import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
||||
import 'package:immich_mobile/widgets/common/location_picker.dart';
|
||||
@ -59,10 +59,11 @@ Future<void> handleArchiveAssets(
|
||||
await ref
|
||||
.read(assetProvider.notifier)
|
||||
.toggleArchive(selection, shouldArchive);
|
||||
|
||||
final message = shouldArchive
|
||||
? t('moved_to_archive', {'count': selection.length})
|
||||
: t('moved_to_library', {'count': selection.length});
|
||||
? 'moved_to_archive'
|
||||
.t(context: context, args: {'count': selection.length})
|
||||
: 'moved_to_library'
|
||||
.t(context: context, args: {'count': selection.length});
|
||||
if (context.mounted) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
|
@ -1,15 +0,0 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:intl/message_format.dart';
|
||||
|
||||
String t(String key, [Map<String, Object>? args]) {
|
||||
try {
|
||||
String message = key.tr();
|
||||
if (args != null) {
|
||||
return MessageFormat(message, locale: Intl.defaultLocale ?? 'en')
|
||||
.format(args);
|
||||
}
|
||||
return message;
|
||||
} catch (e) {
|
||||
return key;
|
||||
}
|
||||
}
|
@ -4,8 +4,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/entities/album.entity.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||
import 'package:immich_mobile/providers/user.provider.dart';
|
||||
import 'package:immich_mobile/utils/translation.dart';
|
||||
import 'package:immich_mobile/widgets/common/immich_thumbnail.dart';
|
||||
|
||||
class AlbumThumbnailCard extends ConsumerWidget {
|
||||
@ -62,7 +62,12 @@ class AlbumThumbnailCard extends ConsumerWidget {
|
||||
if (album.ownerId == ref.read(currentUserProvider)?.id) {
|
||||
owner = 'owned'.tr();
|
||||
} else if (album.ownerName != null) {
|
||||
owner = t('shared_by_user', {'user': album.ownerName!});
|
||||
owner = 'shared_by_user'.t(
|
||||
context: context,
|
||||
args: {
|
||||
'user': album.ownerName!,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,7 +75,12 @@ class AlbumThumbnailCard extends ConsumerWidget {
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: t('items_count', {'count': album.assetCount}),
|
||||
text: 'items_count'.t(
|
||||
context: context,
|
||||
args: {
|
||||
'count': album.assetCount,
|
||||
},
|
||||
),
|
||||
),
|
||||
if (owner != null) const TextSpan(text: ' • '),
|
||||
if (owner != null) TextSpan(text: owner),
|
||||
|
@ -4,10 +4,10 @@ import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:immich_mobile/entities/album.entity.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/services/api.service.dart';
|
||||
import 'package:immich_mobile/utils/image_url_builder.dart';
|
||||
import 'package:immich_mobile/utils/translation.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class AlbumThumbnailListTile extends StatelessWidget {
|
||||
@ -91,7 +91,12 @@ class AlbumThumbnailListTile extends StatelessWidget {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
t('items_count', {'count': album.assetCount}),
|
||||
'items_count'.t(
|
||||
context: context,
|
||||
args: {
|
||||
'count': album.assetCount,
|
||||
},
|
||||
),
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
),
|
||||
|
@ -11,6 +11,7 @@ import 'package:immich_mobile/constants/enums.dart';
|
||||
import 'package:immich_mobile/entities/album.entity.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/extensions/collection_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||
import 'package:immich_mobile/models/asset_selection_state.dart';
|
||||
import 'package:immich_mobile/providers/album/album.provider.dart';
|
||||
import 'package:immich_mobile/providers/asset.provider.dart';
|
||||
@ -24,7 +25,6 @@ import 'package:immich_mobile/services/album.service.dart';
|
||||
import 'package:immich_mobile/services/stack.service.dart';
|
||||
import 'package:immich_mobile/utils/immich_loading_overlay.dart';
|
||||
import 'package:immich_mobile/utils/selection_handlers.dart';
|
||||
import 'package:immich_mobile/utils/translation.dart';
|
||||
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
|
||||
import 'package:immich_mobile/widgets/asset_grid/control_bottom_app_bar.dart';
|
||||
import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart';
|
||||
@ -257,13 +257,19 @@ class MultiselectGrid extends HookConsumerWidget {
|
||||
final failedCount = totalCount - successCount;
|
||||
|
||||
final msg = failedCount > 0
|
||||
? t('assets_downloaded_failed', {
|
||||
'count': successCount,
|
||||
'error': failedCount,
|
||||
})
|
||||
: t('assets_downloaded_successfully', {
|
||||
'count': successCount,
|
||||
});
|
||||
? 'assets_downloaded_failed'.t(
|
||||
context: context,
|
||||
args: {
|
||||
'count': successCount,
|
||||
'error': failedCount,
|
||||
},
|
||||
)
|
||||
: 'assets_downloaded_successfully'.t(
|
||||
context: context,
|
||||
args: {
|
||||
'count': successCount,
|
||||
},
|
||||
);
|
||||
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
|
@ -1,9 +1,9 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||
import 'package:immich_mobile/providers/backup/ios_background_settings.provider.dart';
|
||||
import 'package:immich_mobile/utils/translation.dart';
|
||||
|
||||
/// This is a simple debug widget which should be removed later on when we are
|
||||
/// more confident about background sync
|
||||
@ -22,29 +22,28 @@ class IosDebugInfoTile extends HookConsumerWidget {
|
||||
|
||||
final String title;
|
||||
if (processes == 0) {
|
||||
title = 'ios_debug_info_no_processes_queued'.tr();
|
||||
title = 'ios_debug_info_no_processes_queued'.t(context: context);
|
||||
} else {
|
||||
title = t('ios_debug_info_processes_queued', {'count': processes});
|
||||
title = 'ios_debug_info_processes_queued'
|
||||
.t(context: context, args: {'count': processes});
|
||||
}
|
||||
|
||||
final df = DateFormat.yMd().add_jm();
|
||||
final String subtitle;
|
||||
if (fetch == null && processing == null) {
|
||||
subtitle = 'ios_debug_info_no_sync_yet'.tr();
|
||||
subtitle = 'ios_debug_info_no_sync_yet'.t(context: context);
|
||||
} else if (fetch != null && processing == null) {
|
||||
subtitle =
|
||||
t('ios_debug_info_fetch_ran_at', {'dateTime': df.format(fetch)});
|
||||
subtitle = 'ios_debug_info_fetch_ran_at'
|
||||
.t(context: context, args: {'dateTime': df.format(fetch)});
|
||||
} else if (processing != null && fetch == null) {
|
||||
subtitle = t(
|
||||
'ios_debug_info_processing_ran_at',
|
||||
{'dateTime': df.format(processing)},
|
||||
);
|
||||
subtitle = 'ios_debug_info_processing_ran_at'
|
||||
.t(context: context, args: {'dateTime': df.format(processing)});
|
||||
} else {
|
||||
final fetchOrProcessing =
|
||||
fetch!.isAfter(processing!) ? fetch : processing;
|
||||
subtitle = t(
|
||||
'ios_debug_info_last_sync_at',
|
||||
{'dateTime': df.format(fetchOrProcessing)},
|
||||
subtitle = 'ios_debug_info_last_sync_at'.t(
|
||||
context: context,
|
||||
args: {'dateTime': df.format(fetchOrProcessing)},
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:immich_mobile/constants/locales.dart';
|
||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||
import 'package:immich_mobile/services/localization.service.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/widgets/common/search_field.dart';
|
||||
@ -91,6 +92,7 @@ class LanguageSettings extends HookConsumerWidget {
|
||||
padding: const EdgeInsets.all(8),
|
||||
itemCount: filteredLocaleEntries.value.length,
|
||||
itemExtent: 64.0,
|
||||
cacheExtent: 100,
|
||||
itemBuilder: (context, index) {
|
||||
final countryName =
|
||||
filteredLocaleEntries.value[index].key;
|
||||
@ -100,6 +102,7 @@ class LanguageSettings extends HookConsumerWidget {
|
||||
selectedLocale.value == localeValue;
|
||||
|
||||
return _LanguageItem(
|
||||
key: ValueKey(localeValue.toString()),
|
||||
countryName: countryName,
|
||||
localeValue: localeValue,
|
||||
isSelected: isSelected,
|
||||
@ -162,7 +165,7 @@ class _LanguageSearchBar extends StatelessWidget {
|
||||
child: SearchField(
|
||||
autofocus: false,
|
||||
contentPadding: const EdgeInsets.all(12),
|
||||
hintText: 'language_search_hint'.tr(),
|
||||
hintText: 'language_search_hint'.t(context: context),
|
||||
prefixIcon: const Icon(Icons.search_rounded),
|
||||
suffixIcon: controller.text.isNotEmpty
|
||||
? IconButton(
|
||||
@ -196,14 +199,14 @@ class _LanguageNotFound extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'language_no_results_title'.tr(),
|
||||
'language_no_results_title'.t(context: context),
|
||||
style: context.textTheme.titleMedium?.copyWith(
|
||||
color: context.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'language_no_results_subtitle'.tr(),
|
||||
'language_no_results_subtitle'.t(context: context),
|
||||
style: context.textTheme.bodyMedium?.copyWith(
|
||||
color: context.colorScheme.onSurface.withValues(alpha: 0.8),
|
||||
),
|
||||
@ -246,7 +249,7 @@ class _LanguageApplyButton extends StatelessWidget {
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
'setting_languages_apply'.tr(),
|
||||
'setting_languages_apply'.t(context: context),
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 16.0,
|
||||
@ -261,6 +264,7 @@ class _LanguageApplyButton extends StatelessWidget {
|
||||
|
||||
class _LanguageItem extends StatelessWidget {
|
||||
const _LanguageItem({
|
||||
super.key,
|
||||
required this.countryName,
|
||||
required this.localeValue,
|
||||
required this.isSelected,
|
||||
|
Loading…
x
Reference in New Issue
Block a user