chore: migrate mobile translations accessor (1) (#30667)

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

View file

@ -97,6 +97,15 @@ const _kIntParamNames = [
'quantity', 'quantity',
]; ];
const _kParamTypeOverrides = <String, String>{
'advanced_settings_clear_image_cache_success.size': 'String',
'backup_controller_page_storage_format.total': 'String',
'backup_controller_page_storage_format.used': 'String',
'cleanup_found_assets_with_size.size': 'String',
};
final _usedParamTypeOverrides = <String>{};
void main() async { void main() async {
final sourceFile = File('../i18n/en.json'); final sourceFile = File('../i18n/en.json');
if (!await sourceFile.exists()) { if (!await sourceFile.exists()) {
@ -143,6 +152,11 @@ class TranslationParam {
Future<void> _generateTranslations(Map<String, dynamic> translations, File output) async { Future<void> _generateTranslations(Map<String, dynamic> translations, File output) async {
final root = _buildTranslationTree('', translations); final root = _buildTranslationTree('', translations);
final staleOverrides = _kParamTypeOverrides.keys.toSet().difference(_usedParamTypeOverrides);
if (staleOverrides.isNotEmpty) {
throw StateError('_kParamTypeOverrides has entries matching no plain {placeholder}: ${staleOverrides.join(', ')}');
}
final buffer = StringBuffer(''' final buffer = StringBuffer('''
// DO NOT EDIT. This is code generated via generate_keys.dart // DO NOT EDIT. This is code generated via generate_keys.dart
@ -194,21 +208,22 @@ class Translations extends _BaseTranslations {
await output.writeAsString(buffer.toString()); await output.writeAsString(buffer.toString());
} }
TranslationNode _buildTranslationTree(String key, dynamic value) { TranslationNode _buildTranslationTree(String key, dynamic value, [String parentKey = '']) {
final fullKey = parentKey.isEmpty ? key : '$parentKey.$key';
if (value is Map<String, dynamic>) { if (value is Map<String, dynamic>) {
final children = <String, TranslationNode>{}; final children = <String, TranslationNode>{};
for (final entry in value.entries) { for (final entry in value.entries) {
children[entry.key] = _buildTranslationTree(entry.key, entry.value); children[entry.key] = _buildTranslationTree(entry.key, entry.value, fullKey);
} }
return TranslationNode(key: key, children: children); return TranslationNode(key: key, children: children);
} else { } else {
final stringValue = value.toString(); final stringValue = value.toString();
final params = _extractParams(stringValue); final params = _extractParams(stringValue, fullKey);
return TranslationNode(key: key, value: stringValue, params: params); return TranslationNode(key: key, value: stringValue, params: params);
} }
} }
List<TranslationParam> _extractParams(String value) { List<TranslationParam> _extractParams(String value, String fullKey) {
final params = <String, TranslationParam>{}; final params = <String, TranslationParam>{};
final icuRegex = RegExp(r'\{(\w+),\s*(plural|select|number|date|time)([^}]*(?:\{[^}]*\}[^}]*)*)\}'); final icuRegex = RegExp(r'\{(\w+),\s*(plural|select|number|date|time)([^}]*(?:\{[^}]*\}[^}]*)*)\}');
@ -264,8 +279,11 @@ List<TranslationParam> _extractParams(String value) {
continue; continue;
} }
String type; final String type;
if (_kIntParamNames.contains(name.toLowerCase())) { if (_kParamTypeOverrides['$fullKey.$name'] case final override?) {
type = override;
_usedParamTypeOverrides.add('$fullKey.$name');
} else if (_kIntParamNames.contains(name.toLowerCase())) {
type = 'int'; type = 'int';
} else { } else {
type = 'Object'; type = 'Object';

View file

@ -16,7 +16,6 @@ import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/constants/locales.dart'; import 'package:immich_mobile/constants/locales.dart';
import 'package:immich_mobile/domain/services/background_worker.service.dart'; import 'package:immich_mobile/domain/services/background_worker.service.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/generated/codegen_loader.g.dart'; import 'package:immich_mobile/generated/codegen_loader.g.dart';
import 'package:immich_mobile/generated/translations.g.dart'; import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/infrastructure/repositories/network.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/network.repository.dart';
@ -273,10 +272,7 @@ class ImmichAppState extends ConsumerState<ImmichApp> with WidgetsBindingObserve
darkTheme: getThemeData(colorScheme: immichTheme.dark, locale: context.locale), darkTheme: getThemeData(colorScheme: immichTheme.dark, locale: context.locale),
theme: getThemeData(colorScheme: immichTheme.light, locale: context.locale), theme: getThemeData(colorScheme: immichTheme.light, locale: context.locale),
builder: (context, child) => ImmichTranslationProvider( builder: (context, child) => ImmichTranslationProvider(
translations: ImmichTranslations( translations: ImmichTranslations(submit: context.t.submit, password: context.t.password),
submit: "submit".t(context: context),
password: "password".t(context: context),
),
child: ImmichThemeProvider(colorScheme: context.colorScheme, child: child!), child: ImmichThemeProvider(colorScheme: context.colorScheme, child: child!),
), ),
routerConfig: router.config( routerConfig: router.config(

View file

@ -1,6 +1,6 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/models/server_info/server_config.model.dart'; import 'package:immich_mobile/models/server_info/server_config.model.dart';
import 'package:immich_mobile/models/server_info/server_disk_info.model.dart'; import 'package:immich_mobile/models/server_info/server_disk_info.model.dart';
import 'package:immich_mobile/models/server_info/server_features.model.dart'; import 'package:immich_mobile/models/server_info/server_features.model.dart';
@ -16,9 +16,9 @@ enum VersionStatus {
String get message => switch (this) { String get message => switch (this) {
VersionStatus.upToDate => "", VersionStatus.upToDate => "",
VersionStatus.clientOutOfDate => "app_update_available".tr(), VersionStatus.clientOutOfDate => StaticTranslations.instance.app_update_available,
VersionStatus.serverOutOfDate => "server_update_available".tr(), VersionStatus.serverOutOfDate => StaticTranslations.instance.server_update_available,
VersionStatus.error => "unable_to_check_version".tr(), VersionStatus.error => StaticTranslations.instance.unable_to_check_version,
}; };
} }

View file

@ -1,15 +1,16 @@
import 'dart:async'; import 'dart:async';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:immich_mobile/domain/models/log.model.dart'; import 'package:immich_mobile/domain/models/log.model.dart';
import 'package:immich_mobile/domain/services/log.service.dart'; import 'package:immich_mobile/domain/services/log.service.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/services/immich_logger.service.dart'; import 'package:immich_mobile/services/immich_logger.service.dart';
import 'package:intl/intl.dart';
@RoutePage() @RoutePage()
class AppLogPage extends HookWidget { class AppLogPage extends HookWidget {
@ -50,7 +51,7 @@ class AppLogPage extends HookWidget {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text('logs'.tr(), style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16.0)), title: Text(context.t.logs, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16.0)),
scrolledUnderElevation: 1, scrolledUnderElevation: 1,
elevation: 2, elevation: 2,
actions: [ actions: [

View file

@ -1,12 +1,12 @@
import 'dart:async'; import 'dart:async';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:immich_mobile/domain/models/log.model.dart'; import 'package:immich_mobile/domain/models/log.model.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
@RoutePage() @RoutePage()
class AppLogDetailPage extends HookWidget { class AppLogDetailPage extends HookWidget {
@ -44,7 +44,7 @@ class AppLogDetailPage extends HookWidget {
context.scaffoldMessenger.showSnackBar( context.scaffoldMessenger.showSnackBar(
SnackBar( SnackBar(
content: Text( content: Text(
"copied_to_clipboard".tr(), context.t.copied_to_clipboard,
style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor),
), ),
), ),
@ -106,7 +106,7 @@ class AppLogDetailPage extends HookWidget {
} }
return Scaffold( return Scaffold(
appBar: AppBar(title: Text("log_detail_title".tr())), appBar: AppBar(title: Text(context.t.log_detail_title)),
body: SafeArea( body: SafeArea(
child: ListView( child: ListView(
children: [ children: [

View file

@ -1,10 +1,10 @@
import 'dart:async'; import 'dart:async';
import 'package:background_downloader/background_downloader.dart'; import 'package:background_downloader/background_downloader.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/providers/asset_viewer/download.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/download.provider.dart';
class DownloadPanel extends ConsumerWidget { class DownloadPanel extends ConsumerWidget {
@ -67,14 +67,14 @@ class DownloadTaskTile extends StatelessWidget {
final progressPercent = (progress * 100).round(); final progressPercent = (progress * 100).round();
String getStatusText() => switch (status) { String getStatusText() => switch (status) {
TaskStatus.running => 'downloading'.tr(), TaskStatus.running => context.t.downloading,
TaskStatus.complete => 'download_complete'.tr(), TaskStatus.complete => context.t.download_complete,
TaskStatus.failed => 'download_failed'.tr(), TaskStatus.failed => context.t.download_failed,
TaskStatus.canceled => 'download_canceled'.tr(), TaskStatus.canceled => context.t.download_canceled,
TaskStatus.paused => 'download_paused'.tr(), TaskStatus.paused => context.t.download_paused,
TaskStatus.enqueued => 'download_enqueue'.tr(), TaskStatus.enqueued => context.t.download_enqueue,
TaskStatus.notFound => 'download_notfound'.tr(), TaskStatus.notFound => context.t.download_notfound,
TaskStatus.waitingToRetry => 'download_waiting_to_retry'.tr(), TaskStatus.waitingToRetry => context.t.download_waiting_to_retry,
}; };
return SizedBox( return SizedBox(

View file

@ -1,5 +1,4 @@
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
@ -64,7 +63,7 @@ class HeaderSettingsPage extends HookConsumerWidget {
headers.value = headers.value.toList(); headers.value = headers.value.toList();
}, },
icon: const Icon(Icons.add_outlined), icon: const Icon(Icons.add_outlined),
tooltip: 'header_settings_add_header_tip'.tr(), tooltip: context.t.header_settings_add_header_tip,
), ),
], ],
), ),
@ -111,7 +110,7 @@ class HeaderKeyValueSettings extends StatelessWidget {
String? emptyFieldValidator(String? value) { String? emptyFieldValidator(String? value) {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
return 'header_settings_field_validator_msg'.tr(); return StaticTranslations.instance.header_settings_field_validator_msg;
} }
return null; return null;
@ -129,7 +128,7 @@ class HeaderKeyValueSettings extends StatelessWidget {
child: TextFormField( child: TextFormField(
controller: keyController, controller: keyController,
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'header_settings_header_name_input'.tr(), labelText: context.t.header_settings_header_name_input,
border: const OutlineInputBorder(), border: const OutlineInputBorder(),
), ),
autocorrect: false, autocorrect: false,
@ -157,7 +156,7 @@ class HeaderKeyValueSettings extends StatelessWidget {
child: TextFormField( child: TextFormField(
controller: valueController, controller: valueController,
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'header_settings_header_value_input'.tr(), labelText: context.t.header_settings_header_value_input,
border: const OutlineInputBorder(), border: const OutlineInputBorder(),
), ),
autocorrect: false, autocorrect: false,

View file

@ -1,13 +1,13 @@
import 'dart:async'; import 'dart:async';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/models/events.model.dart'; import 'package:immich_mobile/domain/models/events.model.dart';
import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/domain/utils/event_stream.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/pages/search/paginated_search.provider.dart'; import 'package:immich_mobile/presentation/pages/search/paginated_search.provider.dart';
import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
@ -34,24 +34,24 @@ class _TabShellPageState extends ConsumerState<TabShellPage> {
final navigationDestinations = [ final navigationDestinations = [
NavigationDestination( NavigationDestination(
label: 'photos'.tr(), label: context.t.photos,
icon: const Icon(Icons.photo_library_outlined), icon: const Icon(Icons.photo_library_outlined),
selectedIcon: Icon(Icons.photo_library, color: context.primaryColor), selectedIcon: Icon(Icons.photo_library, color: context.primaryColor),
), ),
NavigationDestination( NavigationDestination(
label: 'search'.tr(), label: context.t.search,
icon: const Icon(Icons.search_rounded), icon: const Icon(Icons.search_rounded),
selectedIcon: Icon(Icons.search, color: context.primaryColor), selectedIcon: Icon(Icons.search, color: context.primaryColor),
enabled: !isReadonlyModeEnabled, enabled: !isReadonlyModeEnabled,
), ),
NavigationDestination( NavigationDestination(
label: 'albums'.tr(), label: context.t.albums,
icon: const Icon(Icons.photo_album_outlined), icon: const Icon(Icons.photo_album_outlined),
selectedIcon: Icon(Icons.photo_album_rounded, color: context.primaryColor), selectedIcon: Icon(Icons.photo_album_rounded, color: context.primaryColor),
enabled: !isReadonlyModeEnabled, enabled: !isReadonlyModeEnabled,
), ),
NavigationDestination( NavigationDestination(
label: 'library'.tr(), label: context.t.library$,
icon: const Icon(Icons.space_dashboard_outlined), icon: const Icon(Icons.space_dashboard_outlined),
selectedIcon: Icon(Icons.space_dashboard_rounded, color: context.primaryColor), selectedIcon: Icon(Icons.space_dashboard_rounded, color: context.primaryColor),
enabled: !isReadonlyModeEnabled, enabled: !isReadonlyModeEnabled,

View file

@ -1,10 +1,10 @@
import 'dart:async'; import 'dart:async';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart'; import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart';
import 'package:immich_mobile/pages/common/large_leading_tile.dart'; import 'package:immich_mobile/pages/common/large_leading_tile.dart';
import 'package:immich_mobile/providers/asset_viewer/share_intent_upload.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/share_intent_upload.provider.dart';
@ -58,7 +58,7 @@ class ShareIntentPage extends ConsumerWidget {
appBar: AppBar( appBar: AppBar(
title: Column( title: Column(
children: [ children: [
const Text('upload_to_immich').tr(namedArgs: {'count': candidates.length.toString()}), Text(context.t.upload_to_immich(count: candidates.length)),
Text( Text(
currentEndpoint, currentEndpoint,
style: context.textTheme.labelMedium?.copyWith(color: context.colorScheme.onSurface.withAlpha(200)), style: context.textTheme.labelMedium?.copyWith(color: context.colorScheme.onSurface.withAlpha(200)),
@ -130,7 +130,7 @@ class ShareIntentPage extends ConsumerWidget {
height: 48, height: 48,
child: ElevatedButton( child: ElevatedButton(
onPressed: (isUploading || isUploaded) ? null : upload, onPressed: (isUploading || isUploaded) ? null : upload,
child: (isUploading || isUploaded) ? UploadingText(candidates: candidates) : const Text('upload').tr(), child: (isUploading || isUploaded) ? UploadingText(candidates: candidates) : Text(context.t.upload),
), ),
), ),
), ),
@ -149,9 +149,7 @@ class UploadingText extends StatelessWidget {
return element.status == UploadStatus.complete; return element.status == UploadStatus.complete;
}).length; }).length;
return const Text( return Text(context.t.shared_intent_upload_button_progress_text(current: uploadedCount, total: candidates.length));
"shared_intent_upload_button_progress_text",
).tr(namedArgs: {'current': uploadedCount.toString(), 'total': candidates.length.toString()});
} }
} }
@ -168,7 +166,7 @@ class UploadStatusIcon extends StatelessWidget {
return Icon( return Icon(
Icons.check_circle_outline_rounded, Icons.check_circle_outline_rounded,
color: context.colorScheme.onSurface.withAlpha(100), color: context.colorScheme.onSurface.withAlpha(100),
semanticLabel: 'not_selected'.tr(), semanticLabel: context.t.not_selected,
); );
} }
@ -176,7 +174,7 @@ class UploadStatusIcon extends StatelessWidget {
UploadStatus.enqueued => Icon( UploadStatus.enqueued => Icon(
Icons.check_circle_rounded, Icons.check_circle_rounded,
color: context.primaryColor, color: context.primaryColor,
semanticLabel: 'enqueued'.tr(), semanticLabel: context.t.enqueued,
), ),
UploadStatus.running => Stack( UploadStatus.running => Stack(
alignment: AlignmentDirectional.center, alignment: AlignmentDirectional.center,
@ -191,7 +189,7 @@ class UploadStatusIcon extends StatelessWidget {
backgroundColor: context.colorScheme.surfaceContainerLow, backgroundColor: context.colorScheme.surfaceContainerLow,
strokeWidth: 3, strokeWidth: 3,
value: value, value: value,
semanticsLabel: 'uploading'.tr(), semanticsLabel: context.t.uploading,
), ),
), ),
), ),
@ -201,8 +199,12 @@ class UploadStatusIcon extends StatelessWidget {
), ),
], ],
), ),
UploadStatus.complete => Icon(Icons.check_circle_rounded, color: Colors.green, semanticLabel: 'completed'.tr()), UploadStatus.complete => Icon(
UploadStatus.failed => Icon(Icons.error_rounded, color: Colors.red, semanticLabel: 'failed'.tr()), Icons.check_circle_rounded,
color: Colors.green,
semanticLabel: context.t.completed,
),
UploadStatus.failed => Icon(Icons.error_rounded, color: Colors.red, semanticLabel: context.t.failed),
}; };
return statusIcon; return statusIcon;

View file

@ -1,9 +1,9 @@
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
@ -56,7 +56,7 @@ class LocalMediaSummaryPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar(title: Text('local_media_summary'.tr())), appBar: AppBar(title: Text(context.t.local_media_summary)),
body: Consumer( body: Consumer(
builder: (ctx, ref, __) { builder: (ctx, ref, __) {
final db = ref.watch(driftProvider); final db = ref.watch(driftProvider);
@ -79,7 +79,7 @@ class LocalMediaSummaryPage extends StatelessWidget {
const Divider(), const Divider(),
Padding( Padding(
padding: const EdgeInsets.only(left: 15), padding: const EdgeInsets.only(left: 15),
child: Text("album_summary".tr(), style: ctx.textTheme.titleMedium), child: Text(ctx.t.album_summary, style: ctx.textTheme.titleMedium),
), ),
], ],
), ),
@ -136,7 +136,7 @@ class RemoteMediaSummaryPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar(title: Text('remote_media_summary'.tr())), appBar: AppBar(title: Text(context.t.remote_media_summary)),
body: Consumer( body: Consumer(
builder: (ctx, ref, __) { builder: (ctx, ref, __) {
final db = ref.watch(driftProvider); final db = ref.watch(driftProvider);
@ -159,7 +159,7 @@ class RemoteMediaSummaryPage extends StatelessWidget {
const Divider(), const Divider(),
Padding( Padding(
padding: const EdgeInsets.only(left: 15), padding: const EdgeInsets.only(left: 15),
child: Text("album_summary".tr(), style: ctx.textTheme.titleMedium), child: Text(ctx.t.album_summary, style: ctx.textTheme.titleMedium),
), ),
], ],
), ),

View file

@ -1,12 +1,12 @@
import 'dart:async'; import 'dart:async';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/exif.model.dart'; import 'package:immich_mobile/domain/models/exif.model.dart';
import 'package:immich_mobile/extensions/platform_extensions.dart'; import 'package:immich_mobile/extensions/platform_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
@RoutePage() @RoutePage()
@ -18,7 +18,7 @@ class AssetTroubleshootPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar(title: Text('asset_troubleshoot'.tr())), appBar: AppBar(title: Text(context.t.asset_troubleshoot)),
body: SingleChildScrollView( body: SingleChildScrollView(
child: Padding( child: Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
@ -42,7 +42,7 @@ class _AssetDetailsView extends StatelessWidget {
_AssetPropertiesSection(asset: asset), _AssetPropertiesSection(asset: asset),
const SizedBox(height: 16), const SizedBox(height: 16),
Text( Text(
'matching_assets'.tr(), context.t.matching_assets,
style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold), style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
), ),
if (asset.checksum != null) ...[ if (asset.checksum != null) ...[
@ -52,12 +52,12 @@ class _AssetDetailsView extends StatelessWidget {
] else ...[ ] else ...[
_PropertySectionCard( _PropertySectionCard(
title: 'Local Assets', title: 'Local Assets',
properties: [_PropertyItem(label: 'Status', value: 'no_checksum_local'.tr())], properties: [_PropertyItem(label: 'Status', value: context.t.no_checksum_local)],
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
_PropertySectionCard( _PropertySectionCard(
title: 'Remote Assets', title: 'Remote Assets',
properties: [_PropertyItem(label: 'Status', value: 'no_checksum_remote'.tr())], properties: [_PropertyItem(label: 'Status', value: context.t.no_checksum_remote)],
), ),
], ],
], ],
@ -243,7 +243,7 @@ class _LocalAssetsSection extends ConsumerWidget {
if (localAssets.isEmpty) { if (localAssets.isEmpty) {
return _PropertySectionCard( return _PropertySectionCard(
title: 'Local Assets', title: 'Local Assets',
properties: [_PropertyItem(label: 'Status', value: 'no_local_assets_found'.tr())], properties: [_PropertyItem(label: 'Status', value: context.t.no_local_assets_found)],
); );
} }
@ -302,7 +302,7 @@ class _RemoteAssetSection extends ConsumerWidget {
if (remoteAsset == null) { if (remoteAsset == null) {
return _PropertySectionCard( return _PropertySectionCard(
title: 'Remote Assets', title: 'Remote Assets',
properties: [_PropertyItem(label: 'Status', value: 'no_remote_assets_found'.tr())], properties: [_PropertyItem(label: 'Status', value: context.t.no_remote_assets_found)],
); );
} }
@ -356,7 +356,7 @@ class _PropertyItem extends StatelessWidget {
), ),
Expanded( Expanded(
child: Text( child: Text(
value ?? 'not_available'.tr(), value ?? context.t.not_available,
style: TextStyle(color: Theme.of(context).colorScheme.secondary), style: TextStyle(color: Theme.of(context).colorScheme.secondary),
), ),
), ),

View file

@ -1,11 +1,11 @@
import 'dart:async'; import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/models/auth/biometric_status.model.dart'; import 'package:immich_mobile/models/auth/biometric_status.model.dart';
import 'package:immich_mobile/services/local_auth.service.dart'; import 'package:immich_mobile/services/local_auth.service.dart';
import 'package:immich_mobile/services/secure_storage.service.dart'; import 'package:immich_mobile/services/secure_storage.service.dart';
@ -43,6 +43,7 @@ class LocalAuthNotifier extends StateNotifier<BiometricStatus> {
} }
Future<bool> authenticate(BuildContext context, String? message) async { Future<bool> authenticate(BuildContext context, String? message) async {
final t = StaticTranslations.instance;
String errorMessage = ""; String errorMessage = "";
try { try {
@ -51,20 +52,20 @@ class LocalAuthNotifier extends StateNotifier<BiometricStatus> {
switch (error.code) { switch (error.code) {
case "NotEnrolled": case "NotEnrolled":
_log.warning("User is not enrolled in biometrics"); _log.warning("User is not enrolled in biometrics");
errorMessage = "biometric_no_options".tr(); errorMessage = t.biometric_no_options;
case "NotAvailable": case "NotAvailable":
_log.warning("Biometric authentication is not available"); _log.warning("Biometric authentication is not available");
errorMessage = "biometric_not_available".tr(); errorMessage = t.biometric_not_available;
case "LockedOut": case "LockedOut":
_log.warning("User is locked out of biometric authentication"); _log.warning("User is locked out of biometric authentication");
errorMessage = "biometric_locked_out".tr(); errorMessage = t.biometric_locked_out;
default: default:
_log.warning("Failed to authenticate with unknown reason"); _log.warning("Failed to authenticate with unknown reason");
errorMessage = 'failed_to_authenticate'.tr(); errorMessage = t.failed_to_authenticate;
} }
} catch (error) { } catch (error) {
_log.warning("Error during authentication: $error"); _log.warning("Error during authentication: $error");
errorMessage = 'failed_to_authenticate'.tr(); errorMessage = t.failed_to_authenticate;
} finally { } finally {
if (errorMessage.isNotEmpty) { if (errorMessage.isNotEmpty) {
context.showSnackBar( context.showSnackBar(

View file

@ -1,7 +1,7 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/generated/translations.g.dart';
part 'sync_status.provider.freezed.dart'; part 'sync_status.provider.freezed.dart';
@ -12,11 +12,12 @@ enum SyncStatus {
error; error;
String localized() { String localized() {
final t = StaticTranslations.instance;
return switch (this) { return switch (this) {
SyncStatus.idle => "idle".tr(), SyncStatus.idle => t.idle,
SyncStatus.syncing => "running".tr(), SyncStatus.syncing => t.running,
SyncStatus.success => "success".tr(), SyncStatus.success => t.success,
SyncStatus.error => "error".tr(), SyncStatus.error => t.error,
}; };
} }
} }

View file

@ -1,5 +1,5 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/models/auth/biometric_status.model.dart'; import 'package:immich_mobile/models/auth/biometric_status.model.dart';
import 'package:local_auth/local_auth.dart'; import 'package:local_auth/local_auth.dart';
@ -19,6 +19,6 @@ class BiometricRepository {
} }
Future<bool> authenticate(String? message) async { Future<bool> authenticate(String? message) async {
return _localAuth.authenticate(localizedReason: message ?? 'please_auth_to_access'.tr()); return _localAuth.authenticate(localizedReason: message ?? StaticTranslations.instance.please_auth_to_access);
} }
} }

View file

@ -10,7 +10,7 @@ import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/extensions/network_capability_extensions.dart'; import 'package:immich_mobile/extensions/network_capability_extensions.dart';
import 'package:immich_mobile/extensions/platform_extensions.dart'; import 'package:immich_mobile/extensions/platform_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/infrastructure/repositories/backup.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/backup.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
@ -241,16 +241,17 @@ class ForegroundUploadService {
Completer<void>? cancelToken, { Completer<void>? cancelToken, {
required UploadCallbacks callbacks, required UploadCallbacks callbacks,
}) async { }) async {
final t = StaticTranslations.instance;
final assetNotFoundOnDevice = CurrentPlatform.isAndroid
? t.asset_not_found_on_device_android
: t.asset_not_found_on_device_ios;
File? file; File? file;
File? livePhotoFile; File? livePhotoFile;
try { try {
final entity = await _storageRepository.getAssetEntityForAsset(asset); final entity = await _storageRepository.getAssetEntityForAsset(asset);
if (entity == null) { if (entity == null) {
callbacks.onError?.call( callbacks.onError?.call(asset.localId!, assetNotFoundOnDevice);
asset.localId!,
CurrentPlatform.isAndroid ? "asset_not_found_on_device_android".t() : "asset_not_found_on_device_ios".t(),
);
return; return;
} }
@ -284,10 +285,7 @@ class ForegroundUploadService {
file = await _storageRepository.getFileForAsset(asset.id); file = await _storageRepository.getFileForAsset(asset.id);
if (file == null) { if (file == null) {
_logger.warning("Failed to get file ${asset.id} - ${asset.name}"); _logger.warning("Failed to get file ${asset.id} - ${asset.name}");
callbacks.onError?.call( callbacks.onError?.call(asset.localId!, assetNotFoundOnDevice);
asset.localId!,
CurrentPlatform.isAndroid ? "asset_not_found_on_device_android".t() : "asset_not_found_on_device_ios".t(),
);
return; return;
} }
@ -296,17 +294,14 @@ class ForegroundUploadService {
livePhotoFile = await _storageRepository.getMotionFileForAsset(asset); livePhotoFile = await _storageRepository.getMotionFileForAsset(asset);
if (livePhotoFile == null) { if (livePhotoFile == null) {
_logger.warning("Failed to obtain motion part of the livePhoto - ${asset.name}"); _logger.warning("Failed to obtain motion part of the livePhoto - ${asset.name}");
callbacks.onError?.call( callbacks.onError?.call(asset.localId!, assetNotFoundOnDevice);
asset.localId!,
CurrentPlatform.isAndroid ? "asset_not_found_on_device_android".t() : "asset_not_found_on_device_ios".t(),
);
} }
} }
} }
if (file == null) { if (file == null) {
_logger.warning("Failed to obtain file from iCloud for asset ${asset.id} - ${asset.name}"); _logger.warning("Failed to obtain file from iCloud for asset ${asset.id} - ${asset.name}");
callbacks.onError?.call(asset.localId!, "asset_not_found_on_icloud".t()); callbacks.onError?.call(asset.localId!, t.asset_not_found_on_icloud);
return; return;
} }

View file

@ -1,5 +1,4 @@
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/models/album/album.model.dart'; import 'package:immich_mobile/domain/models/album/album.model.dart';
@ -7,6 +6,7 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/events.model.dart'; import 'package:immich_mobile/domain/models/events.model.dart';
import 'package:immich_mobile/domain/services/timeline.service.dart'; import 'package:immich_mobile/domain/services/timeline.service.dart';
import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/domain/utils/event_stream.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/actions/action.widget.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/archive.action.dart';
import 'package:immich_mobile/presentation/actions/asset_debug.action.dart'; import 'package:immich_mobile/presentation/actions/asset_debug.action.dart';
@ -203,13 +203,13 @@ enum ActionButtonType {
), ),
ActionButtonType.setProfilePicture => ActionMenuItem(action: SetProfilePictureAction(asset: context.asset)), ActionButtonType.setProfilePicture => ActionMenuItem(action: SetProfilePictureAction(asset: context.asset)),
ActionButtonType.openInfo => BaseActionButton( ActionButtonType.openInfo => BaseActionButton(
label: 'info'.tr(), label: StaticTranslations.instance.info,
iconData: Icons.info_outline, iconData: Icons.info_outline,
menuItem: true, menuItem: true,
onPressed: () => EventStream.shared.emit(const ViewerShowDetailsEvent()), onPressed: () => EventStream.shared.emit(const ViewerShowDetailsEvent()),
), ),
ActionButtonType.viewInTimeline => BaseActionButton( ActionButtonType.viewInTimeline => BaseActionButton(
label: 'view_in_timeline'.tr(), label: StaticTranslations.instance.view_in_timeline,
iconData: Icons.image_search, iconData: Icons.image_search,
iconOnly: iconOnly, iconOnly: iconOnly,
menuItem: menuItem, menuItem: menuItem,

View file

@ -2,7 +2,7 @@ import 'package:background_downloader/background_downloader.dart';
import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/services/log.service.dart'; import 'package:immich_mobile/domain/services/log.service.dart';
import 'package:immich_mobile/domain/services/store.service.dart'; import 'package:immich_mobile/domain/services/store.service.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/log.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/log.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart';
@ -15,31 +15,33 @@ import 'package:photo_manager/photo_manager.dart';
import 'package:sqlite3/common.dart'; import 'package:sqlite3/common.dart';
void configureFileDownloaderNotifications() { void configureFileDownloaderNotifications() {
final t = StaticTranslations.instance;
FileDownloader().configureNotificationForGroup( FileDownloader().configureNotificationForGroup(
kDownloadGroupImage, kDownloadGroupImage,
running: TaskNotification('downloading_media'.t(), '${'file_name_text'.t()}: {filename}'), running: TaskNotification(t.downloading_media, '${t.file_name_text}: {filename}'),
complete: TaskNotification('download_finished'.t(), '${'file_name_text'.t()}: {filename}'), complete: TaskNotification(t.download_finished, '${t.file_name_text}: {filename}'),
progressBar: true, progressBar: true,
); );
FileDownloader().configureNotificationForGroup( FileDownloader().configureNotificationForGroup(
kDownloadGroupVideo, kDownloadGroupVideo,
running: TaskNotification('downloading_media'.t(), '${'file_name_text'.t()}: {filename}'), running: TaskNotification(t.downloading_media, '${t.file_name_text}: {filename}'),
complete: TaskNotification('download_finished'.t(), '${'file_name_text'.t()}: {filename}'), complete: TaskNotification(t.download_finished, '${t.file_name_text}: {filename}'),
progressBar: true, progressBar: true,
); );
FileDownloader().configureNotificationForGroup( FileDownloader().configureNotificationForGroup(
kManualUploadGroup, kManualUploadGroup,
running: TaskNotification('uploading_media'.t(), 'backup_background_service_in_progress_notification'.t()), running: TaskNotification(t.uploading_media, t.backup_background_service_in_progress_notification),
complete: TaskNotification('upload_finished'.t(), 'backup_background_service_complete_notification'.t()), complete: TaskNotification(t.upload_finished, t.backup_background_service_complete_notification),
groupNotificationId: kManualUploadGroup, groupNotificationId: kManualUploadGroup,
); );
FileDownloader().configureNotificationForGroup( FileDownloader().configureNotificationForGroup(
kBackupGroup, kBackupGroup,
running: TaskNotification('uploading_media'.t(), 'backup_background_service_in_progress_notification'.t()), running: TaskNotification(t.uploading_media, t.backup_background_service_in_progress_notification),
complete: TaskNotification('upload_finished'.t(), 'backup_background_service_complete_notification'.t()), complete: TaskNotification(t.upload_finished, t.backup_background_service_complete_notification),
groupNotificationId: kBackupGroup, groupNotificationId: kBackupGroup,
); );
} }

View file

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:immich_mobile/domain/models/person.model.dart'; import 'package:immich_mobile/domain/models/person.model.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/widgets/people/person_edit_birthday_modal.widget.dart'; import 'package:immich_mobile/presentation/widgets/people/person_edit_birthday_modal.widget.dart';
import 'package:immich_mobile/presentation/widgets/people/person_edit_name_modal.widget.dart'; import 'package:immich_mobile/presentation/widgets/people/person_edit_name_modal.widget.dart';
@ -8,12 +8,13 @@ String formatAge(DateTime birthDate, DateTime referenceDate) {
final int ageInYears = _calculateAge(birthDate, referenceDate); final int ageInYears = _calculateAge(birthDate, referenceDate);
final int ageInMonths = _calculateAgeInMonths(birthDate, referenceDate); final int ageInMonths = _calculateAgeInMonths(birthDate, referenceDate);
final t = StaticTranslations.instance;
if (ageInMonths <= 11) { if (ageInMonths <= 11) {
return "person_age_months".t(args: {'months': ageInMonths.toString()}); return t.person_age_months(months: ageInMonths);
} else if (ageInMonths > 12 && ageInMonths <= 23) { } else if (ageInMonths > 12 && ageInMonths <= 23) {
return "person_age_year_months".t(args: {'months': (ageInMonths - 12).toString()}); return t.person_age_year_months(months: ageInMonths - 12);
} else { } else {
return "person_age_years".t(args: {'years': ageInYears.toString()}); return t.person_age_years(years: ageInYears);
} }
} }

View file

@ -1,11 +1,11 @@
import 'dart:async'; import 'dart:async';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart' hide Store; import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/models/server_info/server_disk_info.model.dart'; import 'package:immich_mobile/models/server_info/server_disk_info.model.dart';
import 'package:immich_mobile/pages/common/settings.page.dart'; import 'package:immich_mobile/pages/common/settings.page.dart';
import 'package:immich_mobile/providers/auth.provider.dart'; import 'package:immich_mobile/providers/auth.provider.dart';
@ -78,20 +78,24 @@ class ImmichAppBarDialog extends HookConsumerWidget {
title: Text( title: Text(
text, text,
style: theme.textTheme.labelLarge?.copyWith(color: theme.textTheme.labelLarge?.color?.withAlpha(250)), style: theme.textTheme.labelLarge?.copyWith(color: theme.textTheme.labelLarge?.color?.withAlpha(250)),
).tr(), ),
onTap: onTap, onTap: onTap,
trailing: trailing, trailing: trailing,
); );
} }
ListTile buildSettingButton() { ListTile buildSettingButton() {
return buildActionButton(Icons.settings_outlined, "settings", () => context.pushRoute(const SettingsRoute())); return buildActionButton(
Icons.settings_outlined,
context.t.settings,
() => context.pushRoute(const SettingsRoute()),
);
} }
ListTile buildFreeUpSpaceButton() { ListTile buildFreeUpSpaceButton() {
return buildActionButton( return buildActionButton(
Icons.cleaning_services_outlined, Icons.cleaning_services_outlined,
"free_up_space", context.t.free_up_space,
() => context.pushRoute(SettingsSubRoute(section: SettingSection.freeUpSpace)), () => context.pushRoute(SettingsSubRoute(section: SettingSection.freeUpSpace)),
); );
} }
@ -99,7 +103,7 @@ class ImmichAppBarDialog extends HookConsumerWidget {
ListTile buildAppLogButton() { ListTile buildAppLogButton() {
return buildActionButton( return buildActionButton(
Icons.assignment_outlined, Icons.assignment_outlined,
"profile_drawer_app_logs", context.t.profile_drawer_app_logs,
() => context.pushRoute(const AppLogRoute()), () => context.pushRoute(const AppLogRoute()),
); );
} }
@ -107,7 +111,7 @@ class ImmichAppBarDialog extends HookConsumerWidget {
ListTile buildSignOutButton() { ListTile buildSignOutButton() {
return buildActionButton( return buildActionButton(
Icons.logout_rounded, Icons.logout_rounded,
"sign_out", context.t.sign_out,
() async { () async {
if (isLoggingOut.value) { if (isLoggingOut.value) {
return; return;
@ -163,16 +167,16 @@ class ImmichAppBarDialog extends HookConsumerWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
spacing: 12, spacing: 12,
children: [ children: [
Text("backup_controller_page_server_storage".tr(), style: context.textTheme.labelLarge), Text(context.t.backup_controller_page_server_storage, style: context.textTheme.labelLarge),
LinearProgressIndicator( LinearProgressIndicator(
minHeight: 10.0, minHeight: 10.0,
value: percentage, value: percentage,
borderRadius: const BorderRadius.all(Radius.circular(10.0)), borderRadius: const BorderRadius.all(Radius.circular(10.0)),
), ),
Text( Text(
'backup_controller_page_storage_format', context.t.backup_controller_page_storage_format(used: usedDiskSpace, total: totalDiskSpace),
style: context.textTheme.bodySmall, style: context.textTheme.bodySmall,
).tr(namedArgs: {'used': usedDiskSpace, 'total': totalDiskSpace}), ),
], ],
), ),
); );
@ -189,7 +193,7 @@ class ImmichAppBarDialog extends HookConsumerWidget {
ContextHelper(context).pop(); ContextHelper(context).pop();
unawaited(launchUrl(Uri.parse('https://docs.immich.app'), mode: LaunchMode.externalApplication)); unawaited(launchUrl(Uri.parse('https://docs.immich.app'), mode: LaunchMode.externalApplication));
}, },
child: Text("documentation", style: context.textTheme.bodySmall).tr(), child: Text(context.t.documentation, style: context.textTheme.bodySmall),
), ),
const SizedBox(width: 20, child: Text("", textAlign: TextAlign.center)), const SizedBox(width: 20, child: Text("", textAlign: TextAlign.center)),
InkWell( InkWell(
@ -199,7 +203,7 @@ class ImmichAppBarDialog extends HookConsumerWidget {
launchUrl(Uri.parse('https://github.com/immich-app/immich'), mode: LaunchMode.externalApplication), launchUrl(Uri.parse('https://github.com/immich-app/immich'), mode: LaunchMode.externalApplication),
); );
}, },
child: Text("profile_drawer_github", style: context.textTheme.bodySmall).tr(), child: Text(context.t.profile_drawer_github, style: context.textTheme.bodySmall),
), ),
const SizedBox(width: 20, child: Text("", textAlign: TextAlign.center)), const SizedBox(width: 20, child: Text("", textAlign: TextAlign.center)),
InkWell( InkWell(
@ -219,7 +223,7 @@ class ImmichAppBarDialog extends HookConsumerWidget {
applicationVersion: packageInfo.version, applicationVersion: packageInfo.version,
); );
}, },
child: Text("licenses", style: context.textTheme.bodySmall).tr(), child: Text(context.t.licenses, style: context.textTheme.bodySmall),
), ),
], ],
), ),
@ -237,10 +241,10 @@ class ImmichAppBarDialog extends HookConsumerWidget {
minLeadingWidth: 20, minLeadingWidth: 20,
tileColor: theme.primaryColor.withAlpha(80), tileColor: theme.primaryColor.withAlpha(80),
title: Text( title: Text(
"profile_drawer_readonly_mode", context.t.profile_drawer_readonly_mode,
style: theme.textTheme.labelLarge?.copyWith(color: theme.textTheme.labelLarge?.color?.withAlpha(250)), style: theme.textTheme.labelLarge?.copyWith(color: theme.textTheme.labelLarge?.color?.withAlpha(250)),
textAlign: TextAlign.center, textAlign: TextAlign.center,
).tr(), ),
), ),
); );
} }

View file

@ -1,11 +1,11 @@
import 'dart:async'; import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/providers/auth.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/backup.provider.dart';
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
@ -68,7 +68,7 @@ class AppBarProfileInfoBox extends HookConsumerWidget {
SnackBar( SnackBar(
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
content: Text( content: Text(
(isReadonlyModeEnabled ? "readonly_mode_disabled" : "readonly_mode_enabled").tr(), isReadonlyModeEnabled ? context.t.readonly_mode_disabled : context.t.readonly_mode_enabled,
style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor),
), ),
), ),

View file

@ -1,11 +1,11 @@
import 'dart:async'; import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart' hide Store; import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/models/server_info/server_info.model.dart'; import 'package:immich_mobile/models/server_info/server_info.model.dart';
import 'package:immich_mobile/providers/locale_provider.dart'; import 'package:immich_mobile/providers/locale_provider.dart';
import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart';
@ -46,20 +46,20 @@ class AppBarServerInfo extends HookConsumerWidget {
children: [ children: [
if (showVersionWarning) ...[const ServerUpdateNotification(), divider], if (showVersionWarning) ...[const ServerUpdateNotification(), divider],
_ServerInfoItem( _ServerInfoItem(
label: "server_info_box_app_version".tr(), label: context.t.server_info_box_app_version,
text: "${appInfo.value["version"]} build.${appInfo.value["buildNumber"]}", text: "${appInfo.value["version"]} build.${appInfo.value["buildNumber"]}",
), ),
divider, divider,
_ServerInfoItem( _ServerInfoItem(
label: "server_version".tr(), label: context.t.server_version,
text: serverInfoState.serverVersion.major > 0 ? "${serverInfoState.serverVersion}" : "--", text: serverInfoState.serverVersion.major > 0 ? "${serverInfoState.serverVersion}" : "--",
), ),
divider, divider,
_ServerInfoItem(label: "server_info_box_server_url".tr(), text: getServerUrl() ?? '--', tooltip: true), _ServerInfoItem(label: context.t.server_info_box_server_url, text: getServerUrl() ?? '--', tooltip: true),
if (serverInfoState.latestVersion != null) ...[ if (serverInfoState.latestVersion != null) ...[
divider, divider,
_ServerInfoItem( _ServerInfoItem(
label: "latest_version".tr(), label: context.t.latest_version,
text: serverInfoState.latestVersion!.major > 0 ? "${serverInfoState.latestVersion!}" : "--", text: serverInfoState.latestVersion!.major > 0 ? "${serverInfoState.latestVersion!}" : "--",
tooltip: true, tooltip: true,
icon: serverInfoState.versionStatus == VersionStatus.serverOutOfDate icon: serverInfoState.versionStatus == VersionStatus.serverOutOfDate

View file

@ -1,11 +1,11 @@
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/models/server_info/server_info.model.dart'; import 'package:immich_mobile/models/server_info/server_info.model.dart';
import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart';
import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher/url_launcher_string.dart';
@ -76,8 +76,8 @@ class ServerUpdateNotification extends HookConsumerWidget {
tapTargetSize: MaterialTapTargetSize.shrinkWrap, tapTargetSize: MaterialTapTargetSize.shrinkWrap,
), ),
child: serverInfoState.versionStatus == VersionStatus.clientOutOfDate child: serverInfoState.versionStatus == VersionStatus.clientOutOfDate
? Text("action_common_update".tr(context: context)) ? Text(context.t.action_common_update)
: Text("view".tr()), : Text(context.t.view),
), ),
], ],
], ],

View file

@ -1,10 +1,11 @@
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/duration_extensions.dart'; import 'package:immich_mobile/extensions/duration_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/widgets/common/dropdown_search_menu.dart'; import 'package:immich_mobile/widgets/common/dropdown_search_menu.dart';
import 'package:intl/intl.dart';
import 'package:timezone/timezone.dart' as tz; import 'package:timezone/timezone.dart' as tz;
import 'package:timezone/timezone.dart'; import 'package:timezone/timezone.dart';
@ -116,26 +117,26 @@ class _DateTimePicker extends HookWidget {
TextButton( TextButton(
onPressed: () => context.pop(), onPressed: () => context.pop(),
child: Text( child: Text(
"cancel", context.t.cancel,
style: context.textTheme.bodyMedium?.copyWith( style: context.textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: context.colorScheme.error, color: context.colorScheme.error,
), ),
).tr(), ),
), ),
TextButton( TextButton(
onPressed: popWithDateTime, onPressed: popWithDateTime,
child: Text( child: Text(
"action_common_update", context.t.action_common_update,
style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor),
).tr(), ),
), ),
], ],
content: Column( content: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text("date_and_time", style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)).tr(), Text(context.t.date_and_time, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
const SizedBox(height: 32), const SizedBox(height: 32),
ListTile( ListTile(
tileColor: context.colorScheme.surfaceContainerHighest, tileColor: context.colorScheme.surfaceContainerHighest,
@ -151,8 +152,8 @@ class _DateTimePicker extends HookWidget {
const SizedBox(height: 24), const SizedBox(height: 24),
DropdownSearchMenu( DropdownSearchMenu(
trailingIcon: Icon(Icons.arrow_drop_down, color: context.primaryColor), trailingIcon: Icon(Icons.arrow_drop_down, color: context.primaryColor),
hintText: "timezone".tr(), hintText: context.t.timezone,
label: const Text('timezone').tr(), label: Text(context.t.timezone),
textStyle: context.textTheme.bodyMedium, textStyle: context.textTheme.bodyMedium,
onSelected: (value) => tzOffset.value = value, onSelected: (value) => tzOffset.value = value,
initialSelection: tzOffset.value, initialSelection: tzOffset.value,

View file

@ -1,11 +1,11 @@
import 'dart:async'; import 'dart:async';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/scheduler.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
class DropdownSearchMenu<T> extends HookWidget { class DropdownSearchMenu<T> extends HookWidget {
const DropdownSearchMenu({ const DropdownSearchMenu({
@ -81,7 +81,7 @@ class DropdownSearchMenu<T> extends HookWidget {
autofocus: true, autofocus: true,
focusNode: focusNode, focusNode: focusNode,
controller: textEditingController, controller: textEditingController,
decoration: inputDecoration.copyWith(hintText: "search_timezone".tr()), decoration: inputDecoration.copyWith(hintText: context.t.search_timezone),
maxLines: 1, maxLines: 1,
style: context.textTheme.bodyMedium, style: context.textTheme.bodyMedium,
expands: false, expands: false,

View file

@ -2,12 +2,12 @@ import 'dart:async';
import 'dart:math' as math; import 'dart:math' as math;
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/models/server_info/server_info.model.dart'; import 'package:immich_mobile/models/server_info/server_info.model.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/cast.provider.dart'; import 'package:immich_mobile/providers/cast.provider.dart';
@ -120,7 +120,7 @@ class _ProfileIndicator extends ConsumerWidget {
SnackBar( SnackBar(
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
content: Text( content: Text(
(isReadonlyModeEnabled ? "readonly_mode_disabled" : "readonly_mode_enabled").tr(), isReadonlyModeEnabled ? context.t.readonly_mode_disabled : context.t.readonly_mode_enabled,
style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor),
), ),
), ),
@ -143,7 +143,7 @@ class _ProfileIndicator extends ConsumerWidget {
? context.colorScheme.error ? context.colorScheme.error
: context.primaryColor, : context.primaryColor,
size: widgetSize / 2 - 3, size: widgetSize / 2 - 3,
semanticLabel: 'new_version_available'.tr(), semanticLabel: context.t.new_version_available,
), ),
), ),
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
@ -153,7 +153,7 @@ class _ProfileIndicator extends ConsumerWidget {
child: user == null child: user == null
? const Icon(Icons.face_outlined, size: widgetSize) ? const Icon(Icons.face_outlined, size: widgetSize)
: Semantics( : Semantics(
label: "logged_in_as".tr(namedArgs: {"user": user.name}), label: context.t.logged_in_as(user: user.name),
child: AbsorbPointer( child: AbsorbPointer(
child: Builder( child: Builder(
builder: (context) => UserCircleAvatar( builder: (context) => UserCircleAvatar(
@ -201,7 +201,12 @@ class _BackupIndicator extends ConsumerWidget {
if (!backupEnabled) { if (!backupEnabled) {
return _BadgeLabel( return _BadgeLabel(
Icon(Icons.cloud_off_rounded, size: 9, color: iconColor, semanticLabel: 'backup_controller_page_backup'.tr()), Icon(
Icons.cloud_off_rounded,
size: 9,
color: iconColor,
semanticLabel: context.t.backup_controller_page_backup,
),
); );
} }
@ -211,7 +216,7 @@ class _BackupIndicator extends ConsumerWidget {
Icons.warning_rounded, Icons.warning_rounded,
size: 12, size: 12,
color: context.colorScheme.error, color: context.colorScheme.error,
semanticLabel: 'backup_controller_page_backup'.tr(), semanticLabel: context.t.backup_controller_page_backup,
), ),
backgroundColor: context.colorScheme.errorContainer, backgroundColor: context.colorScheme.errorContainer,
); );
@ -229,7 +234,7 @@ class _BackupIndicator extends ConsumerWidget {
strokeWidth: 2, strokeWidth: 2,
strokeCap: StrokeCap.round, strokeCap: StrokeCap.round,
valueColor: AlwaysStoppedAnimation<Color>(iconColor), valueColor: AlwaysStoppedAnimation<Color>(iconColor),
semanticsLabel: 'backup_controller_page_backup'.tr(), semanticsLabel: context.t.backup_controller_page_backup,
), ),
), ),
), ),
@ -237,7 +242,7 @@ class _BackupIndicator extends ConsumerWidget {
} }
return _BadgeLabel( return _BadgeLabel(
Icon(Icons.check_outlined, size: 9, color: iconColor, semanticLabel: 'backup_controller_page_backup'.tr()), Icon(Icons.check_outlined, size: 9, color: iconColor, semanticLabel: context.t.backup_controller_page_backup),
); );
} }
} }

View file

@ -1,10 +1,10 @@
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/string_extensions.dart'; import 'package:immich_mobile/extensions/string_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/router.dart';
import 'package:maplibre_gl/maplibre_gl.dart'; import 'package:maplibre_gl/maplibre_gl.dart';
@ -73,11 +73,11 @@ class _LocationPicker extends HookWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text("edit_location_dialog_title", style: context.textTheme.titleMedium).tr(), Text(context.t.edit_location_dialog_title, style: context.textTheme.titleMedium),
Align( Align(
alignment: Alignment.center, alignment: Alignment.center,
child: TextButton.icon( child: TextButton.icon(
icon: const Text("location_picker_choose_on_map").tr(), icon: Text(context.t.location_picker_choose_on_map),
label: const Icon(Icons.map_outlined, size: 16), label: const Icon(Icons.map_outlined, size: 16),
onPressed: onMapTap, onPressed: onMapTap,
), ),
@ -85,9 +85,9 @@ class _LocationPicker extends HookWidget {
const SizedBox(height: 12), const SizedBox(height: 12),
_ManualPickerInput( _ManualPickerInput(
controller: latitudeController, controller: latitudeController,
decorationText: "latitude", decorationText: context.t.latitude,
hintText: "location_picker_latitude_hint", hintText: context.t.location_picker_latitude_hint,
errorText: "location_picker_latitude_error", errorText: context.t.location_picker_latitude_error,
focusNode: latitiudeFocusNode, focusNode: latitiudeFocusNode,
validator: _validateLat, validator: _validateLat,
onUpdated: onLatitudeUpdated, onUpdated: onLatitudeUpdated,
@ -95,9 +95,9 @@ class _LocationPicker extends HookWidget {
const SizedBox(height: 24), const SizedBox(height: 24),
_ManualPickerInput( _ManualPickerInput(
controller: longitudeController, controller: longitudeController,
decorationText: "longitude", decorationText: context.t.longitude,
hintText: "location_picker_longitude_hint", hintText: context.t.location_picker_longitude_hint,
errorText: "location_picker_longitude_error", errorText: context.t.location_picker_longitude_error,
focusNode: longitudeFocusNode, focusNode: longitudeFocusNode,
validator: _validateLong, validator: _validateLong,
onUpdated: onLongitudeEditingCompleted, onUpdated: onLongitudeEditingCompleted,
@ -109,19 +109,19 @@ class _LocationPicker extends HookWidget {
TextButton( TextButton(
onPressed: () => ContextHelper(context).pop(), onPressed: () => ContextHelper(context).pop(),
child: Text( child: Text(
"cancel", context.t.cancel,
style: context.textTheme.bodyMedium?.copyWith( style: context.textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: context.colorScheme.error, color: context.colorScheme.error,
), ),
).tr(), ),
), ),
TextButton( TextButton(
onPressed: () => context.maybePop(latlng), onPressed: () => context.maybePop(latlng),
child: Text( child: Text(
"action_common_update", context.t.action_common_update,
style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor),
).tr(), ),
), ),
], ],
); );
@ -163,13 +163,13 @@ class _ManualPickerInput extends HookWidget {
textInputAction: TextInputAction.done, textInputAction: TextInputAction.done,
autofocus: false, autofocus: false,
decoration: InputDecoration( decoration: InputDecoration(
labelText: decorationText.tr(), labelText: decorationText,
labelStyle: TextStyle(fontWeight: FontWeight.bold, color: context.primaryColor), labelStyle: TextStyle(fontWeight: FontWeight.bold, color: context.primaryColor),
floatingLabelBehavior: FloatingLabelBehavior.auto, floatingLabelBehavior: FloatingLabelBehavior.auto,
border: const OutlineInputBorder(), border: const OutlineInputBorder(),
hintText: hintText.tr(), hintText: hintText,
hintStyle: const TextStyle(fontWeight: FontWeight.normal, fontSize: 14), hintStyle: const TextStyle(fontWeight: FontWeight.normal, fontSize: 14),
errorText: isValid.value ? null : errorText.tr(), errorText: isValid.value ? null : errorText,
), ),
onEditingComplete: onEditingComplete, onEditingComplete: onEditingComplete,
keyboardType: const TextInputType.numberWithOptions(decimal: true, signed: true), keyboardType: const TextInputType.numberWithOptions(decimal: true, signed: true),

View file

@ -8,7 +8,7 @@ import 'package:immich_mobile/domain/models/events.model.dart';
import 'package:immich_mobile/domain/services/timeline.service.dart'; import 'package:immich_mobile/domain/services/timeline.service.dart';
import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/domain/utils/event_stream.dart';
import 'package:immich_mobile/extensions/build_context_extensions.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/images/image_provider.dart'; import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
@ -237,7 +237,7 @@ class _ItemCountTextState extends ConsumerState<_ItemCountText> {
final assetCount = ref.watch(timelineServiceProvider.select((s) => s.totalAssets)); final assetCount = ref.watch(timelineServiceProvider.select((s) => s.totalAssets));
return Text( return Text(
'items_count'.t(context: context, args: {"count": assetCount}), context.t.items_count(count: assetCount),
style: context.textTheme.labelLarge?.copyWith( style: context.textTheme.labelLarge?.copyWith(
// letterSpacing: 0.2, // letterSpacing: 0.2,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,

View file

@ -1,6 +1,6 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/translations.g.dart';
// Error widget to be used in Scaffold when an AsyncError is received // Error widget to be used in Scaffold when an AsyncError is received
class ScaffoldErrorBody extends StatelessWidget { class ScaffoldErrorBody extends StatelessWidget {
@ -15,7 +15,11 @@ class ScaffoldErrorBody extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text("scaffold_body_error_occurred", style: context.textTheme.displayMedium, textAlign: TextAlign.center).tr(), Text(
context.t.scaffold_body_error_occurred,
style: context.textTheme.displayMedium,
textAlign: TextAlign.center,
),
if (withIcon) if (withIcon)
Center( Center(
child: Padding( child: Padding(

View file

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
class SelectionSliverAppBar extends ConsumerStatefulWidget { class SelectionSliverAppBar extends ConsumerStatefulWidget {
@ -43,12 +43,12 @@ class _SelectionSliverAppBarState extends ConsumerState<SelectionSliverAppBar> {
}, },
), ),
centerTitle: true, centerTitle: true,
title: Text("Select {count}".t(context: context, args: {'count': filteredAssets.length.toString()})), title: Text(context.t.selected_count(count: filteredAssets.length)),
actions: [ actions: [
TextButton( TextButton(
onPressed: () => onDone(filteredAssets), onPressed: () => onDone(filteredAssets),
child: Text( child: Text(
'done'.t(context: context), context.t.done,
style: context.textTheme.titleSmall?.copyWith(color: context.colorScheme.primary), style: context.textTheme.titleSmall?.copyWith(color: context.colorScheme.primary),
), ),
), ),

View file

@ -1,10 +1,10 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/tag.model.dart'; import 'package:immich_mobile/domain/models/tag.model.dart';
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
import 'package:immich_mobile/extensions/build_context_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/tag.provider.dart'; import 'package:immich_mobile/providers/infrastructure/tag.provider.dart';
import 'package:immich_mobile/widgets/common/search_field.dart'; import 'package:immich_mobile/widgets/common/search_field.dart';
@ -41,19 +41,19 @@ class _TagPickerModal extends HookWidget {
TextButton( TextButton(
onPressed: () => context.pop(), onPressed: () => context.pop(),
child: Text( child: Text(
"cancel", context.t.cancel,
style: context.textTheme.bodyMedium?.copyWith( style: context.textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: context.colorScheme.error, color: context.colorScheme.error,
), ),
).tr(), ),
), ),
TextButton( TextButton(
onPressed: () => context.pop((selectedTagIds.value, newTagValues.value)), onPressed: () => context.pop((selectedTagIds.value, newTagValues.value)),
child: Text( child: Text(
"action_common_update", context.t.action_common_update,
style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor),
).tr(), ),
), ),
], ],
content: SizedBox( content: SizedBox(
@ -98,7 +98,7 @@ class TagPicker extends HookConsumerWidget {
onChanged: (value) => searchQuery.value = value, onChanged: (value) => searchQuery.value = value,
onTapOutside: (_) => formFocus.unfocus(), onTapOutside: (_) => formFocus.unfocus(),
filled: true, filled: true,
hintText: 'filter_tags'.tr(), hintText: context.t.filter_tags,
), ),
), ),
Padding( Padding(