chore: migrate mobile translations accessor (5) (#30672)

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
shenlong 2026-08-11 14:41:13 +05:30 committed by GitHub
parent a8b3b30aba
commit 675408b003
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
41 changed files with 268 additions and 372 deletions

View file

@ -97,6 +97,18 @@ dart_code_metrics:
exclude-paths:
- 'lib/utils/debug_print.dart'
severity: perf
- name: tr
description: 'Use context.t.<key> or StaticTranslations.instance.<key> instead of tr().'
- type: String
entries:
- name: tr
description: 'Use context.t.<key> or StaticTranslations.instance.<key> instead of String.tr().'
- name: plural
description: 'Use the generated plural accessor instead of String.plural().'
- type: Text
entries:
- name: tr
description: 'Use Text(context.t.<key>) instead of Text(<key>).tr().'
# All rules from "recommended" preset
# Show potential errors
# - avoid-cascade-after-if-null

View file

@ -1,5 +1,5 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import '../test_utils/general_helper.dart';
@ -7,45 +7,32 @@ void main() async {
await ImmichTestHelper.initialize();
group("Login input validation test", () {
immichWidgetTest("Test leading/trailing whitespace",
(tester, helper) async {
immichWidgetTest("Test leading/trailing whitespace", (tester, helper) async {
await helper.loginHelper.waitForLoginScreen();
await helper.loginHelper.acknowledgeNewServerVersion();
await helper.loginHelper.enterCredentials(
email: " demo@immich.app",
);
await helper.loginHelper.enterCredentials(email: " demo@immich.app");
await tester.pump(const Duration(milliseconds: 300));
expect(
find.text("login_form_err_leading_whitespace".tr()),
findsOneWidget,
);
expect(find.text(StaticTranslations.instance.login_form_err_leading_whitespace), findsOneWidget);
await helper.loginHelper.enterCredentials(
email: "demo@immich.app ",
);
await helper.loginHelper.enterCredentials(email: "demo@immich.app ");
await tester.pump(const Duration(milliseconds: 300));
expect(
find.text("login_form_err_trailing_whitespace".tr()),
findsOneWidget,
);
expect(find.text(StaticTranslations.instance.login_form_err_trailing_whitespace), findsOneWidget);
});
immichWidgetTest("Test invalid email", (tester, helper) async {
await helper.loginHelper.waitForLoginScreen();
await helper.loginHelper.acknowledgeNewServerVersion();
await helper.loginHelper.enterCredentials(
email: "demo.immich.app",
);
await helper.loginHelper.enterCredentials(email: "demo.immich.app");
await tester.pump(const Duration(milliseconds: 300));
expect(find.text("login_form_err_invalid_email".tr()), findsOneWidget);
expect(find.text(StaticTranslations.instance.login_form_err_invalid_email), findsOneWidget);
});
});
}

View file

@ -1,6 +1,6 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'general_helper.dart';
@ -23,11 +23,7 @@ class ImmichTestLoginHelper {
return true;
}
Future<void> enterCredentials({
String server = "",
String email = "",
String password = "",
}) async {
Future<void> enterCredentials({String server = "", String email = "", String password = ""}) async {
final loginForms = find.byType(TextFormField);
await tester.enterText(loginForms.at(0), email);
@ -44,49 +40,30 @@ class ImmichTestLoginHelper {
}
Future<void> enterCredentialsOf(LoginCredentials credentials) async {
await enterCredentials(
server: credentials.server,
email: credentials.email,
password: credentials.password,
);
await enterCredentials(server: credentials.server, email: credentials.email, password: credentials.password);
}
Future<void> pressLoginButton() async {
await pumpUntilFound(
tester,
find.textContaining("login_form_button_text".tr()),
);
final button = find.textContaining("login_form_button_text".tr());
await pumpUntilFound(tester, find.textContaining(StaticTranslations.instance.login));
final button = find.textContaining(StaticTranslations.instance.login);
await tester.tap(button);
}
Future<void> assertLoginSuccess() async {
await pumpUntilFound(tester, find.text("home_page_building_timeline".tr()));
await pumpUntilFound(tester, find.text(StaticTranslations.instance.home_page_building_timeline));
}
Future<void> assertLoginFailed() async {
await pumpUntilFound(tester, find.text("login_form_failed_login".tr()));
await pumpUntilFound(tester, find.text(StaticTranslations.instance.login_form_failed_login));
}
}
enum LoginCredentials {
testInstance(
"https://flutter-int-test.preview.immich.app",
"demo@immich.app",
"demo",
),
testInstance("https://flutter-int-test.preview.immich.app", "demo@immich.app", "demo"),
testInstanceButWithWrongPassword(
"https://flutter-int-test.preview.immich.app",
"demo@immich.app",
"wrong",
),
testInstanceButWithWrongPassword("https://flutter-int-test.preview.immich.app", "demo@immich.app", "wrong"),
wrongInstanceUrl(
"https://does-not-exist.preview.immich.app",
"demo@immich.app",
"demo",
);
wrongInstanceUrl("https://does-not-exist.preview.immich.app", "demo@immich.app", "demo");
const LoginCredentials(this.server, this.email, this.password);

View file

@ -1,21 +1,40 @@
import 'package:flutter/foundation.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/utils/semver.dart';
class FeatureHighlight {
enum FeatureHighlight {
shareQuality(image: 'assets/feature_message/share_quality.webp'),
slideshow(image: 'assets/feature_message/slideshow.webp'),
recentlyAdded(image: 'assets/feature_message/recently_added.webp'),
ocr(image: 'assets/feature_message/ocr.webp'),
openInImmich(image: 'assets/feature_message/open_in_immich.webp', platform: [.android]),
uploadToAlbum();
/// Asset path of the feature screenshot, or null to show a placeholder.
final String? image;
final String titleKey;
final String bodyKey;
final List<TargetPlatform> platform;
const FeatureHighlight({
this.image,
required this.titleKey,
required this.bodyKey,
this.platform = const [.iOS, .android],
});
const FeatureHighlight({this.image, this.platform = const [.iOS, .android]});
bool get isVisibleOnCurrentPlatform => platform.contains(defaultTargetPlatform);
String title(Translations t) => switch (this) {
FeatureHighlight.shareQuality => t.share_quality_title,
FeatureHighlight.slideshow => t.slideshow_title,
FeatureHighlight.recentlyAdded => t.recently_added_title,
FeatureHighlight.ocr => t.ocr_title,
FeatureHighlight.openInImmich => t.open_in_immich_title,
FeatureHighlight.uploadToAlbum => t.upload_to_album_title,
};
String body(Translations t) => switch (this) {
FeatureHighlight.shareQuality => t.share_quality_body,
FeatureHighlight.slideshow => t.slideshow_body,
FeatureHighlight.recentlyAdded => t.recently_added_body,
FeatureHighlight.ocr => t.ocr_body,
FeatureHighlight.openInImmich => t.open_in_immich_body,
FeatureHighlight.uploadToAlbum => t.upload_to_album_body,
};
}
/// The release this batch of highlights was authored for. Content-defined:
@ -24,31 +43,4 @@ const featureMessageRelease = SemVer(major: 3, minor: 0, patch: 0);
/// Highlights relevant to the current platform.
List<FeatureHighlight> get visibleFeatureMessageHighlights =>
featureMessageHighlights.where((h) => h.isVisibleOnCurrentPlatform).toList();
const List<FeatureHighlight> featureMessageHighlights = [
FeatureHighlight(
image: 'assets/feature_message/share_quality.webp',
titleKey: 'share_quality_title',
bodyKey: 'share_quality_body',
),
FeatureHighlight(
image: 'assets/feature_message/slideshow.webp',
titleKey: 'slideshow_title',
bodyKey: 'slideshow_body',
),
FeatureHighlight(
image: 'assets/feature_message/recently_added.webp',
titleKey: 'recently_added_title',
bodyKey: 'recently_added_body',
),
FeatureHighlight(image: 'assets/feature_message/ocr.webp', titleKey: 'ocr_title', bodyKey: 'ocr_body'),
FeatureHighlight(
image: 'assets/feature_message/open_in_immich.webp',
titleKey: 'open_in_immich_title',
bodyKey: 'open_in_immich_body',
platform: [.android],
),
FeatureHighlight(titleKey: 'upload_to_album_title', bodyKey: 'upload_to_album_body'),
];
FeatureHighlight.values.where((h) => h.isVisibleOnCurrentPlatform).toList();

View file

@ -9,7 +9,7 @@ class FeatureMessageService {
bool shouldShow() {
final seen = _settingsRepository.appConfig.featureMessage.seenRelease;
return featureMessageHighlights.isNotEmpty && featureMessageRelease > seen;
return visibleFeatureMessageHighlights.isNotEmpty && featureMessageRelease > seen;
}
Future<void> markSeen() => _settingsRepository.write(SettingsKey.featureMessageSeenRelease, featureMessageRelease);

View file

@ -1,46 +0,0 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:immich_mobile/utils/debug_print.dart';
import 'package:intl/message_format.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) {
dPrint(() => 'Translation failed for key "$key". Error: $e');
return key;
}
}

View file

@ -427,8 +427,8 @@ class SharedLinkEditPage extends HookConsumerWidget {
return showDialog(
context: context,
builder: (BuildContext context) => ConfirmDialog(
title: "delete_shared_link_dialog_title",
content: "confirm_delete_shared_link",
title: context.t.delete_shared_link_dialog_title,
content: context.t.confirm_delete_shared_link,
onOk: () async {
await ref.read(sharedLinkServiceProvider).deleteSharedLink(existingLink!.id);
ref.invalidate(sharedLinksStateProvider);

View file

@ -127,8 +127,11 @@ class DeleteAction extends AssetActionBuilder {
final assetService = ref.read(assetServiceProvider);
final confirmed = await showDialog<bool>(
context: context,
builder: (_) =>
const ConfirmDialog(title: 'delete_dialog_title', content: 'delete_dialog_alert', ok: 'delete_permanently'),
builder: (_) => ConfirmDialog(
title: context.t.delete_dialog_title,
content: context.t.delete_dialog_alert,
ok: context.t.delete_permanently,
),
);
if (confirmed != true || !context.mounted) {
return null;

View file

@ -5,7 +5,7 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/timeline.model.dart';
import 'package:immich_mobile/domain/services/timeline.service.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart';
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
@ -19,7 +19,7 @@ class CleanupPreviewPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('cleanup_preview_title'.t(context: context, args: {'count': assets.length.toString()})),
title: Text(context.t.cleanup_preview_title(count: assets.length)),
centerTitle: true,
elevation: 0,
scrolledUnderElevation: 0,

View file

@ -4,7 +4,7 @@ import 'package:auto_route/auto_route.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/generated/translations.g.dart';
import 'package:immich_mobile/pages/common/download_panel.dart';
import 'package:immich_mobile/providers/asset_viewer/download.provider.dart';
@ -21,10 +21,7 @@ class DownloadInfoPage extends ConsumerWidget {
}
return Scaffold(
appBar: AppBar(
title: Text("download".t(context: context)),
actions: const [],
),
appBar: AppBar(title: Text(context.t.download), actions: const []),
body: ListView.builder(
physics: const ClampingScrollPhysics(),
shrinkWrap: true,
@ -49,7 +46,7 @@ class DownloadInfoPage extends ConsumerWidget {
},
style: OutlinedButton.styleFrom(side: BorderSide(color: context.colorScheme.primary)),
child: Text(
'clear_all'.t(context: context),
context.t.clear_all,
style: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.primary),
),
),

View file

@ -1,7 +1,7 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart';
import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart';
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
@ -28,10 +28,7 @@ class DriftFavoritePage extends StatelessWidget {
}),
],
child: Timeline(
appBar: MesmerizingSliverAppBar(
title: 'favorites'.t(context: context),
icon: Icons.favorite_outline,
),
appBar: MesmerizingSliverAppBar(title: context.t.favorites, icon: Icons.favorite_outline),
bottomSheet: const FavoriteBottomSheet(),
),
);

View file

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
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/translate_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/pages/common/large_leading_tile.dart';
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
@ -62,10 +62,10 @@ class _PlaceSliverAppBar extends HookWidget {
onTapOutside: (_) => searchFocusNode.unfocus(),
onChanged: (value) => search.value = value,
filled: true,
hintText: 'filter_places'.t(context: context),
hintText: context.t.filter_places,
autofocus: true,
)
: Text('places'.t(context: context)),
: Text(context.t.places),
actions: [
IconButton(
icon: Icon(search.value != null ? Icons.close : Icons.search),

View file

@ -1,7 +1,7 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/widgets.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart';
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
@ -26,7 +26,7 @@ class DriftRecentlyAddedPage extends StatelessWidget {
return timelineService;
}),
],
child: Timeline(appBar: MesmerizingSliverAppBar(title: 'recently_added'.t())),
child: Timeline(appBar: MesmerizingSliverAppBar(title: context.t.recently_added)),
);
}
}

View file

@ -1,7 +1,7 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/widgets.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart';
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
@ -26,7 +26,7 @@ class DriftRecentlyTakenPage extends StatelessWidget {
return timelineService;
}),
],
child: Timeline(appBar: MesmerizingSliverAppBar(title: 'recently_taken'.t())),
child: Timeline(appBar: MesmerizingSliverAppBar(title: context.t.recently_taken)),
);
}
}

