feat(l10n): Update English localization with voice call states and model capabilities
This commit is contained in:
@@ -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,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -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<VoiceCallPage>
|
||||
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<VoiceCallPage>
|
||||
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<VoiceCallPage>
|
||||
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<VoiceCallPage>
|
||||
// Control buttons
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(32),
|
||||
child: _buildControlButtons(primaryColor),
|
||||
child: _buildControlButtons(primaryColor, l10n),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -345,7 +347,7 @@ class _VoiceCallPageState extends ConsumerState<VoiceCallPage>
|
||||
);
|
||||
}
|
||||
|
||||
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<VoiceCallPage>
|
||||
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<VoiceCallPage>
|
||||
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<VoiceCallPage>
|
||||
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<VoiceCallPage>
|
||||
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<VoiceCallPage>
|
||||
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<VoiceCallPage>
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1162,7 +1162,7 @@ class _AssistantMessageWidgetState extends ConsumerState<AssistantMessageWidget>
|
||||
// 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<AssistantMessageWidget>
|
||||
),
|
||||
ChatActionButton(
|
||||
icon: Icons.chevron_right,
|
||||
label: 'Next',
|
||||
label: l10n.nextLabel,
|
||||
onTap: () {
|
||||
setState(() {
|
||||
if (_activeVersionIndex < 0) return; // already live
|
||||
|
||||
@@ -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,
|
||||
@@ -501,7 +502,7 @@ class AppCustomizationPage extends ConsumerWidget {
|
||||
),
|
||||
const SizedBox(width: Spacing.sm),
|
||||
Text(
|
||||
'Engine',
|
||||
l10n.ttsEngineLabel,
|
||||
style:
|
||||
theme.bodyMedium?.copyWith(
|
||||
color: theme.sidebarForeground,
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user