From 8629e1e039bd76427bda47fcb4648cba526f69ed Mon Sep 17 00:00:00 2001 From: cogwheel0 <172976095+cogwheel0@users.noreply.github.com> Date: Sun, 5 Oct 2025 00:05:58 +0530 Subject: [PATCH] refactor: enhance localization support in chat and voice input features - Integrated localization for various dialog messages and UI elements in the chat and voice input components. - Updated the confirmation dialog to utilize localized strings for delete messages, improving user experience across different languages. - Enhanced voice input sheet to reflect localized text for status updates, action buttons, and prompts, ensuring consistency in user interactions. - Improved the file attachment widget to display the attachment label in a localized manner, enhancing accessibility for users in different regions. - Streamlined localization management by centralizing string retrieval, promoting maintainability and clarity in the codebase. --- lib/features/chat/views/chat_page.dart | 41 +++++---- .../chat/widgets/file_attachment_widget.dart | 2 +- .../profile/views/app_customization_page.dart | 8 +- lib/l10n/app_de.arb | 15 ++++ lib/l10n/app_en.arb | 54 +++++++++++ lib/l10n/app_fr.arb | 15 ++++ lib/l10n/app_it.arb | 15 ++++ lib/l10n/app_localizations.dart | 90 +++++++++++++++++++ lib/l10n/app_localizations_de.dart | 46 ++++++++++ lib/l10n/app_localizations_en.dart | 46 ++++++++++ lib/l10n/app_localizations_fr.dart | 46 ++++++++++ lib/l10n/app_localizations_it.dart | 46 ++++++++++ .../widgets/markdown/markdown_config.dart | 72 +++++++-------- lib/shared/widgets/themed_dialogs.dart | 25 ++++-- 14 files changed, 455 insertions(+), 66 deletions(-) diff --git a/lib/features/chat/views/chat_page.dart b/lib/features/chat/views/chat_page.dart index 2b2c1c2..261dca7 100644 --- a/lib/features/chat/views/chat_page.dart +++ b/lib/features/chat/views/chat_page.dart @@ -1673,11 +1673,13 @@ class _ChatPageState extends ConsumerState { final selectedMessages = _getSelectedMessages(); if (selectedMessages.isEmpty) return; + final l10n = AppLocalizations.of(context)!; ThemedDialogs.confirm( context, - title: 'Delete Messages', - message: 'Delete ${selectedMessages.length} messages?', - confirmText: 'Delete', + title: l10n.deleteMessagesTitle, + message: l10n.deleteMessagesMessage(selectedMessages.length), + confirmText: l10n.delete, + cancelText: l10n.cancel, isDestructive: true, ).then((confirmed) async { if (confirmed == true) { @@ -2285,6 +2287,7 @@ class _VoiceInputSheetState extends ConsumerState<_VoiceInputSheet> { context: context, backgroundColor: Colors.transparent, builder: (context) { + final l10n = AppLocalizations.of(context)!; return Container( decoration: BoxDecoration( color: context.conduitTheme.surfaceBackground, @@ -2306,7 +2309,7 @@ class _VoiceInputSheetState extends ConsumerState<_VoiceInputSheet> { const SheetHandle(), const SizedBox(height: Spacing.md), Text( - 'Select Language', + l10n.selectLanguage, style: TextStyle( fontSize: AppTypography.headlineSmall, color: context.conduitTheme.textPrimary, @@ -2395,6 +2398,12 @@ class _VoiceInputSheetState extends ConsumerState<_VoiceInputSheet> { Widget build(BuildContext context) { final media = MediaQuery.of(context); final isCompact = media.size.height < 680; + final l10n = AppLocalizations.of(context)!; + final statusText = _isListening + ? (_voiceService.hasLocalStt + ? l10n.voiceStatusListening + : l10n.voiceStatusRecording) + : l10n.voice; return Container( height: media.size.height * (isCompact ? 0.45 : 0.6), decoration: BoxDecoration( @@ -2425,11 +2434,7 @@ class _VoiceInputSheetState extends ConsumerState<_VoiceInputSheet> { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - _isListening - ? (_voiceService.hasLocalStt - ? 'Listening…' - : 'Recording…') - : 'Voice', + statusText, style: TextStyle( fontSize: AppTypography.headlineMedium, fontWeight: FontWeight.w600, @@ -2532,7 +2537,7 @@ class _VoiceInputSheetState extends ConsumerState<_VoiceInputSheet> { ), const SizedBox(width: Spacing.xs), Text( - 'Hold to talk', + l10n.voiceHoldToTalk, style: TextStyle( color: context.conduitTheme.textSecondary, ), @@ -2555,7 +2560,7 @@ class _VoiceInputSheetState extends ConsumerState<_VoiceInputSheet> { ), const SizedBox(width: Spacing.xs), Text( - 'Auto-send', + l10n.voiceAutoSend, style: TextStyle( color: context.conduitTheme.textSecondary, ), @@ -2729,7 +2734,7 @@ class _VoiceInputSheetState extends ConsumerState<_VoiceInputSheet> { Row( children: [ Text( - 'Transcript', + l10n.voiceTranscript, style: TextStyle( fontSize: AppTypography.labelSmall, fontWeight: FontWeight.w600, @@ -2762,9 +2767,9 @@ class _VoiceInputSheetState extends ConsumerState<_VoiceInputSheet> { _recognizedText.isEmpty ? (_isListening ? (_voiceService.hasLocalStt - ? 'Speak now…' - : 'Recording…') - : 'Tap Start to begin') + ? l10n.voicePromptSpeakNow + : l10n.voiceStatusRecording) + : l10n.voicePromptTapStart) : _recognizedText, style: TextStyle( fontSize: isUltra @@ -2825,7 +2830,9 @@ class _VoiceInputSheetState extends ConsumerState<_VoiceInputSheet> { if (showStartStop) ...[ Expanded( child: ConduitButton( - text: _isListening ? 'Stop' : 'Start', + text: _isListening + ? l10n.voiceActionStop + : l10n.voiceActionStart, isSecondary: true, isCompact: isCompact, onPressed: _isListening @@ -2839,7 +2846,7 @@ class _VoiceInputSheetState extends ConsumerState<_VoiceInputSheet> { if (showSend) ...[ Expanded( child: ConduitButton( - text: 'Send', + text: l10n.send, isCompact: isCompact, onPressed: _recognizedText.isNotEmpty ? _sendText diff --git a/lib/features/chat/widgets/file_attachment_widget.dart b/lib/features/chat/widgets/file_attachment_widget.dart index 54e069d..ce568dd 100644 --- a/lib/features/chat/widgets/file_attachment_widget.dart +++ b/lib/features/chat/widgets/file_attachment_widget.dart @@ -231,7 +231,7 @@ class MessageAttachmentPreview extends StatelessWidget { ), const SizedBox(width: Spacing.xs), Text( - 'Attachment', + AppLocalizations.of(context)!.attachmentLabel, style: TextStyle( color: context.conduitTheme.textPrimary.withValues( alpha: 0.8, diff --git a/lib/features/profile/views/app_customization_page.dart b/lib/features/profile/views/app_customization_page.dart index 643f1cb..ef5d4be 100644 --- a/lib/features/profile/views/app_customization_page.dart +++ b/lib/features/profile/views/app_customization_page.dart @@ -460,11 +460,12 @@ class AppCustomizationPage extends ConsumerWidget { AppSettings settings, ) { final theme = context.conduitTheme; + final l10n = AppLocalizations.of(context)!; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'Chat', + l10n.chatSettings, style: theme.headingSmall?.copyWith(color: theme.textPrimary) ?? TextStyle(color: theme.textPrimary, fontSize: 18), @@ -476,9 +477,8 @@ class AppCustomizationPage extends ConsumerWidget { Platform.isIOS ? CupertinoIcons.paperplane : Icons.keyboard_return, color: theme.buttonPrimary, ), - title: 'Send on Enter', - subtitle: - 'Enter sends (soft keyboard). Cmd/Ctrl+Enter also available', + title: l10n.sendOnEnter, + subtitle: l10n.sendOnEnterDescription, trailing: Switch.adaptive( value: settings.sendOnEnter, onChanged: (value) => diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 903750f..79cf638 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -127,12 +127,24 @@ "onboardQuickBullet2": "Neuer Chat starten oder Modelle oben verwalten" , "addAttachment": "Anhang hinzufügen", + "attachmentLabel": "Anhang", "tools": "Werkzeuge", "voiceInput": "Spracheingabe", + "voice": "Sprache", + "voiceStatusListening": "Hört zu…", + "voiceStatusRecording": "Nimmt auf…", + "voiceHoldToTalk": "Zum Sprechen halten", + "voiceAutoSend": "Automatisch senden", + "voiceTranscript": "Transkript", + "voicePromptSpeakNow": "Jetzt sprechen…", + "voicePromptTapStart": "Tippe auf \"Starten\", um zu beginnen", + "voiceActionStop": "Stopp", + "voiceActionStart": "Starten", "messageInputLabel": "Nachrichteneingabe", "messageInputHint": "Nachricht eingeben", "messageHintText": "Nachricht...", "stopGenerating": "Generierung stoppen", + "codeCopiedToClipboard": "Code in die Zwischenablage kopiert.", "send": "Senden", "sendMessage": "Nachricht senden", "file": "Datei", @@ -289,6 +301,9 @@ "appCustomization": "App-Anpassung", "appCustomizationSubtitle": "Personalisieren, wie Namen und UI angezeigt werden", "quickActionsDescription": "Wähle bis zu zwei Schnellzugriffe, die neben dem Eingabefeld angepinnt werden", + "chatSettings": "Chat", + "sendOnEnter": "Mit Enter senden", + "sendOnEnterDescription": "Enter sendet (Soft-Tastatur). Cmd/Ctrl+Enter ebenfalls verfügbar", "display": "Anzeige", "realtime": "Echtzeit", "transportMode": "Transportmodus", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index bd1e705..2f93113 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -273,10 +273,52 @@ , "addAttachment": "Add attachment", "@addAttachment": {"description": "Button to add an attachment (file/photo)."}, + "attachmentLabel": "Attachment", + "@attachmentLabel": { + "description": "Label shown beside attachment chips in messages." + }, "tools": "Tools", "@tools": {"description": "Header for a tools/actions section."}, "voiceInput": "Voice input", "@voiceInput": {"description": "Label for voice input feature."}, + "voice": "Voice", + "@voice": {"description": "Title for the voice input bottom sheet."}, + "voiceStatusListening": "Listening…", + "@voiceStatusListening": { + "description": "Indicates the app is actively listening during voice input." + }, + "voiceStatusRecording": "Recording…", + "@voiceStatusRecording": { + "description": "Indicates the app is recording audio for speech recognition." + }, + "voiceHoldToTalk": "Hold to talk", + "@voiceHoldToTalk": { + "description": "Toggle label for hold-to-talk mode in voice input." + }, + "voiceAutoSend": "Auto-send", + "@voiceAutoSend": { + "description": "Toggle label for automatically sending the final transcript." + }, + "voiceTranscript": "Transcript", + "@voiceTranscript": { + "description": "Label above the transcribed voice input text." + }, + "voicePromptSpeakNow": "Speak now…", + "@voicePromptSpeakNow": { + "description": "Placeholder prompting the user to start speaking." + }, + "voicePromptTapStart": "Tap Start to begin", + "@voicePromptTapStart": { + "description": "Placeholder instructing the user to tap Start to begin recording." + }, + "voiceActionStop": "Stop", + "@voiceActionStop": { + "description": "Button label to stop voice recording." + }, + "voiceActionStart": "Start", + "@voiceActionStart": { + "description": "Button label to start voice recording." + }, "messageInputLabel": "Message input", "@messageInputLabel": {"description": "Accessibility label for the message input."}, "messageInputHint": "Type your message", @@ -287,6 +329,10 @@ "@stopGenerating": {"description": "Action to stop the assistant's response generation."}, "send": "Send", "@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."}, "file": "File", @@ -580,6 +626,14 @@ "@appCustomizationSubtitle": {"description": "Subtitle shown under App Customization tile and page header."}, "quickActionsDescription": "Pick up to two shortcuts to pin near the composer", "@quickActionsDescription": {"description": "Helper text explaining quick action pill selection in customization."}, + "chatSettings": "Chat", + "@chatSettings": {"description": "Section header for chat-related customization options."}, + "sendOnEnter": "Send on 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." + }, "display": "Display", "@display": {"description": "Section header for visual and layout related settings."}, "realtime": "Realtime", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index f4b5a18..7468ca9 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -127,12 +127,24 @@ "onboardQuickBullet2": "Lancez Nouveau chat ou gérez les modèles depuis la barre" , "addAttachment": "Ajouter une pièce jointe", + "attachmentLabel": "Pièce jointe", "tools": "Outils", "voiceInput": "Entrée vocale", + "voice": "Voix", + "voiceStatusListening": "Écoute…", + "voiceStatusRecording": "Enregistrement…", + "voiceHoldToTalk": "Maintenir pour parler", + "voiceAutoSend": "Envoi automatique", + "voiceTranscript": "Transcription", + "voicePromptSpeakNow": "Parlez maintenant…", + "voicePromptTapStart": "Appuyez sur \"Démarrer\" pour commencer", + "voiceActionStop": "Arrêter", + "voiceActionStart": "Démarrer", "messageInputLabel": "Saisie du message", "messageInputHint": "Saisissez votre message", "messageHintText": "Message...", "stopGenerating": "Arrêter la génération", + "codeCopiedToClipboard": "Code copié dans le presse-papiers.", "send": "Envoyer", "sendMessage": "Envoyer le message", "file": "Fichier", @@ -289,6 +301,9 @@ "appCustomization": "Personnalisation de l'app", "appCustomizationSubtitle": "Personnalisez l'affichage des noms et de l'UI", "quickActionsDescription": "Choisissez jusqu'à deux raccourcis à épingler près du champ de saisie", + "chatSettings": "Discussion", + "sendOnEnter": "Envoyer avec Entrée", + "sendOnEnterDescription": "Entrée envoie (clavier logiciel). Cmd/Ctrl+Entrée aussi disponible", "display": "Affichage", "realtime": "Temps réel", "transportMode": "Mode de transport", diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb index 58ff905..51bf6b7 100644 --- a/lib/l10n/app_it.arb +++ b/lib/l10n/app_it.arb @@ -127,12 +127,24 @@ "onboardQuickBullet2": "Avvia Nuova chat o gestisci i modelli dalla barra" , "addAttachment": "Aggiungi allegato", + "attachmentLabel": "Allegato", "tools": "Strumenti", "voiceInput": "Input vocale", + "voice": "Voce", + "voiceStatusListening": "In ascolto…", + "voiceStatusRecording": "Registrazione…", + "voiceHoldToTalk": "Tieni premuto per parlare", + "voiceAutoSend": "Invio automatico", + "voiceTranscript": "Trascrizione", + "voicePromptSpeakNow": "Parla ora…", + "voicePromptTapStart": "Tocca \"Avvia\" per iniziare", + "voiceActionStop": "Stop", + "voiceActionStart": "Avvia", "messageInputLabel": "Input messaggio", "messageInputHint": "Scrivi il tuo messaggio", "messageHintText": "Messaggio...", "stopGenerating": "Interrompi generazione", + "codeCopiedToClipboard": "Codice copiato negli appunti.", "send": "Invia", "sendMessage": "Invia messaggio", "file": "File", @@ -289,6 +301,9 @@ "appCustomization": "Personalizzazione app", "appCustomizationSubtitle": "Personalizza la visualizzazione dei nomi e dell'UI", "quickActionsDescription": "Scegli fino a due scorciatoie da fissare vicino al campo di input", + "chatSettings": "Chat", + "sendOnEnter": "Invia con Invio", + "sendOnEnterDescription": "Invio invia (tastiera software). Cmd/Ctrl+Invio disponibile", "display": "Schermo", "realtime": "Tempo reale", "transportMode": "Modalità di trasporto", diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 401c635..becc466 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -804,6 +804,12 @@ abstract class AppLocalizations { /// **'Add attachment'** String get addAttachment; + /// Label shown beside attachment chips in messages. + /// + /// In en, this message translates to: + /// **'Attachment'** + String get attachmentLabel; + /// Header for a tools/actions section. /// /// In en, this message translates to: @@ -816,6 +822,66 @@ abstract class AppLocalizations { /// **'Voice input'** String get voiceInput; + /// Title for the voice input bottom sheet. + /// + /// In en, this message translates to: + /// **'Voice'** + String get voice; + + /// Indicates the app is actively listening during voice input. + /// + /// In en, this message translates to: + /// **'Listening…'** + String get voiceStatusListening; + + /// Indicates the app is recording audio for speech recognition. + /// + /// In en, this message translates to: + /// **'Recording…'** + String get voiceStatusRecording; + + /// Toggle label for hold-to-talk mode in voice input. + /// + /// In en, this message translates to: + /// **'Hold to talk'** + String get voiceHoldToTalk; + + /// Toggle label for automatically sending the final transcript. + /// + /// In en, this message translates to: + /// **'Auto-send'** + String get voiceAutoSend; + + /// Label above the transcribed voice input text. + /// + /// In en, this message translates to: + /// **'Transcript'** + String get voiceTranscript; + + /// Placeholder prompting the user to start speaking. + /// + /// In en, this message translates to: + /// **'Speak now…'** + String get voicePromptSpeakNow; + + /// Placeholder instructing the user to tap Start to begin recording. + /// + /// In en, this message translates to: + /// **'Tap Start to begin'** + String get voicePromptTapStart; + + /// Button label to stop voice recording. + /// + /// In en, this message translates to: + /// **'Stop'** + String get voiceActionStop; + + /// Button label to start voice recording. + /// + /// In en, this message translates to: + /// **'Start'** + String get voiceActionStart; + /// Accessibility label for the message input. /// /// In en, this message translates to: @@ -846,6 +912,12 @@ abstract class AppLocalizations { /// **'Send'** String get send; + /// Snack bar message confirming code was copied. + /// + /// In en, this message translates to: + /// **'Code copied to clipboard.'** + String get codeCopiedToClipboard; + /// Semantic label for sending a message. /// /// In en, this message translates to: @@ -1584,6 +1656,24 @@ abstract class AppLocalizations { /// **'Pick up to two shortcuts to pin near the composer'** String get quickActionsDescription; + /// Section header for chat-related customization options. + /// + /// In en, this message translates to: + /// **'Chat'** + String get chatSettings; + + /// Toggle title for sending messages when pressing Enter. + /// + /// In en, this message translates to: + /// **'Send on Enter'** + String get sendOnEnter; + + /// Explanation of how the Send on Enter toggle behaves. + /// + /// In en, this message translates to: + /// **'Enter sends (soft keyboard). Cmd/Ctrl+Enter also available'** + String get sendOnEnterDescription; + /// Section header for visual and layout related settings. /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 43b45dd..aa1370b 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -405,12 +405,45 @@ class AppLocalizationsDe extends AppLocalizations { @override String get addAttachment => 'Anhang hinzufügen'; + @override + String get attachmentLabel => 'Anhang'; + @override String get tools => 'Werkzeuge'; @override String get voiceInput => 'Spracheingabe'; + @override + String get voice => 'Sprache'; + + @override + String get voiceStatusListening => 'Hört zu…'; + + @override + String get voiceStatusRecording => 'Nimmt auf…'; + + @override + String get voiceHoldToTalk => 'Zum Sprechen halten'; + + @override + String get voiceAutoSend => 'Automatisch senden'; + + @override + String get voiceTranscript => 'Transkript'; + + @override + String get voicePromptSpeakNow => 'Jetzt sprechen…'; + + @override + String get voicePromptTapStart => 'Tippe auf \"Starten\", um zu beginnen'; + + @override + String get voiceActionStop => 'Stopp'; + + @override + String get voiceActionStart => 'Starten'; + @override String get messageInputLabel => 'Nachrichteneingabe'; @@ -426,6 +459,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get send => 'Senden'; + @override + String get codeCopiedToClipboard => 'Code in die Zwischenablage kopiert.'; + @override String get sendMessage => 'Nachricht senden'; @@ -829,6 +865,16 @@ class AppLocalizationsDe extends AppLocalizations { String get quickActionsDescription => 'Wähle bis zu zwei Schnellzugriffe, die neben dem Eingabefeld angepinnt werden'; + @override + String get chatSettings => 'Chat'; + + @override + String get sendOnEnter => 'Mit Enter senden'; + + @override + String get sendOnEnterDescription => + 'Enter sendet (Soft-Tastatur). Cmd/Ctrl+Enter ebenfalls verfügbar'; + @override String get display => 'Anzeige'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 0f2c61d..02f032a 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -400,12 +400,45 @@ class AppLocalizationsEn extends AppLocalizations { @override String get addAttachment => 'Add attachment'; + @override + String get attachmentLabel => 'Attachment'; + @override String get tools => 'Tools'; @override String get voiceInput => 'Voice input'; + @override + String get voice => 'Voice'; + + @override + String get voiceStatusListening => 'Listening…'; + + @override + String get voiceStatusRecording => 'Recording…'; + + @override + String get voiceHoldToTalk => 'Hold to talk'; + + @override + String get voiceAutoSend => 'Auto-send'; + + @override + String get voiceTranscript => 'Transcript'; + + @override + String get voicePromptSpeakNow => 'Speak now…'; + + @override + String get voicePromptTapStart => 'Tap Start to begin'; + + @override + String get voiceActionStop => 'Stop'; + + @override + String get voiceActionStart => 'Start'; + @override String get messageInputLabel => 'Message input'; @@ -421,6 +454,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get send => 'Send'; + @override + String get codeCopiedToClipboard => 'Code copied to clipboard.'; + @override String get sendMessage => 'Send message'; @@ -822,6 +858,16 @@ class AppLocalizationsEn extends AppLocalizations { String get quickActionsDescription => 'Pick up to two shortcuts to pin near the composer'; + @override + String get chatSettings => 'Chat'; + + @override + String get sendOnEnter => 'Send on Enter'; + + @override + String get sendOnEnterDescription => + 'Enter sends (soft keyboard). Cmd/Ctrl+Enter also available'; + @override String get display => 'Display'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index a9f8fdd..da42ca8 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -410,12 +410,45 @@ class AppLocalizationsFr extends AppLocalizations { @override String get addAttachment => 'Ajouter une pièce jointe'; + @override + String get attachmentLabel => 'Pièce jointe'; + @override String get tools => 'Outils'; @override String get voiceInput => 'Entrée vocale'; + @override + String get voice => 'Voix'; + + @override + String get voiceStatusListening => 'Écoute…'; + + @override + String get voiceStatusRecording => 'Enregistrement…'; + + @override + String get voiceHoldToTalk => 'Maintenir pour parler'; + + @override + String get voiceAutoSend => 'Envoi automatique'; + + @override + String get voiceTranscript => 'Transcription'; + + @override + String get voicePromptSpeakNow => 'Parlez maintenant…'; + + @override + String get voicePromptTapStart => 'Appuyez sur \"Démarrer\" pour commencer'; + + @override + String get voiceActionStop => 'Arrêter'; + + @override + String get voiceActionStart => 'Démarrer'; + @override String get messageInputLabel => 'Saisie du message'; @@ -431,6 +464,9 @@ class AppLocalizationsFr extends AppLocalizations { @override String get send => 'Envoyer'; + @override + String get codeCopiedToClipboard => 'Code copié dans le presse-papiers.'; + @override String get sendMessage => 'Envoyer le message'; @@ -837,6 +873,16 @@ class AppLocalizationsFr extends AppLocalizations { String get quickActionsDescription => 'Choisissez jusqu\'à deux raccourcis à épingler près du champ de saisie'; + @override + String get chatSettings => 'Discussion'; + + @override + String get sendOnEnter => 'Envoyer avec Entrée'; + + @override + String get sendOnEnterDescription => + 'Entrée envoie (clavier logiciel). Cmd/Ctrl+Entrée aussi disponible'; + @override String get display => 'Affichage'; diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart index 65914a4..a8e9395 100644 --- a/lib/l10n/app_localizations_it.dart +++ b/lib/l10n/app_localizations_it.dart @@ -402,12 +402,45 @@ class AppLocalizationsIt extends AppLocalizations { @override String get addAttachment => 'Aggiungi allegato'; + @override + String get attachmentLabel => 'Allegato'; + @override String get tools => 'Strumenti'; @override String get voiceInput => 'Input vocale'; + @override + String get voice => 'Voce'; + + @override + String get voiceStatusListening => 'In ascolto…'; + + @override + String get voiceStatusRecording => 'Registrazione…'; + + @override + String get voiceHoldToTalk => 'Tieni premuto per parlare'; + + @override + String get voiceAutoSend => 'Invio automatico'; + + @override + String get voiceTranscript => 'Trascrizione'; + + @override + String get voicePromptSpeakNow => 'Parla ora…'; + + @override + String get voicePromptTapStart => 'Tocca \"Avvia\" per iniziare'; + + @override + String get voiceActionStop => 'Stop'; + + @override + String get voiceActionStart => 'Avvia'; + @override String get messageInputLabel => 'Input messaggio'; @@ -423,6 +456,9 @@ class AppLocalizationsIt extends AppLocalizations { @override String get send => 'Invia'; + @override + String get codeCopiedToClipboard => 'Codice copiato negli appunti.'; + @override String get sendMessage => 'Invia messaggio'; @@ -826,6 +862,16 @@ class AppLocalizationsIt extends AppLocalizations { String get quickActionsDescription => 'Scegli fino a due scorciatoie da fissare vicino al campo di input'; + @override + String get chatSettings => 'Chat'; + + @override + String get sendOnEnter => 'Invia con Invio'; + + @override + String get sendOnEnterDescription => + 'Invio invia (tastiera software). Cmd/Ctrl+Invio disponibile'; + @override String get display => 'Schermo'; diff --git a/lib/shared/widgets/markdown/markdown_config.dart b/lib/shared/widgets/markdown/markdown_config.dart index 9885b68..8432643 100644 --- a/lib/shared/widgets/markdown/markdown_config.dart +++ b/lib/shared/widgets/markdown/markdown_config.dart @@ -11,6 +11,8 @@ import 'package:flutter_math_fork/flutter_math.dart'; import 'package:markdown/markdown.dart' as md; import 'package:webview_flutter/webview_flutter.dart'; +import 'package:conduit/l10n/app_localizations.dart'; + import '../../theme/color_tokens.dart'; import '../../theme/theme_extensions.dart'; import 'code_block_header.dart'; @@ -76,18 +78,10 @@ class ConduitMarkdown { return MarkdownStyleSheet( p: baseBody, - h1: AppTypography.headlineLargeStyle.copyWith( - color: theme.textPrimary, - ), - h2: AppTypography.headlineMediumStyle.copyWith( - color: theme.textPrimary, - ), - h3: AppTypography.headlineSmallStyle.copyWith( - color: theme.textPrimary, - ), - h4: AppTypography.bodyLargeStyle.copyWith( - color: theme.textPrimary, - ), + h1: AppTypography.headlineLargeStyle.copyWith(color: theme.textPrimary), + h2: AppTypography.headlineMediumStyle.copyWith(color: theme.textPrimary), + h3: AppTypography.headlineSmallStyle.copyWith(color: theme.textPrimary), + h4: AppTypography.bodyLargeStyle.copyWith(color: theme.textPrimary), h5: baseBody.copyWith(fontWeight: FontWeight.w600), h6: secondaryBody, a: baseBody.copyWith( @@ -122,7 +116,10 @@ class ConduitMarkdown { listIndent: Spacing.lg, tableHead: secondaryBody.copyWith(fontWeight: FontWeight.w600), tableBody: secondaryBody, - tableBorder: TableBorder.all(color: borderColor, width: BorderWidth.micro), + tableBorder: TableBorder.all( + color: borderColor, + width: BorderWidth.micro, + ), tableHeadAlign: TextAlign.start, tableColumnWidth: const FlexColumnWidth(), tableCellsPadding: const EdgeInsets.symmetric( @@ -131,10 +128,7 @@ class ConduitMarkdown { ), horizontalRuleDecoration: BoxDecoration( border: Border( - top: BorderSide( - color: theme.dividerColor, - width: BorderWidth.small, - ), + top: BorderSide(color: theme.dividerColor, width: BorderWidth.small), ), ), ); @@ -153,9 +147,7 @@ class ConduitMarkdown { } static List _buildInlineSyntaxes() { - return [ - _LatexInlineSyntax(), - ]; + return [_LatexInlineSyntax()]; } static Widget buildMermaidBlock(BuildContext context, String code) { @@ -243,7 +235,9 @@ class ConduitMarkdown { textAlign: TextAlign.left, textDirection: TextDirection.ltr, textWidthBasis: TextWidthBasis.parent, - style: AppTypography.codeStyle.copyWith(color: conduitTheme.codeText), + style: AppTypography.codeStyle.copyWith( + color: conduitTheme.codeText, + ), ), ], ), @@ -279,8 +273,12 @@ class _CodeBlockBuilder extends MarkdownElementBuilder { final theme = context.conduitTheme; final isDark = Theme.of(context).brightness == Brightness.dark; final code = element.textContent; - final language = element.attributes['class']?.replaceFirst('language-', '') ?? 'plaintext'; - final normalizedLanguage = language.trim().isEmpty ? 'plaintext' : language.trim(); + final language = + element.attributes['class']?.replaceFirst('language-', '') ?? + 'plaintext'; + final normalizedLanguage = language.trim().isEmpty + ? 'plaintext' + : language.trim(); // Match GitHub/Atom theme colors for code block container final codeBackground = isDark @@ -306,9 +304,12 @@ class _CodeBlockBuilder extends MarkdownElementBuilder { return; } ScaffoldMessenger.of(context).hideCurrentSnackBar(); + final l10n = AppLocalizations.of(context); ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Code copied to clipboard.'), + SnackBar( + content: Text( + l10n?.codeCopiedToClipboard ?? 'Code copied to clipboard.', + ), ), ); }, @@ -365,7 +366,10 @@ class _ImageBuilder extends MarkdownElementBuilder { try { final commaIndex = dataUrl.indexOf(','); if (commaIndex == -1) { - throw const FormatException('Invalid data URL format'); + throw FormatException( + AppLocalizations.of(context)?.invalidDataUrl ?? + 'Invalid data URL format', + ); } final base64String = dataUrl.substring(commaIndex + 1); @@ -421,10 +425,7 @@ class _ImageBuilder extends MarkdownElementBuilder { ); } - Widget _buildImageError( - BuildContext context, - ConduitThemeExtension theme, - ) { + Widget _buildImageError(BuildContext context, ConduitThemeExtension theme) { return Container( height: 120, decoration: BoxDecoration( @@ -467,9 +468,8 @@ class _LatexBuilder extends MarkdownElementBuilder { final content = element.textContent.trim(); final isInline = element.attributes['isInline'] == 'true'; - final baseStyle = (preferredStyle ?? AppTypography.bodyMediumStyle).copyWith( - color: isDark ? Colors.white : Colors.black, - ); + final baseStyle = (preferredStyle ?? AppTypography.bodyMediumStyle) + .copyWith(color: isDark ? Colors.white : Colors.black); if (content.isEmpty) { return Text(element.textContent, style: baseStyle); @@ -499,9 +499,9 @@ class _LatexBuilder extends MarkdownElementBuilder { // LaTeX inline syntax class _LatexInlineSyntax extends md.InlineSyntax { _LatexInlineSyntax() - : super( - r'(\$\$[\s\S]+?\$\$)|(\$[^\n]+?\$)|(\\\([\s\S]+?\\\))|(\\\[[\s\S]+?\\\])', - ); + : super( + r'(\$\$[\s\S]+?\$\$)|(\$[^\n]+?\$)|(\\\([\s\S]+?\\\))|(\\\[[\s\S]+?\\\])', + ); @override bool onMatch(md.InlineParser parser, Match match) { diff --git a/lib/shared/widgets/themed_dialogs.dart b/lib/shared/widgets/themed_dialogs.dart index 6431a52..118447e 100644 --- a/lib/shared/widgets/themed_dialogs.dart +++ b/lib/shared/widgets/themed_dialogs.dart @@ -1,4 +1,7 @@ import 'package:flutter/material.dart'; + +import 'package:conduit/l10n/app_localizations.dart'; + import '../theme/theme_extensions.dart'; /// Centralized helper for building themed dialogs consistently @@ -31,11 +34,14 @@ class ThemedDialogs { BuildContext context, { required String title, required String message, - String confirmText = 'Confirm', - String cancelText = 'Cancel', + String? confirmText, + String? cancelText, bool isDestructive = false, bool barrierDismissible = true, }) async { + final l10n = AppLocalizations.of(context); + final effectiveConfirmText = confirmText ?? l10n?.confirm ?? 'Confirm'; + final effectiveCancelText = cancelText ?? l10n?.cancel ?? 'Cancel'; final result = await showDialog( context: context, barrierDismissible: barrierDismissible, @@ -50,7 +56,7 @@ class ThemedDialogs { TextButton( onPressed: () => Navigator.of(ctx).pop(false), child: Text( - cancelText, + effectiveCancelText, style: TextStyle(color: ctx.conduitTheme.textSecondary), ), ), @@ -62,7 +68,7 @@ class ThemedDialogs { : ctx.conduitTheme.buttonPrimary, ), child: Text( - confirmText, + effectiveConfirmText, style: TextStyle( color: isDestructive ? ctx.conduitTheme.error @@ -103,8 +109,8 @@ class ThemedDialogs { required String title, required String hintText, String? initialValue, - String confirmText = 'Save', - String cancelText = 'Cancel', + String? confirmText, + String? cancelText, bool barrierDismissible = true, TextInputType? keyboardType, TextCapitalization textCapitalization = TextCapitalization.sentences, @@ -112,6 +118,9 @@ class ThemedDialogs { }) async { final theme = context.conduitTheme; final controller = TextEditingController(text: initialValue ?? ''); + final l10n = AppLocalizations.of(context); + final effectiveConfirmText = confirmText ?? l10n?.save ?? 'Save'; + final effectiveCancelText = cancelText ?? l10n?.cancel ?? 'Cancel'; String? result = await showDialog( context: context, @@ -169,7 +178,7 @@ class ThemedDialogs { TextButton( onPressed: () => Navigator.of(ctx).pop(), child: Text( - cancelText, + effectiveCancelText, style: TextStyle(color: theme.textSecondary), ), ), @@ -185,7 +194,7 @@ class ThemedDialogs { ? () => Navigator.of(ctx).pop(trimmed) : null, child: Text( - confirmText, + effectiveConfirmText, style: TextStyle( color: enabled ? theme.buttonPrimary