From ece977d9ca96a713ee417ffd1a8173a984553ef7 Mon Sep 17 00:00:00 2001
From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com>
Date: Tue, 6 May 2025 18:51:05 +0530
Subject: [PATCH] fix(mobile): empty translation placeholders (#18063)

* fix: empty placeholders

* fix: use namedArgs

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
---
 i18n/en.json                                  | 122 +++++++++---------
 .../album/album_asset_selection.page.dart     |   4 +-
 mobile/lib/pages/albums/albums.page.dart      |  12 +-
 .../backup/backup_album_selection.page.dart   |   6 +-
 mobile/lib/pages/editing/edit.page.dart       |  16 +--
 mobile/lib/pages/library/archive.page.dart    |   2 +-
 .../pages/library/partner/partner.page.dart   |   3 +-
 .../shared_link/shared_link_edit.page.dart    |  24 ++--
 mobile/lib/pages/library/trash.page.dart      |  20 +--
 .../pages/share_intent/share_intent.page.dart |  12 +-
 .../backup/manual_upload.provider.dart        |  20 +--
 mobile/lib/services/background.service.dart   |   4 +-
 mobile/lib/services/memory.service.dart       |   3 +-
 .../widgets/album/album_thumbnail_card.dart   |   8 +-
 .../album/album_thumbnail_listtile.dart       |   4 +-
 .../widgets/asset_grid/multiselect_grid.dart  |  36 +++---
 .../detail_panel/people_info.dart             |  12 +-
 .../lib/widgets/backup/asset_info_table.dart  |  21 +--
 .../lib/widgets/backup/error_chip_text.dart   |   4 +-
 .../common/app_bar_dialog/app_bar_dialog.dart |  16 +--
 mobile/lib/widgets/map/map_asset_grid.dart    |  14 +-
 .../map_settings_time_dropdown.dart           |   7 +-
 .../widgets/settings/advanced_settings.dart   |   3 +-
 .../asset_list_layout_settings.dart           |   4 +-
 .../backup_settings/background_settings.dart  |  16 ++-
 .../settings/local_storage_settings.dart      |   6 +-
 .../settings/notification_setting.dart        |  13 +-
 .../widgets/shared_link/shared_link_item.dart |  19 +--
 28 files changed, 228 insertions(+), 203 deletions(-)

diff --git a/i18n/en.json b/i18n/en.json
index 8f190f2c09..80381dcff9 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -369,7 +369,7 @@
   "advanced": "Advanced",
   "advanced_settings_enable_alternate_media_filter_subtitle": "Use this option to filter media during sync based on alternate criteria. Only try this if you have issues with the app detecting all albums.",
   "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Use alternate device album sync filter",
-  "advanced_settings_log_level_title": "Log level: {}",
+  "advanced_settings_log_level_title": "Log level: {level}",
   "advanced_settings_prefer_remote_subtitle": "Some devices are painfully slow to load thumbnails from assets on the device. Activate this setting to load remote images instead.",
   "advanced_settings_prefer_remote_title": "Prefer remote images",
   "advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request",
@@ -400,9 +400,9 @@
   "album_remove_user_confirmation": "Are you sure you want to remove {user}?",
   "album_share_no_users": "Looks like you have shared this album with all users or you don't have any user to share with.",
   "album_thumbnail_card_item": "1 item",
-  "album_thumbnail_card_items": "{} items",
+  "album_thumbnail_card_items": "{count} items",
   "album_thumbnail_card_shared": " · Shared",
-  "album_thumbnail_shared_by": "Shared by {}",
+  "album_thumbnail_shared_by": "Shared by {user}",
   "album_updated": "Album updated",
   "album_updated_setting_description": "Receive an email notification when a shared album has new assets",
   "album_user_left": "Left {album}",
@@ -440,7 +440,7 @@
   "archive": "Archive",
   "archive_or_unarchive_photo": "Archive or unarchive photo",
   "archive_page_no_archived_assets": "No archived assets found",
-  "archive_page_title": "Archive ({})",
+  "archive_page_title": "Archive ({count})",
   "archive_size": "Archive size",
   "archive_size_description": "Configure the archive size for downloads (in GiB)",
   "archived": "Archived",
@@ -477,18 +477,18 @@
   "assets_added_to_album_count": "Added {count, plural, one {# asset} other {# assets}} to the album",
   "assets_added_to_name_count": "Added {count, plural, one {# asset} other {# assets}} to {hasName, select, true {<b>{name}</b>} other {new album}}",
   "assets_count": "{count, plural, one {# asset} other {# assets}}",
-  "assets_deleted_permanently": "{} asset(s) deleted permanently",
-  "assets_deleted_permanently_from_server": "{} asset(s) deleted permanently from the Immich server",
+  "assets_deleted_permanently": "{count} asset(s) deleted permanently",
+  "assets_deleted_permanently_from_server": "{count} asset(s) deleted permanently from the Immich server",
   "assets_moved_to_trash_count": "Moved {count, plural, one {# asset} other {# assets}} to trash",
   "assets_permanently_deleted_count": "Permanently deleted {count, plural, one {# asset} other {# assets}}",
   "assets_removed_count": "Removed {count, plural, one {# asset} other {# assets}}",
-  "assets_removed_permanently_from_device": "{} asset(s) removed permanently from your device",
+  "assets_removed_permanently_from_device": "{count} asset(s) removed permanently from your device",
   "assets_restore_confirmation": "Are you sure you want to restore all your trashed assets? You cannot undo this action! Note that any offline assets cannot be restored this way.",
   "assets_restored_count": "Restored {count, plural, one {# asset} other {# assets}}",
-  "assets_restored_successfully": "{} asset(s) restored successfully",
-  "assets_trashed": "{} asset(s) trashed",
+  "assets_restored_successfully": "{count} asset(s) restored successfully",
+  "assets_trashed": "{count} asset(s) trashed",
   "assets_trashed_count": "Trashed {count, plural, one {# asset} other {# assets}}",
-  "assets_trashed_from_server": "{} asset(s) trashed from the Immich server",
+  "assets_trashed_from_server": "{count} asset(s) trashed from the Immich server",
   "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets were}} already part of the album",
   "authorized_devices": "Authorized Devices",
   "automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere",
@@ -497,7 +497,7 @@
   "back_close_deselect": "Back, close, or deselect",
   "background_location_permission": "Background location permission",
   "background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name",
-  "backup_album_selection_page_albums_device": "Albums on device ({})",
+  "backup_album_selection_page_albums_device": "Albums on device ({count})",
   "backup_album_selection_page_albums_tap": "Tap to include, double tap to exclude",
   "backup_album_selection_page_assets_scatter": "Assets can scatter across multiple albums. Thus, albums can be included or excluded during the backup process.",
   "backup_album_selection_page_select_albums": "Select albums",
@@ -506,11 +506,11 @@
   "backup_all": "All",
   "backup_background_service_backup_failed_message": "Failed to backup assets. Retrying…",
   "backup_background_service_connection_failed_message": "Failed to connect to the server. Retrying…",
-  "backup_background_service_current_upload_notification": "Uploading {}",
+  "backup_background_service_current_upload_notification": "Uploading {filename}",
   "backup_background_service_default_notification": "Checking for new assets…",
   "backup_background_service_error_title": "Backup error",
   "backup_background_service_in_progress_notification": "Backing up your assets…",
-  "backup_background_service_upload_failure_notification": "Failed to upload {}",
+  "backup_background_service_upload_failure_notification": "Failed to upload {filename}",
   "backup_controller_page_albums": "Backup Albums",
   "backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.",
   "backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled",
@@ -521,7 +521,7 @@
   "backup_controller_page_background_battery_info_title": "Battery optimizations",
   "backup_controller_page_background_charging": "Only while charging",
   "backup_controller_page_background_configure_error": "Failed to configure the background service",
-  "backup_controller_page_background_delay": "Delay new assets backup: {}",
+  "backup_controller_page_background_delay": "Delay new assets backup: {duration}",
   "backup_controller_page_background_description": "Turn on the background service to automatically backup any new assets without needing to open the app",
   "backup_controller_page_background_is_off": "Automatic background backup is off",
   "backup_controller_page_background_is_on": "Automatic background backup is on",
@@ -531,12 +531,12 @@
   "backup_controller_page_backup": "Backup",
   "backup_controller_page_backup_selected": "Selected: ",
   "backup_controller_page_backup_sub": "Backed up photos and videos",
-  "backup_controller_page_created": "Created on: {}",
+  "backup_controller_page_created": "Created on: {date}",
   "backup_controller_page_desc_backup": "Turn on foreground backup to automatically upload new assets to the server when opening the app.",
   "backup_controller_page_excluded": "Excluded: ",
-  "backup_controller_page_failed": "Failed ({})",
-  "backup_controller_page_filename": "File name: {} [{}]",
-  "backup_controller_page_id": "ID: {}",
+  "backup_controller_page_failed": "Failed ({count})",
+  "backup_controller_page_filename": "File name: {filename} [{size}]",
+  "backup_controller_page_id": "ID: {id}",
   "backup_controller_page_info": "Backup Information",
   "backup_controller_page_none_selected": "None selected",
   "backup_controller_page_remainder": "Remainder",
@@ -545,7 +545,7 @@
   "backup_controller_page_start_backup": "Start Backup",
   "backup_controller_page_status_off": "Automatic foreground backup is off",
   "backup_controller_page_status_on": "Automatic foreground backup is on",
-  "backup_controller_page_storage_format": "{} of {} used",
+  "backup_controller_page_storage_format": "{used} of {total} used",
   "backup_controller_page_to_backup": "Albums to be backed up",
   "backup_controller_page_total_sub": "All unique photos and videos from selected albums",
   "backup_controller_page_turn_off": "Turn off foreground backup",
@@ -570,21 +570,21 @@
   "bulk_keep_duplicates_confirmation": "Are you sure you want to keep {count, plural, one {# duplicate asset} other {# duplicate assets}}? This will resolve all duplicate groups without deleting anything.",
   "bulk_trash_duplicates_confirmation": "Are you sure you want to bulk trash {count, plural, one {# duplicate asset} other {# duplicate assets}}? This will keep the largest asset of each group and trash all other duplicates.",
   "buy": "Purchase Immich",
-  "cache_settings_album_thumbnails": "Library page thumbnails ({} assets)",
+  "cache_settings_album_thumbnails": "Library page thumbnails ({count} assets)",
   "cache_settings_clear_cache_button": "Clear cache",
   "cache_settings_clear_cache_button_title": "Clears the app's cache. This will significantly impact the app's performance until the cache has rebuilt.",
   "cache_settings_duplicated_assets_clear_button": "CLEAR",
   "cache_settings_duplicated_assets_subtitle": "Photos and videos that are black listed by the app",
-  "cache_settings_duplicated_assets_title": "Duplicated Assets ({})",
-  "cache_settings_image_cache_size": "Image cache size ({} assets)",
+  "cache_settings_duplicated_assets_title": "Duplicated Assets ({count})",
+  "cache_settings_image_cache_size": "Image cache size ({count} assets)",
   "cache_settings_statistics_album": "Library thumbnails",
-  "cache_settings_statistics_assets": "{} assets ({})",
+  "cache_settings_statistics_assets": "{count} assets ({size})",
   "cache_settings_statistics_full": "Full images",
   "cache_settings_statistics_shared": "Shared album thumbnails",
   "cache_settings_statistics_thumbnail": "Thumbnails",
   "cache_settings_statistics_title": "Cache usage",
   "cache_settings_subtitle": "Control the caching behaviour of the Immich mobile application",
-  "cache_settings_thumbnail_size": "Thumbnail cache size ({} assets)",
+  "cache_settings_thumbnail_size": "Thumbnail cache size ({count} assets)",
   "cache_settings_tile_subtitle": "Control the local storage behaviour",
   "cache_settings_tile_title": "Local Storage",
   "cache_settings_title": "Caching Settings",
@@ -654,7 +654,7 @@
   "contain": "Contain",
   "context": "Context",
   "continue": "Continue",
-  "control_bottom_app_bar_album_info_shared": "{} items · Shared",
+  "control_bottom_app_bar_album_info_shared": "{count} items · Shared",
   "control_bottom_app_bar_create_new_album": "Create new album",
   "control_bottom_app_bar_delete_from_immich": "Delete from Immich",
   "control_bottom_app_bar_delete_from_local": "Delete from device",
@@ -763,7 +763,7 @@
   "download_enqueue": "Download enqueued",
   "download_error": "Download Error",
   "download_failed": "Download failed",
-  "download_filename": "file: {}",
+  "download_filename": "file: {filename}",
   "download_finished": "Download finished",
   "download_include_embedded_motion_videos": "Embedded videos",
   "download_include_embedded_motion_videos_description": "Include videos embedded in motion photos as a separate file",
@@ -819,7 +819,7 @@
   "error_change_sort_album": "Failed to change album sort order",
   "error_delete_face": "Error deleting face from asset",
   "error_loading_image": "Error loading image",
-  "error_saving_image": "Error: {}",
+  "error_saving_image": "Error: {error}",
   "error_title": "Error - Something went wrong",
   "errors": {
     "cannot_navigate_next_asset": "Cannot navigate to the next asset",
@@ -955,10 +955,10 @@
   "exif_bottom_sheet_location": "LOCATION",
   "exif_bottom_sheet_people": "PEOPLE",
   "exif_bottom_sheet_person_add_person": "Add name",
-  "exif_bottom_sheet_person_age": "Age {}",
-  "exif_bottom_sheet_person_age_months": "Age {} months",
-  "exif_bottom_sheet_person_age_year_months": "Age 1 year, {} months",
-  "exif_bottom_sheet_person_age_years": "Age {}",
+  "exif_bottom_sheet_person_age": "Age {age}",
+  "exif_bottom_sheet_person_age_months": "Age {months} months",
+  "exif_bottom_sheet_person_age_year_months": "Age 1 year, {months} months",
+  "exif_bottom_sheet_person_age_years": "Age {years}",
   "exit_slideshow": "Exit Slideshow",
   "expand_all": "Expand all",
   "experimental_settings_new_asset_list_subtitle": "Work in progress",
@@ -1173,8 +1173,8 @@
   "manage_your_devices": "Manage your logged-in devices",
   "manage_your_oauth_connection": "Manage your OAuth connection",
   "map": "Map",
-  "map_assets_in_bound": "{} photo",
-  "map_assets_in_bounds": "{} photos",
+  "map_assets_in_bound": "{count} photo",
+  "map_assets_in_bounds": "{count} photos",
   "map_cannot_get_user_location": "Cannot get user's location",
   "map_location_dialog_yes": "Yes",
   "map_location_picker_page_use_location": "Use this location",
@@ -1188,9 +1188,9 @@
   "map_settings": "Map settings",
   "map_settings_dark_mode": "Dark mode",
   "map_settings_date_range_option_day": "Past 24 hours",
-  "map_settings_date_range_option_days": "Past {} days",
+  "map_settings_date_range_option_days": "Past {days} days",
   "map_settings_date_range_option_year": "Past year",
-  "map_settings_date_range_option_years": "Past {} years",
+  "map_settings_date_range_option_years": "Past {years} years",
   "map_settings_dialog_title": "Map Settings",
   "map_settings_include_show_archived": "Include Archived",
   "map_settings_include_show_partners": "Include Partners",
@@ -1209,7 +1209,7 @@
   "memories_start_over": "Start Over",
   "memories_swipe_to_close": "Swipe up to close",
   "memories_year_ago": "A year ago",
-  "memories_years_ago": "{} years ago",
+  "memories_years_ago": "{years} years ago",
   "memory": "Memory",
   "memory_lane_title": "Memory Lane {title}",
   "menu": "Menu",
@@ -1316,7 +1316,7 @@
   "partner_page_partner_add_failed": "Failed to add partner",
   "partner_page_select_partner": "Select partner",
   "partner_page_shared_to_title": "Shared to",
-  "partner_page_stop_sharing_content": "{} will no longer be able to access your photos.",
+  "partner_page_stop_sharing_content": "{partner} will no longer be able to access your photos.",
   "partner_sharing": "Partner Sharing",
   "partners": "Partners",
   "password": "Password",
@@ -1604,12 +1604,12 @@
   "setting_languages_apply": "Apply",
   "setting_languages_subtitle": "Change the app's language",
   "setting_languages_title": "Languages",
-  "setting_notifications_notify_failures_grace_period": "Notify background backup failures: {}",
-  "setting_notifications_notify_hours": "{} hours",
+  "setting_notifications_notify_failures_grace_period": "Notify background backup failures: {duration}",
+  "setting_notifications_notify_hours": "{count} hours",
   "setting_notifications_notify_immediately": "immediately",
-  "setting_notifications_notify_minutes": "{} minutes",
+  "setting_notifications_notify_minutes": "{count} minutes",
   "setting_notifications_notify_never": "never",
-  "setting_notifications_notify_seconds": "{} seconds",
+  "setting_notifications_notify_seconds": "{count} seconds",
   "setting_notifications_single_progress_subtitle": "Detailed upload progress information per asset",
   "setting_notifications_single_progress_title": "Show background backup detail progress",
   "setting_notifications_subtitle": "Adjust your notification preferences",
@@ -1623,7 +1623,7 @@
   "settings_saved": "Settings saved",
   "share": "Share",
   "share_add_photos": "Add photos",
-  "share_assets_selected": "{} selected",
+  "share_assets_selected": "{count} selected",
   "share_dialog_preparing": "Preparing...",
   "shared": "Shared",
   "shared_album_activities_input_disable": "Comment is disabled",
@@ -1637,32 +1637,32 @@
   "shared_by_user": "Shared by {user}",
   "shared_by_you": "Shared by you",
   "shared_from_partner": "Photos from {partner}",
-  "shared_intent_upload_button_progress_text": "{} / {} Uploaded",
+  "shared_intent_upload_button_progress_text": "{current} / {total} Uploaded",
   "shared_link_app_bar_title": "Shared Links",
   "shared_link_clipboard_copied_massage": "Copied to clipboard",
-  "shared_link_clipboard_text": "Link: {}\nPassword: {}",
+  "shared_link_clipboard_text": "Link: {link}\nPassword: {password}",
   "shared_link_create_error": "Error while creating shared link",
   "shared_link_edit_description_hint": "Enter the share description",
   "shared_link_edit_expire_after_option_day": "1 day",
-  "shared_link_edit_expire_after_option_days": "{} days",
+  "shared_link_edit_expire_after_option_days": "{count} days",
   "shared_link_edit_expire_after_option_hour": "1 hour",
-  "shared_link_edit_expire_after_option_hours": "{} hours",
+  "shared_link_edit_expire_after_option_hours": "{count} hours",
   "shared_link_edit_expire_after_option_minute": "1 minute",
-  "shared_link_edit_expire_after_option_minutes": "{} minutes",
-  "shared_link_edit_expire_after_option_months": "{} months",
-  "shared_link_edit_expire_after_option_year": "{} year",
+  "shared_link_edit_expire_after_option_minutes": "{count} minutes",
+  "shared_link_edit_expire_after_option_months": "{count} months",
+  "shared_link_edit_expire_after_option_year": "{count} year",
   "shared_link_edit_password_hint": "Enter the share password",
   "shared_link_edit_submit_button": "Update link",
   "shared_link_error_server_url_fetch": "Cannot fetch the server url",
-  "shared_link_expires_day": "Expires in {} day",
-  "shared_link_expires_days": "Expires in {} days",
-  "shared_link_expires_hour": "Expires in {} hour",
-  "shared_link_expires_hours": "Expires in {} hours",
-  "shared_link_expires_minute": "Expires in {} minute",
-  "shared_link_expires_minutes": "Expires in {} minutes",
+  "shared_link_expires_day": "Expires in {count} day",
+  "shared_link_expires_days": "Expires in {count} days",
+  "shared_link_expires_hour": "Expires in {count} hour",
+  "shared_link_expires_hours": "Expires in {count} hours",
+  "shared_link_expires_minute": "Expires in {count} minute",
+  "shared_link_expires_minutes": "Expires in {count} minutes",
   "shared_link_expires_never": "Expires ∞",
-  "shared_link_expires_second": "Expires in {} second",
-  "shared_link_expires_seconds": "Expires in {} seconds",
+  "shared_link_expires_second": "Expires in {count} second",
+  "shared_link_expires_seconds": "Expires in {count} seconds",
   "shared_link_individual_shared": "Individual shared",
   "shared_link_info_chip_metadata": "EXIF",
   "shared_link_manage_links": "Manage Shared links",
@@ -1763,7 +1763,7 @@
   "theme_selection": "Theme selection",
   "theme_selection_description": "Automatically set the theme to light or dark based on your browser's system preference",
   "theme_setting_asset_list_storage_indicator_title": "Show storage indicator on asset tiles",
-  "theme_setting_asset_list_tiles_per_row_title": "Number of assets per row ({})",
+  "theme_setting_asset_list_tiles_per_row_title": "Number of assets per row ({count})",
   "theme_setting_colorful_interface_subtitle": "Apply primary color to background surfaces.",
   "theme_setting_colorful_interface_title": "Colorful interface",
   "theme_setting_image_viewer_quality_subtitle": "Adjust the quality of the detail image viewer",
@@ -1798,11 +1798,11 @@
   "trash_no_results_message": "Trashed photos and videos will show up here.",
   "trash_page_delete_all": "Delete All",
   "trash_page_empty_trash_dialog_content": "Do you want to empty your trashed assets? These items will be permanently removed from Immich",
-  "trash_page_info": "Trashed items will be permanently deleted after {} days",
+  "trash_page_info": "Trashed items will be permanently deleted after {days} days",
   "trash_page_no_assets": "No trashed assets",
   "trash_page_restore_all": "Restore All",
   "trash_page_select_assets_btn": "Select assets",
-  "trash_page_title": "Trash ({})",
+  "trash_page_title": "Trash ({count})",
   "trashed_items_will_be_permanently_deleted_after": "Trashed items will be permanently deleted after {days, plural, one {# day} other {# days}}.",
   "type": "Type",
   "unarchive": "Unarchive",
@@ -1840,7 +1840,7 @@
   "upload_status_errors": "Errors",
   "upload_status_uploaded": "Uploaded",
   "upload_success": "Upload success, refresh the page to see new upload assets.",
-  "upload_to_immich": "Upload to Immich ({})",
+  "upload_to_immich": "Upload to Immich ({count})",
   "uploading": "Uploading",
   "url": "URL",
   "usage": "Usage",
diff --git a/mobile/lib/pages/album/album_asset_selection.page.dart b/mobile/lib/pages/album/album_asset_selection.page.dart
index 617fbfee11..7b0ce8cdc4 100644
--- a/mobile/lib/pages/album/album_asset_selection.page.dart
+++ b/mobile/lib/pages/album/album_asset_selection.page.dart
@@ -3,13 +3,13 @@ import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_hooks/flutter_hooks.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/entities/asset.entity.dart';
 import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
 import 'package:immich_mobile/models/albums/asset_selection_page_result.model.dart';
 import 'package:immich_mobile/providers/timeline.provider.dart';
 import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
 import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart';
-import 'package:immich_mobile/entities/asset.entity.dart';
 
 @RoutePage()
 class AlbumAssetSelectionPage extends HookConsumerWidget {
@@ -59,7 +59,7 @@ class AlbumAssetSelectionPage extends HookConsumerWidget {
             : const Text(
                 'share_assets_selected',
                 style: TextStyle(fontSize: 18),
-              ).tr(args: [selected.value.length.toString()]),
+              ).tr(namedArgs: {'count': selected.value.length.toString()}),
         centerTitle: false,
         actions: [
           if (selected.value.isNotEmpty || canDeselect)
diff --git a/mobile/lib/pages/albums/albums.page.dart b/mobile/lib/pages/albums/albums.page.dart
index 1269fe0a8e..f09cd5a408 100644
--- a/mobile/lib/pages/albums/albums.page.dart
+++ b/mobile/lib/pages/albums/albums.page.dart
@@ -229,11 +229,13 @@ class AlbumsPage extends HookConsumerWidget {
                             ),
                             subtitle: sorted[index].ownerId != null
                                 ? Text(
-                                    '${(sorted[index].assetCount == 1 ? 'album_thumbnail_card_item'.tr(
-                                        args: ['${sorted[index].assetCount}'],
-                                      ) : 'album_thumbnail_card_items'.tr(
-                                        args: ['${sorted[index].assetCount}'],
-                                      ))} • ${sorted[index].ownerId != userId ? 'album_thumbnail_shared_by'.tr(args: [sorted[index].ownerName!]) : 'owned'.tr()}',
+                                    '${(sorted[index].assetCount == 1 ? 'album_thumbnail_card_item'.tr() : 'album_thumbnail_card_items'.tr(
+                                        namedArgs: {
+                                          'count': sorted[index]
+                                              .assetCount
+                                              .toString(),
+                                        },
+                                      ))} • ${sorted[index].ownerId != userId ? 'album_thumbnail_shared_by'.tr(namedArgs: {'user': sorted[index].ownerName!}) : 'owned'.tr()}',
                                     overflow: TextOverflow.ellipsis,
                                     style:
                                         context.textTheme.bodyMedium?.copyWith(
diff --git a/mobile/lib/pages/backup/backup_album_selection.page.dart b/mobile/lib/pages/backup/backup_album_selection.page.dart
index 671a9bfe16..c4124efb52 100644
--- a/mobile/lib/pages/backup/backup_album_selection.page.dart
+++ b/mobile/lib/pages/backup/backup_album_selection.page.dart
@@ -214,13 +214,13 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
                 ListTile(
                   title: Text(
                     "backup_album_selection_page_albums_device".tr(
-                      args: [
-                        ref
+                      namedArgs: {
+                        'count': ref
                             .watch(backupProvider)
                             .availableAlbums
                             .length
                             .toString(),
-                      ],
+                      },
                     ),
                     style: context.textTheme.titleSmall,
                   ),
diff --git a/mobile/lib/pages/editing/edit.page.dart b/mobile/lib/pages/editing/edit.page.dart
index bfa60eae00..39524df024 100644
--- a/mobile/lib/pages/editing/edit.page.dart
+++ b/mobile/lib/pages/editing/edit.page.dart
@@ -1,18 +1,18 @@
-import 'dart:typed_data';
 import 'dart:async';
+import 'dart:typed_data';
 import 'dart:ui';
 
+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:fluttertoast/fluttertoast.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/entities/asset.entity.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
-import 'package:immich_mobile/repositories/file_media.repository.dart';
-import 'package:immich_mobile/widgets/common/immich_toast.dart';
-import 'package:auto_route/auto_route.dart';
-import 'package:immich_mobile/routing/router.dart';
 import 'package:immich_mobile/providers/album/album.provider.dart';
-import 'package:easy_localization/easy_localization.dart';
+import 'package:immich_mobile/repositories/file_media.repository.dart';
+import 'package:immich_mobile/routing/router.dart';
+import 'package:immich_mobile/widgets/common/immich_toast.dart';
 import 'package:path/path.dart' as p;
 
 /// A stateless widget that provides functionality for editing an image.
@@ -81,7 +81,7 @@ class EditImagePage extends ConsumerWidget {
       ImmichToast.show(
         durationInSecond: 6,
         context: context,
-        msg: "error_saving_image".tr(args: [e.toString()]),
+        msg: "error_saving_image".tr(namedArgs: {'error': e.toString()}),
         gravity: ToastGravity.CENTER,
       );
     }
diff --git a/mobile/lib/pages/library/archive.page.dart b/mobile/lib/pages/library/archive.page.dart
index a13adc21f2..2b4aa64f3b 100644
--- a/mobile/lib/pages/library/archive.page.dart
+++ b/mobile/lib/pages/library/archive.page.dart
@@ -24,7 +24,7 @@ class ArchivePage extends HookConsumerWidget {
         automaticallyImplyLeading: false,
         title: const Text(
           'archive_page_title',
-        ).tr(args: [count]),
+        ).tr(namedArgs: {'count': count}),
       );
     }
 
