diff --git a/lib/core/services/enhanced_accessibility_service.dart b/lib/core/services/enhanced_accessibility_service.dart index ce7d3f0..950706f 100644 --- a/lib/core/services/enhanced_accessibility_service.dart +++ b/lib/core/services/enhanced_accessibility_service.dart @@ -1,12 +1,20 @@ import 'dart:math' as math; +import 'package:conduit/l10n/app_localizations.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter/semantics.dart'; import '../../shared/theme/tweakcn_themes.dart'; import '../../shared/theme/theme_extensions.dart'; +import 'navigation_service.dart'; /// Enhanced accessibility service for WCAG 2.2 AA compliance class EnhancedAccessibilityService { + static AppLocalizations? get _l10n { + final ctx = NavigationService.context; + if (ctx == null) return null; + return AppLocalizations.of(ctx); + } + /// Announce text to screen readers static void announce( String message, { @@ -17,20 +25,28 @@ class EnhancedAccessibilityService { /// Announce loading state static void announceLoading(String loadingMessage) { - announce('Loading: $loadingMessage'); + final l10n = _l10n; + final message = + l10n?.loadingAnnouncement(loadingMessage) ?? 'Loading: $loadingMessage'; + announce(message); } /// Announce error with helpful context static void announceError(String error, {String? suggestion}) { + final l10n = _l10n; final message = suggestion != null - ? 'Error: $error. $suggestion' - : 'Error: $error'; + ? l10n?.errorAnnouncementWithSuggestion(error, suggestion) ?? + 'Error: $error. $suggestion' + : l10n?.errorAnnouncement(error) ?? 'Error: $error'; announce(message); } /// Announce success with context static void announceSuccess(String successMessage) { - announce('Success: $successMessage'); + final l10n = _l10n; + announce( + l10n?.successAnnouncement(successMessage) ?? 'Success: $successMessage', + ); } /// Check if reduce motion is enabled @@ -117,7 +133,10 @@ class EnhancedAccessibilityService { bool obscureText = false, ValueChanged? onChanged, }) { - final effectiveLabel = isRequired ? '$label *' : label; + final l10n = _l10n; + final effectiveLabel = isRequired + ? l10n?.requiredFieldLabel(label) ?? '$label *' + : label; return Semantics( label: effectiveLabel, @@ -132,7 +151,9 @@ class EnhancedAccessibilityService { labelText: effectiveLabel, hintText: hintText, errorText: errorText, - helperText: isRequired ? '* Required field' : null, + helperText: isRequired + ? l10n?.requiredFieldHelper ?? 'Required field' + : null, prefixIcon: errorText != null ? Builder( builder: (context) => Icon( @@ -176,8 +197,9 @@ class EnhancedAccessibilityService { String? loadingMessage, double size = 24, }) { + final l10n = _l10n; return Semantics( - label: loadingMessage ?? 'Loading', + label: loadingMessage ?? l10n?.loadingShort ?? 'Loading', liveRegion: true, child: SizedBox( width: size, @@ -217,10 +239,13 @@ class EnhancedAccessibilityService { required String label, String? description, }) { + final l10n = _l10n; + final onLabel = l10n?.switchOnLabel ?? 'On'; + final offLabel = l10n?.switchOffLabel ?? 'Off'; return Builder( builder: (context) => Semantics( label: label, - value: value ? 'On' : 'Off', + value: value ? onLabel : offLabel, hint: description, toggled: value, onTap: onChanged != null ? () => onChanged(!value) : null, @@ -291,15 +316,18 @@ class EnhancedAccessibilityService { return showDialog( context: context, barrierDismissible: barrierDismissible, - builder: (context) => Semantics( - scopesRoute: true, - explicitChildNodes: true, - label: 'Dialog: $title', - child: AlertDialog( - title: Semantics(header: true, child: Text(title)), - content: child, - ), - ), + builder: (dialogContext) { + final dialogL10n = AppLocalizations.of(dialogContext); + return Semantics( + scopesRoute: true, + explicitChildNodes: true, + label: dialogL10n?.dialogSemanticLabel(title) ?? 'Dialog: $title', + child: AlertDialog( + title: Semantics(header: true, child: Text(title)), + content: child, + ), + ); + }, ); } diff --git a/lib/core/services/navigation_service.dart b/lib/core/services/navigation_service.dart index 78798ef..61a92ee 100644 --- a/lib/core/services/navigation_service.dart +++ b/lib/core/services/navigation_service.dart @@ -1,3 +1,4 @@ +import 'package:conduit/l10n/app_localizations.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import '../../shared/widgets/themed_dialogs.dart'; @@ -57,18 +58,21 @@ class NavigationService { static Future confirmNavigation({ required String title, required String message, - String confirmText = 'Continue', - String cancelText = 'Cancel', + String? confirmText, + String? cancelText, }) async { final ctx = context; if (ctx == null) return false; + final l10n = AppLocalizations.of(ctx); + final resolvedConfirm = confirmText ?? l10n?.continueAction ?? 'Continue'; + final resolvedCancel = cancelText ?? l10n?.cancel ?? 'Cancel'; final result = await ThemedDialogs.confirm( ctx, title: title, message: message, - confirmText: confirmText, - cancelText: cancelText, + confirmText: resolvedConfirm, + cancelText: resolvedCancel, barrierDismissible: false, ); diff --git a/lib/core/services/user_friendly_error_handler.dart b/lib/core/services/user_friendly_error_handler.dart index 70fb7f8..1b612ca 100644 --- a/lib/core/services/user_friendly_error_handler.dart +++ b/lib/core/services/user_friendly_error_handler.dart @@ -12,48 +12,57 @@ class UserFriendlyErrorHandler { factory UserFriendlyErrorHandler() => _instance; UserFriendlyErrorHandler._internal(); + AppLocalizations? get _l10n { + final ctx = NavigationService.context; + if (ctx == null) return null; + return AppLocalizations.of(ctx); + } + /// Convert technical errors to user-friendly messages String getUserMessage(dynamic error) { final errorString = error.toString().toLowerCase(); + final l10n = _l10n; if (_isNetworkError(errorString)) { - return _getNetworkErrorMessage(errorString); + return _getNetworkErrorMessage(errorString, l10n); } else if (_isValidationError(errorString)) { - return _getValidationErrorMessage(errorString); + return _getValidationErrorMessage(errorString, l10n); } else if (_isServerError(errorString)) { - return _getServerErrorMessage(errorString); + return _getServerErrorMessage(errorString, l10n); } else if (_isAuthenticationError(errorString)) { - return _getAuthenticationErrorMessage(errorString); + return _getAuthenticationErrorMessage(errorString, l10n); } else if (_isFileError(errorString)) { - return _getFileErrorMessage(errorString); + return _getFileErrorMessage(errorString, l10n); } else if (_isPermissionError(errorString)) { - return _getPermissionErrorMessage(errorString); + return _getPermissionErrorMessage(errorString, l10n); } // Log technical details for debugging _logError(error); // Return generic user-friendly message - return 'Something unexpected happened. Please try again.'; + return l10n?.errorMessage ?? + 'Something unexpected happened. Please try again.'; } /// Get recovery actions for the error List getRecoveryActions(dynamic error) { final errorString = error.toString().toLowerCase(); + final l10n = _l10n; if (_isNetworkError(errorString)) { - return _getNetworkRecoveryActions(); + return _getNetworkRecoveryActions(l10n); } else if (_isServerError(errorString)) { - return _getServerRecoveryActions(); + return _getServerRecoveryActions(l10n); } else if (_isAuthenticationError(errorString)) { - return _getAuthRecoveryActions(); + return _getAuthRecoveryActions(l10n); } else if (_isFileError(errorString)) { - return _getFileRecoveryActions(); + return _getFileRecoveryActions(l10n); } else if (_isPermissionError(errorString)) { - return _getPermissionRecoveryActions(); + return _getPermissionRecoveryActions(l10n); } - return _getGenericRecoveryActions(); + return _getGenericRecoveryActions(l10n); } /// Build error widget with recovery options @@ -135,28 +144,33 @@ class UserFriendlyErrorHandler { error.contains('no address associated'); } - String _getNetworkErrorMessage(String error) { + String _getNetworkErrorMessage(String error, AppLocalizations? l10n) { if (error.contains('timeout')) { - return 'Connection timed out. Please check your internet connection and try again.'; + return l10n?.networkTimeoutError ?? + 'Connection timed out. Please check your internet connection and try again.'; } else if (error.contains('no address associated')) { - return 'Cannot reach the server. Please check your server URL and internet connection.'; + return l10n?.networkUnreachableError ?? + 'Cannot reach the server. Please check your server URL and internet connection.'; } else if (error.contains('connection refused')) { - return 'Server is not responding. Please verify the server is running and accessible.'; + return l10n?.networkServerNotResponding ?? + 'Server is not responding. Please verify the server is running and accessible.'; } - return 'Network connection problem. Please check your internet connection.'; + return l10n?.networkGenericError ?? + 'Network connection problem. Please check your internet connection.'; } - List _getNetworkRecoveryActions() { + List _getNetworkRecoveryActions(AppLocalizations? l10n) { return [ ErrorRecoveryAction( - label: 'Retry', + label: l10n?.retry ?? 'Retry', action: ErrorActionType.retry, - description: 'Try the request again', + description: l10n?.actionRetryRequest ?? 'Try the request again', ), ErrorRecoveryAction( - label: 'Check Connection', + label: l10n?.checkConnection ?? 'Check Connection', action: ErrorActionType.checkConnection, - description: 'Verify your internet connection', + description: + l10n?.actionVerifyConnection ?? 'Verify your internet connection', ), ]; } @@ -171,28 +185,33 @@ class UserFriendlyErrorHandler { error.contains('internal server error'); } - String _getServerErrorMessage(String error) { + String _getServerErrorMessage(String error, AppLocalizations? l10n) { if (error.contains('500')) { - return 'Server is experiencing issues. This is usually temporary.'; + return l10n?.serverError500 ?? + 'Server is experiencing issues. This is usually temporary.'; } else if (error.contains('502') || error.contains('503')) { - return 'Server is temporarily unavailable. Please try again in a moment.'; + return l10n?.serverErrorUnavailable ?? + 'Server is temporarily unavailable. Please try again in a moment.'; } else if (error.contains('504')) { - return 'Server took too long to respond. Please try again.'; + return l10n?.serverErrorTimeout ?? + 'Server took too long to respond. Please try again.'; } - return 'Server is having problems. Please try again later.'; + return l10n?.serverErrorGeneric ?? + 'Server is having problems. Please try again later.'; } - List _getServerRecoveryActions() { + List _getServerRecoveryActions(AppLocalizations? l10n) { return [ ErrorRecoveryAction( - label: 'Retry', + label: l10n?.retry ?? 'Retry', action: ErrorActionType.retry, - description: 'Retry your request', + description: l10n?.actionRetryRequest ?? 'Retry your request', ), ErrorRecoveryAction( - label: 'Retry', + label: l10n?.retry ?? 'Retry', action: ErrorActionType.retryLater, - description: 'Wait a moment then try again', + description: + l10n?.actionRetryAfterDelay ?? 'Wait a moment then try again', ), ]; } @@ -207,28 +226,32 @@ class UserFriendlyErrorHandler { error.contains('token'); } - String _getAuthenticationErrorMessage(String error) { + String _getAuthenticationErrorMessage(String error, AppLocalizations? l10n) { if (error.contains('401') || error.contains('unauthorized')) { - return 'Your session has expired. Please sign in again.'; + return l10n?.authSessionExpired ?? + 'Your session has expired. Please sign in again.'; } else if (error.contains('403') || error.contains('forbidden')) { - return 'You don\'t have permission to perform this action.'; + return l10n?.authForbidden ?? + 'You don\'t have permission to perform this action.'; } else if (error.contains('token')) { - return 'Authentication token is invalid. Please sign in again.'; + return l10n?.authInvalidToken ?? + 'Authentication token is invalid. Please sign in again.'; } - return 'Authentication problem. Please sign in again.'; + return l10n?.authGenericError ?? + 'Authentication problem. Please sign in again.'; } - List _getAuthRecoveryActions() { + List _getAuthRecoveryActions(AppLocalizations? l10n) { return [ ErrorRecoveryAction( - label: 'Sign In', + label: l10n?.signIn ?? 'Sign In', action: ErrorActionType.signIn, - description: 'Sign in to your account', + description: l10n?.actionSignInToAccount ?? 'Sign in to your account', ), ErrorRecoveryAction( - label: 'Retry', + label: l10n?.retry ?? 'Retry', action: ErrorActionType.retry, - description: 'Retry the request', + description: l10n?.actionRetryOperation ?? 'Retry the request', ), ]; } @@ -242,17 +265,22 @@ class UserFriendlyErrorHandler { error.contains('400'); } - String _getValidationErrorMessage(String error) { + String _getValidationErrorMessage(String error, AppLocalizations? l10n) { if (error.contains('email')) { - return 'Please enter a valid email address.'; + return l10n?.validationInvalidEmail ?? + 'Please enter a valid email address.'; } else if (error.contains('password')) { - return 'Password doesn\'t meet requirements. Please check and try again.'; + return l10n?.validationWeakPassword ?? + 'Password doesn\'t meet requirements. Please check and try again.'; } else if (error.contains('required')) { - return 'Please fill in all required fields.'; + return l10n?.validationMissingRequired ?? + 'Please fill in all required fields.'; } else if (error.contains('format')) { - return 'Some information is in the wrong format. Please check and try again.'; + return l10n?.validationFormatError ?? + 'Some information is in the wrong format. Please check and try again.'; } - return 'Please check your input and try again.'; + return l10n?.validationGenericError ?? + 'Please check your input and try again.'; } // File error detection and handling @@ -264,28 +292,32 @@ class UserFriendlyErrorHandler { error.contains('access denied'); } - String _getFileErrorMessage(String error) { + String _getFileErrorMessage(String error, AppLocalizations? l10n) { if (error.contains('not found')) { - return 'File not found. It may have been moved or deleted.'; + return l10n?.fileNotFound ?? + 'File not found. It may have been moved or deleted.'; } else if (error.contains('access denied')) { - return 'Cannot access the file. Please check permissions.'; + return l10n?.fileAccessDenied ?? + 'Cannot access the file. Please check permissions.'; } else if (error.contains('too large')) { - return 'File is too large. Please choose a smaller file.'; + return l10n?.fileTooLarge ?? + 'File is too large. Please choose a smaller file.'; } - return 'Problem with the file. Please try a different file.'; + return l10n?.fileGenericError ?? + 'Problem with the file. Please try a different file.'; } - List _getFileRecoveryActions() { + List _getFileRecoveryActions(AppLocalizations? l10n) { return [ ErrorRecoveryAction( - label: 'Choose Different File', + label: l10n?.chooseDifferentFile ?? 'Choose Different File', action: ErrorActionType.chooseFile, - description: 'Select another file', + description: l10n?.actionSelectAnotherFile ?? 'Select another file', ), ErrorRecoveryAction( - label: 'Retry', + label: l10n?.retry ?? 'Retry', action: ErrorActionType.retry, - description: 'Retry the operation', + description: l10n?.actionRetryOperation ?? 'Retry the operation', ), ]; } @@ -298,43 +330,54 @@ class UserFriendlyErrorHandler { error.contains('access'); } - String _getPermissionErrorMessage(String error) { + String _getPermissionErrorMessage(String error, AppLocalizations? l10n) { if (error.contains('camera')) { - return 'Camera permission is required. Please enable it in settings.'; + return l10n?.permissionCameraRequired ?? + 'Camera permission is required. Please enable it in settings.'; } else if (error.contains('storage')) { - return 'Storage permission is required. Please enable it in settings.'; + return l10n?.permissionStorageRequired ?? + 'Storage permission is required. Please enable it in settings.'; } else if (error.contains('microphone')) { - return 'Microphone permission is required. Please enable it in settings.'; + return l10n?.permissionMicrophoneRequired ?? + 'Microphone permission is required. Please enable it in settings.'; } - return 'Permission required. Please check app permissions in settings.'; + return l10n?.permissionGenericError ?? + 'Permission required. Please check app permissions in settings.'; } - List _getPermissionRecoveryActions() { + List _getPermissionRecoveryActions( + AppLocalizations? l10n, + ) { return [ ErrorRecoveryAction( - label: 'Open Settings', + label: l10n?.openSettings ?? 'Open Settings', action: ErrorActionType.openSettings, - description: 'Open app settings to grant permissions', + description: + l10n?.actionOpenAppSettings ?? + 'Open app settings to grant permissions', ), ErrorRecoveryAction( - label: 'Retry', + label: l10n?.retry ?? 'Retry', action: ErrorActionType.retry, - description: 'Retry after granting permission', + description: + l10n?.actionRetryAfterPermission ?? + 'Retry after granting permission', ), ]; } - List _getGenericRecoveryActions() { + List _getGenericRecoveryActions(AppLocalizations? l10n) { return [ ErrorRecoveryAction( - label: 'Retry', + label: l10n?.retry ?? 'Retry', action: ErrorActionType.retry, - description: 'Retry the operation', + description: l10n?.actionRetryOperation ?? 'Retry the operation', ), ErrorRecoveryAction( - label: 'Go Back', + label: l10n?.back ?? 'Go Back', action: ErrorActionType.goBack, - description: 'Return to previous screen', + description: + l10n?.actionReturnToPrevious ?? 'Return to previous screen', ), ]; } diff --git a/lib/features/chat/views/chat_page.dart b/lib/features/chat/views/chat_page.dart index 1bc15db..7a28e67 100644 --- a/lib/features/chat/views/chat_page.dart +++ b/lib/features/chat/views/chat_page.dart @@ -2209,14 +2209,18 @@ class _ModelSelectorSheetState extends ConsumerState<_ModelSelectorSheet> { icon: Platform.isIOS ? CupertinoIcons.photo : Icons.image, - label: 'Multimodal', + label: AppLocalizations.of( + context, + )!.modelCapabilityMultimodal, ), if (_modelSupportsReasoning(model)) _capabilityChip( icon: Platform.isIOS ? CupertinoIcons.lightbulb : Icons.psychology_alt, - label: 'Reasoning', + label: AppLocalizations.of( + context, + )!.modelCapabilityReasoning, ), ], ), diff --git a/lib/features/chat/views/voice_call_page.dart b/lib/features/chat/views/voice_call_page.dart index 2780386..4774346 100644 --- a/lib/features/chat/views/voice_call_page.dart +++ b/lib/features/chat/views/voice_call_page.dart @@ -7,6 +7,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../core/providers/app_providers.dart'; import '../../../core/utils/markdown_to_text.dart'; +import '../../../l10n/app_localizations.dart'; import '../services/voice_call_service.dart'; class VoiceCallPage extends ConsumerStatefulWidget { @@ -102,21 +103,24 @@ class _VoiceCallPageState extends ConsumerState void _showErrorDialog(String message) { showDialog( context: context, - builder: (ctx) => AlertDialog( - title: const Text('Error'), - content: Text(message), - actions: [ - TextButton( - onPressed: () { - Navigator.of(ctx).pop(); - if (mounted) { - Navigator.of(context).pop(); - } - }, - child: const Text('OK'), - ), - ], - ), + builder: (ctx) { + final dialogL10n = AppLocalizations.of(ctx)!; + return AlertDialog( + title: Text(dialogL10n.error), + content: Text(message), + actions: [ + TextButton( + onPressed: () { + Navigator.of(ctx).pop(); + if (mounted) { + Navigator.of(context).pop(); + } + }, + child: Text(dialogL10n.ok), + ), + ], + ); + }, ); } @@ -139,11 +143,12 @@ class _VoiceCallPageState extends ConsumerState final primaryColor = Theme.of(context).colorScheme.primary; final backgroundColor = Theme.of(context).scaffoldBackgroundColor; final textColor = Theme.of(context).colorScheme.onSurface; + final l10n = AppLocalizations.of(context)!; return Scaffold( backgroundColor: backgroundColor, appBar: AppBar( - title: const Text('Voice Call'), + title: Text(l10n.voiceCallTitle), leading: IconButton( icon: const Icon(CupertinoIcons.xmark), onPressed: () async { @@ -198,10 +203,7 @@ class _VoiceCallPageState extends ConsumerState vertical: 16, ), child: Text( - 'Please check:\n' - '• Microphone permissions are granted\n' - '• Speech recognition is available on your device\n' - '• You are connected to the server', + l10n.voiceCallErrorHelp, textAlign: TextAlign.center, style: TextStyle( fontSize: 14, @@ -217,7 +219,7 @@ class _VoiceCallPageState extends ConsumerState // Control buttons Padding( padding: const EdgeInsets.all(32), - child: _buildControlButtons(primaryColor), + child: _buildControlButtons(primaryColor, l10n), ), ], ), @@ -345,7 +347,7 @@ class _VoiceCallPageState extends ConsumerState ); } - Widget _buildControlButtons(Color primaryColor) { + Widget _buildControlButtons(Color primaryColor, AppLocalizations l10n) { final errorColor = Theme.of(context).colorScheme.error; final warningColor = Colors.orange; final successColor = Theme.of(context).colorScheme.secondary; @@ -357,7 +359,7 @@ class _VoiceCallPageState extends ConsumerState buttons.add( _buildActionButton( icon: CupertinoIcons.arrow_clockwise, - label: 'Retry', + label: l10n.retry, color: primaryColor, onPressed: () async { await _initializeCall(); @@ -373,7 +375,7 @@ class _VoiceCallPageState extends ConsumerState buttons.add( _buildActionButton( icon: CupertinoIcons.pause_fill, - label: 'Pause', + label: l10n.voiceCallPause, color: warningColor, onPressed: () async { await _service?.pauseListening(); @@ -384,7 +386,7 @@ class _VoiceCallPageState extends ConsumerState buttons.add( _buildActionButton( icon: CupertinoIcons.play_fill, - label: 'Resume', + label: l10n.voiceCallResume, color: successColor, onPressed: () async { await _service?.resumeListening(); @@ -398,7 +400,7 @@ class _VoiceCallPageState extends ConsumerState buttons.add( _buildActionButton( icon: CupertinoIcons.stop_fill, - label: 'Stop', + label: l10n.voiceCallStop, color: warningColor, onPressed: () async { await _service?.cancelSpeaking(); @@ -411,7 +413,7 @@ class _VoiceCallPageState extends ConsumerState buttons.add( _buildActionButton( icon: CupertinoIcons.phone_down_fill, - label: 'End Call', + label: l10n.voiceCallEnd, color: errorColor, onPressed: () async { await _service?.stopCall(); @@ -453,23 +455,24 @@ class _VoiceCallPageState extends ConsumerState } String _getStateLabel() { + final l10n = AppLocalizations.of(context)!; switch (_currentState) { case VoiceCallState.idle: - return 'Ready'; + return l10n.voiceCallReady; case VoiceCallState.connecting: - return 'Connecting...'; + return l10n.voiceCallConnecting; case VoiceCallState.listening: - return 'Listening'; + return l10n.voiceCallListening; case VoiceCallState.paused: - return 'Paused'; + return l10n.voiceCallPaused; case VoiceCallState.processing: - return 'Thinking...'; + return l10n.voiceCallProcessing; case VoiceCallState.speaking: - return 'Speaking'; + return l10n.voiceCallSpeaking; case VoiceCallState.error: - return 'Error'; + return l10n.error; case VoiceCallState.disconnected: - return 'Disconnected'; + return l10n.voiceCallDisconnected; } } } diff --git a/lib/features/chat/widgets/assistant_message_widget.dart b/lib/features/chat/widgets/assistant_message_widget.dart index 00a12ce..bfd348c 100644 --- a/lib/features/chat/widgets/assistant_message_widget.dart +++ b/lib/features/chat/widgets/assistant_message_widget.dart @@ -1162,7 +1162,7 @@ class _AssistantMessageWidgetState extends ConsumerState // Inline version toggle: Prev [1/n] Next ChatActionButton( icon: Icons.chevron_left, - label: 'Prev', + label: l10n.previousLabel, onTap: () { setState(() { if (_activeVersionIndex < 0) { @@ -1181,7 +1181,7 @@ class _AssistantMessageWidgetState extends ConsumerState ), ChatActionButton( icon: Icons.chevron_right, - label: 'Next', + label: l10n.nextLabel, onTap: () { setState(() { if (_activeVersionIndex < 0) return; // already live diff --git a/lib/features/profile/views/app_customization_page.dart b/lib/features/profile/views/app_customization_page.dart index 1c5e0a0..c8adde3 100644 --- a/lib/features/profile/views/app_customization_page.dart +++ b/lib/features/profile/views/app_customization_page.dart @@ -23,17 +23,18 @@ class AppCustomizationPage extends ConsumerWidget { final settings = ref.watch(appSettingsProvider); final themeMode = ref.watch(appThemeModeProvider); final platformBrightness = MediaQuery.platformBrightnessOf(context); + final l10n = AppLocalizations.of(context)!; final themeDescription = () { if (themeMode == ThemeMode.system) { final systemThemeLabel = platformBrightness == Brightness.dark - ? AppLocalizations.of(context)!.themeDark - : AppLocalizations.of(context)!.themeLight; - return AppLocalizations.of(context)!.followingSystem(systemThemeLabel); + ? l10n.themeDark + : l10n.themeLight; + return l10n.followingSystem(systemThemeLabel); } if (themeMode == ThemeMode.dark) { - return AppLocalizations.of(context)!.currentlyUsingDarkTheme; + return l10n.currentlyUsingDarkTheme; } - return AppLocalizations.of(context)!.currentlyUsingLightTheme; + return l10n.currentlyUsingLightTheme; }(); final locale = ref.watch(appLocaleProvider); final currentLanguageCode = locale?.languageCode ?? 'system'; @@ -237,11 +238,12 @@ class AppCustomizationPage extends ConsumerWidget { WidgetRef ref, TweakcnThemeDefinition activeTheme, ) { + final l10n = AppLocalizations.of(context)!; final palettes = TweakcnThemes.all; return _ExpandableCard( - title: AppLocalizations.of(context)!.themePalette, - subtitle: activeTheme.label, + title: l10n.themePalette, + subtitle: activeTheme.label(l10n), icon: UiUtils.platformIcon( ios: CupertinoIcons.square_fill_on_square_fill, android: Icons.palette, @@ -252,6 +254,7 @@ class AppCustomizationPage extends ConsumerWidget { for (final palette in palettes) _PaletteOption( themeDefinition: palette, + l10n: l10n, activeId: activeTheme.id, onSelect: () => ref .read(appThemePaletteProvider.notifier) @@ -333,9 +336,7 @@ class AppCustomizationPage extends ConsumerWidget { } final l10n = AppLocalizations.of(context)!; - final selectedCountText = selectedCount == 0 - ? 'No actions' - : '$selectedCount action${selectedCount == 1 ? '' : 's'} selected'; + final selectedCountText = l10n.quickActionsSelectedCount(selectedCount); return _ExpandableCard( title: l10n.quickActionsDescription, @@ -499,13 +500,13 @@ class AppCustomizationPage extends ConsumerWidget { ), color: theme.buttonPrimary, ), - const SizedBox(width: Spacing.sm), + const SizedBox(width: Spacing.md), Text( - 'Engine', + l10n.ttsEngineLabel, style: theme.bodyMedium?.copyWith( color: theme.sidebarForeground, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, ) ?? TextStyle(color: theme.sidebarForeground, fontSize: 14), ), @@ -514,7 +515,7 @@ class AppCustomizationPage extends ConsumerWidget { spacing: Spacing.sm, children: [ ChoiceChip( - label: const Text('On Device'), + label: Text(l10n.ttsEngineDevice), selected: settings.ttsEngine == TtsEngine.device, showCheckmark: false, selectedColor: theme.buttonPrimary, @@ -541,7 +542,7 @@ class AppCustomizationPage extends ConsumerWidget { }, ), ChoiceChip( - label: const Text('Server'), + label: Text(l10n.ttsEngineServer), selected: settings.ttsEngine == TtsEngine.server, showCheckmark: false, selectedColor: theme.buttonPrimary, @@ -1078,7 +1079,7 @@ class AppCustomizationPage extends ConsumerWidget { if (!context.mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text('${l10n.error}: $e'), + content: Text(l10n.errorWithMessage(e.toString())), backgroundColor: theme.error, ), ); @@ -1509,11 +1510,13 @@ class _PaletteOption extends StatelessWidget { required this.themeDefinition, required this.activeId, required this.onSelect, + required this.l10n, }); final TweakcnThemeDefinition themeDefinition; final String activeId; final VoidCallback onSelect; + final AppLocalizations l10n; @override Widget build(BuildContext context) { @@ -1544,7 +1547,7 @@ class _PaletteOption extends StatelessWidget { children: [ Expanded( child: Text( - themeDefinition.label, + themeDefinition.label(l10n), style: theme.bodyMedium?.copyWith( color: theme.sidebarForeground, fontWeight: isSelected @@ -1567,7 +1570,7 @@ class _PaletteOption extends StatelessWidget { ), const SizedBox(height: Spacing.xxs), Text( - themeDefinition.description, + themeDefinition.description(l10n), style: theme.bodySmall?.copyWith( color: theme.sidebarForeground.withValues( diff --git a/lib/features/profile/views/profile_page.dart b/lib/features/profile/views/profile_page.dart index d244fb7..57aa576 100644 --- a/lib/features/profile/views/profile_page.dart +++ b/lib/features/profile/views/profile_page.dart @@ -1172,7 +1172,7 @@ class _DefaultModelBottomSheetState if (isAutoSelect) ...[ const SizedBox(height: Spacing.xs), Text( - 'Let the app choose the best model', + AppLocalizations.of(context)!.autoSelectDescription, style: TextStyle( fontSize: AppTypography.bodySmall, color: context.conduitTheme.textSecondary, @@ -1189,14 +1189,18 @@ class _DefaultModelBottomSheetState icon: Platform.isIOS ? CupertinoIcons.photo : Icons.image, - label: 'Multimodal', + label: AppLocalizations.of( + context, + )!.modelCapabilityMultimodal, ), if (_modelSupportsReasoning(model)) _capabilityChip( icon: Platform.isIOS ? CupertinoIcons.lightbulb : Icons.psychology_alt, - label: 'Reasoning', + label: AppLocalizations.of( + context, + )!.modelCapabilityReasoning, ), ], ), diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 55fe232..c99b5fa 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -1,7 +1,6 @@ { "@@locale": "de", "appTitle": "Conduit", - "initializationFailed": "Initialisierung fehlgeschlagen", "retry": "Erneut versuchen", "back": "Zurück", "you": "Du", @@ -16,10 +15,6 @@ "@connectionIssueSubtitle": { "description": "Untertitel mit den verfügbaren Aktionen, wenn der Server nicht erreichbar ist" }, - "stillOfflineMessage": "Der Server ist weiterhin nicht erreichbar. Prüfe deine Verbindung und versuche es erneut.", - "@stillOfflineMessage": { - "description": "Statusnachricht nach einem erneuten Versuch ohne wiederhergestellte Verbindung" - }, "account": "Konto", "supportConduit": "Conduit unterstützen", "supportConduitSubtitle": "Hilf, die Weiterentwicklung und neue Funktionen zu finanzieren.", @@ -37,32 +32,14 @@ "noResults": "Keine Ergebnisse", "searchModels": "Modelle suchen...", "errorMessage": "Etwas ist schief gelaufen. Bitte versuche es erneut.", - "loginButton": "Anmelden", - "menuItem": "Einstellungen", - "dynamicContentWithPlaceholder": "Willkommen, {name}!", - "itemsCount": "{count, plural, =0{Keine Elemente} one{1 Element} other{{count} Elemente}}", "closeButtonSemantic": "Schließen", "loadingContent": "Inhalt wird geladen", "noItems": "Keine Elemente", "noItemsToDisplay": "Keine Elemente zum Anzeigen", - "loadMore": "Mehr laden", - "workspace": "Arbeitsbereich", - "recentFiles": "Zuletzt verwendete Dateien", "knowledgeBase": "Wissensdatenbank", - "noFilesYet": "Noch keine Dateien", - "uploadDocsPrompt": "Lade Dokumente hoch, um sie in deinen Unterhaltungen mit Conduit zu verwenden", - "uploadFirstFile": "Erste Datei hochladen", "attachments": "Anhänge", - "knowledgeBaseEmpty": "Wissensdatenbank ist leer", - "createCollectionsPrompt": "Erstelle Sammlungen verwandter Dokumente zur einfachen Referenz", - "chooseSourcePhoto": "Quelle auswählen", "takePhoto": "Foto aufnehmen", - "chooseFromGallery": "Aus Fotos auswählen", "document": "Dokument", - "documentHint": "PDF-, Word- oder Textdatei", - "uploadFileTitle": "Datei hochladen", - "fileUploadComingSoon": "Dateiupload für {type} kommt bald!", - "kbCreationComingSoon": "Erstellung der Wissensdatenbank kommt bald!", "backToServerSetup": "Zur Servereinrichtung zurück", "connectedToServer": "Mit Server verbunden", "signIn": "Anmelden", @@ -124,9 +101,7 @@ "onboardQuickTitle": "Schnellaktionen", "onboardQuickSubtitle": "Menü öffnen, um zwischen Chats, Arbeitsbereich und Profil zu wechseln.", "onboardQuickBullet1": "Menü tippen für Chats, Arbeitsbereich, Profil", - "onboardQuickBullet2": "Neuer Chat starten oder Modelle oben verwalten" - , - "addAttachment": "Anhang hinzufügen", + "onboardQuickBullet2": "Neuer Chat starten oder Modelle oben verwalten", "attachmentLabel": "Anhang", "tools": "Werkzeuge", "voiceInput": "Spracheingabe", @@ -157,19 +132,13 @@ "invalidDataUrl": "Ungültiges Data-URL-Format", "failedToDecodeImage": "Bild konnte nicht decodiert werden", "invalidImageFormat": "Ungültiges Bildformat", - "emptyImageData": "Leere Bilddaten" - , - "featureRequiresInternet": "Diese Funktion erfordert eine Internetverbindung", - "messagesWillSendWhenOnline": "Nachrichten werden gesendet, sobald du wieder online bist", + "emptyImageData": "Leere Bilddaten", "confirm": "Bestätigen", - "cancel": "Abbrechen" - , + "cancel": "Abbrechen", "ok": "OK", "inputField": "Eingabefeld", - "captureDocumentOrImage": "Dokument oder Bild aufnehmen", "checkConnection": "Verbindung prüfen", "openSettings": "Einstellungen öffnen", - "chooseDifferentFile": "Andere Datei wählen", "goBack": "Zurück", "technicalDetails": "Technische Details", "save": "Speichern", @@ -181,18 +150,9 @@ "newChat": "Neuer Chat", "more": "Mehr", "clear": "Leeren", - "searchHint": "Suchen...", "searchConversations": "Konversationen durchsuchen...", "create": "Erstellen", - "folderCreated": "Ordner erstellt", "failedToCreateFolder": "Ordner konnte nicht erstellt werden", - "movedChatToFolder": "\"{title}\" nach \"{folder}\" verschoben", - "@movedChatToFolder": { - "placeholders": { - "title": {"type": "String"}, - "folder": {"type": "String"} - } - }, "failedToMoveChat": "Chat konnte nicht verschoben werden", "failedToLoadChats": "Chats konnten nicht geladen werden", "failedToUpdatePin": "Pin konnte nicht aktualisiert werden", @@ -208,7 +168,7 @@ "archive": "Archivieren", "pin": "Anheften", "unpin": "Lösen", - "recent": "Zuletzt", + "recent": "Zuletzt", "system": "System", "english": "Englisch", "deutsch": "Deutsch", @@ -222,13 +182,17 @@ "deleteMessagesMessage": "{count} Nachrichten löschen?", "@deleteMessagesMessage": { "placeholders": { - "count": {"type": "int"} + "count": { + "type": "int" + } } }, "routeNotFound": "Route nicht gefunden: {routeName}", "@routeNotFound": { "placeholders": { - "routeName": {"type": "String"} + "routeName": { + "type": "String" + } } }, "deleteChatTitle": "Chat löschen", @@ -254,8 +218,7 @@ "ttsStop": "Stoppen", "edit": "Bearbeiten", "regenerate": "Neu generieren", - "noConversationsYet": "Noch keine Unterhaltungen" - , + "noConversationsYet": "Noch keine Unterhaltungen", "usernameOrEmailHint": "Gib deinen Benutzernamen oder deine E‑Mail ein", "passwordHint": "Gib dein Passwort ein", "enterApiKey": "Gib deinen API-Schlüssel ein", @@ -275,31 +238,55 @@ "headerNameTooLong": "Header-Name zu lang (max. 64 Zeichen)", "headerNameInvalidChars": "Ungültiger Header-Name. Verwende nur Buchstaben, Zahlen und diese Zeichen: !#$&-^_`|~", "headerNameReserved": "Reservierten Header \"{key}\" kann nicht überschrieben werden", - "@headerNameReserved": {"placeholders": {"key": {"type": "String"}}}, + "@headerNameReserved": { + "placeholders": { + "key": { + "type": "String" + } + } + }, "headerValueEmpty": "Header-Wert darf nicht leer sein", "headerValueTooLong": "Header-Wert zu lang (max. 1024 Zeichen)", "headerValueInvalidChars": "Header-Wert enthält ungültige Zeichen. Nur druckbare ASCII-Zeichen verwenden.", "headerValueUnsafe": "Header-Wert scheint potenziell unsicheren Inhalt zu enthalten", "headerAlreadyExists": "Header \"{key}\" existiert bereits. Zum Aktualisieren zuerst entfernen.", - "@headerAlreadyExists": {"placeholders": {"key": {"type": "String"}}}, - "maxHeadersReachedDetail": "Maximal 10 benutzerdefinierte Header zulässig. Einige entfernen, um mehr hinzuzufügen." - , - "editMessage": "Nachricht bearbeiten" - , + "@headerAlreadyExists": { + "placeholders": { + "key": { + "type": "String" + } + } + }, + "maxHeadersReachedDetail": "Maximal 10 benutzerdefinierte Header zulässig. Einige entfernen, um mehr hinzuzufügen.", "noModelsAvailable": "Keine Modelle verfügbar", "followingSystem": "Dem System folgen: {theme}", - "@followingSystem": {"placeholders": {"theme": {"type": "String"}}}, + "@followingSystem": { + "placeholders": { + "theme": { + "type": "String" + } + } + }, "themeDark": "Dunkel", "themePalette": "Farbpalette", - "@themePalette": {"description": "Titel für die Auswahl der App-Farbpalette."}, - "themePaletteDescription": "Wählen Sie die Akzentfarben für Schaltflächen, Karten und Chatblasen.", - "@themePaletteDescription": {"description": "Hilfetext zur Erklärung der Palettenauswahl."}, + "@themePalette": { + "description": "Titel für die Auswahl der App-Farbpalette." + }, "themeLight": "Hell", "currentlyUsingDarkTheme": "Aktuell dunkles Thema", "currentlyUsingLightTheme": "Aktuell helles Thema", "aboutConduit": "Über Conduit", "versionLabel": "Version: {version} ({build})", - "@versionLabel": {"placeholders": {"version": {"type": "String"}, "build": {"type": "String"}}}, + "@versionLabel": { + "placeholders": { + "version": { + "type": "String" + }, + "build": { + "type": "String" + } + } + }, "githubRepository": "GitHub-Repository", "unableToLoadAppInfo": "App-Informationen konnten nicht geladen werden", "thinking": "Denkt…", @@ -307,9 +294,13 @@ "thoughtForDuration": "Gedacht für {duration}", "@thoughtForDuration": { "description": "Zeigt an, wie lange der Assistent nachgedacht hat.", - "placeholders": {"duration": {"type": "String", "example": "3 s"}} - } - , + "placeholders": { + "duration": { + "type": "String", + "example": "3 s" + } + } + }, "appCustomization": "Anpassung", "appCustomizationSubtitle": "Design, Sprache, Stimme und Quick Pills", "quickActionsDescription": "Schnellzugriffe im Chat", @@ -332,12 +323,413 @@ "display": "Anzeige", "realtime": "Echtzeit", "transportMode": "Transportmodus", - "transportModeDescription": "Wähle, wie die App für Echtzeit-Updates verbindet.", "mode": "Modus", "transportModePolling": "Polling-Fallback", "transportModeWs": "Nur WebSocket", "transportModePollingInfo": "Fällt auf HTTP-Polling zurück, wenn WebSockets blockiert sind. Wechselt nach Möglichkeit zu WebSocket.", "transportModeWsInfo": "Geringerer Overhead, kann jedoch hinter strikten Proxys/Firewalls fehlschlagen.", - "websocketConnectionError": "Echtzeit-Verbindung konnte nicht hergestellt werden. Bitte überprüfen Sie Ihr Netzwerk und die Serverkonfiguration.", - "websocketReconnectFailed": "Echtzeit-Verbindung fehlgeschlagen. Streaming funktioniert möglicherweise nicht ordnungsgemäß." + "quickActionsSelectedCount": "{count, plural, =0{Keine Aktionen ausgewählt} one{{count} Aktion ausgewählt} other{{count} Aktionen ausgewählt}}", + "@quickActionsSelectedCount": { + "description": "Subtitle indicating how many quick actions are selected.", + "placeholders": { + "count": { + "type": "int", + "example": "2" + } + } + }, + "autoSelectDescription": "Lass die App das beste Modell auswählen", + "@autoSelectDescription": { + "description": "Explains what the auto-select model setting does." + }, + "ttsEngineLabel": "Engine", + "@ttsEngineLabel": { + "description": "Label for selecting the text-to-speech engine." + }, + "ttsEngineDevice": "Auf dem Gerät", + "@ttsEngineDevice": { + "description": "Chip label for using on-device text-to-speech." + }, + "ttsEngineServer": "Server", + "@ttsEngineServer": { + "description": "Chip label for using server-side text-to-speech." + }, + "modelCapabilityMultimodal": "Multimodal", + "@modelCapabilityMultimodal": { + "description": "Capability chip label for models that support multimodal input." + }, + "modelCapabilityReasoning": "Reasoning", + "@modelCapabilityReasoning": { + "description": "Capability chip label for models that support reasoning features." + }, + "voiceCallTitle": "Sprachanruf", + "@voiceCallTitle": { + "description": "Title displayed on the voice call screen." + }, + "voiceCallPause": "Pause", + "@voiceCallPause": { + "description": "Button label to pause a voice call." + }, + "voiceCallResume": "Fortsetzen", + "@voiceCallResume": { + "description": "Button label to resume a paused voice call." + }, + "voiceCallStop": "Stopp", + "@voiceCallStop": { + "description": "Button label to stop the active voice call." + }, + "voiceCallEnd": "Anruf beenden", + "@voiceCallEnd": { + "description": "Button label to end the voice call session." + }, + "chooseDifferentFile": "Andere Datei auswählen", + "@chooseDifferentFile": { + "description": "Action label prompting the user to pick another file." + }, + "errorWithMessage": "Fehler: {message}", + "@errorWithMessage": { + "description": "Error label with appended message text.", + "placeholders": { + "message": { + "type": "String", + "example": "Network timeout" + } + } + }, + "networkTimeoutError": "Verbindung abgelaufen. Bitte überprüfe deine Internetverbindung und versuche es erneut.", + "@networkTimeoutError": { + "description": "User-facing message when a network request times out." + }, + "networkUnreachableError": "Server nicht erreichbar. Bitte überprüfe die Server-URL und deine Internetverbindung.", + "@networkUnreachableError": { + "description": "User-facing message when the server cannot be reached." + }, + "networkServerNotResponding": "Server reagiert nicht. Bitte stelle sicher, dass der Server läuft und erreichbar ist.", + "@networkServerNotResponding": { + "description": "User-facing message when the server does not respond to a request." + }, + "networkGenericError": "Netzwerkproblem. Bitte überprüfe deine Internetverbindung.", + "@networkGenericError": { + "description": "Fallback message for generic network errors." + }, + "serverError500": "Der Server hat Probleme. Das ist meist nur vorübergehend.", + "@serverError500": { + "description": "Message when a 500 error is encountered." + }, + "serverErrorUnavailable": "Server vorübergehend nicht verfügbar. Bitte versuche es gleich noch einmal.", + "@serverErrorUnavailable": { + "description": "Message when a 502/503 error is encountered." + }, + "serverErrorTimeout": "Der Server hat zu lange für eine Antwort gebraucht. Bitte versuche es erneut.", + "@serverErrorTimeout": { + "description": "Message when the server times out." + }, + "serverErrorGeneric": "Der Server hat Schwierigkeiten. Bitte versuche es später erneut.", + "@serverErrorGeneric": { + "description": "Fallback server error message." + }, + "authSessionExpired": "Deine Sitzung ist abgelaufen. Bitte melde dich erneut an.", + "@authSessionExpired": { + "description": "Message when an authentication session expires." + }, + "authForbidden": "Du hast keine Berechtigung für diese Aktion.", + "@authForbidden": { + "description": "Message when the user lacks required permissions." + }, + "authInvalidToken": "Der Authentifizierungstoken ist ungültig. Bitte melde dich erneut an.", + "@authInvalidToken": { + "description": "Message when the authentication token is invalid." + }, + "authGenericError": "Authentifizierungsproblem. Bitte melde dich erneut an.", + "@authGenericError": { + "description": "Fallback authentication error message." + }, + "validationInvalidEmail": "Bitte gib eine gültige E-Mail-Adresse ein.", + "@validationInvalidEmail": { + "description": "Validation message for invalid email input." + }, + "validationWeakPassword": "Das Passwort erfüllt die Anforderungen nicht. Bitte überprüfe es und versuche es erneut.", + "@validationWeakPassword": { + "description": "Validation message for weak passwords." + }, + "validationMissingRequired": "Bitte fülle alle Pflichtfelder aus.", + "@validationMissingRequired": { + "description": "Validation message when required fields are missing." + }, + "validationFormatError": "Einige Angaben haben ein falsches Format. Bitte überprüfe sie und versuche es erneut.", + "@validationFormatError": { + "description": "Validation message for generic formatting issues." + }, + "validationGenericError": "Bitte überprüfe deine Eingaben und versuche es erneut.", + "@validationGenericError": { + "description": "Fallback validation message." + }, + "fileNotFound": "Datei nicht gefunden. Vielleicht wurde sie verschoben oder gelöscht.", + "@fileNotFound": { + "description": "Message when a file cannot be located." + }, + "fileAccessDenied": "Datei kann nicht geöffnet werden. Bitte prüfe die Berechtigungen.", + "@fileAccessDenied": { + "description": "Message when file access is denied." + }, + "fileTooLarge": "Datei ist zu groß. Bitte wähle eine kleinere Datei.", + "@fileTooLarge": { + "description": "Message when a file exceeds size limits." + }, + "fileGenericError": "Problem mit der Datei. Bitte versuche eine andere Datei.", + "@fileGenericError": { + "description": "Fallback file error message." + }, + "permissionCameraRequired": "Kamerazugriff erforderlich. Bitte aktiviere ihn in den Einstellungen.", + "@permissionCameraRequired": { + "description": "Message when camera permission is missing." + }, + "permissionStorageRequired": "Speicherzugriff erforderlich. Bitte aktiviere ihn in den Einstellungen.", + "@permissionStorageRequired": { + "description": "Message when storage permission is missing." + }, + "permissionMicrophoneRequired": "Mikrofonzugriff erforderlich. Bitte aktiviere ihn in den Einstellungen.", + "@permissionMicrophoneRequired": { + "description": "Message when microphone permission is missing." + }, + "permissionGenericError": "Berechtigung erforderlich. Bitte prüfe die App-Berechtigungen in den Einstellungen.", + "@permissionGenericError": { + "description": "Fallback permission error message." + }, + "actionRetryRequest": "Versuche die Anfrage erneut.", + "@actionRetryRequest": { + "description": "Description for retrying a failed request." + }, + "actionVerifyConnection": "Überprüfe deine Internetverbindung.", + "@actionVerifyConnection": { + "description": "Description for checking internet connectivity." + }, + "actionRetryOperation": "Wiederhole den Vorgang.", + "@actionRetryOperation": { + "description": "Description for retrying the same operation." + }, + "actionRetryAfterDelay": "Warte einen Moment und versuche es dann erneut.", + "@actionRetryAfterDelay": { + "description": "Description suggesting a short delay before retrying." + }, + "actionSignInToAccount": "Melde dich bei deinem Konto an.", + "@actionSignInToAccount": { + "description": "Description for signing back into the app." + }, + "actionSelectAnotherFile": "Wähle eine andere Datei.", + "@actionSelectAnotherFile": { + "description": "Description for choosing a different file." + }, + "actionOpenAppSettings": "Öffne die App-Einstellungen, um Berechtigungen zu erteilen.", + "@actionOpenAppSettings": { + "description": "Description for opening system or app settings." + }, + "actionRetryAfterPermission": "Versuche es erneut, nachdem du die Berechtigung erteilt hast.", + "@actionRetryAfterPermission": { + "description": "Description for retrying once permissions are granted." + }, + "actionReturnToPrevious": "Zur vorherigen Ansicht zurückkehren.", + "@actionReturnToPrevious": { + "description": "Description for navigating back to the prior screen." + }, + "continueAction": "Weiter", + "@continueAction": { + "description": "Button label to continue an action or flow." + }, + "loadingShort": "Laden", + "@loadingShort": { + "description": "Short loading label used for accessibility." + }, + "loadingAnnouncement": "Laden: {message}", + "@loadingAnnouncement": { + "description": "Screen reader announcement when loading a resource.", + "placeholders": { + "message": { + "type": "String", + "example": "Messages" + } + } + }, + "errorAnnouncement": "Fehler: {error}", + "@errorAnnouncement": { + "description": "Screen reader announcement for an error.", + "placeholders": { + "error": { + "type": "String", + "example": "Network timeout" + } + } + }, + "errorAnnouncementWithSuggestion": "Fehler: {error}. {suggestion}", + "@errorAnnouncementWithSuggestion": { + "description": "Screen reader announcement for an error with a follow-up suggestion.", + "placeholders": { + "error": { + "type": "String", + "example": "Network timeout" + }, + "suggestion": { + "type": "String", + "example": "Please try again later." + } + } + }, + "successAnnouncement": "Erfolg: {message}", + "@successAnnouncement": { + "description": "Screen reader announcement for successful actions.", + "placeholders": { + "message": { + "type": "String", + "example": "Profile updated" + } + } + }, + "requiredFieldLabel": "{label} *", + "@requiredFieldLabel": { + "description": "Label text indicating a required field.", + "placeholders": { + "label": { + "type": "String", + "example": "Email" + } + } + }, + "requiredFieldHelper": "Pflichtfeld", + "@requiredFieldHelper": { + "description": "Helper text indicating that the field is required." + }, + "switchOnLabel": "Ein", + "@switchOnLabel": { + "description": "Semantic label when a switch is enabled." + }, + "switchOffLabel": "Aus", + "@switchOffLabel": { + "description": "Semantic label when a switch is disabled." + }, + "dialogSemanticLabel": "Dialog: {title}", + "@dialogSemanticLabel": { + "description": "Semantic label describing the dialog title.", + "placeholders": { + "title": { + "type": "String", + "example": "Settings" + } + } + }, + "previousLabel": "Zurück", + "@previousLabel": { + "description": "Label for navigating to the previous item." + }, + "nextLabel": "Weiter", + "@nextLabel": { + "description": "Label for navigating to the next item." + }, + "themePaletteConduitLabel": "Conduit", + "@themePaletteConduitLabel": { + "description": "Palette name for the default Conduit theme." + }, + "themePaletteConduitDescription": "Schlichtes neutrales Design für Conduit.", + "@themePaletteConduitDescription": { + "description": "Description of the Conduit palette." + }, + "themePaletteClaudeLabel": "Claude", + "@themePaletteClaudeLabel": { + "description": "Palette name inspired by the Claude web client." + }, + "themePaletteClaudeDescription": "Warmes, haptisches Farbschema aus dem Claude-Webclient.", + "@themePaletteClaudeDescription": { + "description": "Description of the Claude palette." + }, + "themePaletteT3ChatLabel": "T3 Chat", + "@themePaletteT3ChatLabel": { + "description": "Palette name inspired by the T3 Stack brand." + }, + "themePaletteT3ChatDescription": "Verspielte Verläufe inspiriert vom T3-Stack.", + "@themePaletteT3ChatDescription": { + "description": "Description of the T3 Chat palette." + }, + "themePaletteCatppuccinLabel": "Catppuccin", + "@themePaletteCatppuccinLabel": { + "description": "Palette name for Catppuccin colors." + }, + "themePaletteCatppuccinDescription": "Sanfte Pastellpalette.", + "@themePaletteCatppuccinDescription": { + "description": "Description of the Catppuccin palette." + }, + "themePaletteTangerineLabel": "Tangerine", + "@themePaletteTangerineLabel": { + "description": "Palette name for Tangerine colors." + }, + "themePaletteTangerineDescription": "Warmes Orange-Schiefer-Farbschema.", + "@themePaletteTangerineDescription": { + "description": "Description of the Tangerine palette." + }, + "@onboardStartTitle": { + "description": "Onboarding card: start chatting title.", + "placeholders": { + "username": { + "type": "String", + "example": "Alex" + } + } + }, + "@notAnImageFile": { + "description": "Error when a referenced file is not an image.", + "placeholders": { + "fileName": { + "type": "String", + "example": "image.txt" + } + } + }, + "@failedToLoadImage": { + "description": "Error including the underlying reason when image loading fails.", + "placeholders": { + "error": { + "type": "String", + "example": "Network error" + } + } + }, + "@ttsVoicesForLanguage": { + "description": "Section header for voices matching the app language", + "placeholders": { + "language": { + "type": "String", + "example": "EN" + } + } + }, + "voiceCallReady": "Bereit", + "@voiceCallReady": { + "description": "Status label shown when the voice call is ready to start." + }, + "voiceCallConnecting": "Verbinden...", + "@voiceCallConnecting": { + "description": "Status label shown while the voice call is connecting." + }, + "voiceCallListening": "Zuhören", + "@voiceCallListening": { + "description": "Status label shown while the call is listening for input." + }, + "voiceCallPaused": "Pausiert", + "@voiceCallPaused": { + "description": "Status label shown when the call is paused." + }, + "voiceCallProcessing": "Denkt...", + "@voiceCallProcessing": { + "description": "Status label shown while the call processes a response." + }, + "voiceCallSpeaking": "Spricht", + "@voiceCallSpeaking": { + "description": "Status label shown while the assistant is speaking." + }, + "voiceCallDisconnected": "Getrennt", + "@voiceCallDisconnected": { + "description": "Status label shown when the voice call has ended or disconnected." + }, + "voiceCallErrorHelp": "Bitte prüfe:\n• Mikrofonberechtigungen sind erteilt\n• Spracherkennung ist auf deinem Gerät verfügbar\n• Du bist mit dem Server verbunden", + "@voiceCallErrorHelp": { + "description": "Guidance shown when the voice call encounters an error." + } } diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 5a9c825..d45abb4 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1,19 +1,29 @@ { "@@locale": "en", "appTitle": "Conduit", - "@appTitle": {"description": "Application name displayed in the app and OS UI."}, - "initializationFailed": "Initialization Failed", - "@initializationFailed": {"description": "Shown if the app fails to initialize critical services."}, + "@appTitle": { + "description": "Application name displayed in the app and OS UI." + }, "retry": "Retry", - "@retry": {"description": "Button label to try an action again."}, + "@retry": { + "description": "Button label to try an action again." + }, "back": "Back", - "@back": {"description": "Back navigation label/tooltip."}, + "@back": { + "description": "Back navigation label/tooltip." + }, "you": "You", - "@you": {"description": "Profile tab title."}, + "@you": { + "description": "Profile tab title." + }, "loadingProfile": "Loading profile...", - "@loadingProfile": {"description": "Progress message while fetching profile data."}, + "@loadingProfile": { + "description": "Progress message while fetching profile data." + }, "unableToLoadProfile": "Unable to load profile", - "@unableToLoadProfile": {"description": "Error title shown when profile request fails."}, + "@unableToLoadProfile": { + "description": "Error title shown when profile request fails." + }, "pleaseCheckConnection": "Please check your connection and try again", "connectionIssueTitle": "Can't reach your server", "@connectionIssueTitle": { @@ -23,13 +33,13 @@ "@connectionIssueSubtitle": { "description": "Subtitle explaining available actions when the server cannot be reached" }, - "stillOfflineMessage": "We still can't reach the server. Double-check your connection and try again.", - "@stillOfflineMessage": { - "description": "Status message after a retry when connectivity has not been restored" + "@pleaseCheckConnection": { + "description": "Generic connectivity hint after an error." }, - "@pleaseCheckConnection": {"description": "Generic connectivity hint after an error."}, "account": "Account", - "@account": {"description": "Section header for account-related options."}, + "@account": { + "description": "Section header for account-related options." + }, "supportConduit": "Support Conduit", "@supportConduit": { "description": "Section header inviting the user to financially support the project." @@ -55,234 +65,403 @@ "description": "Subtitle encouraging one-time donations via Buy Me a Coffee." }, "signOut": "Sign Out", - "@signOut": {"description": "Button/title for signing out of the app."}, + "@signOut": { + "description": "Button/title for signing out of the app." + }, "endYourSession": "End your session", - "@endYourSession": {"description": "Subtitle explaining the sign-out action."}, + "@endYourSession": { + "description": "Subtitle explaining the sign-out action." + }, "defaultModel": "Default Model", - "@defaultModel": {"description": "Label for choosing a default AI model."}, + "@defaultModel": { + "description": "Label for choosing a default AI model." + }, "autoSelect": "Auto-select", - "@autoSelect": {"description": "Option to let the app pick a suitable model automatically."}, + "@autoSelect": { + "description": "Option to let the app pick a suitable model automatically." + }, "loadingModels": "Loading models...", - "@loadingModels": {"description": "Progress message while fetching model list."}, + "@loadingModels": { + "description": "Progress message while fetching model list." + }, "failedToLoadModels": "Failed to load models", - "@failedToLoadModels": {"description": "Error message shown when model list cannot be retrieved."}, + "@failedToLoadModels": { + "description": "Error message shown when model list cannot be retrieved." + }, "availableModels": "Available Models", - "@availableModels": {"description": "Header above a list of models to select from."}, + "@availableModels": { + "description": "Header above a list of models to select from." + }, + "modelCapabilityMultimodal": "Multimodal", + "@modelCapabilityMultimodal": { + "description": "Capability chip label for models that support multimodal input." + }, + "modelCapabilityReasoning": "Reasoning", + "@modelCapabilityReasoning": { + "description": "Capability chip label for models that support reasoning features." + }, "noResults": "No results", - "@noResults": {"description": "Shown when a search returns no matches."}, + "@noResults": { + "description": "Shown when a search returns no matches." + }, "searchModels": "Search models...", - "@searchModels": {"description": "Hint text for model search input."}, + "@searchModels": { + "description": "Hint text for model search input." + }, "errorMessage": "Something went wrong. Please try again.", - "@errorMessage": {"description": "Generic error message for unexpected failures."}, - "loginButton": "Login", - "@loginButton": {"description": "Button text for the login action."}, - "menuItem": "Settings", - "@menuItem": {"description": "Generic settings menu item label."}, - "dynamicContentWithPlaceholder": "Welcome, {name}!", - "@dynamicContentWithPlaceholder": { - "description": "Greeting message with a dynamic user name.", + "@errorMessage": { + "description": "Generic error message for unexpected failures." + }, + "closeButtonSemantic": "Close", + "@closeButtonSemantic": { + "description": "Accessible label for a generic Close button." + }, + "loadingContent": "Loading content", + "@loadingContent": { + "description": "Shown while loading page content." + }, + "loadingShort": "Loading", + "@loadingShort": { + "description": "Short loading label used for accessibility." + }, + "loadingAnnouncement": "Loading: {message}", + "@loadingAnnouncement": { + "description": "Screen reader announcement when loading a resource.", "placeholders": { - "name": { + "message": { + "type": "String", + "example": "Messages" + } + } + }, + "errorAnnouncement": "Error: {error}", + "@errorAnnouncement": { + "description": "Screen reader announcement for an error.", + "placeholders": { + "error": { + "type": "String", + "example": "Network timeout" + } + } + }, + "errorAnnouncementWithSuggestion": "Error: {error}. {suggestion}", + "@errorAnnouncementWithSuggestion": { + "description": "Screen reader announcement for an error with a follow-up suggestion.", + "placeholders": { + "error": { + "type": "String", + "example": "Network timeout" + }, + "suggestion": { + "type": "String", + "example": "Please try again later." + } + } + }, + "successAnnouncement": "Success: {message}", + "@successAnnouncement": { + "description": "Screen reader announcement for successful actions.", + "placeholders": { + "message": { + "type": "String", + "example": "Profile updated" + } + } + }, + "noItems": "No items", + "@noItems": { + "description": "Placeholder text when a list is empty." + }, + "noItemsToDisplay": "No items to display", + "@noItemsToDisplay": { + "description": "Alternative empty-state description." + }, + "knowledgeBase": "Knowledge Base", + "@knowledgeBase": { + "description": "Section for knowledge base content." + }, + "attachments": "Attachments", + "@attachments": { + "description": "Header above list of attached files in compose area." + }, + "takePhoto": "Take a photo", + "@takePhoto": { + "description": "Action to open camera and capture a new photo." + }, + "document": "Document", + "@document": { + "description": "Generic document label used in UI." + }, + "backToServerSetup": "Back to server setup", + "@backToServerSetup": { + "description": "Button/back label to return to server configuration flow." + }, + "connectedToServer": "Connected to Server", + "@connectedToServer": { + "description": "Status label indicating a successful server connection." + }, + "signIn": "Sign In", + "@signIn": { + "description": "Button/heading for sign-in flows." + }, + "enterCredentials": "Enter your credentials to access your AI conversations", + "@enterCredentials": { + "description": "Instructional text on the sign-in screen." + }, + "credentials": "Credentials", + "@credentials": { + "description": "Header for credential input section." + }, + "apiKey": "API Key", + "@apiKey": { + "description": "Label for API key input field." + }, + "usernameOrEmail": "Username or Email", + "@usernameOrEmail": { + "description": "Label for username/email input field." + }, + "password": "Password", + "@password": { + "description": "Label for password input field." + }, + "signInWithApiKey": "Sign in with API Key", + "@signInWithApiKey": { + "description": "Alternative sign-in method using an API key." + }, + "connectToServer": "Connect to Server", + "@connectToServer": { + "description": "Call-to-action button for server connection." + }, + "enterServerAddress": "Enter your Open-WebUI server address to get started", + "@enterServerAddress": { + "description": "Instruction telling user to provide server URL to begin." + }, + "serverUrl": "Server URL", + "@serverUrl": { + "description": "Label for server URL field." + }, + "serverUrlHint": "https://your-server.com", + "@serverUrlHint": { + "description": "Hint text showing example server URL format." + }, + "enterServerUrlSemantic": "Enter your server URL or IP address", + "@enterServerUrlSemantic": { + "description": "Semantic/ARIA label instructing to enter server URL or IP." + }, + "headerName": "Header Name", + "@headerName": { + "description": "Label for custom header key." + }, + "headerValue": "Header Value", + "@headerValue": { + "description": "Label for custom header value." + }, + "headerValueHint": "api-key-123 or Bearer token", + "@headerValueHint": { + "description": "Hint text with example header values, including API key or Bearer token." + }, + "addHeader": "Add header", + "@addHeader": { + "description": "Button to add a new custom header row." + }, + "maximumHeadersReached": "Maximum headers reached", + "@maximumHeadersReached": { + "description": "Warning when custom header limit is reached." + }, + "removeHeader": "Remove header", + "@removeHeader": { + "description": "Action to remove a custom header row." + }, + "connecting": "Connecting...", + "@connecting": { + "description": "Status while attempting to connect to server." + }, + "connectToServerButton": "Connect to Server", + "@connectToServerButton": { + "description": "Primary action button to initiate server connection." + }, + "demoModeActive": "Demo Mode Active", + "@demoModeActive": { + "description": "Banner/text indicating the app runs in demo mode." + }, + "skipServerSetupTryDemo": "Skip server setup and try the demo", + "@skipServerSetupTryDemo": { + "description": "CTA to bypass server configuration and enter demo mode." + }, + "enterDemo": "Enter Demo", + "@enterDemo": { + "description": "Button to enter demo mode." + }, + "demoBadge": "Demo", + "@demoBadge": { + "description": "Small badge label for demo content." + }, + "serverNotOpenWebUI": "This does not appear to be an Open-WebUI server.", + "@serverNotOpenWebUI": { + "description": "Validation error when the server does not resemble Open-WebUI." + }, + "serverUrlEmpty": "Server URL cannot be empty", + "@serverUrlEmpty": { + "description": "Validation message for empty server URL." + }, + "invalidUrlFormat": "Invalid URL format. Please check your input.", + "@invalidUrlFormat": { + "description": "Validation message when URL format is incorrect." + }, + "onlyHttpHttps": "Only HTTP and HTTPS protocols are supported.", + "@onlyHttpHttps": { + "description": "Validation note restricting protocols to HTTP/HTTPS." + }, + "serverAddressRequired": "Server address is required (e.g., 192.168.1.10 or example.com).", + "@serverAddressRequired": { + "description": "Validation hint providing examples for server addresses." + }, + "portRange": "Port must be between 1 and 65535.", + "@portRange": { + "description": "Validation message for allowed port range." + }, + "invalidIpFormat": "Invalid IP address format. Use format like 192.168.1.10.", + "@invalidIpFormat": { + "description": "Validation message for IP addresses with example." + }, + "couldNotConnectGeneric": "Couldn't connect. Double-check the address and try again.", + "@couldNotConnectGeneric": { + "description": "Generic failure when connecting to the server." + }, + "weCouldntReachServer": "We couldn't reach the server. Check your connection and that the server is running.", + "@weCouldntReachServer": { + "description": "Connectivity error with hints to verify server status." + }, + "connectionTimedOut": "Connection timed out. The server might be busy or blocked by a firewall.", + "@connectionTimedOut": { + "description": "Timeout error while connecting to server." + }, + "useHttpOrHttpsOnly": "Use http:// or https:// only.", + "@useHttpOrHttpsOnly": { + "description": "Note instructing the user to include protocol in URL." + }, + "loginFailed": "Login failed", + "@loginFailed": { + "description": "Title for failed login attempts." + }, + "invalidCredentials": "Invalid username or password. Please try again.", + "@invalidCredentials": { + "description": "Detailed message when authentication fails." + }, + "serverRedirectingHttps": "The server is redirecting requests. Check your server's HTTPS configuration.", + "@serverRedirectingHttps": { + "description": "Warning about HTTP→HTTPS redirect issues." + }, + "unableToConnectServer": "Unable to connect to server. Please check your connection.", + "@unableToConnectServer": { + "description": "Generic server connection failure message." + }, + "requestTimedOut": "The request timed out. Please try again.", + "@requestTimedOut": { + "description": "Timeout while waiting for a server response." + }, + "genericSignInFailed": "We couldn't sign you in. Check your credentials and server settings.", + "@genericSignInFailed": { + "description": "Fallback sign-in error when no specific cause is known." + }, + "skip": "Skip", + "@skip": { + "description": "Onboarding: skip current step." + }, + "next": "Next", + "@next": { + "description": "Onboarding: go to the next step." + }, + "done": "Done", + "@done": { + "description": "Onboarding: finish the flow." + }, + "onboardStartTitle": "Hello, {username}", + "@onboardStartTitle": { + "description": "Onboarding card: start chatting title.", + "placeholders": { + "username": { "type": "String", "example": "Alex" } } }, - "itemsCount": "{count, plural, =0{No items} one{1 item} other{{count} items}}", - "@itemsCount": { - "description": "Pluralized count of items.", - "placeholders": { - "count": { - "type": "int", - "example": "3" - } - } - }, - "closeButtonSemantic": "Close", - "@closeButtonSemantic": {"description": "Accessible label for a generic Close button."}, - "loadingContent": "Loading content", - "@loadingContent": {"description": "Shown while loading page content."}, - "noItems": "No items", - "@noItems": {"description": "Placeholder text when a list is empty."}, - "noItemsToDisplay": "No items to display", - "@noItemsToDisplay": {"description": "Alternative empty-state description."}, - "loadMore": "Load More", - "@loadMore": {"description": "Button label to load additional items in a paged list."}, - "workspace": "Workspace", - "@workspace": {"description": "Section/tab label for documents and files."}, - "recentFiles": "Recent Files", - "@recentFiles": {"description": "Header for recently accessed files."}, - "knowledgeBase": "Knowledge Base", - "@knowledgeBase": {"description": "Section for knowledge base content."}, - "noFilesYet": "No files yet", - "@noFilesYet": {"description": "Empty state when no files are present."}, - "uploadDocsPrompt": "Upload documents to reference in your conversations with Conduit", - "@uploadDocsPrompt": {"description": "Prompt encouraging users to upload documents."}, - "uploadFirstFile": "Upload your first file", - "@uploadFirstFile": {"description": "CTA to add the first file."}, - "attachments": "Attachments", - "@attachments": {"description": "Header above list of attached files in compose area."}, - "knowledgeBaseEmpty": "Knowledge base is empty", - "@knowledgeBaseEmpty": {"description": "Empty state title for the knowledge base section."}, - "createCollectionsPrompt": "Create collections of related documents for easy reference", - "@createCollectionsPrompt": {"description": "Prompt describing the benefit of creating collections."}, - "chooseSourcePhoto": "Choose your source", - "@chooseSourcePhoto": {"description": "Sheet title to pick camera or photo library."}, - "takePhoto": "Take a photo", - "@takePhoto": {"description": "Action to open camera and capture a new photo."}, - "chooseFromGallery": "Choose from your photos", - "@chooseFromGallery": {"description": "Action to pick an existing photo from library."}, - "document": "Document", - "@document": {"description": "Generic document label used in UI."}, - "documentHint": "PDF, Word, or text file", - "@documentHint": {"description": "Helper hint listing supported document types."}, - "uploadFileTitle": "Upload File", - "@uploadFileTitle": {"description": "Dialog/sheet title for file upload."}, - "fileUploadComingSoon": "File upload for {type} is coming soon!", - "@fileUploadComingSoon": { - "placeholders": {"type": {"type": "String", "example": "gallery"}}, - "description": "Temporary message for upcoming upload feature by type" - }, - "kbCreationComingSoon": "Knowledge base creation is coming soon!", - "@kbCreationComingSoon": {"description": "Temporary message indicating KB creation feature is not yet available."}, - "backToServerSetup": "Back to server setup", - "@backToServerSetup": {"description": "Button/back label to return to server configuration flow."}, - "connectedToServer": "Connected to Server", - "@connectedToServer": {"description": "Status label indicating a successful server connection."}, - "signIn": "Sign In", - "@signIn": {"description": "Button/heading for sign-in flows."}, - "enterCredentials": "Enter your credentials to access your AI conversations", - "@enterCredentials": {"description": "Instructional text on the sign-in screen."}, - "credentials": "Credentials", - "@credentials": {"description": "Header for credential input section."}, - "apiKey": "API Key", - "@apiKey": {"description": "Label for API key input field."}, - "usernameOrEmail": "Username or Email", - "@usernameOrEmail": {"description": "Label for username/email input field."}, - "password": "Password", - "@password": {"description": "Label for password input field."}, - "signInWithApiKey": "Sign in with API Key", - "@signInWithApiKey": {"description": "Alternative sign-in method using an API key."}, - "connectToServer": "Connect to Server", - "@connectToServer": {"description": "Call-to-action button for server connection."}, - "enterServerAddress": "Enter your Open-WebUI server address to get started", - "@enterServerAddress": {"description": "Instruction telling user to provide server URL to begin."}, - "serverUrl": "Server URL", - "@serverUrl": {"description": "Label for server URL field."}, - "serverUrlHint": "https://your-server.com", - "@serverUrlHint": {"description": "Hint text showing example server URL format."}, - "enterServerUrlSemantic": "Enter your server URL or IP address", - "@enterServerUrlSemantic": {"description": "Semantic/ARIA label instructing to enter server URL or IP."}, - "headerName": "Header Name", - "@headerName": {"description": "Label for custom header key."}, - "headerValue": "Header Value", - "@headerValue": {"description": "Label for custom header value."}, - "headerValueHint": "api-key-123 or Bearer token", - "@headerValueHint": {"description": "Hint text with example header values, including API key or Bearer token."}, - "addHeader": "Add header", - "@addHeader": {"description": "Button to add a new custom header row."}, - "maximumHeadersReached": "Maximum headers reached", - "@maximumHeadersReached": {"description": "Warning when custom header limit is reached."}, - "removeHeader": "Remove header", - "@removeHeader": {"description": "Action to remove a custom header row."}, - "connecting": "Connecting...", - "@connecting": {"description": "Status while attempting to connect to server."}, - "connectToServerButton": "Connect to Server", - "@connectToServerButton": {"description": "Primary action button to initiate server connection."}, - "demoModeActive": "Demo Mode Active", - "@demoModeActive": {"description": "Banner/text indicating the app runs in demo mode."}, - "skipServerSetupTryDemo": "Skip server setup and try the demo", - "@skipServerSetupTryDemo": {"description": "CTA to bypass server configuration and enter demo mode."}, - "enterDemo": "Enter Demo", - "@enterDemo": {"description": "Button to enter demo mode."}, - "demoBadge": "Demo", - "@demoBadge": {"description": "Small badge label for demo content."}, - "serverNotOpenWebUI": "This does not appear to be an Open-WebUI server.", - "@serverNotOpenWebUI": {"description": "Validation error when the server does not resemble Open-WebUI."}, - "serverUrlEmpty": "Server URL cannot be empty", - "@serverUrlEmpty": {"description": "Validation message for empty server URL."}, - "invalidUrlFormat": "Invalid URL format. Please check your input.", - "@invalidUrlFormat": {"description": "Validation message when URL format is incorrect."}, - "onlyHttpHttps": "Only HTTP and HTTPS protocols are supported.", - "@onlyHttpHttps": {"description": "Validation note restricting protocols to HTTP/HTTPS."}, - "serverAddressRequired": "Server address is required (e.g., 192.168.1.10 or example.com).", - "@serverAddressRequired": {"description": "Validation hint providing examples for server addresses."}, - "portRange": "Port must be between 1 and 65535.", - "@portRange": {"description": "Validation message for allowed port range."}, - "invalidIpFormat": "Invalid IP address format. Use format like 192.168.1.10.", - "@invalidIpFormat": {"description": "Validation message for IP addresses with example."}, - "couldNotConnectGeneric": "Couldn't connect. Double-check the address and try again.", - "@couldNotConnectGeneric": {"description": "Generic failure when connecting to the server."}, - "weCouldntReachServer": "We couldn't reach the server. Check your connection and that the server is running.", - "@weCouldntReachServer": {"description": "Connectivity error with hints to verify server status."}, - "connectionTimedOut": "Connection timed out. The server might be busy or blocked by a firewall.", - "@connectionTimedOut": {"description": "Timeout error while connecting to server."}, - "useHttpOrHttpsOnly": "Use http:// or https:// only.", - "@useHttpOrHttpsOnly": {"description": "Note instructing the user to include protocol in URL."}, - "loginFailed": "Login failed", - "@loginFailed": {"description": "Title for failed login attempts."}, - "invalidCredentials": "Invalid username or password. Please try again.", - "@invalidCredentials": {"description": "Detailed message when authentication fails."}, - "serverRedirectingHttps": "The server is redirecting requests. Check your server's HTTPS configuration.", - "@serverRedirectingHttps": {"description": "Warning about HTTP→HTTPS redirect issues."}, - "unableToConnectServer": "Unable to connect to server. Please check your connection.", - "@unableToConnectServer": {"description": "Generic server connection failure message."}, - "requestTimedOut": "The request timed out. Please try again.", - "@requestTimedOut": {"description": "Timeout while waiting for a server response."}, - "genericSignInFailed": "We couldn't sign you in. Check your credentials and server settings.", - "@genericSignInFailed": {"description": "Fallback sign-in error when no specific cause is known."} - , - "skip": "Skip", - "@skip": {"description": "Onboarding: skip current step."}, - "next": "Next", - "@next": {"description": "Onboarding: go to the next step."}, - "done": "Done", - "@done": {"description": "Onboarding: finish the flow."}, - "onboardStartTitle": "Hello, {username}", - "@onboardStartTitle": {"description": "Onboarding card: start chatting title.", "placeholders": {"username": {"type": "String", "example": "Alex"}}}, "onboardStartSubtitle": "Choose a model to get started. Tap New Chat anytime.", - "@onboardStartSubtitle": {"description": "Onboarding card: brief guidance to begin a chat."}, + "@onboardStartSubtitle": { + "description": "Onboarding card: brief guidance to begin a chat." + }, "onboardStartBullet1": "Tap the model name in the top bar to switch models", - "@onboardStartBullet1": {"description": "Bullet: how to switch models."}, + "@onboardStartBullet1": { + "description": "Bullet: how to switch models." + }, "onboardStartBullet2": "Use New Chat to reset context", - "@onboardStartBullet2": {"description": "Bullet: how to reset context."}, + "@onboardStartBullet2": { + "description": "Bullet: how to reset context." + }, "onboardAttachTitle": "Add context", - "@onboardAttachTitle": {"description": "Onboarding card: attach context title."}, + "@onboardAttachTitle": { + "description": "Onboarding card: attach context title." + }, "onboardAttachSubtitle": "Ground replies with content from Workspace or photos.", - "@onboardAttachSubtitle": {"description": "Onboarding card: why attaching context helps."}, + "@onboardAttachSubtitle": { + "description": "Onboarding card: why attaching context helps." + }, "onboardAttachBullet1": "Workspace: PDFs, docs, datasets", - "@onboardAttachBullet1": {"description": "Bullet: types of workspace files."}, + "@onboardAttachBullet1": { + "description": "Bullet: types of workspace files." + }, "onboardAttachBullet2": "Photos: camera or library", - "@onboardAttachBullet2": {"description": "Bullet: photo sources supported."}, + "@onboardAttachBullet2": { + "description": "Bullet: photo sources supported." + }, "onboardSpeakTitle": "Speak naturally", - "@onboardSpeakTitle": {"description": "Onboarding card: voice input title."}, + "@onboardSpeakTitle": { + "description": "Onboarding card: voice input title." + }, "onboardSpeakSubtitle": "Tap the mic to dictate with live waveform feedback.", - "@onboardSpeakSubtitle": {"description": "Onboarding card: how voice input works."}, + "@onboardSpeakSubtitle": { + "description": "Onboarding card: how voice input works." + }, "onboardSpeakBullet1": "Stop anytime; partial text is preserved", - "@onboardSpeakBullet1": {"description": "Bullet: stop dictation preserves text."}, + "@onboardSpeakBullet1": { + "description": "Bullet: stop dictation preserves text." + }, "onboardSpeakBullet2": "Great for quick notes or long prompts", - "@onboardSpeakBullet2": {"description": "Bullet: benefits of voice input."}, + "@onboardSpeakBullet2": { + "description": "Bullet: benefits of voice input." + }, "onboardQuickTitle": "Quick actions", - "@onboardQuickTitle": {"description": "Onboarding card: quick actions title."}, + "@onboardQuickTitle": { + "description": "Onboarding card: quick actions title." + }, "onboardQuickSubtitle": "Open the menu to switch between Chats, Workspace, and Profile.", - "@onboardQuickSubtitle": {"description": "Onboarding card: how to use the app menu."}, + "@onboardQuickSubtitle": { + "description": "Onboarding card: how to use the app menu." + }, "onboardQuickBullet1": "Tap the menu to access Chats, Workspace, Profile", - "@onboardQuickBullet1": {"description": "Bullet: menu access to sections."}, + "@onboardQuickBullet1": { + "description": "Bullet: menu access to sections." + }, "onboardQuickBullet2": "Start New Chat or manage models from the top bar", - "@onboardQuickBullet2": {"description": "Bullet: actions available in the top bar."} - , - "addAttachment": "Add attachment", - "@addAttachment": {"description": "Button to add an attachment (file/photo)."}, + "@onboardQuickBullet2": { + "description": "Bullet: actions available in the top bar." + }, "attachmentLabel": "Attachment", "@attachmentLabel": { "description": "Label shown beside attachment chips in messages." }, "tools": "Tools", - "@tools": {"description": "Header for a tools/actions section."}, + "@tools": { + "description": "Header for a tools/actions section." + }, "voiceInput": "Voice input", - "@voiceInput": {"description": "Label for voice input feature."}, + "@voiceInput": { + "description": "Label for voice input feature." + }, "voice": "Voice", - "@voice": {"description": "Title for the voice input bottom sheet."}, + "@voice": { + "description": "Title for the voice input bottom sheet." + }, "voiceStatusListening": "Listening…", "@voiceStatusListening": { "description": "Indicates the app is actively listening during voice input." @@ -319,216 +498,468 @@ "@voiceActionStart": { "description": "Button label to start voice recording." }, + "voiceCallTitle": "Voice Call", + "@voiceCallTitle": { + "description": "Title displayed on the voice call screen." + }, + "voiceCallPause": "Pause", + "@voiceCallPause": { + "description": "Button label to pause a voice call." + }, + "voiceCallResume": "Resume", + "@voiceCallResume": { + "description": "Button label to resume a paused voice call." + }, + "voiceCallStop": "Stop", + "@voiceCallStop": { + "description": "Button label to stop the active voice call." + }, + "voiceCallEnd": "End Call", + "@voiceCallEnd": { + "description": "Button label to end the voice call session." + }, + "voiceCallReady": "Ready", + "@voiceCallReady": { + "description": "Status label shown when the voice call is ready to start." + }, + "voiceCallConnecting": "Connecting...", + "@voiceCallConnecting": { + "description": "Status label shown while the voice call is connecting." + }, + "voiceCallListening": "Listening", + "@voiceCallListening": { + "description": "Status label shown while the call is listening for input." + }, + "voiceCallPaused": "Paused", + "@voiceCallPaused": { + "description": "Status label shown when the call is paused." + }, + "voiceCallProcessing": "Thinking...", + "@voiceCallProcessing": { + "description": "Status label shown while the call processes a response." + }, + "voiceCallSpeaking": "Speaking", + "@voiceCallSpeaking": { + "description": "Status label shown while the assistant is speaking." + }, + "voiceCallDisconnected": "Disconnected", + "@voiceCallDisconnected": { + "description": "Status label shown when the voice call has ended or disconnected." + }, + "voiceCallErrorHelp": "Please check:\n• Microphone permissions are granted\n• Speech recognition is available on your device\n• You are connected to the server", + "@voiceCallErrorHelp": { + "description": "Guidance shown when the voice call encounters an error." + }, "messageInputLabel": "Message input", - "@messageInputLabel": {"description": "Accessibility label for the message input."}, + "@messageInputLabel": { + "description": "Accessibility label for the message input." + }, "messageInputHint": "Type your message", - "@messageInputHint": {"description": "Hint shown in the message input field."}, + "@messageInputHint": { + "description": "Hint shown in the message input field." + }, "messageHintText": "Ask Conduit", - "@messageHintText": {"description": "Short placeholder text in the message input."}, + "@messageHintText": { + "description": "Short placeholder text in the message input." + }, "stopGenerating": "Stop generating", - "@stopGenerating": {"description": "Action to stop the assistant's response generation."}, + "@stopGenerating": { + "description": "Action to stop the assistant's response generation." + }, "send": "Send", - "@send": {"description": "Primary action to send a message."}, + "@send": { + "description": "Primary action to send a message." + }, "codeCopiedToClipboard": "Code copied to clipboard.", "@codeCopiedToClipboard": { "description": "Snack bar message confirming code was copied." }, "sendMessage": "Send message", - "@sendMessage": {"description": "Semantic label for sending a message."}, + "@sendMessage": { + "description": "Semantic label for sending a message." + }, "file": "File", - "@file": {"description": "A file item or attachment type label."}, + "@file": { + "description": "A file item or attachment type label." + }, + "chooseDifferentFile": "Choose Different File", + "@chooseDifferentFile": { + "description": "Action label prompting the user to pick another file." + }, "photo": "Photo", - "@photo": {"description": "A photo item or attachment type label."}, + "@photo": { + "description": "A photo item or attachment type label." + }, "camera": "Camera", - "@camera": {"description": "Camera source label."}, + "@camera": { + "description": "Camera source label." + }, "apiUnavailable": "API service not available", - "@apiUnavailable": {"description": "Shown when backend API service is unavailable."}, + "@apiUnavailable": { + "description": "Shown when backend API service is unavailable." + }, "unableToLoadImage": "Unable to load image", - "@unableToLoadImage": {"description": "General failure to load an image."}, + "@unableToLoadImage": { + "description": "General failure to load an image." + }, "notAnImageFile": "Not an image file: {fileName}", "@notAnImageFile": { "description": "Error when a referenced file is not an image.", - "placeholders": {"fileName": {"type": "String", "example": "image.txt"}} + "placeholders": { + "fileName": { + "type": "String", + "example": "image.txt" + } + } }, "failedToLoadImage": "Failed to load image: {error}", "@failedToLoadImage": { "description": "Error including the underlying reason when image loading fails.", - "placeholders": {"error": {"type": "String", "example": "Network error"}} - }, - "invalidDataUrl": "Invalid data URL format", - "@invalidDataUrl": {"description": "Error for malformed data: URLs."}, - "failedToDecodeImage": "Failed to decode image", - "@failedToDecodeImage": {"description": "Error when decoding image bytes/base64."}, - "invalidImageFormat": "Invalid image format", - "@invalidImageFormat": {"description": "Error when image type/format is not supported."}, - "emptyImageData": "Empty image data", - "@emptyImageData": {"description": "Error when image data buffer is empty."} - , - "featureRequiresInternet": "This feature requires an internet connection", - "@featureRequiresInternet": {"description": "Informational text explaining internet requirement."}, - "messagesWillSendWhenOnline": "Messages will be sent when you're back online", - "@messagesWillSendWhenOnline": {"description": "Queue behavior notice while offline."}, - "confirm": "Confirm", - "@confirm": {"description": "Confirmation button label."}, - "cancel": "Cancel", - "@cancel": {"description": "Cancel button label."} - , - "ok": "OK", - "@ok": {"description": "Generic OK button label."}, - "inputField": "Input field", - "@inputField": {"description": "Accessibility label describing an input field."}, - "captureDocumentOrImage": "Capture a document or image", - "@captureDocumentOrImage": {"description": "Action to capture a document or image using camera."}, - "checkConnection": "Check Connection", - "@checkConnection": {"description": "CTA to verify network connectivity."}, - "openSettings": "Open Settings", - "@openSettings": {"description": "CTA to open device or app settings."}, - "chooseDifferentFile": "Choose Different File", - "@chooseDifferentFile": {"description": "CTA to pick an alternative file."}, - "goBack": "Go Back", - "@goBack": {"description": "CTA to navigate back."}, - "technicalDetails": "Technical Details", - "@technicalDetails": {"description": "Expandable section label to show error details or logs."}, - "save": "Save", - "@save": {"description": "Primary action to save changes."}, - "chooseModel": "Choose Model", - "@chooseModel": {"description": "Button/label to choose a model."}, - "reviewerMode": "REVIEWER MODE", - "@reviewerMode": {"description": "Developer/reviewer mode indicator."}, - "selectLanguage": "Select Language", - "@selectLanguage": {"description": "Dialog title to pick application language."}, - "newFolder": "New Folder", - "@newFolder": {"description": "Action to create a new folder."}, - "folderName": "Folder name", - "@folderName": {"description": "Label for entering a folder's name."}, - "newChat": "New Chat", - "@newChat": {"description": "Action to start a new chat."}, - "more": "More", - "@more": {"description": "Opens additional actions or content."}, - "clear": "Clear", - "@clear": {"description": "Action to clear input or selection."}, - "searchHint": "Search...", - "@searchHint": {"description": "Generic search input hint."}, - "searchConversations": "Search conversations...", - "@searchConversations": {"description": "Search input hint scoped to conversations."}, - "create": "Create", - "@create": {"description": "Primary action to create a resource."}, - "folderCreated": "Folder created", - "@folderCreated": {"description": "Toast/notice after successfully creating a folder."}, - "failedToCreateFolder": "Failed to create folder", - "@failedToCreateFolder": {"description": "Error notice when folder creation fails."}, - "movedChatToFolder": "Moved \"{title}\" to \"{folder}\"", - "@movedChatToFolder": { - "description": "Toast indicating a chat titled {title} was moved to folder {folder}.", "placeholders": { - "title": {"type": "String"}, - "folder": {"type": "String"} + "error": { + "type": "String", + "example": "Network error" + } } }, + "invalidDataUrl": "Invalid data URL format", + "@invalidDataUrl": { + "description": "Error for malformed data: URLs." + }, + "failedToDecodeImage": "Failed to decode image", + "@failedToDecodeImage": { + "description": "Error when decoding image bytes/base64." + }, + "invalidImageFormat": "Invalid image format", + "@invalidImageFormat": { + "description": "Error when image type/format is not supported." + }, + "emptyImageData": "Empty image data", + "@emptyImageData": { + "description": "Error when image data buffer is empty." + }, + "confirm": "Confirm", + "@confirm": { + "description": "Confirmation button label." + }, + "continueAction": "Continue", + "@continueAction": { + "description": "Button label to continue an action or flow." + }, + "cancel": "Cancel", + "@cancel": { + "description": "Cancel button label." + }, + "ok": "OK", + "@ok": { + "description": "Generic OK button label." + }, + "previousLabel": "Prev", + "@previousLabel": { + "description": "Label for navigating to the previous item." + }, + "nextLabel": "Next", + "@nextLabel": { + "description": "Label for navigating to the next item." + }, + "inputField": "Input field", + "@inputField": { + "description": "Accessibility label describing an input field." + }, + "checkConnection": "Check Connection", + "@checkConnection": { + "description": "CTA to verify network connectivity." + }, + "openSettings": "Open Settings", + "@openSettings": { + "description": "CTA to open device or app settings." + }, + "goBack": "Go Back", + "@goBack": { + "description": "CTA to navigate back." + }, + "technicalDetails": "Technical Details", + "@technicalDetails": { + "description": "Expandable section label to show error details or logs." + }, + "requiredFieldLabel": "{label} *", + "@requiredFieldLabel": { + "description": "Label text indicating a required field.", + "placeholders": { + "label": { + "type": "String", + "example": "Email" + } + } + }, + "requiredFieldHelper": "Required field", + "@requiredFieldHelper": { + "description": "Helper text indicating that the field is required." + }, + "switchOnLabel": "On", + "@switchOnLabel": { + "description": "Semantic label when a switch is enabled." + }, + "switchOffLabel": "Off", + "@switchOffLabel": { + "description": "Semantic label when a switch is disabled." + }, + "dialogSemanticLabel": "Dialog: {title}", + "@dialogSemanticLabel": { + "description": "Semantic label describing the dialog title.", + "placeholders": { + "title": { + "type": "String", + "example": "Settings" + } + } + }, + "save": "Save", + "@save": { + "description": "Primary action to save changes." + }, + "chooseModel": "Choose Model", + "@chooseModel": { + "description": "Button/label to choose a model." + }, + "reviewerMode": "REVIEWER MODE", + "@reviewerMode": { + "description": "Developer/reviewer mode indicator." + }, + "selectLanguage": "Select Language", + "@selectLanguage": { + "description": "Dialog title to pick application language." + }, + "newFolder": "New Folder", + "@newFolder": { + "description": "Action to create a new folder." + }, + "folderName": "Folder name", + "@folderName": { + "description": "Label for entering a folder's name." + }, + "newChat": "New Chat", + "@newChat": { + "description": "Action to start a new chat." + }, + "more": "More", + "@more": { + "description": "Opens additional actions or content." + }, + "clear": "Clear", + "@clear": { + "description": "Action to clear input or selection." + }, + "searchConversations": "Search conversations...", + "@searchConversations": { + "description": "Search input hint scoped to conversations." + }, + "create": "Create", + "@create": { + "description": "Primary action to create a resource." + }, + "failedToCreateFolder": "Failed to create folder", + "@failedToCreateFolder": { + "description": "Error notice when folder creation fails." + }, "failedToMoveChat": "Failed to move chat", - "@failedToMoveChat": {"description": "Error notice when moving a chat fails."}, + "@failedToMoveChat": { + "description": "Error notice when moving a chat fails." + }, "failedToLoadChats": "Failed to load chats", - "@failedToLoadChats": {"description": "Error notice when fetching chat list fails."}, + "@failedToLoadChats": { + "description": "Error notice when fetching chat list fails." + }, "failedToUpdatePin": "Failed to update pin", - "@failedToUpdatePin": {"description": "Error notice when updating pin star/flag fails."}, + "@failedToUpdatePin": { + "description": "Error notice when updating pin star/flag fails." + }, "failedToDeleteChat": "Failed to delete chat", - "@failedToDeleteChat": {"description": "Error notice when deleting a chat fails."}, + "@failedToDeleteChat": { + "description": "Error notice when deleting a chat fails." + }, "manage": "Manage", - "@manage": {"description": "Context action to manage an item."}, + "@manage": { + "description": "Context action to manage an item." + }, "rename": "Rename", - "@rename": {"description": "Context action to rename an item."}, + "@rename": { + "description": "Context action to rename an item." + }, "delete": "Delete", - "@delete": {"description": "Context action to delete an item."}, + "@delete": { + "description": "Context action to delete an item." + }, "renameChat": "Rename Chat", - "@renameChat": {"description": "Dialog title to rename a chat."}, + "@renameChat": { + "description": "Dialog title to rename a chat." + }, "enterChatName": "Enter chat name", - "@enterChatName": {"description": "Input hint/label for new chat name."}, + "@enterChatName": { + "description": "Input hint/label for new chat name." + }, "failedToRenameChat": "Failed to rename chat", - "@failedToRenameChat": {"description": "Error notice when renaming chat fails."}, + "@failedToRenameChat": { + "description": "Error notice when renaming chat fails." + }, "failedToUpdateArchive": "Failed to update archive", - "@failedToUpdateArchive": {"description": "Error notice when archiving/unarchiving fails."}, + "@failedToUpdateArchive": { + "description": "Error notice when archiving/unarchiving fails." + }, "unarchive": "Unarchive", - "@unarchive": {"description": "Action to unarchive an item."}, + "@unarchive": { + "description": "Action to unarchive an item." + }, "archive": "Archive", - "@archive": {"description": "Action to archive an item."}, + "@archive": { + "description": "Action to archive an item." + }, "pin": "Pin", - "@pin": {"description": "Action to pin/star an item."}, + "@pin": { + "description": "Action to pin/star an item." + }, "unpin": "Unpin", - "@unpin": {"description": "Action to remove pin from an item."}, + "@unpin": { + "description": "Action to remove pin from an item." + }, "recent": "Recent", - "@recent": {"description": "List filter for recently used items."}, + "@recent": { + "description": "List filter for recently used items." + }, "system": "System", - "@system": {"description": "Option indicating the device/system default."}, + "@system": { + "description": "Option indicating the device/system default." + }, "english": "English", - "@english": {"description": "Language name: English."}, + "@english": { + "description": "Language name: English." + }, "deutsch": "Deutsch", - "@deutsch": {"description": "Language name: German."}, + "@deutsch": { + "description": "Language name: German." + }, "francais": "Français", - "@francais": {"description": "Language name: French."}, + "@francais": { + "description": "Language name: French." + }, "italiano": "Italiano", - "@italiano": {"description": "Language name: Italian."}, + "@italiano": { + "description": "Language name: Italian." + }, "espanol": "Español", - "@espanol": {"description": "Language name: Spanish."}, + "@espanol": { + "description": "Language name: Spanish." + }, "nederlands": "Nederlands", - "@nederlands": {"description": "Language name: Dutch."}, + "@nederlands": { + "description": "Language name: Dutch." + }, "russian": "Русский", - "@russian": {"description": "Language name: Russian."}, + "@russian": { + "description": "Language name: Russian." + }, "chinese": "中文", - "@chinese": {"description": "Language name: Chinese."}, + "@chinese": { + "description": "Language name: Chinese." + }, "deleteMessagesTitle": "Delete Messages", - "@deleteMessagesTitle": {"description": "Dialog title asking to confirm deletion of messages."}, + "@deleteMessagesTitle": { + "description": "Dialog title asking to confirm deletion of messages." + }, "deleteMessagesMessage": "Delete {count} messages?", "@deleteMessagesMessage": { "description": "Confirmation prompt asking to delete a number of messages.", "placeholders": { - "count": {"type": "int"} + "count": { + "type": "int" + } } }, "routeNotFound": "Route not found: {routeName}", "@routeNotFound": { "description": "Displayed when navigation fails to find a route name.", "placeholders": { - "routeName": {"type": "String"} + "routeName": { + "type": "String" + } } }, "deleteChatTitle": "Delete Chat", - "@deleteChatTitle": {"description": "Dialog title asking to confirm deletion of a chat."}, + "@deleteChatTitle": { + "description": "Dialog title asking to confirm deletion of a chat." + }, "deleteChatMessage": "This chat will be permanently deleted.", - "@deleteChatMessage": {"description": "Warning that deleting a chat cannot be undone."}, + "@deleteChatMessage": { + "description": "Warning that deleting a chat cannot be undone." + }, "deleteFolderTitle": "Delete Folder", - "@deleteFolderTitle": {"description": "Dialog title asking to confirm deletion of a folder."}, + "@deleteFolderTitle": { + "description": "Dialog title asking to confirm deletion of a folder." + }, "deleteFolderMessage": "This folder and its assignment references will be removed.", - "@deleteFolderMessage": {"description": "Warning that deleting a folder will remove it and its associations."}, + "@deleteFolderMessage": { + "description": "Warning that deleting a folder will remove it and its associations." + }, "failedToDeleteFolder": "Failed to delete folder", - "@failedToDeleteFolder": {"description": "Error notice when deleting a folder fails."}, + "@failedToDeleteFolder": { + "description": "Error notice when deleting a folder fails." + }, "aboutApp": "About", - "@aboutApp": {"description": "Settings tile title to view app information."}, + "@aboutApp": { + "description": "Settings tile title to view app information." + }, "aboutAppSubtitle": "Conduit information and links", - "@aboutAppSubtitle": {"description": "Subtitle/description for the About section."}, + "@aboutAppSubtitle": { + "description": "Subtitle/description for the About section." + }, "web": "Web", - "@web": {"description": "Tab/section label for web features."}, + "@web": { + "description": "Tab/section label for web features." + }, "imageGen": "Image Gen", - "@imageGen": {"description": "Short label for image generation section/tab."}, + "@imageGen": { + "description": "Short label for image generation section/tab." + }, "pinned": "Pinned", - "@pinned": {"description": "Filter/tab for pinned items."}, + "@pinned": { + "description": "Filter/tab for pinned items." + }, "folders": "Folders", - "@folders": {"description": "Tab listing chat folders."}, + "@folders": { + "description": "Tab listing chat folders." + }, "archived": "Archived", - "@archived": {"description": "Filter/tab for archived chats."}, + "@archived": { + "description": "Filter/tab for archived chats." + }, "appLanguage": "App Language", - "@appLanguage": {"description": "Label for choosing the app's display language."}, + "@appLanguage": { + "description": "Label for choosing the app's display language." + }, "darkMode": "Dark Mode", - "@darkMode": {"description": "Label for toggling dark theme."}, + "@darkMode": { + "description": "Label for toggling dark theme." + }, "webSearch": "Web Search", - "@webSearch": {"description": "Feature toggle/section for web search."}, + "@webSearch": { + "description": "Feature toggle/section for web search." + }, "webSearchDescription": "Search the web and cite sources in replies.", - "@webSearchDescription": {"description": "Explains that responses can include citations from the web."}, + "@webSearchDescription": { + "description": "Explains that responses can include citations from the web." + }, "imageGeneration": "Image Generation", - "@imageGeneration": {"description": "Feature toggle/section for image generation."}, + "@imageGeneration": { + "description": "Feature toggle/section for image generation." + }, "imageGenerationDescription": "Create images from your prompts.", - "@imageGenerationDescription": {"description": "Explains creating images via model prompts."}, + "@imageGenerationDescription": { + "description": "Explains creating images via model prompts." + }, "copy": "Copy", - "@copy": {"description": "Action to copy text to clipboard."}, + "@copy": { + "description": "Action to copy text to clipboard." + }, "ttsListen": "Listen", "@ttsListen": { "description": "Action to play the assistant message using text to speech" @@ -538,26 +969,45 @@ "description": "Action to stop text to speech playback" }, "edit": "Edit", - "@edit": {"description": "Action to edit an item/message."}, + "@edit": { + "description": "Action to edit an item/message." + }, "regenerate": "Regenerate", - "@regenerate": {"description": "Action to request a new assistant response."}, + "@regenerate": { + "description": "Action to request a new assistant response." + }, "noConversationsYet": "No conversations yet", - "@noConversationsYet": {"description": "Empty state when the user has no chats."} - , + "@noConversationsYet": { + "description": "Empty state when the user has no chats." + }, "usernameOrEmailHint": "Enter your username or email", - "@usernameOrEmailHint": {"description": "Hint text for username/email input."}, + "@usernameOrEmailHint": { + "description": "Hint text for username/email input." + }, "passwordHint": "Enter your password", - "@passwordHint": {"description": "Hint text for password input."}, + "@passwordHint": { + "description": "Hint text for password input." + }, "enterApiKey": "Enter your API key", - "@enterApiKey": {"description": "Hint text for API key input."}, + "@enterApiKey": { + "description": "Hint text for API key input." + }, "signingIn": "Signing in...", - "@signingIn": {"description": "Status message shown while signing in."}, + "@signingIn": { + "description": "Status message shown while signing in." + }, "advancedSettings": "Advanced Settings", - "@advancedSettings": {"description": "Section that contains additional/optional configuration."}, + "@advancedSettings": { + "description": "Section that contains additional/optional configuration." + }, "customHeaders": "Custom Headers", - "@customHeaders": {"description": "Section title for adding custom HTTP headers."}, + "@customHeaders": { + "description": "Section title for adding custom HTTP headers." + }, "customHeadersDescription": "Add custom HTTP headers for authentication, API keys, or special server requirements.", - "@customHeadersDescription": {"description": "Helper text explaining use-cases for custom headers."}, + "@customHeadersDescription": { + "description": "Helper text explaining use-cases for custom headers." + }, "allowSelfSignedCertificates": "Trust self-signed certificates", "@allowSelfSignedCertificates": { "description": "Toggle label that allows trusting self-signed TLS certificates for the configured server." @@ -567,109 +1017,260 @@ "description": "Helper text clarifying the risks of enabling the self-signed certificate toggle." }, "headerNameEmpty": "Header name cannot be empty", - "@headerNameEmpty": {"description": "Validation message for empty header name."}, + "@headerNameEmpty": { + "description": "Validation message for empty header name." + }, "headerNameTooLong": "Header name too long (max 64 characters)", - "@headerNameTooLong": {"description": "Validation message for header name length."}, + "@headerNameTooLong": { + "description": "Validation message for header name length." + }, "headerNameInvalidChars": "Invalid header name. Use only letters, numbers, and these symbols: !#$&-^_`|~", - "@headerNameInvalidChars": {"description": "Validation message for invalid characters in header name."}, + "@headerNameInvalidChars": { + "description": "Validation message for invalid characters in header name." + }, "headerNameReserved": "Cannot override reserved header \"{key}\"", "@headerNameReserved": { "description": "Error when attempting to override a reserved HTTP header {key}.", - "placeholders": {"key": {"type": "String"}} + "placeholders": { + "key": { + "type": "String" + } + } }, "headerValueEmpty": "Header value cannot be empty", - "@headerValueEmpty": {"description": "Validation message for empty header value."}, + "@headerValueEmpty": { + "description": "Validation message for empty header value." + }, "headerValueTooLong": "Header value too long (max 1024 characters)", - "@headerValueTooLong": {"description": "Validation message for header value length."}, + "@headerValueTooLong": { + "description": "Validation message for header value length." + }, "headerValueInvalidChars": "Header value contains invalid characters. Use only printable ASCII.", - "@headerValueInvalidChars": {"description": "Validation message for invalid characters in header value."}, + "@headerValueInvalidChars": { + "description": "Validation message for invalid characters in header value." + }, "headerValueUnsafe": "Header value appears to contain potentially unsafe content", - "@headerValueUnsafe": {"description": "Security warning for suspicious header values."}, + "@headerValueUnsafe": { + "description": "Security warning for suspicious header values." + }, "headerAlreadyExists": "Header \"{key}\" already exists. Remove it first to update.", "@headerAlreadyExists": { "description": "Error when a custom header with key {key} already exists.", - "placeholders": {"key": {"type": "String"}} + "placeholders": { + "key": { + "type": "String" + } + } }, "maxHeadersReachedDetail": "Maximum of 10 custom headers allowed. Remove some to add more.", - "@maxHeadersReachedDetail": {"description": "Explains the upper limit of custom headers."} - , - "editMessage": "Edit Message", - "@editMessage": {"description": "Action to edit a previously sent message."} - , + "@maxHeadersReachedDetail": { + "description": "Explains the upper limit of custom headers." + }, "noModelsAvailable": "No models available", - "@noModelsAvailable": {"description": "Shown when model list is empty or failed to load."}, + "@noModelsAvailable": { + "description": "Shown when model list is empty or failed to load." + }, "followingSystem": "Following system: {theme}", "@followingSystem": { "description": "Indicates the app is following the system theme (\"Dark\"/\"Light\").", - "placeholders": {"theme": {"type": "String"}} + "placeholders": { + "theme": { + "type": "String" + } + } }, "themeDark": "Dark", - "@themeDark": {"description": "Theme label for dark appearance."}, + "@themeDark": { + "description": "Theme label for dark appearance." + }, "themePalette": "Accent palette", - "@themePalette": {"description": "Title for selecting the app color palette."}, - "themePaletteDescription": "Choose the accent colors used for buttons, cards, and chat bubbles.", - "@themePaletteDescription": {"description": "Helper text explaining palette selection."}, + "@themePalette": { + "description": "Title for selecting the app color palette." + }, + "themePaletteConduitLabel": "Conduit", + "@themePaletteConduitLabel": { + "description": "Palette name for the default Conduit theme." + }, + "themePaletteConduitDescription": "Clean neutral theme designed for Conduit.", + "@themePaletteConduitDescription": { + "description": "Description of the Conduit palette." + }, + "themePaletteClaudeLabel": "Claude", + "@themePaletteClaudeLabel": { + "description": "Palette name inspired by the Claude web client." + }, + "themePaletteClaudeDescription": "Warm, tactile palette lifted from the Claude web client.", + "@themePaletteClaudeDescription": { + "description": "Description of the Claude palette." + }, + "themePaletteT3ChatLabel": "T3 Chat", + "@themePaletteT3ChatLabel": { + "description": "Palette name inspired by the T3 Stack brand." + }, + "themePaletteT3ChatDescription": "Playful gradients inspired by the T3 Stack brand.", + "@themePaletteT3ChatDescription": { + "description": "Description of the T3 Chat palette." + }, + "themePaletteCatppuccinLabel": "Catppuccin", + "@themePaletteCatppuccinLabel": { + "description": "Palette name for Catppuccin colors." + }, + "themePaletteCatppuccinDescription": "Soft pastel palette.", + "@themePaletteCatppuccinDescription": { + "description": "Description of the Catppuccin palette." + }, + "themePaletteTangerineLabel": "Tangerine", + "@themePaletteTangerineLabel": { + "description": "Palette name for Tangerine colors." + }, + "themePaletteTangerineDescription": "Warm orange-and-slate palette.", + "@themePaletteTangerineDescription": { + "description": "Description of the Tangerine palette." + }, "themeLight": "Light", - "@themeLight": {"description": "Theme label for light appearance."}, + "@themeLight": { + "description": "Theme label for light appearance." + }, "currentlyUsingDarkTheme": "Currently using Dark theme", - "@currentlyUsingDarkTheme": {"description": "Status text indicating dark theme is active."}, + "@currentlyUsingDarkTheme": { + "description": "Status text indicating dark theme is active." + }, "currentlyUsingLightTheme": "Currently using Light theme", - "@currentlyUsingLightTheme": {"description": "Status text indicating light theme is active."}, + "@currentlyUsingLightTheme": { + "description": "Status text indicating light theme is active." + }, "aboutConduit": "About Conduit", - "@aboutConduit": {"description": "Dialog title for app information."}, + "@aboutConduit": { + "description": "Dialog title for app information." + }, "versionLabel": "Version: {version} ({build})", "@versionLabel": { "description": "Displays version and build number in the About dialog.", - "placeholders": {"version": {"type": "String"}, "build": {"type": "String"}} + "placeholders": { + "version": { + "type": "String" + }, + "build": { + "type": "String" + } + } }, "githubRepository": "GitHub Repository", - "@githubRepository": {"description": "Link label pointing to the app repository."}, + "@githubRepository": { + "description": "Link label pointing to the app repository." + }, "unableToLoadAppInfo": "Unable to load app info", - "@unableToLoadAppInfo": {"description": "Error text when package info cannot be retrieved."}, + "@unableToLoadAppInfo": { + "description": "Error text when package info cannot be retrieved." + }, "thinking": "Thinking…", - "@thinking": {"description": "Label shown while the assistant is reasoning."}, + "@thinking": { + "description": "Label shown while the assistant is reasoning." + }, "thoughts": "Thoughts", - "@thoughts": {"description": "Section title for showing reasoning content."}, + "@thoughts": { + "description": "Section title for showing reasoning content." + }, "thoughtForDuration": "Thought for {duration}", "@thoughtForDuration": { "description": "Shows how long the assistant thought before replying.", - "placeholders": {"duration": {"type": "String", "example": "3s"}} - } - , + "placeholders": { + "duration": { + "type": "String", + "example": "3s" + } + } + }, "appCustomization": "Customization", - "@appCustomization": {"description": "Title of the customization settings page."}, + "@appCustomization": { + "description": "Title of the customization settings page." + }, "appCustomizationSubtitle": "Theme, language, voice, and quickpills", - "@appCustomizationSubtitle": {"description": "Subtitle shown under App Customization tile and page header."}, + "@appCustomizationSubtitle": { + "description": "Subtitle shown under App Customization tile and page header." + }, "quickActionsDescription": "Quickpills in chat", - "@quickActionsDescription": {"description": "Helper text explaining quick action pill selection in customization."}, + "@quickActionsDescription": { + "description": "Helper text explaining quick action pill selection in customization." + }, + "quickActionsSelectedCount": "{count, plural, =0{No actions selected} one{1 action selected} other{{count} actions selected}}", + "@quickActionsSelectedCount": { + "description": "Subtitle indicating how many quick actions are selected.", + "placeholders": { + "count": { + "type": "int", + "example": "2" + } + } + }, + "autoSelectDescription": "Let the app choose the best model", + "@autoSelectDescription": { + "description": "Explains what the auto-select model setting does." + }, "chatSettings": "Chat", - "@chatSettings": {"description": "Section header for chat-related customization options."}, + "@chatSettings": { + "description": "Section header for chat-related customization options." + }, "sendOnEnter": "Send on Enter", - "@sendOnEnter": {"description": "Toggle title for sending messages when pressing Enter."}, + "@sendOnEnter": { + "description": "Toggle title for sending messages when pressing Enter." + }, "sendOnEnterDescription": "Enter sends (soft keyboard). Cmd/Ctrl+Enter also available", "@sendOnEnterDescription": { "description": "Explanation of how the Send on Enter toggle behaves." }, + "ttsEngineLabel": "Engine", + "@ttsEngineLabel": { + "description": "Label for selecting the text-to-speech engine." + }, + "ttsEngineDevice": "On device", + "@ttsEngineDevice": { + "description": "Chip label for using on-device text-to-speech." + }, + "ttsEngineServer": "Server", + "@ttsEngineServer": { + "description": "Chip label for using server-side text-to-speech." + }, "ttsSettings": "Text to Speech", - "@ttsSettings": {"description": "Section header for TTS-related customization options."}, + "@ttsSettings": { + "description": "Section header for TTS-related customization options." + }, "ttsVoice": "Voice", - "@ttsVoice": {"description": "Title for voice selection tile."}, + "@ttsVoice": { + "description": "Title for voice selection tile." + }, "ttsSpeechRate": "Speech Rate", - "@ttsSpeechRate": {"description": "Title for speech rate slider."}, + "@ttsSpeechRate": { + "description": "Title for speech rate slider." + }, "ttsPitch": "Pitch", - "@ttsPitch": {"description": "Title for pitch slider."}, + "@ttsPitch": { + "description": "Title for pitch slider." + }, "ttsVolume": "Volume", - "@ttsVolume": {"description": "Title for volume slider."}, + "@ttsVolume": { + "description": "Title for volume slider." + }, "ttsPreview": "Preview Voice", - "@ttsPreview": {"description": "Title for preview button."}, + "@ttsPreview": { + "description": "Title for preview button." + }, "ttsSystemDefault": "System Default", - "@ttsSystemDefault": {"description": "Label for system default voice option."}, + "@ttsSystemDefault": { + "description": "Label for system default voice option." + }, "ttsSelectVoice": "Select Voice", - "@ttsSelectVoice": {"description": "Title for voice picker bottom sheet."}, + "@ttsSelectVoice": { + "description": "Title for voice picker bottom sheet." + }, "ttsPreviewText": "This is a preview of the selected voice.", - "@ttsPreviewText": {"description": "Sample text spoken during voice preview."}, + "@ttsPreviewText": { + "description": "Sample text spoken during voice preview." + }, "ttsNoVoicesAvailable": "No voices available", - "@ttsNoVoicesAvailable": {"description": "Error message when no TTS voices can be found."}, + "@ttsNoVoicesAvailable": { + "description": "Error message when no TTS voices can be found." + }, "ttsVoicesForLanguage": "{language} Voices", "@ttsVoicesForLanguage": { "description": "Section header for voices matching the app language", @@ -681,29 +1282,189 @@ } }, "ttsOtherVoices": "Other Languages", - "@ttsOtherVoices": {"description": "Section header for voices in other languages."}, + "@ttsOtherVoices": { + "description": "Section header for voices in other languages." + }, "error": "Error", - "@error": {"description": "Generic error label."}, + "@error": { + "description": "Generic error label." + }, + "errorWithMessage": "Error: {message}", + "@errorWithMessage": { + "description": "Error label with appended message text.", + "placeholders": { + "message": { + "type": "String", + "example": "Network timeout" + } + } + }, + "networkTimeoutError": "Connection timed out. Please check your internet connection and try again.", + "@networkTimeoutError": { + "description": "User-facing message when a network request times out." + }, + "networkUnreachableError": "Cannot reach the server. Please check your server URL and internet connection.", + "@networkUnreachableError": { + "description": "User-facing message when the server cannot be reached." + }, + "networkServerNotResponding": "Server is not responding. Please verify the server is running and accessible.", + "@networkServerNotResponding": { + "description": "User-facing message when the server does not respond to a request." + }, + "networkGenericError": "Network connection problem. Please check your internet connection.", + "@networkGenericError": { + "description": "Fallback message for generic network errors." + }, + "serverError500": "Server is experiencing issues. This is usually temporary.", + "@serverError500": { + "description": "Message when a 500 error is encountered." + }, + "serverErrorUnavailable": "Server is temporarily unavailable. Please try again in a moment.", + "@serverErrorUnavailable": { + "description": "Message when a 502/503 error is encountered." + }, + "serverErrorTimeout": "Server took too long to respond. Please try again.", + "@serverErrorTimeout": { + "description": "Message when the server times out." + }, + "serverErrorGeneric": "Server is having problems. Please try again later.", + "@serverErrorGeneric": { + "description": "Fallback server error message." + }, + "authSessionExpired": "Your session has expired. Please sign in again.", + "@authSessionExpired": { + "description": "Message when an authentication session expires." + }, + "authForbidden": "You don't have permission to perform this action.", + "@authForbidden": { + "description": "Message when the user lacks required permissions." + }, + "authInvalidToken": "Authentication token is invalid. Please sign in again.", + "@authInvalidToken": { + "description": "Message when the authentication token is invalid." + }, + "authGenericError": "Authentication problem. Please sign in again.", + "@authGenericError": { + "description": "Fallback authentication error message." + }, + "validationInvalidEmail": "Please enter a valid email address.", + "@validationInvalidEmail": { + "description": "Validation message for invalid email input." + }, + "validationWeakPassword": "Password doesn't meet requirements. Please check and try again.", + "@validationWeakPassword": { + "description": "Validation message for weak passwords." + }, + "validationMissingRequired": "Please fill in all required fields.", + "@validationMissingRequired": { + "description": "Validation message when required fields are missing." + }, + "validationFormatError": "Some information is in the wrong format. Please check and try again.", + "@validationFormatError": { + "description": "Validation message for generic formatting issues." + }, + "validationGenericError": "Please check your input and try again.", + "@validationGenericError": { + "description": "Fallback validation message." + }, + "fileNotFound": "File not found. It may have been moved or deleted.", + "@fileNotFound": { + "description": "Message when a file cannot be located." + }, + "fileAccessDenied": "Cannot access the file. Please check permissions.", + "@fileAccessDenied": { + "description": "Message when file access is denied." + }, + "fileTooLarge": "File is too large. Please choose a smaller file.", + "@fileTooLarge": { + "description": "Message when a file exceeds size limits." + }, + "fileGenericError": "Problem with the file. Please try a different file.", + "@fileGenericError": { + "description": "Fallback file error message." + }, + "permissionCameraRequired": "Camera permission is required. Please enable it in settings.", + "@permissionCameraRequired": { + "description": "Message when camera permission is missing." + }, + "permissionStorageRequired": "Storage permission is required. Please enable it in settings.", + "@permissionStorageRequired": { + "description": "Message when storage permission is missing." + }, + "permissionMicrophoneRequired": "Microphone permission is required. Please enable it in settings.", + "@permissionMicrophoneRequired": { + "description": "Message when microphone permission is missing." + }, + "permissionGenericError": "Permission required. Please check app permissions in settings.", + "@permissionGenericError": { + "description": "Fallback permission error message." + }, + "actionRetryRequest": "Try the request again.", + "@actionRetryRequest": { + "description": "Description for retrying a failed request." + }, + "actionVerifyConnection": "Verify your internet connection.", + "@actionVerifyConnection": { + "description": "Description for checking internet connectivity." + }, + "actionRetryOperation": "Retry the operation.", + "@actionRetryOperation": { + "description": "Description for retrying the same operation." + }, + "actionRetryAfterDelay": "Wait a moment then try again.", + "@actionRetryAfterDelay": { + "description": "Description suggesting a short delay before retrying." + }, + "actionSignInToAccount": "Sign in to your account.", + "@actionSignInToAccount": { + "description": "Description for signing back into the app." + }, + "actionSelectAnotherFile": "Select another file.", + "@actionSelectAnotherFile": { + "description": "Description for choosing a different file." + }, + "actionOpenAppSettings": "Open app settings to grant permissions.", + "@actionOpenAppSettings": { + "description": "Description for opening system or app settings." + }, + "actionRetryAfterPermission": "Retry after granting permission.", + "@actionRetryAfterPermission": { + "description": "Description for retrying once permissions are granted." + }, + "actionReturnToPrevious": "Return to previous screen.", + "@actionReturnToPrevious": { + "description": "Description for navigating back to the prior screen." + }, "display": "Display", - "@display": {"description": "Section header for visual and layout related settings."}, + "@display": { + "description": "Section header for visual and layout related settings." + }, "realtime": "Realtime", - "@realtime": {"description": "Section header for realtime/transport settings."}, + "@realtime": { + "description": "Section header for realtime/transport settings." + }, "transportMode": "Transport mode", - "@transportMode": {"description": "Title for selecting the networking transport used for realtime."}, - "transportModeDescription": "Choose how the app connects for realtime updates.", - "@transportModeDescription": {"description": "Helper text explaining the transport setting."}, + "@transportMode": { + "description": "Title for selecting the networking transport used for realtime." + }, "mode": "Mode", - "@mode": {"description": "Form field label for transport mode dropdown."}, + "@mode": { + "description": "Form field label for transport mode dropdown." + }, "transportModePolling": "Polling fallback", - "@transportModePolling": {"description": "Dropdown option label for HTTP polling fallback transport."}, + "@transportModePolling": { + "description": "Dropdown option label for HTTP polling fallback transport." + }, "transportModeWs": "WebSocket only", - "@transportModeWs": {"description": "Dropdown option label for WebSocket-only transport."}, + "@transportModeWs": { + "description": "Dropdown option label for WebSocket-only transport." + }, "transportModePollingInfo": "Falls back to HTTP polling when WebSocket is blocked. Upgrades to WebSocket when possible.", - "@transportModePollingInfo": {"description": "Footnote text for the polling fallback transport mode."}, + "@transportModePollingInfo": { + "description": "Footnote text for the polling fallback transport mode." + }, "transportModeWsInfo": "Lower overhead, but may fail behind strict proxies/firewalls.", - "@transportModeWsInfo": {"description": "Footnote text for the WebSocket-only transport mode."}, - "websocketConnectionError": "Unable to establish real-time connection. Please check your network and server configuration.", - "@websocketConnectionError": {"description": "Error message shown when WebSocket connection fails initially."}, - "websocketReconnectFailed": "Real-time connection failed. Streaming may not work properly.", - "@websocketReconnectFailed": {"description": "Error message shown when WebSocket reconnection attempts fail."} + "@transportModeWsInfo": { + "description": "Footnote text for the WebSocket-only transport mode." + } } diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 5bcf573..a8d8ddc 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -1,7 +1,6 @@ { "@@locale": "es", "appTitle": "Conduit", - "initializationFailed": "Error de inicialización", "retry": "Reintentar", "back": "Atrás", "you": "Tú", @@ -16,10 +15,6 @@ "@connectionIssueSubtitle": { "description": "Subtítulo que explica las acciones disponibles cuando no se puede acceder al servidor" }, - "stillOfflineMessage": "Todavía no podemos conectarnos al servidor. Verifica tu conexión e inténtalo de nuevo.", - "@stillOfflineMessage": { - "description": "Mensaje de estado después de un reintento cuando no se ha restaurado la conexión" - }, "account": "Cuenta", "supportConduit": "Apoyar Conduit", "supportConduitSubtitle": "Mantén Conduit independiente financiando el desarrollo continuo.", @@ -37,32 +32,14 @@ "noResults": "Sin resultados", "searchModels": "Buscar modelos...", "errorMessage": "Algo salió mal. Por favor, inténtalo de nuevo.", - "loginButton": "Iniciar sesión", - "menuItem": "Configuración", - "dynamicContentWithPlaceholder": "¡Bienvenido, {name}!", - "itemsCount": "{count, plural, =0{Sin elementos} one{1 elemento} other{{count} elementos}}", "closeButtonSemantic": "Cerrar", "loadingContent": "Cargando contenido", "noItems": "Sin elementos", "noItemsToDisplay": "No hay elementos para mostrar", - "loadMore": "Cargar más", - "workspace": "Espacio de trabajo", - "recentFiles": "Archivos recientes", "knowledgeBase": "Base de conocimientos", - "noFilesYet": "Aún no hay archivos", - "uploadDocsPrompt": "Sube documentos para referenciarlos en tus conversaciones con Conduit", - "uploadFirstFile": "Sube tu primer archivo", "attachments": "Adjuntos", - "knowledgeBaseEmpty": "La base de conocimientos está vacía", - "createCollectionsPrompt": "Crea colecciones de documentos relacionados para referencia fácil", - "chooseSourcePhoto": "Elige tu fuente", "takePhoto": "Tomar una foto", - "chooseFromGallery": "Elegir de tus fotos", "document": "Documento", - "documentHint": "Archivo PDF, Word o de texto", - "uploadFileTitle": "Subir archivo", - "fileUploadComingSoon": "¡La carga de archivos para {type} estará disponible pronto!", - "kbCreationComingSoon": "¡La creación de base de conocimientos estará disponible pronto!", "backToServerSetup": "Volver a configuración del servidor", "connectedToServer": "Conectado al servidor", "signIn": "Iniciar sesión", @@ -125,7 +102,6 @@ "onboardQuickSubtitle": "Abre el menú para cambiar entre Conversaciones, Espacio de trabajo y Perfil.", "onboardQuickBullet1": "Toca el menú para acceder a Conversaciones, Espacio de trabajo, Perfil", "onboardQuickBullet2": "Inicia Nueva conversación o gestiona modelos desde la barra superior", - "addAttachment": "Añadir adjunto", "attachmentLabel": "Adjunto", "tools": "Herramientas", "voiceInput": "Entrada de voz", @@ -157,16 +133,12 @@ "failedToDecodeImage": "No se pudo decodificar la imagen", "invalidImageFormat": "Formato de imagen inválido", "emptyImageData": "Datos de imagen vacíos", - "featureRequiresInternet": "Esta función requiere conexión a Internet", - "messagesWillSendWhenOnline": "Los mensajes se enviarán cuando vuelvas a estar en línea", "confirm": "Confirmar", "cancel": "Cancelar", "ok": "OK", "inputField": "Campo de entrada", - "captureDocumentOrImage": "Capturar un documento o imagen", "checkConnection": "Verificar conexión", "openSettings": "Abrir configuración", - "chooseDifferentFile": "Elegir otro archivo", "goBack": "Volver", "technicalDetails": "Detalles técnicos", "save": "Guardar", @@ -178,18 +150,9 @@ "newChat": "Nueva conversación", "more": "Más", "clear": "Limpiar", - "searchHint": "Buscar...", "searchConversations": "Buscar conversaciones...", "create": "Crear", - "folderCreated": "Carpeta creada", "failedToCreateFolder": "No se pudo crear la carpeta", - "movedChatToFolder": "Se movió \"{title}\" a \"{folder}\"", - "@movedChatToFolder": { - "placeholders": { - "title": {"type": "String"}, - "folder": {"type": "String"} - } - }, "failedToMoveChat": "No se pudo mover la conversación", "failedToLoadChats": "No se pudieron cargar las conversaciones", "failedToUpdatePin": "No se pudo actualizar el anclaje", @@ -219,13 +182,17 @@ "deleteMessagesMessage": "¿Eliminar {count} mensajes?", "@deleteMessagesMessage": { "placeholders": { - "count": {"type": "int"} + "count": { + "type": "int" + } } }, "routeNotFound": "Ruta no encontrada: {routeName}", "@routeNotFound": { "placeholders": { - "routeName": {"type": "String"} + "routeName": { + "type": "String" + } } }, "deleteChatTitle": "Eliminar conversación", @@ -271,29 +238,55 @@ "headerNameTooLong": "Nombre de encabezado demasiado largo (máx. 64 caracteres)", "headerNameInvalidChars": "Nombre de encabezado inválido. Usa solo letras, números y estos símbolos: !#$&-^_`|~", "headerNameReserved": "No se puede sobrescribir el encabezado reservado \"{key}\"", - "@headerNameReserved": {"placeholders": {"key": {"type": "String"}}}, + "@headerNameReserved": { + "placeholders": { + "key": { + "type": "String" + } + } + }, "headerValueEmpty": "El valor del encabezado no puede estar vacío", "headerValueTooLong": "Valor de encabezado demasiado largo (máx. 1024 caracteres)", "headerValueInvalidChars": "El valor del encabezado contiene caracteres inválidos. Usa solo ASCII imprimible.", "headerValueUnsafe": "El valor del encabezado parece contener contenido potencialmente inseguro", "headerAlreadyExists": "El encabezado \"{key}\" ya existe. Elimínalo primero para actualizarlo.", - "@headerAlreadyExists": {"placeholders": {"key": {"type": "String"}}}, + "@headerAlreadyExists": { + "placeholders": { + "key": { + "type": "String" + } + } + }, "maxHeadersReachedDetail": "Máximo de 10 encabezados personalizados permitidos. Elimina algunos para añadir más.", - "editMessage": "Editar mensaje", "noModelsAvailable": "No hay modelos disponibles", "followingSystem": "Siguiendo el sistema: {theme}", - "@followingSystem": {"placeholders": {"theme": {"type": "String"}}}, + "@followingSystem": { + "placeholders": { + "theme": { + "type": "String" + } + } + }, "themeDark": "Oscuro", "themePalette": "Paleta de acentos", - "@themePalette": {"description": "Título para seleccionar la paleta de colores de la aplicación."}, - "themePaletteDescription": "Elige los colores de acento usados para botones, tarjetas y burbujas de chat.", - "@themePaletteDescription": {"description": "Texto de ayuda que explica la selección de paleta."}, + "@themePalette": { + "description": "Título para seleccionar la paleta de colores de la aplicación." + }, "themeLight": "Claro", "currentlyUsingDarkTheme": "Usando actualmente el tema oscuro", "currentlyUsingLightTheme": "Usando actualmente el tema claro", "aboutConduit": "Acerca de Conduit", "versionLabel": "Versión: {version} ({build})", - "@versionLabel": {"placeholders": {"version": {"type": "String"}, "build": {"type": "String"}}}, + "@versionLabel": { + "placeholders": { + "version": { + "type": "String" + }, + "build": { + "type": "String" + } + } + }, "githubRepository": "Repositorio GitHub", "unableToLoadAppInfo": "No se puede cargar información de la aplicación", "thinking": "Pensando...", @@ -301,7 +294,12 @@ "thoughtForDuration": "Pensó durante {duration}", "@thoughtForDuration": { "description": "Muestra cuánto tiempo el asistente estuvo pensando antes de responder.", - "placeholders": {"duration": {"type": "String", "example": "3s"}} + "placeholders": { + "duration": { + "type": "String", + "example": "3s" + } + } }, "appCustomization": "Personalización", "appCustomizationSubtitle": "Tema, idioma, voz y quickpills", @@ -325,12 +323,413 @@ "display": "Visualización", "realtime": "Tiempo real", "transportMode": "Modo de transporte", - "transportModeDescription": "Elige cómo se conecta la aplicación para actualizaciones en tiempo real.", "mode": "Modo", "transportModePolling": "Polling de respaldo", "transportModeWs": "Solo WebSocket", "transportModePollingInfo": "Recurrirá a HTTP polling si WebSocket está bloqueado. Se actualizará a WebSocket cuando sea posible.", "transportModeWsInfo": "Menor sobrecarga, pero puede fallar detrás de proxies/firewalls estrictos.", - "websocketConnectionError": "No se puede establecer la conexión en tiempo real. Por favor, verifica tu red y la configuración del servidor.", - "websocketReconnectFailed": "Fallo en la conexión en tiempo real. El streaming podría no funcionar correctamente." + "quickActionsSelectedCount": "{count, plural, =0{No hay acciones seleccionadas} one{{count} acción seleccionada} other{{count} acciones seleccionadas}}", + "@quickActionsSelectedCount": { + "description": "Subtitle indicating how many quick actions are selected.", + "placeholders": { + "count": { + "type": "int", + "example": "2" + } + } + }, + "autoSelectDescription": "Deja que la aplicación elija el mejor modelo", + "@autoSelectDescription": { + "description": "Explains what the auto-select model setting does." + }, + "ttsEngineLabel": "Motor", + "@ttsEngineLabel": { + "description": "Label for selecting the text-to-speech engine." + }, + "ttsEngineDevice": "En el dispositivo", + "@ttsEngineDevice": { + "description": "Chip label for using on-device text-to-speech." + }, + "ttsEngineServer": "Servidor", + "@ttsEngineServer": { + "description": "Chip label for using server-side text-to-speech." + }, + "modelCapabilityMultimodal": "Multimodal", + "@modelCapabilityMultimodal": { + "description": "Capability chip label for models that support multimodal input." + }, + "modelCapabilityReasoning": "Razonamiento", + "@modelCapabilityReasoning": { + "description": "Capability chip label for models that support reasoning features." + }, + "voiceCallTitle": "Llamada de voz", + "@voiceCallTitle": { + "description": "Title displayed on the voice call screen." + }, + "voiceCallPause": "Pausar", + "@voiceCallPause": { + "description": "Button label to pause a voice call." + }, + "voiceCallResume": "Reanudar", + "@voiceCallResume": { + "description": "Button label to resume a paused voice call." + }, + "voiceCallStop": "Detener", + "@voiceCallStop": { + "description": "Button label to stop the active voice call." + }, + "voiceCallEnd": "Finalizar llamada", + "@voiceCallEnd": { + "description": "Button label to end the voice call session." + }, + "chooseDifferentFile": "Seleccionar otro archivo", + "@chooseDifferentFile": { + "description": "Action label prompting the user to pick another file." + }, + "errorWithMessage": "Error: {message}", + "@errorWithMessage": { + "description": "Error label with appended message text.", + "placeholders": { + "message": { + "type": "String", + "example": "Network timeout" + } + } + }, + "networkTimeoutError": "La conexión agotó el tiempo de espera. Verifica tu conexión a Internet e inténtalo de nuevo.", + "@networkTimeoutError": { + "description": "User-facing message when a network request times out." + }, + "networkUnreachableError": "No se puede alcanzar el servidor. Verifica la URL del servidor y tu conexión a Internet.", + "@networkUnreachableError": { + "description": "User-facing message when the server cannot be reached." + }, + "networkServerNotResponding": "El servidor no responde. Verifica que esté en ejecución y accesible.", + "@networkServerNotResponding": { + "description": "User-facing message when the server does not respond to a request." + }, + "networkGenericError": "Problema de conexión de red. Verifica tu conexión a Internet.", + "@networkGenericError": { + "description": "Fallback message for generic network errors." + }, + "serverError500": "El servidor tiene problemas. Normalmente es temporal.", + "@serverError500": { + "description": "Message when a 500 error is encountered." + }, + "serverErrorUnavailable": "El servidor está temporalmente no disponible. Inténtalo de nuevo en un momento.", + "@serverErrorUnavailable": { + "description": "Message when a 502/503 error is encountered." + }, + "serverErrorTimeout": "El servidor tardó demasiado en responder. Inténtalo de nuevo.", + "@serverErrorTimeout": { + "description": "Message when the server times out." + }, + "serverErrorGeneric": "El servidor está teniendo problemas. Inténtalo más tarde.", + "@serverErrorGeneric": { + "description": "Fallback server error message." + }, + "authSessionExpired": "Tu sesión ha expirado. Vuelve a iniciar sesión.", + "@authSessionExpired": { + "description": "Message when an authentication session expires." + }, + "authForbidden": "No tienes permiso para realizar esta acción.", + "@authForbidden": { + "description": "Message when the user lacks required permissions." + }, + "authInvalidToken": "El token de autenticación no es válido. Vuelve a iniciar sesión.", + "@authInvalidToken": { + "description": "Message when the authentication token is invalid." + }, + "authGenericError": "Problema de autenticación. Vuelve a iniciar sesión.", + "@authGenericError": { + "description": "Fallback authentication error message." + }, + "validationInvalidEmail": "Introduce una dirección de correo válida.", + "@validationInvalidEmail": { + "description": "Validation message for invalid email input." + }, + "validationWeakPassword": "La contraseña no cumple los requisitos. Revísala e inténtalo de nuevo.", + "@validationWeakPassword": { + "description": "Validation message for weak passwords." + }, + "validationMissingRequired": "Completa todos los campos obligatorios.", + "@validationMissingRequired": { + "description": "Validation message when required fields are missing." + }, + "validationFormatError": "Algunos datos tienen un formato incorrecto. Revísalos e inténtalo de nuevo.", + "@validationFormatError": { + "description": "Validation message for generic formatting issues." + }, + "validationGenericError": "Revisa tu entrada e inténtalo de nuevo.", + "@validationGenericError": { + "description": "Fallback validation message." + }, + "fileNotFound": "Archivo no encontrado. Puede que se haya movido o eliminado.", + "@fileNotFound": { + "description": "Message when a file cannot be located." + }, + "fileAccessDenied": "No se puede acceder al archivo. Verifica los permisos.", + "@fileAccessDenied": { + "description": "Message when file access is denied." + }, + "fileTooLarge": "El archivo es demasiado grande. Elige uno más pequeño.", + "@fileTooLarge": { + "description": "Message when a file exceeds size limits." + }, + "fileGenericError": "Problema con el archivo. Prueba con otro archivo.", + "@fileGenericError": { + "description": "Fallback file error message." + }, + "permissionCameraRequired": "Se requiere permiso de cámara. Actívalo en los ajustes.", + "@permissionCameraRequired": { + "description": "Message when camera permission is missing." + }, + "permissionStorageRequired": "Se requiere permiso de almacenamiento. Actívalo en los ajustes.", + "@permissionStorageRequired": { + "description": "Message when storage permission is missing." + }, + "permissionMicrophoneRequired": "Se requiere permiso de micrófono. Actívalo en los ajustes.", + "@permissionMicrophoneRequired": { + "description": "Message when microphone permission is missing." + }, + "permissionGenericError": "Se requiere un permiso. Revisa los permisos de la app en los ajustes.", + "@permissionGenericError": { + "description": "Fallback permission error message." + }, + "actionRetryRequest": "Intenta la solicitud nuevamente.", + "@actionRetryRequest": { + "description": "Description for retrying a failed request." + }, + "actionVerifyConnection": "Verifica tu conexión a Internet.", + "@actionVerifyConnection": { + "description": "Description for checking internet connectivity." + }, + "actionRetryOperation": "Vuelve a intentar la operación.", + "@actionRetryOperation": { + "description": "Description for retrying the same operation." + }, + "actionRetryAfterDelay": "Espera un momento y vuelve a intentarlo.", + "@actionRetryAfterDelay": { + "description": "Description suggesting a short delay before retrying." + }, + "actionSignInToAccount": "Inicia sesión en tu cuenta.", + "@actionSignInToAccount": { + "description": "Description for signing back into the app." + }, + "actionSelectAnotherFile": "Selecciona otro archivo.", + "@actionSelectAnotherFile": { + "description": "Description for choosing a different file." + }, + "actionOpenAppSettings": "Abre la configuración de la aplicación para otorgar permisos.", + "@actionOpenAppSettings": { + "description": "Description for opening system or app settings." + }, + "actionRetryAfterPermission": "Vuelve a intentarlo después de otorgar el permiso.", + "@actionRetryAfterPermission": { + "description": "Description for retrying once permissions are granted." + }, + "actionReturnToPrevious": "Vuelve a la pantalla anterior.", + "@actionReturnToPrevious": { + "description": "Description for navigating back to the prior screen." + }, + "continueAction": "Continuar", + "@continueAction": { + "description": "Button label to continue an action or flow." + }, + "loadingShort": "Cargando", + "@loadingShort": { + "description": "Short loading label used for accessibility." + }, + "loadingAnnouncement": "Cargando: {message}", + "@loadingAnnouncement": { + "description": "Screen reader announcement when loading a resource.", + "placeholders": { + "message": { + "type": "String", + "example": "Messages" + } + } + }, + "errorAnnouncement": "Error: {error}", + "@errorAnnouncement": { + "description": "Screen reader announcement for an error.", + "placeholders": { + "error": { + "type": "String", + "example": "Network timeout" + } + } + }, + "errorAnnouncementWithSuggestion": "Error: {error}. {suggestion}", + "@errorAnnouncementWithSuggestion": { + "description": "Screen reader announcement for an error with a follow-up suggestion.", + "placeholders": { + "error": { + "type": "String", + "example": "Network timeout" + }, + "suggestion": { + "type": "String", + "example": "Please try again later." + } + } + }, + "successAnnouncement": "Éxito: {message}", + "@successAnnouncement": { + "description": "Screen reader announcement for successful actions.", + "placeholders": { + "message": { + "type": "String", + "example": "Profile updated" + } + } + }, + "requiredFieldLabel": "{label} *", + "@requiredFieldLabel": { + "description": "Label text indicating a required field.", + "placeholders": { + "label": { + "type": "String", + "example": "Email" + } + } + }, + "requiredFieldHelper": "Campo obligatorio", + "@requiredFieldHelper": { + "description": "Helper text indicating that the field is required." + }, + "switchOnLabel": "Activado", + "@switchOnLabel": { + "description": "Semantic label when a switch is enabled." + }, + "switchOffLabel": "Desactivado", + "@switchOffLabel": { + "description": "Semantic label when a switch is disabled." + }, + "dialogSemanticLabel": "Diálogo: {title}", + "@dialogSemanticLabel": { + "description": "Semantic label describing the dialog title.", + "placeholders": { + "title": { + "type": "String", + "example": "Settings" + } + } + }, + "previousLabel": "Anterior", + "@previousLabel": { + "description": "Label for navigating to the previous item." + }, + "nextLabel": "Siguiente", + "@nextLabel": { + "description": "Label for navigating to the next item." + }, + "themePaletteConduitLabel": "Conduit", + "@themePaletteConduitLabel": { + "description": "Palette name for the default Conduit theme." + }, + "themePaletteConduitDescription": "Tema neutro y limpio diseñado para Conduit.", + "@themePaletteConduitDescription": { + "description": "Description of the Conduit palette." + }, + "themePaletteClaudeLabel": "Claude", + "@themePaletteClaudeLabel": { + "description": "Palette name inspired by the Claude web client." + }, + "themePaletteClaudeDescription": "Paleta cálida y táctil inspirada en el cliente web de Claude.", + "@themePaletteClaudeDescription": { + "description": "Description of the Claude palette." + }, + "themePaletteT3ChatLabel": "T3 Chat", + "@themePaletteT3ChatLabel": { + "description": "Palette name inspired by the T3 Stack brand." + }, + "themePaletteT3ChatDescription": "Degradados divertidos inspirados en la marca T3 Stack.", + "@themePaletteT3ChatDescription": { + "description": "Description of the T3 Chat palette." + }, + "themePaletteCatppuccinLabel": "Catppuccin", + "@themePaletteCatppuccinLabel": { + "description": "Palette name for Catppuccin colors." + }, + "themePaletteCatppuccinDescription": "Paleta suave de tonos pastel.", + "@themePaletteCatppuccinDescription": { + "description": "Description of the Catppuccin palette." + }, + "themePaletteTangerineLabel": "Tangerine", + "@themePaletteTangerineLabel": { + "description": "Palette name for Tangerine colors." + }, + "themePaletteTangerineDescription": "Paleta cálida de tonos naranja y gris pizarra.", + "@themePaletteTangerineDescription": { + "description": "Description of the Tangerine palette." + }, + "@onboardStartTitle": { + "description": "Onboarding card: start chatting title.", + "placeholders": { + "username": { + "type": "String", + "example": "Alex" + } + } + }, + "@notAnImageFile": { + "description": "Error when a referenced file is not an image.", + "placeholders": { + "fileName": { + "type": "String", + "example": "image.txt" + } + } + }, + "@failedToLoadImage": { + "description": "Error including the underlying reason when image loading fails.", + "placeholders": { + "error": { + "type": "String", + "example": "Network error" + } + } + }, + "@ttsVoicesForLanguage": { + "description": "Section header for voices matching the app language", + "placeholders": { + "language": { + "type": "String", + "example": "EN" + } + } + }, + "voiceCallReady": "Listo", + "@voiceCallReady": { + "description": "Status label shown when the voice call is ready to start." + }, + "voiceCallConnecting": "Conectando...", + "@voiceCallConnecting": { + "description": "Status label shown while the voice call is connecting." + }, + "voiceCallListening": "Escuchando", + "@voiceCallListening": { + "description": "Status label shown while the call is listening for input." + }, + "voiceCallPaused": "En pausa", + "@voiceCallPaused": { + "description": "Status label shown when the call is paused." + }, + "voiceCallProcessing": "Pensando...", + "@voiceCallProcessing": { + "description": "Status label shown while the call processes a response." + }, + "voiceCallSpeaking": "Hablando", + "@voiceCallSpeaking": { + "description": "Status label shown while the assistant is speaking." + }, + "voiceCallDisconnected": "Desconectado", + "@voiceCallDisconnected": { + "description": "Status label shown when the voice call has ended or disconnected." + }, + "voiceCallErrorHelp": "Comprueba lo siguiente:\n• Los permisos del micrófono están concedidos\n• El reconocimiento de voz está disponible en tu dispositivo\n• Estás conectado al servidor", + "@voiceCallErrorHelp": { + "description": "Guidance shown when the voice call encounters an error." + } } diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index cd5a0cb..d50ae5e 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -1,7 +1,6 @@ { "@@locale": "fr", "appTitle": "Conduit", - "initializationFailed": "Échec de l'initialisation", "retry": "Réessayer", "back": "Retour", "you": "Vous", @@ -16,10 +15,6 @@ "@connectionIssueSubtitle": { "description": "Sous-titre expliquant les actions possibles quand le serveur est injoignable" }, - "stillOfflineMessage": "Nous ne pouvons toujours pas joindre le serveur. Vérifiez votre connexion et réessayez.", - "@stillOfflineMessage": { - "description": "Message d'état après une tentative de reconnexion sans succès" - }, "account": "Compte", "supportConduit": "Soutenir Conduit", "supportConduitSubtitle": "Financez le développement continu et les nouvelles fonctionnalités.", @@ -37,32 +32,14 @@ "noResults": "Aucun résultat", "searchModels": "Rechercher des modèles...", "errorMessage": "Une erreur s'est produite. Veuillez réessayer.", - "loginButton": "Connexion", - "menuItem": "Paramètres", - "dynamicContentWithPlaceholder": "Bienvenue, {name} !", - "itemsCount": "{count, plural, =0{Aucun élément} one{1 élément} other{{count} éléments}}", "closeButtonSemantic": "Fermer", "loadingContent": "Chargement du contenu", "noItems": "Aucun élément", "noItemsToDisplay": "Aucun élément à afficher", - "loadMore": "Charger plus", - "workspace": "Espace de travail", - "recentFiles": "Fichiers récents", "knowledgeBase": "Base de connaissances", - "noFilesYet": "Pas encore de fichiers", - "uploadDocsPrompt": "Importez des documents à utiliser dans vos conversations avec Conduit", - "uploadFirstFile": "Importer votre premier fichier", "attachments": "Pièces jointes", - "knowledgeBaseEmpty": "La base de connaissances est vide", - "createCollectionsPrompt": "Créez des collections de documents liés pour une référence facile", - "chooseSourcePhoto": "Choisir la source", "takePhoto": "Prendre une photo", - "chooseFromGallery": "Choisir depuis vos photos", "document": "Document", - "documentHint": "Fichier PDF, Word ou texte", - "uploadFileTitle": "Importer un fichier", - "fileUploadComingSoon": "Le téléversement de fichiers pour {type} arrive bientôt !", - "kbCreationComingSoon": "La création de la base de connaissances arrive bientôt !", "backToServerSetup": "Retour à la configuration du serveur", "connectedToServer": "Connecté au serveur", "signIn": "Se connecter", @@ -124,9 +101,7 @@ "onboardQuickTitle": "Actions rapides", "onboardQuickSubtitle": "Ouvrez le menu pour passer entre Chats, Espace de travail et Profil.", "onboardQuickBullet1": "Touchez le menu pour accéder à Chats, Espace, Profil", - "onboardQuickBullet2": "Lancez Nouveau chat ou gérez les modèles depuis la barre" - , - "addAttachment": "Ajouter une pièce jointe", + "onboardQuickBullet2": "Lancez Nouveau chat ou gérez les modèles depuis la barre", "attachmentLabel": "Pièce jointe", "tools": "Outils", "voiceInput": "Entrée vocale", @@ -157,19 +132,13 @@ "invalidDataUrl": "Format d'URL de données invalide", "failedToDecodeImage": "Échec du décodage de l'image", "invalidImageFormat": "Format d'image invalide", - "emptyImageData": "Données d'image vides" - , - "featureRequiresInternet": "Cette fonctionnalité nécessite une connexion Internet", - "messagesWillSendWhenOnline": "Les messages seront envoyés lorsque vous serez de nouveau en ligne", + "emptyImageData": "Données d'image vides", "confirm": "Confirmer", - "cancel": "Annuler" - , + "cancel": "Annuler", "ok": "OK", "inputField": "Champ de saisie", - "captureDocumentOrImage": "Capturer un document ou une image", "checkConnection": "Vérifier la connexion", "openSettings": "Ouvrir les réglages", - "chooseDifferentFile": "Choisir un autre fichier", "goBack": "Retour", "technicalDetails": "Détails techniques", "save": "Enregistrer", @@ -181,18 +150,9 @@ "newChat": "Nouveau chat", "more": "Plus", "clear": "Effacer", - "searchHint": "Rechercher...", "searchConversations": "Rechercher des conversations...", "create": "Créer", - "folderCreated": "Dossier créé", "failedToCreateFolder": "Échec de la création du dossier", - "movedChatToFolder": "\"{title}\" déplacé vers \"{folder}\"", - "@movedChatToFolder": { - "placeholders": { - "title": {"type": "String"}, - "folder": {"type": "String"} - } - }, "failedToMoveChat": "Échec du déplacement du chat", "failedToLoadChats": "Échec du chargement des chats", "failedToUpdatePin": "Échec de la mise à jour de l'épingle", @@ -222,13 +182,17 @@ "deleteMessagesMessage": "Supprimer {count} messages ?", "@deleteMessagesMessage": { "placeholders": { - "count": {"type": "int"} + "count": { + "type": "int" + } } }, "routeNotFound": "Route introuvable : {routeName}", "@routeNotFound": { "placeholders": { - "routeName": {"type": "String"} + "routeName": { + "type": "String" + } } }, "deleteChatTitle": "Supprimer le chat", @@ -254,8 +218,7 @@ "ttsStop": "Arrêter", "edit": "Modifier", "regenerate": "Régénérer", - "noConversationsYet": "Aucune conversation pour l'instant" - , + "noConversationsYet": "Aucune conversation pour l'instant", "usernameOrEmailHint": "Entrez votre nom d'utilisateur ou e‑mail", "passwordHint": "Entrez votre mot de passe", "enterApiKey": "Entrez votre clé API", @@ -275,31 +238,55 @@ "headerNameTooLong": "Nom d'en-tête trop long (max 64 caractères)", "headerNameInvalidChars": "Nom d'en-tête invalide. Utilisez uniquement des lettres, des chiffres et ces symboles : !#$&-^_`|~", "headerNameReserved": "Impossible d'écraser l'en-tête réservé « {key} »", - "@headerNameReserved": {"placeholders": {"key": {"type": "String"}}}, + "@headerNameReserved": { + "placeholders": { + "key": { + "type": "String" + } + } + }, "headerValueEmpty": "La valeur de l'en-tête ne peut pas être vide", "headerValueTooLong": "Valeur d'en-tête trop longue (max 1024 caractères)", "headerValueInvalidChars": "La valeur de l'en-tête contient des caractères invalides. Utilisez uniquement des caractères ASCII imprimables.", "headerValueUnsafe": "La valeur de l'en-tête semble contenir du contenu potentiellement dangereux", "headerAlreadyExists": "L'en-tête « {key} » existe déjà. Supprimez-le d'abord pour le modifier.", - "@headerAlreadyExists": {"placeholders": {"key": {"type": "String"}}}, - "maxHeadersReachedDetail": "Maximum 10 en-têtes personnalisés. Supprimez-en pour en ajouter." - , - "editMessage": "Modifier le message" - , + "@headerAlreadyExists": { + "placeholders": { + "key": { + "type": "String" + } + } + }, + "maxHeadersReachedDetail": "Maximum 10 en-têtes personnalisés. Supprimez-en pour en ajouter.", "noModelsAvailable": "Aucun modèle disponible", "followingSystem": "Selon le système : {theme}", - "@followingSystem": {"placeholders": {"theme": {"type": "String"}}}, + "@followingSystem": { + "placeholders": { + "theme": { + "type": "String" + } + } + }, "themeDark": "Sombre", "themePalette": "Palette de couleurs", - "@themePalette": {"description": "Titre pour choisir la palette de couleurs de l'application."}, - "themePaletteDescription": "Choisissez les couleurs d'accent utilisées pour les boutons, les cartes et les bulles de discussion.", - "@themePaletteDescription": {"description": "Texte d'aide expliquant la sélection de la palette."}, + "@themePalette": { + "description": "Titre pour choisir la palette de couleurs de l'application." + }, "themeLight": "Clair", "currentlyUsingDarkTheme": "Thème sombre actuellement utilisé", "currentlyUsingLightTheme": "Thème clair actuellement utilisé", "aboutConduit": "À propos de Conduit", "versionLabel": "Version : {version} ({build})", - "@versionLabel": {"placeholders": {"version": {"type": "String"}, "build": {"type": "String"}}}, + "@versionLabel": { + "placeholders": { + "version": { + "type": "String" + }, + "build": { + "type": "String" + } + } + }, "githubRepository": "Dépôt GitHub", "unableToLoadAppInfo": "Impossible de charger les informations de l'application", "thinking": "Réflexion…", @@ -307,9 +294,13 @@ "thoughtForDuration": "A réfléchi pendant {duration}", "@thoughtForDuration": { "description": "Indique la durée de réflexion de l'assistant.", - "placeholders": {"duration": {"type": "String", "example": "3 s"}} - } - , + "placeholders": { + "duration": { + "type": "String", + "example": "3 s" + } + } + }, "appCustomization": "Personnalisation", "appCustomizationSubtitle": "Thème, langue, voix et quickpills", "quickActionsDescription": "Raccourcis dans le chat", @@ -332,12 +323,413 @@ "display": "Affichage", "realtime": "Temps réel", "transportMode": "Mode de transport", - "transportModeDescription": "Choisissez comment l'app se connecte pour les mises à jour en temps réel.", "mode": "Mode", "transportModePolling": "Polling de secours", "transportModeWs": "WebSocket uniquement", "transportModePollingInfo": "Bascule sur HTTP polling lorsque WebSocket est bloqué. Repasse à WebSocket dès que possible.", "transportModeWsInfo": "Moins de surcharge, mais peut échouer derrière des proxys/firewalls stricts.", - "websocketConnectionError": "Impossible d'établir une connexion en temps réel. Veuillez vérifier votre réseau et la configuration du serveur.", - "websocketReconnectFailed": "Échec de la connexion en temps réel. Le streaming pourrait ne pas fonctionner correctement." + "quickActionsSelectedCount": "{count, plural, =0{Aucune action sélectionnée} one{{count} action sélectionnée} other{{count} actions sélectionnées}}", + "@quickActionsSelectedCount": { + "description": "Subtitle indicating how many quick actions are selected.", + "placeholders": { + "count": { + "type": "int", + "example": "2" + } + } + }, + "autoSelectDescription": "Laissez l'application choisir le meilleur modèle", + "@autoSelectDescription": { + "description": "Explains what the auto-select model setting does." + }, + "ttsEngineLabel": "Moteur", + "@ttsEngineLabel": { + "description": "Label for selecting the text-to-speech engine." + }, + "ttsEngineDevice": "Sur l'appareil", + "@ttsEngineDevice": { + "description": "Chip label for using on-device text-to-speech." + }, + "ttsEngineServer": "Serveur", + "@ttsEngineServer": { + "description": "Chip label for using server-side text-to-speech." + }, + "modelCapabilityMultimodal": "Multimodal", + "@modelCapabilityMultimodal": { + "description": "Capability chip label for models that support multimodal input." + }, + "modelCapabilityReasoning": "Raisonnement", + "@modelCapabilityReasoning": { + "description": "Capability chip label for models that support reasoning features." + }, + "voiceCallTitle": "Appel vocal", + "@voiceCallTitle": { + "description": "Title displayed on the voice call screen." + }, + "voiceCallPause": "Pause", + "@voiceCallPause": { + "description": "Button label to pause a voice call." + }, + "voiceCallResume": "Reprendre", + "@voiceCallResume": { + "description": "Button label to resume a paused voice call." + }, + "voiceCallStop": "Arrêter", + "@voiceCallStop": { + "description": "Button label to stop the active voice call." + }, + "voiceCallEnd": "Terminer l'appel", + "@voiceCallEnd": { + "description": "Button label to end the voice call session." + }, + "chooseDifferentFile": "Choisir un autre fichier", + "@chooseDifferentFile": { + "description": "Action label prompting the user to pick another file." + }, + "errorWithMessage": "Erreur : {message}", + "@errorWithMessage": { + "description": "Error label with appended message text.", + "placeholders": { + "message": { + "type": "String", + "example": "Network timeout" + } + } + }, + "networkTimeoutError": "La connexion a expiré. Vérifiez votre connexion Internet et réessayez.", + "@networkTimeoutError": { + "description": "User-facing message when a network request times out." + }, + "networkUnreachableError": "Impossible d'atteindre le serveur. Vérifiez l'URL du serveur et votre connexion Internet.", + "@networkUnreachableError": { + "description": "User-facing message when the server cannot be reached." + }, + "networkServerNotResponding": "Le serveur ne répond pas. Vérifiez qu'il est en cours d'exécution et accessible.", + "@networkServerNotResponding": { + "description": "User-facing message when the server does not respond to a request." + }, + "networkGenericError": "Problème de connexion réseau. Vérifiez votre connexion Internet.", + "@networkGenericError": { + "description": "Fallback message for generic network errors." + }, + "serverError500": "Le serveur rencontre des problèmes. Cela est généralement temporaire.", + "@serverError500": { + "description": "Message when a 500 error is encountered." + }, + "serverErrorUnavailable": "Le serveur est temporairement indisponible. Réessayez dans un instant.", + "@serverErrorUnavailable": { + "description": "Message when a 502/503 error is encountered." + }, + "serverErrorTimeout": "Le serveur a mis trop de temps à répondre. Réessayez.", + "@serverErrorTimeout": { + "description": "Message when the server times out." + }, + "serverErrorGeneric": "Le serveur rencontre des difficultés. Réessayez plus tard.", + "@serverErrorGeneric": { + "description": "Fallback server error message." + }, + "authSessionExpired": "Votre session a expiré. Veuillez vous reconnecter.", + "@authSessionExpired": { + "description": "Message when an authentication session expires." + }, + "authForbidden": "Vous n'avez pas l'autorisation d'effectuer cette action.", + "@authForbidden": { + "description": "Message when the user lacks required permissions." + }, + "authInvalidToken": "Le jeton d'authentification est invalide. Veuillez vous reconnecter.", + "@authInvalidToken": { + "description": "Message when the authentication token is invalid." + }, + "authGenericError": "Problème d'authentification. Veuillez vous reconnecter.", + "@authGenericError": { + "description": "Fallback authentication error message." + }, + "validationInvalidEmail": "Veuillez saisir une adresse e-mail valide.", + "@validationInvalidEmail": { + "description": "Validation message for invalid email input." + }, + "validationWeakPassword": "Le mot de passe ne respecte pas les exigences. Vérifiez-le et réessayez.", + "@validationWeakPassword": { + "description": "Validation message for weak passwords." + }, + "validationMissingRequired": "Veuillez remplir tous les champs obligatoires.", + "@validationMissingRequired": { + "description": "Validation message when required fields are missing." + }, + "validationFormatError": "Certaines informations sont au mauvais format. Vérifiez-les et réessayez.", + "@validationFormatError": { + "description": "Validation message for generic formatting issues." + }, + "validationGenericError": "Veuillez vérifier vos informations et réessayer.", + "@validationGenericError": { + "description": "Fallback validation message." + }, + "fileNotFound": "Fichier introuvable. Il a peut-être été déplacé ou supprimé.", + "@fileNotFound": { + "description": "Message when a file cannot be located." + }, + "fileAccessDenied": "Impossible d'accéder au fichier. Vérifiez les autorisations.", + "@fileAccessDenied": { + "description": "Message when file access is denied." + }, + "fileTooLarge": "Le fichier est trop volumineux. Choisissez un fichier plus petit.", + "@fileTooLarge": { + "description": "Message when a file exceeds size limits." + }, + "fileGenericError": "Problème avec le fichier. Essayez un autre fichier.", + "@fileGenericError": { + "description": "Fallback file error message." + }, + "permissionCameraRequired": "L'autorisation de la caméra est nécessaire. Activez-la dans les paramètres.", + "@permissionCameraRequired": { + "description": "Message when camera permission is missing." + }, + "permissionStorageRequired": "L'autorisation de stockage est nécessaire. Activez-la dans les paramètres.", + "@permissionStorageRequired": { + "description": "Message when storage permission is missing." + }, + "permissionMicrophoneRequired": "L'autorisation du microphone est nécessaire. Activez-la dans les paramètres.", + "@permissionMicrophoneRequired": { + "description": "Message when microphone permission is missing." + }, + "permissionGenericError": "Autorisation requise. Vérifiez les autorisations de l'application dans les paramètres.", + "@permissionGenericError": { + "description": "Fallback permission error message." + }, + "actionRetryRequest": "Réessayez la requête.", + "@actionRetryRequest": { + "description": "Description for retrying a failed request." + }, + "actionVerifyConnection": "Vérifiez votre connexion Internet.", + "@actionVerifyConnection": { + "description": "Description for checking internet connectivity." + }, + "actionRetryOperation": "Réessayez l'opération.", + "@actionRetryOperation": { + "description": "Description for retrying the same operation." + }, + "actionRetryAfterDelay": "Attendez un instant puis réessayez.", + "@actionRetryAfterDelay": { + "description": "Description suggesting a short delay before retrying." + }, + "actionSignInToAccount": "Connectez-vous à votre compte.", + "@actionSignInToAccount": { + "description": "Description for signing back into the app." + }, + "actionSelectAnotherFile": "Sélectionnez un autre fichier.", + "@actionSelectAnotherFile": { + "description": "Description for choosing a different file." + }, + "actionOpenAppSettings": "Ouvrez les paramètres de l'application pour accorder les autorisations.", + "@actionOpenAppSettings": { + "description": "Description for opening system or app settings." + }, + "actionRetryAfterPermission": "Réessayez après avoir accordé l'autorisation.", + "@actionRetryAfterPermission": { + "description": "Description for retrying once permissions are granted." + }, + "actionReturnToPrevious": "Revenir à l'écran précédent.", + "@actionReturnToPrevious": { + "description": "Description for navigating back to the prior screen." + }, + "continueAction": "Continuer", + "@continueAction": { + "description": "Button label to continue an action or flow." + }, + "loadingShort": "Chargement", + "@loadingShort": { + "description": "Short loading label used for accessibility." + }, + "loadingAnnouncement": "Chargement : {message}", + "@loadingAnnouncement": { + "description": "Screen reader announcement when loading a resource.", + "placeholders": { + "message": { + "type": "String", + "example": "Messages" + } + } + }, + "errorAnnouncement": "Erreur : {error}", + "@errorAnnouncement": { + "description": "Screen reader announcement for an error.", + "placeholders": { + "error": { + "type": "String", + "example": "Network timeout" + } + } + }, + "errorAnnouncementWithSuggestion": "Erreur : {error}. {suggestion}", + "@errorAnnouncementWithSuggestion": { + "description": "Screen reader announcement for an error with a follow-up suggestion.", + "placeholders": { + "error": { + "type": "String", + "example": "Network timeout" + }, + "suggestion": { + "type": "String", + "example": "Please try again later." + } + } + }, + "successAnnouncement": "Succès : {message}", + "@successAnnouncement": { + "description": "Screen reader announcement for successful actions.", + "placeholders": { + "message": { + "type": "String", + "example": "Profile updated" + } + } + }, + "requiredFieldLabel": "{label} *", + "@requiredFieldLabel": { + "description": "Label text indicating a required field.", + "placeholders": { + "label": { + "type": "String", + "example": "Email" + } + } + }, + "requiredFieldHelper": "Champ obligatoire", + "@requiredFieldHelper": { + "description": "Helper text indicating that the field is required." + }, + "switchOnLabel": "Activé", + "@switchOnLabel": { + "description": "Semantic label when a switch is enabled." + }, + "switchOffLabel": "Désactivé", + "@switchOffLabel": { + "description": "Semantic label when a switch is disabled." + }, + "dialogSemanticLabel": "Dialogue : {title}", + "@dialogSemanticLabel": { + "description": "Semantic label describing the dialog title.", + "placeholders": { + "title": { + "type": "String", + "example": "Settings" + } + } + }, + "previousLabel": "Précédent", + "@previousLabel": { + "description": "Label for navigating to the previous item." + }, + "nextLabel": "Suivant", + "@nextLabel": { + "description": "Label for navigating to the next item." + }, + "themePaletteConduitLabel": "Conduit", + "@themePaletteConduitLabel": { + "description": "Palette name for the default Conduit theme." + }, + "themePaletteConduitDescription": "Thème neutre et épuré conçu pour Conduit.", + "@themePaletteConduitDescription": { + "description": "Description of the Conduit palette." + }, + "themePaletteClaudeLabel": "Claude", + "@themePaletteClaudeLabel": { + "description": "Palette name inspired by the Claude web client." + }, + "themePaletteClaudeDescription": "Palette chaleureuse inspirée du client web de Claude.", + "@themePaletteClaudeDescription": { + "description": "Description of the Claude palette." + }, + "themePaletteT3ChatLabel": "T3 Chat", + "@themePaletteT3ChatLabel": { + "description": "Palette name inspired by the T3 Stack brand." + }, + "themePaletteT3ChatDescription": "Dégradés ludiques inspirés de la marque T3 Stack.", + "@themePaletteT3ChatDescription": { + "description": "Description of the T3 Chat palette." + }, + "themePaletteCatppuccinLabel": "Catppuccin", + "@themePaletteCatppuccinLabel": { + "description": "Palette name for Catppuccin colors." + }, + "themePaletteCatppuccinDescription": "Palette douce de tons pastel.", + "@themePaletteCatppuccinDescription": { + "description": "Description of the Catppuccin palette." + }, + "themePaletteTangerineLabel": "Tangerine", + "@themePaletteTangerineLabel": { + "description": "Palette name for Tangerine colors." + }, + "themePaletteTangerineDescription": "Palette chaleureuse d'oranges et d'ardoises.", + "@themePaletteTangerineDescription": { + "description": "Description of the Tangerine palette." + }, + "@onboardStartTitle": { + "description": "Onboarding card: start chatting title.", + "placeholders": { + "username": { + "type": "String", + "example": "Alex" + } + } + }, + "@notAnImageFile": { + "description": "Error when a referenced file is not an image.", + "placeholders": { + "fileName": { + "type": "String", + "example": "image.txt" + } + } + }, + "@failedToLoadImage": { + "description": "Error including the underlying reason when image loading fails.", + "placeholders": { + "error": { + "type": "String", + "example": "Network error" + } + } + }, + "@ttsVoicesForLanguage": { + "description": "Section header for voices matching the app language", + "placeholders": { + "language": { + "type": "String", + "example": "EN" + } + } + }, + "voiceCallReady": "Prêt", + "@voiceCallReady": { + "description": "Status label shown when the voice call is ready to start." + }, + "voiceCallConnecting": "Connexion…", + "@voiceCallConnecting": { + "description": "Status label shown while the voice call is connecting." + }, + "voiceCallListening": "Écoute", + "@voiceCallListening": { + "description": "Status label shown while the call is listening for input." + }, + "voiceCallPaused": "En pause", + "@voiceCallPaused": { + "description": "Status label shown when the call is paused." + }, + "voiceCallProcessing": "Réflexion…", + "@voiceCallProcessing": { + "description": "Status label shown while the call processes a response." + }, + "voiceCallSpeaking": "Parle", + "@voiceCallSpeaking": { + "description": "Status label shown while the assistant is speaking." + }, + "voiceCallDisconnected": "Déconnecté", + "@voiceCallDisconnected": { + "description": "Status label shown when the voice call has ended or disconnected." + }, + "voiceCallErrorHelp": "Veuillez vérifier :\n• Les autorisations du microphone sont accordées\n• La reconnaissance vocale est disponible sur votre appareil\n• Vous êtes connecté au serveur", + "@voiceCallErrorHelp": { + "description": "Guidance shown when the voice call encounters an error." + } } diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb index 1b2f9cd..cc2e0f0 100644 --- a/lib/l10n/app_it.arb +++ b/lib/l10n/app_it.arb @@ -1,7 +1,6 @@ { "@@locale": "it", "appTitle": "Conduit", - "initializationFailed": "Inizializzazione non riuscita", "retry": "Riprova", "back": "Indietro", "you": "Tu", @@ -16,10 +15,6 @@ "@connectionIssueSubtitle": { "description": "Sottotitolo che spiega le azioni disponibili quando il server non è raggiungibile" }, - "stillOfflineMessage": "Non riusciamo ancora a raggiungere il server. Controlla la connessione e riprova.", - "@stillOfflineMessage": { - "description": "Messaggio di stato dopo un tentativo di riconnessione senza successo" - }, "account": "Account", "supportConduit": "Sostieni Conduit", "supportConduitSubtitle": "Mantieni Conduit indipendente finanziando lo sviluppo continuo.", @@ -37,32 +32,14 @@ "noResults": "Nessun risultato", "searchModels": "Cerca modelli...", "errorMessage": "Qualcosa è andato storto. Riprova.", - "loginButton": "Accedi", - "menuItem": "Impostazioni", - "dynamicContentWithPlaceholder": "Benvenuto, {name}!", - "itemsCount": "{count, plural, =0{Nessun elemento} one{1 elemento} other{{count} elementi}}", "closeButtonSemantic": "Chiudi", "loadingContent": "Caricamento contenuto", "noItems": "Nessun elemento", "noItemsToDisplay": "Nessun elemento da visualizzare", - "loadMore": "Carica altro", - "workspace": "Spazio di lavoro", - "recentFiles": "File recenti", "knowledgeBase": "Base di conoscenza", - "noFilesYet": "Ancora nessun file", - "uploadDocsPrompt": "Carica documenti da usare nelle conversazioni con Conduit", - "uploadFirstFile": "Carica il tuo primo file", "attachments": "Allegati", - "knowledgeBaseEmpty": "La base di conoscenza è vuota", - "createCollectionsPrompt": "Crea raccolte di documenti correlati per un rapido riferimento", - "chooseSourcePhoto": "Scegli origine", "takePhoto": "Scatta una foto", - "chooseFromGallery": "Scegli dalle foto", "document": "Documento", - "documentHint": "File PDF, Word o di testo", - "uploadFileTitle": "Carica file", - "fileUploadComingSoon": "Il caricamento file per {type} arriverà presto!", - "kbCreationComingSoon": "La creazione della base di conoscenza arriverà presto!", "backToServerSetup": "Torna alla configurazione del server", "connectedToServer": "Connesso al server", "signIn": "Accedi", @@ -124,9 +101,7 @@ "onboardQuickTitle": "Azioni rapide", "onboardQuickSubtitle": "Apri il menu per passare tra Chat, Workspace e Profilo.", "onboardQuickBullet1": "Tocca il menu per accedere a Chat, Workspace, Profilo", - "onboardQuickBullet2": "Avvia Nuova chat o gestisci i modelli dalla barra" - , - "addAttachment": "Aggiungi allegato", + "onboardQuickBullet2": "Avvia Nuova chat o gestisci i modelli dalla barra", "attachmentLabel": "Allegato", "tools": "Strumenti", "voiceInput": "Input vocale", @@ -157,19 +132,13 @@ "invalidDataUrl": "Formato data URL non valido", "failedToDecodeImage": "Impossibile decodificare l'immagine", "invalidImageFormat": "Formato immagine non valido", - "emptyImageData": "Dati immagine vuoti" - , - "featureRequiresInternet": "Questa funzione richiede una connessione Internet", - "messagesWillSendWhenOnline": "I messaggi verranno inviati quando tornerai online", + "emptyImageData": "Dati immagine vuoti", "confirm": "Conferma", - "cancel": "Annulla" - , + "cancel": "Annulla", "ok": "OK", "inputField": "Campo di input", - "captureDocumentOrImage": "Acquisisci un documento o un'immagine", "checkConnection": "Controlla connessione", "openSettings": "Apri impostazioni", - "chooseDifferentFile": "Scegli un altro file", "goBack": "Indietro", "technicalDetails": "Dettagli tecnici", "save": "Salva", @@ -181,18 +150,9 @@ "newChat": "Nuova chat", "more": "Altro", "clear": "Pulisci", - "searchHint": "Cerca...", "searchConversations": "Cerca conversazioni...", "create": "Crea", - "folderCreated": "Cartella creata", "failedToCreateFolder": "Impossibile creare la cartella", - "movedChatToFolder": "\"{title}\" spostata in \"{folder}\"", - "@movedChatToFolder": { - "placeholders": { - "title": {"type": "String"}, - "folder": {"type": "String"} - } - }, "failedToMoveChat": "Impossibile spostare la chat", "failedToLoadChats": "Impossibile caricare le chat", "failedToUpdatePin": "Impossibile aggiornare il pin", @@ -222,13 +182,17 @@ "deleteMessagesMessage": "Eliminare {count} messaggi?", "@deleteMessagesMessage": { "placeholders": { - "count": {"type": "int"} + "count": { + "type": "int" + } } }, "routeNotFound": "Percorso non trovato: {routeName}", "@routeNotFound": { "placeholders": { - "routeName": {"type": "String"} + "routeName": { + "type": "String" + } } }, "deleteChatTitle": "Elimina chat", @@ -254,8 +218,7 @@ "ttsStop": "Interrompi", "edit": "Modifica", "regenerate": "Rigenera", - "noConversationsYet": "Ancora nessuna conversazione" - , + "noConversationsYet": "Ancora nessuna conversazione", "usernameOrEmailHint": "Inserisci il tuo username o e‑mail", "passwordHint": "Inserisci la password", "enterApiKey": "Inserisci la tua chiave API", @@ -275,31 +238,55 @@ "headerNameTooLong": "Nome header troppo lungo (max 64 caratteri)", "headerNameInvalidChars": "Nome header non valido. Usa solo lettere, numeri e questi simboli: !#$&-^_`|~", "headerNameReserved": "Impossibile sovrascrivere l'header riservato \"{key}\"", - "@headerNameReserved": {"placeholders": {"key": {"type": "String"}}}, + "@headerNameReserved": { + "placeholders": { + "key": { + "type": "String" + } + } + }, "headerValueEmpty": "Il valore dell'header non può essere vuoto", "headerValueTooLong": "Valore header troppo lungo (max 1024 caratteri)", "headerValueInvalidChars": "Il valore dell'header contiene caratteri non validi. Usa solo ASCII stampabile.", "headerValueUnsafe": "Il valore dell'header sembra contenere contenuti potenzialmente non sicuri", "headerAlreadyExists": "L'header \"{key}\" esiste già. Rimuovilo prima per aggiornarlo.", - "@headerAlreadyExists": {"placeholders": {"key": {"type": "String"}}}, - "maxHeadersReachedDetail": "Massimo 10 header personalizzati consentiti. Rimuovine alcuni per aggiungerne altri." - , - "editMessage": "Modifica messaggio" - , + "@headerAlreadyExists": { + "placeholders": { + "key": { + "type": "String" + } + } + }, + "maxHeadersReachedDetail": "Massimo 10 header personalizzati consentiti. Rimuovine alcuni per aggiungerne altri.", "noModelsAvailable": "Nessun modello disponibile", "followingSystem": "Segue il sistema: {theme}", - "@followingSystem": {"placeholders": {"theme": {"type": "String"}}}, + "@followingSystem": { + "placeholders": { + "theme": { + "type": "String" + } + } + }, "themeDark": "Scuro", "themePalette": "Palette di colori", - "@themePalette": {"description": "Titolo per scegliere la palette di colori dell'app."}, - "themePaletteDescription": "Scegli i colori di accento usati per pulsanti, schede e bolle di chat.", - "@themePaletteDescription": {"description": "Testo di supporto che spiega la scelta della palette."}, + "@themePalette": { + "description": "Titolo per scegliere la palette di colori dell'app." + }, "themeLight": "Chiaro", "currentlyUsingDarkTheme": "Attualmente tema scuro", "currentlyUsingLightTheme": "Attualmente tema chiaro", "aboutConduit": "Informazioni su Conduit", "versionLabel": "Versione: {version} ({build})", - "@versionLabel": {"placeholders": {"version": {"type": "String"}, "build": {"type": "String"}}}, + "@versionLabel": { + "placeholders": { + "version": { + "type": "String" + }, + "build": { + "type": "String" + } + } + }, "githubRepository": "Repository GitHub", "unableToLoadAppInfo": "Impossibile caricare le informazioni dell'app", "thinking": "Sta pensando…", @@ -307,9 +294,13 @@ "thoughtForDuration": "Ha pensato per {duration}", "@thoughtForDuration": { "description": "Mostra per quanto tempo ha pensato l'assistente.", - "placeholders": {"duration": {"type": "String", "example": "3 s"}} - } - , + "placeholders": { + "duration": { + "type": "String", + "example": "3 s" + } + } + }, "appCustomization": "Personalizzazione", "appCustomizationSubtitle": "Tema, lingua, voce e quickpills", "quickActionsDescription": "Scorciatoie nella chat", @@ -332,12 +323,413 @@ "display": "Schermo", "realtime": "Tempo reale", "transportMode": "Modalità di trasporto", - "transportModeDescription": "Scegli come l'app si connette per gli aggiornamenti in tempo reale.", "mode": "Modalità", "transportModePolling": "Polling di fallback", "transportModeWs": "Solo WebSocket", "transportModePollingInfo": "Quando WebSocket è bloccato passa a HTTP polling. Torna a WebSocket appena possibile.", "transportModeWsInfo": "Minore overhead, ma può fallire dietro proxy/firewall restrittivi.", - "websocketConnectionError": "Impossibile stabilire una connessione in tempo reale. Si prega di controllare la rete e la configurazione del server.", - "websocketReconnectFailed": "Connessione in tempo reale fallita. Lo streaming potrebbe non funzionare correttamente." + "quickActionsSelectedCount": "{count, plural, =0{Nessuna azione selezionata} one{{count} azione selezionata} other{{count} azioni selezionate}}", + "@quickActionsSelectedCount": { + "description": "Subtitle indicating how many quick actions are selected.", + "placeholders": { + "count": { + "type": "int", + "example": "2" + } + } + }, + "autoSelectDescription": "Lascia che l'app scelga il modello migliore", + "@autoSelectDescription": { + "description": "Explains what the auto-select model setting does." + }, + "ttsEngineLabel": "Motore", + "@ttsEngineLabel": { + "description": "Label for selecting the text-to-speech engine." + }, + "ttsEngineDevice": "Sul dispositivo", + "@ttsEngineDevice": { + "description": "Chip label for using on-device text-to-speech." + }, + "ttsEngineServer": "Server", + "@ttsEngineServer": { + "description": "Chip label for using server-side text-to-speech." + }, + "modelCapabilityMultimodal": "Multimodale", + "@modelCapabilityMultimodal": { + "description": "Capability chip label for models that support multimodal input." + }, + "modelCapabilityReasoning": "Ragionamento", + "@modelCapabilityReasoning": { + "description": "Capability chip label for models that support reasoning features." + }, + "voiceCallTitle": "Chiamata vocale", + "@voiceCallTitle": { + "description": "Title displayed on the voice call screen." + }, + "voiceCallPause": "Pausa", + "@voiceCallPause": { + "description": "Button label to pause a voice call." + }, + "voiceCallResume": "Riprendi", + "@voiceCallResume": { + "description": "Button label to resume a paused voice call." + }, + "voiceCallStop": "Stop", + "@voiceCallStop": { + "description": "Button label to stop the active voice call." + }, + "voiceCallEnd": "Termina chiamata", + "@voiceCallEnd": { + "description": "Button label to end the voice call session." + }, + "chooseDifferentFile": "Scegli un altro file", + "@chooseDifferentFile": { + "description": "Action label prompting the user to pick another file." + }, + "errorWithMessage": "Errore: {message}", + "@errorWithMessage": { + "description": "Error label with appended message text.", + "placeholders": { + "message": { + "type": "String", + "example": "Network timeout" + } + } + }, + "networkTimeoutError": "Connessione scaduta. Controlla la tua connessione Internet e riprova.", + "@networkTimeoutError": { + "description": "User-facing message when a network request times out." + }, + "networkUnreachableError": "Impossibile raggiungere il server. Controlla l'URL del server e la connessione Internet.", + "@networkUnreachableError": { + "description": "User-facing message when the server cannot be reached." + }, + "networkServerNotResponding": "Il server non risponde. Verifica che sia attivo e raggiungibile.", + "@networkServerNotResponding": { + "description": "User-facing message when the server does not respond to a request." + }, + "networkGenericError": "Problema di connessione di rete. Controlla la connessione Internet.", + "@networkGenericError": { + "description": "Fallback message for generic network errors." + }, + "serverError500": "Il server sta avendo problemi. Di solito è temporaneo.", + "@serverError500": { + "description": "Message when a 500 error is encountered." + }, + "serverErrorUnavailable": "Il server è temporaneamente non disponibile. Riprova tra poco.", + "@serverErrorUnavailable": { + "description": "Message when a 502/503 error is encountered." + }, + "serverErrorTimeout": "Il server ha impiegato troppo tempo a rispondere. Riprova.", + "@serverErrorTimeout": { + "description": "Message when the server times out." + }, + "serverErrorGeneric": "Il server è in difficoltà. Riprova più tardi.", + "@serverErrorGeneric": { + "description": "Fallback server error message." + }, + "authSessionExpired": "La sessione è scaduta. Accedi di nuovo.", + "@authSessionExpired": { + "description": "Message when an authentication session expires." + }, + "authForbidden": "Non hai l'autorizzazione per eseguire questa azione.", + "@authForbidden": { + "description": "Message when the user lacks required permissions." + }, + "authInvalidToken": "Il token di autenticazione non è valido. Accedi di nuovo.", + "@authInvalidToken": { + "description": "Message when the authentication token is invalid." + }, + "authGenericError": "Problema di autenticazione. Accedi di nuovo.", + "@authGenericError": { + "description": "Fallback authentication error message." + }, + "validationInvalidEmail": "Inserisci un indirizzo email valido.", + "@validationInvalidEmail": { + "description": "Validation message for invalid email input." + }, + "validationWeakPassword": "La password non soddisfa i requisiti. Controllala e riprova.", + "@validationWeakPassword": { + "description": "Validation message for weak passwords." + }, + "validationMissingRequired": "Compila tutti i campi obbligatori.", + "@validationMissingRequired": { + "description": "Validation message when required fields are missing." + }, + "validationFormatError": "Alcune informazioni non sono nel formato corretto. Controllale e riprova.", + "@validationFormatError": { + "description": "Validation message for generic formatting issues." + }, + "validationGenericError": "Controlla i dati inseriti e riprova.", + "@validationGenericError": { + "description": "Fallback validation message." + }, + "fileNotFound": "File non trovato. Potrebbe essere stato spostato o eliminato.", + "@fileNotFound": { + "description": "Message when a file cannot be located." + }, + "fileAccessDenied": "Impossibile accedere al file. Controlla i permessi.", + "@fileAccessDenied": { + "description": "Message when file access is denied." + }, + "fileTooLarge": "Il file è troppo grande. Scegline uno più piccolo.", + "@fileTooLarge": { + "description": "Message when a file exceeds size limits." + }, + "fileGenericError": "Problema con il file. Prova con un file diverso.", + "@fileGenericError": { + "description": "Fallback file error message." + }, + "permissionCameraRequired": "È necessario il permesso della fotocamera. Attivalo nelle impostazioni.", + "@permissionCameraRequired": { + "description": "Message when camera permission is missing." + }, + "permissionStorageRequired": "È necessario il permesso di archiviazione. Attivalo nelle impostazioni.", + "@permissionStorageRequired": { + "description": "Message when storage permission is missing." + }, + "permissionMicrophoneRequired": "È necessario il permesso del microfono. Attivalo nelle impostazioni.", + "@permissionMicrophoneRequired": { + "description": "Message when microphone permission is missing." + }, + "permissionGenericError": "È necessaria un'autorizzazione. Controlla i permessi dell'app nelle impostazioni.", + "@permissionGenericError": { + "description": "Fallback permission error message." + }, + "actionRetryRequest": "Riprova la richiesta.", + "@actionRetryRequest": { + "description": "Description for retrying a failed request." + }, + "actionVerifyConnection": "Verifica la connessione a Internet.", + "@actionVerifyConnection": { + "description": "Description for checking internet connectivity." + }, + "actionRetryOperation": "Riprova l'operazione.", + "@actionRetryOperation": { + "description": "Description for retrying the same operation." + }, + "actionRetryAfterDelay": "Attendi un momento e riprova.", + "@actionRetryAfterDelay": { + "description": "Description suggesting a short delay before retrying." + }, + "actionSignInToAccount": "Accedi al tuo account.", + "@actionSignInToAccount": { + "description": "Description for signing back into the app." + }, + "actionSelectAnotherFile": "Seleziona un altro file.", + "@actionSelectAnotherFile": { + "description": "Description for choosing a different file." + }, + "actionOpenAppSettings": "Apri le impostazioni dell'app per concedere i permessi.", + "@actionOpenAppSettings": { + "description": "Description for opening system or app settings." + }, + "actionRetryAfterPermission": "Riprova dopo aver concesso il permesso.", + "@actionRetryAfterPermission": { + "description": "Description for retrying once permissions are granted." + }, + "actionReturnToPrevious": "Torna alla schermata precedente.", + "@actionReturnToPrevious": { + "description": "Description for navigating back to the prior screen." + }, + "continueAction": "Continua", + "@continueAction": { + "description": "Button label to continue an action or flow." + }, + "loadingShort": "Caricamento", + "@loadingShort": { + "description": "Short loading label used for accessibility." + }, + "loadingAnnouncement": "Caricamento: {message}", + "@loadingAnnouncement": { + "description": "Screen reader announcement when loading a resource.", + "placeholders": { + "message": { + "type": "String", + "example": "Messages" + } + } + }, + "errorAnnouncement": "Errore: {error}", + "@errorAnnouncement": { + "description": "Screen reader announcement for an error.", + "placeholders": { + "error": { + "type": "String", + "example": "Network timeout" + } + } + }, + "errorAnnouncementWithSuggestion": "Errore: {error}. {suggestion}", + "@errorAnnouncementWithSuggestion": { + "description": "Screen reader announcement for an error with a follow-up suggestion.", + "placeholders": { + "error": { + "type": "String", + "example": "Network timeout" + }, + "suggestion": { + "type": "String", + "example": "Please try again later." + } + } + }, + "successAnnouncement": "Operazione riuscita: {message}", + "@successAnnouncement": { + "description": "Screen reader announcement for successful actions.", + "placeholders": { + "message": { + "type": "String", + "example": "Profile updated" + } + } + }, + "requiredFieldLabel": "{label} *", + "@requiredFieldLabel": { + "description": "Label text indicating a required field.", + "placeholders": { + "label": { + "type": "String", + "example": "Email" + } + } + }, + "requiredFieldHelper": "Campo obbligatorio", + "@requiredFieldHelper": { + "description": "Helper text indicating that the field is required." + }, + "switchOnLabel": "Attivo", + "@switchOnLabel": { + "description": "Semantic label when a switch is enabled." + }, + "switchOffLabel": "Disattivo", + "@switchOffLabel": { + "description": "Semantic label when a switch is disabled." + }, + "dialogSemanticLabel": "Dialogo: {title}", + "@dialogSemanticLabel": { + "description": "Semantic label describing the dialog title.", + "placeholders": { + "title": { + "type": "String", + "example": "Settings" + } + } + }, + "previousLabel": "Precedente", + "@previousLabel": { + "description": "Label for navigating to the previous item." + }, + "nextLabel": "Successivo", + "@nextLabel": { + "description": "Label for navigating to the next item." + }, + "themePaletteConduitLabel": "Conduit", + "@themePaletteConduitLabel": { + "description": "Palette name for the default Conduit theme." + }, + "themePaletteConduitDescription": "Tema neutro e pulito progettato per Conduit.", + "@themePaletteConduitDescription": { + "description": "Description of the Conduit palette." + }, + "themePaletteClaudeLabel": "Claude", + "@themePaletteClaudeLabel": { + "description": "Palette name inspired by the Claude web client." + }, + "themePaletteClaudeDescription": "Palette calda e tattile ispirata al client web Claude.", + "@themePaletteClaudeDescription": { + "description": "Description of the Claude palette." + }, + "themePaletteT3ChatLabel": "T3 Chat", + "@themePaletteT3ChatLabel": { + "description": "Palette name inspired by the T3 Stack brand." + }, + "themePaletteT3ChatDescription": "Sfumature vivaci ispirate al brand T3 Stack.", + "@themePaletteT3ChatDescription": { + "description": "Description of the T3 Chat palette." + }, + "themePaletteCatppuccinLabel": "Catppuccin", + "@themePaletteCatppuccinLabel": { + "description": "Palette name for Catppuccin colors." + }, + "themePaletteCatppuccinDescription": "Palette morbida di tonalità pastello.", + "@themePaletteCatppuccinDescription": { + "description": "Description of the Catppuccin palette." + }, + "themePaletteTangerineLabel": "Tangerine", + "@themePaletteTangerineLabel": { + "description": "Palette name for Tangerine colors." + }, + "themePaletteTangerineDescription": "Palette calda arancione e ardesia.", + "@themePaletteTangerineDescription": { + "description": "Description of the Tangerine palette." + }, + "@onboardStartTitle": { + "description": "Onboarding card: start chatting title.", + "placeholders": { + "username": { + "type": "String", + "example": "Alex" + } + } + }, + "@notAnImageFile": { + "description": "Error when a referenced file is not an image.", + "placeholders": { + "fileName": { + "type": "String", + "example": "image.txt" + } + } + }, + "@failedToLoadImage": { + "description": "Error including the underlying reason when image loading fails.", + "placeholders": { + "error": { + "type": "String", + "example": "Network error" + } + } + }, + "@ttsVoicesForLanguage": { + "description": "Section header for voices matching the app language", + "placeholders": { + "language": { + "type": "String", + "example": "EN" + } + } + }, + "voiceCallReady": "Pronto", + "@voiceCallReady": { + "description": "Status label shown when the voice call is ready to start." + }, + "voiceCallConnecting": "Connessione...", + "@voiceCallConnecting": { + "description": "Status label shown while the voice call is connecting." + }, + "voiceCallListening": "In ascolto", + "@voiceCallListening": { + "description": "Status label shown while the call is listening for input." + }, + "voiceCallPaused": "In pausa", + "@voiceCallPaused": { + "description": "Status label shown when the call is paused." + }, + "voiceCallProcessing": "Elaborazione...", + "@voiceCallProcessing": { + "description": "Status label shown while the call processes a response." + }, + "voiceCallSpeaking": "Sta parlando", + "@voiceCallSpeaking": { + "description": "Status label shown while the assistant is speaking." + }, + "voiceCallDisconnected": "Disconnesso", + "@voiceCallDisconnected": { + "description": "Status label shown when the voice call has ended or disconnected." + }, + "voiceCallErrorHelp": "Controlla:\n• Sono state concesse le autorizzazioni del microfono\n• Il riconoscimento vocale è disponibile sul dispositivo\n• Sei connesso al server", + "@voiceCallErrorHelp": { + "description": "Guidance shown when the voice call encounters an error." + } } diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index e97c797..91f7949 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -116,12 +116,6 @@ abstract class AppLocalizations { /// **'Conduit'** String get appTitle; - /// Shown if the app fails to initialize critical services. - /// - /// In en, this message translates to: - /// **'Initialization Failed'** - String get initializationFailed; - /// Button label to try an action again. /// /// In en, this message translates to: @@ -170,12 +164,6 @@ abstract class AppLocalizations { /// **'Reconnect to continue or sign out to choose a different server.'** String get connectionIssueSubtitle; - /// Status message after a retry when connectivity has not been restored - /// - /// In en, this message translates to: - /// **'We still can\'t reach the server. Double-check your connection and try again.'** - String get stillOfflineMessage; - /// Section header for account-related options. /// /// In en, this message translates to: @@ -260,6 +248,18 @@ abstract class AppLocalizations { /// **'Available Models'** String get availableModels; + /// Capability chip label for models that support multimodal input. + /// + /// In en, this message translates to: + /// **'Multimodal'** + String get modelCapabilityMultimodal; + + /// Capability chip label for models that support reasoning features. + /// + /// In en, this message translates to: + /// **'Reasoning'** + String get modelCapabilityReasoning; + /// Shown when a search returns no matches. /// /// In en, this message translates to: @@ -278,30 +278,6 @@ abstract class AppLocalizations { /// **'Something went wrong. Please try again.'** String get errorMessage; - /// Button text for the login action. - /// - /// In en, this message translates to: - /// **'Login'** - String get loginButton; - - /// Generic settings menu item label. - /// - /// In en, this message translates to: - /// **'Settings'** - String get menuItem; - - /// Greeting message with a dynamic user name. - /// - /// In en, this message translates to: - /// **'Welcome, {name}!'** - String dynamicContentWithPlaceholder(String name); - - /// Pluralized count of items. - /// - /// In en, this message translates to: - /// **'{count, plural, =0{No items} one{1 item} other{{count} items}}'** - String itemsCount(int count); - /// Accessible label for a generic Close button. /// /// In en, this message translates to: @@ -314,6 +290,36 @@ abstract class AppLocalizations { /// **'Loading content'** String get loadingContent; + /// Short loading label used for accessibility. + /// + /// In en, this message translates to: + /// **'Loading'** + String get loadingShort; + + /// Screen reader announcement when loading a resource. + /// + /// In en, this message translates to: + /// **'Loading: {message}'** + String loadingAnnouncement(String message); + + /// Screen reader announcement for an error. + /// + /// In en, this message translates to: + /// **'Error: {error}'** + String errorAnnouncement(String error); + + /// Screen reader announcement for an error with a follow-up suggestion. + /// + /// In en, this message translates to: + /// **'Error: {error}. {suggestion}'** + String errorAnnouncementWithSuggestion(String error, String suggestion); + + /// Screen reader announcement for successful actions. + /// + /// In en, this message translates to: + /// **'Success: {message}'** + String successAnnouncement(String message); + /// Placeholder text when a list is empty. /// /// In en, this message translates to: @@ -326,114 +332,30 @@ abstract class AppLocalizations { /// **'No items to display'** String get noItemsToDisplay; - /// Button label to load additional items in a paged list. - /// - /// In en, this message translates to: - /// **'Load More'** - String get loadMore; - - /// Section/tab label for documents and files. - /// - /// In en, this message translates to: - /// **'Workspace'** - String get workspace; - - /// Header for recently accessed files. - /// - /// In en, this message translates to: - /// **'Recent Files'** - String get recentFiles; - /// Section for knowledge base content. /// /// In en, this message translates to: /// **'Knowledge Base'** String get knowledgeBase; - /// Empty state when no files are present. - /// - /// In en, this message translates to: - /// **'No files yet'** - String get noFilesYet; - - /// Prompt encouraging users to upload documents. - /// - /// In en, this message translates to: - /// **'Upload documents to reference in your conversations with Conduit'** - String get uploadDocsPrompt; - - /// CTA to add the first file. - /// - /// In en, this message translates to: - /// **'Upload your first file'** - String get uploadFirstFile; - /// Header above list of attached files in compose area. /// /// In en, this message translates to: /// **'Attachments'** String get attachments; - /// Empty state title for the knowledge base section. - /// - /// In en, this message translates to: - /// **'Knowledge base is empty'** - String get knowledgeBaseEmpty; - - /// Prompt describing the benefit of creating collections. - /// - /// In en, this message translates to: - /// **'Create collections of related documents for easy reference'** - String get createCollectionsPrompt; - - /// Sheet title to pick camera or photo library. - /// - /// In en, this message translates to: - /// **'Choose your source'** - String get chooseSourcePhoto; - /// Action to open camera and capture a new photo. /// /// In en, this message translates to: /// **'Take a photo'** String get takePhoto; - /// Action to pick an existing photo from library. - /// - /// In en, this message translates to: - /// **'Choose from your photos'** - String get chooseFromGallery; - /// Generic document label used in UI. /// /// In en, this message translates to: /// **'Document'** String get document; - /// Helper hint listing supported document types. - /// - /// In en, this message translates to: - /// **'PDF, Word, or text file'** - String get documentHint; - - /// Dialog/sheet title for file upload. - /// - /// In en, this message translates to: - /// **'Upload File'** - String get uploadFileTitle; - - /// Temporary message for upcoming upload feature by type - /// - /// In en, this message translates to: - /// **'File upload for {type} is coming soon!'** - String fileUploadComingSoon(String type); - - /// Temporary message indicating KB creation feature is not yet available. - /// - /// In en, this message translates to: - /// **'Knowledge base creation is coming soon!'** - String get kbCreationComingSoon; - /// Button/back label to return to server configuration flow. /// /// In en, this message translates to: @@ -806,12 +728,6 @@ abstract class AppLocalizations { /// **'Start New Chat or manage models from the top bar'** String get onboardQuickBullet2; - /// Button to add an attachment (file/photo). - /// - /// In en, this message translates to: - /// **'Add attachment'** - String get addAttachment; - /// Label shown beside attachment chips in messages. /// /// In en, this message translates to: @@ -890,6 +806,84 @@ abstract class AppLocalizations { /// **'Start'** String get voiceActionStart; + /// Title displayed on the voice call screen. + /// + /// In en, this message translates to: + /// **'Voice Call'** + String get voiceCallTitle; + + /// Button label to pause a voice call. + /// + /// In en, this message translates to: + /// **'Pause'** + String get voiceCallPause; + + /// Button label to resume a paused voice call. + /// + /// In en, this message translates to: + /// **'Resume'** + String get voiceCallResume; + + /// Button label to stop the active voice call. + /// + /// In en, this message translates to: + /// **'Stop'** + String get voiceCallStop; + + /// Button label to end the voice call session. + /// + /// In en, this message translates to: + /// **'End Call'** + String get voiceCallEnd; + + /// Status label shown when the voice call is ready to start. + /// + /// In en, this message translates to: + /// **'Ready'** + String get voiceCallReady; + + /// Status label shown while the voice call is connecting. + /// + /// In en, this message translates to: + /// **'Connecting...'** + String get voiceCallConnecting; + + /// Status label shown while the call is listening for input. + /// + /// In en, this message translates to: + /// **'Listening'** + String get voiceCallListening; + + /// Status label shown when the call is paused. + /// + /// In en, this message translates to: + /// **'Paused'** + String get voiceCallPaused; + + /// Status label shown while the call processes a response. + /// + /// In en, this message translates to: + /// **'Thinking...'** + String get voiceCallProcessing; + + /// Status label shown while the assistant is speaking. + /// + /// In en, this message translates to: + /// **'Speaking'** + String get voiceCallSpeaking; + + /// Status label shown when the voice call has ended or disconnected. + /// + /// In en, this message translates to: + /// **'Disconnected'** + String get voiceCallDisconnected; + + /// Guidance shown when the voice call encounters an error. + /// + /// In en, this message translates to: + /// **'Please check:\n• Microphone permissions are granted\n• Speech recognition is available on your device\n• You are connected to the server'** + String get voiceCallErrorHelp; + /// Accessibility label for the message input. /// /// In en, this message translates to: @@ -938,6 +932,12 @@ abstract class AppLocalizations { /// **'File'** String get file; + /// Action label prompting the user to pick another file. + /// + /// In en, this message translates to: + /// **'Choose Different File'** + String get chooseDifferentFile; + /// A photo item or attachment type label. /// /// In en, this message translates to: @@ -998,24 +998,18 @@ abstract class AppLocalizations { /// **'Empty image data'** String get emptyImageData; - /// Informational text explaining internet requirement. - /// - /// In en, this message translates to: - /// **'This feature requires an internet connection'** - String get featureRequiresInternet; - - /// Queue behavior notice while offline. - /// - /// In en, this message translates to: - /// **'Messages will be sent when you\'re back online'** - String get messagesWillSendWhenOnline; - /// Confirmation button label. /// /// In en, this message translates to: /// **'Confirm'** String get confirm; + /// Button label to continue an action or flow. + /// + /// In en, this message translates to: + /// **'Continue'** + String get continueAction; + /// Cancel button label. /// /// In en, this message translates to: @@ -1028,18 +1022,24 @@ abstract class AppLocalizations { /// **'OK'** String get ok; + /// Label for navigating to the previous item. + /// + /// In en, this message translates to: + /// **'Prev'** + String get previousLabel; + + /// Label for navigating to the next item. + /// + /// In en, this message translates to: + /// **'Next'** + String get nextLabel; + /// Accessibility label describing an input field. /// /// In en, this message translates to: /// **'Input field'** String get inputField; - /// Action to capture a document or image using camera. - /// - /// In en, this message translates to: - /// **'Capture a document or image'** - String get captureDocumentOrImage; - /// CTA to verify network connectivity. /// /// In en, this message translates to: @@ -1052,12 +1052,6 @@ abstract class AppLocalizations { /// **'Open Settings'** String get openSettings; - /// CTA to pick an alternative file. - /// - /// In en, this message translates to: - /// **'Choose Different File'** - String get chooseDifferentFile; - /// CTA to navigate back. /// /// In en, this message translates to: @@ -1070,6 +1064,36 @@ abstract class AppLocalizations { /// **'Technical Details'** String get technicalDetails; + /// Label text indicating a required field. + /// + /// In en, this message translates to: + /// **'{label} *'** + String requiredFieldLabel(String label); + + /// Helper text indicating that the field is required. + /// + /// In en, this message translates to: + /// **'Required field'** + String get requiredFieldHelper; + + /// Semantic label when a switch is enabled. + /// + /// In en, this message translates to: + /// **'On'** + String get switchOnLabel; + + /// Semantic label when a switch is disabled. + /// + /// In en, this message translates to: + /// **'Off'** + String get switchOffLabel; + + /// Semantic label describing the dialog title. + /// + /// In en, this message translates to: + /// **'Dialog: {title}'** + String dialogSemanticLabel(String title); + /// Primary action to save changes. /// /// In en, this message translates to: @@ -1124,12 +1148,6 @@ abstract class AppLocalizations { /// **'Clear'** String get clear; - /// Generic search input hint. - /// - /// In en, this message translates to: - /// **'Search...'** - String get searchHint; - /// Search input hint scoped to conversations. /// /// In en, this message translates to: @@ -1142,24 +1160,12 @@ abstract class AppLocalizations { /// **'Create'** String get create; - /// Toast/notice after successfully creating a folder. - /// - /// In en, this message translates to: - /// **'Folder created'** - String get folderCreated; - /// Error notice when folder creation fails. /// /// In en, this message translates to: /// **'Failed to create folder'** String get failedToCreateFolder; - /// Toast indicating a chat titled {title} was moved to folder {folder}. - /// - /// In en, this message translates to: - /// **'Moved \"{title}\" to \"{folder}\"'** - String movedChatToFolder(String title, String folder); - /// Error notice when moving a chat fails. /// /// In en, this message translates to: @@ -1586,12 +1592,6 @@ abstract class AppLocalizations { /// **'Maximum of 10 custom headers allowed. Remove some to add more.'** String get maxHeadersReachedDetail; - /// Action to edit a previously sent message. - /// - /// In en, this message translates to: - /// **'Edit Message'** - String get editMessage; - /// Shown when model list is empty or failed to load. /// /// In en, this message translates to: @@ -1616,11 +1616,65 @@ abstract class AppLocalizations { /// **'Accent palette'** String get themePalette; - /// Helper text explaining palette selection. + /// Palette name for the default Conduit theme. /// /// In en, this message translates to: - /// **'Choose the accent colors used for buttons, cards, and chat bubbles.'** - String get themePaletteDescription; + /// **'Conduit'** + String get themePaletteConduitLabel; + + /// Description of the Conduit palette. + /// + /// In en, this message translates to: + /// **'Clean neutral theme designed for Conduit.'** + String get themePaletteConduitDescription; + + /// Palette name inspired by the Claude web client. + /// + /// In en, this message translates to: + /// **'Claude'** + String get themePaletteClaudeLabel; + + /// Description of the Claude palette. + /// + /// In en, this message translates to: + /// **'Warm, tactile palette lifted from the Claude web client.'** + String get themePaletteClaudeDescription; + + /// Palette name inspired by the T3 Stack brand. + /// + /// In en, this message translates to: + /// **'T3 Chat'** + String get themePaletteT3ChatLabel; + + /// Description of the T3 Chat palette. + /// + /// In en, this message translates to: + /// **'Playful gradients inspired by the T3 Stack brand.'** + String get themePaletteT3ChatDescription; + + /// Palette name for Catppuccin colors. + /// + /// In en, this message translates to: + /// **'Catppuccin'** + String get themePaletteCatppuccinLabel; + + /// Description of the Catppuccin palette. + /// + /// In en, this message translates to: + /// **'Soft pastel palette.'** + String get themePaletteCatppuccinDescription; + + /// Palette name for Tangerine colors. + /// + /// In en, this message translates to: + /// **'Tangerine'** + String get themePaletteTangerineLabel; + + /// Description of the Tangerine palette. + /// + /// In en, this message translates to: + /// **'Warm orange-and-slate palette.'** + String get themePaletteTangerineDescription; /// Theme label for light appearance. /// @@ -1700,6 +1754,18 @@ abstract class AppLocalizations { /// **'Quickpills in chat'** String get quickActionsDescription; + /// Subtitle indicating how many quick actions are selected. + /// + /// In en, this message translates to: + /// **'{count, plural, =0{No actions selected} one{1 action selected} other{{count} actions selected}}'** + String quickActionsSelectedCount(int count); + + /// Explains what the auto-select model setting does. + /// + /// In en, this message translates to: + /// **'Let the app choose the best model'** + String get autoSelectDescription; + /// Section header for chat-related customization options. /// /// In en, this message translates to: @@ -1718,6 +1784,24 @@ abstract class AppLocalizations { /// **'Enter sends (soft keyboard). Cmd/Ctrl+Enter also available'** String get sendOnEnterDescription; + /// Label for selecting the text-to-speech engine. + /// + /// In en, this message translates to: + /// **'Engine'** + String get ttsEngineLabel; + + /// Chip label for using on-device text-to-speech. + /// + /// In en, this message translates to: + /// **'On device'** + String get ttsEngineDevice; + + /// Chip label for using server-side text-to-speech. + /// + /// In en, this message translates to: + /// **'Server'** + String get ttsEngineServer; + /// Section header for TTS-related customization options. /// /// In en, this message translates to: @@ -1796,6 +1880,216 @@ abstract class AppLocalizations { /// **'Error'** String get error; + /// Error label with appended message text. + /// + /// In en, this message translates to: + /// **'Error: {message}'** + String errorWithMessage(String message); + + /// User-facing message when a network request times out. + /// + /// In en, this message translates to: + /// **'Connection timed out. Please check your internet connection and try again.'** + String get networkTimeoutError; + + /// User-facing message when the server cannot be reached. + /// + /// In en, this message translates to: + /// **'Cannot reach the server. Please check your server URL and internet connection.'** + String get networkUnreachableError; + + /// User-facing message when the server does not respond to a request. + /// + /// In en, this message translates to: + /// **'Server is not responding. Please verify the server is running and accessible.'** + String get networkServerNotResponding; + + /// Fallback message for generic network errors. + /// + /// In en, this message translates to: + /// **'Network connection problem. Please check your internet connection.'** + String get networkGenericError; + + /// Message when a 500 error is encountered. + /// + /// In en, this message translates to: + /// **'Server is experiencing issues. This is usually temporary.'** + String get serverError500; + + /// Message when a 502/503 error is encountered. + /// + /// In en, this message translates to: + /// **'Server is temporarily unavailable. Please try again in a moment.'** + String get serverErrorUnavailable; + + /// Message when the server times out. + /// + /// In en, this message translates to: + /// **'Server took too long to respond. Please try again.'** + String get serverErrorTimeout; + + /// Fallback server error message. + /// + /// In en, this message translates to: + /// **'Server is having problems. Please try again later.'** + String get serverErrorGeneric; + + /// Message when an authentication session expires. + /// + /// In en, this message translates to: + /// **'Your session has expired. Please sign in again.'** + String get authSessionExpired; + + /// Message when the user lacks required permissions. + /// + /// In en, this message translates to: + /// **'You don\'t have permission to perform this action.'** + String get authForbidden; + + /// Message when the authentication token is invalid. + /// + /// In en, this message translates to: + /// **'Authentication token is invalid. Please sign in again.'** + String get authInvalidToken; + + /// Fallback authentication error message. + /// + /// In en, this message translates to: + /// **'Authentication problem. Please sign in again.'** + String get authGenericError; + + /// Validation message for invalid email input. + /// + /// In en, this message translates to: + /// **'Please enter a valid email address.'** + String get validationInvalidEmail; + + /// Validation message for weak passwords. + /// + /// In en, this message translates to: + /// **'Password doesn\'t meet requirements. Please check and try again.'** + String get validationWeakPassword; + + /// Validation message when required fields are missing. + /// + /// In en, this message translates to: + /// **'Please fill in all required fields.'** + String get validationMissingRequired; + + /// Validation message for generic formatting issues. + /// + /// In en, this message translates to: + /// **'Some information is in the wrong format. Please check and try again.'** + String get validationFormatError; + + /// Fallback validation message. + /// + /// In en, this message translates to: + /// **'Please check your input and try again.'** + String get validationGenericError; + + /// Message when a file cannot be located. + /// + /// In en, this message translates to: + /// **'File not found. It may have been moved or deleted.'** + String get fileNotFound; + + /// Message when file access is denied. + /// + /// In en, this message translates to: + /// **'Cannot access the file. Please check permissions.'** + String get fileAccessDenied; + + /// Message when a file exceeds size limits. + /// + /// In en, this message translates to: + /// **'File is too large. Please choose a smaller file.'** + String get fileTooLarge; + + /// Fallback file error message. + /// + /// In en, this message translates to: + /// **'Problem with the file. Please try a different file.'** + String get fileGenericError; + + /// Message when camera permission is missing. + /// + /// In en, this message translates to: + /// **'Camera permission is required. Please enable it in settings.'** + String get permissionCameraRequired; + + /// Message when storage permission is missing. + /// + /// In en, this message translates to: + /// **'Storage permission is required. Please enable it in settings.'** + String get permissionStorageRequired; + + /// Message when microphone permission is missing. + /// + /// In en, this message translates to: + /// **'Microphone permission is required. Please enable it in settings.'** + String get permissionMicrophoneRequired; + + /// Fallback permission error message. + /// + /// In en, this message translates to: + /// **'Permission required. Please check app permissions in settings.'** + String get permissionGenericError; + + /// Description for retrying a failed request. + /// + /// In en, this message translates to: + /// **'Try the request again.'** + String get actionRetryRequest; + + /// Description for checking internet connectivity. + /// + /// In en, this message translates to: + /// **'Verify your internet connection.'** + String get actionVerifyConnection; + + /// Description for retrying the same operation. + /// + /// In en, this message translates to: + /// **'Retry the operation.'** + String get actionRetryOperation; + + /// Description suggesting a short delay before retrying. + /// + /// In en, this message translates to: + /// **'Wait a moment then try again.'** + String get actionRetryAfterDelay; + + /// Description for signing back into the app. + /// + /// In en, this message translates to: + /// **'Sign in to your account.'** + String get actionSignInToAccount; + + /// Description for choosing a different file. + /// + /// In en, this message translates to: + /// **'Select another file.'** + String get actionSelectAnotherFile; + + /// Description for opening system or app settings. + /// + /// In en, this message translates to: + /// **'Open app settings to grant permissions.'** + String get actionOpenAppSettings; + + /// Description for retrying once permissions are granted. + /// + /// In en, this message translates to: + /// **'Retry after granting permission.'** + String get actionRetryAfterPermission; + + /// Description for navigating back to the prior screen. + /// + /// In en, this message translates to: + /// **'Return to previous screen.'** + String get actionReturnToPrevious; + /// Section header for visual and layout related settings. /// /// In en, this message translates to: @@ -1814,12 +2108,6 @@ abstract class AppLocalizations { /// **'Transport mode'** String get transportMode; - /// Helper text explaining the transport setting. - /// - /// In en, this message translates to: - /// **'Choose how the app connects for realtime updates.'** - String get transportModeDescription; - /// Form field label for transport mode dropdown. /// /// In en, this message translates to: @@ -1849,18 +2137,6 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'Lower overhead, but may fail behind strict proxies/firewalls.'** String get transportModeWsInfo; - - /// Error message shown when WebSocket connection fails initially. - /// - /// In en, this message translates to: - /// **'Unable to establish real-time connection. Please check your network and server configuration.'** - String get websocketConnectionError; - - /// Error message shown when WebSocket reconnection attempts fail. - /// - /// In en, this message translates to: - /// **'Real-time connection failed. Streaming may not work properly.'** - String get websocketReconnectFailed; } class _AppLocalizationsDelegate diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 19578db..cbb3f39 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -11,9 +11,6 @@ class AppLocalizationsDe extends AppLocalizations { @override String get appTitle => 'Conduit'; - @override - String get initializationFailed => 'Initialisierung fehlgeschlagen'; - @override String get retry => 'Erneut versuchen'; @@ -40,10 +37,6 @@ class AppLocalizationsDe extends AppLocalizations { String get connectionIssueSubtitle => 'Verbindung wiederherstellen oder abmelden, um einen anderen Server zu wählen.'; - @override - String get stillOfflineMessage => - 'Der Server ist weiterhin nicht erreichbar. Prüfe deine Verbindung und versuche es erneut.'; - @override String get account => 'Konto'; @@ -89,6 +82,12 @@ class AppLocalizationsDe extends AppLocalizations { @override String get availableModels => 'Verfügbare Modelle'; + @override + String get modelCapabilityMultimodal => 'Multimodal'; + + @override + String get modelCapabilityReasoning => 'Reasoning'; + @override String get noResults => 'Keine Ergebnisse'; @@ -99,100 +98,53 @@ class AppLocalizationsDe extends AppLocalizations { String get errorMessage => 'Etwas ist schief gelaufen. Bitte versuche es erneut.'; - @override - String get loginButton => 'Anmelden'; - - @override - String get menuItem => 'Einstellungen'; - - @override - String dynamicContentWithPlaceholder(String name) { - return 'Willkommen, $name!'; - } - - @override - String itemsCount(int count) { - String _temp0 = intl.Intl.pluralLogic( - count, - locale: localeName, - other: '$count Elemente', - one: '1 Element', - zero: 'Keine Elemente', - ); - return '$_temp0'; - } - @override String get closeButtonSemantic => 'Schließen'; @override String get loadingContent => 'Inhalt wird geladen'; + @override + String get loadingShort => 'Laden'; + + @override + String loadingAnnouncement(String message) { + return 'Laden: $message'; + } + + @override + String errorAnnouncement(String error) { + return 'Fehler: $error'; + } + + @override + String errorAnnouncementWithSuggestion(String error, String suggestion) { + return 'Fehler: $error. $suggestion'; + } + + @override + String successAnnouncement(String message) { + return 'Erfolg: $message'; + } + @override String get noItems => 'Keine Elemente'; @override String get noItemsToDisplay => 'Keine Elemente zum Anzeigen'; - @override - String get loadMore => 'Mehr laden'; - - @override - String get workspace => 'Arbeitsbereich'; - - @override - String get recentFiles => 'Zuletzt verwendete Dateien'; - @override String get knowledgeBase => 'Wissensdatenbank'; - @override - String get noFilesYet => 'Noch keine Dateien'; - - @override - String get uploadDocsPrompt => - 'Lade Dokumente hoch, um sie in deinen Unterhaltungen mit Conduit zu verwenden'; - - @override - String get uploadFirstFile => 'Erste Datei hochladen'; - @override String get attachments => 'Anhänge'; - @override - String get knowledgeBaseEmpty => 'Wissensdatenbank ist leer'; - - @override - String get createCollectionsPrompt => - 'Erstelle Sammlungen verwandter Dokumente zur einfachen Referenz'; - - @override - String get chooseSourcePhoto => 'Quelle auswählen'; - @override String get takePhoto => 'Foto aufnehmen'; - @override - String get chooseFromGallery => 'Aus Fotos auswählen'; - @override String get document => 'Dokument'; - @override - String get documentHint => 'PDF-, Word- oder Textdatei'; - - @override - String get uploadFileTitle => 'Datei hochladen'; - - @override - String fileUploadComingSoon(String type) { - return 'Dateiupload für $type kommt bald!'; - } - - @override - String get kbCreationComingSoon => - 'Erstellung der Wissensdatenbank kommt bald!'; - @override String get backToServerSetup => 'Zur Servereinrichtung zurück'; @@ -402,9 +354,6 @@ class AppLocalizationsDe extends AppLocalizations { String get onboardQuickBullet2 => 'Neuer Chat starten oder Modelle oben verwalten'; - @override - String get addAttachment => 'Anhang hinzufügen'; - @override String get attachmentLabel => 'Anhang'; @@ -444,6 +393,46 @@ class AppLocalizationsDe extends AppLocalizations { @override String get voiceActionStart => 'Starten'; + @override + String get voiceCallTitle => 'Sprachanruf'; + + @override + String get voiceCallPause => 'Pause'; + + @override + String get voiceCallResume => 'Fortsetzen'; + + @override + String get voiceCallStop => 'Stopp'; + + @override + String get voiceCallEnd => 'Anruf beenden'; + + @override + String get voiceCallReady => 'Bereit'; + + @override + String get voiceCallConnecting => 'Verbinden...'; + + @override + String get voiceCallListening => 'Zuhören'; + + @override + String get voiceCallPaused => 'Pausiert'; + + @override + String get voiceCallProcessing => 'Denkt...'; + + @override + String get voiceCallSpeaking => 'Spricht'; + + @override + String get voiceCallDisconnected => 'Getrennt'; + + @override + String get voiceCallErrorHelp => + 'Bitte prüfe:\n• Mikrofonberechtigungen sind erteilt\n• Spracherkennung ist auf deinem Gerät verfügbar\n• Du bist mit dem Server verbunden'; + @override String get messageInputLabel => 'Nachrichteneingabe'; @@ -468,6 +457,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get file => 'Datei'; + @override + String get chooseDifferentFile => 'Andere Datei auswählen'; + @override String get photo => 'Foto'; @@ -502,17 +494,12 @@ class AppLocalizationsDe extends AppLocalizations { @override String get emptyImageData => 'Leere Bilddaten'; - @override - String get featureRequiresInternet => - 'Diese Funktion erfordert eine Internetverbindung'; - - @override - String get messagesWillSendWhenOnline => - 'Nachrichten werden gesendet, sobald du wieder online bist'; - @override String get confirm => 'Bestätigen'; + @override + String get continueAction => 'Weiter'; + @override String get cancel => 'Abbrechen'; @@ -520,10 +507,13 @@ class AppLocalizationsDe extends AppLocalizations { String get ok => 'OK'; @override - String get inputField => 'Eingabefeld'; + String get previousLabel => 'Zurück'; @override - String get captureDocumentOrImage => 'Dokument oder Bild aufnehmen'; + String get nextLabel => 'Weiter'; + + @override + String get inputField => 'Eingabefeld'; @override String get checkConnection => 'Verbindung prüfen'; @@ -531,15 +521,31 @@ class AppLocalizationsDe extends AppLocalizations { @override String get openSettings => 'Einstellungen öffnen'; - @override - String get chooseDifferentFile => 'Andere Datei wählen'; - @override String get goBack => 'Zurück'; @override String get technicalDetails => 'Technische Details'; + @override + String requiredFieldLabel(String label) { + return '$label *'; + } + + @override + String get requiredFieldHelper => 'Pflichtfeld'; + + @override + String get switchOnLabel => 'Ein'; + + @override + String get switchOffLabel => 'Aus'; + + @override + String dialogSemanticLabel(String title) { + return 'Dialog: $title'; + } + @override String get save => 'Speichern'; @@ -567,26 +573,15 @@ class AppLocalizationsDe extends AppLocalizations { @override String get clear => 'Leeren'; - @override - String get searchHint => 'Suchen...'; - @override String get searchConversations => 'Konversationen durchsuchen...'; @override String get create => 'Erstellen'; - @override - String get folderCreated => 'Ordner erstellt'; - @override String get failedToCreateFolder => 'Ordner konnte nicht erstellt werden'; - @override - String movedChatToFolder(String title, String folder) { - return '\"$title\" nach \"$folder\" verschoben'; - } - @override String get failedToMoveChat => 'Chat konnte nicht verschoben werden'; @@ -818,9 +813,6 @@ class AppLocalizationsDe extends AppLocalizations { String get maxHeadersReachedDetail => 'Maximal 10 benutzerdefinierte Header zulässig. Einige entfernen, um mehr hinzuzufügen.'; - @override - String get editMessage => 'Nachricht bearbeiten'; - @override String get noModelsAvailable => 'Keine Modelle verfügbar'; @@ -836,8 +828,38 @@ class AppLocalizationsDe extends AppLocalizations { String get themePalette => 'Farbpalette'; @override - String get themePaletteDescription => - 'Wählen Sie die Akzentfarben für Schaltflächen, Karten und Chatblasen.'; + String get themePaletteConduitLabel => 'Conduit'; + + @override + String get themePaletteConduitDescription => + 'Schlichtes neutrales Design für Conduit.'; + + @override + String get themePaletteClaudeLabel => 'Claude'; + + @override + String get themePaletteClaudeDescription => + 'Warmes, haptisches Farbschema aus dem Claude-Webclient.'; + + @override + String get themePaletteT3ChatLabel => 'T3 Chat'; + + @override + String get themePaletteT3ChatDescription => + 'Verspielte Verläufe inspiriert vom T3-Stack.'; + + @override + String get themePaletteCatppuccinLabel => 'Catppuccin'; + + @override + String get themePaletteCatppuccinDescription => 'Sanfte Pastellpalette.'; + + @override + String get themePaletteTangerineLabel => 'Tangerine'; + + @override + String get themePaletteTangerineDescription => + 'Warmes Orange-Schiefer-Farbschema.'; @override String get themeLight => 'Hell'; @@ -884,6 +906,21 @@ class AppLocalizationsDe extends AppLocalizations { @override String get quickActionsDescription => 'Schnellzugriffe im Chat'; + @override + String quickActionsSelectedCount(int count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: '$count Aktionen ausgewählt', + one: '$count Aktion ausgewählt', + zero: 'Keine Aktionen ausgewählt', + ); + return '$_temp0'; + } + + @override + String get autoSelectDescription => 'Lass die App das beste Modell auswählen'; + @override String get chatSettings => 'Chat'; @@ -894,6 +931,15 @@ class AppLocalizationsDe extends AppLocalizations { String get sendOnEnterDescription => 'Enter sendet (Soft-Tastatur). Cmd/Ctrl+Enter ebenfalls verfügbar'; + @override + String get ttsEngineLabel => 'Engine'; + + @override + String get ttsEngineDevice => 'Auf dem Gerät'; + + @override + String get ttsEngineServer => 'Server'; + @override String get ttsSettings => 'Text zu Sprache'; @@ -936,6 +982,139 @@ class AppLocalizationsDe extends AppLocalizations { @override String get error => 'Fehler'; + @override + String errorWithMessage(String message) { + return 'Fehler: $message'; + } + + @override + String get networkTimeoutError => + 'Verbindung abgelaufen. Bitte überprüfe deine Internetverbindung und versuche es erneut.'; + + @override + String get networkUnreachableError => + 'Server nicht erreichbar. Bitte überprüfe die Server-URL und deine Internetverbindung.'; + + @override + String get networkServerNotResponding => + 'Server reagiert nicht. Bitte stelle sicher, dass der Server läuft und erreichbar ist.'; + + @override + String get networkGenericError => + 'Netzwerkproblem. Bitte überprüfe deine Internetverbindung.'; + + @override + String get serverError500 => + 'Der Server hat Probleme. Das ist meist nur vorübergehend.'; + + @override + String get serverErrorUnavailable => + 'Server vorübergehend nicht verfügbar. Bitte versuche es gleich noch einmal.'; + + @override + String get serverErrorTimeout => + 'Der Server hat zu lange für eine Antwort gebraucht. Bitte versuche es erneut.'; + + @override + String get serverErrorGeneric => + 'Der Server hat Schwierigkeiten. Bitte versuche es später erneut.'; + + @override + String get authSessionExpired => + 'Deine Sitzung ist abgelaufen. Bitte melde dich erneut an.'; + + @override + String get authForbidden => 'Du hast keine Berechtigung für diese Aktion.'; + + @override + String get authInvalidToken => + 'Der Authentifizierungstoken ist ungültig. Bitte melde dich erneut an.'; + + @override + String get authGenericError => + 'Authentifizierungsproblem. Bitte melde dich erneut an.'; + + @override + String get validationInvalidEmail => + 'Bitte gib eine gültige E-Mail-Adresse ein.'; + + @override + String get validationWeakPassword => + 'Das Passwort erfüllt die Anforderungen nicht. Bitte überprüfe es und versuche es erneut.'; + + @override + String get validationMissingRequired => 'Bitte fülle alle Pflichtfelder aus.'; + + @override + String get validationFormatError => + 'Einige Angaben haben ein falsches Format. Bitte überprüfe sie und versuche es erneut.'; + + @override + String get validationGenericError => + 'Bitte überprüfe deine Eingaben und versuche es erneut.'; + + @override + String get fileNotFound => + 'Datei nicht gefunden. Vielleicht wurde sie verschoben oder gelöscht.'; + + @override + String get fileAccessDenied => + 'Datei kann nicht geöffnet werden. Bitte prüfe die Berechtigungen.'; + + @override + String get fileTooLarge => + 'Datei ist zu groß. Bitte wähle eine kleinere Datei.'; + + @override + String get fileGenericError => + 'Problem mit der Datei. Bitte versuche eine andere Datei.'; + + @override + String get permissionCameraRequired => + 'Kamerazugriff erforderlich. Bitte aktiviere ihn in den Einstellungen.'; + + @override + String get permissionStorageRequired => + 'Speicherzugriff erforderlich. Bitte aktiviere ihn in den Einstellungen.'; + + @override + String get permissionMicrophoneRequired => + 'Mikrofonzugriff erforderlich. Bitte aktiviere ihn in den Einstellungen.'; + + @override + String get permissionGenericError => + 'Berechtigung erforderlich. Bitte prüfe die App-Berechtigungen in den Einstellungen.'; + + @override + String get actionRetryRequest => 'Versuche die Anfrage erneut.'; + + @override + String get actionVerifyConnection => 'Überprüfe deine Internetverbindung.'; + + @override + String get actionRetryOperation => 'Wiederhole den Vorgang.'; + + @override + String get actionRetryAfterDelay => + 'Warte einen Moment und versuche es dann erneut.'; + + @override + String get actionSignInToAccount => 'Melde dich bei deinem Konto an.'; + + @override + String get actionSelectAnotherFile => 'Wähle eine andere Datei.'; + + @override + String get actionOpenAppSettings => + 'Öffne die App-Einstellungen, um Berechtigungen zu erteilen.'; + + @override + String get actionRetryAfterPermission => + 'Versuche es erneut, nachdem du die Berechtigung erteilt hast.'; + + @override + String get actionReturnToPrevious => 'Zur vorherigen Ansicht zurückkehren.'; + @override String get display => 'Anzeige'; @@ -945,10 +1124,6 @@ class AppLocalizationsDe extends AppLocalizations { @override String get transportMode => 'Transportmodus'; - @override - String get transportModeDescription => - 'Wähle, wie die App für Echtzeit-Updates verbindet.'; - @override String get mode => 'Modus'; @@ -965,12 +1140,4 @@ class AppLocalizationsDe extends AppLocalizations { @override String get transportModeWsInfo => 'Geringerer Overhead, kann jedoch hinter strikten Proxys/Firewalls fehlschlagen.'; - - @override - String get websocketConnectionError => - 'Echtzeit-Verbindung konnte nicht hergestellt werden. Bitte überprüfen Sie Ihr Netzwerk und die Serverkonfiguration.'; - - @override - String get websocketReconnectFailed => - 'Echtzeit-Verbindung fehlgeschlagen. Streaming funktioniert möglicherweise nicht ordnungsgemäß.'; } diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index bb3189d..72eb92f 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -11,9 +11,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get appTitle => 'Conduit'; - @override - String get initializationFailed => 'Initialization Failed'; - @override String get retry => 'Retry'; @@ -40,10 +37,6 @@ class AppLocalizationsEn extends AppLocalizations { String get connectionIssueSubtitle => 'Reconnect to continue or sign out to choose a different server.'; - @override - String get stillOfflineMessage => - 'We still can\'t reach the server. Double-check your connection and try again.'; - @override String get account => 'Account'; @@ -88,6 +81,12 @@ class AppLocalizationsEn extends AppLocalizations { @override String get availableModels => 'Available Models'; + @override + String get modelCapabilityMultimodal => 'Multimodal'; + + @override + String get modelCapabilityReasoning => 'Reasoning'; + @override String get noResults => 'No results'; @@ -97,99 +96,53 @@ class AppLocalizationsEn extends AppLocalizations { @override String get errorMessage => 'Something went wrong. Please try again.'; - @override - String get loginButton => 'Login'; - - @override - String get menuItem => 'Settings'; - - @override - String dynamicContentWithPlaceholder(String name) { - return 'Welcome, $name!'; - } - - @override - String itemsCount(int count) { - String _temp0 = intl.Intl.pluralLogic( - count, - locale: localeName, - other: '$count items', - one: '1 item', - zero: 'No items', - ); - return '$_temp0'; - } - @override String get closeButtonSemantic => 'Close'; @override String get loadingContent => 'Loading content'; + @override + String get loadingShort => 'Loading'; + + @override + String loadingAnnouncement(String message) { + return 'Loading: $message'; + } + + @override + String errorAnnouncement(String error) { + return 'Error: $error'; + } + + @override + String errorAnnouncementWithSuggestion(String error, String suggestion) { + return 'Error: $error. $suggestion'; + } + + @override + String successAnnouncement(String message) { + return 'Success: $message'; + } + @override String get noItems => 'No items'; @override String get noItemsToDisplay => 'No items to display'; - @override - String get loadMore => 'Load More'; - - @override - String get workspace => 'Workspace'; - - @override - String get recentFiles => 'Recent Files'; - @override String get knowledgeBase => 'Knowledge Base'; - @override - String get noFilesYet => 'No files yet'; - - @override - String get uploadDocsPrompt => - 'Upload documents to reference in your conversations with Conduit'; - - @override - String get uploadFirstFile => 'Upload your first file'; - @override String get attachments => 'Attachments'; - @override - String get knowledgeBaseEmpty => 'Knowledge base is empty'; - - @override - String get createCollectionsPrompt => - 'Create collections of related documents for easy reference'; - - @override - String get chooseSourcePhoto => 'Choose your source'; - @override String get takePhoto => 'Take a photo'; - @override - String get chooseFromGallery => 'Choose from your photos'; - @override String get document => 'Document'; - @override - String get documentHint => 'PDF, Word, or text file'; - - @override - String get uploadFileTitle => 'Upload File'; - - @override - String fileUploadComingSoon(String type) { - return 'File upload for $type is coming soon!'; - } - - @override - String get kbCreationComingSoon => 'Knowledge base creation is coming soon!'; - @override String get backToServerSetup => 'Back to server setup'; @@ -397,9 +350,6 @@ class AppLocalizationsEn extends AppLocalizations { String get onboardQuickBullet2 => 'Start New Chat or manage models from the top bar'; - @override - String get addAttachment => 'Add attachment'; - @override String get attachmentLabel => 'Attachment'; @@ -439,6 +389,46 @@ class AppLocalizationsEn extends AppLocalizations { @override String get voiceActionStart => 'Start'; + @override + String get voiceCallTitle => 'Voice Call'; + + @override + String get voiceCallPause => 'Pause'; + + @override + String get voiceCallResume => 'Resume'; + + @override + String get voiceCallStop => 'Stop'; + + @override + String get voiceCallEnd => 'End Call'; + + @override + String get voiceCallReady => 'Ready'; + + @override + String get voiceCallConnecting => 'Connecting...'; + + @override + String get voiceCallListening => 'Listening'; + + @override + String get voiceCallPaused => 'Paused'; + + @override + String get voiceCallProcessing => 'Thinking...'; + + @override + String get voiceCallSpeaking => 'Speaking'; + + @override + String get voiceCallDisconnected => 'Disconnected'; + + @override + String get voiceCallErrorHelp => + 'Please check:\n• Microphone permissions are granted\n• Speech recognition is available on your device\n• You are connected to the server'; + @override String get messageInputLabel => 'Message input'; @@ -463,6 +453,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get file => 'File'; + @override + String get chooseDifferentFile => 'Choose Different File'; + @override String get photo => 'Photo'; @@ -497,17 +490,12 @@ class AppLocalizationsEn extends AppLocalizations { @override String get emptyImageData => 'Empty image data'; - @override - String get featureRequiresInternet => - 'This feature requires an internet connection'; - - @override - String get messagesWillSendWhenOnline => - 'Messages will be sent when you\'re back online'; - @override String get confirm => 'Confirm'; + @override + String get continueAction => 'Continue'; + @override String get cancel => 'Cancel'; @@ -515,10 +503,13 @@ class AppLocalizationsEn extends AppLocalizations { String get ok => 'OK'; @override - String get inputField => 'Input field'; + String get previousLabel => 'Prev'; @override - String get captureDocumentOrImage => 'Capture a document or image'; + String get nextLabel => 'Next'; + + @override + String get inputField => 'Input field'; @override String get checkConnection => 'Check Connection'; @@ -526,15 +517,31 @@ class AppLocalizationsEn extends AppLocalizations { @override String get openSettings => 'Open Settings'; - @override - String get chooseDifferentFile => 'Choose Different File'; - @override String get goBack => 'Go Back'; @override String get technicalDetails => 'Technical Details'; + @override + String requiredFieldLabel(String label) { + return '$label *'; + } + + @override + String get requiredFieldHelper => 'Required field'; + + @override + String get switchOnLabel => 'On'; + + @override + String get switchOffLabel => 'Off'; + + @override + String dialogSemanticLabel(String title) { + return 'Dialog: $title'; + } + @override String get save => 'Save'; @@ -562,26 +569,15 @@ class AppLocalizationsEn extends AppLocalizations { @override String get clear => 'Clear'; - @override - String get searchHint => 'Search...'; - @override String get searchConversations => 'Search conversations...'; @override String get create => 'Create'; - @override - String get folderCreated => 'Folder created'; - @override String get failedToCreateFolder => 'Failed to create folder'; - @override - String movedChatToFolder(String title, String folder) { - return 'Moved \"$title\" to \"$folder\"'; - } - @override String get failedToMoveChat => 'Failed to move chat'; @@ -812,9 +808,6 @@ class AppLocalizationsEn extends AppLocalizations { String get maxHeadersReachedDetail => 'Maximum of 10 custom headers allowed. Remove some to add more.'; - @override - String get editMessage => 'Edit Message'; - @override String get noModelsAvailable => 'No models available'; @@ -830,8 +823,38 @@ class AppLocalizationsEn extends AppLocalizations { String get themePalette => 'Accent palette'; @override - String get themePaletteDescription => - 'Choose the accent colors used for buttons, cards, and chat bubbles.'; + String get themePaletteConduitLabel => 'Conduit'; + + @override + String get themePaletteConduitDescription => + 'Clean neutral theme designed for Conduit.'; + + @override + String get themePaletteClaudeLabel => 'Claude'; + + @override + String get themePaletteClaudeDescription => + 'Warm, tactile palette lifted from the Claude web client.'; + + @override + String get themePaletteT3ChatLabel => 'T3 Chat'; + + @override + String get themePaletteT3ChatDescription => + 'Playful gradients inspired by the T3 Stack brand.'; + + @override + String get themePaletteCatppuccinLabel => 'Catppuccin'; + + @override + String get themePaletteCatppuccinDescription => 'Soft pastel palette.'; + + @override + String get themePaletteTangerineLabel => 'Tangerine'; + + @override + String get themePaletteTangerineDescription => + 'Warm orange-and-slate palette.'; @override String get themeLight => 'Light'; @@ -877,6 +900,21 @@ class AppLocalizationsEn extends AppLocalizations { @override String get quickActionsDescription => 'Quickpills in chat'; + @override + String quickActionsSelectedCount(int count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: '$count actions selected', + one: '1 action selected', + zero: 'No actions selected', + ); + return '$_temp0'; + } + + @override + String get autoSelectDescription => 'Let the app choose the best model'; + @override String get chatSettings => 'Chat'; @@ -887,6 +925,15 @@ class AppLocalizationsEn extends AppLocalizations { String get sendOnEnterDescription => 'Enter sends (soft keyboard). Cmd/Ctrl+Enter also available'; + @override + String get ttsEngineLabel => 'Engine'; + + @override + String get ttsEngineDevice => 'On device'; + + @override + String get ttsEngineServer => 'Server'; + @override String get ttsSettings => 'Text to Speech'; @@ -928,6 +975,134 @@ class AppLocalizationsEn extends AppLocalizations { @override String get error => 'Error'; + @override + String errorWithMessage(String message) { + return 'Error: $message'; + } + + @override + String get networkTimeoutError => + 'Connection timed out. Please check your internet connection and try again.'; + + @override + String get networkUnreachableError => + 'Cannot reach the server. Please check your server URL and internet connection.'; + + @override + String get networkServerNotResponding => + 'Server is not responding. Please verify the server is running and accessible.'; + + @override + String get networkGenericError => + 'Network connection problem. Please check your internet connection.'; + + @override + String get serverError500 => + 'Server is experiencing issues. This is usually temporary.'; + + @override + String get serverErrorUnavailable => + 'Server is temporarily unavailable. Please try again in a moment.'; + + @override + String get serverErrorTimeout => + 'Server took too long to respond. Please try again.'; + + @override + String get serverErrorGeneric => + 'Server is having problems. Please try again later.'; + + @override + String get authSessionExpired => + 'Your session has expired. Please sign in again.'; + + @override + String get authForbidden => + 'You don\'t have permission to perform this action.'; + + @override + String get authInvalidToken => + 'Authentication token is invalid. Please sign in again.'; + + @override + String get authGenericError => + 'Authentication problem. Please sign in again.'; + + @override + String get validationInvalidEmail => 'Please enter a valid email address.'; + + @override + String get validationWeakPassword => + 'Password doesn\'t meet requirements. Please check and try again.'; + + @override + String get validationMissingRequired => 'Please fill in all required fields.'; + + @override + String get validationFormatError => + 'Some information is in the wrong format. Please check and try again.'; + + @override + String get validationGenericError => 'Please check your input and try again.'; + + @override + String get fileNotFound => + 'File not found. It may have been moved or deleted.'; + + @override + String get fileAccessDenied => + 'Cannot access the file. Please check permissions.'; + + @override + String get fileTooLarge => 'File is too large. Please choose a smaller file.'; + + @override + String get fileGenericError => + 'Problem with the file. Please try a different file.'; + + @override + String get permissionCameraRequired => + 'Camera permission is required. Please enable it in settings.'; + + @override + String get permissionStorageRequired => + 'Storage permission is required. Please enable it in settings.'; + + @override + String get permissionMicrophoneRequired => + 'Microphone permission is required. Please enable it in settings.'; + + @override + String get permissionGenericError => + 'Permission required. Please check app permissions in settings.'; + + @override + String get actionRetryRequest => 'Try the request again.'; + + @override + String get actionVerifyConnection => 'Verify your internet connection.'; + + @override + String get actionRetryOperation => 'Retry the operation.'; + + @override + String get actionRetryAfterDelay => 'Wait a moment then try again.'; + + @override + String get actionSignInToAccount => 'Sign in to your account.'; + + @override + String get actionSelectAnotherFile => 'Select another file.'; + + @override + String get actionOpenAppSettings => 'Open app settings to grant permissions.'; + + @override + String get actionRetryAfterPermission => 'Retry after granting permission.'; + + @override + String get actionReturnToPrevious => 'Return to previous screen.'; + @override String get display => 'Display'; @@ -937,10 +1112,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get transportMode => 'Transport mode'; - @override - String get transportModeDescription => - 'Choose how the app connects for realtime updates.'; - @override String get mode => 'Mode'; @@ -957,12 +1128,4 @@ class AppLocalizationsEn extends AppLocalizations { @override String get transportModeWsInfo => 'Lower overhead, but may fail behind strict proxies/firewalls.'; - - @override - String get websocketConnectionError => - 'Unable to establish real-time connection. Please check your network and server configuration.'; - - @override - String get websocketReconnectFailed => - 'Real-time connection failed. Streaming may not work properly.'; } diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index b132f89..e312b8e 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -11,9 +11,6 @@ class AppLocalizationsFr extends AppLocalizations { @override String get appTitle => 'Conduit'; - @override - String get initializationFailed => 'Échec de l\'initialisation'; - @override String get retry => 'Réessayer'; @@ -40,10 +37,6 @@ class AppLocalizationsFr extends AppLocalizations { String get connectionIssueSubtitle => 'Reconnectez-vous pour continuer ou déconnectez-vous pour choisir un autre serveur.'; - @override - String get stillOfflineMessage => - 'Nous ne pouvons toujours pas joindre le serveur. Vérifiez votre connexion et réessayez.'; - @override String get account => 'Compte'; @@ -89,6 +82,12 @@ class AppLocalizationsFr extends AppLocalizations { @override String get availableModels => 'Modèles disponibles'; + @override + String get modelCapabilityMultimodal => 'Multimodal'; + + @override + String get modelCapabilityReasoning => 'Raisonnement'; + @override String get noResults => 'Aucun résultat'; @@ -98,100 +97,53 @@ class AppLocalizationsFr extends AppLocalizations { @override String get errorMessage => 'Une erreur s\'est produite. Veuillez réessayer.'; - @override - String get loginButton => 'Connexion'; - - @override - String get menuItem => 'Paramètres'; - - @override - String dynamicContentWithPlaceholder(String name) { - return 'Bienvenue, $name !'; - } - - @override - String itemsCount(int count) { - String _temp0 = intl.Intl.pluralLogic( - count, - locale: localeName, - other: '$count éléments', - one: '1 élément', - zero: 'Aucun élément', - ); - return '$_temp0'; - } - @override String get closeButtonSemantic => 'Fermer'; @override String get loadingContent => 'Chargement du contenu'; + @override + String get loadingShort => 'Chargement'; + + @override + String loadingAnnouncement(String message) { + return 'Chargement : $message'; + } + + @override + String errorAnnouncement(String error) { + return 'Erreur : $error'; + } + + @override + String errorAnnouncementWithSuggestion(String error, String suggestion) { + return 'Erreur : $error. $suggestion'; + } + + @override + String successAnnouncement(String message) { + return 'Succès : $message'; + } + @override String get noItems => 'Aucun élément'; @override String get noItemsToDisplay => 'Aucun élément à afficher'; - @override - String get loadMore => 'Charger plus'; - - @override - String get workspace => 'Espace de travail'; - - @override - String get recentFiles => 'Fichiers récents'; - @override String get knowledgeBase => 'Base de connaissances'; - @override - String get noFilesYet => 'Pas encore de fichiers'; - - @override - String get uploadDocsPrompt => - 'Importez des documents à utiliser dans vos conversations avec Conduit'; - - @override - String get uploadFirstFile => 'Importer votre premier fichier'; - @override String get attachments => 'Pièces jointes'; - @override - 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'; - - @override - String get chooseSourcePhoto => 'Choisir la source'; - @override String get takePhoto => 'Prendre une photo'; - @override - String get chooseFromGallery => 'Choisir depuis vos photos'; - @override String get document => 'Document'; - @override - String get documentHint => 'Fichier PDF, Word ou texte'; - - @override - String get uploadFileTitle => 'Importer un fichier'; - - @override - String fileUploadComingSoon(String type) { - return 'Le téléversement de fichiers pour $type arrive bientôt !'; - } - - @override - String get kbCreationComingSoon => - 'La création de la base de connaissances arrive bientôt !'; - @override String get backToServerSetup => 'Retour à la configuration du serveur'; @@ -407,9 +359,6 @@ class AppLocalizationsFr extends AppLocalizations { String get onboardQuickBullet2 => 'Lancez Nouveau chat ou gérez les modèles depuis la barre'; - @override - String get addAttachment => 'Ajouter une pièce jointe'; - @override String get attachmentLabel => 'Pièce jointe'; @@ -449,6 +398,46 @@ class AppLocalizationsFr extends AppLocalizations { @override String get voiceActionStart => 'Démarrer'; + @override + String get voiceCallTitle => 'Appel vocal'; + + @override + String get voiceCallPause => 'Pause'; + + @override + String get voiceCallResume => 'Reprendre'; + + @override + String get voiceCallStop => 'Arrêter'; + + @override + String get voiceCallEnd => 'Terminer l\'appel'; + + @override + String get voiceCallReady => 'Prêt'; + + @override + String get voiceCallConnecting => 'Connexion…'; + + @override + String get voiceCallListening => 'Écoute'; + + @override + String get voiceCallPaused => 'En pause'; + + @override + String get voiceCallProcessing => 'Réflexion…'; + + @override + String get voiceCallSpeaking => 'Parle'; + + @override + String get voiceCallDisconnected => 'Déconnecté'; + + @override + String get voiceCallErrorHelp => + 'Veuillez vérifier :\n• Les autorisations du microphone sont accordées\n• La reconnaissance vocale est disponible sur votre appareil\n• Vous êtes connecté au serveur'; + @override String get messageInputLabel => 'Saisie du message'; @@ -473,6 +462,9 @@ class AppLocalizationsFr extends AppLocalizations { @override String get file => 'Fichier'; + @override + String get chooseDifferentFile => 'Choisir un autre fichier'; + @override String get photo => 'Photo'; @@ -507,17 +499,12 @@ class AppLocalizationsFr extends AppLocalizations { @override String get emptyImageData => 'Données d\'image vides'; - @override - 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'; - @override String get confirm => 'Confirmer'; + @override + String get continueAction => 'Continuer'; + @override String get cancel => 'Annuler'; @@ -525,10 +512,13 @@ class AppLocalizationsFr extends AppLocalizations { String get ok => 'OK'; @override - String get inputField => 'Champ de saisie'; + String get previousLabel => 'Précédent'; @override - String get captureDocumentOrImage => 'Capturer un document ou une image'; + String get nextLabel => 'Suivant'; + + @override + String get inputField => 'Champ de saisie'; @override String get checkConnection => 'Vérifier la connexion'; @@ -536,15 +526,31 @@ class AppLocalizationsFr extends AppLocalizations { @override String get openSettings => 'Ouvrir les réglages'; - @override - String get chooseDifferentFile => 'Choisir un autre fichier'; - @override String get goBack => 'Retour'; @override String get technicalDetails => 'Détails techniques'; + @override + String requiredFieldLabel(String label) { + return '$label *'; + } + + @override + String get requiredFieldHelper => 'Champ obligatoire'; + + @override + String get switchOnLabel => 'Activé'; + + @override + String get switchOffLabel => 'Désactivé'; + + @override + String dialogSemanticLabel(String title) { + return 'Dialogue : $title'; + } + @override String get save => 'Enregistrer'; @@ -572,26 +578,15 @@ class AppLocalizationsFr extends AppLocalizations { @override String get clear => 'Effacer'; - @override - String get searchHint => 'Rechercher...'; - @override String get searchConversations => 'Rechercher des conversations...'; @override String get create => 'Créer'; - @override - String get folderCreated => 'Dossier créé'; - @override String get failedToCreateFolder => 'Échec de la création du dossier'; - @override - String movedChatToFolder(String title, String folder) { - return '\"$title\" déplacé vers \"$folder\"'; - } - @override String get failedToMoveChat => 'Échec du déplacement du chat'; @@ -826,9 +821,6 @@ class AppLocalizationsFr extends AppLocalizations { String get maxHeadersReachedDetail => 'Maximum 10 en-têtes personnalisés. Supprimez-en pour en ajouter.'; - @override - String get editMessage => 'Modifier le message'; - @override String get noModelsAvailable => 'Aucun modèle disponible'; @@ -844,8 +836,39 @@ class AppLocalizationsFr extends AppLocalizations { String get themePalette => 'Palette de couleurs'; @override - String get themePaletteDescription => - 'Choisissez les couleurs d\'accent utilisées pour les boutons, les cartes et les bulles de discussion.'; + String get themePaletteConduitLabel => 'Conduit'; + + @override + String get themePaletteConduitDescription => + 'Thème neutre et épuré conçu pour Conduit.'; + + @override + String get themePaletteClaudeLabel => 'Claude'; + + @override + String get themePaletteClaudeDescription => + 'Palette chaleureuse inspirée du client web de Claude.'; + + @override + String get themePaletteT3ChatLabel => 'T3 Chat'; + + @override + String get themePaletteT3ChatDescription => + 'Dégradés ludiques inspirés de la marque T3 Stack.'; + + @override + String get themePaletteCatppuccinLabel => 'Catppuccin'; + + @override + String get themePaletteCatppuccinDescription => + 'Palette douce de tons pastel.'; + + @override + String get themePaletteTangerineLabel => 'Tangerine'; + + @override + String get themePaletteTangerineDescription => + 'Palette chaleureuse d\'oranges et d\'ardoises.'; @override String get themeLight => 'Clair'; @@ -891,6 +914,22 @@ class AppLocalizationsFr extends AppLocalizations { @override String get quickActionsDescription => 'Raccourcis dans le chat'; + @override + String quickActionsSelectedCount(int count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: '$count actions sélectionnées', + one: '$count action sélectionnée', + zero: 'Aucune action sélectionnée', + ); + return '$_temp0'; + } + + @override + String get autoSelectDescription => + 'Laissez l\'application choisir le meilleur modèle'; + @override String get chatSettings => 'Discussion'; @@ -901,6 +940,15 @@ class AppLocalizationsFr extends AppLocalizations { String get sendOnEnterDescription => 'Entrée envoie (clavier logiciel). Cmd/Ctrl+Entrée aussi disponible'; + @override + String get ttsEngineLabel => 'Moteur'; + + @override + String get ttsEngineDevice => 'Sur l\'appareil'; + + @override + String get ttsEngineServer => 'Serveur'; + @override String get ttsSettings => 'Synthèse vocale'; @@ -942,6 +990,140 @@ class AppLocalizationsFr extends AppLocalizations { @override String get error => 'Erreur'; + @override + String errorWithMessage(String message) { + return 'Erreur : $message'; + } + + @override + String get networkTimeoutError => + 'La connexion a expiré. Vérifiez votre connexion Internet et réessayez.'; + + @override + String get networkUnreachableError => + 'Impossible d\'atteindre le serveur. Vérifiez l\'URL du serveur et votre connexion Internet.'; + + @override + String get networkServerNotResponding => + 'Le serveur ne répond pas. Vérifiez qu\'il est en cours d\'exécution et accessible.'; + + @override + String get networkGenericError => + 'Problème de connexion réseau. Vérifiez votre connexion Internet.'; + + @override + String get serverError500 => + 'Le serveur rencontre des problèmes. Cela est généralement temporaire.'; + + @override + String get serverErrorUnavailable => + 'Le serveur est temporairement indisponible. Réessayez dans un instant.'; + + @override + String get serverErrorTimeout => + 'Le serveur a mis trop de temps à répondre. Réessayez.'; + + @override + String get serverErrorGeneric => + 'Le serveur rencontre des difficultés. Réessayez plus tard.'; + + @override + String get authSessionExpired => + 'Votre session a expiré. Veuillez vous reconnecter.'; + + @override + String get authForbidden => + 'Vous n\'avez pas l\'autorisation d\'effectuer cette action.'; + + @override + String get authInvalidToken => + 'Le jeton d\'authentification est invalide. Veuillez vous reconnecter.'; + + @override + String get authGenericError => + 'Problème d\'authentification. Veuillez vous reconnecter.'; + + @override + String get validationInvalidEmail => + 'Veuillez saisir une adresse e-mail valide.'; + + @override + String get validationWeakPassword => + 'Le mot de passe ne respecte pas les exigences. Vérifiez-le et réessayez.'; + + @override + String get validationMissingRequired => + 'Veuillez remplir tous les champs obligatoires.'; + + @override + String get validationFormatError => + 'Certaines informations sont au mauvais format. Vérifiez-les et réessayez.'; + + @override + String get validationGenericError => + 'Veuillez vérifier vos informations et réessayer.'; + + @override + String get fileNotFound => + 'Fichier introuvable. Il a peut-être été déplacé ou supprimé.'; + + @override + String get fileAccessDenied => + 'Impossible d\'accéder au fichier. Vérifiez les autorisations.'; + + @override + String get fileTooLarge => + 'Le fichier est trop volumineux. Choisissez un fichier plus petit.'; + + @override + String get fileGenericError => + 'Problème avec le fichier. Essayez un autre fichier.'; + + @override + String get permissionCameraRequired => + 'L\'autorisation de la caméra est nécessaire. Activez-la dans les paramètres.'; + + @override + String get permissionStorageRequired => + 'L\'autorisation de stockage est nécessaire. Activez-la dans les paramètres.'; + + @override + String get permissionMicrophoneRequired => + 'L\'autorisation du microphone est nécessaire. Activez-la dans les paramètres.'; + + @override + String get permissionGenericError => + 'Autorisation requise. Vérifiez les autorisations de l\'application dans les paramètres.'; + + @override + String get actionRetryRequest => 'Réessayez la requête.'; + + @override + String get actionVerifyConnection => 'Vérifiez votre connexion Internet.'; + + @override + String get actionRetryOperation => 'Réessayez l\'opération.'; + + @override + String get actionRetryAfterDelay => 'Attendez un instant puis réessayez.'; + + @override + String get actionSignInToAccount => 'Connectez-vous à votre compte.'; + + @override + String get actionSelectAnotherFile => 'Sélectionnez un autre fichier.'; + + @override + String get actionOpenAppSettings => + 'Ouvrez les paramètres de l\'application pour accorder les autorisations.'; + + @override + String get actionRetryAfterPermission => + 'Réessayez après avoir accordé l\'autorisation.'; + + @override + String get actionReturnToPrevious => 'Revenir à l\'écran précédent.'; + @override String get display => 'Affichage'; @@ -951,10 +1133,6 @@ class AppLocalizationsFr extends AppLocalizations { @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'; @@ -971,12 +1149,4 @@ class AppLocalizationsFr extends AppLocalizations { @override String get transportModeWsInfo => 'Moins de surcharge, mais peut échouer derrière des proxys/firewalls stricts.'; - - @override - String get websocketConnectionError => - 'Impossible d\'établir une connexion en temps réel. Veuillez vérifier votre réseau et la configuration du serveur.'; - - @override - String get websocketReconnectFailed => - 'Échec de la connexion en temps réel. Le streaming pourrait ne pas fonctionner correctement.'; } diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart index 896850a..0af2b1b 100644 --- a/lib/l10n/app_localizations_it.dart +++ b/lib/l10n/app_localizations_it.dart @@ -11,9 +11,6 @@ class AppLocalizationsIt extends AppLocalizations { @override String get appTitle => 'Conduit'; - @override - String get initializationFailed => 'Inizializzazione non riuscita'; - @override String get retry => 'Riprova'; @@ -39,10 +36,6 @@ class AppLocalizationsIt extends AppLocalizations { String get connectionIssueSubtitle => 'Riconnettiti per continuare oppure esci per scegliere un server diverso.'; - @override - String get stillOfflineMessage => - 'Non riusciamo ancora a raggiungere il server. Controlla la connessione e riprova.'; - @override String get account => 'Account'; @@ -88,6 +81,12 @@ class AppLocalizationsIt extends AppLocalizations { @override String get availableModels => 'Modelli disponibili'; + @override + String get modelCapabilityMultimodal => 'Multimodale'; + + @override + String get modelCapabilityReasoning => 'Ragionamento'; + @override String get noResults => 'Nessun risultato'; @@ -97,100 +96,53 @@ class AppLocalizationsIt extends AppLocalizations { @override String get errorMessage => 'Qualcosa è andato storto. Riprova.'; - @override - String get loginButton => 'Accedi'; - - @override - String get menuItem => 'Impostazioni'; - - @override - String dynamicContentWithPlaceholder(String name) { - return 'Benvenuto, $name!'; - } - - @override - String itemsCount(int count) { - String _temp0 = intl.Intl.pluralLogic( - count, - locale: localeName, - other: '$count elementi', - one: '1 elemento', - zero: 'Nessun elemento', - ); - return '$_temp0'; - } - @override String get closeButtonSemantic => 'Chiudi'; @override String get loadingContent => 'Caricamento contenuto'; + @override + String get loadingShort => 'Caricamento'; + + @override + String loadingAnnouncement(String message) { + return 'Caricamento: $message'; + } + + @override + String errorAnnouncement(String error) { + return 'Errore: $error'; + } + + @override + String errorAnnouncementWithSuggestion(String error, String suggestion) { + return 'Errore: $error. $suggestion'; + } + + @override + String successAnnouncement(String message) { + return 'Operazione riuscita: $message'; + } + @override String get noItems => 'Nessun elemento'; @override String get noItemsToDisplay => 'Nessun elemento da visualizzare'; - @override - String get loadMore => 'Carica altro'; - - @override - String get workspace => 'Spazio di lavoro'; - - @override - String get recentFiles => 'File recenti'; - @override String get knowledgeBase => 'Base di conoscenza'; - @override - String get noFilesYet => 'Ancora nessun file'; - - @override - String get uploadDocsPrompt => - 'Carica documenti da usare nelle conversazioni con Conduit'; - - @override - String get uploadFirstFile => 'Carica il tuo primo file'; - @override String get attachments => 'Allegati'; - @override - String get knowledgeBaseEmpty => 'La base di conoscenza è vuota'; - - @override - String get createCollectionsPrompt => - 'Crea raccolte di documenti correlati per un rapido riferimento'; - - @override - String get chooseSourcePhoto => 'Scegli origine'; - @override String get takePhoto => 'Scatta una foto'; - @override - String get chooseFromGallery => 'Scegli dalle foto'; - @override String get document => 'Documento'; - @override - String get documentHint => 'File PDF, Word o di testo'; - - @override - String get uploadFileTitle => 'Carica file'; - - @override - String fileUploadComingSoon(String type) { - return 'Il caricamento file per $type arriverà presto!'; - } - - @override - String get kbCreationComingSoon => - 'La creazione della base di conoscenza arriverà presto!'; - @override String get backToServerSetup => 'Torna alla configurazione del server'; @@ -399,9 +351,6 @@ class AppLocalizationsIt extends AppLocalizations { String get onboardQuickBullet2 => 'Avvia Nuova chat o gestisci i modelli dalla barra'; - @override - String get addAttachment => 'Aggiungi allegato'; - @override String get attachmentLabel => 'Allegato'; @@ -441,6 +390,46 @@ class AppLocalizationsIt extends AppLocalizations { @override String get voiceActionStart => 'Avvia'; + @override + String get voiceCallTitle => 'Chiamata vocale'; + + @override + String get voiceCallPause => 'Pausa'; + + @override + String get voiceCallResume => 'Riprendi'; + + @override + String get voiceCallStop => 'Stop'; + + @override + String get voiceCallEnd => 'Termina chiamata'; + + @override + String get voiceCallReady => 'Pronto'; + + @override + String get voiceCallConnecting => 'Connessione...'; + + @override + String get voiceCallListening => 'In ascolto'; + + @override + String get voiceCallPaused => 'In pausa'; + + @override + String get voiceCallProcessing => 'Elaborazione...'; + + @override + String get voiceCallSpeaking => 'Sta parlando'; + + @override + String get voiceCallDisconnected => 'Disconnesso'; + + @override + String get voiceCallErrorHelp => + 'Controlla:\n• Sono state concesse le autorizzazioni del microfono\n• Il riconoscimento vocale è disponibile sul dispositivo\n• Sei connesso al server'; + @override String get messageInputLabel => 'Input messaggio'; @@ -465,6 +454,9 @@ class AppLocalizationsIt extends AppLocalizations { @override String get file => 'File'; + @override + String get chooseDifferentFile => 'Scegli un altro file'; + @override String get photo => 'Foto'; @@ -499,17 +491,12 @@ class AppLocalizationsIt extends AppLocalizations { @override String get emptyImageData => 'Dati immagine vuoti'; - @override - String get featureRequiresInternet => - 'Questa funzione richiede una connessione Internet'; - - @override - String get messagesWillSendWhenOnline => - 'I messaggi verranno inviati quando tornerai online'; - @override String get confirm => 'Conferma'; + @override + String get continueAction => 'Continua'; + @override String get cancel => 'Annulla'; @@ -517,10 +504,13 @@ class AppLocalizationsIt extends AppLocalizations { String get ok => 'OK'; @override - String get inputField => 'Campo di input'; + String get previousLabel => 'Precedente'; @override - String get captureDocumentOrImage => 'Acquisisci un documento o un\'immagine'; + String get nextLabel => 'Successivo'; + + @override + String get inputField => 'Campo di input'; @override String get checkConnection => 'Controlla connessione'; @@ -528,15 +518,31 @@ class AppLocalizationsIt extends AppLocalizations { @override String get openSettings => 'Apri impostazioni'; - @override - String get chooseDifferentFile => 'Scegli un altro file'; - @override String get goBack => 'Indietro'; @override String get technicalDetails => 'Dettagli tecnici'; + @override + String requiredFieldLabel(String label) { + return '$label *'; + } + + @override + String get requiredFieldHelper => 'Campo obbligatorio'; + + @override + String get switchOnLabel => 'Attivo'; + + @override + String get switchOffLabel => 'Disattivo'; + + @override + String dialogSemanticLabel(String title) { + return 'Dialogo: $title'; + } + @override String get save => 'Salva'; @@ -564,26 +570,15 @@ class AppLocalizationsIt extends AppLocalizations { @override String get clear => 'Pulisci'; - @override - String get searchHint => 'Cerca...'; - @override String get searchConversations => 'Cerca conversazioni...'; @override String get create => 'Crea'; - @override - String get folderCreated => 'Cartella creata'; - @override String get failedToCreateFolder => 'Impossibile creare la cartella'; - @override - String movedChatToFolder(String title, String folder) { - return '\"$title\" spostata in \"$folder\"'; - } - @override String get failedToMoveChat => 'Impossibile spostare la chat'; @@ -815,9 +810,6 @@ class AppLocalizationsIt extends AppLocalizations { String get maxHeadersReachedDetail => 'Massimo 10 header personalizzati consentiti. Rimuovine alcuni per aggiungerne altri.'; - @override - String get editMessage => 'Modifica messaggio'; - @override String get noModelsAvailable => 'Nessun modello disponibile'; @@ -833,8 +825,39 @@ class AppLocalizationsIt extends AppLocalizations { String get themePalette => 'Palette di colori'; @override - String get themePaletteDescription => - 'Scegli i colori di accento usati per pulsanti, schede e bolle di chat.'; + String get themePaletteConduitLabel => 'Conduit'; + + @override + String get themePaletteConduitDescription => + 'Tema neutro e pulito progettato per Conduit.'; + + @override + String get themePaletteClaudeLabel => 'Claude'; + + @override + String get themePaletteClaudeDescription => + 'Palette calda e tattile ispirata al client web Claude.'; + + @override + String get themePaletteT3ChatLabel => 'T3 Chat'; + + @override + String get themePaletteT3ChatDescription => + 'Sfumature vivaci ispirate al brand T3 Stack.'; + + @override + String get themePaletteCatppuccinLabel => 'Catppuccin'; + + @override + String get themePaletteCatppuccinDescription => + 'Palette morbida di tonalità pastello.'; + + @override + String get themePaletteTangerineLabel => 'Tangerine'; + + @override + String get themePaletteTangerineDescription => + 'Palette calda arancione e ardesia.'; @override String get themeLight => 'Chiaro'; @@ -880,6 +903,22 @@ class AppLocalizationsIt extends AppLocalizations { @override String get quickActionsDescription => 'Scorciatoie nella chat'; + @override + String quickActionsSelectedCount(int count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: '$count azioni selezionate', + one: '$count azione selezionata', + zero: 'Nessuna azione selezionata', + ); + return '$_temp0'; + } + + @override + String get autoSelectDescription => + 'Lascia che l\'app scelga il modello migliore'; + @override String get chatSettings => 'Chat'; @@ -890,6 +929,15 @@ class AppLocalizationsIt extends AppLocalizations { String get sendOnEnterDescription => 'Invio invia (tastiera software). Cmd/Ctrl+Invio disponibile'; + @override + String get ttsEngineLabel => 'Motore'; + + @override + String get ttsEngineDevice => 'Sul dispositivo'; + + @override + String get ttsEngineServer => 'Server'; + @override String get ttsSettings => 'Sintesi vocale'; @@ -931,6 +979,135 @@ class AppLocalizationsIt extends AppLocalizations { @override String get error => 'Errore'; + @override + String errorWithMessage(String message) { + return 'Errore: $message'; + } + + @override + String get networkTimeoutError => + 'Connessione scaduta. Controlla la tua connessione Internet e riprova.'; + + @override + String get networkUnreachableError => + 'Impossibile raggiungere il server. Controlla l\'URL del server e la connessione Internet.'; + + @override + String get networkServerNotResponding => + 'Il server non risponde. Verifica che sia attivo e raggiungibile.'; + + @override + String get networkGenericError => + 'Problema di connessione di rete. Controlla la connessione Internet.'; + + @override + String get serverError500 => + 'Il server sta avendo problemi. Di solito è temporaneo.'; + + @override + String get serverErrorUnavailable => + 'Il server è temporaneamente non disponibile. Riprova tra poco.'; + + @override + String get serverErrorTimeout => + 'Il server ha impiegato troppo tempo a rispondere. Riprova.'; + + @override + String get serverErrorGeneric => + 'Il server è in difficoltà. Riprova più tardi.'; + + @override + String get authSessionExpired => 'La sessione è scaduta. Accedi di nuovo.'; + + @override + String get authForbidden => + 'Non hai l\'autorizzazione per eseguire questa azione.'; + + @override + String get authInvalidToken => + 'Il token di autenticazione non è valido. Accedi di nuovo.'; + + @override + String get authGenericError => 'Problema di autenticazione. Accedi di nuovo.'; + + @override + String get validationInvalidEmail => 'Inserisci un indirizzo email valido.'; + + @override + String get validationWeakPassword => + 'La password non soddisfa i requisiti. Controllala e riprova.'; + + @override + String get validationMissingRequired => 'Compila tutti i campi obbligatori.'; + + @override + String get validationFormatError => + 'Alcune informazioni non sono nel formato corretto. Controllale e riprova.'; + + @override + String get validationGenericError => 'Controlla i dati inseriti e riprova.'; + + @override + String get fileNotFound => + 'File non trovato. Potrebbe essere stato spostato o eliminato.'; + + @override + String get fileAccessDenied => + 'Impossibile accedere al file. Controlla i permessi.'; + + @override + String get fileTooLarge => + 'Il file è troppo grande. Scegline uno più piccolo.'; + + @override + String get fileGenericError => + 'Problema con il file. Prova con un file diverso.'; + + @override + String get permissionCameraRequired => + 'È necessario il permesso della fotocamera. Attivalo nelle impostazioni.'; + + @override + String get permissionStorageRequired => + 'È necessario il permesso di archiviazione. Attivalo nelle impostazioni.'; + + @override + String get permissionMicrophoneRequired => + 'È necessario il permesso del microfono. Attivalo nelle impostazioni.'; + + @override + String get permissionGenericError => + 'È necessaria un\'autorizzazione. Controlla i permessi dell\'app nelle impostazioni.'; + + @override + String get actionRetryRequest => 'Riprova la richiesta.'; + + @override + String get actionVerifyConnection => 'Verifica la connessione a Internet.'; + + @override + String get actionRetryOperation => 'Riprova l\'operazione.'; + + @override + String get actionRetryAfterDelay => 'Attendi un momento e riprova.'; + + @override + String get actionSignInToAccount => 'Accedi al tuo account.'; + + @override + String get actionSelectAnotherFile => 'Seleziona un altro file.'; + + @override + String get actionOpenAppSettings => + 'Apri le impostazioni dell\'app per concedere i permessi.'; + + @override + String get actionRetryAfterPermission => + 'Riprova dopo aver concesso il permesso.'; + + @override + String get actionReturnToPrevious => 'Torna alla schermata precedente.'; + @override String get display => 'Schermo'; @@ -940,10 +1117,6 @@ class AppLocalizationsIt extends AppLocalizations { @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à'; @@ -960,12 +1133,4 @@ class AppLocalizationsIt extends AppLocalizations { @override String get transportModeWsInfo => 'Minore overhead, ma può fallire dietro proxy/firewall restrittivi.'; - - @override - String get websocketConnectionError => - 'Impossibile stabilire una connessione in tempo reale. Si prega di controllare la rete e la configurazione del server.'; - - @override - String get websocketReconnectFailed => - 'Connessione in tempo reale fallita. Lo streaming potrebbe non funzionare correttamente.'; } diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb index 984c5ac..d6133d2 100644 --- a/lib/l10n/app_nl.arb +++ b/lib/l10n/app_nl.arb @@ -1,7 +1,6 @@ { "@@locale": "nl", "appTitle": "Conduit", - "initializationFailed": "Initialisatie mislukt", "retry": "Opnieuw proberen", "back": "Terug", "you": "Jij", @@ -16,10 +15,6 @@ "@connectionIssueSubtitle": { "description": "Ondertitel die beschikbare acties uitlegt wanneer de server niet bereikbaar is" }, - "stillOfflineMessage": "We kunnen de server nog steeds niet bereiken. Controleer je verbinding en probeer het opnieuw.", - "@stillOfflineMessage": { - "description": "Statusbericht na een herhaalde poging wanneer de verbinding niet is hersteld" - }, "account": "Account", "supportConduit": "Ondersteun Conduit", "supportConduitSubtitle": "Houd Conduit onafhankelijk door doorlopende ontwikkeling te financieren.", @@ -37,32 +32,14 @@ "noResults": "Geen resultaten", "searchModels": "Modellen zoeken...", "errorMessage": "Er is iets misgegaan. Probeer het opnieuw.", - "loginButton": "Inloggen", - "menuItem": "Instellingen", - "dynamicContentWithPlaceholder": "Welkom, {name}!", - "itemsCount": "{count, plural, =0{Geen items} one{1 item} other{{count} items}}", "closeButtonSemantic": "Sluiten", "loadingContent": "Inhoud laden", "noItems": "Geen items", "noItemsToDisplay": "Geen items om weer te geven", - "loadMore": "Meer laden", - "workspace": "Werkruimte", - "recentFiles": "Recente bestanden", "knowledgeBase": "Kennisbank", - "noFilesYet": "Nog geen bestanden", - "uploadDocsPrompt": "Upload documenten om te gebruiken in je gesprekken met Conduit", - "uploadFirstFile": "Upload je eerste bestand", "attachments": "Bijlagen", - "knowledgeBaseEmpty": "Kennisbank is leeg", - "createCollectionsPrompt": "Maak verzamelingen van gerelateerde documenten voor eenvoudige verwijzing", - "chooseSourcePhoto": "Kies je bron", "takePhoto": "Foto maken", - "chooseFromGallery": "Kies uit je foto's", "document": "Document", - "documentHint": "PDF, Word of tekstbestand", - "uploadFileTitle": "Bestand uploaden", - "fileUploadComingSoon": "Bestand uploaden voor {type} komt binnenkort!", - "kbCreationComingSoon": "Kennisbank aanmaken komt binnenkort!", "backToServerSetup": "Terug naar serverinstelling", "connectedToServer": "Verbonden met server", "signIn": "Inloggen", @@ -125,7 +102,6 @@ "onboardQuickSubtitle": "Open het menu om te schakelen tussen Chats, Werkruimte en Profiel.", "onboardQuickBullet1": "Tik op het menu voor toegang tot Chats, Werkruimte, Profiel", "onboardQuickBullet2": "Start Nieuwe chat of beheer modellen vanuit de bovenbalk", - "addAttachment": "Bijlage toevoegen", "attachmentLabel": "Bijlage", "tools": "Hulpmiddelen", "voiceInput": "Spraakinvoer", @@ -157,16 +133,12 @@ "failedToDecodeImage": "Kan afbeelding niet decoderen", "invalidImageFormat": "Ongeldig afbeeldingsformaat", "emptyImageData": "Lege afbeeldingsgegevens", - "featureRequiresInternet": "Deze functie vereist een internetverbinding", - "messagesWillSendWhenOnline": "Berichten worden verzonden wanneer je weer online bent", "confirm": "Bevestigen", "cancel": "Annuleren", "ok": "OK", "inputField": "Invoerveld", - "captureDocumentOrImage": "Document of afbeelding vastleggen", "checkConnection": "Verbinding controleren", "openSettings": "Instellingen openen", - "chooseDifferentFile": "Ander bestand kiezen", "goBack": "Terug", "technicalDetails": "Technische details", "save": "Opslaan", @@ -178,18 +150,9 @@ "newChat": "Nieuwe chat", "more": "Meer", "clear": "Wissen", - "searchHint": "Zoeken...", "searchConversations": "Gesprekken zoeken...", "create": "Aanmaken", - "folderCreated": "Map aangemaakt", "failedToCreateFolder": "Kan map niet aanmaken", - "movedChatToFolder": "'{title}' verplaatst naar '{folder}'", - "@movedChatToFolder": { - "placeholders": { - "title": {"type": "String"}, - "folder": {"type": "String"} - } - }, "failedToMoveChat": "Kan chat niet verplaatsen", "failedToLoadChats": "Kan chats niet laden", "failedToUpdatePin": "Kan vastpinning niet bijwerken", @@ -219,13 +182,17 @@ "deleteMessagesMessage": "{count} berichten verwijderen?", "@deleteMessagesMessage": { "placeholders": { - "count": {"type": "int"} + "count": { + "type": "int" + } } }, "routeNotFound": "Route niet gevonden: {routeName}", "@routeNotFound": { "placeholders": { - "routeName": {"type": "String"} + "routeName": { + "type": "String" + } } }, "deleteChatTitle": "Chat verwijderen", @@ -271,29 +238,55 @@ "headerNameTooLong": "Header-naam te lang (max 64 tekens)", "headerNameInvalidChars": "Ongeldige header-naam. Gebruik alleen letters, cijfers en deze symbolen: !#$&-^_`|~", "headerNameReserved": "Kan gereserveerde header '{key}' niet overschrijven", - "@headerNameReserved": {"placeholders": {"key": {"type": "String"}}}, + "@headerNameReserved": { + "placeholders": { + "key": { + "type": "String" + } + } + }, "headerValueEmpty": "Header-waarde mag niet leeg zijn", "headerValueTooLong": "Header-waarde te lang (max 1024 tekens)", "headerValueInvalidChars": "Header-waarde bevat ongeldige tekens. Gebruik alleen afdrukbare ASCII.", "headerValueUnsafe": "Header-waarde lijkt mogelijk onveilige inhoud te bevatten", "headerAlreadyExists": "Header '{key}' bestaat al. Verwijder deze eerst om bij te werken.", - "@headerAlreadyExists": {"placeholders": {"key": {"type": "String"}}}, + "@headerAlreadyExists": { + "placeholders": { + "key": { + "type": "String" + } + } + }, "maxHeadersReachedDetail": "Maximaal 10 aangepaste headers toegestaan. Verwijder er enkele om meer toe te voegen.", - "editMessage": "Bericht bewerken", "noModelsAvailable": "Geen modellen beschikbaar", "followingSystem": "Volgt systeem: {theme}", - "@followingSystem": {"placeholders": {"theme": {"type": "String"}}}, + "@followingSystem": { + "placeholders": { + "theme": { + "type": "String" + } + } + }, "themeDark": "Donker", "themePalette": "Accentpalet", - "@themePalette": {"description": "Titel voor het selecteren van het app-kleurenpalet."}, - "themePaletteDescription": "Kies de accentkleuren voor knoppen, kaarten en chatballonnen.", - "@themePaletteDescription": {"description": "Hulptekst die de paletselectie uitlegt."}, + "@themePalette": { + "description": "Titel voor het selecteren van het app-kleurenpalet." + }, "themeLight": "Licht", "currentlyUsingDarkTheme": "Momenteel donker thema in gebruik", "currentlyUsingLightTheme": "Momenteel licht thema in gebruik", "aboutConduit": "Over Conduit", "versionLabel": "Versie: {version} ({build})", - "@versionLabel": {"placeholders": {"version": {"type": "String"}, "build": {"type": "String"}}}, + "@versionLabel": { + "placeholders": { + "version": { + "type": "String" + }, + "build": { + "type": "String" + } + } + }, "githubRepository": "GitHub-repository", "unableToLoadAppInfo": "Kan app-info niet laden", "thinking": "Denken...", @@ -301,7 +294,12 @@ "thoughtForDuration": "Dacht {duration}", "@thoughtForDuration": { "description": "Toont hoe lang de assistent dacht voordat hij antwoordde.", - "placeholders": {"duration": {"type": "String", "example": "3s"}} + "placeholders": { + "duration": { + "type": "String", + "example": "3s" + } + } }, "appCustomization": "Aanpassing", "appCustomizationSubtitle": "Thema, taal, stem en quickpills", @@ -325,12 +323,413 @@ "display": "Weergave", "realtime": "Realtime", "transportMode": "Transportmodus", - "transportModeDescription": "Kies hoe de app verbindt voor realtime updates.", "mode": "Modus", "transportModePolling": "Polling-fallback", "transportModeWs": "Alleen WebSocket", "transportModePollingInfo": "Valt terug op HTTP-polling wanneer WebSocket geblokkeerd is. Upgrade naar WebSocket zodra dat kan.", "transportModeWsInfo": "Lagere overhead, maar kan mislukken achter strikte proxies/firewalls.", - "websocketConnectionError": "Kan geen realtime verbinding maken. Controleer uw netwerk en serverconfiguratie.", - "websocketReconnectFailed": "Realtime verbinding mislukt. Streaming werkt mogelijk niet goed." + "quickActionsSelectedCount": "{count, plural, =0{Geen acties geselecteerd} one{{count} actie geselecteerd} other{{count} acties geselecteerd}}", + "@quickActionsSelectedCount": { + "description": "Subtitle indicating how many quick actions are selected.", + "placeholders": { + "count": { + "type": "int", + "example": "2" + } + } + }, + "autoSelectDescription": "Laat de app het beste model kiezen", + "@autoSelectDescription": { + "description": "Explains what the auto-select model setting does." + }, + "ttsEngineLabel": "Engine", + "@ttsEngineLabel": { + "description": "Label for selecting the text-to-speech engine." + }, + "ttsEngineDevice": "Op het apparaat", + "@ttsEngineDevice": { + "description": "Chip label for using on-device text-to-speech." + }, + "ttsEngineServer": "Server", + "@ttsEngineServer": { + "description": "Chip label for using server-side text-to-speech." + }, + "modelCapabilityMultimodal": "Multimodaal", + "@modelCapabilityMultimodal": { + "description": "Capability chip label for models that support multimodal input." + }, + "modelCapabilityReasoning": "Redeneren", + "@modelCapabilityReasoning": { + "description": "Capability chip label for models that support reasoning features." + }, + "voiceCallTitle": "Spraakoproep", + "@voiceCallTitle": { + "description": "Title displayed on the voice call screen." + }, + "voiceCallPause": "Pauze", + "@voiceCallPause": { + "description": "Button label to pause a voice call." + }, + "voiceCallResume": "Hervatten", + "@voiceCallResume": { + "description": "Button label to resume a paused voice call." + }, + "voiceCallStop": "Stoppen", + "@voiceCallStop": { + "description": "Button label to stop the active voice call." + }, + "voiceCallEnd": "Oproep beëindigen", + "@voiceCallEnd": { + "description": "Button label to end the voice call session." + }, + "chooseDifferentFile": "Ander bestand kiezen", + "@chooseDifferentFile": { + "description": "Action label prompting the user to pick another file." + }, + "errorWithMessage": "Fout: {message}", + "@errorWithMessage": { + "description": "Error label with appended message text.", + "placeholders": { + "message": { + "type": "String", + "example": "Network timeout" + } + } + }, + "networkTimeoutError": "De verbinding is verlopen. Controleer je internetverbinding en probeer het opnieuw.", + "@networkTimeoutError": { + "description": "User-facing message when a network request times out." + }, + "networkUnreachableError": "Server niet bereikbaar. Controleer de server-URL en je internetverbinding.", + "@networkUnreachableError": { + "description": "User-facing message when the server cannot be reached." + }, + "networkServerNotResponding": "Server reageert niet. Controleer of de server actief en bereikbaar is.", + "@networkServerNotResponding": { + "description": "User-facing message when the server does not respond to a request." + }, + "networkGenericError": "Netwerkprobleem. Controleer je internetverbinding.", + "@networkGenericError": { + "description": "Fallback message for generic network errors." + }, + "serverError500": "De server heeft problemen. Dit is meestal tijdelijk.", + "@serverError500": { + "description": "Message when a 500 error is encountered." + }, + "serverErrorUnavailable": "Server tijdelijk niet beschikbaar. Probeer het zo weer.", + "@serverErrorUnavailable": { + "description": "Message when a 502/503 error is encountered." + }, + "serverErrorTimeout": "De server deed te lang over een antwoord. Probeer het opnieuw.", + "@serverErrorTimeout": { + "description": "Message when the server times out." + }, + "serverErrorGeneric": "De server ondervindt problemen. Probeer het later opnieuw.", + "@serverErrorGeneric": { + "description": "Fallback server error message." + }, + "authSessionExpired": "Je sessie is verlopen. Log opnieuw in.", + "@authSessionExpired": { + "description": "Message when an authentication session expires." + }, + "authForbidden": "Je hebt geen toestemming voor deze actie.", + "@authForbidden": { + "description": "Message when the user lacks required permissions." + }, + "authInvalidToken": "Authenticatietoken is ongeldig. Log opnieuw in.", + "@authInvalidToken": { + "description": "Message when the authentication token is invalid." + }, + "authGenericError": "Authenticatieprobleem. Log opnieuw in.", + "@authGenericError": { + "description": "Fallback authentication error message." + }, + "validationInvalidEmail": "Voer een geldig e-mailadres in.", + "@validationInvalidEmail": { + "description": "Validation message for invalid email input." + }, + "validationWeakPassword": "Het wachtwoord voldoet niet aan de vereisten. Controleer het en probeer opnieuw.", + "@validationWeakPassword": { + "description": "Validation message for weak passwords." + }, + "validationMissingRequired": "Vul alle verplichte velden in.", + "@validationMissingRequired": { + "description": "Validation message when required fields are missing." + }, + "validationFormatError": "Sommige informatie heeft het verkeerde formaat. Controleer en probeer opnieuw.", + "@validationFormatError": { + "description": "Validation message for generic formatting issues." + }, + "validationGenericError": "Controleer je invoer en probeer opnieuw.", + "@validationGenericError": { + "description": "Fallback validation message." + }, + "fileNotFound": "Bestand niet gevonden. Het is mogelijk verplaatst of verwijderd.", + "@fileNotFound": { + "description": "Message when a file cannot be located." + }, + "fileAccessDenied": "Kan geen toegang krijgen tot het bestand. Controleer de rechten.", + "@fileAccessDenied": { + "description": "Message when file access is denied." + }, + "fileTooLarge": "Het bestand is te groot. Kies een kleiner bestand.", + "@fileTooLarge": { + "description": "Message when a file exceeds size limits." + }, + "fileGenericError": "Probleem met het bestand. Probeer een ander bestand.", + "@fileGenericError": { + "description": "Fallback file error message." + }, + "permissionCameraRequired": "Cameratoegang is vereist. Schakel dit in via de instellingen.", + "@permissionCameraRequired": { + "description": "Message when camera permission is missing." + }, + "permissionStorageRequired": "Opslagtoegang is vereist. Schakel dit in via de instellingen.", + "@permissionStorageRequired": { + "description": "Message when storage permission is missing." + }, + "permissionMicrophoneRequired": "Microfoontoegang is vereist. Schakel dit in via de instellingen.", + "@permissionMicrophoneRequired": { + "description": "Message when microphone permission is missing." + }, + "permissionGenericError": "Toestemming vereist. Controleer de app-machtigingen in de instellingen.", + "@permissionGenericError": { + "description": "Fallback permission error message." + }, + "actionRetryRequest": "Probeer de aanvraag opnieuw.", + "@actionRetryRequest": { + "description": "Description for retrying a failed request." + }, + "actionVerifyConnection": "Controleer je internetverbinding.", + "@actionVerifyConnection": { + "description": "Description for checking internet connectivity." + }, + "actionRetryOperation": "Probeer de actie opnieuw.", + "@actionRetryOperation": { + "description": "Description for retrying the same operation." + }, + "actionRetryAfterDelay": "Wacht even en probeer het opnieuw.", + "@actionRetryAfterDelay": { + "description": "Description suggesting a short delay before retrying." + }, + "actionSignInToAccount": "Log in op je account.", + "@actionSignInToAccount": { + "description": "Description for signing back into the app." + }, + "actionSelectAnotherFile": "Kies een ander bestand.", + "@actionSelectAnotherFile": { + "description": "Description for choosing a different file." + }, + "actionOpenAppSettings": "Open de app-instellingen om toestemming te geven.", + "@actionOpenAppSettings": { + "description": "Description for opening system or app settings." + }, + "actionRetryAfterPermission": "Probeer opnieuw nadat je toestemming hebt gegeven.", + "@actionRetryAfterPermission": { + "description": "Description for retrying once permissions are granted." + }, + "actionReturnToPrevious": "Keer terug naar het vorige scherm.", + "@actionReturnToPrevious": { + "description": "Description for navigating back to the prior screen." + }, + "continueAction": "Doorgaan", + "@continueAction": { + "description": "Button label to continue an action or flow." + }, + "loadingShort": "Laden", + "@loadingShort": { + "description": "Short loading label used for accessibility." + }, + "loadingAnnouncement": "Laden: {message}", + "@loadingAnnouncement": { + "description": "Screen reader announcement when loading a resource.", + "placeholders": { + "message": { + "type": "String", + "example": "Messages" + } + } + }, + "errorAnnouncement": "Fout: {error}", + "@errorAnnouncement": { + "description": "Screen reader announcement for an error.", + "placeholders": { + "error": { + "type": "String", + "example": "Network timeout" + } + } + }, + "errorAnnouncementWithSuggestion": "Fout: {error}. {suggestion}", + "@errorAnnouncementWithSuggestion": { + "description": "Screen reader announcement for an error with a follow-up suggestion.", + "placeholders": { + "error": { + "type": "String", + "example": "Network timeout" + }, + "suggestion": { + "type": "String", + "example": "Please try again later." + } + } + }, + "successAnnouncement": "Succes: {message}", + "@successAnnouncement": { + "description": "Screen reader announcement for successful actions.", + "placeholders": { + "message": { + "type": "String", + "example": "Profile updated" + } + } + }, + "requiredFieldLabel": "{label} *", + "@requiredFieldLabel": { + "description": "Label text indicating a required field.", + "placeholders": { + "label": { + "type": "String", + "example": "Email" + } + } + }, + "requiredFieldHelper": "Verplicht veld", + "@requiredFieldHelper": { + "description": "Helper text indicating that the field is required." + }, + "switchOnLabel": "Aan", + "@switchOnLabel": { + "description": "Semantic label when a switch is enabled." + }, + "switchOffLabel": "Uit", + "@switchOffLabel": { + "description": "Semantic label when a switch is disabled." + }, + "dialogSemanticLabel": "Dialoog: {title}", + "@dialogSemanticLabel": { + "description": "Semantic label describing the dialog title.", + "placeholders": { + "title": { + "type": "String", + "example": "Settings" + } + } + }, + "previousLabel": "Vorige", + "@previousLabel": { + "description": "Label for navigating to the previous item." + }, + "nextLabel": "Volgende", + "@nextLabel": { + "description": "Label for navigating to the next item." + }, + "themePaletteConduitLabel": "Conduit", + "@themePaletteConduitLabel": { + "description": "Palette name for the default Conduit theme." + }, + "themePaletteConduitDescription": "Strak neutraal thema ontworpen voor Conduit.", + "@themePaletteConduitDescription": { + "description": "Description of the Conduit palette." + }, + "themePaletteClaudeLabel": "Claude", + "@themePaletteClaudeLabel": { + "description": "Palette name inspired by the Claude web client." + }, + "themePaletteClaudeDescription": "Warm, tactiel palet geïnspireerd op de Claude-webclient.", + "@themePaletteClaudeDescription": { + "description": "Description of the Claude palette." + }, + "themePaletteT3ChatLabel": "T3 Chat", + "@themePaletteT3ChatLabel": { + "description": "Palette name inspired by the T3 Stack brand." + }, + "themePaletteT3ChatDescription": "Speelse verlopen geïnspireerd op het T3 Stack-merk.", + "@themePaletteT3ChatDescription": { + "description": "Description of the T3 Chat palette." + }, + "themePaletteCatppuccinLabel": "Catppuccin", + "@themePaletteCatppuccinLabel": { + "description": "Palette name for Catppuccin colors." + }, + "themePaletteCatppuccinDescription": "Zacht pastelkleurig palet.", + "@themePaletteCatppuccinDescription": { + "description": "Description of the Catppuccin palette." + }, + "themePaletteTangerineLabel": "Tangerine", + "@themePaletteTangerineLabel": { + "description": "Palette name for Tangerine colors." + }, + "themePaletteTangerineDescription": "Warm oranje-en-leisteenpalet.", + "@themePaletteTangerineDescription": { + "description": "Description of the Tangerine palette." + }, + "@onboardStartTitle": { + "description": "Onboarding card: start chatting title.", + "placeholders": { + "username": { + "type": "String", + "example": "Alex" + } + } + }, + "@notAnImageFile": { + "description": "Error when a referenced file is not an image.", + "placeholders": { + "fileName": { + "type": "String", + "example": "image.txt" + } + } + }, + "@failedToLoadImage": { + "description": "Error including the underlying reason when image loading fails.", + "placeholders": { + "error": { + "type": "String", + "example": "Network error" + } + } + }, + "@ttsVoicesForLanguage": { + "description": "Section header for voices matching the app language", + "placeholders": { + "language": { + "type": "String", + "example": "EN" + } + } + }, + "voiceCallReady": "Gereed", + "@voiceCallReady": { + "description": "Status label shown when the voice call is ready to start." + }, + "voiceCallConnecting": "Verbinding maken...", + "@voiceCallConnecting": { + "description": "Status label shown while the voice call is connecting." + }, + "voiceCallListening": "Luistert", + "@voiceCallListening": { + "description": "Status label shown while the call is listening for input." + }, + "voiceCallPaused": "Gepauzeerd", + "@voiceCallPaused": { + "description": "Status label shown when the call is paused." + }, + "voiceCallProcessing": "Denkt na...", + "@voiceCallProcessing": { + "description": "Status label shown while the call processes a response." + }, + "voiceCallSpeaking": "Spreekt", + "@voiceCallSpeaking": { + "description": "Status label shown while the assistant is speaking." + }, + "voiceCallDisconnected": "Verbinding verbroken", + "@voiceCallDisconnected": { + "description": "Status label shown when the voice call has ended or disconnected." + }, + "voiceCallErrorHelp": "Controleer het volgende:\n• Microfoonmachtigingen zijn toegestaan\n• Spraakherkenning is beschikbaar op je apparaat\n• Je bent verbonden met de server", + "@voiceCallErrorHelp": { + "description": "Guidance shown when the voice call encounters an error." + } } diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb index 68f1f41..d438815 100644 --- a/lib/l10n/app_ru.arb +++ b/lib/l10n/app_ru.arb @@ -1,7 +1,6 @@ { "@@locale": "ru", "appTitle": "Conduit", - "initializationFailed": "Ошибка инициализации", "retry": "Повторить", "back": "Назад", "you": "Вы", @@ -16,10 +15,6 @@ "@connectionIssueSubtitle": { "description": "Подзаголовок с доступными действиями при недоступности сервера" }, - "stillOfflineMessage": "Мы все еще не можем подключиться к серверу. Проверьте соединение и повторите попытку.", - "@stillOfflineMessage": { - "description": "Сообщение после повторной попытки, когда соединение не восстановлено" - }, "account": "Аккаунт", "supportConduit": "Поддержать Conduit", "supportConduitSubtitle": "Сохраните независимость Conduit, финансируя разработку.", @@ -37,32 +32,14 @@ "noResults": "Нет результатов", "searchModels": "Поиск моделей...", "errorMessage": "Что-то пошло не так. Пожалуйста, попробуйте еще раз.", - "loginButton": "Войти", - "menuItem": "Настройки", - "dynamicContentWithPlaceholder": "Добро пожаловать, {name}!", - "itemsCount": "{count, plural, =0{Нет элементов} one{{count} элемент} few{{count} элемента} other{{count} элементов}}", "closeButtonSemantic": "Закрыть", "loadingContent": "Загрузка содержимого", "noItems": "Нет элементов", "noItemsToDisplay": "Нет элементов для отображения", - "loadMore": "Загрузить еще", - "workspace": "Рабочее пространство", - "recentFiles": "Недавние файлы", "knowledgeBase": "База знаний", - "noFilesYet": "Пока нет файлов", - "uploadDocsPrompt": "Загрузите документы для использования в разговорах с Conduit", - "uploadFirstFile": "Загрузить первый файл", "attachments": "Вложения", - "knowledgeBaseEmpty": "База знаний пуста", - "createCollectionsPrompt": "Создайте коллекции связанных документов для удобной ссылки", - "chooseSourcePhoto": "Выберите источник", "takePhoto": "Сделать фото", - "chooseFromGallery": "Выбрать из галереи", "document": "Документ", - "documentHint": "PDF, Word или текстовый файл", - "uploadFileTitle": "Загрузить файл", - "fileUploadComingSoon": "Загрузка файлов для {type} скоро появится!", - "kbCreationComingSoon": "Создание базы знаний скоро появится!", "backToServerSetup": "Вернуться к настройке сервера", "connectedToServer": "Подключено к серверу", "signIn": "Войти", @@ -125,7 +102,6 @@ "onboardQuickSubtitle": "Откройте меню для переключения между чатами, рабочим пространством и профилем.", "onboardQuickBullet1": "Нажмите на меню для доступа к чатам, рабочему пространству, профилю", "onboardQuickBullet2": "Начните новый чат или управляйте моделями из верхней панели", - "addAttachment": "Добавить вложение", "attachmentLabel": "Вложение", "tools": "Инструменты", "voiceInput": "Голосовой ввод", @@ -157,16 +133,12 @@ "failedToDecodeImage": "Не удалось декодировать изображение", "invalidImageFormat": "Неверный формат изображения", "emptyImageData": "Пустые данные изображения", - "featureRequiresInternet": "Эта функция требует подключения к интернету", - "messagesWillSendWhenOnline": "Сообщения будут отправлены, когда вы снова будете онлайн", "confirm": "Подтвердить", "cancel": "Отмена", "ok": "OK", "inputField": "Поле ввода", - "captureDocumentOrImage": "Сфотографировать документ или изображение", "checkConnection": "Проверить соединение", "openSettings": "Открыть настройки", - "chooseDifferentFile": "Выбрать другой файл", "goBack": "Назад", "technicalDetails": "Технические детали", "save": "Сохранить", @@ -178,18 +150,9 @@ "newChat": "Новый чат", "more": "Еще", "clear": "Очистить", - "searchHint": "Поиск...", "searchConversations": "Поиск разговоров...", "create": "Создать", - "folderCreated": "Папка создана", "failedToCreateFolder": "Не удалось создать папку", - "movedChatToFolder": "Перемещено «{title}» в «{folder}»", - "@movedChatToFolder": { - "placeholders": { - "title": {"type": "String"}, - "folder": {"type": "String"} - } - }, "failedToMoveChat": "Не удалось переместить чат", "failedToLoadChats": "Не удалось загрузить чаты", "failedToUpdatePin": "Не удалось обновить закрепление", @@ -219,13 +182,17 @@ "deleteMessagesMessage": "Удалить {count, plural, one{{count} сообщение} few{{count} сообщения} other{{count} сообщений}}?", "@deleteMessagesMessage": { "placeholders": { - "count": {"type": "int"} + "count": { + "type": "int" + } } }, "routeNotFound": "Маршрут не найден: {routeName}", "@routeNotFound": { "placeholders": { - "routeName": {"type": "String"} + "routeName": { + "type": "String" + } } }, "deleteChatTitle": "Удалить чат", @@ -271,29 +238,55 @@ "headerNameTooLong": "Имя заголовка слишком длинное (максимум 64 символа)", "headerNameInvalidChars": "Недопустимое имя заголовка. Используйте только буквы, цифры и эти символы: !#$&-^_`|~", "headerNameReserved": "Невозможно переопределить зарезервированный заголовок «{key}»", - "@headerNameReserved": {"placeholders": {"key": {"type": "String"}}}, + "@headerNameReserved": { + "placeholders": { + "key": { + "type": "String" + } + } + }, "headerValueEmpty": "Значение заголовка не может быть пустым", "headerValueTooLong": "Значение заголовка слишком длинное (максимум 1024 символа)", "headerValueInvalidChars": "Значение заголовка содержит недопустимые символы. Используйте только печатаемые ASCII.", "headerValueUnsafe": "Значение заголовка содержит потенциально небезопасное содержимое", "headerAlreadyExists": "Заголовок «{key}» уже существует. Сначала удалите его для обновления.", - "@headerAlreadyExists": {"placeholders": {"key": {"type": "String"}}}, + "@headerAlreadyExists": { + "placeholders": { + "key": { + "type": "String" + } + } + }, "maxHeadersReachedDetail": "Разрешено максимум 10 пользовательских заголовков. Удалите некоторые, чтобы добавить больше.", - "editMessage": "Редактировать сообщение", "noModelsAvailable": "Нет доступных моделей", "followingSystem": "Следует за системой: {theme}", - "@followingSystem": {"placeholders": {"theme": {"type": "String"}}}, + "@followingSystem": { + "placeholders": { + "theme": { + "type": "String" + } + } + }, "themeDark": "Темная", "themePalette": "Цветовая палитра", - "@themePalette": {"description": "Заголовок для выбора цветовой палитры приложения."}, - "themePaletteDescription": "Выберите акцентные цвета для кнопок, карточек и пузырьков чата.", - "@themePaletteDescription": {"description": "Вспомогательный текст, объясняющий выбор палитры."}, + "@themePalette": { + "description": "Заголовок для выбора цветовой палитры приложения." + }, "themeLight": "Светлая", "currentlyUsingDarkTheme": "Используется темная тема", "currentlyUsingLightTheme": "Используется светлая тема", "aboutConduit": "О Conduit", "versionLabel": "Версия: {version} ({build})", - "@versionLabel": {"placeholders": {"version": {"type": "String"}, "build": {"type": "String"}}}, + "@versionLabel": { + "placeholders": { + "version": { + "type": "String" + }, + "build": { + "type": "String" + } + } + }, "githubRepository": "Репозиторий GitHub", "unableToLoadAppInfo": "Не удалось загрузить информацию о приложении", "thinking": "Думаю...", @@ -301,7 +294,12 @@ "thoughtForDuration": "Думал {duration}", "@thoughtForDuration": { "description": "Показывает, сколько времени ассистент думал перед ответом.", - "placeholders": {"duration": {"type": "String", "example": "3с"}} + "placeholders": { + "duration": { + "type": "String", + "example": "3с" + } + } }, "appCustomization": "Настройка", "appCustomizationSubtitle": "Тема, язык, голос и quickpills", @@ -325,12 +323,413 @@ "display": "Отображение", "realtime": "Реальное время", "transportMode": "Режим транспорта", - "transportModeDescription": "Выберите, как приложение подключается для обновлений в реальном времени.", "mode": "Режим", "transportModePolling": "Опрос (резерв)", "transportModeWs": "Только WebSocket", "transportModePollingInfo": "Переходит на HTTP-опрос, если WebSocket заблокирован. Возвращается к WebSocket, когда это возможно.", "transportModeWsInfo": "Меньше накладных расходов, но может не работать за строгими прокси/брандмауэрами.", - "websocketConnectionError": "Не удалось установить соединение в реальном времени. Пожалуйста, проверьте сеть и конфигурацию сервера.", - "websocketReconnectFailed": "Сбой соединения в реальном времени. Потоковая передача может работать неправильно." + "quickActionsSelectedCount": "{count, plural, =0{Действия не выбраны} one{{count} действие выбрано} few{{count} действия выбраны} many{{count} действий выбрано} other{{count} действий выбрано}}", + "@quickActionsSelectedCount": { + "description": "Subtitle indicating how many quick actions are selected.", + "placeholders": { + "count": { + "type": "int", + "example": "2" + } + } + }, + "autoSelectDescription": "Позвольте приложению выбрать лучшую модель", + "@autoSelectDescription": { + "description": "Explains what the auto-select model setting does." + }, + "ttsEngineLabel": "Движок", + "@ttsEngineLabel": { + "description": "Label for selecting the text-to-speech engine." + }, + "ttsEngineDevice": "На устройстве", + "@ttsEngineDevice": { + "description": "Chip label for using on-device text-to-speech." + }, + "ttsEngineServer": "Сервер", + "@ttsEngineServer": { + "description": "Chip label for using server-side text-to-speech." + }, + "modelCapabilityMultimodal": "Мультимодальность", + "@modelCapabilityMultimodal": { + "description": "Capability chip label for models that support multimodal input." + }, + "modelCapabilityReasoning": "Рассуждения", + "@modelCapabilityReasoning": { + "description": "Capability chip label for models that support reasoning features." + }, + "voiceCallTitle": "Голосовой звонок", + "@voiceCallTitle": { + "description": "Title displayed on the voice call screen." + }, + "voiceCallPause": "Пауза", + "@voiceCallPause": { + "description": "Button label to pause a voice call." + }, + "voiceCallResume": "Продолжить", + "@voiceCallResume": { + "description": "Button label to resume a paused voice call." + }, + "voiceCallStop": "Остановить", + "@voiceCallStop": { + "description": "Button label to stop the active voice call." + }, + "voiceCallEnd": "Завершить звонок", + "@voiceCallEnd": { + "description": "Button label to end the voice call session." + }, + "chooseDifferentFile": "Выбрать другой файл", + "@chooseDifferentFile": { + "description": "Action label prompting the user to pick another file." + }, + "errorWithMessage": "Ошибка: {message}", + "@errorWithMessage": { + "description": "Error label with appended message text.", + "placeholders": { + "message": { + "type": "String", + "example": "Network timeout" + } + } + }, + "networkTimeoutError": "Время соединения истекло. Проверьте подключение к интернету и повторите попытку.", + "@networkTimeoutError": { + "description": "User-facing message when a network request times out." + }, + "networkUnreachableError": "Не удаётся связаться с сервером. Проверьте URL сервера и подключение к интернету.", + "@networkUnreachableError": { + "description": "User-facing message when the server cannot be reached." + }, + "networkServerNotResponding": "Сервер не отвечает. Убедитесь, что он запущен и доступен.", + "@networkServerNotResponding": { + "description": "User-facing message when the server does not respond to a request." + }, + "networkGenericError": "Проблема с подключением. Проверьте подключение к интернету.", + "@networkGenericError": { + "description": "Fallback message for generic network errors." + }, + "serverError500": "На сервере возникли проблемы. Обычно это временно.", + "@serverError500": { + "description": "Message when a 500 error is encountered." + }, + "serverErrorUnavailable": "Сервер временно недоступен. Повторите попытку чуть позже.", + "@serverErrorUnavailable": { + "description": "Message when a 502/503 error is encountered." + }, + "serverErrorTimeout": "Сервер слишком долго отвечает. Повторите попытку.", + "@serverErrorTimeout": { + "description": "Message when the server times out." + }, + "serverErrorGeneric": "Сервер испытывает трудности. Повторите попытку позже.", + "@serverErrorGeneric": { + "description": "Fallback server error message." + }, + "authSessionExpired": "Сессия истекла. Выполните вход снова.", + "@authSessionExpired": { + "description": "Message when an authentication session expires." + }, + "authForbidden": "У вас нет прав для выполнения этого действия.", + "@authForbidden": { + "description": "Message when the user lacks required permissions." + }, + "authInvalidToken": "Токен аутентификации недействителен. Выполните вход снова.", + "@authInvalidToken": { + "description": "Message when the authentication token is invalid." + }, + "authGenericError": "Проблема с аутентификацией. Выполните вход снова.", + "@authGenericError": { + "description": "Fallback authentication error message." + }, + "validationInvalidEmail": "Введите корректный адрес электронной почты.", + "@validationInvalidEmail": { + "description": "Validation message for invalid email input." + }, + "validationWeakPassword": "Пароль не соответствует требованиям. Проверьте его и повторите попытку.", + "@validationWeakPassword": { + "description": "Validation message for weak passwords." + }, + "validationMissingRequired": "Заполните все обязательные поля.", + "@validationMissingRequired": { + "description": "Validation message when required fields are missing." + }, + "validationFormatError": "Некоторые данные указаны в неверном формате. Проверьте и повторите попытку.", + "@validationFormatError": { + "description": "Validation message for generic formatting issues." + }, + "validationGenericError": "Проверьте введённые данные и повторите попытку.", + "@validationGenericError": { + "description": "Fallback validation message." + }, + "fileNotFound": "Файл не найден. Возможно, он был перемещён или удалён.", + "@fileNotFound": { + "description": "Message when a file cannot be located." + }, + "fileAccessDenied": "Нет доступа к файлу. Проверьте разрешения.", + "@fileAccessDenied": { + "description": "Message when file access is denied." + }, + "fileTooLarge": "Файл слишком большой. Выберите файл меньшего размера.", + "@fileTooLarge": { + "description": "Message when a file exceeds size limits." + }, + "fileGenericError": "Проблема с файлом. Попробуйте другой файл.", + "@fileGenericError": { + "description": "Fallback file error message." + }, + "permissionCameraRequired": "Требуется доступ к камере. Включите его в настройках.", + "@permissionCameraRequired": { + "description": "Message when camera permission is missing." + }, + "permissionStorageRequired": "Требуется доступ к хранилищу. Включите его в настройках.", + "@permissionStorageRequired": { + "description": "Message when storage permission is missing." + }, + "permissionMicrophoneRequired": "Требуется доступ к микрофону. Включите его в настройках.", + "@permissionMicrophoneRequired": { + "description": "Message when microphone permission is missing." + }, + "permissionGenericError": "Требуется разрешение. Проверьте разрешения приложения в настройках.", + "@permissionGenericError": { + "description": "Fallback permission error message." + }, + "actionRetryRequest": "Повторите запрос.", + "@actionRetryRequest": { + "description": "Description for retrying a failed request." + }, + "actionVerifyConnection": "Проверьте подключение к интернету.", + "@actionVerifyConnection": { + "description": "Description for checking internet connectivity." + }, + "actionRetryOperation": "Повторите операцию.", + "@actionRetryOperation": { + "description": "Description for retrying the same operation." + }, + "actionRetryAfterDelay": "Подождите немного и попробуйте снова.", + "@actionRetryAfterDelay": { + "description": "Description suggesting a short delay before retrying." + }, + "actionSignInToAccount": "Войдите в свою учётную запись.", + "@actionSignInToAccount": { + "description": "Description for signing back into the app." + }, + "actionSelectAnotherFile": "Выберите другой файл.", + "@actionSelectAnotherFile": { + "description": "Description for choosing a different file." + }, + "actionOpenAppSettings": "Откройте настройки приложения, чтобы предоставить разрешения.", + "@actionOpenAppSettings": { + "description": "Description for opening system or app settings." + }, + "actionRetryAfterPermission": "Повторите попытку после предоставления разрешения.", + "@actionRetryAfterPermission": { + "description": "Description for retrying once permissions are granted." + }, + "actionReturnToPrevious": "Вернуться на предыдущий экран.", + "@actionReturnToPrevious": { + "description": "Description for navigating back to the prior screen." + }, + "continueAction": "Продолжить", + "@continueAction": { + "description": "Button label to continue an action or flow." + }, + "loadingShort": "Загрузка", + "@loadingShort": { + "description": "Short loading label used for accessibility." + }, + "loadingAnnouncement": "Загрузка: {message}", + "@loadingAnnouncement": { + "description": "Screen reader announcement when loading a resource.", + "placeholders": { + "message": { + "type": "String", + "example": "Messages" + } + } + }, + "errorAnnouncement": "Ошибка: {error}", + "@errorAnnouncement": { + "description": "Screen reader announcement for an error.", + "placeholders": { + "error": { + "type": "String", + "example": "Network timeout" + } + } + }, + "errorAnnouncementWithSuggestion": "Ошибка: {error}. {suggestion}", + "@errorAnnouncementWithSuggestion": { + "description": "Screen reader announcement for an error with a follow-up suggestion.", + "placeholders": { + "error": { + "type": "String", + "example": "Network timeout" + }, + "suggestion": { + "type": "String", + "example": "Please try again later." + } + } + }, + "successAnnouncement": "Успешно: {message}", + "@successAnnouncement": { + "description": "Screen reader announcement for successful actions.", + "placeholders": { + "message": { + "type": "String", + "example": "Profile updated" + } + } + }, + "requiredFieldLabel": "{label} *", + "@requiredFieldLabel": { + "description": "Label text indicating a required field.", + "placeholders": { + "label": { + "type": "String", + "example": "Email" + } + } + }, + "requiredFieldHelper": "Обязательное поле", + "@requiredFieldHelper": { + "description": "Helper text indicating that the field is required." + }, + "switchOnLabel": "Вкл.", + "@switchOnLabel": { + "description": "Semantic label when a switch is enabled." + }, + "switchOffLabel": "Выкл.", + "@switchOffLabel": { + "description": "Semantic label when a switch is disabled." + }, + "dialogSemanticLabel": "Диалог: {title}", + "@dialogSemanticLabel": { + "description": "Semantic label describing the dialog title.", + "placeholders": { + "title": { + "type": "String", + "example": "Settings" + } + } + }, + "previousLabel": "Назад", + "@previousLabel": { + "description": "Label for navigating to the previous item." + }, + "nextLabel": "Далее", + "@nextLabel": { + "description": "Label for navigating to the next item." + }, + "themePaletteConduitLabel": "Conduit", + "@themePaletteConduitLabel": { + "description": "Palette name for the default Conduit theme." + }, + "themePaletteConduitDescription": "Нейтральная чистая тема, созданная для Conduit.", + "@themePaletteConduitDescription": { + "description": "Description of the Conduit palette." + }, + "themePaletteClaudeLabel": "Claude", + "@themePaletteClaudeLabel": { + "description": "Palette name inspired by the Claude web client." + }, + "themePaletteClaudeDescription": "Тёплая тактильная палитра из веб-клиента Claude.", + "@themePaletteClaudeDescription": { + "description": "Description of the Claude palette." + }, + "themePaletteT3ChatLabel": "T3 Chat", + "@themePaletteT3ChatLabel": { + "description": "Palette name inspired by the T3 Stack brand." + }, + "themePaletteT3ChatDescription": "Игривые градиенты, вдохновлённые брендом T3 Stack.", + "@themePaletteT3ChatDescription": { + "description": "Description of the T3 Chat palette." + }, + "themePaletteCatppuccinLabel": "Catppuccin", + "@themePaletteCatppuccinLabel": { + "description": "Palette name for Catppuccin colors." + }, + "themePaletteCatppuccinDescription": "Мягкая пастельная палитра.", + "@themePaletteCatppuccinDescription": { + "description": "Description of the Catppuccin palette." + }, + "themePaletteTangerineLabel": "Tangerine", + "@themePaletteTangerineLabel": { + "description": "Palette name for Tangerine colors." + }, + "themePaletteTangerineDescription": "Тёплая палитра оранжевых и сланцевых оттенков.", + "@themePaletteTangerineDescription": { + "description": "Description of the Tangerine palette." + }, + "@onboardStartTitle": { + "description": "Onboarding card: start chatting title.", + "placeholders": { + "username": { + "type": "String", + "example": "Alex" + } + } + }, + "@notAnImageFile": { + "description": "Error when a referenced file is not an image.", + "placeholders": { + "fileName": { + "type": "String", + "example": "image.txt" + } + } + }, + "@failedToLoadImage": { + "description": "Error including the underlying reason when image loading fails.", + "placeholders": { + "error": { + "type": "String", + "example": "Network error" + } + } + }, + "@ttsVoicesForLanguage": { + "description": "Section header for voices matching the app language", + "placeholders": { + "language": { + "type": "String", + "example": "EN" + } + } + }, + "voiceCallReady": "Готово", + "@voiceCallReady": { + "description": "Status label shown when the voice call is ready to start." + }, + "voiceCallConnecting": "Подключение...", + "@voiceCallConnecting": { + "description": "Status label shown while the voice call is connecting." + }, + "voiceCallListening": "Слушает", + "@voiceCallListening": { + "description": "Status label shown while the call is listening for input." + }, + "voiceCallPaused": "Пауза", + "@voiceCallPaused": { + "description": "Status label shown when the call is paused." + }, + "voiceCallProcessing": "Обрабатывает...", + "@voiceCallProcessing": { + "description": "Status label shown while the call processes a response." + }, + "voiceCallSpeaking": "Говорит", + "@voiceCallSpeaking": { + "description": "Status label shown while the assistant is speaking." + }, + "voiceCallDisconnected": "Отключено", + "@voiceCallDisconnected": { + "description": "Status label shown when the voice call has ended or disconnected." + }, + "voiceCallErrorHelp": "Проверьте:\n• Разрешение на микрофон предоставлено\n• Распознавание речи доступно на устройстве\n• Есть подключение к серверу", + "@voiceCallErrorHelp": { + "description": "Guidance shown when the voice call encounters an error." + } } diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index a615ced..b8b41f9 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -1,7 +1,6 @@ { "@@locale": "zh", "appTitle": "Conduit", - "initializationFailed": "初始化失败", "retry": "重试", "back": "返回", "you": "你", @@ -16,10 +15,6 @@ "@connectionIssueSubtitle": { "description": "当无法访问服务器时解释可用操作的副标题" }, - "stillOfflineMessage": "我们仍然无法访问服务器。请仔细检查您的连接并重试。", - "@stillOfflineMessage": { - "description": "重试后连接未恢复时的状态消息" - }, "account": "账户", "supportConduit": "支持 Conduit", "supportConduitSubtitle": "通过资助持续开发来保持 Conduit 的独立性。", @@ -37,32 +32,14 @@ "noResults": "无结果", "searchModels": "搜索模型...", "errorMessage": "出了点问题。请重试。", - "loginButton": "登录", - "menuItem": "设置", - "dynamicContentWithPlaceholder": "欢迎,{name}!", - "itemsCount": "{count, plural, =0{无项目} other{{count} 个项目}}", "closeButtonSemantic": "关闭", "loadingContent": "加载内容中", "noItems": "无项目", "noItemsToDisplay": "无可显示的项目", - "loadMore": "加载更多", - "workspace": "工作区", - "recentFiles": "最近文件", "knowledgeBase": "知识库", - "noFilesYet": "尚无文件", - "uploadDocsPrompt": "上传文档以在您与 Conduit 的对话中引用", - "uploadFirstFile": "上传您的第一个文件", "attachments": "附件", - "knowledgeBaseEmpty": "知识库为空", - "createCollectionsPrompt": "创建相关文档集合以便于引用", - "chooseSourcePhoto": "选择来源", "takePhoto": "拍照", - "chooseFromGallery": "从相册中选择", "document": "文档", - "documentHint": "PDF、Word 或文本文件", - "uploadFileTitle": "上传文件", - "fileUploadComingSoon": "{type} 的文件上传即将推出!", - "kbCreationComingSoon": "知识库创建即将推出!", "backToServerSetup": "返回服务器设置", "connectedToServer": "已连接到服务器", "signIn": "登录", @@ -125,7 +102,6 @@ "onboardQuickSubtitle": "打开菜单在对话、工作区和个人资料之间切换。", "onboardQuickBullet1": "点击菜单访问对话、工作区、个人资料", "onboardQuickBullet2": "从顶部栏开始新对话或管理模型", - "addAttachment": "添加附件", "attachmentLabel": "附件", "tools": "工具", "voiceInput": "语音输入", @@ -157,16 +133,12 @@ "failedToDecodeImage": "无法解码图像", "invalidImageFormat": "无效的图像格式", "emptyImageData": "空图像数据", - "featureRequiresInternet": "此功能需要互联网连接", - "messagesWillSendWhenOnline": "当您重新上线时将发送消息", "confirm": "确认", "cancel": "取消", "ok": "确定", "inputField": "输入字段", - "captureDocumentOrImage": "捕获文档或图像", "checkConnection": "检查连接", "openSettings": "打开设置", - "chooseDifferentFile": "选择其他文件", "goBack": "返回", "technicalDetails": "技术详情", "save": "保存", @@ -178,18 +150,9 @@ "newChat": "新对话", "more": "更多", "clear": "清除", - "searchHint": "搜索...", "searchConversations": "搜索对话...", "create": "创建", - "folderCreated": "文件夹已创建", "failedToCreateFolder": "无法创建文件夹", - "movedChatToFolder": "已将「{title}」移至「{folder}」", - "@movedChatToFolder": { - "placeholders": { - "title": {"type": "String"}, - "folder": {"type": "String"} - } - }, "failedToMoveChat": "无法移动对话", "failedToLoadChats": "无法加载对话", "failedToUpdatePin": "无法更新置顶", @@ -219,13 +182,17 @@ "deleteMessagesMessage": "删除 {count} 条消息?", "@deleteMessagesMessage": { "placeholders": { - "count": {"type": "int"} + "count": { + "type": "int" + } } }, "routeNotFound": "未找到路由:{routeName}", "@routeNotFound": { "placeholders": { - "routeName": {"type": "String"} + "routeName": { + "type": "String" + } } }, "deleteChatTitle": "删除对话", @@ -271,29 +238,55 @@ "headerNameTooLong": "标头名称太长(最多 64 个字符)", "headerNameInvalidChars": "无效的标头名称。仅使用字母、数字和这些符号:!#$&-^_`|~", "headerNameReserved": "无法覆盖保留的标头「{key}」", - "@headerNameReserved": {"placeholders": {"key": {"type": "String"}}}, + "@headerNameReserved": { + "placeholders": { + "key": { + "type": "String" + } + } + }, "headerValueEmpty": "标头值不能为空", "headerValueTooLong": "标头值太长(最多 1024 个字符)", "headerValueInvalidChars": "标头值包含无效字符。仅使用可打印的 ASCII。", "headerValueUnsafe": "标头值似乎包含潜在的不安全内容", "headerAlreadyExists": "标头「{key}」已存在。首先删除它以更新。", - "@headerAlreadyExists": {"placeholders": {"key": {"type": "String"}}}, + "@headerAlreadyExists": { + "placeholders": { + "key": { + "type": "String" + } + } + }, "maxHeadersReachedDetail": "最多允许 10 个自定义标头。删除一些以添加更多。", - "editMessage": "编辑消息", "noModelsAvailable": "无可用模型", "followingSystem": "跟随系统:{theme}", - "@followingSystem": {"placeholders": {"theme": {"type": "String"}}}, + "@followingSystem": { + "placeholders": { + "theme": { + "type": "String" + } + } + }, "themeDark": "深色", "themePalette": "强调色调色板", - "@themePalette": {"description": "选择应用颜色调色板的标题。"}, - "themePaletteDescription": "选择用于按钮、卡片和对话气泡的强调色。", - "@themePaletteDescription": {"description": "解释调色板选择的帮助文本。"}, + "@themePalette": { + "description": "选择应用颜色调色板的标题。" + }, "themeLight": "浅色", "currentlyUsingDarkTheme": "当前使用深色主题", "currentlyUsingLightTheme": "当前使用浅色主题", "aboutConduit": "关于 Conduit", "versionLabel": "版本:{version}({build})", - "@versionLabel": {"placeholders": {"version": {"type": "String"}, "build": {"type": "String"}}}, + "@versionLabel": { + "placeholders": { + "version": { + "type": "String" + }, + "build": { + "type": "String" + } + } + }, "githubRepository": "GitHub 仓库", "unableToLoadAppInfo": "无法加载应用信息", "thinking": "思考中...", @@ -301,7 +294,12 @@ "thoughtForDuration": "思考了 {duration}", "@thoughtForDuration": { "description": "显示助手在回复前思考了多长时间。", - "placeholders": {"duration": {"type": "String", "example": "3s"}} + "placeholders": { + "duration": { + "type": "String", + "example": "3s" + } + } }, "appCustomization": "自定义", "appCustomizationSubtitle": "主题、语言、语音和 quickpills", @@ -325,12 +323,413 @@ "display": "显示", "realtime": "实时", "transportMode": "传输模式", - "transportModeDescription": "选择应用如何连接以进行实时更新。", "mode": "模式", "transportModePolling": "轮询回退", "transportModeWs": "仅 WebSocket", "transportModePollingInfo": "当 WebSocket 被阻止时改用 HTTP 轮询,在条件允许时切换回 WebSocket。", "transportModeWsInfo": "开销较低,但可能在严格的代理/防火墙后失败。", - "websocketConnectionError": "无法建立实时连接。请检查您的网络和服务器配置。", - "websocketReconnectFailed": "实时连接失败。流式传输可能无法正常工作。" + "quickActionsSelectedCount": "{count, plural, =0{未选择操作} other{已选择{count}个操作}}", + "@quickActionsSelectedCount": { + "description": "Subtitle indicating how many quick actions are selected.", + "placeholders": { + "count": { + "type": "int", + "example": "2" + } + } + }, + "autoSelectDescription": "让应用自动选择最佳模型", + "@autoSelectDescription": { + "description": "Explains what the auto-select model setting does." + }, + "ttsEngineLabel": "引擎", + "@ttsEngineLabel": { + "description": "Label for selecting the text-to-speech engine." + }, + "ttsEngineDevice": "本机", + "@ttsEngineDevice": { + "description": "Chip label for using on-device text-to-speech." + }, + "ttsEngineServer": "服务器", + "@ttsEngineServer": { + "description": "Chip label for using server-side text-to-speech." + }, + "modelCapabilityMultimodal": "多模态", + "@modelCapabilityMultimodal": { + "description": "Capability chip label for models that support multimodal input." + }, + "modelCapabilityReasoning": "推理", + "@modelCapabilityReasoning": { + "description": "Capability chip label for models that support reasoning features." + }, + "voiceCallTitle": "语音通话", + "@voiceCallTitle": { + "description": "Title displayed on the voice call screen." + }, + "voiceCallPause": "暂停", + "@voiceCallPause": { + "description": "Button label to pause a voice call." + }, + "voiceCallResume": "继续", + "@voiceCallResume": { + "description": "Button label to resume a paused voice call." + }, + "voiceCallStop": "停止", + "@voiceCallStop": { + "description": "Button label to stop the active voice call." + }, + "voiceCallEnd": "结束通话", + "@voiceCallEnd": { + "description": "Button label to end the voice call session." + }, + "chooseDifferentFile": "选择其他文件", + "@chooseDifferentFile": { + "description": "Action label prompting the user to pick another file." + }, + "errorWithMessage": "错误:{message}", + "@errorWithMessage": { + "description": "Error label with appended message text.", + "placeholders": { + "message": { + "type": "String", + "example": "Network timeout" + } + } + }, + "networkTimeoutError": "连接超时。请检查网络后重试。", + "@networkTimeoutError": { + "description": "User-facing message when a network request times out." + }, + "networkUnreachableError": "无法连接服务器。请检查服务器地址和网络。", + "@networkUnreachableError": { + "description": "User-facing message when the server cannot be reached." + }, + "networkServerNotResponding": "服务器没有响应。请确认服务器正在运行且可访问。", + "@networkServerNotResponding": { + "description": "User-facing message when the server does not respond to a request." + }, + "networkGenericError": "网络连接出现问题。请检查网络连接。", + "@networkGenericError": { + "description": "Fallback message for generic network errors." + }, + "serverError500": "服务器出现问题,通常是暂时的。", + "@serverError500": { + "description": "Message when a 500 error is encountered." + }, + "serverErrorUnavailable": "服务器暂时不可用。请稍后再试。", + "@serverErrorUnavailable": { + "description": "Message when a 502/503 error is encountered." + }, + "serverErrorTimeout": "服务器响应超时。请重试。", + "@serverErrorTimeout": { + "description": "Message when the server times out." + }, + "serverErrorGeneric": "服务器出现故障。请稍后再试。", + "@serverErrorGeneric": { + "description": "Fallback server error message." + }, + "authSessionExpired": "会话已过期,请重新登录。", + "@authSessionExpired": { + "description": "Message when an authentication session expires." + }, + "authForbidden": "您没有执行此操作的权限。", + "@authForbidden": { + "description": "Message when the user lacks required permissions." + }, + "authInvalidToken": "认证令牌无效,请重新登录。", + "@authInvalidToken": { + "description": "Message when the authentication token is invalid." + }, + "authGenericError": "认证出现问题,请重新登录。", + "@authGenericError": { + "description": "Fallback authentication error message." + }, + "validationInvalidEmail": "请输入有效的邮箱地址。", + "@validationInvalidEmail": { + "description": "Validation message for invalid email input." + }, + "validationWeakPassword": "密码不符合要求,请检查后重试。", + "@validationWeakPassword": { + "description": "Validation message for weak passwords." + }, + "validationMissingRequired": "请填写所有必填项。", + "@validationMissingRequired": { + "description": "Validation message when required fields are missing." + }, + "validationFormatError": "部分信息格式不正确,请检查后重试。", + "@validationFormatError": { + "description": "Validation message for generic formatting issues." + }, + "validationGenericError": "请检查输入内容并重试。", + "@validationGenericError": { + "description": "Fallback validation message." + }, + "fileNotFound": "未找到文件,可能已移动或删除。", + "@fileNotFound": { + "description": "Message when a file cannot be located." + }, + "fileAccessDenied": "无法访问文件,请检查权限。", + "@fileAccessDenied": { + "description": "Message when file access is denied." + }, + "fileTooLarge": "文件过大,请选择较小的文件。", + "@fileTooLarge": { + "description": "Message when a file exceeds size limits." + }, + "fileGenericError": "文件出现问题,请尝试其他文件。", + "@fileGenericError": { + "description": "Fallback file error message." + }, + "permissionCameraRequired": "需要相机权限,请在设置中开启。", + "@permissionCameraRequired": { + "description": "Message when camera permission is missing." + }, + "permissionStorageRequired": "需要存储权限,请在设置中开启。", + "@permissionStorageRequired": { + "description": "Message when storage permission is missing." + }, + "permissionMicrophoneRequired": "需要麦克风权限,请在设置中开启。", + "@permissionMicrophoneRequired": { + "description": "Message when microphone permission is missing." + }, + "permissionGenericError": "需要权限,请在设置中检查应用权限。", + "@permissionGenericError": { + "description": "Fallback permission error message." + }, + "actionRetryRequest": "请再次尝试该请求。", + "@actionRetryRequest": { + "description": "Description for retrying a failed request." + }, + "actionVerifyConnection": "请检查网络连接。", + "@actionVerifyConnection": { + "description": "Description for checking internet connectivity." + }, + "actionRetryOperation": "请重试此操作。", + "@actionRetryOperation": { + "description": "Description for retrying the same operation." + }, + "actionRetryAfterDelay": "稍等片刻再试一次。", + "@actionRetryAfterDelay": { + "description": "Description suggesting a short delay before retrying." + }, + "actionSignInToAccount": "登录到您的账户。", + "@actionSignInToAccount": { + "description": "Description for signing back into the app." + }, + "actionSelectAnotherFile": "请选择其他文件。", + "@actionSelectAnotherFile": { + "description": "Description for choosing a different file." + }, + "actionOpenAppSettings": "打开应用设置以授予权限。", + "@actionOpenAppSettings": { + "description": "Description for opening system or app settings." + }, + "actionRetryAfterPermission": "授予权限后请重试。", + "@actionRetryAfterPermission": { + "description": "Description for retrying once permissions are granted." + }, + "actionReturnToPrevious": "返回上一屏。", + "@actionReturnToPrevious": { + "description": "Description for navigating back to the prior screen." + }, + "continueAction": "继续", + "@continueAction": { + "description": "Button label to continue an action or flow." + }, + "loadingShort": "加载中", + "@loadingShort": { + "description": "Short loading label used for accessibility." + }, + "loadingAnnouncement": "正在加载:{message}", + "@loadingAnnouncement": { + "description": "Screen reader announcement when loading a resource.", + "placeholders": { + "message": { + "type": "String", + "example": "Messages" + } + } + }, + "errorAnnouncement": "错误:{error}", + "@errorAnnouncement": { + "description": "Screen reader announcement for an error.", + "placeholders": { + "error": { + "type": "String", + "example": "Network timeout" + } + } + }, + "errorAnnouncementWithSuggestion": "错误:{error}。{suggestion}", + "@errorAnnouncementWithSuggestion": { + "description": "Screen reader announcement for an error with a follow-up suggestion.", + "placeholders": { + "error": { + "type": "String", + "example": "Network timeout" + }, + "suggestion": { + "type": "String", + "example": "Please try again later." + } + } + }, + "successAnnouncement": "成功:{message}", + "@successAnnouncement": { + "description": "Screen reader announcement for successful actions.", + "placeholders": { + "message": { + "type": "String", + "example": "Profile updated" + } + } + }, + "requiredFieldLabel": "{label} *", + "@requiredFieldLabel": { + "description": "Label text indicating a required field.", + "placeholders": { + "label": { + "type": "String", + "example": "Email" + } + } + }, + "requiredFieldHelper": "必填项", + "@requiredFieldHelper": { + "description": "Helper text indicating that the field is required." + }, + "switchOnLabel": "开启", + "@switchOnLabel": { + "description": "Semantic label when a switch is enabled." + }, + "switchOffLabel": "关闭", + "@switchOffLabel": { + "description": "Semantic label when a switch is disabled." + }, + "dialogSemanticLabel": "对话框:{title}", + "@dialogSemanticLabel": { + "description": "Semantic label describing the dialog title.", + "placeholders": { + "title": { + "type": "String", + "example": "Settings" + } + } + }, + "previousLabel": "上一步", + "@previousLabel": { + "description": "Label for navigating to the previous item." + }, + "nextLabel": "下一步", + "@nextLabel": { + "description": "Label for navigating to the next item." + }, + "themePaletteConduitLabel": "Conduit", + "@themePaletteConduitLabel": { + "description": "Palette name for the default Conduit theme." + }, + "themePaletteConduitDescription": "为 Conduit 设计的简洁中性色主题。", + "@themePaletteConduitDescription": { + "description": "Description of the Conduit palette." + }, + "themePaletteClaudeLabel": "Claude", + "@themePaletteClaudeLabel": { + "description": "Palette name inspired by the Claude web client." + }, + "themePaletteClaudeDescription": "源自 Claude 网页端的温暖触感配色。", + "@themePaletteClaudeDescription": { + "description": "Description of the Claude palette." + }, + "themePaletteT3ChatLabel": "T3 Chat", + "@themePaletteT3ChatLabel": { + "description": "Palette name inspired by the T3 Stack brand." + }, + "themePaletteT3ChatDescription": "灵感来自 T3 Stack 品牌的活泼渐变。", + "@themePaletteT3ChatDescription": { + "description": "Description of the T3 Chat palette." + }, + "themePaletteCatppuccinLabel": "Catppuccin", + "@themePaletteCatppuccinLabel": { + "description": "Palette name for Catppuccin colors." + }, + "themePaletteCatppuccinDescription": "柔和的马卡龙色调。", + "@themePaletteCatppuccinDescription": { + "description": "Description of the Catppuccin palette." + }, + "themePaletteTangerineLabel": "Tangerine", + "@themePaletteTangerineLabel": { + "description": "Palette name for Tangerine colors." + }, + "themePaletteTangerineDescription": "温暖的橙色与石板色调。", + "@themePaletteTangerineDescription": { + "description": "Description of the Tangerine palette." + }, + "@onboardStartTitle": { + "description": "Onboarding card: start chatting title.", + "placeholders": { + "username": { + "type": "String", + "example": "Alex" + } + } + }, + "@notAnImageFile": { + "description": "Error when a referenced file is not an image.", + "placeholders": { + "fileName": { + "type": "String", + "example": "image.txt" + } + } + }, + "@failedToLoadImage": { + "description": "Error including the underlying reason when image loading fails.", + "placeholders": { + "error": { + "type": "String", + "example": "Network error" + } + } + }, + "@ttsVoicesForLanguage": { + "description": "Section header for voices matching the app language", + "placeholders": { + "language": { + "type": "String", + "example": "EN" + } + } + }, + "voiceCallReady": "已就绪", + "@voiceCallReady": { + "description": "Status label shown when the voice call is ready to start." + }, + "voiceCallConnecting": "正在连接...", + "@voiceCallConnecting": { + "description": "Status label shown while the voice call is connecting." + }, + "voiceCallListening": "正在聆听", + "@voiceCallListening": { + "description": "Status label shown while the call is listening for input." + }, + "voiceCallPaused": "已暂停", + "@voiceCallPaused": { + "description": "Status label shown when the call is paused." + }, + "voiceCallProcessing": "正在思考...", + "@voiceCallProcessing": { + "description": "Status label shown while the call processes a response." + }, + "voiceCallSpeaking": "正在讲话", + "@voiceCallSpeaking": { + "description": "Status label shown while the assistant is speaking." + }, + "voiceCallDisconnected": "已断开", + "@voiceCallDisconnected": { + "description": "Status label shown when the voice call has ended or disconnected." + }, + "voiceCallErrorHelp": "请检查:\n• 已授予麦克风权限\n• 设备支持语音识别\n• 已连接到服务器", + "@voiceCallErrorHelp": { + "description": "Guidance shown when the voice call encounters an error." + } } diff --git a/lib/shared/theme/tweakcn_themes.dart b/lib/shared/theme/tweakcn_themes.dart index ffae344..89ecd4f 100644 --- a/lib/shared/theme/tweakcn_themes.dart +++ b/lib/shared/theme/tweakcn_themes.dart @@ -1,3 +1,4 @@ +import 'package:conduit/l10n/app_localizations.dart'; import 'package:flutter/material.dart'; /// Represents a single tweakcn theme variant (light or dark) and exposes the @@ -88,16 +89,16 @@ class TweakcnThemeVariant { class TweakcnThemeDefinition { const TweakcnThemeDefinition({ required this.id, - required this.label, - required this.description, + required this.labelBuilder, + required this.descriptionBuilder, required this.light, required this.dark, required this.preview, }); final String id; - final String label; - final String description; + final String Function(AppLocalizations) labelBuilder; + final String Function(AppLocalizations) descriptionBuilder; final TweakcnThemeVariant light; final TweakcnThemeVariant dark; final List preview; @@ -105,6 +106,10 @@ class TweakcnThemeDefinition { TweakcnThemeVariant variantFor(Brightness brightness) { return brightness == Brightness.dark ? dark : light; } + + String label(AppLocalizations l10n) => labelBuilder(l10n); + + String description(AppLocalizations l10n) => descriptionBuilder(l10n); } Color mix(Color a, Color b, double amount) { @@ -771,8 +776,8 @@ class TweakcnThemes { static final TweakcnThemeDefinition claude = TweakcnThemeDefinition( id: 'claude', - label: 'Claude', - description: 'Warm, tactile palette lifted from the Claude web client.', + labelBuilder: (l10n) => l10n.themePaletteClaudeLabel, + descriptionBuilder: (l10n) => l10n.themePaletteClaudeDescription, light: _claudeLight, dark: _claudeDark, preview: const [ @@ -784,8 +789,8 @@ class TweakcnThemes { static final TweakcnThemeDefinition t3Chat = TweakcnThemeDefinition( id: 't3_chat', - label: 'T3 Chat', - description: 'Playful gradients inspired by the T3 Stack brand.', + labelBuilder: (l10n) => l10n.themePaletteT3ChatLabel, + descriptionBuilder: (l10n) => l10n.themePaletteT3ChatDescription, light: _t3ChatLight, dark: _t3ChatDark, preview: const [ @@ -797,8 +802,8 @@ class TweakcnThemes { static final TweakcnThemeDefinition conduit = TweakcnThemeDefinition( id: 'conduit', - label: 'Conduit', - description: 'Clean neutral theme designed for Conduit.', + labelBuilder: (l10n) => l10n.themePaletteConduitLabel, + descriptionBuilder: (l10n) => l10n.themePaletteConduitDescription, light: _conduitLight, dark: _conduitDark, preview: const [ @@ -810,8 +815,8 @@ class TweakcnThemes { static final TweakcnThemeDefinition catppuccin = TweakcnThemeDefinition( id: 'catppuccin', - label: 'Catppuccin', - description: 'Soft pastel palette.', + labelBuilder: (l10n) => l10n.themePaletteCatppuccinLabel, + descriptionBuilder: (l10n) => l10n.themePaletteCatppuccinDescription, light: _catppuccinLight, dark: _catppuccinDark, preview: const [ @@ -823,8 +828,8 @@ class TweakcnThemes { static final TweakcnThemeDefinition tangerine = TweakcnThemeDefinition( id: 'tangerine', - label: 'Tangerine', - description: 'Warm orange-and-slate palette.', + labelBuilder: (l10n) => l10n.themePaletteTangerineLabel, + descriptionBuilder: (l10n) => l10n.themePaletteTangerineDescription, light: _tangerineLight, dark: _tangerineDark, preview: const [ diff --git a/tool/validate_arb_locales.dart b/tool/validate_arb_locales.dart index 9ebc25a..3ad2f28 100644 --- a/tool/validate_arb_locales.dart +++ b/tool/validate_arb_locales.dart @@ -71,7 +71,7 @@ Future main(List args) async { } // Unused keys (best-effort) — WARNINGS only - final usedKeys = await _scanUsedLocalizationKeys(); + final usedKeys = await _scanUsedLocalizationKeys(baseKeys); final unused = baseKeys.difference(usedKeys); if (unused.isNotEmpty) { warnings.add('Unused keys in EN (best-effort): ${unused.toList()..sort()}'); @@ -120,22 +120,34 @@ Map> _placeholdersMap(Map m) { // Duplicate detection intentionally omitted (see note above). -Future> _scanUsedLocalizationKeys() async { - final libDir = Directory('lib'); +Future> _scanUsedLocalizationKeys(Set baseKeys) async { final used = {}; - final dartFiles = await libDir - .list(recursive: true) - .where((e) => e.path.endsWith('.dart')) - .map((e) => File(e.path)) - .toList(); - final regex = RegExp(r'AppLocalizations\.of\([^)]*\)!\.([a-zA-Z0-9_]+)'); - for (final f in dartFiles) { - final text = await f.readAsString(); - for (final m in regex.allMatches(text)) { - final key = m.group(1); - if (key != null) used.add(key); + Future keyIsUsed(String key) async { + final result = await Process.run('rg', [ + '--fixed-strings', + '--quiet', + '--glob=*.dart', + '--glob=!lib/l10n/app_localizations*.dart', + key, + 'lib', + ]); + if (result.exitCode == 0) { + return true; + } + if (result.exitCode > 1) { + stderr.writeln( + 'warning: failed to search for key "$key": ${result.stderr}'.trim(), + ); + } + return false; + } + + for (final key in baseKeys) { + if (await keyIsUsed(key)) { + used.add(key); } } + return used; }