From cc46799e20235e93930c4b2e9586d1b4b133739b Mon Sep 17 00:00:00 2001 From: cogwheel0 <172976095+cogwheel0@users.noreply.github.com> Date: Sun, 24 Aug 2025 20:27:11 +0530 Subject: [PATCH] feat: enhance localization support with additional strings and improved structure --- ios/Runner/Info.plist | 7 + .../services/user_friendly_error_handler.dart | 9 +- lib/core/widgets/error_boundary.dart | 4 +- lib/features/chat/views/chat_page.dart | 21 +- .../chat/widgets/modern_chat_input.dart | 8 +- .../chat/widgets/voice_input_sheet.dart | 84 ++-- .../navigation/widgets/chats_drawer.dart | 409 ++++++++++++------ lib/features/profile/views/profile_page.dart | 103 +++-- .../tools/widgets/unified_tools_modal.dart | 15 +- lib/l10n/app_localizations.dart | 187 +++++++- lib/l10n/app_localizations_de.dart | 158 +++++-- lib/l10n/app_localizations_en.dart | 146 ++++++- lib/l10n/app_localizations_fr.dart | 176 ++++++-- lib/l10n/app_localizations_it.dart | 152 +++++-- lib/main.dart | 36 +- 15 files changed, 1150 insertions(+), 365 deletions(-) diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 89681d8..587522b 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -4,6 +4,13 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) + CFBundleLocalizations + + en + de + fr + it + CFBundleDisplayName Conduit CFBundleExecutable diff --git a/lib/core/services/user_friendly_error_handler.dart b/lib/core/services/user_friendly_error_handler.dart index 7ceec73..3297799 100644 --- a/lib/core/services/user_friendly_error_handler.dart +++ b/lib/core/services/user_friendly_error_handler.dart @@ -1,5 +1,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:conduit/l10n/app_localizations.dart'; import '../../shared/theme/theme_extensions.dart'; /// User-friendly error messages and recovery actions @@ -438,7 +439,7 @@ class ErrorCard extends StatelessWidget { if (showDetails && technicalDetails != null) ...[ const SizedBox(height: Spacing.md), ExpansionTile( - title: const Text('Technical Details'), + title: Text(AppLocalizations.of(context)!.technicalDetails), children: [ Container( width: double.infinity, @@ -517,7 +518,7 @@ class ErrorDialog extends StatelessWidget { children: [ Icon(Icons.error_outline, color: Theme.of(context).colorScheme.error), const SizedBox(width: Spacing.sm + Spacing.xs), - const Text('Error'), + Text(AppLocalizations.of(context)!.errorMessage), ], ), content: Column( @@ -528,7 +529,7 @@ class ErrorDialog extends StatelessWidget { if (showDetails && technicalDetails != null) ...[ const SizedBox(height: Spacing.md), ExpansionTile( - title: const Text('Technical Details'), + title: Text(AppLocalizations.of(context)!.technicalDetails), children: [ SelectableText( technicalDetails!, @@ -545,7 +546,7 @@ class ErrorDialog extends StatelessWidget { actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), - child: const Text('Cancel'), + child: Text(AppLocalizations.of(context)!.cancel), ), if (actions.isNotEmpty) ElevatedButton( diff --git a/lib/core/widgets/error_boundary.dart b/lib/core/widgets/error_boundary.dart index a9edb20..fc16dcc 100644 --- a/lib/core/widgets/error_boundary.dart +++ b/lib/core/widgets/error_boundary.dart @@ -243,7 +243,9 @@ class AsyncErrorBoundary extends ConsumerWidget { (context as Element).markNeedsBuild(); }, icon: const Icon(Icons.refresh), - label: Text(AppLocalizations.of(context)!.retry), + label: Text( + AppLocalizations.of(context)?.retry ?? 'Retry', + ), ), ], ], diff --git a/lib/features/chat/views/chat_page.dart b/lib/features/chat/views/chat_page.dart index 277b348..d1d85c3 100644 --- a/lib/features/chat/views/chat_page.dart +++ b/lib/features/chat/views/chat_page.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:conduit/l10n/app_localizations.dart'; import '../../../core/widgets/error_boundary.dart'; import '../../../shared/widgets/optimized_list.dart'; import '../../../shared/theme/theme_extensions.dart'; @@ -805,7 +806,7 @@ class _ChatPageState extends ConsumerState { style: TextStyle(color: context.conduitTheme.textPrimary), maxLines: null, decoration: InputDecoration( - hintText: 'Enter your message', + hintText: AppLocalizations.of(context)!.messageHintText, hintStyle: TextStyle(color: context.conduitTheme.inputPlaceholder), border: OutlineInputBorder( borderSide: BorderSide(color: context.conduitTheme.inputBorder), @@ -831,7 +832,7 @@ class _ChatPageState extends ConsumerState { style: TextButton.styleFrom( foregroundColor: context.conduitTheme.buttonPrimary, ), - child: const Text('Save'), + child: Text(AppLocalizations.of(context)!.save), ), ], ), @@ -911,7 +912,7 @@ class _ChatPageState extends ConsumerState { const SizedBox(height: Spacing.xl), Text( - 'Start a conversation', + AppLocalizations.of(context)!.onboardStartTitle, style: theme.textTheme.headlineSmall?.copyWith( fontWeight: FontWeight.w600, color: context.conduitTheme.textPrimary, @@ -921,7 +922,7 @@ class _ChatPageState extends ConsumerState { const SizedBox(height: Spacing.sm), Text( - 'Type below to begin', + AppLocalizations.of(context)!.typeBelowToBegin, style: theme.textTheme.bodyLarge?.copyWith( color: context.conduitTheme.textSecondary, fontWeight: FontWeight.w400, @@ -1221,7 +1222,7 @@ class _ChatPageState extends ConsumerState { size: IconSize.appBar, ), onPressed: _handleNewChat, - tooltip: 'New Chat', + tooltip: AppLocalizations.of(context)!.newChat, ), ] else ...[ IconButton( @@ -1530,7 +1531,7 @@ class _ModelSelectorSheetState extends ConsumerState<_ModelSelectorSheet> { controller: _searchController, style: TextStyle(color: context.conduitTheme.textPrimary), decoration: InputDecoration( - hintText: 'Search...', + hintText: AppLocalizations.of(context)!.searchModels, hintStyle: TextStyle( color: context.conduitTheme.inputPlaceholder, ), @@ -2235,7 +2236,9 @@ class _VoiceInputSheetState extends ConsumerState<_VoiceInputSheet> { icon: Platform.isIOS ? CupertinoIcons.xmark : Icons.close, - tooltip: 'Close', + tooltip: AppLocalizations.of( + context, + )!.closeButtonSemantic, isCompact: true, onPressed: () => Navigator.of(context).pop(), ), @@ -2475,7 +2478,9 @@ class _VoiceInputSheetState extends ConsumerState<_VoiceInputSheet> { ConduitIconButton( icon: Icons.close, isCompact: true, - tooltip: 'Clear', + tooltip: AppLocalizations.of( + context, + )!.clear, onPressed: _recognizedText.isNotEmpty && !_isTranscribing diff --git a/lib/features/chat/widgets/modern_chat_input.dart b/lib/features/chat/widgets/modern_chat_input.dart index 42ab4bf..f65cdfd 100644 --- a/lib/features/chat/widgets/modern_chat_input.dart +++ b/lib/features/chat/widgets/modern_chat_input.dart @@ -393,7 +393,9 @@ class _ModernChatInputState extends ConsumerState icon: Platform.isIOS ? CupertinoIcons.search : Icons.search, - label: 'Web', + label: AppLocalizations.of( + context, + )!.web, isActive: webSearchEnabled, onTap: widget.enabled ? () { @@ -413,7 +415,9 @@ class _ModernChatInputState extends ConsumerState icon: Platform.isIOS ? CupertinoIcons.photo : Icons.image, - label: 'Image Gen', + label: AppLocalizations.of( + context, + )!.imageGen, isActive: imageGenEnabled, onTap: widget.enabled ? () { diff --git a/lib/features/chat/widgets/voice_input_sheet.dart b/lib/features/chat/widgets/voice_input_sheet.dart index bc5cb34..f1b87c2 100644 --- a/lib/features/chat/widgets/voice_input_sheet.dart +++ b/lib/features/chat/widgets/voice_input_sheet.dart @@ -3,6 +3,7 @@ import 'dart:io' show File, Platform; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:conduit/l10n/app_localizations.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../core/providers/app_providers.dart'; @@ -353,12 +354,12 @@ class _VoiceInputSheetState extends ConsumerState { children: [ Text( _isTranscribing - ? 'Transcribing…' + ? AppLocalizations.of(context)!.transcribing : _isListening ? (_voiceService.hasLocalStt - ? 'Listening…' - : 'Recording…') - : 'Voice', + ? AppLocalizations.of(context)!.listening + : AppLocalizations.of(context)!.recording) + : AppLocalizations.of(context)!.voiceInput, style: TextStyle( fontSize: AppTypography.headlineMedium, fontWeight: FontWeight.w600, @@ -426,7 +427,9 @@ class _VoiceInputSheetState extends ConsumerState { icon: Platform.isIOS ? CupertinoIcons.xmark : Icons.close, - tooltip: 'Close', + tooltip: AppLocalizations.of( + context, + )!.closeButtonSemantic, isCompact: true, onPressed: () => Navigator.of(context).pop(), ), @@ -456,11 +459,16 @@ class _VoiceInputSheetState extends ConsumerState { activeColor: context.conduitTheme.buttonPrimary, ), const SizedBox(width: Spacing.xs), - Text( - 'Hold to talk', - style: TextStyle(color: context.conduitTheme.textSecondary), + Flexible( + child: Text( + AppLocalizations.of(context)!.holdToTalk, + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: context.conduitTheme.textSecondary, + ), + ), ), - const Spacer(), + const SizedBox(width: Spacing.sm), ps.PlatformService.getPlatformSwitch( value: _autoSendFinal, onChanged: (v) async { @@ -472,9 +480,14 @@ class _VoiceInputSheetState extends ConsumerState { activeColor: context.conduitTheme.buttonPrimary, ), const SizedBox(width: Spacing.xs), - Text( - 'Auto-send', - style: TextStyle(color: context.conduitTheme.textSecondary), + Flexible( + child: Text( + AppLocalizations.of(context)!.autoSend, + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: context.conduitTheme.textSecondary, + ), + ), ), ], ), @@ -521,8 +534,10 @@ class _VoiceInputSheetState extends ConsumerState { child: Semantics( button: true, label: _isListening - ? 'Stop listening' - : 'Start listening', + ? AppLocalizations.of(context)!.stopListening + : AppLocalizations.of( + context, + )!.startListening, child: Stack( alignment: Alignment.center, children: [ @@ -617,7 +632,9 @@ class _VoiceInputSheetState extends ConsumerState { Row( children: [ Text( - 'Transcript', + AppLocalizations.of( + context, + )!.transcript, style: TextStyle( fontSize: AppTypography.labelSmall, fontWeight: FontWeight.w600, @@ -630,7 +647,9 @@ class _VoiceInputSheetState extends ConsumerState { ConduitIconButton( icon: Icons.close, isCompact: true, - tooltip: 'Clear', + tooltip: AppLocalizations.of( + context, + )!.clear, onPressed: _recognizedText.isNotEmpty && !_isTranscribing @@ -656,18 +675,13 @@ class _VoiceInputSheetState extends ConsumerState { ), const SizedBox(width: Spacing.xs), Text( - 'Transcribing…', + AppLocalizations.of( + context, + )!.transcribing, style: TextStyle( fontSize: isUltra - ? AppTypography.bodySmall - : (isCompact - ? AppTypography - .bodyMedium - : AppTypography - .bodyLarge), - color: context - .conduitTheme - .textSecondary, + ? 12 + : (isCompact ? 12 : 13), ), ), ], @@ -680,9 +694,15 @@ class _VoiceInputSheetState extends ConsumerState { _recognizedText.isEmpty ? (_isListening ? (_voiceService.hasLocalStt - ? 'Speak now…' - : 'Recording…') - : 'Tap Start to begin') + ? AppLocalizations.of( + context, + )!.speakNow + : AppLocalizations.of( + context, + )!.recording) + : AppLocalizations.of( + context, + )!.typeBelowToBegin) : _recognizedText, style: TextStyle( fontSize: isUltra @@ -731,7 +751,9 @@ class _VoiceInputSheetState extends ConsumerState { children: [ Expanded( child: ConduitButton( - text: _isListening ? 'Stop' : 'Start', + text: _isListening + ? AppLocalizations.of(context)!.stop + : AppLocalizations.of(context)!.start, isSecondary: true, isCompact: isCompact, onPressed: _isListening @@ -742,7 +764,7 @@ class _VoiceInputSheetState extends ConsumerState { const SizedBox(width: Spacing.xs), Expanded( child: ConduitButton( - text: 'Send', + text: AppLocalizations.of(context)!.send, isCompact: isCompact, onPressed: _recognizedText.isNotEmpty ? _sendText : null, ), diff --git a/lib/features/navigation/widgets/chats_drawer.dart b/lib/features/navigation/widgets/chats_drawer.dart index a540a77..fc9bb7c 100644 --- a/lib/features/navigation/widgets/chats_drawer.dart +++ b/lib/features/navigation/widgets/chats_drawer.dart @@ -34,8 +34,9 @@ class _ChatsDrawerState extends ConsumerState { // UI state providers for sections static final _showArchivedProvider = StateProvider((ref) => false); - static final _expandedFoldersProvider = - StateProvider>((ref) => {}); + static final _expandedFoldersProvider = StateProvider>( + (ref) => {}, + ); @override void dispose() { @@ -96,7 +97,7 @@ class _ChatsDrawerState extends ConsumerState { children: [ // Centered title (no leading icon) Text( - 'Chats', + AppLocalizations.of(context)!.chats, style: AppTypography.headlineSmallStyle.copyWith( color: theme.textPrimary, fontWeight: FontWeight.w600, @@ -214,17 +215,21 @@ class _ChatsDrawerState extends ConsumerState { // Build sections final pinned = list.where((c) => c.pinned == true).toList(); final regular = list - .where((c) => - c.pinned != true && - c.archived != true && - (c.folderId == null || c.folderId!.isEmpty)) + .where( + (c) => + c.pinned != true && + c.archived != true && + (c.folderId == null || c.folderId!.isEmpty), + ) .toList(); final foldered = list - .where((c) => - c.pinned != true && - c.archived != true && - c.folderId != null && - c.folderId!.isNotEmpty) + .where( + (c) => + c.pinned != true && + c.archived != true && + c.folderId != null && + c.folderId!.isNotEmpty, + ) .toList(); final archived = list.where((c) => c.archived == true).toList(); @@ -237,7 +242,10 @@ class _ChatsDrawerState extends ConsumerState { ), children: [ if (pinned.isNotEmpty) ...[ - _buildSectionHeader('Pinned', pinned.length), + _buildSectionHeader( + AppLocalizations.of(context)!.pinned, + pinned.length, + ), const SizedBox(height: Spacing.xs), ...pinned.map((conv) => _buildTileFor(conv)), const SizedBox(height: Spacing.md), @@ -250,40 +258,53 @@ class _ChatsDrawerState extends ConsumerState { _buildUnfileDropTarget(), const SizedBox(height: Spacing.sm), ], - ...ref.watch(foldersProvider).when( - data: (folders) { - final grouped = >{}; - for (final c in foldered) { - final id = c.folderId!; - grouped.putIfAbsent(id, () => []).add(c); - } + ...ref + .watch(foldersProvider) + .when( + data: (folders) { + final grouped = >{}; + for (final c in foldered) { + final id = c.folderId!; + grouped.putIfAbsent(id, () => []).add(c); + } - // Show all folders (including empty) - final sections = folders.map((folder) { - final expandedMap = ref.watch(_expandedFoldersProvider); - final isExpanded = expandedMap[folder.id] ?? false; - final convs = grouped[folder.id] ?? const []; - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - _buildFolderHeader(folder.id, folder.name, convs.length), - if (isExpanded && convs.isNotEmpty) ...[ - const SizedBox(height: Spacing.xs), - ...convs.map((c) => _buildTileFor(c, inFolder: true)), - const SizedBox(height: Spacing.sm), - ], + // Show all folders (including empty) + final sections = folders.map((folder) { + final expandedMap = ref.watch(_expandedFoldersProvider); + final isExpanded = expandedMap[folder.id] ?? false; + final convs = grouped[folder.id] ?? const []; + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _buildFolderHeader( + folder.id, + folder.name, + convs.length, + ), + if (isExpanded && convs.isNotEmpty) ...[ + const SizedBox(height: Spacing.xs), + ...convs.map( + (c) => _buildTileFor(c, inFolder: true), + ), + const SizedBox(height: Spacing.sm), ], - ); - }).toList(); - return sections.isEmpty ? [const SizedBox.shrink()] : sections; - }, - loading: () => [const SizedBox.shrink()], - error: (e, st) => [const SizedBox.shrink()], - ), + ], + ); + }).toList(); + return sections.isEmpty + ? [const SizedBox.shrink()] + : sections; + }, + loading: () => [const SizedBox.shrink()], + error: (e, st) => [const SizedBox.shrink()], + ), const SizedBox(height: Spacing.md), if (regular.isNotEmpty) ...[ - _buildSectionHeader(AppLocalizations.of(context)!.recent, regular.length), + _buildSectionHeader( + AppLocalizations.of(context)!.recent, + regular.length, + ), const SizedBox(height: Spacing.xs), ...regular.map(_buildTileFor), ], @@ -295,7 +316,8 @@ class _ChatsDrawerState extends ConsumerState { ], ); }, - loading: () => const Center(child: CircularProgressIndicator(strokeWidth: 2.0)), + loading: () => + const Center(child: CircularProgressIndicator(strokeWidth: 2.0)), error: (e, _) => Center( child: Padding( padding: const EdgeInsets.all(Spacing.md), @@ -330,17 +352,21 @@ class _ChatsDrawerState extends ConsumerState { final pinned = list.where((c) => c.pinned == true).toList(); final regular = list - .where((c) => - c.pinned != true && - c.archived != true && - (c.folderId == null || c.folderId!.isEmpty)) + .where( + (c) => + c.pinned != true && + c.archived != true && + (c.folderId == null || c.folderId!.isEmpty), + ) .toList(); final foldered = list - .where((c) => - c.pinned != true && - c.archived != true && - c.folderId != null && - c.folderId!.isNotEmpty) + .where( + (c) => + c.pinned != true && + c.archived != true && + c.folderId != null && + c.folderId!.isNotEmpty, + ) .toList(); final archived = list.where((c) => c.archived == true).toList(); @@ -355,7 +381,10 @@ class _ChatsDrawerState extends ConsumerState { _buildSectionHeader('Results', list.length), const SizedBox(height: Spacing.xs), if (pinned.isNotEmpty) ...[ - _buildSectionHeader('Pinned', pinned.length), + _buildSectionHeader( + AppLocalizations.of(context)!.pinned, + pinned.length, + ), const SizedBox(height: Spacing.xs), ...pinned.map((conv) => _buildTileFor(conv)), const SizedBox(height: Spacing.md), @@ -367,7 +396,9 @@ class _ChatsDrawerState extends ConsumerState { _buildUnfileDropTarget(), const SizedBox(height: Spacing.sm), ], - ...ref.watch(foldersProvider).when( + ...ref + .watch(foldersProvider) + .when( data: (folders) { final grouped = >{}; for (final c in foldered) { @@ -375,30 +406,41 @@ class _ChatsDrawerState extends ConsumerState { grouped.putIfAbsent(id, () => []).add(c); } - final sections = folders.map((folder) { - final expandedMap = ref.watch(_expandedFoldersProvider); - final isExpanded = expandedMap[folder.id] ?? false; - final convs = grouped[folder.id] ?? const []; - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - _buildFolderHeader(folder.id, folder.name, convs.length), - if (isExpanded && convs.isNotEmpty) ...[ - const SizedBox(height: Spacing.xs), - ...convs.map((c) => _buildTileFor(c, inFolder: true)), - const SizedBox(height: Spacing.sm), - ], + final sections = folders.map((folder) { + final expandedMap = ref.watch(_expandedFoldersProvider); + final isExpanded = expandedMap[folder.id] ?? false; + final convs = grouped[folder.id] ?? const []; + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _buildFolderHeader( + folder.id, + folder.name, + convs.length, + ), + if (isExpanded && convs.isNotEmpty) ...[ + const SizedBox(height: Spacing.xs), + ...convs.map( + (c) => _buildTileFor(c, inFolder: true), + ), + const SizedBox(height: Spacing.sm), ], - ); - }).toList(); - return sections.isEmpty ? [const SizedBox.shrink()] : sections; - }, - loading: () => [const SizedBox.shrink()], - error: (e, st) => [const SizedBox.shrink()], - ), + ], + ); + }).toList(); + return sections.isEmpty + ? [const SizedBox.shrink()] + : sections; + }, + loading: () => [const SizedBox.shrink()], + error: (e, st) => [const SizedBox.shrink()], + ), const SizedBox(height: Spacing.md), if (regular.isNotEmpty) ...[ - _buildSectionHeader(AppLocalizations.of(context)!.recent, regular.length), + _buildSectionHeader( + AppLocalizations.of(context)!.recent, + regular.length, + ), const SizedBox(height: Spacing.xs), ...regular.map(_buildTileFor), ], @@ -409,7 +451,8 @@ class _ChatsDrawerState extends ConsumerState { ], ); }, - loading: () => const Center(child: CircularProgressIndicator(strokeWidth: 2.0)), + loading: () => + const Center(child: CircularProgressIndicator(strokeWidth: 2.0)), error: (e, _) => Center( child: Padding( padding: const EdgeInsets.all(Spacing.md), @@ -442,7 +485,10 @@ class _ChatsDrawerState extends ConsumerState { decoration: BoxDecoration( color: theme.surfaceBackground.withValues(alpha: 0.6), borderRadius: BorderRadius.circular(AppBorderRadius.xs), - border: Border.all(color: theme.dividerColor, width: BorderWidth.thin), + border: Border.all( + color: theme.dividerColor, + width: BorderWidth.thin, + ), ), child: Text( '$count', @@ -461,7 +507,7 @@ class _ChatsDrawerState extends ConsumerState { return Row( children: [ Text( - 'Folders', + AppLocalizations.of(context)!.folders, style: AppTypography.bodySmallStyle.copyWith( fontWeight: FontWeight.w600, color: theme.textSecondary, @@ -473,7 +519,9 @@ class _ChatsDrawerState extends ConsumerState { visualDensity: VisualDensity.compact, tooltip: AppLocalizations.of(context)!.newFolder, icon: Icon( - Platform.isIOS ? CupertinoIcons.folder_badge_plus : Icons.create_new_folder_outlined, + Platform.isIOS + ? CupertinoIcons.folder_badge_plus + : Icons.create_new_folder_outlined, color: theme.iconPrimary, ), onPressed: _promptCreateFolder, @@ -489,7 +537,10 @@ class _ChatsDrawerState extends ConsumerState { context: context, builder: (ctx) => AlertDialog( backgroundColor: theme.surfaceBackground, - title: Text(AppLocalizations.of(context)!.newFolder, style: TextStyle(color: theme.textPrimary)), + title: Text( + AppLocalizations.of(context)!.newFolder, + style: TextStyle(color: theme.textPrimary), + ), content: TextField( controller: controller, autofocus: true, @@ -497,8 +548,12 @@ class _ChatsDrawerState extends ConsumerState { decoration: InputDecoration( hintText: AppLocalizations.of(context)!.folderName, hintStyle: TextStyle(color: theme.inputPlaceholder), - enabledBorder: UnderlineInputBorder(borderSide: BorderSide(color: theme.inputBorder)), - focusedBorder: UnderlineInputBorder(borderSide: BorderSide(color: theme.buttonPrimary)), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: theme.inputBorder), + ), + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: theme.buttonPrimary), + ), ), onSubmitted: (v) => Navigator.pop(ctx, controller.text.trim()), ), @@ -527,7 +582,11 @@ class _ChatsDrawerState extends ConsumerState { UiUtils.showMessage(context, AppLocalizations.of(context)!.folderCreated); } catch (e) { if (!mounted) return; - UiUtils.showMessage(context, AppLocalizations.of(context)!.failedToCreateFolder, isError: true); + UiUtils.showMessage( + context, + AppLocalizations.of(context)!.failedToCreateFolder, + isError: true, + ); } } @@ -557,12 +616,18 @@ class _ChatsDrawerState extends ConsumerState { if (mounted) { UiUtils.showMessage( context, - AppLocalizations.of(context)!.movedChatToFolder(details.data.title, name), + AppLocalizations.of( + context, + )!.movedChatToFolder(details.data.title, name), ); } } catch (_) { if (mounted) { - UiUtils.showMessage(context, AppLocalizations.of(context)!.failedToMoveChat, isError: true); + UiUtils.showMessage( + context, + AppLocalizations.of(context)!.failedToMoveChat, + isError: true, + ); } } }, @@ -596,8 +661,12 @@ class _ChatsDrawerState extends ConsumerState { children: [ Icon( isExpanded - ? (Platform.isIOS ? CupertinoIcons.folder_open : Icons.folder_open) - : (Platform.isIOS ? CupertinoIcons.folder : Icons.folder), + ? (Platform.isIOS + ? CupertinoIcons.folder_open + : Icons.folder_open) + : (Platform.isIOS + ? CupertinoIcons.folder + : Icons.folder), color: theme.iconPrimary, ), const SizedBox(width: Spacing.sm), @@ -619,10 +688,14 @@ class _ChatsDrawerState extends ConsumerState { const SizedBox(width: Spacing.xs), Icon( isExpanded - ? (Platform.isIOS ? CupertinoIcons.chevron_up : Icons.expand_less) - : (Platform.isIOS ? CupertinoIcons.chevron_down : Icons.expand_more), + ? (Platform.isIOS + ? CupertinoIcons.chevron_up + : Icons.expand_less) + : (Platform.isIOS + ? CupertinoIcons.chevron_down + : Icons.expand_more), color: theme.iconSecondary, - ) + ), ], ), ), @@ -654,11 +727,18 @@ class _ChatsDrawerState extends ConsumerState { ref.invalidate(conversationsProvider); ref.invalidate(foldersProvider); if (mounted) { - UiUtils.showMessage(context, 'Removed "${details.data.title}" from folder'); + UiUtils.showMessage( + context, + 'Removed "${details.data.title}" from folder', + ); } } catch (_) { if (mounted) { - UiUtils.showMessage(context, AppLocalizations.of(context)!.failedToMoveChat, isError: true); + UiUtils.showMessage( + context, + AppLocalizations.of(context)!.failedToMoveChat, + isError: true, + ); } } }, @@ -713,7 +793,9 @@ class _ChatsDrawerState extends ConsumerState { title: title, pinned: conv.pinned == true, selected: isActive, - onTap: _isLoadingConversation ? null : () => _selectConversation(context, conv.id), + onTap: _isLoadingConversation + ? null + : () => _selectConversation(context, conv.id), // Remove long-press context menu to avoid conflict with drag gesture onLongPress: null, onMorePressed: () { @@ -723,7 +805,10 @@ class _ChatsDrawerState extends ConsumerState { ); return Padding( - padding: EdgeInsets.only(bottom: Spacing.xs, left: inFolder ? Spacing.md : 0), + padding: EdgeInsets.only( + bottom: Spacing.xs, + left: inFolder ? Spacing.md : 0, + ), child: LongPressDraggable<_DragConversationData>( data: _DragConversationData(id: conv.id, title: title), dragAnchorStrategy: pointerDragAnchorStrategy, @@ -768,7 +853,8 @@ class _ChatsDrawerState extends ConsumerState { ), onDragStarted: () { HapticFeedback.lightImpact(); - final hasFolder = (conv.folderId != null && (conv.folderId as String).isNotEmpty); + final hasFolder = + (conv.folderId != null && (conv.folderId as String).isNotEmpty); setState(() { _isDragging = true; _draggingHasFolder = hasFolder; @@ -794,12 +880,14 @@ class _ChatsDrawerState extends ConsumerState { color: theme.surfaceBackground.withValues(alpha: 0.05), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(AppBorderRadius.md), - side: BorderSide(color: theme.dividerColor, width: BorderWidth.regular), + side: BorderSide( + color: theme.dividerColor, + width: BorderWidth.regular, + ), ), child: InkWell( borderRadius: BorderRadius.circular(AppBorderRadius.md), - onTap: () => - ref.read(_showArchivedProvider.notifier).state = !show, + onTap: () => ref.read(_showArchivedProvider.notifier).state = !show, child: Padding( padding: const EdgeInsets.symmetric( horizontal: Spacing.md, @@ -816,7 +904,7 @@ class _ChatsDrawerState extends ConsumerState { const SizedBox(width: Spacing.sm), Expanded( child: Text( - 'Archived', + AppLocalizations.of(context)!.archived, style: AppTypography.bodyLargeStyle.copyWith( color: theme.textPrimary, fontWeight: FontWeight.w600, @@ -833,11 +921,11 @@ class _ChatsDrawerState extends ConsumerState { Icon( show ? (Platform.isIOS - ? CupertinoIcons.chevron_up - : Icons.expand_less) + ? CupertinoIcons.chevron_up + : Icons.expand_less) : (Platform.isIOS - ? CupertinoIcons.chevron_down - : Icons.expand_more), + ? CupertinoIcons.chevron_down + : Icons.expand_more), color: theme.iconSecondary, ), ], @@ -867,9 +955,9 @@ class _ChatsDrawerState extends ConsumerState { ref.read(activeConversationProvider.notifier).state = full; } else { // Fallback: let ChatPage handle if API missing - ref.read(activeConversationProvider.notifier).state = - (await ref.read(conversationsProvider.future)) - .firstWhere((c) => c.id == id); + ref.read(activeConversationProvider.notifier).state = (await ref.read( + conversationsProvider.future, + )).firstWhere((c) => c.id == id); } // Clear global loading before closing drawer @@ -890,7 +978,12 @@ class _ChatsDrawerState extends ConsumerState { return SafeArea( top: false, child: Padding( - padding: const EdgeInsets.fromLTRB(Spacing.sm, 0, Spacing.sm, Spacing.sm), + padding: const EdgeInsets.fromLTRB( + Spacing.sm, + 0, + Spacing.sm, + Spacing.sm, + ), child: Column( mainAxisSize: MainAxisSize.min, children: [ @@ -901,7 +994,10 @@ class _ChatsDrawerState extends ConsumerState { decoration: BoxDecoration( color: theme.surfaceBackground.withValues(alpha: 0.04), borderRadius: BorderRadius.circular(AppBorderRadius.md), - border: Border.all(color: theme.dividerColor, width: BorderWidth.regular), + border: Border.all( + color: theme.dividerColor, + width: BorderWidth.regular, + ), ), child: Row( children: [ @@ -910,12 +1006,20 @@ class _ChatsDrawerState extends ConsumerState { height: IconSize.avatar, decoration: BoxDecoration( color: theme.buttonPrimary.withValues(alpha: 0.15), - borderRadius: BorderRadius.circular(AppBorderRadius.avatar), - border: Border.all(color: theme.buttonPrimary.withValues(alpha: 0.35), width: BorderWidth.thin), + borderRadius: BorderRadius.circular( + AppBorderRadius.avatar, + ), + border: Border.all( + color: theme.buttonPrimary.withValues(alpha: 0.35), + width: BorderWidth.thin, + ), ), alignment: Alignment.center, child: Text( - (user.name ?? user.username ?? 'U').toString().substring(0, 1).toUpperCase(), + (user.name ?? user.username ?? 'U') + .toString() + .substring(0, 1) + .toUpperCase(), style: AppTypography.bodyLargeStyle.copyWith( color: theme.buttonPrimary, fontWeight: FontWeight.w700, @@ -949,11 +1053,13 @@ class _ChatsDrawerState extends ConsumerState { onPressed: () { Navigator.of(context).maybePop(); Navigator.of(context).push( - MaterialPageRoute(builder: (_) => const ProfilePage()), + MaterialPageRoute( + builder: (_) => const ProfilePage(), + ), ); }, child: Text(AppLocalizations.of(context)!.manage), - ) + ), ], ), ), @@ -974,7 +1080,9 @@ class _ChatsDrawerState extends ConsumerState { context: context, backgroundColor: theme.surfaceBackground, shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(AppBorderRadius.lg)), + borderRadius: BorderRadius.vertical( + top: Radius.circular(AppBorderRadius.lg), + ), ), builder: (sheetContext) { return SafeArea( @@ -984,8 +1092,12 @@ class _ChatsDrawerState extends ConsumerState { ListTile( leading: Icon( isPinned - ? (Platform.isIOS ? CupertinoIcons.pin_slash : Icons.push_pin_outlined) - : (Platform.isIOS ? CupertinoIcons.pin_fill : Icons.push_pin_rounded), + ? (Platform.isIOS + ? CupertinoIcons.pin_slash + : Icons.push_pin_outlined) + : (Platform.isIOS + ? CupertinoIcons.pin_fill + : Icons.push_pin_rounded), color: theme.iconPrimary, ), title: Text( @@ -1001,15 +1113,23 @@ class _ChatsDrawerState extends ConsumerState { await chat.pinConversation(ref, conv.id, !isPinned); } catch (_) { if (!mounted) return; - UiUtils.showMessage(this.context, AppLocalizations.of(context)!.failedToUpdatePin, isError: true); + UiUtils.showMessage( + this.context, + AppLocalizations.of(context)!.failedToUpdatePin, + isError: true, + ); } }, ), ListTile( leading: Icon( isArchived - ? (Platform.isIOS ? CupertinoIcons.archivebox_fill : Icons.unarchive_rounded) - : (Platform.isIOS ? CupertinoIcons.archivebox : Icons.archive_rounded), + ? (Platform.isIOS + ? CupertinoIcons.archivebox_fill + : Icons.unarchive_rounded) + : (Platform.isIOS + ? CupertinoIcons.archivebox + : Icons.archive_rounded), color: theme.iconPrimary, ), title: Text( @@ -1025,7 +1145,11 @@ class _ChatsDrawerState extends ConsumerState { await chat.archiveConversation(ref, conv.id, !isArchived); } catch (_) { if (!mounted) return; - UiUtils.showMessage(this.context, AppLocalizations.of(context)!.failedToUpdateArchive, isError: true); + UiUtils.showMessage( + this.context, + AppLocalizations.of(context)!.failedToUpdateArchive, + isError: true, + ); } }, ), @@ -1034,7 +1158,10 @@ class _ChatsDrawerState extends ConsumerState { Platform.isIOS ? CupertinoIcons.pencil : Icons.edit_rounded, color: theme.iconPrimary, ), - title: Text(AppLocalizations.of(context)!.rename, style: TextStyle(color: theme.textPrimary)), + title: Text( + AppLocalizations.of(context)!.rename, + style: TextStyle(color: theme.textPrimary), + ), onTap: () async { HapticFeedback.selectionClick(); Navigator.pop(sheetContext); @@ -1047,7 +1174,10 @@ class _ChatsDrawerState extends ConsumerState { Platform.isIOS ? CupertinoIcons.delete : Icons.delete_rounded, color: theme.error, ), - title: Text(AppLocalizations.of(context)!.delete, style: TextStyle(color: theme.error)), + title: Text( + AppLocalizations.of(context)!.delete, + style: TextStyle(color: theme.error), + ), onTap: () async { HapticFeedback.mediumImpact(); Navigator.pop(sheetContext); @@ -1074,7 +1204,10 @@ class _ChatsDrawerState extends ConsumerState { builder: (dialogContext) { return AlertDialog( backgroundColor: theme.surfaceBackground, - title: Text(AppLocalizations.of(context)!.renameChat, style: TextStyle(color: theme.textPrimary)), + title: Text( + AppLocalizations.of(context)!.renameChat, + style: TextStyle(color: theme.textPrimary), + ), content: TextField( controller: controller, autofocus: true, @@ -1121,12 +1254,17 @@ class _ChatsDrawerState extends ConsumerState { ref.invalidate(conversationsProvider); final active = ref.read(activeConversationProvider); if (active?.id == conversationId) { - ref.read(activeConversationProvider.notifier).state = - active!.copyWith(title: newName); + ref.read(activeConversationProvider.notifier).state = active!.copyWith( + title: newName, + ); } } catch (_) { if (!mounted) return; - UiUtils.showMessage(this.context, AppLocalizations.of(context)!.failedToRenameChat, isError: true); + UiUtils.showMessage( + this.context, + AppLocalizations.of(context)!.failedToRenameChat, + isError: true, + ); } } @@ -1157,7 +1295,11 @@ class _ChatsDrawerState extends ConsumerState { ref.invalidate(conversationsProvider); } catch (_) { if (!mounted) return; - UiUtils.showMessage(this.context, AppLocalizations.of(context)!.failedToDeleteChat, isError: true); + UiUtils.showMessage( + this.context, + AppLocalizations.of(context)!.failedToDeleteChat, + isError: true, + ); } } } @@ -1195,7 +1337,9 @@ class _ConversationTile extends StatelessWidget { shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(AppBorderRadius.md), side: BorderSide( - color: selected ? theme.buttonPrimary.withValues(alpha: 0.5) : theme.dividerColor, + color: selected + ? theme.buttonPrimary.withValues(alpha: 0.5) + : theme.dividerColor, width: BorderWidth.regular, ), ), @@ -1226,9 +1370,14 @@ class _ConversationTile extends StatelessWidget { IconButton( visualDensity: VisualDensity.compact, padding: EdgeInsets.zero, - constraints: const BoxConstraints(minWidth: 36, minHeight: 36), + constraints: const BoxConstraints( + minWidth: 36, + minHeight: 36, + ), icon: Icon( - Platform.isIOS ? CupertinoIcons.ellipsis : Icons.more_vert_rounded, + Platform.isIOS + ? CupertinoIcons.ellipsis + : Icons.more_vert_rounded, color: theme.iconSecondary, size: IconSize.md, ), diff --git a/lib/features/profile/views/profile_page.dart b/lib/features/profile/views/profile_page.dart index 2271957..4b6e01a 100644 --- a/lib/features/profile/views/profile_page.dart +++ b/lib/features/profile/views/profile_page.dart @@ -119,7 +119,9 @@ class ProfilePage extends ConsumerWidget { centerTitle: true, ), body: Center( - child: ImprovedLoadingState(message: AppLocalizations.of(context)!.loadingProfile), + child: ImprovedLoadingState( + message: AppLocalizations.of(context)!.loadingProfile, + ), ), ), error: (error, stack) => Scaffold( @@ -311,17 +313,16 @@ class ProfilePage extends ConsumerWidget { Widget _buildDefaultModelTile(BuildContext context, WidgetRef ref) { final settings = ref.watch(appSettingsProvider); final modelsAsync = ref.watch(modelsProvider); - + return modelsAsync.when( data: (models) { final currentModel = models.firstWhere( (m) => m.id == settings.defaultModel, - orElse: () => models.isNotEmpty ? models.first : const Model( - id: 'none', - name: 'No models available', - ), + orElse: () => models.isNotEmpty + ? models.first + : const Model(id: 'none', name: 'No models available'), ); - + return ListTile( contentPadding: const EdgeInsets.symmetric( horizontal: Spacing.listItemPadding, @@ -352,7 +353,9 @@ class ProfilePage extends ConsumerWidget { ), ), subtitle: Text( - settings.defaultModel != null ? currentModel.name : AppLocalizations.of(context)!.autoSelect, + settings.defaultModel != null + ? currentModel.name + : AppLocalizations.of(context)!.autoSelect, style: context.conduitTheme.bodySmall?.copyWith( color: context.conduitTheme.textSecondary, ), @@ -484,7 +487,7 @@ class ProfilePage extends ConsumerWidget { ), ), title: Text( - AppLocalizations.of(context)!.menuItem, + AppLocalizations.of(context)!.appLanguage, style: context.conduitTheme.bodyLarge?.copyWith( color: context.conduitTheme.textPrimary, fontWeight: FontWeight.w500, @@ -600,7 +603,7 @@ class ProfilePage extends ConsumerWidget { ), ), title: Text( - 'Dark Mode', + AppLocalizations.of(context)!.darkMode, style: context.conduitTheme.bodyLarge?.copyWith( color: context.conduitTheme.textPrimary, fontWeight: FontWeight.w500, @@ -720,7 +723,11 @@ class ProfilePage extends ConsumerWidget { } } - Future _showModelSelector(BuildContext context, WidgetRef ref, List models) async { + Future _showModelSelector( + BuildContext context, + WidgetRef ref, + List models, + ) async { final result = await showModalBottomSheet( context: context, isScrollControlled: true, @@ -730,13 +737,15 @@ class ProfilePage extends ConsumerWidget { currentDefaultModelId: ref.read(appSettingsProvider).defaultModel, ), ); - + // result is non-null only when Save button is pressed // null means the sheet was dismissed without saving if (result != null) { // Handle special case: 'auto-select' should be stored as null final modelIdToSave = result == 'auto-select' ? null : result; - await ref.read(appSettingsProvider.notifier).setDefaultModel(modelIdToSave); + await ref + .read(appSettingsProvider.notifier) + .setDefaultModel(modelIdToSave); } } @@ -765,10 +774,12 @@ class _DefaultModelBottomSheet extends ConsumerStatefulWidget { }); @override - ConsumerState<_DefaultModelBottomSheet> createState() => _DefaultModelBottomSheetState(); + ConsumerState<_DefaultModelBottomSheet> createState() => + _DefaultModelBottomSheetState(); } -class _DefaultModelBottomSheetState extends ConsumerState<_DefaultModelBottomSheet> { +class _DefaultModelBottomSheetState + extends ConsumerState<_DefaultModelBottomSheet> { final TextEditingController _searchController = TextEditingController(); String _searchQuery = ''; List _filteredModels = []; @@ -833,7 +844,7 @@ class _DefaultModelBottomSheetState extends ConsumerState<_DefaultModelBottomShe const Model(id: 'auto-select', name: 'Auto-select'), ...widget.models, ]; - + if (_searchQuery.isNotEmpty) { _filteredModels = allModels.where((model) { return model.name.toLowerCase().contains(_searchQuery) || @@ -896,18 +907,24 @@ class _DefaultModelBottomSheetState extends ConsumerState<_DefaultModelBottomShe filled: true, fillColor: context.conduitTheme.inputBackground, border: OutlineInputBorder( - borderRadius: BorderRadius.circular(AppBorderRadius.md), + borderRadius: BorderRadius.circular( + AppBorderRadius.md, + ), borderSide: BorderSide.none, ), enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(AppBorderRadius.md), + borderRadius: BorderRadius.circular( + AppBorderRadius.md, + ), borderSide: BorderSide( color: context.conduitTheme.inputBorder, width: 1, ), ), focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(AppBorderRadius.md), + borderRadius: BorderRadius.circular( + AppBorderRadius.md, + ), borderSide: BorderSide( color: context.conduitTheme.buttonPrimary, width: 1, @@ -937,10 +954,16 @@ class _DefaultModelBottomSheetState extends ConsumerState<_DefaultModelBottomShe ), const SizedBox(width: Spacing.xs), Container( - padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), + padding: const EdgeInsets.symmetric( + horizontal: 6, + vertical: 2, + ), decoration: BoxDecoration( - color: context.conduitTheme.surfaceBackground.withValues(alpha: 0.6), - borderRadius: BorderRadius.circular(AppBorderRadius.xs), + color: context.conduitTheme.surfaceBackground + .withValues(alpha: 0.6), + borderRadius: BorderRadius.circular( + AppBorderRadius.xs, + ), border: Border.all( color: context.conduitTheme.dividerColor, width: BorderWidth.thin, @@ -993,8 +1016,9 @@ class _DefaultModelBottomSheetState extends ConsumerState<_DefaultModelBottomShe itemBuilder: (context, index) { final model = _filteredModels[index]; final isAutoSelect = model.id == 'auto-select'; - final isSelected = isAutoSelect - ? _selectedModelId == null || _selectedModelId == 'auto-select' + final isSelected = isAutoSelect + ? _selectedModelId == null || + _selectedModelId == 'auto-select' : _selectedModelId == model.id; return _buildModelListTile( @@ -1003,8 +1027,9 @@ class _DefaultModelBottomSheetState extends ConsumerState<_DefaultModelBottomShe isAutoSelect: isAutoSelect, onTap: () { HapticFeedback.lightImpact(); - final selectedId = - isAutoSelect ? 'auto-select' : model.id; + final selectedId = isAutoSelect + ? 'auto-select' + : model.id; // Return selection immediately; caller handles persisting Navigator.pop(context, selectedId); }, @@ -1070,13 +1095,19 @@ class _DefaultModelBottomSheetState extends ConsumerState<_DefaultModelBottomShe width: 32, height: 32, decoration: BoxDecoration( - color: context.conduitTheme.buttonPrimary.withValues(alpha: 0.15), + color: context.conduitTheme.buttonPrimary.withValues( + alpha: 0.15, + ), borderRadius: BorderRadius.circular(AppBorderRadius.md), ), child: Icon( - isAutoSelect - ? (Platform.isIOS ? CupertinoIcons.wand_stars : Icons.auto_awesome) - : (Platform.isIOS ? CupertinoIcons.cube : Icons.psychology), + isAutoSelect + ? (Platform.isIOS + ? CupertinoIcons.wand_stars + : Icons.auto_awesome) + : (Platform.isIOS + ? CupertinoIcons.cube + : Icons.psychology), color: context.conduitTheme.buttonPrimary, size: 16, ), @@ -1087,7 +1118,9 @@ class _DefaultModelBottomSheetState extends ConsumerState<_DefaultModelBottomShe crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - isAutoSelect ? AppLocalizations.of(context)!.autoSelect : model.name, + isAutoSelect + ? AppLocalizations.of(context)!.autoSelect + : model.name, style: TextStyle( color: context.conduitTheme.textPrimary, fontWeight: FontWeight.w600, @@ -1143,13 +1176,17 @@ class _DefaultModelBottomSheetState extends ConsumerState<_DefaultModelBottomShe borderRadius: BorderRadius.circular(AppBorderRadius.md), border: Border.all( color: isSelected - ? context.conduitTheme.buttonPrimary.withValues(alpha: 0.6) + ? context.conduitTheme.buttonPrimary.withValues( + alpha: 0.6, + ) : context.conduitTheme.dividerColor, ), ), child: Icon( isSelected - ? (Platform.isIOS ? CupertinoIcons.check_mark : Icons.check) + ? (Platform.isIOS + ? CupertinoIcons.check_mark + : Icons.check) : (Platform.isIOS ? CupertinoIcons.add : Icons.add), color: isSelected ? context.conduitTheme.textInverse diff --git a/lib/features/tools/widgets/unified_tools_modal.dart b/lib/features/tools/widgets/unified_tools_modal.dart index 73c0da1..b5979c9 100644 --- a/lib/features/tools/widgets/unified_tools_modal.dart +++ b/lib/features/tools/widgets/unified_tools_modal.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:conduit/l10n/app_localizations.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -59,9 +60,10 @@ class _UnifiedToolsModalState extends ConsumerState { Column( children: [ _buildFeatureTile( - title: 'Web Search', - description: - 'Let the assistant search the internet while answering.', + title: AppLocalizations.of(context)!.webSearch, + description: AppLocalizations.of( + context, + )!.webSearchDescription, icon: Platform.isIOS ? CupertinoIcons.search : Icons.search, @@ -74,9 +76,10 @@ class _UnifiedToolsModalState extends ConsumerState { ), if (imageGenAvailable) _buildFeatureTile( - title: 'Image Generation', - description: - 'Generate images from your prompt and attach them.', + title: AppLocalizations.of(context)!.imageGeneration, + description: AppLocalizations.of( + context, + )!.imageGenerationDescription, icon: Platform.isIOS ? CupertinoIcons.photo : Icons.image, diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index f1f7c2e..4dd1cd3 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -64,7 +64,8 @@ import 'app_localizations_it.dart'; /// be consistent with the languages listed in the AppLocalizations.supportedLocales /// property. abstract class AppLocalizations { - AppLocalizations(String locale) : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + AppLocalizations(String locale) + : localeName = intl.Intl.canonicalizedLocale(locale.toString()); final String localeName; @@ -72,7 +73,8 @@ abstract class AppLocalizations { return Localizations.of(context, AppLocalizations); } - static const LocalizationsDelegate delegate = _AppLocalizationsDelegate(); + static const LocalizationsDelegate delegate = + _AppLocalizationsDelegate(); /// A list of this localizations delegate along with the default localizations /// delegates. @@ -84,19 +86,20 @@ abstract class AppLocalizations { /// Additional delegates can be added by appending to this list in /// MaterialApp. This list does not have to be used at all if a custom list /// of delegates is preferred or required. - static const List> localizationsDelegates = >[ - delegate, - GlobalMaterialLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - ]; + static const List> localizationsDelegates = + >[ + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; /// A list of this localizations delegate's supported locales. static const List supportedLocales = [ Locale('de'), Locale('en'), Locale('fr'), - Locale('it') + Locale('it'), ]; /// No description provided for @appTitle. @@ -1190,9 +1193,154 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'Conduit information and links'** String get aboutAppSubtitle; + + /// No description provided for @appLanguage. + /// + /// In en, this message translates to: + /// **'App language'** + String get appLanguage; + + /// No description provided for @typeBelowToBegin. + /// + /// In en, this message translates to: + /// **'Type below to begin'** + String get typeBelowToBegin; + + /// No description provided for @listening. + /// + /// In en, this message translates to: + /// **'Listening…'** + String get listening; + + /// No description provided for @recording. + /// + /// In en, this message translates to: + /// **'Recording…'** + String get recording; + + /// No description provided for @transcribing. + /// + /// In en, this message translates to: + /// **'Transcribing…'** + String get transcribing; + + /// No description provided for @speakNow. + /// + /// In en, this message translates to: + /// **'Speak now…'** + String get speakNow; + + /// No description provided for @chats. + /// + /// In en, this message translates to: + /// **'Chats'** + String get chats; + + /// No description provided for @darkMode. + /// + /// In en, this message translates to: + /// **'Dark Mode'** + String get darkMode; + + /// No description provided for @transcript. + /// + /// In en, this message translates to: + /// **'Transcript'** + String get transcript; + + /// No description provided for @pinned. + /// + /// In en, this message translates to: + /// **'Pinned'** + String get pinned; + + /// No description provided for @folders. + /// + /// In en, this message translates to: + /// **'Folders'** + String get folders; + + /// No description provided for @archived. + /// + /// In en, this message translates to: + /// **'Archived'** + String get archived; + + /// No description provided for @holdToTalk. + /// + /// In en, this message translates to: + /// **'Hold to talk'** + String get holdToTalk; + + /// No description provided for @autoSend. + /// + /// In en, this message translates to: + /// **'Auto-send'** + String get autoSend; + + /// No description provided for @stopListening. + /// + /// In en, this message translates to: + /// **'Stop listening'** + String get stopListening; + + /// No description provided for @startListening. + /// + /// In en, this message translates to: + /// **'Start listening'** + String get startListening; + + /// No description provided for @start. + /// + /// In en, this message translates to: + /// **'Start'** + String get start; + + /// No description provided for @stop. + /// + /// In en, this message translates to: + /// **'Stop'** + String get stop; + + /// No description provided for @web. + /// + /// In en, this message translates to: + /// **'Web'** + String get web; + + /// No description provided for @imageGen. + /// + /// In en, this message translates to: + /// **'Image Gen'** + String get imageGen; + + /// No description provided for @webSearch. + /// + /// In en, this message translates to: + /// **'Web Search'** + String get webSearch; + + /// No description provided for @webSearchDescription. + /// + /// In en, this message translates to: + /// **'Let the assistant search the internet while answering.'** + String get webSearchDescription; + + /// No description provided for @imageGeneration. + /// + /// In en, this message translates to: + /// **'Image Generation'** + String get imageGeneration; + + /// No description provided for @imageGenerationDescription. + /// + /// In en, this message translates to: + /// **'Generate images from your prompt and attach them.'** + String get imageGenerationDescription; } -class _AppLocalizationsDelegate extends LocalizationsDelegate { +class _AppLocalizationsDelegate + extends LocalizationsDelegate { const _AppLocalizationsDelegate(); @override @@ -1201,27 +1349,30 @@ class _AppLocalizationsDelegate extends LocalizationsDelegate } @override - bool isSupported(Locale locale) => ['de', 'en', 'fr', 'it'].contains(locale.languageCode); + bool isSupported(Locale locale) => + ['de', 'en', 'fr', 'it'].contains(locale.languageCode); @override bool shouldReload(_AppLocalizationsDelegate old) => false; } AppLocalizations lookupAppLocalizations(Locale locale) { - - // Lookup logic when only language code is specified. switch (locale.languageCode) { - case 'de': return AppLocalizationsDe(); - case 'en': return AppLocalizationsEn(); - case 'fr': return AppLocalizationsFr(); - case 'it': return AppLocalizationsIt(); + case 'de': + return AppLocalizationsDe(); + case 'en': + return AppLocalizationsEn(); + case 'fr': + return AppLocalizationsFr(); + case 'it': + return AppLocalizationsIt(); } throw FlutterError( 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' 'an issue with the localizations generation tool. Please file an issue ' 'on GitHub with a reproducible sample app and the gen-l10n configuration ' - 'that was used.' + 'that was used.', ); } diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index cbd39d2..5fddf73 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -30,7 +30,8 @@ class AppLocalizationsDe extends AppLocalizations { String get unableToLoadProfile => 'Profil konnte nicht geladen werden'; @override - String get pleaseCheckConnection => 'Bitte überprüfe deine Verbindung und versuche es erneut'; + String get pleaseCheckConnection => + 'Bitte überprüfe deine Verbindung und versuche es erneut'; @override String get account => 'Konto'; @@ -63,7 +64,8 @@ class AppLocalizationsDe extends AppLocalizations { String get searchModels => 'Modelle suchen...'; @override - String get errorMessage => 'Etwas ist schief gelaufen. Bitte versuche es erneut.'; + String get errorMessage => + 'Etwas ist schief gelaufen. Bitte versuche es erneut.'; @override String get loginButton => 'Anmelden'; @@ -116,7 +118,8 @@ class AppLocalizationsDe extends AppLocalizations { String get noFilesYet => 'Noch keine Dateien'; @override - String get uploadDocsPrompt => 'Lade Dokumente hoch, um sie in deinen Unterhaltungen mit Conduit zu verwenden'; + String get uploadDocsPrompt => + 'Lade Dokumente hoch, um sie in deinen Unterhaltungen mit Conduit zu verwenden'; @override String get uploadFirstFile => 'Erste Datei hochladen'; @@ -125,7 +128,8 @@ class AppLocalizationsDe extends AppLocalizations { String get knowledgeBaseEmpty => 'Wissensdatenbank ist leer'; @override - String get createCollectionsPrompt => 'Erstelle Sammlungen verwandter Dokumente zur einfachen Referenz'; + String get createCollectionsPrompt => + 'Erstelle Sammlungen verwandter Dokumente zur einfachen Referenz'; @override String get chooseSourcePhoto => 'Quelle auswählen'; @@ -151,7 +155,8 @@ class AppLocalizationsDe extends AppLocalizations { } @override - String get kbCreationComingSoon => 'Erstellung der Wissensdatenbank kommt bald!'; + String get kbCreationComingSoon => + 'Erstellung der Wissensdatenbank kommt bald!'; @override String get backToServerSetup => 'Zur Servereinrichtung zurück'; @@ -163,7 +168,8 @@ class AppLocalizationsDe extends AppLocalizations { String get signIn => 'Anmelden'; @override - String get enterCredentials => 'Gib deine Anmeldedaten ein, um auf deine KI-Unterhaltungen zuzugreifen'; + String get enterCredentials => + 'Gib deine Anmeldedaten ein, um auf deine KI-Unterhaltungen zuzugreifen'; @override String get credentials => 'Zugangsdaten'; @@ -184,7 +190,8 @@ class AppLocalizationsDe extends AppLocalizations { String get connectToServer => 'Mit Server verbinden'; @override - String get enterServerAddress => 'Gib die Adresse deines Open-WebUI-Servers ein, um zu beginnen'; + String get enterServerAddress => + 'Gib die Adresse deines Open-WebUI-Servers ein, um zu beginnen'; @override String get serverUrl => 'Server-URL'; @@ -193,7 +200,8 @@ class AppLocalizationsDe extends AppLocalizations { String get serverUrlHint => 'https://dein-server.com'; @override - String get enterServerUrlSemantic => 'Gib deine Server-URL oder IP-Adresse ein'; + String get enterServerUrlSemantic => + 'Gib deine Server-URL oder IP-Adresse ein'; @override String get headerName => 'Header-Name'; @@ -223,7 +231,8 @@ class AppLocalizationsDe extends AppLocalizations { String get demoModeActive => 'Demo-Modus aktiv'; @override - String get skipServerSetupTryDemo => 'Servereinrichtung überspringen und Demo testen'; + String get skipServerSetupTryDemo => + 'Servereinrichtung überspringen und Demo testen'; @override String get enterDemo => 'Demo starten'; @@ -232,7 +241,8 @@ class AppLocalizationsDe extends AppLocalizations { String get demoBadge => 'Demo'; @override - String get serverNotOpenWebUI => 'Dies scheint kein Open-WebUI-Server zu sein.'; + String get serverNotOpenWebUI => + 'Dies scheint kein Open-WebUI-Server zu sein.'; @override String get serverUrlEmpty => 'Server-URL darf nicht leer sein'; @@ -241,10 +251,12 @@ class AppLocalizationsDe extends AppLocalizations { String get invalidUrlFormat => 'Ungültiges URL-Format. Bitte Eingabe prüfen.'; @override - String get onlyHttpHttps => 'Nur HTTP- und HTTPS-Protokolle werden unterstützt.'; + String get onlyHttpHttps => + 'Nur HTTP- und HTTPS-Protokolle werden unterstützt.'; @override - String get serverAddressRequired => 'Serveradresse erforderlich (z. B. 192.168.1.10 oder example.com).'; + String get serverAddressRequired => + 'Serveradresse erforderlich (z. B. 192.168.1.10 oder example.com).'; @override String get portRange => 'Port muss zwischen 1 und 65535 liegen.'; @@ -253,13 +265,16 @@ class AppLocalizationsDe extends AppLocalizations { String get invalidIpFormat => 'Ungültiges IP-Format. Beispiel: 192.168.1.10.'; @override - String get couldNotConnectGeneric => 'Verbindung fehlgeschlagen. Adresse prüfen und erneut versuchen.'; + String get couldNotConnectGeneric => + 'Verbindung fehlgeschlagen. Adresse prüfen und erneut versuchen.'; @override - String get weCouldntReachServer => 'Server nicht erreichbar. Verbindung und Serverstatus prüfen.'; + String get weCouldntReachServer => + 'Server nicht erreichbar. Verbindung und Serverstatus prüfen.'; @override - String get connectionTimedOut => 'Zeitüberschreitung. Server eventuell ausgelastet oder blockiert.'; + String get connectionTimedOut => + 'Zeitüberschreitung. Server eventuell ausgelastet oder blockiert.'; @override String get useHttpOrHttpsOnly => 'Nur http:// oder https:// verwenden.'; @@ -268,19 +283,23 @@ class AppLocalizationsDe extends AppLocalizations { String get loginFailed => 'Anmeldung fehlgeschlagen'; @override - String get invalidCredentials => 'Ungültiger Benutzername oder Passwort. Bitte erneut versuchen.'; + String get invalidCredentials => + 'Ungültiger Benutzername oder Passwort. Bitte erneut versuchen.'; @override - String get serverRedirectingHttps => 'Server leitet um. HTTPS-Konfiguration prüfen.'; + String get serverRedirectingHttps => + 'Server leitet um. HTTPS-Konfiguration prüfen.'; @override - String get unableToConnectServer => 'Verbindung zum Server nicht möglich. Bitte Verbindung prüfen.'; + String get unableToConnectServer => + 'Verbindung zum Server nicht möglich. Bitte Verbindung prüfen.'; @override String get requestTimedOut => 'Zeitüberschreitung. Bitte erneut versuchen.'; @override - String get genericSignInFailed => 'Anmeldung nicht möglich. Zugangsdaten und Server prüfen.'; + String get genericSignInFailed => + 'Anmeldung nicht möglich. Zugangsdaten und Server prüfen.'; @override String get skip => 'Überspringen'; @@ -295,7 +314,8 @@ class AppLocalizationsDe extends AppLocalizations { String get onboardStartTitle => 'Unterhaltung starten'; @override - String get onboardStartSubtitle => 'Wähle ein Modell und tippe los. Tippe jederzeit auf Neuer Chat.'; + String get onboardStartSubtitle => + 'Wähle ein Modell und tippe los. Tippe jederzeit auf Neuer Chat.'; @override String get onboardStartBullet1 => 'Modellname oben antippen, um zu wechseln'; @@ -307,7 +327,8 @@ class AppLocalizationsDe extends AppLocalizations { String get onboardAttachTitle => 'Kontext anhängen'; @override - String get onboardAttachSubtitle => 'Antworten mit Dateien oder Bildern untermauern.'; + String get onboardAttachSubtitle => + 'Antworten mit Dateien oder Bildern untermauern.'; @override String get onboardAttachBullet1 => 'Dateien: PDFs, Dokumente, Datensätze'; @@ -325,19 +346,23 @@ class AppLocalizationsDe extends AppLocalizations { String get onboardSpeakBullet1 => 'Jederzeit stoppen; Text bleibt erhalten'; @override - String get onboardSpeakBullet2 => 'Ideal für kurze Notizen oder lange Prompts'; + String get onboardSpeakBullet2 => + 'Ideal für kurze Notizen oder lange Prompts'; @override String get onboardQuickTitle => 'Schnellaktionen'; @override - String get onboardQuickSubtitle => 'Links oben das Menü für Chats und Navigation öffnen.'; + String get onboardQuickSubtitle => + 'Links oben das Menü für Chats und Navigation öffnen.'; @override - String get onboardQuickBullet1 => 'Menü tippen, um Chats und Navigation zu öffnen'; + String get onboardQuickBullet1 => + 'Menü tippen, um Chats und Navigation zu öffnen'; @override - String get onboardQuickBullet2 => 'Schnell zu Neuer Chat, Dateien oder Profil springen'; + String get onboardQuickBullet2 => + 'Schnell zu Neuer Chat, Dateien oder Profil springen'; @override String get addAttachment => 'Anhang hinzufügen'; @@ -404,13 +429,16 @@ class AppLocalizationsDe extends AppLocalizations { String get emptyImageData => 'Leere Bilddaten'; @override - String get offlineBanner => 'Du bist offline. Einige Funktionen sind eingeschränkt.'; + String get offlineBanner => + 'Du bist offline. Einige Funktionen sind eingeschränkt.'; @override - String get featureRequiresInternet => 'Diese Funktion erfordert eine Internetverbindung'; + String get featureRequiresInternet => + 'Diese Funktion erfordert eine Internetverbindung'; @override - String get messagesWillSendWhenOnline => 'Nachrichten werden gesendet, sobald du wieder online bist'; + String get messagesWillSendWhenOnline => + 'Nachrichten werden gesendet, sobald du wieder online bist'; @override String get confirm => 'Bestätigen'; @@ -576,4 +604,78 @@ class AppLocalizationsDe extends AppLocalizations { @override String get aboutAppSubtitle => 'Conduit Informationen und Links'; + + @override + String get appLanguage => 'App-Sprache'; + + @override + String get typeBelowToBegin => 'Unten tippen, um zu beginnen'; + + @override + String get listening => 'Zuhören…'; + + @override + String get recording => 'Aufnahme…'; + + @override + String get transcribing => 'Transkription…'; + + @override + String get speakNow => 'Jetzt sprechen…'; + + @override + String get chats => 'Chats'; + + @override + String get darkMode => 'Dunkler Modus'; + + @override + String get transcript => 'Transkript'; + + @override + String get pinned => 'Angeheftet'; + + @override + String get folders => 'Ordner'; + + @override + String get archived => 'Archiviert'; + + @override + String get holdToTalk => 'Zum Sprechen halten'; + + @override + String get autoSend => 'Automatisch senden'; + + @override + String get stopListening => 'Zuhören stoppen'; + + @override + String get startListening => 'Zuhören starten'; + + @override + String get start => 'Start'; + + @override + String get stop => 'Stopp'; + + @override + String get web => 'Web'; + + @override + String get imageGen => 'Bild-Gen'; + + @override + String get webSearch => 'Websuche'; + + @override + String get webSearchDescription => + 'Lassen Sie den Assistenten beim Antworten im Internet suchen.'; + + @override + String get imageGeneration => 'Bilderzeugung'; + + @override + String get imageGenerationDescription => + 'Bilder aus Ihrer Eingabe generieren und anhängen.'; } diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 48713d8..fd048bc 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -30,7 +30,8 @@ class AppLocalizationsEn extends AppLocalizations { String get unableToLoadProfile => 'Unable to load profile'; @override - String get pleaseCheckConnection => 'Please check your connection and try again'; + String get pleaseCheckConnection => + 'Please check your connection and try again'; @override String get account => 'Account'; @@ -116,7 +117,8 @@ class AppLocalizationsEn extends AppLocalizations { String get noFilesYet => 'No files yet'; @override - String get uploadDocsPrompt => 'Upload documents to reference in your conversations with Conduit'; + String get uploadDocsPrompt => + 'Upload documents to reference in your conversations with Conduit'; @override String get uploadFirstFile => 'Upload your first file'; @@ -125,7 +127,8 @@ class AppLocalizationsEn extends AppLocalizations { String get knowledgeBaseEmpty => 'Knowledge base is empty'; @override - String get createCollectionsPrompt => 'Create collections of related documents for easy reference'; + String get createCollectionsPrompt => + 'Create collections of related documents for easy reference'; @override String get chooseSourcePhoto => 'Choose your source'; @@ -163,7 +166,8 @@ class AppLocalizationsEn extends AppLocalizations { String get signIn => 'Sign In'; @override - String get enterCredentials => 'Enter your credentials to access your AI conversations'; + String get enterCredentials => + 'Enter your credentials to access your AI conversations'; @override String get credentials => 'Credentials'; @@ -184,7 +188,8 @@ class AppLocalizationsEn extends AppLocalizations { String get connectToServer => 'Connect to Server'; @override - String get enterServerAddress => 'Enter your Open-WebUI server address to get started'; + String get enterServerAddress => + 'Enter your Open-WebUI server address to get started'; @override String get serverUrl => 'Server URL'; @@ -232,7 +237,8 @@ class AppLocalizationsEn extends AppLocalizations { String get demoBadge => 'Demo'; @override - String get serverNotOpenWebUI => 'This does not appear to be an Open-WebUI server.'; + String get serverNotOpenWebUI => + 'This does not appear to be an Open-WebUI server.'; @override String get serverUrlEmpty => 'Server URL cannot be empty'; @@ -244,22 +250,27 @@ class AppLocalizationsEn extends AppLocalizations { String get onlyHttpHttps => 'Only HTTP and HTTPS protocols are supported.'; @override - String get serverAddressRequired => 'Server address is required (e.g., 192.168.1.10 or example.com).'; + String get serverAddressRequired => + 'Server address is required (e.g., 192.168.1.10 or example.com).'; @override String get portRange => 'Port must be between 1 and 65535.'; @override - String get invalidIpFormat => 'Invalid IP address format. Use format like 192.168.1.10.'; + String get invalidIpFormat => + 'Invalid IP address format. Use format like 192.168.1.10.'; @override - String get couldNotConnectGeneric => 'Couldn\'t connect. Double-check the address and try again.'; + String get couldNotConnectGeneric => + 'Couldn\'t connect. Double-check the address and try again.'; @override - String get weCouldntReachServer => 'We couldn\'t reach the server. Check your connection and that the server is running.'; + String get weCouldntReachServer => + 'We couldn\'t reach the server. Check your connection and that the server is running.'; @override - String get connectionTimedOut => 'Connection timed out. The server might be busy or blocked by a firewall.'; + String get connectionTimedOut => + 'Connection timed out. The server might be busy or blocked by a firewall.'; @override String get useHttpOrHttpsOnly => 'Use http:// or https:// only.'; @@ -268,19 +279,23 @@ class AppLocalizationsEn extends AppLocalizations { String get loginFailed => 'Login failed'; @override - String get invalidCredentials => 'Invalid username or password. Please try again.'; + String get invalidCredentials => + 'Invalid username or password. Please try again.'; @override - String get serverRedirectingHttps => 'The server is redirecting requests. Check your server\'s HTTPS configuration.'; + String get serverRedirectingHttps => + 'The server is redirecting requests. Check your server\'s HTTPS configuration.'; @override - String get unableToConnectServer => 'Unable to connect to server. Please check your connection.'; + String get unableToConnectServer => + 'Unable to connect to server. Please check your connection.'; @override String get requestTimedOut => 'The request timed out. Please try again.'; @override - String get genericSignInFailed => 'We couldn\'t sign you in. Check your credentials and server settings.'; + String get genericSignInFailed => + 'We couldn\'t sign you in. Check your credentials and server settings.'; @override String get skip => 'Skip'; @@ -295,10 +310,12 @@ class AppLocalizationsEn extends AppLocalizations { String get onboardStartTitle => 'Start a conversation'; @override - String get onboardStartSubtitle => 'Choose a model, then type below to begin. Tap New Chat anytime.'; + String get onboardStartSubtitle => + 'Choose a model, then type below to begin. Tap New Chat anytime.'; @override - String get onboardStartBullet1 => 'Tap the model name in the top bar to switch models'; + String get onboardStartBullet1 => + 'Tap the model name in the top bar to switch models'; @override String get onboardStartBullet2 => 'Use New Chat to reset context'; @@ -307,7 +324,8 @@ class AppLocalizationsEn extends AppLocalizations { String get onboardAttachTitle => 'Attach context'; @override - String get onboardAttachSubtitle => 'Ground responses by adding files or images.'; + String get onboardAttachSubtitle => + 'Ground responses by adding files or images.'; @override String get onboardAttachBullet1 => 'Files: PDFs, docs, datasets'; @@ -319,7 +337,8 @@ class AppLocalizationsEn extends AppLocalizations { String get onboardSpeakTitle => 'Speak naturally'; @override - String get onboardSpeakSubtitle => 'Tap the mic to dictate with live waveform feedback.'; + String get onboardSpeakSubtitle => + 'Tap the mic to dictate with live waveform feedback.'; @override String get onboardSpeakBullet1 => 'Stop anytime; partial text is preserved'; @@ -331,13 +350,16 @@ class AppLocalizationsEn extends AppLocalizations { String get onboardQuickTitle => 'Quick actions'; @override - String get onboardQuickSubtitle => 'Use the top‑left menu to open the chats list and navigation.'; + String get onboardQuickSubtitle => + 'Use the top‑left menu to open the chats list and navigation.'; @override - String get onboardQuickBullet1 => 'Tap the menu to open the chats list and navigation'; + String get onboardQuickBullet1 => + 'Tap the menu to open the chats list and navigation'; @override - String get onboardQuickBullet2 => 'Jump instantly to New Chat, Files, or Profile'; + String get onboardQuickBullet2 => + 'Jump instantly to New Chat, Files, or Profile'; @override String get addAttachment => 'Add attachment'; @@ -407,10 +429,12 @@ class AppLocalizationsEn extends AppLocalizations { String get offlineBanner => 'You\'re offline. Some features may be limited.'; @override - String get featureRequiresInternet => 'This feature requires an internet connection'; + String get featureRequiresInternet => + 'This feature requires an internet connection'; @override - String get messagesWillSendWhenOnline => 'Messages will be sent when you\'re back online'; + String get messagesWillSendWhenOnline => + 'Messages will be sent when you\'re back online'; @override String get confirm => 'Confirm'; @@ -576,4 +600,78 @@ class AppLocalizationsEn extends AppLocalizations { @override String get aboutAppSubtitle => 'Conduit information and links'; + + @override + String get appLanguage => 'App language'; + + @override + String get typeBelowToBegin => 'Type below to begin'; + + @override + String get listening => 'Listening…'; + + @override + String get recording => 'Recording…'; + + @override + String get transcribing => 'Transcribing…'; + + @override + String get speakNow => 'Speak now…'; + + @override + String get chats => 'Chats'; + + @override + String get darkMode => 'Dark Mode'; + + @override + String get transcript => 'Transcript'; + + @override + String get pinned => 'Pinned'; + + @override + String get folders => 'Folders'; + + @override + String get archived => 'Archived'; + + @override + String get holdToTalk => 'Hold to talk'; + + @override + String get autoSend => 'Auto-send'; + + @override + String get stopListening => 'Stop listening'; + + @override + String get startListening => 'Start listening'; + + @override + String get start => 'Start'; + + @override + String get stop => 'Stop'; + + @override + String get web => 'Web'; + + @override + String get imageGen => 'Image Gen'; + + @override + String get webSearch => 'Web Search'; + + @override + String get webSearchDescription => + 'Let the assistant search the internet while answering.'; + + @override + String get imageGeneration => 'Image Generation'; + + @override + String get imageGenerationDescription => + 'Generate images from your prompt and attach them.'; } diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index 2c6eac8..395bbb9 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -30,7 +30,8 @@ class AppLocalizationsFr extends AppLocalizations { String get unableToLoadProfile => 'Impossible de charger le profil'; @override - String get pleaseCheckConnection => 'Veuillez vérifier votre connexion et réessayer'; + String get pleaseCheckConnection => + 'Veuillez vérifier votre connexion et réessayer'; @override String get account => 'Compte'; @@ -116,7 +117,8 @@ class AppLocalizationsFr extends AppLocalizations { String get noFilesYet => 'Pas encore de fichiers'; @override - String get uploadDocsPrompt => 'Importez des documents à utiliser dans vos conversations avec Conduit'; + String get uploadDocsPrompt => + 'Importez des documents à utiliser dans vos conversations avec Conduit'; @override String get uploadFirstFile => 'Importer votre premier fichier'; @@ -125,7 +127,8 @@ class AppLocalizationsFr extends AppLocalizations { String get knowledgeBaseEmpty => 'La base de connaissances est vide'; @override - String get createCollectionsPrompt => 'Créez des collections de documents liés pour une référence facile'; + String get createCollectionsPrompt => + 'Créez des collections de documents liés pour une référence facile'; @override String get chooseSourcePhoto => 'Choisir la source'; @@ -151,7 +154,8 @@ class AppLocalizationsFr extends AppLocalizations { } @override - String get kbCreationComingSoon => 'La création de la base de connaissances arrive bientôt !'; + String get kbCreationComingSoon => + 'La création de la base de connaissances arrive bientôt !'; @override String get backToServerSetup => 'Retour à la configuration du serveur'; @@ -163,7 +167,8 @@ class AppLocalizationsFr extends AppLocalizations { String get signIn => 'Se connecter'; @override - String get enterCredentials => 'Entrez vos identifiants pour accéder à vos conversations IA'; + String get enterCredentials => + 'Entrez vos identifiants pour accéder à vos conversations IA'; @override String get credentials => 'Identifiants'; @@ -184,7 +189,8 @@ class AppLocalizationsFr extends AppLocalizations { String get connectToServer => 'Se connecter au serveur'; @override - String get enterServerAddress => 'Saisissez l\'adresse de votre serveur Open-WebUI pour commencer'; + String get enterServerAddress => + 'Saisissez l\'adresse de votre serveur Open-WebUI pour commencer'; @override String get serverUrl => 'URL du serveur'; @@ -193,7 +199,8 @@ class AppLocalizationsFr extends AppLocalizations { String get serverUrlHint => 'https://votre-serveur.com'; @override - String get enterServerUrlSemantic => 'Saisissez l\'URL ou l\'adresse IP de votre serveur'; + String get enterServerUrlSemantic => + 'Saisissez l\'URL ou l\'adresse IP de votre serveur'; @override String get headerName => 'Nom de l\'en-tête'; @@ -223,7 +230,8 @@ class AppLocalizationsFr extends AppLocalizations { String get demoModeActive => 'Mode démo activé'; @override - String get skipServerSetupTryDemo => 'Ignorer la configuration et essayer la démo'; + String get skipServerSetupTryDemo => + 'Ignorer la configuration et essayer la démo'; @override String get enterDemo => 'Entrer en démo'; @@ -232,34 +240,42 @@ class AppLocalizationsFr extends AppLocalizations { String get demoBadge => 'Démo'; @override - String get serverNotOpenWebUI => 'Ceci ne semble pas être un serveur Open-WebUI.'; + String get serverNotOpenWebUI => + 'Ceci ne semble pas être un serveur Open-WebUI.'; @override String get serverUrlEmpty => 'L\'URL du serveur ne peut pas être vide'; @override - String get invalidUrlFormat => 'Format d\'URL invalide. Veuillez vérifier votre saisie.'; + String get invalidUrlFormat => + 'Format d\'URL invalide. Veuillez vérifier votre saisie.'; @override - String get onlyHttpHttps => 'Seuls les protocoles HTTP et HTTPS sont pris en charge.'; + String get onlyHttpHttps => + 'Seuls les protocoles HTTP et HTTPS sont pris en charge.'; @override - String get serverAddressRequired => 'Adresse du serveur requise (ex. 192.168.1.10 ou example.com).'; + String get serverAddressRequired => + 'Adresse du serveur requise (ex. 192.168.1.10 ou example.com).'; @override String get portRange => 'Le port doit être compris entre 1 et 65535.'; @override - String get invalidIpFormat => 'Format d\'IP invalide. Exemple : 192.168.1.10.'; + String get invalidIpFormat => + 'Format d\'IP invalide. Exemple : 192.168.1.10.'; @override - String get couldNotConnectGeneric => 'Connexion impossible. Vérifiez l\'adresse et réessayez.'; + String get couldNotConnectGeneric => + 'Connexion impossible. Vérifiez l\'adresse et réessayez.'; @override - String get weCouldntReachServer => 'Impossible d\'atteindre le serveur. Vérifiez la connexion et l\'état du serveur.'; + String get weCouldntReachServer => + 'Impossible d\'atteindre le serveur. Vérifiez la connexion et l\'état du serveur.'; @override - String get connectionTimedOut => 'Délai d\'attente dépassé. Le serveur est peut-être occupé ou bloqué.'; + String get connectionTimedOut => + 'Délai d\'attente dépassé. Le serveur est peut-être occupé ou bloqué.'; @override String get useHttpOrHttpsOnly => 'Utilisez uniquement http:// ou https://.'; @@ -268,19 +284,23 @@ class AppLocalizationsFr extends AppLocalizations { String get loginFailed => 'Échec de la connexion'; @override - String get invalidCredentials => 'Nom d\'utilisateur ou mot de passe invalide. Réessayez.'; + String get invalidCredentials => + 'Nom d\'utilisateur ou mot de passe invalide. Réessayez.'; @override - String get serverRedirectingHttps => 'Le serveur redirige les requêtes. Vérifiez la configuration HTTPS.'; + String get serverRedirectingHttps => + 'Le serveur redirige les requêtes. Vérifiez la configuration HTTPS.'; @override - String get unableToConnectServer => 'Impossible de se connecter au serveur. Vérifiez votre connexion.'; + String get unableToConnectServer => + 'Impossible de se connecter au serveur. Vérifiez votre connexion.'; @override String get requestTimedOut => 'Délai d\'attente dépassé. Réessayez.'; @override - String get genericSignInFailed => 'Connexion impossible. Vérifiez vos identifiants et le serveur.'; + String get genericSignInFailed => + 'Connexion impossible. Vérifiez vos identifiants et le serveur.'; @override String get skip => 'Ignorer'; @@ -295,22 +315,27 @@ class AppLocalizationsFr extends AppLocalizations { String get onboardStartTitle => 'Commencer une conversation'; @override - String get onboardStartSubtitle => 'Choisissez un modèle puis commencez à écrire. Touchez Nouveau chat à tout moment.'; + String get onboardStartSubtitle => + 'Choisissez un modèle puis commencez à écrire. Touchez Nouveau chat à tout moment.'; @override - String get onboardStartBullet1 => 'Touchez le nom du modèle en haut pour changer'; + String get onboardStartBullet1 => + 'Touchez le nom du modèle en haut pour changer'; @override - String get onboardStartBullet2 => 'Utilisez Nouveau chat pour réinitialiser le contexte'; + String get onboardStartBullet2 => + 'Utilisez Nouveau chat pour réinitialiser le contexte'; @override String get onboardAttachTitle => 'Ajouter du contexte'; @override - String get onboardAttachSubtitle => 'Améliorez les réponses en ajoutant des fichiers ou des images.'; + String get onboardAttachSubtitle => + 'Améliorez les réponses en ajoutant des fichiers ou des images.'; @override - String get onboardAttachBullet1 => 'Fichiers : PDF, documents, jeux de données'; + String get onboardAttachBullet1 => + 'Fichiers : PDF, documents, jeux de données'; @override String get onboardAttachBullet2 => 'Images : photos ou captures d\'écran'; @@ -319,25 +344,31 @@ class AppLocalizationsFr extends AppLocalizations { String get onboardSpeakTitle => 'Parlez naturellement'; @override - String get onboardSpeakSubtitle => 'Touchez le micro pour dicter avec retour visuel.'; + String get onboardSpeakSubtitle => + 'Touchez le micro pour dicter avec retour visuel.'; @override - String get onboardSpeakBullet1 => 'Arrêtez à tout moment ; le texte partiel est conservé'; + String get onboardSpeakBullet1 => + 'Arrêtez à tout moment ; le texte partiel est conservé'; @override - String get onboardSpeakBullet2 => 'Idéal pour des notes rapides ou de longs prompts'; + String get onboardSpeakBullet2 => + 'Idéal pour des notes rapides ou de longs prompts'; @override String get onboardQuickTitle => 'Actions rapides'; @override - String get onboardQuickSubtitle => 'Utilisez le menu en haut à gauche pour ouvrir la liste des chats et la navigation.'; + String get onboardQuickSubtitle => + 'Utilisez le menu en haut à gauche pour ouvrir la liste des chats et la navigation.'; @override - String get onboardQuickBullet1 => 'Touchez le menu pour ouvrir les chats et la navigation'; + String get onboardQuickBullet1 => + 'Touchez le menu pour ouvrir les chats et la navigation'; @override - String get onboardQuickBullet2 => 'Accédez rapidement à Nouveau chat, Fichiers ou Profil'; + String get onboardQuickBullet2 => + 'Accédez rapidement à Nouveau chat, Fichiers ou Profil'; @override String get addAttachment => 'Ajouter une pièce jointe'; @@ -404,13 +435,16 @@ class AppLocalizationsFr extends AppLocalizations { String get emptyImageData => 'Données d\'image vides'; @override - String get offlineBanner => 'Vous êtes hors ligne. Certaines fonctions peuvent être limitées.'; + String get offlineBanner => + 'Vous êtes hors ligne. Certaines fonctions peuvent être limitées.'; @override - String get featureRequiresInternet => 'Cette fonctionnalité nécessite une connexion Internet'; + String get featureRequiresInternet => + 'Cette fonctionnalité nécessite une connexion Internet'; @override - String get messagesWillSendWhenOnline => 'Les messages seront envoyés lorsque vous serez de nouveau en ligne'; + String get messagesWillSendWhenOnline => + 'Les messages seront envoyés lorsque vous serez de nouveau en ligne'; @override String get confirm => 'Confirmer'; @@ -576,4 +610,78 @@ class AppLocalizationsFr extends AppLocalizations { @override String get aboutAppSubtitle => 'Informations et liens Conduit'; + + @override + String get appLanguage => 'Langue de l\'app'; + + @override + String get typeBelowToBegin => 'Tapez ci-dessous pour commencer'; + + @override + String get listening => 'Écoute…'; + + @override + String get recording => 'Enregistrement…'; + + @override + String get transcribing => 'Transcription…'; + + @override + String get speakNow => 'Parlez maintenant…'; + + @override + String get chats => 'Discussions'; + + @override + String get darkMode => 'Mode sombre'; + + @override + String get transcript => 'Transcription'; + + @override + String get pinned => 'Épinglés'; + + @override + String get folders => 'Dossiers'; + + @override + String get archived => 'Archivés'; + + @override + String get holdToTalk => 'Maintenir pour parler'; + + @override + String get autoSend => 'Envoi auto'; + + @override + String get stopListening => 'Arrêter l\'écoute'; + + @override + String get startListening => 'Commencer l\'écoute'; + + @override + String get start => 'Démarrer'; + + @override + String get stop => 'Arrêter'; + + @override + String get web => 'Web'; + + @override + String get imageGen => 'Gén. image'; + + @override + String get webSearch => 'Recherche Web'; + + @override + String get webSearchDescription => + 'Laissez l\'assistant rechercher sur Internet pendant la réponse.'; + + @override + String get imageGeneration => 'Génération d\'images'; + + @override + String get imageGenerationDescription => + 'Générez des images à partir de votre prompt et joignez-les.'; } diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart index 75403ac..085a907 100644 --- a/lib/l10n/app_localizations_it.dart +++ b/lib/l10n/app_localizations_it.dart @@ -116,7 +116,8 @@ class AppLocalizationsIt extends AppLocalizations { String get noFilesYet => 'Ancora nessun file'; @override - String get uploadDocsPrompt => 'Carica documenti da usare nelle conversazioni con Conduit'; + String get uploadDocsPrompt => + 'Carica documenti da usare nelle conversazioni con Conduit'; @override String get uploadFirstFile => 'Carica il tuo primo file'; @@ -125,7 +126,8 @@ class AppLocalizationsIt extends AppLocalizations { String get knowledgeBaseEmpty => 'La base di conoscenza è vuota'; @override - String get createCollectionsPrompt => 'Crea raccolte di documenti correlati per un rapido riferimento'; + String get createCollectionsPrompt => + 'Crea raccolte di documenti correlati per un rapido riferimento'; @override String get chooseSourcePhoto => 'Scegli origine'; @@ -151,7 +153,8 @@ class AppLocalizationsIt extends AppLocalizations { } @override - String get kbCreationComingSoon => 'La creazione della base di conoscenza arriverà presto!'; + String get kbCreationComingSoon => + 'La creazione della base di conoscenza arriverà presto!'; @override String get backToServerSetup => 'Torna alla configurazione del server'; @@ -163,7 +166,8 @@ class AppLocalizationsIt extends AppLocalizations { String get signIn => 'Accedi'; @override - String get enterCredentials => 'Inserisci le credenziali per accedere alle conversazioni IA'; + String get enterCredentials => + 'Inserisci le credenziali per accedere alle conversazioni IA'; @override String get credentials => 'Credenziali'; @@ -184,7 +188,8 @@ class AppLocalizationsIt extends AppLocalizations { String get connectToServer => 'Connetti al server'; @override - String get enterServerAddress => 'Inserisci l\'indirizzo del server Open-WebUI per iniziare'; + String get enterServerAddress => + 'Inserisci l\'indirizzo del server Open-WebUI per iniziare'; @override String get serverUrl => 'URL del server'; @@ -193,7 +198,8 @@ class AppLocalizationsIt extends AppLocalizations { String get serverUrlHint => 'https://tuo-server.com'; @override - String get enterServerUrlSemantic => 'Inserisci l\'URL o l\'indirizzo IP del server'; + String get enterServerUrlSemantic => + 'Inserisci l\'URL o l\'indirizzo IP del server'; @override String get headerName => 'Nome header'; @@ -223,7 +229,8 @@ class AppLocalizationsIt extends AppLocalizations { String get demoModeActive => 'Modalità demo attiva'; @override - String get skipServerSetupTryDemo => 'Salta configurazione server e prova la demo'; + String get skipServerSetupTryDemo => + 'Salta configurazione server e prova la demo'; @override String get enterDemo => 'Entra in demo'; @@ -244,7 +251,8 @@ class AppLocalizationsIt extends AppLocalizations { String get onlyHttpHttps => 'Sono supportati solo i protocolli HTTP e HTTPS.'; @override - String get serverAddressRequired => 'Indirizzo server richiesto (es. 192.168.1.10 o example.com).'; + String get serverAddressRequired => + 'Indirizzo server richiesto (es. 192.168.1.10 o example.com).'; @override String get portRange => 'La porta deve essere tra 1 e 65535.'; @@ -253,13 +261,16 @@ class AppLocalizationsIt extends AppLocalizations { String get invalidIpFormat => 'Formato IP non valido. Esempio: 192.168.1.10.'; @override - String get couldNotConnectGeneric => 'Impossibile connettersi. Verifica l\'indirizzo e riprova.'; + String get couldNotConnectGeneric => + 'Impossibile connettersi. Verifica l\'indirizzo e riprova.'; @override - String get weCouldntReachServer => 'Impossibile raggiungere il server. Verifica connessione e stato del server.'; + String get weCouldntReachServer => + 'Impossibile raggiungere il server. Verifica connessione e stato del server.'; @override - String get connectionTimedOut => 'Tempo scaduto. Il server potrebbe essere occupato o bloccato.'; + String get connectionTimedOut => + 'Tempo scaduto. Il server potrebbe essere occupato o bloccato.'; @override String get useHttpOrHttpsOnly => 'Usa solo http:// o https://.'; @@ -268,19 +279,23 @@ class AppLocalizationsIt extends AppLocalizations { String get loginFailed => 'Accesso non riuscito'; @override - String get invalidCredentials => 'Nome utente o password non validi. Riprova.'; + String get invalidCredentials => + 'Nome utente o password non validi. Riprova.'; @override - String get serverRedirectingHttps => 'Il server sta reindirizzando. Controlla la configurazione HTTPS.'; + String get serverRedirectingHttps => + 'Il server sta reindirizzando. Controlla la configurazione HTTPS.'; @override - String get unableToConnectServer => 'Impossibile connettersi al server. Controlla la connessione.'; + String get unableToConnectServer => + 'Impossibile connettersi al server. Controlla la connessione.'; @override String get requestTimedOut => 'Richiesta scaduta. Riprova.'; @override - String get genericSignInFailed => 'Impossibile accedere. Controlla credenziali e server.'; + String get genericSignInFailed => + 'Impossibile accedere. Controlla credenziali e server.'; @override String get skip => 'Salta'; @@ -295,10 +310,12 @@ class AppLocalizationsIt extends AppLocalizations { String get onboardStartTitle => 'Inizia una conversazione'; @override - String get onboardStartSubtitle => 'Scegli un modello e inizia a scrivere. Tocca Nuova chat in qualsiasi momento.'; + String get onboardStartSubtitle => + 'Scegli un modello e inizia a scrivere. Tocca Nuova chat in qualsiasi momento.'; @override - String get onboardStartBullet1 => 'Tocca il nome del modello in alto per cambiare'; + String get onboardStartBullet1 => + 'Tocca il nome del modello in alto per cambiare'; @override String get onboardStartBullet2 => 'Usa Nuova chat per azzerare il contesto'; @@ -307,7 +324,8 @@ class AppLocalizationsIt extends AppLocalizations { String get onboardAttachTitle => 'Aggiungi contesto'; @override - String get onboardAttachSubtitle => 'Migliora le risposte aggiungendo file o immagini.'; + String get onboardAttachSubtitle => + 'Migliora le risposte aggiungendo file o immagini.'; @override String get onboardAttachBullet1 => 'File: PDF, documenti, dataset'; @@ -319,10 +337,12 @@ class AppLocalizationsIt extends AppLocalizations { String get onboardSpeakTitle => 'Parla in modo naturale'; @override - String get onboardSpeakSubtitle => 'Tocca il microfono per dettare con feedback visivo.'; + String get onboardSpeakSubtitle => + 'Tocca il microfono per dettare con feedback visivo.'; @override - String get onboardSpeakBullet1 => 'Interrompi in qualsiasi momento; il testo parziale viene mantenuto'; + String get onboardSpeakBullet1 => + 'Interrompi in qualsiasi momento; il testo parziale viene mantenuto'; @override String get onboardSpeakBullet2 => 'Ottimo per note rapide o prompt lunghi'; @@ -331,10 +351,12 @@ class AppLocalizationsIt extends AppLocalizations { String get onboardQuickTitle => 'Azioni rapide'; @override - String get onboardQuickSubtitle => 'Usa il menu in alto a sinistra per aprire l\'elenco chat e la navigazione.'; + String get onboardQuickSubtitle => + 'Usa il menu in alto a sinistra per aprire l\'elenco chat e la navigazione.'; @override - String get onboardQuickBullet1 => 'Tocca il menu per aprire chat e navigazione'; + String get onboardQuickBullet1 => + 'Tocca il menu per aprire chat e navigazione'; @override String get onboardQuickBullet2 => 'Vai subito a Nuova chat, File o Profilo'; @@ -404,13 +426,16 @@ class AppLocalizationsIt extends AppLocalizations { String get emptyImageData => 'Dati immagine vuoti'; @override - String get offlineBanner => 'Sei offline. Alcune funzioni potrebbero essere limitate.'; + String get offlineBanner => + 'Sei offline. Alcune funzioni potrebbero essere limitate.'; @override - String get featureRequiresInternet => 'Questa funzione richiede una connessione Internet'; + String get featureRequiresInternet => + 'Questa funzione richiede una connessione Internet'; @override - String get messagesWillSendWhenOnline => 'I messaggi verranno inviati quando tornerai online'; + String get messagesWillSendWhenOnline => + 'I messaggi verranno inviati quando tornerai online'; @override String get confirm => 'Conferma'; @@ -569,11 +594,86 @@ class AppLocalizationsIt extends AppLocalizations { String get deleteChatTitle => 'Elimina chat'; @override - String get deleteChatMessage => 'Questa chat verrà eliminata definitivamente.'; + String get deleteChatMessage => + 'Questa chat verrà eliminata definitivamente.'; @override String get aboutApp => 'Informazioni sull\'app'; @override String get aboutAppSubtitle => 'Informazioni e link di Conduit'; + + @override + String get appLanguage => 'Lingua dell\'app'; + + @override + String get typeBelowToBegin => 'Scrivi qui sotto per iniziare'; + + @override + String get listening => 'In ascolto…'; + + @override + String get recording => 'Registrazione…'; + + @override + String get transcribing => 'Trascrizione…'; + + @override + String get speakNow => 'Parla ora…'; + + @override + String get chats => 'Chat'; + + @override + String get darkMode => 'Tema scuro'; + + @override + String get transcript => 'Trascrizione'; + + @override + String get pinned => 'In evidenza'; + + @override + String get folders => 'Cartelle'; + + @override + String get archived => 'Archiviati'; + + @override + String get holdToTalk => 'Tieni premuto per parlare'; + + @override + String get autoSend => 'Invio automatico'; + + @override + String get stopListening => 'Interrompi ascolto'; + + @override + String get startListening => 'Avvia ascolto'; + + @override + String get start => 'Avvia'; + + @override + String get stop => 'Stop'; + + @override + String get web => 'Web'; + + @override + String get imageGen => 'Gen. immagini'; + + @override + String get webSearch => 'Ricerca Web'; + + @override + String get webSearchDescription => + 'Lascia che l\'assistente cerchi sul web mentre risponde.'; + + @override + String get imageGeneration => 'Generazione immagini'; + + @override + String get imageGenerationDescription => + 'Genera immagini dal prompt e allegale.'; } diff --git a/lib/main.dart b/lib/main.dart index 175db81..98b8461 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -15,7 +15,6 @@ import 'core/auth/auth_state_manager.dart'; import 'core/utils/debug_logger.dart'; import 'features/onboarding/views/onboarding_sheet.dart'; -import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:conduit/l10n/app_localizations.dart'; import 'features/chat/views/chat_page.dart'; import 'features/navigation/views/splash_launcher_page.dart'; @@ -101,25 +100,19 @@ class _ConduitAppState extends ConsumerState { debugShowCheckedModeBanner: false, navigatorKey: NavigationService.navigatorKey, locale: locale, - localizationsDelegates: const [ - AppLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: const [ - Locale('en'), - Locale('de'), - Locale('fr'), - Locale('it'), - ], - localeResolutionCallback: (deviceLocale, supported) { + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + localeListResolutionCallback: (deviceLocales, supported) { if (locale != null) return locale; // User override wins - if (deviceLocale == null) return const Locale('en'); - for (final loc in supported) { - if (loc.languageCode == deviceLocale.languageCode) return loc; + if (deviceLocales == null || deviceLocales.isEmpty) { + return supported.first; } - return const Locale('en'); + for (final device in deviceLocales) { + for (final loc in supported) { + if (loc.languageCode == device.languageCode) return loc; + } + } + return supported.first; }, builder: (context, child) { // Keep a subtle fade for navigation transitions only @@ -184,7 +177,8 @@ class _ConduitAppState extends ConsumerState { if (authNavState == AuthNavigationState.error) { return _buildErrorState( - ref.watch(authErrorProvider3) ?? AppLocalizations.of(context)!.errorMessage, + ref.watch(authErrorProvider3) ?? + AppLocalizations.of(context)!.errorMessage, ); } @@ -199,7 +193,9 @@ class _ConduitAppState extends ConsumerState { loading: () => _buildInitialLoadingSkeleton(context), error: (error, stackTrace) { DebugLogger.error('Server provider error', error); - return _buildErrorState(AppLocalizations.of(context)!.unableToConnectServer); + return _buildErrorState( + AppLocalizations.of(context)!.unableToConnectServer, + ); }, ); },