diff --git a/mobile/lib/pages/library/partner/partner.page.dart b/mobile/lib/pages/library/partner/partner.page.dart
index 90a6a7f04e..91b661e7ce 100644
--- a/mobile/lib/pages/library/partner/partner.page.dart
+++ b/mobile/lib/pages/library/partner/partner.page.dart
@@ -73,7 +73,8 @@ class PartnerPage extends HookConsumerWidget {
         builder: (BuildContext context) {
           return ConfirmDialog(
             title: "stop_photo_sharing",
-            content: "partner_page_stop_sharing_content".tr(args: [u.name]),
+            content: "partner_page_stop_sharing_content"
+                .tr(namedArgs: {'partner': u.name}),
             onOk: () => ref.read(partnerServiceProvider).removePartner(u),
           );
         },
diff --git a/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart b/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart
index e4bf1ebe9b..6c18841089 100644
--- a/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart
+++ b/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart
@@ -7,11 +7,11 @@ 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/models/shared_link/shared_link.model.dart';
+import 'package:immich_mobile/providers/server_info.provider.dart';
 import 'package:immich_mobile/providers/shared_link.provider.dart';
 import 'package:immich_mobile/services/shared_link.service.dart';
-import 'package:immich_mobile/providers/server_info.provider.dart';
-import 'package:immich_mobile/widgets/common/immich_toast.dart';
 import 'package:immich_mobile/utils/url_helper.dart';
+import 'package:immich_mobile/widgets/common/immich_toast.dart';
 
 @RoutePage()
 class SharedLinkEditPage extends HookConsumerWidget {
@@ -241,8 +241,8 @@ class SharedLinkEditPage extends HookConsumerWidget {
           ),
           DropdownMenuEntry(
             value: 30,
-            label:
-                "shared_link_edit_expire_after_option_minutes".tr(args: ["30"]),
+            label: "shared_link_edit_expire_after_option_minutes"
+                .tr(namedArgs: {'count': "30"}),
           ),
           DropdownMenuEntry(
             value: 60,
@@ -250,7 +250,8 @@ class SharedLinkEditPage extends HookConsumerWidget {
           ),
           DropdownMenuEntry(
             value: 60 * 6,
-            label: "shared_link_edit_expire_after_option_hours".tr(args: ["6"]),
+            label: "shared_link_edit_expire_after_option_hours"
+                .tr(namedArgs: {'count': "6"}),
           ),
           DropdownMenuEntry(
             value: 60 * 24,
@@ -258,20 +259,23 @@ class SharedLinkEditPage extends HookConsumerWidget {
           ),
           DropdownMenuEntry(
             value: 60 * 24 * 7,
-            label: "shared_link_edit_expire_after_option_days".tr(args: ["7"]),
+            label: "shared_link_edit_expire_after_option_days"
+                .tr(namedArgs: {'count': "7"}),
           ),
           DropdownMenuEntry(
             value: 60 * 24 * 30,
-            label: "shared_link_edit_expire_after_option_days".tr(args: ["30"]),
+            label: "shared_link_edit_expire_after_option_days"
+                .tr(namedArgs: {'count': "30"}),
           ),
           DropdownMenuEntry(
             value: 60 * 24 * 30 * 3,
-            label:
-                "shared_link_edit_expire_after_option_months".tr(args: ["3"]),
+            label: "shared_link_edit_expire_after_option_months"
+                .tr(namedArgs: {'count': "3"}),
           ),
           DropdownMenuEntry(
             value: 60 * 24 * 30 * 12,
-            label: "shared_link_edit_expire_after_option_year".tr(args: ["1"]),
+            label: "shared_link_edit_expire_after_option_year"
+                .tr(namedArgs: {'count': "1"}),
           ),
         ],
       );
diff --git a/mobile/lib/pages/library/trash.page.dart b/mobile/lib/pages/library/trash.page.dart
index f8a1d7605d..c645719974 100644
--- a/mobile/lib/pages/library/trash.page.dart
+++ b/mobile/lib/pages/library/trash.page.dart
@@ -4,18 +4,18 @@ import 'package:flutter/material.dart';
 import 'package:flutter_hooks/flutter_hooks.dart';
 import 'package:fluttertoast/fluttertoast.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/entities/asset.entity.dart';
 import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
 import 'package:immich_mobile/providers/asset.provider.dart';
-import 'package:immich_mobile/providers/timeline.provider.dart';
-import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart';
-import 'package:immich_mobile/widgets/asset_grid/delete_dialog.dart';
-import 'package:immich_mobile/providers/trash.provider.dart';
-import 'package:immich_mobile/entities/asset.entity.dart';
 import 'package:immich_mobile/providers/server_info.provider.dart';
+import 'package:immich_mobile/providers/timeline.provider.dart';
+import 'package:immich_mobile/providers/trash.provider.dart';
+import 'package:immich_mobile/utils/immich_loading_overlay.dart';
+import 'package:immich_mobile/widgets/asset_grid/delete_dialog.dart';
+import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart';
 import 'package:immich_mobile/widgets/common/confirm_dialog.dart';
 import 'package:immich_mobile/widgets/common/immich_toast.dart';
-import 'package:immich_mobile/utils/immich_loading_overlay.dart';
 
 @RoutePage()
 class TrashPage extends HookConsumerWidget {
@@ -77,7 +77,7 @@ class TrashPage extends HookConsumerWidget {
               ImmichToast.show(
                 context: context,
                 msg: 'assets_deleted_permanently'
-                    .tr(args: ["${selection.value.length}"]),
+                    .tr(namedArgs: {'count': "${selection.value.length}"}),
                 gravity: ToastGravity.BOTTOM,
               );
             }
@@ -118,7 +118,7 @@ class TrashPage extends HookConsumerWidget {
             ImmichToast.show(
               context: context,
               msg: 'assets_restored_successfully'
-                  .tr(args: ["${selection.value.length}"]),
+                  .tr(namedArgs: {'count': "${selection.value.length}"}),
               gravity: ToastGravity.BOTTOM,
             );
           }
@@ -135,7 +135,7 @@ class TrashPage extends HookConsumerWidget {
             ? "${selection.value.length}"
             : "trash_page_select_assets_btn".tr();
       }
-      return 'trash_page_title'.tr(args: [count]);
+      return 'trash_page_title'.tr(namedArgs: {'count': count});
     }
 
     AppBar buildAppBar(String count) {
@@ -260,7 +260,7 @@ class TrashPage extends HookConsumerWidget {
                         ),
                         child: const Text(
                           "trash_page_info",
-                        ).tr(args: ["$trashDays"]),
+                        ).tr(namedArgs: {"days": "$trashDays"}),
                       ),
                     ),
                   ),
