feat(l10n): Update English localization with voice call states and model capabilities

This commit is contained in:
cogwheel0
2025-11-02 17:44:23 +05:30
parent b648ae29ea
commit 8321e3c721
23 changed files with 6242 additions and 1662 deletions

View File

@@ -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<String>? 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<T>(
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,
),
);
},
);
}

View File

@@ -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<bool> 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,
);

View File

@@ -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<ErrorRecoveryAction> 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<ErrorRecoveryAction> _getNetworkRecoveryActions() {
List<ErrorRecoveryAction> _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<ErrorRecoveryAction> _getServerRecoveryActions() {
List<ErrorRecoveryAction> _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<ErrorRecoveryAction> _getAuthRecoveryActions() {
List<ErrorRecoveryAction> _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<ErrorRecoveryAction> _getFileRecoveryActions() {
List<ErrorRecoveryAction> _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<ErrorRecoveryAction> _getPermissionRecoveryActions() {
List<ErrorRecoveryAction> _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<ErrorRecoveryAction> _getGenericRecoveryActions() {
List<ErrorRecoveryAction> _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',
),
];
}