View file

@ -11,7 +11,7 @@ import 'package:immich_mobile/domain/models/config/slideshow_config.dart';
import 'package:immich_mobile/domain/services/timeline.service.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/scroll_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/pages/common/settings.page.dart';
import 'package:immich_mobile/presentation/widgets/asset_viewer/video_viewer.widget.dart';
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
@ -411,7 +411,7 @@ class _DriftSlideshowPageState extends ConsumerState<DriftSlideshowPage> with Si
children: [
AppBar(
backgroundColor: context.scaffoldBackgroundColor,
title: Text("slideshow".t(context: context)),
title: Text(context.t.slideshow),
actions: [
IconButton(
onPressed: _paused ? _play : _pause,

View file

@ -1,7 +1,6 @@
import 'dart:async';
import 'package:auto_route/auto_route.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';
@ -9,6 +8,7 @@ import 'package:immich_mobile/domain/models/album/album.model.dart';
import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
import 'package:immich_mobile/providers/infrastructure/remote_album.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
@ -86,7 +86,7 @@ class DriftUserSelectionPage extends HookConsumerWidget {
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'suggestions'.tr(),
context.t.suggestions,
style: const TextStyle(fontSize: 14, color: Colors.grey, fontWeight: FontWeight.bold),
),
),
@ -118,7 +118,7 @@ class DriftUserSelectionPage extends HookConsumerWidget {
return Scaffold(
appBar: AppBar(
title: const Text('invite_to_album').tr(),
title: Text(context.t.invite_to_album),
elevation: 0,
centerTitle: false,
leading: IconButton(
@ -130,7 +130,7 @@ class DriftUserSelectionPage extends HookConsumerWidget {
actions: [
TextButton(
onPressed: sharedUsersList.value.isEmpty ? null : () => addNewUsersHandler(),
child: const Text("add", style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(),
child: Text(context.t.add, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)),
),
],
),

View file

@ -1,7 +1,7 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/widgets.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart';
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
@ -26,7 +26,7 @@ class DriftVideoPage extends StatelessWidget {
return timelineService;
}),
],
child: Timeline(appBar: MesmerizingSliverAppBar(title: 'videos'.t())),
child: Timeline(appBar: MesmerizingSliverAppBar(title: context.t.videos)),
);
}
}

View file

@ -9,6 +9,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/aspect_ratios.dart';
import 'package:immich_mobile/domain/models/asset_edit.model.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/pages/edit/editor.provider.dart';
import 'package:immich_mobile/providers/theme.provider.dart';
import 'package:immich_mobile/theme/theme_data.dart';
@ -63,14 +64,14 @@ class _DriftEditImagePageState extends ConsumerState<DriftEditImagePage> with Ti
return;
}
ImmichToast.show(context: context, msg: 'success'.tr(), toastType: ToastType.success);
ImmichToast.show(context: context, msg: context.t.success, toastType: ToastType.success);
Navigator.of(context).pop();
} catch (e) {
if (!mounted) {
return;
}
ImmichToast.show(context: context, msg: 'error_title'.tr(), toastType: ToastType.error);
ImmichToast.show(context: context, msg: context.t.error_title, toastType: ToastType.error);
} finally {
ref.read(editorStateProvider.notifier).setIsEditing(false);
}
@ -80,17 +81,17 @@ class _DriftEditImagePageState extends ConsumerState<DriftEditImagePage> with Ti
return showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: Text('editor_discard_edits_title'.tr()),
content: Text('editor_discard_edits_prompt'.tr()),
title: Text(context.t.editor_discard_edits_title),
content: Text(context.t.editor_discard_edits_prompt),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
style: ButtonStyle(
foregroundColor: WidgetStateProperty.all(context.themeData.colorScheme.onSurfaceVariant),
),
child: Text('cancel'.tr()),
child: Text(context.t.cancel),
),
TextButton(onPressed: () => Navigator.of(context).pop(true), child: Text('confirm'.tr())),
TextButton(onPressed: () => Navigator.of(context).pop(true), child: Text(context.t.confirm)),
],
),
);
@ -116,7 +117,7 @@ class _DriftEditImagePageState extends ConsumerState<DriftEditImagePage> with Ti
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.black,
title: Text("edit".tr()),
title: Text(context.t.edit),
leading: ImmichCloseButton(onPressed: () => Navigator.of(context).maybePop()),
actions: [_SaveEditsButton(onSave: _saveEditedImage)],
),
@ -336,7 +337,7 @@ class _ResetEditsButton extends ConsumerWidget {
final editorNotifier = ref.watch(editorStateProvider.notifier);
return ImmichTextButton(
labelText: 'reset'.tr(),
labelText: context.t.reset,
onPressed: editorNotifier.resetEdits,
variant: ImmichVariant.ghost,
expanded: false,

View file

@ -1,5 +1,4 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:immich_mobile/domain/models/feature_message.model.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
@ -60,10 +59,10 @@ class _HighlightCard extends StatelessWidget {
),
),
const SizedBox(height: 12),
Text(highlight.titleKey.tr(), style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600)),
Text(highlight.title(context.t), style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600)),
const SizedBox(height: 4),
Text(
highlight.bodyKey.tr(),
highlight.body(context.t),
style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceVariant),
),
],