diff --git a/mobile/lib/pages/share_intent/share_intent.page.dart b/mobile/lib/pages/share_intent/share_intent.page.dart
index 4e19fda373..ff137ce0aa 100644
--- a/mobile/lib/pages/share_intent/share_intent.page.dart
+++ b/mobile/lib/pages/share_intent/share_intent.page.dart
@@ -56,9 +56,7 @@ class ShareIntentPage extends HookConsumerWidget {
         title: Column(
           children: [
             const Text('upload_to_immich').tr(
-              args: [
-                candidates.length.toString(),
-              ],
+              namedArgs: {'count': candidates.length.toString()},
             ),
             Text(
               currentEndpoint,
@@ -176,8 +174,12 @@ class UploadingText extends StatelessWidget {
       return element.status == UploadStatus.complete;
     }).length;
 
-    return const Text("shared_intent_upload_button_progress_text")
-        .tr(args: [uploadedCount.toString(), candidates.length.toString()]);
+    return const Text("shared_intent_upload_button_progress_text").tr(
+      namedArgs: {
+        'current': uploadedCount.toString(),
+        'total': candidates.length.toString(),
+      },
+    );
   }
 }
 
diff --git a/mobile/lib/providers/backup/manual_upload.provider.dart b/mobile/lib/providers/backup/manual_upload.provider.dart
index fc7e4d866c..646a03cebc 100644
--- a/mobile/lib/providers/backup/manual_upload.provider.dart
+++ b/mobile/lib/providers/backup/manual_upload.provider.dart
@@ -6,27 +6,27 @@ import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/widgets.dart';
 import 'package:fluttertoast/fluttertoast.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/entities/asset.entity.dart';
 import 'package:immich_mobile/entities/backup_album.entity.dart';
 import 'package:immich_mobile/models/backup/backup_candidate.model.dart';
-import 'package:immich_mobile/models/backup/success_upload_asset.model.dart';
-import 'package:immich_mobile/repositories/file_media.repository.dart';
-import 'package:immich_mobile/services/background.service.dart';
 import 'package:immich_mobile/models/backup/backup_state.model.dart';
 import 'package:immich_mobile/models/backup/current_upload_asset.model.dart';
 import 'package:immich_mobile/models/backup/error_upload_asset.model.dart';
 import 'package:immich_mobile/models/backup/manual_upload_state.model.dart';
+import 'package:immich_mobile/models/backup/success_upload_asset.model.dart';
+import 'package:immich_mobile/providers/app_life_cycle.provider.dart';
+import 'package:immich_mobile/providers/app_settings.provider.dart';
 import 'package:immich_mobile/providers/backup/backup.provider.dart';
 import 'package:immich_mobile/providers/backup/error_backup_list.provider.dart';
-import 'package:immich_mobile/services/backup.service.dart';
 import 'package:immich_mobile/providers/gallery_permission.provider.dart';
-import 'package:immich_mobile/providers/app_settings.provider.dart';
+import 'package:immich_mobile/repositories/file_media.repository.dart';
 import 'package:immich_mobile/services/app_settings.service.dart';
-import 'package:immich_mobile/entities/asset.entity.dart';
-import 'package:immich_mobile/providers/app_life_cycle.provider.dart';
+import 'package:immich_mobile/services/background.service.dart';
+import 'package:immich_mobile/services/backup.service.dart';
 import 'package:immich_mobile/services/backup_album.service.dart';
 import 'package:immich_mobile/services/local_notification.service.dart';
-import 'package:immich_mobile/widgets/common/immich_toast.dart';
 import 'package:immich_mobile/utils/backup_progress.dart';
+import 'package:immich_mobile/widgets/common/immich_toast.dart';
 import 'package:logging/logging.dart';
 import 'package:permission_handler/permission_handler.dart';
 import 'package:photo_manager/photo_manager.dart' show PMProgressHandler;
@@ -170,7 +170,7 @@ class ManualUploadNotifier extends StateNotifier<ManualUploadState> {
 
     if (state.showDetailedNotification) {
       final title = "backup_background_service_current_upload_notification"
-          .tr(args: [state.currentUploadAsset.fileName]);
+          .tr(namedArgs: {'filename': state.currentUploadAsset.fileName});
       _throttledDetailNotify(title: title, progress: sent, total: total);
     }
   }
@@ -186,7 +186,7 @@ class ManualUploadNotifier extends StateNotifier<ManualUploadState> {
     if (state.showDetailedNotification) {
       _throttledDetailNotify.title =
           "backup_background_service_current_upload_notification"
-              .tr(args: [currentUploadAsset.fileName]);
+              .tr(namedArgs: {'filename': currentUploadAsset.fileName});
       _throttledDetailNotify.progress = 0;
       _throttledDetailNotify.total = 0;
     }
diff --git a/mobile/lib/services/background.service.dart b/mobile/lib/services/background.service.dart
index 2d63e1fc18..84cbaae6ed 100644
--- a/mobile/lib/services/background.service.dart
+++ b/mobile/lib/services/background.service.dart
@@ -562,7 +562,7 @@ class BackgroundService {
   void _onBackupError(ErrorUploadAsset errorAssetInfo) {
     _showErrorNotification(
       title: "backup_background_service_upload_failure_notification"
-          .tr(args: [errorAssetInfo.fileName]),
+          .tr(namedArgs: {'filename': errorAssetInfo.fileName}),
       individualTag: errorAssetInfo.id,
     );
   }
@@ -577,7 +577,7 @@ class BackgroundService {
 
     _throttledDetailNotify.title =
         "backup_background_service_current_upload_notification"
-            .tr(args: [currentUploadAsset.fileName]);
+            .tr(namedArgs: {'filename': currentUploadAsset.fileName});
     _throttledDetailNotify.progress = 0;
     _throttledDetailNotify.total = 0;
   }
diff --git a/mobile/lib/services/memory.service.dart b/mobile/lib/services/memory.service.dart
index 6ae8e1d0bb..efd38f1140 100644
--- a/mobile/lib/services/memory.service.dart
+++ b/mobile/lib/services/memory.service.dart
@@ -42,7 +42,8 @@ class MemoryService {
         if (dbAssets.isNotEmpty) {
           final String title = yearsAgo <= 1
               ? 'memories_year_ago'.tr()
-              : 'memories_years_ago'.tr(args: [yearsAgo.toString()]);
+              : 'memories_years_ago'
+                  .tr(namedArgs: {'years': yearsAgo.toString()});
           memories.add(
             Memory(
               title: title,
diff --git a/mobile/lib/widgets/album/album_thumbnail_card.dart b/mobile/lib/widgets/album/album_thumbnail_card.dart
index f6f834e00d..79944ef15f 100644
--- a/mobile/lib/widgets/album/album_thumbnail_card.dart
+++ b/mobile/lib/widgets/album/album_thumbnail_card.dart
@@ -61,7 +61,8 @@ class AlbumThumbnailCard extends ConsumerWidget {
             if (album.ownerId == ref.read(currentUserProvider)?.id) {
               owner = 'owned'.tr();
             } else if (album.ownerName != null) {
-              owner = 'album_thumbnail_shared_by'.tr(args: [album.ownerName!]);
+              owner = 'album_thumbnail_shared_by'
+                  .tr(namedArgs: {'user': album.ownerName!});
             }
           }
 
@@ -74,10 +75,9 @@ class AlbumThumbnailCard extends ConsumerWidget {
               children: [
                 TextSpan(
                   text: album.assetCount == 1
-                      ? 'album_thumbnail_card_item'
-                          .tr(args: ['${album.assetCount}'])
+                      ? 'album_thumbnail_card_item'.tr()
                       : 'album_thumbnail_card_items'
-                          .tr(args: ['${album.assetCount}']),
+                          .tr(namedArgs: {'count': '${album.assetCount}'}),
                 ),
                 if (owner != null) const TextSpan(text: ' • '),
                 if (owner != null) TextSpan(text: owner),
diff --git a/mobile/lib/widgets/album/album_thumbnail_listtile.dart b/mobile/lib/widgets/album/album_thumbnail_listtile.dart
index 3c1e3a8ee0..17c2a6bd12 100644
--- a/mobile/lib/widgets/album/album_thumbnail_listtile.dart
+++ b/mobile/lib/widgets/album/album_thumbnail_listtile.dart
@@ -2,9 +2,9 @@ import 'package:auto_route/auto_route.dart';
 import 'package:cached_network_image/cached_network_image.dart';
 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/routing/router.dart';
-import 'package:immich_mobile/entities/album.entity.dart';
 import 'package:immich_mobile/services/api.service.dart';
 import 'package:immich_mobile/utils/image_url_builder.dart';
 import 'package:openapi/api.dart';
@@ -96,7 +96,7 @@ class AlbumThumbnailListTile extends StatelessWidget {
                           style: const TextStyle(
                             fontSize: 12,
                           ),
-                        ).tr(args: ['${album.assetCount}']),
+                        ).tr(namedArgs: {'count': '${album.assetCount}'}),
                         if (album.shared)
                           const Text(
                             'album_thumbnail_card_shared',
diff --git a/mobile/lib/widgets/asset_grid/multiselect_grid.dart b/mobile/lib/widgets/asset_grid/multiselect_grid.dart
index 5ec59e3eeb..ceaee581d2 100644
--- a/mobile/lib/widgets/asset_grid/multiselect_grid.dart
+++ b/mobile/lib/widgets/asset_grid/multiselect_grid.dart
@@ -7,24 +7,24 @@ import 'package:flutter/material.dart';
 import 'package:flutter_hooks/flutter_hooks.dart';
 import 'package:fluttertoast/fluttertoast.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:immich_mobile/extensions/collection_extensions.dart';
-import 'package:immich_mobile/providers/album/album.provider.dart';
-import 'package:immich_mobile/services/album.service.dart';
-import 'package:immich_mobile/services/stack.service.dart';
-import 'package:immich_mobile/providers/backup/manual_upload.provider.dart';
-import 'package:immich_mobile/models/asset_selection_state.dart';
-import 'package:immich_mobile/providers/multiselect.provider.dart';
-import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
-import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart';
-import 'package:immich_mobile/widgets/asset_grid/control_bottom_app_bar.dart';
-import 'package:immich_mobile/routing/router.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/models/asset_selection_state.dart';
+import 'package:immich_mobile/providers/album/album.provider.dart';
 import 'package:immich_mobile/providers/asset.provider.dart';
+import 'package:immich_mobile/providers/backup/manual_upload.provider.dart';
+import 'package:immich_mobile/providers/multiselect.provider.dart';
 import 'package:immich_mobile/providers/user.provider.dart';
-import 'package:immich_mobile/widgets/common/immich_toast.dart';
+import 'package:immich_mobile/routing/router.dart';
+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/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';
+import 'package:immich_mobile/widgets/common/immich_toast.dart';
 
 class MultiselectGrid extends HookConsumerWidget {
   const MultiselectGrid({
@@ -190,8 +190,9 @@ class MultiselectGrid extends HookConsumerWidget {
             context: context,
             msg: force
                 ? 'assets_deleted_permanently'
-                    .tr(args: ["${selection.value.length}"])
-                : 'assets_trashed'.tr(args: ["${selection.value.length}"]),
+                    .tr(namedArgs: {'count': "${selection.value.length}"})
+                : 'assets_trashed'
+                    .tr(namedArgs: {'count': "${selection.value.length}"}),
             gravity: ToastGravity.BOTTOM,
           );
           selectionEnabledHook.value = false;
@@ -225,7 +226,7 @@ class MultiselectGrid extends HookConsumerWidget {
           ImmichToast.show(
             context: context,
             msg: 'assets_removed_permanently_from_device'
-                .tr(args: ["$deletedCount"]),
+                .tr(namedArgs: {'count': "$deletedCount"}),
             gravity: ToastGravity.BOTTOM,
           );
 
@@ -254,8 +255,9 @@ class MultiselectGrid extends HookConsumerWidget {
             context: context,
             msg: shouldDeletePermanently
                 ? 'assets_deleted_permanently_from_server'
-                    .tr(args: ["${toDelete.length}"])
-                : 'assets_trashed_from_server'.tr(args: ["${toDelete.length}"]),
+                    .tr(namedArgs: {'count': "${toDelete.length}"})
+                : 'assets_trashed_from_server'
+                    .tr(namedArgs: {'count': "${toDelete.length}"}),
             gravity: ToastGravity.BOTTOM,
           );
         }
diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart b/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart
index 712e939ad5..8d2a38c700 100644
--- a/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart
+++ b/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart
@@ -2,13 +2,13 @@ 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/entities/asset.entity.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
-import 'package:immich_mobile/providers/asset_viewer/asset_people.provider.dart';
 import 'package:immich_mobile/models/search/search_curated_content.model.dart';
+import 'package:immich_mobile/providers/asset_viewer/asset_people.provider.dart';
+import 'package:immich_mobile/routing/router.dart';
 import 'package:immich_mobile/widgets/search/curated_people_row.dart';
 import 'package:immich_mobile/widgets/search/person_name_edit_form.dart';
-import 'package:immich_mobile/routing/router.dart';
-import 'package:immich_mobile/entities/asset.entity.dart';
 
 class PeopleInfo extends ConsumerWidget {
   final Asset asset;
@@ -109,13 +109,13 @@ class PeopleInfo extends ConsumerWidget {
 
     if (ageInMonths <= 11) {
       return "exif_bottom_sheet_person_age_months"
-          .tr(args: [ageInMonths.toString()]);
+          .tr(namedArgs: {'months': ageInMonths.toString()});
     } else if (ageInMonths > 12 && ageInMonths <= 23) {
       return "exif_bottom_sheet_person_age_year_months"
-          .tr(args: [(ageInMonths - 12).toString()]);
+          .tr(namedArgs: {'months': (ageInMonths - 12).toString()});
     } else {
       return "exif_bottom_sheet_person_age_years"
-          .tr(args: [ageInYears.toString()]);
+          .tr(namedArgs: {'years': ageInYears.toString()});
     }
   }
 
diff --git a/mobile/lib/widgets/backup/asset_info_table.dart b/mobile/lib/widgets/backup/asset_info_table.dart
index 222e516cf7..98bcc2b3da 100644
--- a/mobile/lib/widgets/backup/asset_info_table.dart
+++ b/mobile/lib/widgets/backup/asset_info_table.dart
@@ -56,9 +56,12 @@ class BackupAssetInfoTable extends ConsumerWidget {
                       fontSize: 10.0,
                     ),
                   ).tr(
-                    args: isUploadInProgress
-                        ? [asset.fileName, asset.fileType.toLowerCase()]
-                        : ["-", "-"],
+                    namedArgs: isUploadInProgress
+                        ? {
+                            'filename': asset.fileName,
+                            'size': asset.fileType.toLowerCase(),
+                          }
+                        : {'filename': "-", 'size': "-"},
                   ),
                 ),
               ),
@@ -78,9 +81,11 @@ class BackupAssetInfoTable extends ConsumerWidget {
                       fontSize: 10.0,
                     ),
                   ).tr(
-                    args: [
-                      isUploadInProgress ? _getAssetCreationDate(asset) : "-",
-                    ],
+                    namedArgs: {
+                      'date': isUploadInProgress
+                          ? _getAssetCreationDate(asset)
+                          : "-",
+                    },
                   ),
                 ),
               ),
@@ -99,9 +104,7 @@ class BackupAssetInfoTable extends ConsumerWidget {
                       fontSize: 10.0,
                     ),
                   ).tr(
-                    args: [
-                      isUploadInProgress ? asset.id : "-",
-                    ],
+                    namedArgs: {'id': isUploadInProgress ? asset.id : "-"},
                   ),
                 ),
               ),
diff --git a/mobile/lib/widgets/backup/error_chip_text.dart b/mobile/lib/widgets/backup/error_chip_text.dart
index 540e136722..38c527ccfa 100644
--- a/mobile/lib/widgets/backup/error_chip_text.dart
+++ b/mobile/lib/widgets/backup/error_chip_text.dart
@@ -21,8 +21,6 @@ class BackupErrorChipText extends ConsumerWidget {
         fontWeight: FontWeight.bold,
         fontSize: 11,
       ),
-    ).tr(
-      args: [count.toString()],
-    );
+    ).tr(namedArgs: {'count': count.toString()});
   }
 }
