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.
This commit is contained in:
@@ -1673,11 +1673,13 @@ class _ChatPageState extends ConsumerState<ChatPage> {
|
||||
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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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<md.InlineSyntax> _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);
|
||||
|
||||
@@ -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<bool>(
|
||||
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<String>(
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user