View file

@ -2,13 +2,13 @@ import 'dart:async';
import 'package:auto_route/auto_route.dart';
import 'package:crop_image/crop_image.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:image_picker/image_picker.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
import 'package:immich_mobile/providers/auth.provider.dart';
import 'package:immich_mobile/providers/backup/backup.provider.dart';
@ -93,7 +93,7 @@ class _ProfilePictureCropPageState extends ConsumerState<ProfilePictureCropPage>
ImmichToast.show(
context: context,
msg: 'profile_picture_set'.tr(),
msg: context.t.profile_picture_set,
gravity: ToastGravity.BOTTOM,
toastType: ToastType.success,
);
@ -102,7 +102,7 @@ class _ProfilePictureCropPageState extends ConsumerState<ProfilePictureCropPage>
} else {
ImmichToast.show(
context: context,
msg: 'errors.unable_to_set_profile_picture'.tr(),
msg: context.t.errors.unable_to_set_profile_picture,
toastType: ToastType.error,
gravity: ToastGravity.BOTTOM,
);
@ -114,7 +114,7 @@ class _ProfilePictureCropPageState extends ConsumerState<ProfilePictureCropPage>
ImmichToast.show(
context: context,
msg: 'errors.unable_to_set_profile_picture'.tr(),
msg: context.t.errors.unable_to_set_profile_picture,
toastType: ToastType.error,
gravity: ToastGravity.BOTTOM,
);
@ -135,7 +135,7 @@ class _ProfilePictureCropPageState extends ConsumerState<ProfilePictureCropPage>
return Scaffold(
appBar: AppBar(
backgroundColor: context.scaffoldBackgroundColor,
title: Text("set_profile_picture".tr()),
title: Text(context.t.set_profile_picture),
leading: _isLoading ? null : const ImmichCloseButton(),
actions: [
if (_isLoading)

View file

@ -12,7 +12,6 @@ import 'package:immich_mobile/domain/models/tag.model.dart';
import 'package:immich_mobile/domain/models/timeline.model.dart';
import 'package:immich_mobile/domain/services/timeline.service.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/models/search/search_filter.model.dart';
import 'package:immich_mobile/presentation/pages/search/paginated_search.provider.dart';
@ -48,9 +47,7 @@ class DriftSearchPage extends HookConsumerWidget {
serverFeatures.smartSearch ? TextSearchType.context : TextSearchType.filename,
);
final searchHintText = useState<String>(
serverFeatures.smartSearch
? 'sunrise_on_the_beach'.t(context: context)
: 'file_name_or_extension'.t(context: context),
serverFeatures.smartSearch ? context.t.sunrise_on_the_beach : context.t.file_name_or_extension,
);
final textSearchController = useTextEditingController();
final filter = useState<SearchFilter>(
@ -142,7 +139,7 @@ class DriftSearchPage extends HookConsumerWidget {
}
void handleApply() {
final label = people.map((e) => e.name != '' ? e.name : 'no_name'.t(context: context)).join(', ');
final label = people.map((e) => e.name != '' ? e.name : context.t.no_name).join(', ');
peopleCurrentFilterWidget.value = label.isNotEmpty ? Text(label, style: context.textTheme.labelLarge) : null;
search(filter.value.copyWith(people: people));
}
@ -154,7 +151,7 @@ class DriftSearchPage extends HookConsumerWidget {
child: FractionallySizedBox(
heightFactor: 0.8,
child: FilterBottomSheetScaffold(
title: 'search_filter_people_title'.t(context: context),
title: context.t.search_filter_people_title,
expanded: true,
onSearch: handleApply,
onClear: handleClear,
@ -191,7 +188,7 @@ class DriftSearchPage extends HookConsumerWidget {
child: FractionallySizedBox(
heightFactor: 0.8,
child: FilterBottomSheetScaffold(
title: 'search_filter_tags_title'.t(context: context),
title: context.t.search_filter_tags_title,
expanded: true,
onSearch: handleApply,
onClear: handleClear,
@ -232,7 +229,7 @@ class DriftSearchPage extends HookConsumerWidget {
isScrollControlled: true,
isDismissible: true,
child: FilterBottomSheetScaffold(
title: 'search_filter_location_title'.t(context: context),
title: context.t.search_filter_location_title,
onSearch: handleApply,
onClear: handleClear,
child: Padding(
@ -277,7 +274,7 @@ class DriftSearchPage extends HookConsumerWidget {
isScrollControlled: true,
isDismissible: true,
child: FilterBottomSheetScaffold(
title: 'search_filter_camera_title'.t(context: context),
title: context.t.search_filter_camera_title,
onSearch: handleApply,
onClear: handleClear,
child: Padding(
@ -333,14 +330,14 @@ class DriftSearchPage extends HookConsumerWidget {
lastDate: lastDate,
currentDate: DateTime.now(),
initialDateRange: dateRange,
helpText: 'search_filter_date_title'.t(context: context),
cancelText: 'cancel'.t(context: context),
confirmText: 'select'.t(context: context),
saveText: 'save'.t(context: context),
errorFormatText: 'invalid_date_format'.t(context: context),
errorInvalidText: 'invalid_date'.t(context: context),
fieldStartHintText: 'start_date'.t(context: context),
fieldEndHintText: 'end_date'.t(context: context),
helpText: context.t.search_filter_date_title,
cancelText: context.t.cancel,
confirmText: context.t.select,
saveText: context.t.save,
errorFormatText: context.t.invalid_date_format,
errorInvalidText: context.t.invalid_date,
fieldStartHintText: context.t.start_date,
fieldEndHintText: context.t.end_date,
initialEntryMode: DatePickerEntryMode.calendar,
keyboardType: TextInputType.text,
);
@ -357,7 +354,7 @@ class DriftSearchPage extends HookConsumerWidget {
showFilterBottomSheet(
context: context,
child: FilterBottomSheetScaffold(
title: "pick_date_range".tr(),
title: context.t.pick_date_range,
expanded: true,
onClear: () => datePicked(null),
child: QuickDatePicker(
@ -392,7 +389,7 @@ class DriftSearchPage extends HookConsumerWidget {
void handleApply() {
mediaTypeCurrentFilterWidget.value = mediaType != AssetType.other
? Text(
mediaType == AssetType.image ? 'image'.t(context: context) : 'video'.t(context: context),
mediaType == AssetType.image ? context.t.image : context.t.video,
style: context.textTheme.labelLarge,
)
: null;
@ -403,7 +400,7 @@ class DriftSearchPage extends HookConsumerWidget {
showFilterBottomSheet(
context: context,
child: FilterBottomSheetScaffold(
title: 'search_filter_media_type_title'.t(context: context),
title: context.t.search_filter_media_type_title,
onSearch: handleApply,
onClear: handleClear,
child: MediaTypePicker(onSelect: handleOnSelected, filter: filter.value.mediaType),
@ -427,10 +424,7 @@ class DriftSearchPage extends HookConsumerWidget {
void handleApply() {
ratingCurrentFilterWidget.value = rating.rating.isSome
? Text(
'rating_count'.t(args: {'count': rating.rating.unwrapOrNull ?? 0}),
style: context.textTheme.labelLarge,
)
? Text(context.t.rating_count(count: rating.rating.unwrapOrNull ?? 0), style: context.textTheme.labelLarge)
: null;
search(filter.value.copyWith(rating: rating));
}
@ -440,7 +434,7 @@ class DriftSearchPage extends HookConsumerWidget {
context: context,
isScrollControlled: true,
child: FilterBottomSheetScaffold(
title: 'rating'.t(context: context),
title: context.t.rating,
onSearch: handleApply,
onClear: handleClear,
child: StarRatingPicker(onSelect: handleOnSelected, filter: filter.value.rating),
@ -472,9 +466,9 @@ class DriftSearchPage extends HookConsumerWidget {
void handleApply() {
final filterText = [
if (display.isNotInAlbum) 'search_filter_display_option_not_in_album'.t(context: context),
if (display.isArchive) 'archive'.t(context: context),
if (display.isFavorite) 'favorite'.t(context: context),
if (display.isNotInAlbum) context.t.search_filter_display_option_not_in_album,
if (display.isArchive) context.t.archive,
if (display.isFavorite) context.t.favorite,
];
displayOptionCurrentFilterWidget.value = filterText.isNotEmpty
? Text(filterText.join(', '), style: context.textTheme.labelLarge)
@ -486,7 +480,7 @@ class DriftSearchPage extends HookConsumerWidget {
showFilterBottomSheet(
context: context,
child: FilterBottomSheetScaffold(
title: 'display_options'.t(context: context),
title: context.t.display_options,
onSearch: handleApply,
onClear: handleClear,
child: DisplayOptionPicker(onSelect: handleOnSelect, filter: filter.value.display),
@ -534,7 +528,7 @@ class DriftSearchPage extends HookConsumerWidget {
}
},
icon: const Icon(Icons.more_vert_rounded),
tooltip: 'show_text_search_menu'.tr(),
tooltip: context.t.show_text_search_menu,
);
},
menuChildren: [
@ -544,7 +538,7 @@ class DriftSearchPage extends HookConsumerWidget {
child: ListTile(
leading: const Icon(Icons.image_search_rounded),
title: Text(
'search_by_context'.t(context: context),
context.t.search_by_context,
style: context.textTheme.bodyLarge?.copyWith(
fontWeight: FontWeight.w500,
color: textSearchType.value == TextSearchType.context ? context.colorScheme.primary : null,
@ -555,7 +549,7 @@ class DriftSearchPage extends HookConsumerWidget {
),
onPressed: () {
textSearchType.value = TextSearchType.context;
searchHintText.value = 'sunrise_on_the_beach'.t(context: context);
searchHintText.value = context.t.sunrise_on_the_beach;
},
),
),
@ -563,7 +557,7 @@ class DriftSearchPage extends HookConsumerWidget {
child: ListTile(
leading: const Icon(Icons.abc_rounded),
title: Text(
'search_filter_filename'.t(context: context),
context.t.search_filter_filename,
style: context.textTheme.bodyLarge?.copyWith(
fontWeight: FontWeight.w500,
color: textSearchType.value == TextSearchType.filename ? context.colorScheme.primary : null,
@ -574,14 +568,14 @@ class DriftSearchPage extends HookConsumerWidget {
),
onPressed: () {
textSearchType.value = TextSearchType.filename;
searchHintText.value = 'file_name_or_extension'.t(context: context);
searchHintText.value = context.t.file_name_or_extension;
},
),
MenuItemButton(
child: ListTile(
leading: const Icon(Icons.text_snippet_outlined),
title: Text(
'search_by_description'.t(context: context),
context.t.search_by_description,
style: context.textTheme.bodyLarge?.copyWith(
fontWeight: FontWeight.w500,
color: textSearchType.value == TextSearchType.description ? context.colorScheme.primary : null,
@ -592,7 +586,7 @@ class DriftSearchPage extends HookConsumerWidget {
),
onPressed: () {
textSearchType.value = TextSearchType.description;
searchHintText.value = 'search_by_description_example'.t(context: context);
searchHintText.value = context.t.search_by_description_example;
},
),
FeatureCheck(
@ -601,7 +595,7 @@ class DriftSearchPage extends HookConsumerWidget {
child: ListTile(
leading: const Icon(Icons.document_scanner_outlined),
title: Text(
'search_by_ocr'.t(context: context),
context.t.search_by_ocr,
style: context.textTheme.bodyLarge?.copyWith(
fontWeight: FontWeight.w500,
color: textSearchType.value == TextSearchType.ocr ? context.colorScheme.primary : null,
@ -612,7 +606,7 @@ class DriftSearchPage extends HookConsumerWidget {
),
onPressed: () {
textSearchType.value = TextSearchType.ocr;
searchHintText.value = 'search_by_ocr_example'.t(context: context);
searchHintText.value = context.t.search_by_ocr_example;
},
),
),
@ -663,52 +657,52 @@ class DriftSearchPage extends HookConsumerWidget {
SearchFilterChip(
icon: Icons.people_alt_outlined,
onTap: showPeoplePicker,
label: 'people'.t(context: context),
label: context.t.people,
currentFilter: peopleCurrentFilterWidget.value,
),
SearchFilterChip(
icon: Icons.location_on_outlined,
onTap: showLocationPicker,
label: 'search_filter_location'.t(context: context),
label: context.t.search_filter_location,
currentFilter: locationCurrentFilterWidget.value,
),
if (userPreferences.valueOrNull?.tagsEnabled ?? false)
SearchFilterChip(
icon: Icons.sell_outlined,
onTap: showTagPicker,
label: 'tags'.t(context: context),
label: context.t.tags,
currentFilter: tagCurrentFilterWidget.value,
),
SearchFilterChip(
icon: Icons.camera_alt_outlined,
onTap: showCameraPicker,
label: 'camera'.t(context: context),
label: context.t.camera,
currentFilter: cameraCurrentFilterWidget.value,
),
SearchFilterChip(
icon: Icons.date_range_outlined,
onTap: showQuickDatePicker,
label: 'search_filter_date'.t(context: context),
label: context.t.search_filter_date,
currentFilter: dateRangeCurrentFilterWidget.value,
),
SearchFilterChip(
key: const Key('media_type_chip'),
icon: Icons.video_collection_outlined,
onTap: showMediaTypePicker,
label: 'search_filter_media_type'.t(context: context),
label: context.t.search_filter_media_type,
currentFilter: mediaTypeCurrentFilterWidget.value,
),
if (userPreferences.valueOrNull?.ratingsEnabled ?? false)
SearchFilterChip(
icon: Icons.star_outline_rounded,
onTap: showStarRatingPicker,
label: 'search_filter_star_rating'.t(context: context),
label: context.t.search_filter_star_rating,
currentFilter: ratingCurrentFilterWidget.value,
),
SearchFilterChip(
icon: Icons.display_settings_outlined,
onTap: showDisplayOptionPicker,
label: 'search_filter_display_options'.t(context: context),
label: context.t.search_filter_display_options,
currentFilter: displayOptionCurrentFilterWidget.value,
),
],
@ -772,7 +766,7 @@ class _SearchResultGrid extends ConsumerWidget {
padding: const EdgeInsets.symmetric(vertical: 32),
child: Center(
child: Text(
'search_no_more_result'.t(context: context),
context.t.search_no_more_result,
style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceVariant),
),
),
@ -837,7 +831,7 @@ class _SearchNoResults extends StatelessWidget {
Icon(Icons.search_off_rounded, size: 72, color: context.colorScheme.onSurfaceVariant),
const SizedBox(height: 24),
Text(
'search_no_result'.t(context: context),
context.t.search_no_result,
textAlign: TextAlign.center,
style: context.textTheme.bodyLarge?.copyWith(color: context.colorScheme.onSurfaceVariant),
),
@ -865,9 +859,7 @@ class _SearchSuggestions extends StatelessWidget {
),
),
const SizedBox(height: 16),
Center(
child: Text('search_page_search_photos_videos'.t(context: context), style: context.textTheme.labelLarge),
),
Center(child: Text(context.t.search_page_search_photos_videos, style: context.textTheme.labelLarge)),
const SizedBox(height: 32),
const Padding(padding: EdgeInsets.symmetric(horizontal: 16), child: _QuickLinkList()),
],
@ -900,7 +892,7 @@ class _QuickLinkList extends StatelessWidget {
physics: const NeverScrollableScrollPhysics(),
children: [
_QuickLink(
title: 'recently_taken'.t(context: context),
title: context.t.recently_taken,
icon: Icons.schedule_outlined,
isTop: true,
onTap: () => context.pushRoute(const DriftRecentlyTakenRoute()),
@ -911,12 +903,12 @@ class _QuickLinkList extends StatelessWidget {
onTap: () => context.pushRoute(const DriftRecentlyAddedRoute()),
),
_QuickLink(
title: 'videos'.t(context: context),
title: context.t.videos,
icon: Icons.play_circle_outline_rounded,
onTap: () => context.pushRoute(const DriftVideoRoute()),
),
_QuickLink(
title: 'favorites'.t(context: context),
title: context.t.favorites,
icon: Icons.favorite_border_rounded,
isBottom: true,
onTap: () => context.pushRoute(const DriftFavoriteRoute()),

View file

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/album/album.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/actions/action.widget.dart';
import 'package:immich_mobile/presentation/actions/archive.action.dart';
import 'package:immich_mobile/presentation/actions/asset_debug.action.dart';
@ -43,28 +43,25 @@ class FavoriteBottomSheet extends ConsumerWidget {
}
if (selectedAssets.length != remoteAssets.length) {
ImmichToast.show(
context: context,
msg: 'add_to_album_bottom_sheet_some_local_assets'.t(context: context),
);
ImmichToast.show(context: context, msg: context.t.add_to_album_bottom_sheet_some_local_assets);
}
// Only report the failure when nothing was added; if some succeeded we show "added".
if (result.added > 0) {
ImmichToast.show(
context: context,
msg: 'add_to_album_bottom_sheet_added'.t(args: {"album": album.name}),
msg: context.t.add_to_album_bottom_sheet_added(album: album.name),
);
} else if (result.failed > 0) {
ImmichToast.show(
context: context,
msg: 'assets_cannot_be_added_to_album_count'.t(context: context, args: {'count': result.failed}),
msg: context.t.assets_cannot_be_added_to_album_count(count: result.failed),
toastType: ToastType.error,
);
} else {
ImmichToast.show(
context: context,
msg: 'add_to_album_bottom_sheet_already_exists'.t(args: {"album": album.name}),
msg: context.t.add_to_album_bottom_sheet_already_exists(album: album.name),
);
}

View file

@ -1,8 +1,8 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/models/album/album.model.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/actions/action.widget.dart';
import 'package:immich_mobile/presentation/actions/archive.action.dart';
import 'package:immich_mobile/presentation/actions/asset_debug.action.dart';
@ -54,14 +54,14 @@ class _GeneralBottomSheetState extends ConsumerState<GeneralBottomSheet> {
}
if (!result.success) {
ImmichToast.show(context: context, msg: 'scaffold_body_error_occurred'.tr(), toastType: ToastType.error);
ImmichToast.show(context: context, msg: context.t.scaffold_body_error_occurred, toastType: ToastType.error);
return;
}
ImmichToast.show(
context: context,
msg: result.count == 0
? 'add_to_album_bottom_sheet_already_exists'.tr(namedArgs: {'album': album.name})
: 'add_to_album_bottom_sheet_added'.tr(namedArgs: {'album': album.name}),
? context.t.add_to_album_bottom_sheet_already_exists(album: album.name)
: context.t.add_to_album_bottom_sheet_added(album: album.name),
);
}

View file

@ -1,8 +1,8 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/models/album/album.model.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/actions/action.widget.dart';
import 'package:immich_mobile/presentation/actions/asset_debug.action.dart';
import 'package:immich_mobile/presentation/actions/download.action.dart';
@ -44,15 +44,15 @@ class _PartnerDetailBottomSheetState extends ConsumerState<PartnerDetailBottomSh
}
if (!result.success) {
ImmichToast.show(context: context, msg: 'scaffold_body_error_occurred'.tr(), toastType: ToastType.error);
ImmichToast.show(context: context, msg: context.t.scaffold_body_error_occurred, toastType: ToastType.error);
return;
}
ImmichToast.show(
context: context,
msg: result.count == 0
? 'add_to_album_bottom_sheet_already_exists'.tr(namedArgs: {'album': album.name})
: 'add_to_album_bottom_sheet_added'.tr(namedArgs: {'album': album.name}),
? context.t.add_to_album_bottom_sheet_already_exists(album: album.name)
: context.t.add_to_album_bottom_sheet_added(album: album.name),
);
}

View file

@ -1,7 +1,6 @@
import 'dart:async';
import 'dart:math' as math;
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:immich_mobile/domain/models/feature_message.model.dart';
@ -263,12 +262,12 @@ class _FeaturePage extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
Text(
highlight.titleKey.tr(),
highlight.title(context.t),
style: context.textTheme.titleLarge?.copyWith(fontWeight: FontWeight.w700, fontSize: 24),
),
const SizedBox(height: 8),
Text(
highlight.bodyKey.tr(),
highlight.body(context.t),
style: context.textTheme.bodyLarge?.copyWith(color: scheme.onSurfaceVariant, height: 1.4),
),
],

View file

@ -1,7 +1,7 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:intl/intl.dart';
sealed class DateFilterInputModel {
DateTimeRange<DateTime> asDateTimeRange();
@ -13,12 +13,9 @@ sealed class DateFilterInputModel {
if (date.end.difference(date.start).inHours < 24) {
return DateFormat.yMMMd().format(date.start.toLocal());
} else {
return 'search_filter_date_interval'.t(
context: context,
args: {
"start": DateFormat.yMMMd().format(date.start.toLocal()),
"end": DateFormat.yMMMd().format(date.end.toLocal()),
},
return context.t.search_filter_date_interval(
start: DateFormat.yMMMd().format(date.start.toLocal()),
end: DateFormat.yMMMd().format(date.end.toLocal()),
);
}
}
@ -38,7 +35,7 @@ class RecentMonthRangeFilter extends DateFilterInputModel {
@override
String asHumanReadable(BuildContext context) {
return 'last_months'.t(context: context, args: {"count": monthDelta.toString()});
return context.t.last_months(count: monthDelta);
}
}
@ -62,7 +59,7 @@ class YearFilter extends DateFilterInputModel {
@override
String asHumanReadable(BuildContext context) {
return 'in_year'.tr(namedArgs: {"year": year.toString()});
return context.t.in_year(year: year);
}
}
@ -121,14 +118,13 @@ class QuickDatePicker extends HookWidget {
return null;
}
Text _monthLabel(BuildContext context, int monthsFromNow) =>
const Text('last_months').t(context: context, args: {"count": monthsFromNow.toString()});
Text _monthLabel(BuildContext context, int monthsFromNow) => Text(context.t.last_months(count: monthsFromNow));
Widget _yearPicker(BuildContext context) {
final size = MediaQuery.of(context).size;
return Row(
children: [
const Text("in_year_selector").tr(),
Text(context.t.in_year_selector),
const SizedBox(width: 15),
Expanded(
child: DropdownMenu(
@ -158,7 +154,7 @@ class QuickDatePicker extends HookWidget {
child: IgnorePointer(
ignoring: true,
child: RadioListTile(
title: const Text('pick_custom_range').tr(),
title: Text(context.t.pick_custom_range),
subtitle: hasPreviousInput ? Text(currentInput!.asHumanReadable(context)) : null,
secondary: hasPreviousInput ? const Icon(Icons.edit) : null,
value: _QuickPickerType.custom,

View file

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/widgets/common/confirm_dialog.dart';
class DismissibleActivity extends StatelessWidget {
@ -23,9 +24,9 @@ class DismissibleActivity extends StatelessWidget {
context: context,
builder: (context) => ConfirmDialog(
onOk: () {},
title: "shared_album_activity_remove_title",
content: "shared_album_activity_remove_content",
ok: "delete",
title: context.t.shared_album_activity_remove_title,
content: context.t.shared_album_activity_remove_content,
ok: context.t.delete,
),
)
: Future.value(false),

View file

@ -122,9 +122,9 @@ class ImmichAppBarDialog extends HookConsumerWidget {
context: context,
builder: (BuildContext ctx) {
return ConfirmDialog(
title: "app_bar_signout_dialog_title",
content: "app_bar_signout_dialog_content",
ok: "yes",
title: context.t.app_bar_signout_dialog_title,
content: context.t.app_bar_signout_dialog_content,
ok: context.t.yes,
onOk: () async {
isLoggingOut.value = true;
await ref.read(authProvider.notifier).logout().whenComplete(() => isLoggingOut.value = false);

View file

@ -1,22 +1,15 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
class ConfirmDialog extends StatelessWidget {
final Function? onOk;
final String title;
final String content;
final String cancel;
final String ok;
final String? cancel;
final String? ok;
const ConfirmDialog({
super.key,
this.onOk,
required this.title,
required this.content,
this.cancel = "cancel",
this.ok = "backup_controller_page_background_battery_info_ok",
});
const ConfirmDialog({super.key, this.onOk, required this.title, required this.content, this.cancel, this.ok});
@override
Widget build(BuildContext context) {
@ -27,22 +20,22 @@ class ConfirmDialog extends StatelessWidget {
return AlertDialog(
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))),
title: Text(title).tr(),
content: Text(content).tr(),
title: Text(title),
content: Text(content),
actions: [
TextButton(
onPressed: () => context.pop(false),
child: Text(
cancel,
cancel ?? context.t.cancel,
style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold),
).tr(),
),
),
TextButton(
onPressed: onOkPressed,
child: Text(
ok,
ok ?? context.t.backup_controller_page_background_battery_info_ok,
style: TextStyle(color: context.colorScheme.error, fontWeight: FontWeight.bold),
).tr(),
),
),
],
);

View file

@ -1,10 +1,10 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
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/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/providers/auth.provider.dart';
import 'package:immich_mobile/providers/websocket.provider.dart';
import 'package:immich_mobile/widgets/common/immich_toast.dart';
@ -29,13 +29,13 @@ class ChangePasswordForm extends HookConsumerWidget {
alignment: WrapAlignment.start,
children: [
Text(
'change_password'.tr(),
context.t.change_password,
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: context.primaryColor),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 24.0),
child: Text(
'change_password_form_description'.tr(namedArgs: {'name': authState.name}),
context.t.change_password_form_description(name: authState.name),
style: TextStyle(fontSize: 14, color: context.colorScheme.onSurface, fontWeight: FontWeight.w600),
),
),
@ -62,7 +62,7 @@ class ChangePasswordForm extends HookConsumerWidget {
if (!isSuccess && context.mounted) {
ImmichToast.show(
context: context,
msg: "login_password_changed_error".tr(),
msg: context.t.login_password_changed_error,
toastType: ToastType.error,
gravity: ToastGravity.TOP,
);
@ -82,7 +82,7 @@ class ChangePasswordForm extends HookConsumerWidget {
AutoRouter.of(context).back();
ImmichToast.show(
context: context,
msg: "login_password_changed_success".tr(),
msg: context.t.login_password_changed_success,
toastType: ToastType.success,
gravity: ToastGravity.TOP,
);
@ -92,7 +92,7 @@ class ChangePasswordForm extends HookConsumerWidget {
TextButton.icon(
icon: const Icon(Icons.arrow_back),
onPressed: () => AutoRouter.of(context).back(),
label: const Text('back').tr(),
label: Text(context.t.back),
),
],
),
@ -116,9 +116,9 @@ class PasswordInput extends StatelessWidget {
obscureText: true,
controller: controller,
decoration: InputDecoration(
labelText: 'change_password_form_new_password'.tr(),
labelText: context.t.change_password_form_new_password,
border: const OutlineInputBorder(),
hintText: 'change_password_form_new_password'.tr(),
hintText: context.t.change_password_form_new_password,
),
);
}
@ -132,7 +132,7 @@ class ConfirmPasswordInput extends StatelessWidget {
String? _validateInput(String? email) {
if (confirmController.value != originalController.value) {
return 'change_password_form_password_mismatch'.tr();
return StaticTranslations.instance.change_password_form_password_mismatch;
}
return null;
}
@ -143,8 +143,8 @@ class ConfirmPasswordInput extends StatelessWidget {
obscureText: true,
controller: confirmController,
decoration: InputDecoration(
labelText: 'change_password_form_confirm_password'.tr(),
hintText: 'change_password_form_reenter_new_password'.tr(),
labelText: context.t.change_password_form_confirm_password,
hintText: context.t.change_password_form_reenter_new_password,
border: const OutlineInputBorder(),
),
validator: _validateInput,
@ -166,7 +166,7 @@ class ChangePasswordButton extends StatelessWidget {
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 25),
),
onPressed: onPressed,
child: Text('change_password'.tr(), style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)),
child: Text(context.t.change_password, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)),
);
}
}

View file

@ -5,7 +5,6 @@ import 'dart:math';
import 'package:auto_route/auto_route.dart';
import 'package:crypto/crypto.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
@ -14,7 +13,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
import 'package:immich_mobile/providers/auth.provider.dart';
import 'package:immich_mobile/providers/background_sync.provider.dart';
@ -43,20 +42,21 @@ class LoginForm extends HookConsumerWidget {
final log = Logger('LoginForm');
String? _validateUrl(String? url) => normalizeAndValidateServerUrl(url) ? null : 'login_form_err_invalid_url'.tr();
String? _validateUrl(String? url) =>
normalizeAndValidateServerUrl(url) ? null : StaticTranslations.instance.login_form_err_invalid_url;
String? _validateEmail(String? email) {
if (email == null || email == '') {
return null;
}
if (email.endsWith(' ')) {
return 'login_form_err_trailing_whitespace'.tr();
return StaticTranslations.instance.login_form_err_trailing_whitespace;
}
if (email.startsWith(' ')) {
return 'login_form_err_leading_whitespace'.tr();
return StaticTranslations.instance.login_form_err_leading_whitespace;
}
if (email.contains(' ') || !email.contains('@')) {
return 'login_form_err_invalid_email'.tr();
return StaticTranslations.instance.login_form_err_invalid_email;
}
return null;
}
@ -96,7 +96,7 @@ class LoginForm extends HookConsumerWidget {
// Guard empty URL
if (serverUrl.isEmpty) {
ImmichToast.show(context: context, msg: "login_form_server_empty".tr(), toastType: ToastType.error);
ImmichToast.show(context: context, msg: context.t.login_form_server_empty, toastType: ToastType.error);
}
try {
@ -127,7 +127,7 @@ class LoginForm extends HookConsumerWidget {
ImmichToast.show(
context: context,
msg: e.message ?? 'login_form_api_exception'.tr(),
msg: e.message ?? context.t.login_form_api_exception,
toastType: ToastType.error,
gravity: ToastGravity.TOP,
);
@ -140,7 +140,7 @@ class LoginForm extends HookConsumerWidget {
ImmichToast.show(
context: context,
msg: 'login_form_handshake_exception'.tr(),
msg: context.t.login_form_handshake_exception,
toastType: ToastType.error,
gravity: ToastGravity.TOP,
);
@ -153,7 +153,7 @@ class LoginForm extends HookConsumerWidget {
ImmichToast.show(
context: context,
msg: 'login_form_server_error'.tr(),
msg: context.t.login_form_server_error,
toastType: ToastType.error,
gravity: ToastGravity.TOP,
);
@ -210,15 +210,15 @@ class LoginForm extends HookConsumerWidget {
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))),
elevation: 5,
title: Text(
'manage_media_access_title',
context.t.manage_media_access_title,
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: context.primaryColor),
).tr(),
),
content: SingleChildScrollView(
child: ListBody(
children: [
const Text('manage_media_access_subtitle', style: TextStyle(fontSize: 14)).tr(),
Text(context.t.manage_media_access_subtitle, style: const TextStyle(fontSize: 14)),
const SizedBox(height: 4),
const Text('manage_media_access_rationale', style: TextStyle(fontSize: 12)).tr(),
Text(context.t.manage_media_access_rationale, style: const TextStyle(fontSize: 12)),
],
),
),
@ -226,7 +226,7 @@ class LoginForm extends HookConsumerWidget {
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(
'cancel'.tr(),
context.t.cancel,
style: TextStyle(fontWeight: FontWeight.w600, color: context.primaryColor),
),
),
@ -236,7 +236,7 @@ class LoginForm extends HookConsumerWidget {
Navigator.of(context).pop();
},
child: Text(
'manage_media_access_settings'.tr(),
context.t.manage_media_access_settings,
style: TextStyle(fontWeight: FontWeight.w600, color: context.primaryColor),
),
),
@ -290,7 +290,7 @@ class LoginForm extends HookConsumerWidget {
ImmichToast.show(
context: context,
msg: "login_form_failed_login".tr(),
msg: context.t.login_form_failed_login,
toastType: ToastType.error,
gravity: ToastGravity.TOP,
);
@ -348,7 +348,7 @@ class LoginForm extends HookConsumerWidget {
ImmichToast.show(
context: context,
msg: "login_form_failed_get_oauth_server_config".tr(),
msg: context.t.login_form_failed_get_oauth_server_config,
toastType: ToastType.error,
gravity: ToastGravity.TOP,
);
@ -404,7 +404,7 @@ class LoginForm extends HookConsumerWidget {
ImmichToast.show(
context: context,
msg: "login_form_failed_get_oauth_server_disable".tr(),
msg: context.t.login_form_failed_get_oauth_server_disable,
toastType: ToastType.info,
gravity: ToastGravity.TOP,
);
@ -451,19 +451,19 @@ class LoginForm extends HookConsumerWidget {
children: [
ImmichForm(
onSubmit: getServerAuthSettings,
submitText: 'next'.t(context: context),
submitText: context.t.next,
submitIcon: Icons.arrow_forward_rounded,
builder: (_, form) => ImmichURLInput(
controller: serverEndpointController,
label: 'login_form_endpoint_url'.t(context: context),
hintText: 'login_form_endpoint_hint'.t(context: context),
label: context.t.login_form_endpoint_url,
hintText: context.t.login_form_endpoint_hint,
validator: _validateUrl,
keyboardAction: .next,
onSubmit: (_) => form.submit(),
),
),
ImmichTextButton(
labelText: 'settings'.t(context: context),
labelText: context.t.settings,
icon: Icons.settings,
variant: ImmichVariant.ghost,
onPressed: () => context.pushRoute(const SettingsRoute()),
@ -488,15 +488,15 @@ class LoginForm extends HookConsumerWidget {
if (isPasswordLoginEnable.value)
ImmichForm(
onSubmit: login,
submitText: 'login'.t(context: context),
submitText: context.t.login,
submitIcon: Icons.login_rounded,
builder: (context, form) => Column(
spacing: ImmichSpacing.md,
children: [
ImmichTextInput(
controller: emailController,
label: 'email'.t(context: context),
hintText: 'login_form_email_hint'.t(context: context),
label: context.t.email,
hintText: context.t.login_form_email_hint,
validator: _validateEmail,
keyboardAction: TextInputAction.next,
keyboardType: TextInputType.emailAddress,
@ -506,8 +506,8 @@ class LoginForm extends HookConsumerWidget {
ImmichPasswordInput(
controller: passwordController,
focusNode: passwordFocusNode,
label: 'password'.t(context: context),
hintText: 'login_form_password_hint'.t(context: context),
label: context.t.password,
hintText: context.t.login_form_password_hint,
keyboardAction: TextInputAction.go,
onSubmit: (_) => form.submit(),
),
@ -526,10 +526,9 @@ class LoginForm extends HookConsumerWidget {
)
: const SizedBox.shrink(),
),
if (!isOauthEnable.value && !isPasswordLoginEnable.value)
Center(child: const Text('login_disabled').tr()),
if (!isOauthEnable.value && !isPasswordLoginEnable.value) Center(child: Text(context.t.login_disabled)),
ImmichTextButton(
labelText: 'back'.t(context: context),
labelText: context.t.back,
icon: Icons.arrow_back,
variant: ImmichVariant.ghost,
onPressed: () => serverEndpoint.value = null,

View file

@ -1,8 +1,8 @@
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/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/providers/auth.provider.dart';
import 'package:immich_mobile/widgets/forms/pin_input.dart';
@ -58,7 +58,7 @@ class PinRegistrationForm extends HookConsumerWidget {
SizedBox(
width: context.width * 0.7,
child: Text(
'setup_pin_code'.tr(),
context.t.setup_pin_code,
style: context.textTheme.labelLarge!.copyWith(fontSize: 24),
textAlign: TextAlign.center,
),
@ -66,7 +66,7 @@ class PinRegistrationForm extends HookConsumerWidget {
SizedBox(
width: context.width * 0.8,
child: Text(
'new_pin_code_subtitle'.tr(),
context.t.new_pin_code_subtitle,
style: context.textTheme.bodyLarge!.copyWith(fontSize: 16),
textAlign: TextAlign.center,
),
@ -74,7 +74,7 @@ class PinRegistrationForm extends HookConsumerWidget {
const SizedBox(height: 32),
PinInput(
controller: newPinCodeController,
label: 'new_pin_code'.tr(),
label: context.t.new_pin_code,
length: 6,
autoFocus: true,
hasError: hasError.value,
@ -87,7 +87,7 @@ class PinRegistrationForm extends HookConsumerWidget {
const SizedBox(height: 32),
PinInput(
controller: confirmPinCodeController,
label: 'confirm_new_pin_code'.tr(),
label: context.t.confirm_new_pin_code,
length: 6,
hasError: hasError.value,
onChanged: (input) {
@ -102,7 +102,7 @@ class PinRegistrationForm extends HookConsumerWidget {
child: Row(
children: [
Expanded(
child: ElevatedButton(onPressed: createNewPinCode, child: Text('create'.tr())),
child: ElevatedButton(onPressed: createNewPinCode, child: Text(context.t.create)),
),
],
),

View file

@ -1,8 +1,8 @@
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/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/providers/auth.provider.dart';
import 'package:immich_mobile/widgets/forms/pin_input.dart';
@ -60,7 +60,7 @@ class PinVerificationForm extends HookConsumerWidget {
SizedBox(
width: context.width * 0.7,
child: Text(
description ?? 'enter_your_pin_code_subtitle'.tr(),
description ?? context.t.enter_your_pin_code_subtitle,
style: context.textTheme.labelLarge!.copyWith(fontSize: 18),
textAlign: TextAlign.center,
),

View file

@ -1,8 +1,8 @@
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/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/models/search/search_filter.model.dart';
import 'package:immich_mobile/providers/search/search_filter.provider.dart';
import 'package:immich_mobile/widgets/search/search_filter/common/dropdown.dart';
@ -37,7 +37,7 @@ class CameraPicker extends HookConsumerWidget {
AsyncData(:final value) => value.map((e) => DropdownMenuEntry(value: e, label: e)).toList(),
_ => [],
},
label: const Text('make').tr(),
label: Text(context.t.make),
controller: makeTextController,
leadingIcon: const Icon(Icons.photo_camera_rounded),
onSelected: (value) {
@ -56,7 +56,7 @@ class CameraPicker extends HookConsumerWidget {
AsyncData(:final value) => value.map((e) => DropdownMenuEntry(value: e, label: e)).toList(),
_ => [],
},
label: const Text('model').tr(),
label: Text(context.t.model),
controller: modelTextController,
leadingIcon: const Icon(Icons.camera),
onSelected: (value) {

View file

@ -1,6 +1,6 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/models/search/search_filter.model.dart';
enum DisplayOption { notInAlbum, favorite, archive }
@ -23,7 +23,7 @@ class DisplayOptionPicker extends HookWidget {
shrinkWrap: true,
children: [
CheckboxListTile(
title: const Text('search_filter_display_option_not_in_album').tr(),
title: Text(context.t.search_filter_display_option_not_in_album),
value: options.value[DisplayOption.notInAlbum],
onChanged: (bool? value) {
options.value = {...options.value, DisplayOption.notInAlbum: value!};
@ -31,7 +31,7 @@ class DisplayOptionPicker extends HookWidget {
},
),
CheckboxListTile(
title: const Text('favorite').tr(),
title: Text(context.t.favorite),
value: options.value[DisplayOption.favorite],
onChanged: (value) {
options.value = {...options.value, DisplayOption.favorite: value!};
@ -39,7 +39,7 @@ class DisplayOptionPicker extends HookWidget {
},
),
CheckboxListTile(
title: const Text('archive').tr(),
title: Text(context.t.archive),
value: options.value[DisplayOption.archive],
onChanged: (value) {
options.value = {...options.value, DisplayOption.archive: value!};

View file

@ -1,6 +1,6 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
class FilterBottomSheetScaffold extends StatelessWidget {
const FilterBottomSheetScaffold({
@ -46,7 +46,7 @@ class FilterBottomSheetScaffold extends StatelessWidget {
onClear();
context.pop();
},
child: const Text('clear').tr(),
child: Text(context.t.clear),
),
if (onSearch != null) const SizedBox(width: 8),
if (onSearch != null)
@ -56,7 +56,7 @@ class FilterBottomSheetScaffold extends StatelessWidget {
onSearch!();
context.pop();
},
child: const Text('search_filter_apply').tr(),
child: Text(context.t.search_filter_apply),
),
],
),

View file

@ -1,7 +1,7 @@
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/generated/translations.g.dart';
import 'package:immich_mobile/models/search/search_filter.model.dart';
import 'package:immich_mobile/providers/search/search_filter.provider.dart';
import 'package:immich_mobile/widgets/search/search_filter/common/dropdown.dart';
@ -61,7 +61,7 @@ class LocationPicker extends HookConsumerWidget {
AsyncData(:final value) => value.map((e) => DropdownMenuEntry(value: e, label: e)).toList(),
_ => [],
},
label: const Text('country').tr(),
label: Text(context.t.country),
controller: countryTextController,
onSelected: (value) {
if (value.toString() == selectedCountry.value) {
@ -80,7 +80,7 @@ class LocationPicker extends HookConsumerWidget {
AsyncData(:final value) => value.map((e) => DropdownMenuEntry(value: e, label: e)).toList(),
_ => [],
},
label: const Text('state').tr(),
label: Text(context.t.state),
controller: stateTextController,
onSelected: (value) {
if (value.toString() == selectedState.value) {
@ -98,7 +98,7 @@ class LocationPicker extends HookConsumerWidget {
AsyncData(:final value) => value.map((e) => DropdownMenuEntry(value: e, label: e)).toList(),
_ => [],
},
label: const Text('city').tr(),
label: Text(context.t.city),
controller: cityTextController,
onSelected: (value) {
selectedCity.value = value.toString();

View file

@ -1,7 +1,7 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/generated/translations.g.dart';
class MediaTypePicker extends HookWidget {
const MediaTypePicker({super.key, required this.onSelect, this.filter});
@ -21,9 +21,9 @@ class MediaTypePicker extends HookWidget {
groupValue: selectedMediaType.value,
child: Column(
children: [
RadioListTile(key: const Key("all"), title: const Text("all").tr(), value: AssetType.other),
RadioListTile(key: const Key("image"), title: const Text("image").tr(), value: AssetType.image),
RadioListTile(key: const Key("video"), title: const Text("video").tr(), value: AssetType.video),
RadioListTile(key: const Key("all"), title: Text(context.t.all), value: AssetType.other),
RadioListTile(key: const Key("image"), title: Text(context.t.image), value: AssetType.image),
RadioListTile(key: const Key("video"), title: Text(context.t.video), value: AssetType.video),
],
),
);

View file

@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/models/search/search_filter.model.dart';
import 'package:immich_mobile/utils/option.dart';
@ -28,7 +28,7 @@ class StarRatingPicker extends HookWidget {
6,
(index) => RadioListTile<int>(
key: Key("star_$index"),
title: Text('rating_count'.t(args: {'count': index})),
title: Text(context.t.rating_count(count: index)),
value: index,
),
),

View file

@ -1,6 +1,6 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
class SearchRowTitle extends StatelessWidget {
const SearchRowTitle({super.key, required this.onViewAllPressed, required this.title});
@ -17,9 +17,9 @@ class SearchRowTitle extends StatelessWidget {
TextButton(
onPressed: onViewAllPressed,
child: Text(
'search_page_view_all_button',
context.t.search_page_view_all_button,
style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor),
).tr(),
),
),
],
);

View file

@ -173,8 +173,8 @@ class SharedLinkItem extends ConsumerWidget {
final confirmed = await showDialog<bool>(
context: context,
builder: (BuildContext context) => ConfirmDialog(
title: "delete_shared_link_dialog_title",
content: "confirm_delete_shared_link",
title: context.t.delete_shared_link_dialog_title,
content: context.t.confirm_delete_shared_link,
onOk: () {},
),
);