diff --git a/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart b/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart
index da863ef142..b2b24bd01c 100644
--- a/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart
+++ b/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart
@@ -5,17 +5,17 @@ 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/models/backup/backup_state.model.dart';
+import 'package:immich_mobile/providers/asset.provider.dart';
+import 'package:immich_mobile/providers/auth.provider.dart';
 import 'package:immich_mobile/providers/backup/backup.provider.dart';
 import 'package:immich_mobile/providers/backup/manual_upload.provider.dart';
-import 'package:immich_mobile/providers/auth.provider.dart';
-import 'package:immich_mobile/routing/router.dart';
-import 'package:immich_mobile/providers/asset.provider.dart';
 import 'package:immich_mobile/providers/user.provider.dart';
 import 'package:immich_mobile/providers/websocket.provider.dart';
+import 'package:immich_mobile/routing/router.dart';
+import 'package:immich_mobile/utils/bytes_units.dart';
 import 'package:immich_mobile/widgets/common/app_bar_dialog/app_bar_profile_info.dart';
 import 'package:immich_mobile/widgets/common/app_bar_dialog/app_bar_server_info.dart';
 import 'package:immich_mobile/widgets/common/confirm_dialog.dart';
-import 'package:immich_mobile/utils/bytes_units.dart';
 import 'package:url_launcher/url_launcher.dart';
 
 class ImmichAppBarDialog extends HookConsumerWidget {
@@ -200,10 +200,10 @@ class ImmichAppBarDialog extends HookConsumerWidget {
                     padding: const EdgeInsets.only(top: 12.0),
                     child:
                         const Text('backup_controller_page_storage_format').tr(
-                      args: [
-                        usedDiskSpace,
-                        totalDiskSpace,
-                      ],
+                      namedArgs: {
+                        'used': usedDiskSpace,
+                        'total': totalDiskSpace,
+                      },
                     ),
                   ),
                 ],
