feat: localisation with en, de, fr and it
This commit is contained in:
@@ -6,6 +6,7 @@ import 'api_error_handler.dart';
|
||||
import 'api_error_interceptor.dart';
|
||||
import '../../shared/theme/app_theme.dart';
|
||||
import '../../shared/theme/theme_extensions.dart';
|
||||
import 'package:conduit/l10n/app_localizations.dart';
|
||||
|
||||
/// Enhanced error service with comprehensive error handling capabilities
|
||||
/// Provides unified error management across the application
|
||||
@@ -136,8 +137,8 @@ class EnhancedErrorService {
|
||||
action: isRetryableError && onRetry != null
|
||||
? SnackBarAction(
|
||||
label: retryDelay != null && retryDelay.inSeconds > 5
|
||||
? 'Retry (${retryDelay.inSeconds}s)'
|
||||
: 'Retry',
|
||||
? "${AppLocalizations.of(context)!.retry} (${retryDelay.inSeconds}s)"
|
||||
: AppLocalizations.of(context)!.retry,
|
||||
textColor: AppTheme.neutral50,
|
||||
onPressed: onRetry,
|
||||
)
|
||||
@@ -208,14 +209,14 @@ class EnhancedErrorService {
|
||||
Navigator.of(context).pop();
|
||||
onRetry();
|
||||
},
|
||||
child: const Text('Retry'),
|
||||
child: Text(AppLocalizations.of(context)!.retry),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
onDismiss?.call();
|
||||
},
|
||||
child: const Text('OK'),
|
||||
child: Text(AppLocalizations.of(context)!.ok),
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -281,7 +282,7 @@ class EnhancedErrorService {
|
||||
ElevatedButton.icon(
|
||||
onPressed: onRetry,
|
||||
icon: const Icon(Icons.refresh),
|
||||
label: const Text('Try Again'),
|
||||
label: const Text('Retry'),
|
||||
),
|
||||
],
|
||||
],
|
||||
|
||||
@@ -79,6 +79,36 @@ class ThemeModeNotifier extends StateNotifier<ThemeMode> {
|
||||
}
|
||||
}
|
||||
|
||||
// Locale provider
|
||||
final localeProvider = StateNotifierProvider<LocaleNotifier, Locale?>(
|
||||
(ref) {
|
||||
final storage = ref.watch(optimizedStorageServiceProvider);
|
||||
return LocaleNotifier(storage);
|
||||
},
|
||||
);
|
||||
|
||||
class LocaleNotifier extends StateNotifier<Locale?> {
|
||||
final OptimizedStorageService _storage;
|
||||
|
||||
LocaleNotifier(this._storage) : super(null) {
|
||||
_loadLocale();
|
||||
}
|
||||
|
||||
void _loadLocale() {
|
||||
final code = _storage.getLocaleCode();
|
||||
if (code != null && code.isNotEmpty) {
|
||||
state = Locale(code);
|
||||
} else {
|
||||
state = null; // system
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setLocale(Locale? locale) async {
|
||||
state = locale;
|
||||
await _storage.setLocaleCode(locale?.languageCode);
|
||||
}
|
||||
}
|
||||
|
||||
// Server connection providers - optimized with caching
|
||||
final serverConfigsProvider = FutureProvider<List<ServerConfig>>((ref) async {
|
||||
final storage = ref.watch(optimizedStorageServiceProvider);
|
||||
|
||||
@@ -23,6 +23,7 @@ class OptimizedStorageService {
|
||||
static const String _activeServerIdKey = 'active_server_id';
|
||||
static const String _rememberCredentialsKey = 'remember_credentials';
|
||||
static const String _themeModeKey = 'theme_mode';
|
||||
static const String _localeCodeKey = 'locale_code_v1';
|
||||
static const String _localConversationsKey = 'local_conversations';
|
||||
static const String _onboardingSeenKey = 'onboarding_seen_v1';
|
||||
static const String _reviewerModeKey = 'reviewer_mode_v1';
|
||||
@@ -226,6 +227,20 @@ class OptimizedStorageService {
|
||||
await _prefs.setString(_themeModeKey, mode);
|
||||
}
|
||||
|
||||
/// Locale Management
|
||||
String? getLocaleCode() {
|
||||
// Returns a locale code like 'en', 'de', 'fr', 'it'. Null means system.
|
||||
return _prefs.getString(_localeCodeKey);
|
||||
}
|
||||
|
||||
Future<void> setLocaleCode(String? code) async {
|
||||
if (code == null || code.isEmpty) {
|
||||
await _prefs.remove(_localeCodeKey);
|
||||
} else {
|
||||
await _prefs.setString(_localeCodeKey, code);
|
||||
}
|
||||
}
|
||||
|
||||
/// Onboarding
|
||||
Future<bool> getOnboardingSeen() async {
|
||||
return _prefs.getBool(_onboardingSeenKey) ?? false;
|
||||
|
||||
@@ -182,12 +182,12 @@ class UserFriendlyErrorHandler {
|
||||
List<ErrorRecoveryAction> _getServerRecoveryActions() {
|
||||
return [
|
||||
ErrorRecoveryAction(
|
||||
label: 'Try Again',
|
||||
label: 'Retry',
|
||||
action: ErrorActionType.retry,
|
||||
description: 'Retry your request',
|
||||
),
|
||||
ErrorRecoveryAction(
|
||||
label: 'Wait & Retry',
|
||||
label: 'Retry',
|
||||
action: ErrorActionType.retryLater,
|
||||
description: 'Wait a moment then try again',
|
||||
),
|
||||
@@ -223,7 +223,7 @@ class UserFriendlyErrorHandler {
|
||||
description: 'Sign in to your account',
|
||||
),
|
||||
ErrorRecoveryAction(
|
||||
label: 'Try Again',
|
||||
label: 'Retry',
|
||||
action: ErrorActionType.retry,
|
||||
description: 'Retry the request',
|
||||
),
|
||||
@@ -280,7 +280,7 @@ class UserFriendlyErrorHandler {
|
||||
description: 'Select another file',
|
||||
),
|
||||
ErrorRecoveryAction(
|
||||
label: 'Try Again',
|
||||
label: 'Retry',
|
||||
action: ErrorActionType.retry,
|
||||
description: 'Retry the operation',
|
||||
),
|
||||
@@ -314,7 +314,7 @@ class UserFriendlyErrorHandler {
|
||||
description: 'Open app settings to grant permissions',
|
||||
),
|
||||
ErrorRecoveryAction(
|
||||
label: 'Try Again',
|
||||
label: 'Retry',
|
||||
action: ErrorActionType.retry,
|
||||
description: 'Retry after granting permission',
|
||||
),
|
||||
@@ -324,7 +324,7 @@ class UserFriendlyErrorHandler {
|
||||
List<ErrorRecoveryAction> _getGenericRecoveryActions() {
|
||||
return [
|
||||
ErrorRecoveryAction(
|
||||
label: 'Try Again',
|
||||
label: 'Retry',
|
||||
action: ErrorActionType.retry,
|
||||
description: 'Retry the operation',
|
||||
),
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import '../../shared/theme/theme_extensions.dart';
|
||||
import '../error/enhanced_error_service.dart';
|
||||
import 'package:conduit/l10n/app_localizations.dart';
|
||||
|
||||
/// Error boundary widget that catches and handles errors in child widgets
|
||||
class ErrorBoundary extends ConsumerStatefulWidget {
|
||||
@@ -122,7 +123,7 @@ class _ErrorBoundaryState extends ConsumerState<ErrorBoundary> {
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Something went wrong',
|
||||
AppLocalizations.of(context)!.errorMessage,
|
||||
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
|
||||
color: context.conduitTheme.textPrimary,
|
||||
),
|
||||
@@ -140,7 +141,7 @@ class _ErrorBoundaryState extends ConsumerState<ErrorBoundary> {
|
||||
FilledButton.icon(
|
||||
onPressed: _retry,
|
||||
icon: const Icon(Icons.refresh),
|
||||
label: const Text('Try Again'),
|
||||
label: Text(AppLocalizations.of(context)!.retry),
|
||||
),
|
||||
],
|
||||
],
|
||||
@@ -239,7 +240,7 @@ class AsyncErrorBoundary extends ConsumerWidget {
|
||||
(context as Element).markNeedsBuild();
|
||||
},
|
||||
icon: const Icon(Icons.refresh),
|
||||
label: const Text('Retry'),
|
||||
label: Text(AppLocalizations.of(context)!.retry),
|
||||
),
|
||||
],
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user