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 fdd7386020.

* 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 16faca46d0.

* 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:
Thien Dang 2025-06-16 22:01:16 +07:00 committed by GitHub
parent 16fcb657b7
commit 3d0c851636
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 133 additions and 60 deletions

View file

@ -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),

View file

@ -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,
),

View file

@ -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,

View file

@ -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)},
);
}

View file

@ -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,