diff --git a/mobile/lib/widgets/map/map_asset_grid.dart b/mobile/lib/widgets/map/map_asset_grid.dart
index a9ddc86df9..4a3bb69cc0 100644
--- a/mobile/lib/widgets/map/map_asset_grid.dart
+++ b/mobile/lib/widgets/map/map_asset_grid.dart
@@ -1,20 +1,21 @@
 import 'dart:math' as math;
+
 import 'package:collection/collection.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_hooks/flutter_hooks.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/entities/asset.entity.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
 import 'package:immich_mobile/extensions/collection_extensions.dart';
-import 'package:immich_mobile/providers/timeline.provider.dart';
-import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
-import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart';
 import 'package:immich_mobile/models/map/map_event.model.dart';
-import 'package:immich_mobile/entities/asset.entity.dart';
 import 'package:immich_mobile/providers/db.provider.dart';
-import 'package:immich_mobile/widgets/common/drag_sheet.dart';
+import 'package:immich_mobile/providers/timeline.provider.dart';
 import 'package:immich_mobile/utils/color_filter_generator.dart';
 import 'package:immich_mobile/utils/throttle.dart';
+import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
+import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart';
+import 'package:immich_mobile/widgets/common/drag_sheet.dart';
 import 'package:logging/logging.dart';
 import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
 
