diff --git a/lib/features/chat/widgets/enhanced_image_attachment.dart b/lib/features/chat/widgets/enhanced_image_attachment.dart index f7a7a96..b1f44e7 100644 --- a/lib/features/chat/widgets/enhanced_image_attachment.dart +++ b/lib/features/chat/widgets/enhanced_image_attachment.dart @@ -43,13 +43,11 @@ class EnhancedImageAttachment extends ConsumerStatefulWidget { class _EnhancedImageAttachmentState extends ConsumerState - with AutomaticKeepAliveClientMixin, SingleTickerProviderStateMixin { + with AutomaticKeepAliveClientMixin { String? _cachedImageData; bool _isLoading = true; String? _errorMessage; - late AnimationController _animationController; - late Animation _fadeAnimation; - bool _hasShownContent = false; + // Removed unused animation and state flags @override bool get wantKeepAlive => true; @@ -57,14 +55,6 @@ class _EnhancedImageAttachmentState @override void initState() { super.initState(); - _animationController = AnimationController( - duration: const Duration(milliseconds: 300), - vsync: this, - ); - _fadeAnimation = CurvedAnimation( - parent: _animationController, - curve: Curves.easeInOut, - ); // Defer loading until after first frame to avoid accessing inherited widgets // (e.g., Localizations) during initState WidgetsBinding.instance.addPostFrameCallback((_) { @@ -75,7 +65,6 @@ class _EnhancedImageAttachmentState @override void dispose() { - _animationController.dispose(); super.dispose(); } @@ -87,11 +76,7 @@ class _EnhancedImageAttachmentState setState(() { _cachedImageData = _globalImageCache[widget.attachmentId]; _isLoading = false; - _hasShownContent = true; }); - if (!widget.disableAnimation) { - _animationController.forward(); - } } return; } @@ -119,11 +104,7 @@ class _EnhancedImageAttachmentState setState(() { _cachedImageData = widget.attachmentId; _isLoading = false; - _hasShownContent = true; }); - if (!widget.disableAnimation) { - _animationController.forward(); - } } return; } @@ -140,11 +121,7 @@ class _EnhancedImageAttachmentState setState(() { _cachedImageData = fullUrl; _isLoading = false; - _hasShownContent = true; }); - if (!widget.disableAnimation) { - _animationController.forward(); - } } return; } else { @@ -214,11 +191,7 @@ class _EnhancedImageAttachmentState setState(() { _cachedImageData = fileContent; _isLoading = false; - _hasShownContent = true; }); - if (!widget.disableAnimation) { - _animationController.forward(); - } } } catch (e) { final error = l10n.failedToLoadImage(e.toString()); diff --git a/lib/features/profile/views/app_customization_page.dart b/lib/features/profile/views/app_customization_page.dart index 02fd0d8..8941468 100644 --- a/lib/features/profile/views/app_customization_page.dart +++ b/lib/features/profile/views/app_customization_page.dart @@ -8,6 +8,8 @@ import '../../../core/services/settings_service.dart'; import '../../../shared/theme/theme_extensions.dart'; import '../../../shared/widgets/conduit_components.dart'; import '../../../shared/utils/ui_utils.dart'; +import '../../../core/providers/app_providers.dart'; +import '../../../l10n/app_localizations.dart'; class AppCustomizationPage extends ConsumerWidget { const AppCustomizationPage({super.key}); @@ -15,6 +17,13 @@ class AppCustomizationPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final settings = ref.watch(appSettingsProvider); + final themeMode = ref.watch(themeModeProvider); + final platformBrightness = MediaQuery.platformBrightnessOf(context); + final bool isDarkEffective = + themeMode == ThemeMode.dark || + (themeMode == ThemeMode.system && + platformBrightness == Brightness.dark); + final locale = ref.watch(localeProvider); return Scaffold( backgroundColor: context.conduitTheme.surfaceBackground, @@ -30,10 +39,10 @@ class AppCustomizationPage extends ConsumerWidget { color: context.conduitTheme.textPrimary, ), onPressed: () => Navigator.of(context).maybePop(), - tooltip: 'Back', + tooltip: AppLocalizations.of(context)!.back, ), title: Text( - 'App Customization', + AppLocalizations.of(context)!.appCustomization, style: AppTypography.headlineSmallStyle.copyWith( color: context.conduitTheme.textPrimary, fontWeight: FontWeight.w600, @@ -47,7 +56,7 @@ class AppCustomizationPage extends ConsumerWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'Display', + AppLocalizations.of(context)!.display, style: context.conduitTheme.headingSmall?.copyWith( color: context.conduitTheme.textPrimary, ), @@ -57,6 +66,150 @@ class AppCustomizationPage extends ConsumerWidget { padding: EdgeInsets.zero, child: Column( children: [ + // Dark mode toggle + ListTile( + contentPadding: const EdgeInsets.symmetric( + horizontal: Spacing.listItemPadding, + vertical: Spacing.sm, + ), + leading: Container( + padding: const EdgeInsets.all(Spacing.sm), + decoration: BoxDecoration( + color: context.conduitTheme.buttonPrimary + .withValues(alpha: Alpha.highlight), + borderRadius: + BorderRadius.circular(AppBorderRadius.small), + ), + child: Icon( + UiUtils.platformIcon( + ios: CupertinoIcons.moon_stars, + android: Icons.dark_mode, + ), + color: context.conduitTheme.buttonPrimary, + size: IconSize.medium, + ), + ), + title: Text( + AppLocalizations.of(context)!.darkMode, + style: context.conduitTheme.bodyLarge?.copyWith( + color: context.conduitTheme.textPrimary, + fontWeight: FontWeight.w500, + ), + ), + subtitle: Text( + themeMode == ThemeMode.system + ? AppLocalizations.of(context)!.followingSystem( + platformBrightness == Brightness.dark + ? AppLocalizations.of(context)!.themeDark + : AppLocalizations.of(context)!.themeLight, + ) + : (isDarkEffective + ? AppLocalizations.of(context)! + .currentlyUsingDarkTheme + : AppLocalizations.of(context)! + .currentlyUsingLightTheme), + style: context.conduitTheme.bodySmall?.copyWith( + color: context.conduitTheme.textSecondary, + ), + ), + trailing: Switch.adaptive( + value: isDarkEffective, + onChanged: (value) { + ref + .read(themeModeProvider.notifier) + .setTheme(value ? ThemeMode.dark : ThemeMode.light); + }, + ), + onTap: () { + final newValue = !isDarkEffective; + ref + .read(themeModeProvider.notifier) + .setTheme( + newValue ? ThemeMode.dark : ThemeMode.light); + }, + ), + Divider(color: context.conduitTheme.dividerColor, height: 1), + + // App language selector + Builder(builder: (context) { + final currentCode = locale?.languageCode ?? 'system'; + final label = () { + switch (currentCode) { + case 'en': + return 'English'; + case 'de': + return 'Deutsch'; + case 'fr': + return 'Français'; + case 'it': + return 'Italiano'; + default: + return 'System'; + } + }(); + + return ListTile( + contentPadding: const EdgeInsets.symmetric( + horizontal: Spacing.listItemPadding, + vertical: Spacing.sm, + ), + leading: Container( + padding: const EdgeInsets.all(Spacing.sm), + decoration: BoxDecoration( + color: context.conduitTheme.buttonPrimary + .withValues(alpha: Alpha.highlight), + borderRadius: + BorderRadius.circular(AppBorderRadius.small), + ), + child: Icon( + UiUtils.platformIcon( + ios: CupertinoIcons.globe, + android: Icons.language, + ), + color: context.conduitTheme.buttonPrimary, + size: IconSize.medium, + ), + ), + title: Text( + AppLocalizations.of(context)!.appLanguage, + style: context.conduitTheme.bodyLarge?.copyWith( + color: context.conduitTheme.textPrimary, + fontWeight: FontWeight.w500, + ), + ), + subtitle: Text( + label, + style: context.conduitTheme.bodySmall?.copyWith( + color: context.conduitTheme.textSecondary, + ), + ), + trailing: Icon( + UiUtils.platformIcon( + ios: CupertinoIcons.chevron_right, + android: Icons.chevron_right, + ), + color: context.conduitTheme.iconSecondary, + size: IconSize.small, + ), + onTap: () async { + final selected = await _showLanguageSelector( + context, currentCode); + if (selected != null) { + if (selected == 'system') { + await ref + .read(localeProvider.notifier) + .setLocale(null); + } else { + await ref + .read(localeProvider.notifier) + .setLocale(Locale(selected)); + } + } + }, + ); + }), + Divider(color: context.conduitTheme.dividerColor, height: 1), + SwitchListTile.adaptive( contentPadding: const EdgeInsets.symmetric( horizontal: Spacing.listItemPadding, @@ -65,14 +218,15 @@ class AppCustomizationPage extends ConsumerWidget { // Use platform defaults for switch colors to match theme value: settings.omitProviderInModelName, title: Text( - 'Hide provider in model names', + AppLocalizations.of(context)!.hideProviderInModelNames, style: context.conduitTheme.bodyLarge?.copyWith( color: context.conduitTheme.textPrimary, fontWeight: FontWeight.w500, ), ), subtitle: Text( - 'Show names like "gpt-4o" instead of "openai/gpt-4o".', + AppLocalizations.of(context)! + .hideProviderInModelNamesDescription, style: context.conduitTheme.bodySmall?.copyWith( color: context.conduitTheme.textSecondary, ), @@ -105,7 +259,7 @@ class AppCustomizationPage extends ConsumerWidget { const SizedBox(height: Spacing.lg), Text( - 'Realtime', + AppLocalizations.of(context)!.realtime, style: context.conduitTheme.headingSmall?.copyWith( color: context.conduitTheme.textPrimary, ), @@ -138,14 +292,14 @@ class AppCustomizationPage extends ConsumerWidget { ), ), title: Text( - 'Transport mode', + AppLocalizations.of(context)!.transportMode, style: context.conduitTheme.bodyLarge?.copyWith( color: context.conduitTheme.textPrimary, fontWeight: FontWeight.w600, ), ), subtitle: Text( - 'Choose how the app connects for realtime updates.', + AppLocalizations.of(context)!.transportModeDescription, style: context.conduitTheme.bodySmall?.copyWith( color: context.conduitTheme.textSecondary, ), @@ -166,21 +320,23 @@ class AppCustomizationPage extends ConsumerWidget { .read(appSettingsProvider.notifier) .setSocketTransportMode(v); }, - items: const [ + items: [ DropdownMenuItem( value: 'auto', - child: Text('Auto (Polling + WebSocket)'), + child: Text(AppLocalizations.of(context)! + .transportModeAuto), ), DropdownMenuItem( value: 'ws', - child: Text('WebSocket only'), + child: Text(AppLocalizations.of(context)! + .transportModeWs), ), ], - decoration: const InputDecoration( - labelText: 'Mode', - border: OutlineInputBorder(), + decoration: InputDecoration( + labelText: AppLocalizations.of(context)!.mode, + border: const OutlineInputBorder(), isDense: true, - contentPadding: EdgeInsets.symmetric( + contentPadding: const EdgeInsets.symmetric( horizontal: 12, vertical: 10, ), @@ -196,8 +352,10 @@ class AppCustomizationPage extends ConsumerWidget { ), child: Text( settings.socketTransportMode == 'auto' - ? 'More robust on restrictive networks. Upgrades to WebSocket when possible.' - : 'Lower overhead, but may fail behind strict proxies/firewalls.', + ? AppLocalizations.of(context)! + .transportModeAutoInfo + : AppLocalizations.of(context)! + .transportModeWsInfo, style: context.conduitTheme.caption?.copyWith( color: context.conduitTheme.textSecondary, ), @@ -211,4 +369,56 @@ class AppCustomizationPage extends ConsumerWidget { ), ); } + + Future _showLanguageSelector(BuildContext context, String current) { + return showModalBottomSheet( + context: context, + backgroundColor: Colors.transparent, + isScrollControlled: true, + builder: (context) => Container( + decoration: BoxDecoration( + color: context.conduitTheme.surfaceBackground, + borderRadius: const BorderRadius.vertical( + top: Radius.circular(AppBorderRadius.modal), + ), + boxShadow: ConduitShadows.modal, + ), + child: SafeArea( + top: false, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: Spacing.sm), + ListTile( + title: Text(AppLocalizations.of(context)!.system), + trailing: current == 'system' ? const Icon(Icons.check) : null, + onTap: () => Navigator.pop(context, 'system'), + ), + ListTile( + title: Text(AppLocalizations.of(context)!.english), + trailing: current == 'en' ? const Icon(Icons.check) : null, + onTap: () => Navigator.pop(context, 'en'), + ), + ListTile( + title: Text(AppLocalizations.of(context)!.deutsch), + trailing: current == 'de' ? const Icon(Icons.check) : null, + onTap: () => Navigator.pop(context, 'de'), + ), + ListTile( + title: Text(AppLocalizations.of(context)!.francais), + trailing: current == 'fr' ? const Icon(Icons.check) : null, + onTap: () => Navigator.pop(context, 'fr'), + ), + ListTile( + title: Text(AppLocalizations.of(context)!.italiano), + trailing: current == 'it' ? const Icon(Icons.check) : null, + onTap: () => Navigator.pop(context, 'it'), + ), + const SizedBox(height: Spacing.sm), + ], + ), + ), + ), + ); + } } diff --git a/lib/features/profile/views/profile_page.dart b/lib/features/profile/views/profile_page.dart index c659428..9efe2a6 100644 --- a/lib/features/profile/views/profile_page.dart +++ b/lib/features/profile/views/profile_page.dart @@ -229,17 +229,13 @@ class ProfilePage extends ConsumerWidget { children: [ _buildDefaultModelTile(context, ref), Divider(color: context.conduitTheme.dividerColor, height: 1), - _buildThemeToggleTile(context, ref), - Divider(color: context.conduitTheme.dividerColor, height: 1), - _buildLanguageTile(context, ref), - Divider(color: context.conduitTheme.dividerColor, height: 1), _buildAccountOption( icon: UiUtils.platformIcon( ios: CupertinoIcons.slider_horizontal_3, android: Icons.tune, ), - title: 'App Customization', - subtitle: 'Personalize how names and UI display', + title: AppLocalizations.of(context)!.appCustomization, + subtitle: AppLocalizations.of(context)!.appCustomizationSubtitle, onTap: () { Navigator.of(context).push( MaterialPageRoute( @@ -463,199 +459,7 @@ class ProfilePage extends ConsumerWidget { ); } - Widget _buildLanguageTile(BuildContext context, WidgetRef ref) { - final locale = ref.watch(localeProvider); - final currentCode = locale?.languageCode ?? 'system'; - final label = () { - switch (currentCode) { - case 'en': - return 'English'; - case 'de': - return 'Deutsch'; - case 'fr': - return 'Français'; - case 'it': - return 'Italiano'; - default: - return 'System'; - } - }(); - - return ListTile( - contentPadding: const EdgeInsets.symmetric( - horizontal: Spacing.listItemPadding, - vertical: Spacing.sm, - ), - leading: Container( - padding: const EdgeInsets.all(Spacing.sm), - decoration: BoxDecoration( - color: context.conduitTheme.buttonPrimary.withValues( - alpha: Alpha.highlight, - ), - borderRadius: BorderRadius.circular(AppBorderRadius.small), - ), - child: Icon( - UiUtils.platformIcon( - ios: CupertinoIcons.globe, - android: Icons.language, - ), - color: context.conduitTheme.buttonPrimary, - size: IconSize.medium, - ), - ), - title: Text( - AppLocalizations.of(context)!.appLanguage, - style: context.conduitTheme.bodyLarge?.copyWith( - color: context.conduitTheme.textPrimary, - fontWeight: FontWeight.w500, - ), - ), - subtitle: Text( - label, - style: context.conduitTheme.bodySmall?.copyWith( - color: context.conduitTheme.textSecondary, - ), - ), - trailing: Icon( - UiUtils.platformIcon( - ios: CupertinoIcons.chevron_right, - android: Icons.chevron_right, - ), - color: context.conduitTheme.iconSecondary, - size: IconSize.small, - ), - onTap: () async { - final selected = await _showLanguageSelector(context, currentCode); - if (selected != null) { - if (selected == 'system') { - await ref.read(localeProvider.notifier).setLocale(null); - } else { - await ref.read(localeProvider.notifier).setLocale(Locale(selected)); - } - } - }, - ); - } - - Future _showLanguageSelector(BuildContext context, String current) { - return showModalBottomSheet( - context: context, - backgroundColor: Colors.transparent, - isScrollControlled: true, - builder: (context) => Container( - decoration: BoxDecoration( - color: context.conduitTheme.surfaceBackground, - borderRadius: const BorderRadius.vertical( - top: Radius.circular(AppBorderRadius.modal), - ), - boxShadow: ConduitShadows.modal, - ), - child: SafeArea( - top: false, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox(height: Spacing.sm), - ListTile( - title: Text(AppLocalizations.of(context)!.system), - trailing: current == 'system' ? const Icon(Icons.check) : null, - onTap: () => Navigator.pop(context, 'system'), - ), - ListTile( - title: Text(AppLocalizations.of(context)!.english), - trailing: current == 'en' ? const Icon(Icons.check) : null, - onTap: () => Navigator.pop(context, 'en'), - ), - ListTile( - title: Text(AppLocalizations.of(context)!.deutsch), - trailing: current == 'de' ? const Icon(Icons.check) : null, - onTap: () => Navigator.pop(context, 'de'), - ), - ListTile( - title: Text(AppLocalizations.of(context)!.francais), - trailing: current == 'fr' ? const Icon(Icons.check) : null, - onTap: () => Navigator.pop(context, 'fr'), - ), - ListTile( - title: Text(AppLocalizations.of(context)!.italiano), - trailing: current == 'it' ? const Icon(Icons.check) : null, - onTap: () => Navigator.pop(context, 'it'), - ), - const SizedBox(height: Spacing.sm), - ], - ), - ), - ), - ); - } - - Widget _buildThemeToggleTile(BuildContext context, WidgetRef ref) { - final themeMode = ref.watch(themeModeProvider); - final platformBrightness = MediaQuery.platformBrightnessOf(context); - final bool isDarkEffective = - themeMode == ThemeMode.dark || - (themeMode == ThemeMode.system && - platformBrightness == Brightness.dark); - - return ListTile( - contentPadding: const EdgeInsets.symmetric( - horizontal: Spacing.listItemPadding, - vertical: Spacing.sm, - ), - leading: Container( - padding: const EdgeInsets.all(Spacing.sm), - decoration: BoxDecoration( - color: context.conduitTheme.buttonPrimary.withValues( - alpha: Alpha.highlight, - ), - borderRadius: BorderRadius.circular(AppBorderRadius.small), - ), - child: Icon( - UiUtils.platformIcon( - ios: CupertinoIcons.moon_stars, - android: Icons.dark_mode, - ), - color: context.conduitTheme.buttonPrimary, - size: IconSize.medium, - ), - ), - title: Text( - AppLocalizations.of(context)!.darkMode, - style: context.conduitTheme.bodyLarge?.copyWith( - color: context.conduitTheme.textPrimary, - fontWeight: FontWeight.w500, - ), - ), - subtitle: Text( - themeMode == ThemeMode.system - ? AppLocalizations.of(context)!.followingSystem( - platformBrightness == Brightness.dark - ? AppLocalizations.of(context)!.themeDark - : AppLocalizations.of(context)!.themeLight, - ) - : (isDarkEffective - ? AppLocalizations.of(context)!.currentlyUsingDarkTheme - : AppLocalizations.of(context)!.currentlyUsingLightTheme), - style: context.conduitTheme.bodySmall?.copyWith( - color: context.conduitTheme.textSecondary, - ), - ), - trailing: Switch.adaptive( - value: isDarkEffective, - onChanged: (value) { - ref - .read(themeModeProvider.notifier) - .setTheme(value ? ThemeMode.dark : ThemeMode.light); - }, - ), - onTap: () { - final newValue = !isDarkEffective; - ref - .read(themeModeProvider.notifier) - .setTheme(newValue ? ThemeMode.dark : ThemeMode.light); - }, - ); - } + // Theme and language controls moved to AppCustomizationPage. Widget _buildAboutTile(BuildContext context) { return _buildAccountOption( diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 0b9e5d9..185caa1 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -259,4 +259,18 @@ "description": "Zeigt an, wie lange der Assistent nachgedacht hat.", "placeholders": {"duration": {"type": "String", "example": "3 s"}} } + , + "appCustomization": "App-Anpassung", + "appCustomizationSubtitle": "Personalisieren, wie Namen und UI angezeigt werden", + "display": "Anzeige", + "realtime": "Echtzeit", + "hideProviderInModelNames": "Anbieter in Modellnamen ausblenden", + "hideProviderInModelNamesDescription": "Zeige Namen wie \"gpt-4o\" statt \"openai/gpt-4o\".", + "transportMode": "Transportmodus", + "transportModeDescription": "Wähle, wie die App für Echtzeit-Updates verbindet.", + "mode": "Modus", + "transportModeAuto": "Auto (Polling + WebSocket)", + "transportModeWs": "Nur WebSocket", + "transportModeAutoInfo": "Robuster in restriktiven Netzwerken. Wechselt nach Möglichkeit zu WebSocket.", + "transportModeWsInfo": "Geringerer Overhead, kann jedoch hinter strikten Proxys/Firewalls fehlschlagen." } diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index b64df28..200afaf 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -283,4 +283,18 @@ "description": "Shows how long the assistant thought before replying.", "placeholders": {"duration": {"type": "String", "example": "3s"}} } + , + "appCustomization": "App Customization", + "appCustomizationSubtitle": "Personalize how names and UI display", + "display": "Display", + "realtime": "Realtime", + "hideProviderInModelNames": "Hide provider in model names", + "hideProviderInModelNamesDescription": "Show names like \"gpt-4o\" instead of \"openai/gpt-4o\".", + "transportMode": "Transport mode", + "transportModeDescription": "Choose how the app connects for realtime updates.", + "mode": "Mode", + "transportModeAuto": "Auto (Polling + WebSocket)", + "transportModeWs": "WebSocket only", + "transportModeAutoInfo": "More robust on restrictive networks. Upgrades to WebSocket when possible.", + "transportModeWsInfo": "Lower overhead, but may fail behind strict proxies/firewalls." } diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 922ddff..03705f1 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -259,4 +259,18 @@ "description": "Indique la durée de réflexion de l'assistant.", "placeholders": {"duration": {"type": "String", "example": "3 s"}} } + , + "appCustomization": "Personnalisation de l'app", + "appCustomizationSubtitle": "Personnalisez l'affichage des noms et de l'UI", + "display": "Affichage", + "realtime": "Temps réel", + "hideProviderInModelNames": "Masquer le fournisseur dans les noms de modèles", + "hideProviderInModelNamesDescription": "Afficher des noms comme \"gpt-4o\" au lieu de \"openai/gpt-4o\".", + "transportMode": "Mode de transport", + "transportModeDescription": "Choisissez comment l'app se connecte pour les mises à jour en temps réel.", + "mode": "Mode", + "transportModeAuto": "Auto (Polling + WebSocket)", + "transportModeWs": "WebSocket uniquement", + "transportModeAutoInfo": "Plus robuste sur les réseaux restrictifs. Passe à WebSocket lorsque possible.", + "transportModeWsInfo": "Moins de surcharge, mais peut échouer derrière des proxys/firewalls stricts." } diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb index 704222b..659eaa8 100644 --- a/lib/l10n/app_it.arb +++ b/lib/l10n/app_it.arb @@ -259,4 +259,18 @@ "description": "Mostra per quanto tempo ha pensato l'assistente.", "placeholders": {"duration": {"type": "String", "example": "3 s"}} } + , + "appCustomization": "Personalizzazione app", + "appCustomizationSubtitle": "Personalizza la visualizzazione dei nomi e dell'UI", + "display": "Schermo", + "realtime": "Tempo reale", + "hideProviderInModelNames": "Nascondi provider nei nomi dei modelli", + "hideProviderInModelNamesDescription": "Mostra nomi come \"gpt-4o\" invece di \"openai/gpt-4o\".", + "transportMode": "Modalità di trasporto", + "transportModeDescription": "Scegli come l'app si connette per gli aggiornamenti in tempo reale.", + "mode": "Modalità", + "transportModeAuto": "Auto (Polling + WebSocket)", + "transportModeWs": "Solo WebSocket", + "transportModeAutoInfo": "Più robusto nelle reti restrittive. Passa a WebSocket quando possibile.", + "transportModeWsInfo": "Minore overhead, ma può fallire dietro proxy/firewall restrittivi." } diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 73d048a..6626d67 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -1230,13 +1230,13 @@ abstract class AppLocalizations { /// No description provided for @appLanguage. /// /// In en, this message translates to: - /// **'App language'** + /// **'App Language'** String get appLanguage; /// No description provided for @darkMode. /// /// In en, this message translates to: - /// **'Dark mode'** + /// **'Dark Mode'** String get darkMode; /// No description provided for @webSearch. @@ -1472,6 +1472,84 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'Thought for {duration}'** String thoughtForDuration(String duration); + + /// No description provided for @appCustomization. + /// + /// In en, this message translates to: + /// **'App Customization'** + String get appCustomization; + + /// No description provided for @appCustomizationSubtitle. + /// + /// In en, this message translates to: + /// **'Personalize how names and UI display'** + String get appCustomizationSubtitle; + + /// No description provided for @display. + /// + /// In en, this message translates to: + /// **'Display'** + String get display; + + /// No description provided for @realtime. + /// + /// In en, this message translates to: + /// **'Realtime'** + String get realtime; + + /// No description provided for @hideProviderInModelNames. + /// + /// In en, this message translates to: + /// **'Hide provider in model names'** + String get hideProviderInModelNames; + + /// No description provided for @hideProviderInModelNamesDescription. + /// + /// In en, this message translates to: + /// **'Show names like \"gpt-4o\" instead of \"openai/gpt-4o\".'** + String get hideProviderInModelNamesDescription; + + /// No description provided for @transportMode. + /// + /// In en, this message translates to: + /// **'Transport mode'** + String get transportMode; + + /// No description provided for @transportModeDescription. + /// + /// In en, this message translates to: + /// **'Choose how the app connects for realtime updates.'** + String get transportModeDescription; + + /// No description provided for @mode. + /// + /// In en, this message translates to: + /// **'Mode'** + String get mode; + + /// No description provided for @transportModeAuto. + /// + /// In en, this message translates to: + /// **'Auto (Polling + WebSocket)'** + String get transportModeAuto; + + /// No description provided for @transportModeWs. + /// + /// In en, this message translates to: + /// **'WebSocket only'** + String get transportModeWs; + + /// No description provided for @transportModeAutoInfo. + /// + /// In en, this message translates to: + /// **'More robust on restrictive networks. Upgrades to WebSocket when possible.'** + String get transportModeAutoInfo; + + /// No description provided for @transportModeWsInfo. + /// + /// In en, this message translates to: + /// **'Lower overhead, but may fail behind strict proxies/firewalls.'** + String get transportModeWsInfo; } class _AppLocalizationsDelegate extends LocalizationsDelegate { diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 8b83461..0b7b246 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -727,4 +727,43 @@ class AppLocalizationsDe extends AppLocalizations { String thoughtForDuration(String duration) { return 'Gedacht für $duration'; } + + @override + String get appCustomization => 'App-Anpassung'; + + @override + String get appCustomizationSubtitle => 'Personalisieren, wie Namen und UI angezeigt werden'; + + @override + String get display => 'Anzeige'; + + @override + String get realtime => 'Echtzeit'; + + @override + String get hideProviderInModelNames => 'Anbieter in Modellnamen ausblenden'; + + @override + String get hideProviderInModelNamesDescription => 'Zeige Namen wie \"gpt-4o\" statt \"openai/gpt-4o\".'; + + @override + String get transportMode => 'Transportmodus'; + + @override + String get transportModeDescription => 'Wähle, wie die App für Echtzeit-Updates verbindet.'; + + @override + String get mode => 'Modus'; + + @override + String get transportModeAuto => 'Auto (Polling + WebSocket)'; + + @override + String get transportModeWs => 'Nur WebSocket'; + + @override + String get transportModeAutoInfo => 'Robuster in restriktiven Netzwerken. Wechselt nach Möglichkeit zu WebSocket.'; + + @override + String get transportModeWsInfo => 'Geringerer Overhead, kann jedoch hinter strikten Proxys/Firewalls fehlschlagen.'; } diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index c6739fb..96f24a6 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -727,4 +727,43 @@ class AppLocalizationsEn extends AppLocalizations { String thoughtForDuration(String duration) { return 'Thought for $duration'; } + + @override + String get appCustomization => 'App Customization'; + + @override + String get appCustomizationSubtitle => 'Personalize how names and UI display'; + + @override + String get display => 'Display'; + + @override + String get realtime => 'Realtime'; + + @override + String get hideProviderInModelNames => 'Hide provider in model names'; + + @override + String get hideProviderInModelNamesDescription => 'Show names like \"gpt-4o\" instead of \"openai/gpt-4o\".'; + + @override + String get transportMode => 'Transport mode'; + + @override + String get transportModeDescription => 'Choose how the app connects for realtime updates.'; + + @override + String get mode => 'Mode'; + + @override + String get transportModeAuto => 'Auto (Polling + WebSocket)'; + + @override + String get transportModeWs => 'WebSocket only'; + + @override + String get transportModeAutoInfo => 'More robust on restrictive networks. Upgrades to WebSocket when possible.'; + + @override + String get transportModeWsInfo => 'Lower overhead, but may fail behind strict proxies/firewalls.'; } diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index e3b24d7..54acd2c 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -727,4 +727,43 @@ class AppLocalizationsFr extends AppLocalizations { String thoughtForDuration(String duration) { return 'A réfléchi pendant $duration'; } + + @override + String get appCustomization => 'Personnalisation de l\'app'; + + @override + String get appCustomizationSubtitle => 'Personnalisez l\'affichage des noms et de l\'UI'; + + @override + String get display => 'Affichage'; + + @override + String get realtime => 'Temps réel'; + + @override + String get hideProviderInModelNames => 'Masquer le fournisseur dans les noms de modèles'; + + @override + String get hideProviderInModelNamesDescription => 'Afficher des noms comme \"gpt-4o\" au lieu de \"openai/gpt-4o\".'; + + @override + String get transportMode => 'Mode de transport'; + + @override + String get transportModeDescription => 'Choisissez comment l\'app se connecte pour les mises à jour en temps réel.'; + + @override + String get mode => 'Mode'; + + @override + String get transportModeAuto => 'Auto (Polling + WebSocket)'; + + @override + String get transportModeWs => 'WebSocket uniquement'; + + @override + String get transportModeAutoInfo => 'Plus robuste sur les réseaux restrictifs. Passe à WebSocket lorsque possible.'; + + @override + String get transportModeWsInfo => 'Moins de surcharge, mais peut échouer derrière des proxys/firewalls stricts.'; } diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart index d3d06ba..734b8a4 100644 --- a/lib/l10n/app_localizations_it.dart +++ b/lib/l10n/app_localizations_it.dart @@ -727,4 +727,43 @@ class AppLocalizationsIt extends AppLocalizations { String thoughtForDuration(String duration) { return 'Ha pensato per $duration'; } + + @override + String get appCustomization => 'Personalizzazione app'; + + @override + String get appCustomizationSubtitle => 'Personalizza la visualizzazione dei nomi e dell\'UI'; + + @override + String get display => 'Schermo'; + + @override + String get realtime => 'Tempo reale'; + + @override + String get hideProviderInModelNames => 'Nascondi provider nei nomi dei modelli'; + + @override + String get hideProviderInModelNamesDescription => 'Mostra nomi come \"gpt-4o\" invece di \"openai/gpt-4o\".'; + + @override + String get transportMode => 'Modalità di trasporto'; + + @override + String get transportModeDescription => 'Scegli come l\'app si connette per gli aggiornamenti in tempo reale.'; + + @override + String get mode => 'Modalità'; + + @override + String get transportModeAuto => 'Auto (Polling + WebSocket)'; + + @override + String get transportModeWs => 'Solo WebSocket'; + + @override + String get transportModeAutoInfo => 'Più robusto nelle reti restrittive. Passa a WebSocket quando possibile.'; + + @override + String get transportModeWsInfo => 'Minore overhead, ma può fallire dietro proxy/firewall restrittivi.'; }