@@ -252,7 +253,8 @@ class _MapSheetDragRegion extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
     final assetsInBoundsText = assetsInBoundCount > 0
-        ? "map_assets_in_bounds".tr(args: [assetsInBoundCount.toString()])
+        ? "map_assets_in_bounds"
+            .tr(namedArgs: {'count': assetsInBoundCount.toString()})
         : "map_no_assets_in_bounds".tr();
 
     return SingleChildScrollView(
diff --git a/mobile/lib/widgets/map/map_settings/map_settings_time_dropdown.dart b/mobile/lib/widgets/map/map_settings/map_settings_time_dropdown.dart
index c613029886..e23716af95 100644
--- a/mobile/lib/widgets/map/map_settings/map_settings_time_dropdown.dart
+++ b/mobile/lib/widgets/map/map_settings/map_settings_time_dropdown.dart
@@ -44,13 +44,13 @@ class MapTimeDropDown extends StatelessWidget {
               DropdownMenuEntry(
                 value: 7,
                 label: "map_settings_date_range_option_days".tr(
-                  args: ["7"],
+                  namedArgs: {'days': "7"},
                 ),
               ),
               DropdownMenuEntry(
                 value: 30,
                 label: "map_settings_date_range_option_days".tr(
-                  args: ["30"],
+                  namedArgs: {'days': "30"},
                 ),
               ),
               DropdownMenuEntry(
@@ -81,7 +81,8 @@ class MapTimeDropDown extends StatelessWidget {
                       ),
                     )
                     .inDays,
-                label: "map_settings_date_range_option_years".tr(args: ["3"]),
+                label: "map_settings_date_range_option_years"
+                    .tr(namedArgs: {'years': "3"}),
               ),
             ],
           ),
diff --git a/mobile/lib/widgets/settings/advanced_settings.dart b/mobile/lib/widgets/settings/advanced_settings.dart
index d65186a191..6a67992712 100644
--- a/mobile/lib/widgets/settings/advanced_settings.dart
+++ b/mobile/lib/widgets/settings/advanced_settings.dart
@@ -85,7 +85,8 @@ class AdvancedSettings extends HookConsumerWidget {
         },
       ),
       SettingsSliderListTile(
-        text: "advanced_settings_log_level_title".tr(args: [logLevel]),
+        text: "advanced_settings_log_level_title"
+            .tr(namedArgs: {'level': logLevel}),
         valueNotifier: levelId,
         maxValue: 8,
         minValue: 1,
diff --git a/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart b/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart
index 4584b7e688..72402c8d55 100644
--- a/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart
+++ b/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart
@@ -3,10 +3,10 @@ import 'package:flutter/material.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/providers/app_settings.provider.dart';
 import 'package:immich_mobile/services/app_settings.service.dart';
+import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart';
 import 'package:immich_mobile/widgets/settings/settings_slider_list_tile.dart';
 import 'package:immich_mobile/widgets/settings/settings_sub_title.dart';
 import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart';
-import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart';
 
 class LayoutSettings extends HookConsumerWidget {
   const LayoutSettings({
@@ -30,7 +30,7 @@ class LayoutSettings extends HookConsumerWidget {
         SettingsSliderListTile(
           valueNotifier: tilesPerRow,
           text: 'theme_setting_asset_list_tiles_per_row_title'
-              .tr(args: ["${tilesPerRow.value}"]),
+              .tr(namedArgs: {'count': "${tilesPerRow.value}"}),
           label: "${tilesPerRow.value}",
           maxValue: 6,
           minValue: 2,
diff --git a/mobile/lib/widgets/settings/backup_settings/background_settings.dart b/mobile/lib/widgets/settings/backup_settings/background_settings.dart
index 4cdeb501c1..d628309050 100644
--- a/mobile/lib/widgets/settings/backup_settings/background_settings.dart
+++ b/mobile/lib/widgets/settings/backup_settings/background_settings.dart
@@ -164,10 +164,14 @@ class _BackgroundSettingsEnabled extends HookConsumerWidget {
         switch (v) { 0 => 5000, 1 => 30000, 2 => 120000, _ => 600000 };
 
     String formatBackupDelaySliderValue(int v) => switch (v) {
-          0 => 'setting_notifications_notify_seconds'.tr(args: const ['5']),
-          1 => 'setting_notifications_notify_seconds'.tr(args: const ['30']),
-          2 => 'setting_notifications_notify_minutes'.tr(args: const ['2']),
-          _ => 'setting_notifications_notify_minutes'.tr(args: const ['10']),
+          0 => 'setting_notifications_notify_seconds'
+              .tr(namedArgs: {'count': '5'}),
+          1 => 'setting_notifications_notify_seconds'
+              .tr(namedArgs: {'count': '30'}),
+          2 => 'setting_notifications_notify_minutes'
+              .tr(namedArgs: {'count': '2'}),
+          _ => 'setting_notifications_notify_minutes'
+              .tr(namedArgs: {'count': '10'}),
         };
 
     final backupTriggerDelay =
@@ -221,7 +225,9 @@ class _BackgroundSettingsEnabled extends HookConsumerWidget {
             SettingsSliderListTile(
               valueNotifier: triggerDelay,
               text: 'backup_controller_page_background_delay'.tr(
-                args: [formatBackupDelaySliderValue(triggerDelay.value)],
+                namedArgs: {
+                  'duration': formatBackupDelaySliderValue(triggerDelay.value),
+                },
               ),
               maxValue: 3.0,
               noDivisons: 3,
diff --git a/mobile/lib/widgets/settings/local_storage_settings.dart b/mobile/lib/widgets/settings/local_storage_settings.dart
index 5b21d9bd4d..06db78cb97 100644
--- a/mobile/lib/widgets/settings/local_storage_settings.dart
+++ b/mobile/lib/widgets/settings/local_storage_settings.dart
@@ -1,9 +1,9 @@
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_hooks/flutter_hooks.dart' show useEffect, useState;
-import 'package:immich_mobile/extensions/build_context_extensions.dart';
-import 'package:immich_mobile/entities/duplicated_asset.entity.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/entities/duplicated_asset.entity.dart';
+import 'package:immich_mobile/extensions/build_context_extensions.dart';
 import 'package:immich_mobile/extensions/theme_extensions.dart';
 import 'package:immich_mobile/providers/db.provider.dart';
 
@@ -35,7 +35,7 @@ class LocalStorageSettings extends HookConsumerWidget {
         style: context.textTheme.bodyLarge?.copyWith(
           fontWeight: FontWeight.w500,
         ),
-      ).tr(args: ["${cacheItemCount.value}"]),
+      ).tr(namedArgs: {'count': "${cacheItemCount.value}"}),
       subtitle: Text(
         "cache_settings_duplicated_assets_subtitle",
         style: context.textTheme.bodyMedium?.copyWith(
diff --git a/mobile/lib/widgets/settings/notification_setting.dart b/mobile/lib/widgets/settings/notification_setting.dart
index 6aef8f29b0..cf6745199e 100644
--- a/mobile/lib/widgets/settings/notification_setting.dart
+++ b/mobile/lib/widgets/settings/notification_setting.dart
@@ -4,11 +4,11 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
 import 'package:immich_mobile/providers/notification_permission.provider.dart';
 import 'package:immich_mobile/services/app_settings.service.dart';
+import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart';
 import 'package:immich_mobile/widgets/settings/settings_button_list_tile.dart';
 import 'package:immich_mobile/widgets/settings/settings_slider_list_tile.dart';
 import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart';
 import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart';
-import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart';
 import 'package:permission_handler/permission_handler.dart';
 
 class NotificationSetting extends HookConsumerWidget {
@@ -90,7 +90,7 @@ class NotificationSetting extends HookConsumerWidget {
         enabled: hasPermission,
         valueNotifier: sliderValue,
         text: 'setting_notifications_notify_failures_grace_period'
-            .tr(args: [formattedValue]),
+            .tr(namedArgs: {'duration': formattedValue}),
         maxValue: 5.0,
         noDivisons: 5,
         label: formattedValue,
@@ -105,13 +105,14 @@ String _formatSliderValue(double v) {
   if (v == 0.0) {
     return 'setting_notifications_notify_immediately'.tr();
   } else if (v == 1.0) {
-    return 'setting_notifications_notify_minutes'.tr(args: const ['30']);
+    return 'setting_notifications_notify_minutes'
+        .tr(namedArgs: {'count': '30'});
   } else if (v == 2.0) {
-    return 'setting_notifications_notify_hours'.tr(args: const ['2']);
+    return 'setting_notifications_notify_hours'.tr(namedArgs: {'count': '2'});
   } else if (v == 3.0) {
-    return 'setting_notifications_notify_hours'.tr(args: const ['8']);
+    return 'setting_notifications_notify_hours'.tr(namedArgs: {'count': '8'});
   } else if (v == 4.0) {
-    return 'setting_notifications_notify_hours'.tr(args: const ['24']);
+    return 'setting_notifications_notify_hours'.tr(namedArgs: {'count': '24'});
   } else {
     return 'setting_notifications_notify_never'.tr();
   }
diff --git a/mobile/lib/widgets/shared_link/shared_link_item.dart b/mobile/lib/widgets/shared_link/shared_link_item.dart
index 5fdffa0537..69e763ea09 100644
--- a/mobile/lib/widgets/shared_link/shared_link_item.dart
+++ b/mobile/lib/widgets/shared_link/shared_link_item.dart
@@ -1,4 +1,5 @@
 import 'dart:math' as math;
+
 import 'package:auto_route/auto_route.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
@@ -6,15 +7,15 @@ import 'package:flutter/services.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/widgets/search/thumbnail_with_info.dart';
 import 'package:immich_mobile/models/shared_link/shared_link.model.dart';
+import 'package:immich_mobile/providers/server_info.provider.dart';
 import 'package:immich_mobile/providers/shared_link.provider.dart';
 import 'package:immich_mobile/routing/router.dart';
-import 'package:immich_mobile/providers/server_info.provider.dart';
-import 'package:immich_mobile/widgets/common/confirm_dialog.dart';
-import 'package:immich_mobile/widgets/common/immich_toast.dart';
 import 'package:immich_mobile/utils/image_url_builder.dart';
 import 'package:immich_mobile/utils/url_helper.dart';
+import 'package:immich_mobile/widgets/common/confirm_dialog.dart';
+import 'package:immich_mobile/widgets/common/immich_toast.dart';
+import 'package:immich_mobile/widgets/search/thumbnail_with_info.dart';
 
 class SharedLinkItem extends ConsumerWidget {
   final SharedLink sharedLink;
@@ -44,17 +45,17 @@ class SharedLinkItem extends ConsumerWidget {
         if (difference.inHours % 24 > 12) {
           dayDifference += 1;
         }
-        expiresText =
-            "shared_link_expires_days".tr(args: [dayDifference.toString()]);
+        expiresText = "shared_link_expires_days"
+            .tr(namedArgs: {'count': dayDifference.toString()});
       } else if (difference.inHours > 0) {
         expiresText = "shared_link_expires_hours"
-            .tr(args: [difference.inHours.toString()]);
+            .tr(namedArgs: {'count': difference.inHours.toString()});
       } else if (difference.inMinutes > 0) {
         expiresText = "shared_link_expires_minutes"
-            .tr(args: [difference.inMinutes.toString()]);
+            .tr(namedArgs: {'count': difference.inMinutes.toString()});
       } else if (difference.inSeconds > 0) {
         expiresText = "shared_link_expires_seconds"
-            .tr(args: [difference.inSeconds.toString()]);
+            .tr(namedArgs: {'count': difference.inSeconds.toString()});
       }
     }
     return Text(