From fa0e43885ac23b6e4ef9423ec3d4eeca017c7cd3 Mon Sep 17 00:00:00 2001 From: Deep Koluguri Date: Mon, 6 Apr 2026 00:08:34 -0400 Subject: [PATCH] added i8n --- luckychit/l10n.yaml | 6 + .../core/controllers/locale_controller.dart | 46 ++ .../app_components/state_view.dart | 17 +- luckychit/lib/core/services/auth_service.dart | 15 +- .../lib/core/services/chit_group_service.dart | 87 +-- .../lib/features/auth/views/login_screen.dart | 37 +- .../features/auth/views/signup_screen.dart | 385 +++++----- .../lib/features/settings/settings_page.dart | 359 +++++---- .../interfaces/manager/chit_groups_page.dart | 10 +- .../interfaces/manager/manager_dashboard.dart | 160 ++-- .../interfaces/member/member_dashboard.dart | 131 ++-- luckychit/lib/l10n/app_en.arb | 69 ++ luckychit/lib/l10n/app_localizations.dart | 694 +++++++++++++++++ luckychit/lib/l10n/app_localizations_en.dart | 706 +++++++++++++++++ luckychit/lib/l10n/app_localizations_te.dart | 714 ++++++++++++++++++ luckychit/lib/l10n/app_te.arb | 69 ++ luckychit/lib/l10n/l10n_x.dart | 26 + luckychit/lib/main.dart | 30 +- .../shared/widgets/empty_state_widget.dart | 56 +- luckychit/pubspec.lock | 5 + luckychit/pubspec.yaml | 2 + 21 files changed, 3029 insertions(+), 595 deletions(-) create mode 100644 luckychit/l10n.yaml create mode 100644 luckychit/lib/core/controllers/locale_controller.dart create mode 100644 luckychit/lib/l10n/app_en.arb create mode 100644 luckychit/lib/l10n/app_localizations.dart create mode 100644 luckychit/lib/l10n/app_localizations_en.dart create mode 100644 luckychit/lib/l10n/app_localizations_te.dart create mode 100644 luckychit/lib/l10n/app_te.arb create mode 100644 luckychit/lib/l10n/l10n_x.dart diff --git a/luckychit/l10n.yaml b/luckychit/l10n.yaml new file mode 100644 index 0000000..640a6c7 --- /dev/null +++ b/luckychit/l10n.yaml @@ -0,0 +1,6 @@ +arb-dir: lib/l10n +template-arb-file: app_en.arb +output-localization-file: app_localizations.dart +output-class: AppLocalizations +synthetic-package: false +output-dir: lib/l10n diff --git a/luckychit/lib/core/controllers/locale_controller.dart b/luckychit/lib/core/controllers/locale_controller.dart new file mode 100644 index 0000000..6fdbd4e --- /dev/null +++ b/luckychit/lib/core/controllers/locale_controller.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../../l10n/app_localizations.dart'; + +/// Persists UI language (English / Telugu) for [AppLocalizations]. +class LocaleController extends GetxController { + static LocaleController get to => Get.find(); + + static const String _prefKey = 'app_locale'; + + final Rx locale = const Locale('en').obs; + + /// Call from [main] after [WidgetsFlutterBinding.ensureInitialized]. + Future ensureLoaded() async { + final prefs = await SharedPreferences.getInstance(); + final code = prefs.getString(_prefKey); + if (code == 'te' || code == 'en') { + locale.value = Locale(code!); + } else { + locale.value = const Locale('en'); + } + } + + Future setLocale(Locale value) async { + if (value.languageCode != 'en' && value.languageCode != 'te') { + return; + } + locale.value = value; + final prefs = await SharedPreferences.getInstance(); + await prefs.setString(_prefKey, value.languageCode); + update(); + } + + /// Current choice label for settings subtitle (uses [l10n] for both languages). + String currentLanguageLabel(AppLocalizations l10n) { + switch (locale.value.languageCode) { + case 'te': + return l10n.languageTelugu; + case 'en': + default: + return l10n.languageEnglish; + } + } +} diff --git a/luckychit/lib/core/design_system/app_components/state_view.dart b/luckychit/lib/core/design_system/app_components/state_view.dart index 73a46da..5f77781 100644 --- a/luckychit/lib/core/design_system/app_components/state_view.dart +++ b/luckychit/lib/core/design_system/app_components/state_view.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import '../../../l10n/l10n_x.dart'; import '../../../shared/widgets/empty_state_widget.dart'; class StateView extends StatelessWidget { @@ -8,8 +9,8 @@ class StateView extends StatelessWidget { final bool isEmpty; final Widget child; final Widget? loading; - final String emptyTitle; - final String emptyMessage; + final String? emptyTitle; + final String? emptyMessage; final EmptyStateType emptyType; const StateView({ @@ -20,8 +21,8 @@ class StateView extends StatelessWidget { this.onRetry, this.isEmpty = false, this.loading, - this.emptyTitle = 'Nothing here yet', - this.emptyMessage = 'Try again later.', + this.emptyTitle, + this.emptyMessage, this.emptyType = EmptyStateType.noResults, }); @@ -35,6 +36,7 @@ class StateView extends StatelessWidget { } if (error != null) { + final l = context.l10n; return Center( child: Padding( padding: const EdgeInsets.all(24), @@ -44,7 +46,7 @@ class StateView extends StatelessWidget { Icon(Icons.error_outline, size: 44, color: Theme.of(context).colorScheme.error), const SizedBox(height: 12), Text( - 'Something went wrong', + l.stateSomethingWentWrong, style: Theme.of(context).textTheme.titleMedium, textAlign: TextAlign.center, ), @@ -60,7 +62,7 @@ class StateView extends StatelessWidget { const SizedBox(height: 16), FilledButton( onPressed: onRetry, - child: const Text('Retry'), + child: Text(l.retry), ) ] ], @@ -70,12 +72,13 @@ class StateView extends StatelessWidget { } if (isEmpty) { + final l = context.l10n; return Center( child: EmptyStateWidget( type: emptyType, customTitle: emptyTitle, customMessage: emptyMessage, - actionLabel: onRetry != null ? 'Retry' : null, + actionLabel: onRetry != null ? l.retry : null, onActionPressed: onRetry, ), ); diff --git a/luckychit/lib/core/services/auth_service.dart b/luckychit/lib/core/services/auth_service.dart index 033f2ab..b71b94e 100644 --- a/luckychit/lib/core/services/auth_service.dart +++ b/luckychit/lib/core/services/auth_service.dart @@ -3,6 +3,7 @@ import 'package:get/get.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../models/user.dart'; import 'api_service.dart'; +import '../../l10n/l10n_x.dart'; class AuthService extends GetxController { static AuthService get to => Get.find(); @@ -87,12 +88,12 @@ class AuthService extends GetxController { return true; } else { - Get.snackbar('Signup Failed', response['message']); + Get.snackbar(L10nSvc.of().signupFailedTitle, response['message']); return false; } } catch (e) { print('Signup error: $e'); - Get.snackbar('Error', 'Signup failed. Please try again.'); + Get.snackbar(L10nSvc.of().snackTitleError, L10nSvc.of().signupFailedGeneric); return false; } finally { _isLoading.value = false; @@ -118,12 +119,12 @@ class AuthService extends GetxController { return true; } else { - Get.snackbar('Login Failed', response['message']); + Get.snackbar(L10nSvc.of().loginFailedTitle, response['message']); return false; } } catch (e) { print('Login error: $e'); - Get.snackbar('Error', 'Login failed. Please try again.'); + Get.snackbar(L10nSvc.of().snackTitleError, L10nSvc.of().loginFailedGeneric); return false; } finally { _isLoading.value = false; @@ -159,15 +160,15 @@ class AuthService extends GetxController { final response = await _apiService.changePassword(currentPassword, newPassword); if (response['success']) { - Get.snackbar('Success', 'Password changed successfully'); + Get.snackbar(L10nSvc.of().snackTitleSuccess, L10nSvc.of().passwordChangedSuccess); return true; } else { - Get.snackbar('Failed', response['message']); + Get.snackbar(L10nSvc.of().snackTitleError, response['message']); return false; } } catch (e) { print('Change password error: $e'); - Get.snackbar('Error', 'Failed to change password'); + Get.snackbar(L10nSvc.of().snackTitleError, L10nSvc.of().failedChangePassword); return false; } finally { _isLoading.value = false; diff --git a/luckychit/lib/core/services/chit_group_service.dart b/luckychit/lib/core/services/chit_group_service.dart index 4f7bd43..abc473f 100644 --- a/luckychit/lib/core/services/chit_group_service.dart +++ b/luckychit/lib/core/services/chit_group_service.dart @@ -7,6 +7,7 @@ import '../models/monthly_draw.dart'; import '../models/financial_table_entry.dart'; import '../models/user.dart'; import 'api_service.dart'; +import '../../l10n/l10n_x.dart'; class ChitGroupService extends GetxController { static ChitGroupService get to => Get.find(); @@ -55,11 +56,11 @@ class ChitGroupService extends GetxController { print('Successfully loaded ${_chitGroups.length} chit groups'); } else { print('API returned error: ${response['message']}'); - Get.snackbar('Error', response['message']); + Get.snackbar(L10nSvc.of().snackTitleError, response['message']); } } catch (e) { print('Error loading manager chit groups: $e'); - Get.snackbar('Error', 'Failed to load chit groups'); + Get.snackbar(L10nSvc.of().snackTitleError, L10nSvc.of().failedLoadChitGroups); } finally { _isLoading.value = false; } @@ -76,11 +77,11 @@ class ChitGroupService extends GetxController { final groupsData = response['data']['chitGroups'] as List; _chitGroups.value = groupsData.map((json) => ChitGroup.fromJson(json)).toList(); } else { - Get.snackbar('Error', response['message']); + Get.snackbar(L10nSvc.of().snackTitleError, response['message']); } } catch (e) { print('Error loading member chit groups: $e'); - Get.snackbar('Error', 'Failed to load chit groups'); + Get.snackbar(L10nSvc.of().snackTitleError, L10nSvc.of().failedLoadChitGroups); } finally { _isLoading.value = false; } @@ -96,15 +97,15 @@ class ChitGroupService extends GetxController { if (response['success']) { final newGroup = ChitGroup.fromJson(response['data']); _chitGroups.add(newGroup); - Get.snackbar('Success', 'Chitfund created successfully'); + Get.snackbar(L10nSvc.of().snackTitleSuccess, L10nSvc.of().chitfundCreatedSuccess); return true; } else { - Get.snackbar('Error', response['message']); + Get.snackbar(L10nSvc.of().snackTitleError, response['message']); return false; } } catch (e) { print('Error creating chit group: $e'); - Get.snackbar('Error', 'Failed to create chit group'); + Get.snackbar(L10nSvc.of().snackTitleError, L10nSvc.of().failedCreateChitGroup); return false; } finally { _isLoading.value = false; @@ -122,12 +123,12 @@ class ChitGroupService extends GetxController { await loadChitGroupDetails(groupId); return true; } else { - Get.snackbar('Error', response['message']); + Get.snackbar(L10nSvc.of().snackTitleError, response['message']); return false; } } catch (e) { print('Error updating chit group: $e'); - Get.snackbar('Error', 'Failed to update chit group'); + Get.snackbar(L10nSvc.of().snackTitleError, L10nSvc.of().failedUpdateChitGroup); return false; } finally { _isLoading.value = false; @@ -143,15 +144,15 @@ class ChitGroupService extends GetxController { if (response['success']) { _chitGroups.removeWhere((g) => g.id == groupId); - Get.snackbar('Success', 'Chit group deleted successfully'); + Get.snackbar(L10nSvc.of().snackTitleSuccess, L10nSvc.of().chitGroupDeletedSuccess); return true; } else { - Get.snackbar('Error', response['message']); + Get.snackbar(L10nSvc.of().snackTitleError, response['message']); return false; } } catch (e) { print('Error deleting chit group: $e'); - Get.snackbar('Error', 'Failed to delete chit group'); + Get.snackbar(L10nSvc.of().snackTitleError, L10nSvc.of().failedDeleteChitGroup); return false; } finally { _isLoading.value = false; @@ -169,11 +170,11 @@ class ChitGroupService extends GetxController { final group = ChitGroup.fromJson(response['data']); _selectedGroup.value = group; } else { - Get.snackbar('Error', response['message']); + Get.snackbar(L10nSvc.of().snackTitleError, response['message']); } } catch (e) { print('Error loading chit group details: $e'); - Get.snackbar('Error', 'Failed to load group details'); + Get.snackbar(L10nSvc.of().snackTitleError, L10nSvc.of().failedLoadGroupDetails); } finally { _isLoading.value = false; } @@ -193,7 +194,7 @@ class ChitGroupService extends GetxController { if (showErrors) { // Use post frame callback to avoid showing snackbar during build WidgetsBinding.instance.addPostFrameCallback((_) { - Get.snackbar('Error', response['message']); + Get.snackbar(L10nSvc.of().snackTitleError, response['message']); }); } } @@ -202,7 +203,7 @@ class ChitGroupService extends GetxController { if (showErrors) { // Use post frame callback to avoid showing snackbar during build WidgetsBinding.instance.addPostFrameCallback((_) { - Get.snackbar('Error', 'Failed to load group members'); + Get.snackbar(L10nSvc.of().snackTitleError, L10nSvc.of().failedLoadGroupMembers); }); } } finally { @@ -220,15 +221,15 @@ class ChitGroupService extends GetxController { if (response['success']) { // Reload group members await loadGroupMembers(groupId); - Get.snackbar('Success', 'Member added successfully'); + Get.snackbar(L10nSvc.of().snackTitleSuccess, L10nSvc.of().memberAddedSuccess); return true; } else { - Get.snackbar('Error', response['message']); + Get.snackbar(L10nSvc.of().snackTitleError, response['message']); return false; } } catch (e) { print('Error adding member: $e'); - Get.snackbar('Error', 'Failed to add member'); + Get.snackbar(L10nSvc.of().snackTitleError, L10nSvc.of().failedAddMember); return false; } finally { _isLoading.value = false; @@ -245,15 +246,15 @@ class ChitGroupService extends GetxController { if (response['success']) { // Reload group members await loadGroupMembers(groupId); - Get.snackbar('Success', 'Member removed successfully'); + Get.snackbar(L10nSvc.of().snackTitleSuccess, L10nSvc.of().memberRemovedSuccess); return true; } else { - Get.snackbar('Error', response['message']); + Get.snackbar(L10nSvc.of().snackTitleError, response['message']); return false; } } catch (e) { print('Error removing member: $e'); - Get.snackbar('Error', 'Failed to remove member'); + Get.snackbar(L10nSvc.of().snackTitleError, L10nSvc.of().failedRemoveMember); return false; } finally { _isLoading.value = false; @@ -313,15 +314,15 @@ class ChitGroupService extends GetxController { if (response['success']) { // Reload group members await loadGroupMembers(groupId); - Get.snackbar('Success', 'Member status updated successfully'); + Get.snackbar(L10nSvc.of().snackTitleSuccess, L10nSvc.of().memberStatusUpdatedSuccess); return true; } else { - Get.snackbar('Error', response['message']); + Get.snackbar(L10nSvc.of().snackTitleError, response['message']); return false; } } catch (e) { print('Error updating member status: $e'); - Get.snackbar('Error', 'Failed to update member status'); + Get.snackbar(L10nSvc.of().snackTitleError, L10nSvc.of().failedUpdateMemberStatus); return false; } finally { _isLoading.value = false; @@ -339,11 +340,11 @@ class ChitGroupService extends GetxController { final paymentsData = response['data']['payments'] as List; _groupPayments.value = paymentsData.map((json) => Payment.fromJson(json)).toList(); } else { - Get.snackbar('Error', response['message']); + Get.snackbar(L10nSvc.of().snackTitleError, response['message']); } } catch (e) { print('Error loading group payments: $e'); - Get.snackbar('Error', 'Failed to load payments'); + Get.snackbar(L10nSvc.of().snackTitleError, L10nSvc.of().failedLoadPayments); } finally { _isLoading.value = false; } @@ -359,15 +360,15 @@ class ChitGroupService extends GetxController { if (response['success']) { // Reload group payments await loadGroupPayments(data['group_id']); - Get.snackbar('Success', 'Payment recorded successfully'); + Get.snackbar(L10nSvc.of().snackTitleSuccess, L10nSvc.of().paymentRecordedSuccess); return true; } else { - Get.snackbar('Error', response['message']); + Get.snackbar(L10nSvc.of().snackTitleError, response['message']); return false; } } catch (e) { print('Error recording payment: $e'); - Get.snackbar('Error', 'Failed to record payment'); + Get.snackbar(L10nSvc.of().snackTitleError, L10nSvc.of().failedRecordPayment); return false; } finally { _isLoading.value = false; @@ -384,11 +385,11 @@ class ChitGroupService extends GetxController { if (response['success']) { _groupStats.value = response['data']; } else { - Get.snackbar('Error', response['message']); + Get.snackbar(L10nSvc.of().snackTitleError, response['message']); } } catch (e) { print('Error loading group stats: $e'); - Get.snackbar('Error', 'Failed to load group statistics'); + Get.snackbar(L10nSvc.of().snackTitleError, L10nSvc.of().failedLoadGroupStatistics); } finally { _isLoading.value = false; } @@ -408,15 +409,15 @@ class ChitGroupService extends GetxController { final updatedGroup = ChitGroup.fromJson(response['data']); _chitGroups[index] = updatedGroup; } - Get.snackbar('Success', 'Chitfund started successfully'); + Get.snackbar(L10nSvc.of().snackTitleSuccess, L10nSvc.of().chitfundStartedSuccess); return true; } else { - Get.snackbar('Error', response['message']); + Get.snackbar(L10nSvc.of().snackTitleError, response['message']); return false; } } catch (e) { print('Error starting chit group: $e'); - Get.snackbar('Error', 'Failed to start chit group'); + Get.snackbar(L10nSvc.of().snackTitleError, L10nSvc.of().failedStartChitGroup); return false; } finally { _isLoading.value = false; @@ -450,11 +451,11 @@ class ChitGroupService extends GetxController { )) .toList(); } else { - Get.snackbar('Error', response['message']); + Get.snackbar(L10nSvc.of().snackTitleError, response['message']); } } catch (e) { print('Error loading group monthly draws: $e'); - Get.snackbar('Error', 'Failed to load monthly draws'); + Get.snackbar(L10nSvc.of().snackTitleError, L10nSvc.of().failedLoadMonthlyDraws); } finally { _isLoading.value = false; } @@ -499,14 +500,14 @@ class ChitGroupService extends GetxController { return {}; } else { WidgetsBinding.instance.addPostFrameCallback((_) { - Get.snackbar('Error', response['message']?.toString() ?? 'Failed'); + Get.snackbar(L10nSvc.of().snackTitleError, response['message']?.toString() ?? L10nSvc.of().operationFailedShort); }); return null; } } catch (e) { print('Error creating monthly draw: $e'); WidgetsBinding.instance.addPostFrameCallback((_) { - Get.snackbar('Error', 'Failed to create monthly draw'); + Get.snackbar(L10nSvc.of().snackTitleError, L10nSvc.of().failedCreateMonthlyDraw); }); return null; } finally { @@ -524,11 +525,11 @@ class ChitGroupService extends GetxController { if (response['success']) { _drawStats.value = response['data']; } else { - Get.snackbar('Error', response['message']); + Get.snackbar(L10nSvc.of().snackTitleError, response['message']); } } catch (e) { print('Error loading draw statistics: $e'); - Get.snackbar('Error', 'Failed to load draw statistics'); + Get.snackbar(L10nSvc.of().snackTitleError, L10nSvc.of().failedLoadDrawStatistics); } finally { _isLoading.value = false; } @@ -555,12 +556,12 @@ class ChitGroupService extends GetxController { _financialData.value = entries; } else { print('API returned error: ${response['message']}'); - Get.snackbar('Error', response['message']); + Get.snackbar(L10nSvc.of().snackTitleError, response['message']); } } catch (e) { print('Error loading group financial data: $e'); print('Error stack trace: ${e.toString()}'); - Get.snackbar('Error', 'Failed to load financial data'); + Get.snackbar(L10nSvc.of().snackTitleError, L10nSvc.of().failedLoadFinancialData); } finally { _isLoading.value = false; } diff --git a/luckychit/lib/features/auth/views/login_screen.dart b/luckychit/lib/features/auth/views/login_screen.dart index bb4c0a0..6962802 100644 --- a/luckychit/lib/features/auth/views/login_screen.dart +++ b/luckychit/lib/features/auth/views/login_screen.dart @@ -7,6 +7,7 @@ import '../../../core/design_system/app_components/auth_shell.dart'; import '../../../core/design_system/app_components/app_card.dart'; import '../../../core/design_system/app_spacing.dart'; import '../../../core/design_system/app_text.dart'; +import '../../../l10n/l10n_x.dart'; import 'signup_screen.dart'; class LoginScreen extends StatefulWidget { @@ -37,10 +38,11 @@ class _LoginScreenState extends State { _passwordController.text, ); - if (!success) { + if (!success && mounted) { + final l = context.l10n; SnackbarUtil.showError( - 'Invalid mobile number or password. Please try again.', - title: 'Login Failed', + l.loginInvalidCredentials, + title: l.loginFailedTitle, ); } } @@ -48,6 +50,7 @@ class _LoginScreenState extends State { @override Widget build(BuildContext context) { + final l = context.l10n; final scheme = Theme.of(context).colorScheme; return Scaffold( body: AuthShell( @@ -61,7 +64,7 @@ class _LoginScreenState extends State { mainAxisSize: MainAxisSize.min, children: [ Semantics( - label: 'LuckyChit logo', + label: '${l.appDisplayName} logo', child: Icon( Icons.account_balance_wallet_rounded, size: 56.w, @@ -69,10 +72,10 @@ class _LoginScreenState extends State { ), ), SizedBox(height: 16.h), - Text('LuckyChit', style: AppText.headline(context), textAlign: TextAlign.center), + Text(l.appDisplayName, style: AppText.headline(context), textAlign: TextAlign.center), SizedBox(height: 6.h), Text( - 'Chit fund management that feels effortless.', + l.authLoginTagline, style: AppText.bodyMuted(context), textAlign: TextAlign.center, ), @@ -82,13 +85,13 @@ class _LoginScreenState extends State { controller: _mobileController, keyboardType: TextInputType.phone, textInputAction: TextInputAction.next, - decoration: const InputDecoration( - labelText: 'Mobile number', - prefixIcon: Icon(Icons.phone_rounded), + decoration: InputDecoration( + labelText: l.labelMobileNumber, + prefixIcon: const Icon(Icons.phone_rounded), ), validator: (value) { - if (value == null || value.isEmpty) return 'Please enter mobile number'; - if (value.length != 10) return 'Mobile number must be 10 digits'; + if (value == null || value.isEmpty) return l.validatorEnterMobile; + if (value.length != 10) return l.validatorMobileTenDigits; return null; }, ), @@ -98,17 +101,17 @@ class _LoginScreenState extends State { obscureText: !_isPasswordVisible, textInputAction: TextInputAction.done, decoration: InputDecoration( - labelText: 'Password', + labelText: l.labelPassword, prefixIcon: const Icon(Icons.lock_rounded), suffixIcon: IconButton( - tooltip: _isPasswordVisible ? 'Hide password' : 'Show password', + tooltip: _isPasswordVisible ? l.tooltipHidePassword : l.tooltipShowPassword, icon: Icon(_isPasswordVisible ? Icons.visibility : Icons.visibility_off), onPressed: () => setState(() => _isPasswordVisible = !_isPasswordVisible), ), ), validator: (value) { - if (value == null || value.isEmpty) return 'Please enter password'; - if (value.length < 6) return 'Password must be at least 6 characters'; + if (value == null || value.isEmpty) return l.validatorEnterPasswordAuth; + if (value.length < 6) return l.validatorPasswordMinSixAuth; return null; }, ), @@ -127,13 +130,13 @@ class _LoginScreenState extends State { valueColor: AlwaysStoppedAnimation(scheme.onPrimary), ), ) - : const Text('Sign in'), + : Text(l.signInButton), ); }), SizedBox(height: 10.h), OutlinedButton( onPressed: () => Get.to(() => const SignupScreen()), - child: const Text('Create account'), + child: Text(l.createAccountButton), ), ], ), diff --git a/luckychit/lib/features/auth/views/signup_screen.dart b/luckychit/lib/features/auth/views/signup_screen.dart index bae3f43..6f5da0a 100644 --- a/luckychit/lib/features/auth/views/signup_screen.dart +++ b/luckychit/lib/features/auth/views/signup_screen.dart @@ -1,13 +1,14 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import '../../../app.dart'; import '../../../core/services/auth_service.dart'; import '../../../core/utils/snackbar_util.dart'; -import '../../../app.dart'; import '../../../core/design_system/app_components/auth_shell.dart'; import '../../../core/design_system/app_components/app_card.dart'; import '../../../core/design_system/app_spacing.dart'; import '../../../core/design_system/app_text.dart'; +import '../../../l10n/l10n_x.dart'; class SignupScreen extends StatefulWidget { const SignupScreen({super.key}); @@ -52,19 +53,19 @@ class _SignupScreenState extends State { emergencyContact: _emergencyContactController.text.trim().isEmpty ? null : _emergencyContactController.text.trim(), ); + if (!mounted) return; + final l = context.l10n; + if (success) { SnackbarUtil.showSuccess( - 'Account created successfully! Welcome to LuckyChit.', - title: 'Success', + l.signupSuccessWelcome, + title: l.snackTitleSuccess, ); - - // Navigate to home - App widget will auto-route based on role - // Use offAll to remove all previous routes (can't go back to signup) Get.offAll(() => const App()); } else { SnackbarUtil.showError( - 'Failed to create account. Please try again.', - title: 'Signup Failed', + l.signupFailedGenericUi, + title: l.signupFailedTitle, ); } } @@ -72,6 +73,7 @@ class _SignupScreenState extends State { @override Widget build(BuildContext context) { + final l = context.l10n; final scheme = Theme.of(context).colorScheme; return Scaffold( body: AuthShell( @@ -90,206 +92,184 @@ class _SignupScreenState extends State { color: scheme.primary, ), SizedBox(height: 16.h), - Text('Create account', style: AppText.headline(context), textAlign: TextAlign.center), + Text(l.authSignupScreenTitle, style: AppText.headline(context), textAlign: TextAlign.center), SizedBox(height: 6.h), Text( - 'Set up your profile in under a minute.', + l.authSignupTagline, style: AppText.bodyMuted(context), textAlign: TextAlign.center, ), SizedBox(height: 24.h), - - // Mobile Number Field - TextFormField( - controller: _mobileController, - keyboardType: TextInputType.phone, - textInputAction: TextInputAction.next, - decoration: const InputDecoration( - labelText: 'Mobile number *', - prefixIcon: Icon(Icons.phone_rounded), - ), - validator: (value) { - if (value == null || value.isEmpty) { - return 'Please enter mobile number'; - } - if (value.length != 10) { - return 'Mobile number must be 10 digits'; - } - if (!RegExp(r'^[0-9]+$').hasMatch(value)) { - return 'Mobile number must contain only digits'; - } - return null; - }, - ), - SizedBox(height: 20.h), - - // Full Name Field - TextFormField( - controller: _fullNameController, - keyboardType: TextInputType.name, - textInputAction: TextInputAction.next, - decoration: const InputDecoration( - labelText: 'Full name *', - prefixIcon: Icon(Icons.person_rounded), - ), - validator: (value) { - if (value == null || value.isEmpty) { - return 'Please enter your full name'; - } - return null; - }, - ), - SizedBox(height: 20.h), - - // Email Field (Optional) - TextFormField( - controller: _emailController, - keyboardType: TextInputType.emailAddress, - textInputAction: TextInputAction.next, - decoration: const InputDecoration( - labelText: 'Email (optional)', - prefixIcon: Icon(Icons.email_rounded), - ), - validator: (value) { - if (value != null && value.isNotEmpty) { - if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value)) { - return 'Please enter a valid email address'; - } - } - return null; - }, - ), - SizedBox(height: 20.h), - - // Address Field (Optional) - TextFormField( - controller: _addressController, - keyboardType: TextInputType.streetAddress, - maxLines: 2, - textInputAction: TextInputAction.newline, - decoration: const InputDecoration( - labelText: 'Address (optional)', - prefixIcon: Icon(Icons.home_rounded), - ), - ), - SizedBox(height: 20.h), - - // Emergency Contact Field (Optional) - TextFormField( - controller: _emergencyContactController, - keyboardType: TextInputType.phone, - textInputAction: TextInputAction.next, - decoration: const InputDecoration( - labelText: 'Emergency contact (optional)', - prefixIcon: Icon(Icons.contact_phone_rounded), - ), - validator: (value) { - if (value != null && value.isNotEmpty) { - if (value.length != 10) { - return 'Emergency contact must be 10 digits'; - } - if (!RegExp(r'^[0-9]+$').hasMatch(value)) { - return 'Emergency contact must contain only digits'; - } - } - return null; - }, - ), - SizedBox(height: 20.h), - - // Password Field - TextFormField( - controller: _passwordController, - obscureText: !_isPasswordVisible, - textInputAction: TextInputAction.next, - decoration: InputDecoration( - labelText: 'Password *', - prefixIcon: const Icon(Icons.lock_rounded), - suffixIcon: IconButton( - tooltip: _isPasswordVisible ? 'Hide password' : 'Show password', - icon: Icon(_isPasswordVisible ? Icons.visibility : Icons.visibility_off), - onPressed: () => setState(() => _isPasswordVisible = !_isPasswordVisible), - ), - ), - validator: (value) { - if (value == null || value.isEmpty) { - return 'Please enter password'; - } - if (value.length < 6) { - return 'Password must be at least 6 characters'; - } - return null; - }, - ), - SizedBox(height: 20.h), - - // Confirm Password Field - TextFormField( - controller: _confirmPasswordController, - obscureText: !_isConfirmPasswordVisible, - textInputAction: TextInputAction.done, - decoration: InputDecoration( - labelText: 'Confirm password *', - prefixIcon: const Icon(Icons.lock_outline_rounded), - suffixIcon: IconButton( - tooltip: _isConfirmPasswordVisible ? 'Hide password' : 'Show password', - icon: Icon(_isConfirmPasswordVisible ? Icons.visibility : Icons.visibility_off), - onPressed: () => setState(() => _isConfirmPasswordVisible = !_isConfirmPasswordVisible), - ), - ), - validator: (value) { - if (value == null || value.isEmpty) { - return 'Please confirm password'; - } - if (value != _passwordController.text) { - return 'Passwords do not match'; - } - return null; - }, - ), - SizedBox(height: 28.h), - - // Signup Button - Obx(() => SizedBox( - width: double.infinity, - height: 52.h, - child: FilledButton( - onPressed: _authService.isLoading.value - ? null - : _handleSignup, - child: _authService.isLoading.value - ? SizedBox( - height: 24.h, - width: 24.w, - child: CircularProgressIndicator( - strokeWidth: 2, - valueColor: AlwaysStoppedAnimation(scheme.onPrimary), - ), - ) - : const Text('Create account'), - ), - )), - SizedBox(height: 20.h), - - // Login Link - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Already have an account? ', - style: AppText.bodyMuted(context), - ), - TextButton( - onPressed: () { - Get.back(); - }, - child: Text( - 'Login', - style: AppText.label(context).copyWith(color: scheme.primary), - ), - ), - ], - ), + TextFormField( + controller: _mobileController, + keyboardType: TextInputType.phone, + textInputAction: TextInputAction.next, + decoration: InputDecoration( + labelText: l.labelMobileNumberRequired, + prefixIcon: const Icon(Icons.phone_rounded), + ), + validator: (value) { + if (value == null || value.isEmpty) { + return l.validatorEnterMobile; + } + if (value.length != 10) { + return l.validatorMobileTenDigits; + } + if (!RegExp(r'^[0-9]+$').hasMatch(value)) { + return l.validatorMobileDigitsOnly; + } + return null; + }, + ), + SizedBox(height: 20.h), + TextFormField( + controller: _fullNameController, + keyboardType: TextInputType.name, + textInputAction: TextInputAction.next, + decoration: InputDecoration( + labelText: l.labelFullNameRequired, + prefixIcon: const Icon(Icons.person_rounded), + ), + validator: (value) { + if (value == null || value.isEmpty) { + return l.validatorEnterFullName; + } + return null; + }, + ), + SizedBox(height: 20.h), + TextFormField( + controller: _emailController, + keyboardType: TextInputType.emailAddress, + textInputAction: TextInputAction.next, + decoration: InputDecoration( + labelText: l.labelEmailOptional, + prefixIcon: const Icon(Icons.email_rounded), + ), + validator: (value) { + if (value != null && value.isNotEmpty) { + if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value)) { + return l.validatorValidEmail; + } + } + return null; + }, + ), + SizedBox(height: 20.h), + TextFormField( + controller: _addressController, + keyboardType: TextInputType.streetAddress, + maxLines: 2, + textInputAction: TextInputAction.newline, + decoration: InputDecoration( + labelText: l.labelAddressOptional, + prefixIcon: const Icon(Icons.home_rounded), + ), + ), + SizedBox(height: 20.h), + TextFormField( + controller: _emergencyContactController, + keyboardType: TextInputType.phone, + textInputAction: TextInputAction.next, + decoration: InputDecoration( + labelText: l.labelEmergencyContactOptional, + prefixIcon: const Icon(Icons.contact_phone_rounded), + ), + validator: (value) { + if (value != null && value.isNotEmpty) { + if (value.length != 10) { + return l.validatorEmergencyTenDigits; + } + if (!RegExp(r'^[0-9]+$').hasMatch(value)) { + return l.validatorEmergencyDigitsOnly; + } + } + return null; + }, + ), + SizedBox(height: 20.h), + TextFormField( + controller: _passwordController, + obscureText: !_isPasswordVisible, + textInputAction: TextInputAction.next, + decoration: InputDecoration( + labelText: l.labelPasswordRequired, + prefixIcon: const Icon(Icons.lock_rounded), + suffixIcon: IconButton( + tooltip: _isPasswordVisible ? l.tooltipHidePassword : l.tooltipShowPassword, + icon: Icon(_isPasswordVisible ? Icons.visibility : Icons.visibility_off), + onPressed: () => setState(() => _isPasswordVisible = !_isPasswordVisible), + ), + ), + validator: (value) { + if (value == null || value.isEmpty) { + return l.validatorEnterPasswordAuth; + } + if (value.length < 6) { + return l.validatorPasswordMinSixAuth; + } + return null; + }, + ), + SizedBox(height: 20.h), + TextFormField( + controller: _confirmPasswordController, + obscureText: !_isConfirmPasswordVisible, + textInputAction: TextInputAction.done, + decoration: InputDecoration( + labelText: l.labelConfirmPasswordRequired, + prefixIcon: const Icon(Icons.lock_outline_rounded), + suffixIcon: IconButton( + tooltip: _isConfirmPasswordVisible ? l.tooltipHidePassword : l.tooltipShowPassword, + icon: Icon(_isConfirmPasswordVisible ? Icons.visibility : Icons.visibility_off), + onPressed: () => setState(() => _isConfirmPasswordVisible = !_isConfirmPasswordVisible), + ), + ), + validator: (value) { + if (value == null || value.isEmpty) { + return l.validatorConfirmPassword; + } + if (value != _passwordController.text) { + return l.validatorPasswordsMismatch; + } + return null; + }, + ), + SizedBox(height: 28.h), + Obx(() => SizedBox( + width: double.infinity, + height: 52.h, + child: FilledButton( + onPressed: _authService.isLoading.value ? null : _handleSignup, + child: _authService.isLoading.value + ? SizedBox( + height: 24.h, + width: 24.w, + child: CircularProgressIndicator( + strokeWidth: 2, + valueColor: AlwaysStoppedAnimation(scheme.onPrimary), + ), + ) + : Text(l.createAccountButton), + ), + )), + SizedBox(height: 20.h), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + l.alreadyHaveAccount, + style: AppText.bodyMuted(context), + ), + TextButton( + onPressed: () => Get.back(), + child: Text( + l.loginLink, + style: AppText.label(context).copyWith(color: scheme.primary), + ), + ), + ], + ), ], ), ), @@ -299,4 +279,3 @@ class _SignupScreenState extends State { ); } } - diff --git a/luckychit/lib/features/settings/settings_page.dart b/luckychit/lib/features/settings/settings_page.dart index 6519237..d2e31e1 100644 --- a/luckychit/lib/features/settings/settings_page.dart +++ b/luckychit/lib/features/settings/settings_page.dart @@ -3,27 +3,44 @@ import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import '../../core/controllers/theme_controller.dart'; +import '../../core/controllers/locale_controller.dart'; import '../../core/services/auth_service.dart'; import '../../core/services/api_service.dart'; import '../../core/utils/snackbar_util.dart'; +import '../../l10n/app_localizations.dart'; + +String _themeModeLabel(AppLocalizations l10n) { + switch (ThemeController.to.themeMode) { + case ThemeMode.light: + return l10n.themeLight; + case ThemeMode.dark: + return l10n.themeDark; + case ThemeMode.system: + return l10n.themeSystem; + } +} class SettingsPage extends StatelessWidget { const SettingsPage({super.key}); @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context); return Scaffold( appBar: AppBar( - title: const Text('Settings'), + title: Text(l10n.settingsTitle), ), body: ListView( padding: EdgeInsets.all(16.w), children: [ - _buildSectionHeader(context, 'Appearance'), - _buildThemeSettings(context), + _buildSectionHeader(context, l10n.sectionAppearance), + _buildThemeSettings(context, l10n), SizedBox(height: 24.h), - _buildSectionHeader(context, 'Account'), - _buildAccountSettings(context), + _buildSectionHeader(context, l10n.sectionLanguage), + _buildLanguageSettings(context, l10n), + SizedBox(height: 24.h), + _buildSectionHeader(context, l10n.sectionAccount), + _buildAccountSettings(context, l10n), SizedBox(height: 24.h), Obx(() { final user = AuthService.to.currentUser.value; @@ -31,21 +48,21 @@ class SettingsPage extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - _buildSectionHeader(context, 'Payment Settings'), - _buildPaymentSettings(context), + _buildSectionHeader(context, l10n.sectionPaymentSettings), + _buildPaymentSettings(context, l10n), SizedBox(height: 24.h), ], ); } return const SizedBox.shrink(); }), - _buildSectionHeader(context, 'Notifications'), - _buildNotificationSettings(context), + _buildSectionHeader(context, l10n.sectionNotifications), + _buildNotificationSettings(context, l10n), SizedBox(height: 24.h), - _buildSectionHeader(context, 'About'), - _buildAboutSettings(context), + _buildSectionHeader(context, l10n.sectionAbout), + _buildAboutSettings(context, l10n), SizedBox(height: 32.h), - _buildLogoutButton(context), + _buildLogoutButton(context, l10n), ], ), ); @@ -67,13 +84,88 @@ class SettingsPage extends StatelessWidget { ); } - Widget _buildThemeSettings(BuildContext context) { + Widget _buildLanguageSettings(BuildContext context, AppLocalizations l10n) { + final scheme = Theme.of(context).colorScheme; + return Card( + child: Obx( + () => ListTile( + leading: Container( + padding: EdgeInsets.all(10.w), + decoration: BoxDecoration( + color: scheme.secondaryContainer, + borderRadius: BorderRadius.circular(12.r), + ), + child: Icon( + Icons.language_rounded, + color: scheme.onSecondaryContainer, + size: 24.w, + ), + ), + title: Text( + l10n.languageTitle, + style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600), + ), + subtitle: Text( + LocaleController.to.currentLanguageLabel(l10n), + style: TextStyle(fontSize: 14.sp), + ), + trailing: Icon(Icons.arrow_forward_ios_rounded, size: 16.w), + onTap: () => _showLanguageDialog(context, l10n), + ), + ), + ); + } + + void _showLanguageDialog(BuildContext context, AppLocalizations l10n) { + final scheme = Theme.of(context).colorScheme; + Get.dialog( + Obx( + () { + final code = LocaleController.to.locale.value.languageCode; + return AlertDialog( + title: Text(l10n.chooseLanguageTitle), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + RadioListTile( + title: Text(l10n.languageEnglish), + value: 'en', + groupValue: code, + onChanged: (v) async { + if (v != null) { + await LocaleController.to.setLocale(Locale(v)); + Get.back(); + } + }, + activeColor: scheme.primary, + ), + RadioListTile( + title: Text(l10n.languageTelugu), + value: 'te', + groupValue: code, + onChanged: (v) async { + if (v != null) { + await LocaleController.to.setLocale(Locale(v)); + Get.back(); + } + }, + activeColor: scheme.primary, + ), + ], + ), + ); + }, + ), + ); + } + + Widget _buildThemeSettings(BuildContext context, AppLocalizations l10n) { final scheme = Theme.of(context).colorScheme; return Card( child: Column( children: [ - Obx(() { - return ListTile( + Obx( + () => ListTile( leading: Container( padding: EdgeInsets.all(10.w), decoration: BoxDecoration( @@ -87,22 +179,20 @@ class SettingsPage extends StatelessWidget { ), ), title: Text( - 'Theme', + l10n.themeTitle, style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600), ), subtitle: Text( - ThemeController.to.getThemeModeString(), + _themeModeLabel(l10n), style: TextStyle(fontSize: 14.sp), ), trailing: Icon(Icons.arrow_forward_ios_rounded, size: 16.w), - onTap: () => _showThemeDialog(context), - ); - }), - + onTap: () => _showThemeDialog(context, l10n), + ), + ), Divider(height: 1.h, indent: 72.w), - - Obx(() { - return SwitchListTile( + Obx( + () => SwitchListTile( secondary: Container( padding: EdgeInsets.all(10.w), decoration: BoxDecoration( @@ -116,11 +206,11 @@ class SettingsPage extends StatelessWidget { ), ), title: Text( - 'Dark Mode', + l10n.darkModeTitle, style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600), ), subtitle: Text( - 'Override system settings', + l10n.darkModeSubtitle, style: TextStyle(fontSize: 14.sp), ), value: ThemeController.to.isDarkMode, @@ -128,51 +218,50 @@ class SettingsPage extends StatelessWidget { ThemeController.to.toggleTheme(); }, activeColor: scheme.primary, - ); - }), + ), + ), ], ), ); } - Widget _buildAccountSettings(BuildContext context) { + Widget _buildAccountSettings(BuildContext context, AppLocalizations l10n) { final scheme = Theme.of(context).colorScheme; return Card( child: Column( children: [ - Obx(() { - final user = AuthService.to.currentUser.value; - return ListTile( - leading: Container( - padding: EdgeInsets.all(10.w), - decoration: BoxDecoration( - color: scheme.primaryContainer, - borderRadius: BorderRadius.circular(12.r), + Obx( + () { + final user = AuthService.to.currentUser.value; + return ListTile( + leading: Container( + padding: EdgeInsets.all(10.w), + decoration: BoxDecoration( + color: scheme.primaryContainer, + borderRadius: BorderRadius.circular(12.r), + ), + child: Icon( + Icons.person_rounded, + color: scheme.onPrimaryContainer, + size: 24.w, + ), ), - child: Icon( - Icons.person_rounded, - color: scheme.onPrimaryContainer, - size: 24.w, + title: Text( + user?.fullName ?? 'User', + style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600), ), - ), - title: Text( - user?.fullName ?? 'User', - style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600), - ), - subtitle: Text( - user?.mobileNumber ?? '', - style: TextStyle(fontSize: 14.sp), - ), - trailing: Icon(Icons.arrow_forward_ios_rounded, size: 16.w), - onTap: () { - SnackbarUtil.showInfo('Profile page coming soon'); - }, - ); - }), - + subtitle: Text( + user?.mobileNumber ?? '', + style: TextStyle(fontSize: 14.sp), + ), + trailing: Icon(Icons.arrow_forward_ios_rounded, size: 16.w), + onTap: () { + SnackbarUtil.showInfo(l10n.profileComingSoon); + }, + ); + }, + ), Divider(height: 1.h, indent: 72.w), - - // Change Password ListTile( leading: Container( padding: EdgeInsets.all(10.w), @@ -187,16 +276,16 @@ class SettingsPage extends StatelessWidget { ), ), title: Text( - 'Change Password', + l10n.changePasswordTitle, style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600), ), subtitle: Text( - 'Update your password', + l10n.changePasswordSubtitle, style: TextStyle(fontSize: 14.sp), ), trailing: Icon(Icons.arrow_forward_ios_rounded, size: 16.w), onTap: () { - SnackbarUtil.showInfo('Change password feature coming soon'); + SnackbarUtil.showInfo(l10n.changePasswordComingSoon); }, ), ], @@ -204,7 +293,8 @@ class SettingsPage extends StatelessWidget { ); } - Widget _buildNotificationSettings(BuildContext context) { + Widget _buildNotificationSettings( + BuildContext context, AppLocalizations l10n) { final scheme = Theme.of(context).colorScheme; return Card( child: Column( @@ -223,22 +313,20 @@ class SettingsPage extends StatelessWidget { ), ), title: Text( - 'Push Notifications', + l10n.pushNotificationsTitle, style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600), ), subtitle: Text( - 'Receive push notifications', + l10n.pushNotificationsSubtitle, style: TextStyle(fontSize: 14.sp), ), value: true, onChanged: (value) { - SnackbarUtil.showInfo('Notification settings coming soon'); + SnackbarUtil.showInfo(l10n.notificationComingSoon); }, activeColor: scheme.primary, ), - Divider(height: 1.h, indent: 72.w), - SwitchListTile( secondary: Container( padding: EdgeInsets.all(10.w), @@ -253,22 +341,20 @@ class SettingsPage extends StatelessWidget { ), ), title: Text( - 'Payment Reminders', + l10n.paymentRemindersTitle, style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600), ), subtitle: Text( - 'Reminders for upcoming payments', + l10n.paymentRemindersSubtitle, style: TextStyle(fontSize: 14.sp), ), value: true, onChanged: (value) { - SnackbarUtil.showInfo('Notification settings coming soon'); + SnackbarUtil.showInfo(l10n.notificationComingSoon); }, activeColor: scheme.primary, ), - Divider(height: 1.h, indent: 72.w), - SwitchListTile( secondary: Container( padding: EdgeInsets.all(10.w), @@ -283,16 +369,16 @@ class SettingsPage extends StatelessWidget { ), ), title: Text( - 'Draw Notifications', + l10n.drawNotificationsTitle, style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600), ), subtitle: Text( - 'Alerts for lottery draws', + l10n.drawNotificationsSubtitle, style: TextStyle(fontSize: 14.sp), ), value: true, onChanged: (value) { - SnackbarUtil.showInfo('Notification settings coming soon'); + SnackbarUtil.showInfo(l10n.notificationComingSoon); }, activeColor: scheme.primary, ), @@ -301,7 +387,7 @@ class SettingsPage extends StatelessWidget { ); } - Widget _buildPaymentSettings(BuildContext context) { + Widget _buildPaymentSettings(BuildContext context, AppLocalizations l10n) { final apiService = ApiService(); final scheme = Theme.of(context).colorScheme; @@ -326,17 +412,17 @@ class SettingsPage extends StatelessWidget { ), ), title: Text( - 'UPI ID', + l10n.upiIdTitle, style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600), ), subtitle: Text( - 'Loading...', + l10n.loading, style: TextStyle(fontSize: 14.sp), ), ); } - final upiId = snapshot.data?['data']?['upi_id'] ?? 'Not configured'; + final upiId = snapshot.data?['data']?['upi_id'] ?? l10n.notConfigured; final isConfigured = snapshot.data?['data']?['is_configured'] ?? false; return ListTile( @@ -359,7 +445,7 @@ class SettingsPage extends StatelessWidget { title: Row( children: [ Text( - 'UPI ID', + l10n.upiIdTitle, style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600), ), if (isConfigured) ...[ @@ -371,7 +457,7 @@ class SettingsPage extends StatelessWidget { borderRadius: BorderRadius.circular(12.r), ), child: Text( - 'Active', + l10n.active, style: TextStyle( fontSize: 10.sp, fontWeight: FontWeight.w600, @@ -387,7 +473,7 @@ class SettingsPage extends StatelessWidget { children: [ SizedBox(height: 4.h), SelectableText( - upiId, + upiId.toString(), style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w600, @@ -400,7 +486,7 @@ class SettingsPage extends StatelessWidget { if (!isConfigured) ...[ SizedBox(height: 4.h), Text( - 'Configure in backend/.env', + l10n.configureBackend, style: TextStyle( fontSize: 12.sp, color: scheme.tertiary, @@ -413,29 +499,26 @@ class SettingsPage extends StatelessWidget { trailing: isConfigured ? IconButton( onPressed: () { - Clipboard.setData(ClipboardData(text: upiId)); - SnackbarUtil.showSuccess('UPI ID copied to clipboard'); + Clipboard.setData(ClipboardData(text: upiId.toString())); + SnackbarUtil.showSuccess(l10n.upiCopiedClipboard); }, icon: Icon( Icons.copy_rounded, size: 20.w, color: scheme.secondary, ), - tooltip: 'Copy UPI ID', + tooltip: l10n.copyUpiIdTooltip, ) : Icon( Icons.warning_rounded, size: 20.w, color: scheme.tertiary, ), - onTap: () => _showUPIInfoDialog(context, upiId, isConfigured), + onTap: () => _showUPIInfoDialog(context, l10n, upiId.toString(), isConfigured), ); }, ), - Divider(height: 1.h, indent: 72.w), - - // Payment Statistics ListTile( leading: Container( padding: EdgeInsets.all(10.w), @@ -450,21 +533,19 @@ class SettingsPage extends StatelessWidget { ), ), title: Text( - 'Payment Statistics', + l10n.paymentStatisticsTitle, style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600), ), subtitle: Text( - 'View payment insights', + l10n.paymentStatisticsSubtitle, style: TextStyle(fontSize: 14.sp), ), trailing: Icon(Icons.arrow_forward_ios_rounded, size: 16.w), onTap: () { - SnackbarUtil.showInfo('Payment statistics coming soon'); + SnackbarUtil.showInfo(l10n.paymentStatsComingSoon); }, ), - Divider(height: 1.h, indent: 72.w), - ListTile( leading: Container( padding: EdgeInsets.all(10.w), @@ -479,11 +560,11 @@ class SettingsPage extends StatelessWidget { ), ), title: Text( - 'Transaction Fees', + l10n.transactionFeesTitle, style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600), ), subtitle: Text( - '0% fees • Save lakhs per year!', + l10n.transactionFeesSubtitle, style: TextStyle( fontSize: 14.sp, color: scheme.primary, @@ -497,7 +578,7 @@ class SettingsPage extends StatelessWidget { borderRadius: BorderRadius.circular(8.r), ), child: Text( - 'FREE', + l10n.free, style: TextStyle( fontSize: 12.sp, fontWeight: FontWeight.w800, @@ -511,7 +592,12 @@ class SettingsPage extends StatelessWidget { ); } - void _showUPIInfoDialog(BuildContext context, String upiId, bool isConfigured) { + void _showUPIInfoDialog( + BuildContext context, + AppLocalizations l10n, + String upiId, + bool isConfigured, + ) { final scheme = Theme.of(context).colorScheme; Get.dialog( AlertDialog( @@ -523,7 +609,7 @@ class SettingsPage extends StatelessWidget { size: 24.w, ), SizedBox(width: 12.w), - const Text('UPI Payment Settings'), + Expanded(child: Text(l10n.upiPaymentSettingsTitle)), ], ), content: SingleChildScrollView( @@ -532,7 +618,7 @@ class SettingsPage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'Current UPI ID', + l10n.currentUpiId, style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w600, @@ -573,7 +659,7 @@ class SettingsPage extends StatelessWidget { IconButton( onPressed: () { Clipboard.setData(ClipboardData(text: upiId)); - SnackbarUtil.showSuccess('Copied!'); + SnackbarUtil.showSuccess(l10n.copied); }, icon: Icon( Icons.copy_rounded, @@ -584,7 +670,6 @@ class SettingsPage extends StatelessWidget { ], ), ), - if (!isConfigured) ...[ SizedBox(height: 16.h), Container( @@ -603,7 +688,7 @@ class SettingsPage extends StatelessWidget { SizedBox(width: 8.w), Expanded( child: Text( - 'UPI ID not configured. Update backend/.env file.', + l10n.upiNotConfiguredMessage, style: TextStyle( fontSize: 12.sp, color: scheme.onTertiaryContainer, @@ -614,13 +699,11 @@ class SettingsPage extends StatelessWidget { ), ), ], - SizedBox(height: 16.h), const Divider(), SizedBox(height: 16.h), - Text( - 'How to Update UPI ID', + l10n.howToUpdateUpi, style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w600, @@ -628,11 +711,10 @@ class SettingsPage extends StatelessWidget { ), ), SizedBox(height: 12.h), - _buildInfoStep(context, '1', 'Open backend/.env file'), - _buildInfoStep(context, '2', 'Update PHONEPE_UPI_ID=your_upi@paytm'), - _buildInfoStep(context, '3', 'Restart backend server'), - _buildInfoStep(context, '4', 'Refresh this screen'), - + _buildInfoStep(context, '1', l10n.stepOpenEnv), + _buildInfoStep(context, '2', l10n.stepUpdatePhonepe), + _buildInfoStep(context, '3', l10n.stepRestartBackend), + _buildInfoStep(context, '4', l10n.stepRefreshScreen), SizedBox(height: 16.h), Container( padding: EdgeInsets.all(12.w), @@ -652,7 +734,7 @@ class SettingsPage extends StatelessWidget { ), SizedBox(width: 8.w), Text( - 'Pro Tip', + l10n.proTipTitle, style: TextStyle( fontSize: 13.sp, fontWeight: FontWeight.w600, @@ -663,7 +745,7 @@ class SettingsPage extends StatelessWidget { ), SizedBox(height: 8.h), Text( - 'Members can pay using ANY UPI app (PhonePe, GPay, Paytm) directly to your personal UPI ID with 0% transaction fees!', + l10n.proTipBody, style: TextStyle( fontSize: 12.sp, color: scheme.onPrimaryContainer.withOpacity(0.92), @@ -679,7 +761,7 @@ class SettingsPage extends StatelessWidget { actions: [ TextButton( onPressed: () => Get.back(), - child: const Text('Close'), + child: Text(l10n.close), ), ], ), @@ -730,7 +812,7 @@ class SettingsPage extends StatelessWidget { ); } - Widget _buildAboutSettings(BuildContext context) { + Widget _buildAboutSettings(BuildContext context, AppLocalizations l10n) { final scheme = Theme.of(context).colorScheme; return Card( child: Column( @@ -749,7 +831,7 @@ class SettingsPage extends StatelessWidget { ), ), title: Text( - 'App Version', + l10n.appVersionTitle, style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600), ), subtitle: Text( @@ -757,9 +839,7 @@ class SettingsPage extends StatelessWidget { style: TextStyle(fontSize: 14.sp), ), ), - Divider(height: 1.h, indent: 72.w), - ListTile( leading: Container( padding: EdgeInsets.all(10.w), @@ -774,17 +854,15 @@ class SettingsPage extends StatelessWidget { ), ), title: Text( - 'Privacy Policy', + l10n.privacyPolicyTitle, style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600), ), trailing: Icon(Icons.arrow_forward_ios_rounded, size: 16.w), onTap: () { - SnackbarUtil.showInfo('Privacy policy page coming soon'); + SnackbarUtil.showInfo(l10n.privacyComingSoon); }, ), - Divider(height: 1.h, indent: 72.w), - ListTile( leading: Container( padding: EdgeInsets.all(10.w), @@ -799,12 +877,12 @@ class SettingsPage extends StatelessWidget { ), ), title: Text( - 'Terms of Service', + l10n.termsOfServiceTitle, style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600), ), trailing: Icon(Icons.arrow_forward_ios_rounded, size: 16.w), onTap: () { - SnackbarUtil.showInfo('Terms of service page coming soon'); + SnackbarUtil.showInfo(l10n.termsComingSoon); }, ), ], @@ -812,15 +890,15 @@ class SettingsPage extends StatelessWidget { ); } - Widget _buildLogoutButton(BuildContext context) { + Widget _buildLogoutButton(BuildContext context, AppLocalizations l10n) { return SizedBox( width: double.infinity, height: 52.h, child: ElevatedButton.icon( - onPressed: () => _showLogoutDialog(context), + onPressed: () => _showLogoutDialog(context, l10n), icon: Icon(Icons.logout_rounded, size: 20.w), label: Text( - 'Logout', + l10n.logout, style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600), ), style: ElevatedButton.styleFrom( @@ -834,16 +912,16 @@ class SettingsPage extends StatelessWidget { ); } - void _showThemeDialog(BuildContext context) { + void _showThemeDialog(BuildContext context, AppLocalizations l10n) { final scheme = Theme.of(context).colorScheme; Get.dialog( AlertDialog( - title: const Text('Choose Theme'), + title: Text(l10n.chooseThemeTitle), content: Column( mainAxisSize: MainAxisSize.min, children: [ RadioListTile( - title: const Text('Light'), + title: Text(l10n.themeLight), value: ThemeMode.light, groupValue: ThemeController.to.themeMode, onChanged: (value) { @@ -853,7 +931,7 @@ class SettingsPage extends StatelessWidget { activeColor: scheme.primary, ), RadioListTile( - title: const Text('Dark'), + title: Text(l10n.themeDark), value: ThemeMode.dark, groupValue: ThemeController.to.themeMode, onChanged: (value) { @@ -863,7 +941,7 @@ class SettingsPage extends StatelessWidget { activeColor: scheme.primary, ), RadioListTile( - title: const Text('System Default'), + title: Text(l10n.themeSystem), value: ThemeMode.system, groupValue: ThemeController.to.themeMode, onChanged: (value) { @@ -878,32 +956,31 @@ class SettingsPage extends StatelessWidget { ); } - void _showLogoutDialog(BuildContext context) { + void _showLogoutDialog(BuildContext context, AppLocalizations l10n) { showDialog( context: context, builder: (context) => AlertDialog( - title: const Text('Logout'), - content: const Text('Are you sure you want to logout?'), + title: Text(l10n.logoutConfirmTitle), + content: Text(l10n.logoutConfirmMessage), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), - child: const Text('Cancel'), + child: Text(l10n.cancel), ), ElevatedButton( onPressed: () { Navigator.of(context).pop(); AuthService.to.logout(); - SnackbarUtil.showSuccess('Logged out successfully'); + SnackbarUtil.showSuccess(l10n.loggedOutSuccess); }, style: ElevatedButton.styleFrom( backgroundColor: Theme.of(context).colorScheme.error, foregroundColor: Theme.of(context).colorScheme.onError, ), - child: const Text('Logout'), + child: Text(l10n.logout), ), ], ), ); } } - diff --git a/luckychit/lib/interfaces/manager/chit_groups_page.dart b/luckychit/lib/interfaces/manager/chit_groups_page.dart index 1a698e9..7cc3454 100644 --- a/luckychit/lib/interfaces/manager/chit_groups_page.dart +++ b/luckychit/lib/interfaces/manager/chit_groups_page.dart @@ -11,16 +11,18 @@ import 'import_existing_group_dialog.dart'; import 'member_selection_dialog.dart'; import 'group_details_page.dart'; import 'combined_draw_dialog.dart'; +import '../../l10n/l10n_x.dart'; class ChitGroupsPage extends StatelessWidget { const ChitGroupsPage({super.key}); @override Widget build(BuildContext context) { + final l = context.l10n; final scheme = Theme.of(context).colorScheme; return AppScaffold( - title: 'My Chitfunds', + title: l.pageMyChitfunds, actions: [ PopupMenuButton( icon: Icon(Icons.add, size: 24.w), @@ -38,7 +40,7 @@ class ChitGroupsPage extends StatelessWidget { children: [ Icon(Icons.add_circle, color: scheme.primary, size: 20.w), SizedBox(width: 12.w), - const Text('Create New Group'), + Text(l.createNewGroupMenu), ], ), ), @@ -48,7 +50,7 @@ class ChitGroupsPage extends StatelessWidget { children: [ Icon(Icons.upload, color: scheme.secondary, size: 20.w), SizedBox(width: 12.w), - const Text('Import Existing Group'), + Text(l.importExistingGroupMenu), ], ), ), @@ -62,8 +64,6 @@ class ChitGroupsPage extends StatelessWidget { isLoading: service.isLoading.value, isEmpty: service.chitGroups.isEmpty, emptyType: EmptyStateType.noGroups, - emptyTitle: 'No chit groups yet', - emptyMessage: 'Create a new chit group or import an existing one.', onRetry: () => service.loadManagerChitGroups(), child: RefreshIndicator( onRefresh: () => service.loadManagerChitGroups(), diff --git a/luckychit/lib/interfaces/manager/manager_dashboard.dart b/luckychit/lib/interfaces/manager/manager_dashboard.dart index b2dbf3a..c12b61c 100644 --- a/luckychit/lib/interfaces/manager/manager_dashboard.dart +++ b/luckychit/lib/interfaces/manager/manager_dashboard.dart @@ -19,12 +19,15 @@ import 'import_existing_group_dialog.dart'; import 'members_page.dart'; import '../../test_animated_draw.dart'; import '../../features/recordings/recordings_page.dart'; +import '../../l10n/app_localizations.dart'; +import '../../l10n/l10n_x.dart'; class ManagerDashboard extends StatelessWidget { const ManagerDashboard({super.key}); @override Widget build(BuildContext context) { + final l = context.l10n; final scheme = Theme.of(context).colorScheme; // Initialize services Get.put(ChitGroupService()); @@ -40,7 +43,7 @@ class ManagerDashboard extends StatelessWidget { return Scaffold( appBar: AppBar( - title: Text('Dashboard', style: AppText.title(context)), + title: Text(l.dashboardTitle, style: AppText.title(context)), actions: [ // Notifications with badge Obx(() { @@ -51,13 +54,13 @@ class ManagerDashboard extends StatelessWidget { child: Icon(Icons.notifications_rounded, size: 24.w), ), onPressed: () => Get.to(() => const NotificationCenterPage()), - tooltip: 'Notifications', + tooltip: l.notificationsTooltip, ); }), IconButton( icon: Icon(Icons.videocam, size: 24.w), onPressed: () => Get.to(() => const RecordingsPage()), - tooltip: 'View Draw Recordings', + tooltip: l.recordingsTooltip, ), IconButton( icon: Icon(Icons.logout, size: 24.w), @@ -81,7 +84,7 @@ class ManagerDashboard extends StatelessWidget { backgroundColor: scheme.tertiary, foregroundColor: scheme.onTertiary, child: const Icon(Icons.casino), - tooltip: 'Test Animated Draw', + tooltip: l.testDrawTooltip, ), ); } @@ -94,8 +97,6 @@ class ManagerDashboard extends StatelessWidget { isLoading: service.isLoading.value, isEmpty: service.chitGroups.isEmpty, emptyType: EmptyStateType.noGroups, - emptyTitle: 'No chit groups yet', - emptyMessage: 'Create your first group to get started.', onRetry: () => service.loadManagerChitGroups(), loading: const SkeletonDashboard(), child: RefreshIndicator( @@ -109,9 +110,9 @@ class ManagerDashboard extends StatelessWidget { children: [ _buildWelcomeSection(context), SizedBox(height: 24.h), - _buildRecentActivitiesSection(), + _buildRecentActivitiesSection(context), SizedBox(height: 24.h), - _buildQuickActionsSection(), + _buildQuickActionsSection(context), ], ), ), @@ -142,7 +143,7 @@ class ManagerDashboard extends StatelessWidget { children: [ _buildWelcomeSection(context), SizedBox(height: 24.h), - _buildDesktopActivitiesAndActions(), + _buildDesktopActivitiesAndActions(context), ], ), ), @@ -176,7 +177,7 @@ class ManagerDashboard extends StatelessWidget { ), SizedBox(height: 12.h), Obx(() => Text( - AuthService.to.currentUser.value?.fullName ?? 'Manager', + AuthService.to.currentUser.value?.fullName ?? context.l10n.managerFallbackName, style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, @@ -188,7 +189,7 @@ class ManagerDashboard extends StatelessWidget { )), SizedBox(height: 4.h), Text( - 'Chit Fund Manager', + context.l10n.chitFundManagerRole, style: TextStyle( fontSize: 14.sp, color: scheme.onSurfaceVariant, @@ -206,38 +207,38 @@ class ManagerDashboard extends StatelessWidget { children: [ _buildMenuItem( icon: Icons.dashboard, - title: 'Dashboard', + title: context.l10n.menuDashboard, isSelected: true, onTap: () {}, ), _buildMenuItem( icon: Icons.group, - title: 'My Chitfunds', + title: context.l10n.menuMyChitfunds, onTap: () => Get.to(() => const ChitGroupsPage()), ), _buildMenuItem( icon: Icons.people, - title: 'Members', + title: context.l10n.menuMembers, onTap: () {}, ), _buildMenuItem( icon: Icons.payment, - title: 'Payments', + title: context.l10n.menuPayments, onTap: () {}, ), _buildMenuItem( icon: Icons.casino, - title: 'Lottery Draws', + title: context.l10n.menuLotteryDraws, onTap: () {}, ), _buildMenuItem( icon: Icons.analytics, - title: 'Reports', + title: context.l10n.menuReports, onTap: () {}, ), _buildMenuItem( icon: Icons.settings, - title: 'Settings', + title: context.l10n.settingsTitle, onTap: () => Get.to(() => const SettingsPage()), ), ], @@ -277,16 +278,17 @@ class ManagerDashboard extends StatelessWidget { } Widget _buildWelcomeSection(BuildContext context) { + final l = context.l10n; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'Welcome back!', + l.welcomeBackTitle, style: AppText.headline(context), ), SizedBox(height: 8.h), Text( - 'Here\'s what\'s happening with your chit funds today.', + l.welcomeBackSubtitle, style: AppText.bodyMuted(context), maxLines: 2, overflow: TextOverflow.ellipsis, @@ -305,7 +307,8 @@ class ManagerDashboard extends StatelessWidget { return SizedBox.shrink(); } - Widget _buildQuickActionsSection() { + Widget _buildQuickActionsSection(BuildContext context) { + final l = context.l10n; return Card( child: Padding( padding: EdgeInsets.all(20.w), @@ -313,7 +316,7 @@ class ManagerDashboard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'Quick Actions', + l.quickActionsTitle, style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.w600, @@ -323,36 +326,36 @@ class ManagerDashboard extends StatelessWidget { Column( children: [ QuickActionCard( - title: 'Create New Chitfund', - subtitle: 'Start a new chit fund group', + title: l.qaCreateChitfundTitle, + subtitle: l.qaCreateChitfundSubtitle, icon: Icons.group_add, color: Colors.green.shade600, - onTap: () => _showCreateGroupDialog(Get.context!), + onTap: () => _showCreateGroupDialog(context), ), QuickActionCard( - title: 'Import Existing Chitfund', - subtitle: 'Add a group that already started', + title: l.qaImportChitfundTitle, + subtitle: l.qaImportChitfundSubtitle, icon: Icons.upload, color: Colors.blue.shade600, - onTap: () => _showImportGroupDialog(Get.context!), + onTap: () => _showImportGroupDialog(context), ), QuickActionCard( - title: 'View All Chitfunds', - subtitle: 'Manage your existing groups', + title: l.qaViewAllChitfundsTitle, + subtitle: l.qaViewAllChitfundsSubtitle, icon: Icons.list, color: Colors.teal.shade600, onTap: () => _navigateToGroups(), ), QuickActionCard( - title: 'Manage Members', - subtitle: 'Add or remove members', + title: l.qaManageMembersTitle, + subtitle: l.qaManageMembersSubtitle, icon: Icons.people, color: Colors.orange.shade600, onTap: () => _navigateToMembers(), ), QuickActionCard( - title: 'Payment Records', - subtitle: 'Track all transactions', + title: l.qaPaymentRecordsTitle, + subtitle: l.qaPaymentRecordsSubtitle, icon: Icons.payment, color: Colors.purple.shade600, onTap: () => _navigateToPayments(), @@ -365,7 +368,8 @@ class ManagerDashboard extends StatelessWidget { ); } - Widget _buildRecentActivitiesSection() { + Widget _buildRecentActivitiesSection(BuildContext context) { + final l = context.l10n; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -373,7 +377,7 @@ class ManagerDashboard extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - 'My Chitfunds', + l.sectionMyChitfunds, style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.w600, @@ -382,7 +386,7 @@ class ManagerDashboard extends StatelessWidget { TextButton.icon( onPressed: _navigateToGroups, icon: Icon(Icons.arrow_forward, size: 18.w), - label: Text('View All', style: TextStyle(fontSize: 14.sp)), + label: Text(l.viewAll, style: TextStyle(fontSize: 14.sp)), ), ], ), @@ -395,7 +399,7 @@ class ManagerDashboard extends StatelessWidget { padding: EdgeInsets.all(32.w), child: Center( child: Text( - 'No chit funds yet', + l.noChitFundsYetShort, style: TextStyle( fontSize: 14.sp, color: Colors.grey.shade600, @@ -408,7 +412,7 @@ class ManagerDashboard extends StatelessWidget { return Column( children: groups.map((group) => Padding( padding: EdgeInsets.only(bottom: 12.h), - child: _buildGroupQuickCard(group), + child: _buildGroupQuickCard(context, group), )).toList(), ); }), @@ -416,7 +420,8 @@ class ManagerDashboard extends StatelessWidget { ); } - Widget _buildGroupQuickCard(dynamic group) { + Widget _buildGroupQuickCard(BuildContext context, dynamic group) { + final l = context.l10n; final status = group.status ?? 'forming'; final statusColor = _getStatusColor(status); @@ -447,7 +452,7 @@ class ManagerDashboard extends StatelessWidget { border: Border.all(color: statusColor, width: 1.5), ), child: Text( - _getStatusText(status), + _getStatusText(l, status), style: TextStyle( fontSize: 12.sp, fontWeight: FontWeight.w600, @@ -492,7 +497,7 @@ class ManagerDashboard extends StatelessWidget { Get.to(() => const ChitGroupsPage()); }, icon: Icon(Icons.payment, size: 18.w), - label: Text('Record', style: TextStyle(fontSize: 13.sp)), + label: Text(l.actionRecord, style: TextStyle(fontSize: 13.sp)), style: ElevatedButton.styleFrom( backgroundColor: Colors.green.shade600, foregroundColor: Colors.white, @@ -508,7 +513,7 @@ class ManagerDashboard extends StatelessWidget { Get.to(() => const ChitGroupsPage()); }, icon: Icon(Icons.casino, size: 18.w), - label: Text('Draw', style: TextStyle(fontSize: 13.sp)), + label: Text(l.actionDraw, style: TextStyle(fontSize: 13.sp)), style: ElevatedButton.styleFrom( backgroundColor: Colors.purple.shade600, foregroundColor: Colors.white, @@ -526,7 +531,7 @@ class ManagerDashboard extends StatelessWidget { padding: EdgeInsets.symmetric(vertical: 10.h), side: BorderSide(color: Colors.grey.shade400), ), - child: Text('View', style: TextStyle(fontSize: 13.sp)), + child: Text(l.actionView, style: TextStyle(fontSize: 13.sp)), ), ), ], @@ -540,7 +545,7 @@ class ManagerDashboard extends StatelessWidget { Get.to(() => const ChitGroupsPage()); }, icon: Icon(Icons.arrow_forward, size: 18.w), - label: Text('Manage Group', style: TextStyle(fontSize: 14.sp)), + label: Text(l.actionManageGroup, style: TextStyle(fontSize: 14.sp)), style: OutlinedButton.styleFrom( padding: EdgeInsets.symmetric(vertical: 12.h), ), @@ -583,14 +588,14 @@ class ManagerDashboard extends StatelessWidget { } } - String _getStatusText(String status) { + String _getStatusText(AppLocalizations l, String status) { switch (status.toLowerCase()) { case 'active': - return 'Active'; + return l.groupStatusActive; case 'forming': - return 'Forming'; + return l.groupStatusForming; case 'completed': - return 'Completed'; + return l.groupStatusCompleted; default: return status; } @@ -656,16 +661,16 @@ class ManagerDashboard extends StatelessWidget { ); } - Widget _buildDesktopActivitiesAndActions() { + Widget _buildDesktopActivitiesAndActions(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // My Chitfunds (full width on desktop) - _buildRecentActivitiesSection(), + _buildRecentActivitiesSection(context), SizedBox(height: 24.h), // Quick Actions (below chitfunds) - _buildQuickActionsSection(), + _buildQuickActionsSection(context), ], ); } @@ -674,27 +679,30 @@ class ManagerDashboard extends StatelessWidget { void _showLogoutDialog(BuildContext context) { showDialog( context: context, - builder: (context) => AlertDialog( - title: const Text('Logout'), - content: const Text('Are you sure you want to logout?'), - actions: [ - TextButton( - onPressed: () => Navigator.of(context).pop(), - child: const Text('Cancel'), - ), - ElevatedButton( - onPressed: () { - Navigator.of(context).pop(); - AuthService.to.logout(); - }, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.red, - foregroundColor: Colors.white, + builder: (dialogContext) { + final l = dialogContext.l10n; + return AlertDialog( + title: Text(l.logoutConfirmTitle), + content: Text(l.logoutConfirmMessage), + actions: [ + TextButton( + onPressed: () => Navigator.of(dialogContext).pop(), + child: Text(l.cancel), ), - child: const Text('Logout'), - ), - ], - ), + ElevatedButton( + onPressed: () { + Navigator.of(dialogContext).pop(); + AuthService.to.logout(); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.red, + foregroundColor: Colors.white, + ), + child: Text(l.logout), + ), + ], + ); + }, ); } @@ -712,8 +720,8 @@ class ManagerDashboard extends StatelessWidget { // Reload groups after successful import ChitGroupService.to.loadManagerChitGroups(); SnackbarUtil.showSuccess( - 'Group imported! Now add members and backfill past data.', - title: 'Success', + L10nSvc.of().groupImportedMessage, + title: L10nSvc.of().groupImportedTitle, ); } }); @@ -729,8 +737,8 @@ class ManagerDashboard extends StatelessWidget { void _navigateToPayments() { SnackbarUtil.showInfo( - 'Payments page will be implemented next', - title: 'Coming Soon', + L10nSvc.of().paymentsPageComingSoon, + title: L10nSvc.of().comingSoonTitle, ); } } diff --git a/luckychit/lib/interfaces/member/member_dashboard.dart b/luckychit/lib/interfaces/member/member_dashboard.dart index 4a09276..69fe6d1 100644 --- a/luckychit/lib/interfaces/member/member_dashboard.dart +++ b/luckychit/lib/interfaces/member/member_dashboard.dart @@ -10,6 +10,8 @@ import '../../shared/widgets/notification_badge.dart'; import '../../features/notifications/notification_center_page.dart'; import 'member_group_details_page.dart'; import 'member_payment_dialog.dart'; +import '../../l10n/app_localizations.dart'; +import '../../l10n/l10n_x.dart'; class MemberDashboard extends StatefulWidget { const MemberDashboard({super.key}); @@ -38,12 +40,13 @@ class _MemberDashboardState extends State { @override Widget build(BuildContext context) { + final l = context.l10n; final scheme = Theme.of(context).colorScheme; return Scaffold( backgroundColor: scheme.surface, appBar: AppBar( title: Text( - 'My Chitfunds', + l.pageMyChitfunds, style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.w600, @@ -63,7 +66,7 @@ class _MemberDashboardState extends State { child: Icon(Icons.notifications_rounded, size: 24.w), ), onPressed: () => Get.to(() => const NotificationCenterPage()), - tooltip: 'Notifications', + tooltip: l.notificationsTooltip, ); }), IconButton( @@ -116,7 +119,9 @@ class _MemberDashboardState extends State { SizedBox(width: 12.w), Expanded( child: Text( - 'Welcome, ${AuthService.to.currentUser.value?.fullName ?? 'Member'}!', + l.memberWelcomeGreeting( + AuthService.to.currentUser.value?.fullName ?? l.memberFallbackName, + ), style: TextStyle( fontSize: 20.sp, fontWeight: FontWeight.bold, @@ -128,9 +133,7 @@ class _MemberDashboardState extends State { ), SizedBox(height: 12.h), Text( - groups.isEmpty - ? 'Join a chit fund to start managing your investments.' - : 'Manage your chit fund investments and track your payments.', + groups.isEmpty ? l.memberSubtitleEmpty : l.memberSubtitleHasGroups, style: TextStyle( fontSize: 16.sp, color: scheme.onPrimaryContainer.withOpacity(0.92), @@ -157,7 +160,7 @@ class _MemberDashboardState extends State { if (!isLoading && groups.isNotEmpty) ...[ Text( - 'My Chitfunds', + l.sectionMyChitfunds, style: TextStyle( fontSize: 20.sp, fontWeight: FontWeight.bold, @@ -187,31 +190,31 @@ class _MemberDashboardState extends State { unselectedLabelStyle: TextStyle(fontSize: 12.sp), iconSize: 24.w, items: [ - const BottomNavigationBarItem( - icon: Icon(Icons.home), - label: 'Home', + BottomNavigationBarItem( + icon: const Icon(Icons.home), + label: l.navHome, ), - const BottomNavigationBarItem( - icon: Icon(Icons.payment), - label: 'Payments', + BottomNavigationBarItem( + icon: const Icon(Icons.payment), + label: l.navPayments, ), BottomNavigationBarItem( icon: NotificationBadge( count: unreadCount, child: const Icon(Icons.notifications), ), - label: 'Notifications', + label: l.navNotifications, ), - const BottomNavigationBarItem( - icon: Icon(Icons.person), - label: 'Profile', + BottomNavigationBarItem( + icon: const Icon(Icons.person), + label: l.navProfile, ), ], onTap: (index) { if (index == 2) { Get.to(() => const NotificationCenterPage()); } else { - SnackbarUtil.showInfo('Feature coming soon'); + SnackbarUtil.showInfo(l.featureComingSoonMessage); } }, ); @@ -220,6 +223,7 @@ class _MemberDashboardState extends State { } Widget _buildEmptyState(BuildContext context) { + final l = context.l10n; final scheme = Theme.of(context).colorScheme; return Center( child: Padding( @@ -234,7 +238,7 @@ class _MemberDashboardState extends State { ), SizedBox(height: 24.h), Text( - 'No Chit Funds Yet', + l.memberEmptyChitTitle, style: TextStyle( fontSize: 22.sp, fontWeight: FontWeight.bold, @@ -243,7 +247,7 @@ class _MemberDashboardState extends State { ), SizedBox(height: 12.h), Text( - 'You haven\'t joined any chit funds yet.\nContact your manager to get started!', + l.memberEmptyChitBody, textAlign: TextAlign.center, style: TextStyle( fontSize: 16.sp, @@ -270,7 +274,7 @@ class _MemberDashboardState extends State { ), SizedBox(width: 8.w), Text( - 'How to get started?', + l.memberHowToStartTitle, style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.bold, @@ -281,9 +285,7 @@ class _MemberDashboardState extends State { ), SizedBox(height: 12.h), Text( - '1. Your manager will add you to a chit group\n' - '2. You\'ll receive a notification\n' - '3. Start managing your payments here!', + l.memberHowToStartBody, style: TextStyle( fontSize: 14.sp, color: scheme.onSurfaceVariant, @@ -300,9 +302,10 @@ class _MemberDashboardState extends State { } Widget _buildGroupCard(BuildContext context, dynamic group) { + final l = context.l10n; final scheme = Theme.of(context).colorScheme; final color = scheme.primary; - final groupName = group.name ?? 'Unnamed Group'; + final groupName = group.name ?? l.unnamedGroupLong; final totalValue = group.totalValue ?? 0.0; final monthlyInstallment = group.monthlyInstallment ?? 0.0; final durationMonths = group.durationMonths ?? 0; @@ -343,7 +346,7 @@ class _MemberDashboardState extends State { border: Border.all(color: _getStatusColor(context, status), width: 1.5), ), child: Text( - _getStatusText(status), + _memberStatusLabel(l, status), style: TextStyle( fontSize: 14.sp, color: _getStatusColor(context, status), @@ -359,11 +362,15 @@ class _MemberDashboardState extends State { Row( children: [ Expanded( - child: _buildGroupInfo(context, 'Total Value', totalValueFormatted), + child: _buildGroupInfo(context, l.labelTotalValue, totalValueFormatted), ), SizedBox(width: 8.w), Expanded( - child: _buildGroupInfo(context, 'Duration', '$durationMonths months'), + child: _buildGroupInfo( + context, + l.labelDuration, + '$durationMonths ${l.monthsSuffix}', + ), ), ], ), @@ -371,11 +378,11 @@ class _MemberDashboardState extends State { Row( children: [ Expanded( - child: _buildGroupInfo(context, 'Installment', installmentValueFormatted), + child: _buildGroupInfo(context, l.labelInstallment, installmentValueFormatted), ), SizedBox(width: 8.w), Expanded( - child: _buildGroupInfo(context, 'Status', _getStatusText(status)), + child: _buildGroupInfo(context, l.labelStatus, _memberStatusLabel(l, status)), ), ], ), @@ -393,7 +400,7 @@ class _MemberDashboardState extends State { onPressed: () => _showPaymentDialog(group), icon: Icon(Icons.payment, size: 20.w), label: Text( - 'Pay Now', + l.payNowButton, style: TextStyle( fontSize: 15.sp, fontWeight: FontWeight.w600, @@ -424,7 +431,7 @@ class _MemberDashboardState extends State { ), ), child: Text( - 'Details', + l.detailsButton, style: TextStyle( fontSize: 15.sp, fontWeight: FontWeight.w600, @@ -449,7 +456,7 @@ class _MemberDashboardState extends State { ); if (myMember == null) { - SnackbarUtil.showError('Member information not found'); + SnackbarUtil.showError(context.l10n.memberInfoNotFound); return; } @@ -481,8 +488,19 @@ class _MemberDashboardState extends State { } } - String _getStatusText(String status) { - return status[0].toUpperCase() + status.substring(1).toLowerCase(); + String _memberStatusLabel(AppLocalizations l, String status) { + switch (status.toLowerCase()) { + case 'active': + return l.groupStatusActive; + case 'pending': + case 'forming': + return l.groupStatusPending; + case 'completed': + return l.groupStatusCompleted; + default: + if (status.isEmpty) return status; + return status[0].toUpperCase() + status.substring(1).toLowerCase(); + } } String _formatCurrency(double amount) { @@ -532,27 +550,30 @@ class _MemberDashboardState extends State { void _showLogoutDialog(BuildContext context) { showDialog( context: context, - builder: (context) => AlertDialog( - title: const Text('Logout'), - content: const Text('Are you sure you want to logout?'), - actions: [ - TextButton( - onPressed: () => Navigator.of(context).pop(), - child: const Text('Cancel'), - ), - ElevatedButton( - onPressed: () { - Navigator.of(context).pop(); - AuthService.to.logout(); - }, - style: ElevatedButton.styleFrom( - backgroundColor: Theme.of(context).colorScheme.error, - foregroundColor: Theme.of(context).colorScheme.onError, + builder: (dialogContext) { + final l = dialogContext.l10n; + return AlertDialog( + title: Text(l.logoutConfirmTitle), + content: Text(l.logoutConfirmMessage), + actions: [ + TextButton( + onPressed: () => Navigator.of(dialogContext).pop(), + child: Text(l.cancel), ), - child: const Text('Logout'), - ), - ], - ), + ElevatedButton( + onPressed: () { + Navigator.of(dialogContext).pop(); + AuthService.to.logout(); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Theme.of(dialogContext).colorScheme.error, + foregroundColor: Theme.of(dialogContext).colorScheme.onError, + ), + child: Text(l.logout), + ), + ], + ); + }, ); } } diff --git a/luckychit/lib/l10n/app_en.arb b/luckychit/lib/l10n/app_en.arb new file mode 100644 index 0000000..1ccd386 --- /dev/null +++ b/luckychit/lib/l10n/app_en.arb @@ -0,0 +1,69 @@ +{ + "@@locale": "en", + "appTitle": "LuckyChit", + "settingsTitle": "Settings", + "sectionAppearance": "Appearance", + "sectionLanguage": "Language", + "sectionAccount": "Account", + "sectionPaymentSettings": "Payment Settings", + "sectionNotifications": "Notifications", + "sectionAbout": "About", + "languageTitle": "App language", + "languageSubtitle": "English or Telugu", + "languageEnglish": "English", + "languageTelugu": "Telugu (తెలుగు)", + "chooseLanguageTitle": "Choose language", + "themeTitle": "Theme", + "themeLight": "Light", + "themeDark": "Dark", + "themeSystem": "System default", + "chooseThemeTitle": "Choose theme", + "darkModeTitle": "Dark mode", + "darkModeSubtitle": "Override system settings", + "changePasswordTitle": "Change password", + "changePasswordSubtitle": "Update your password", + "pushNotificationsTitle": "Push notifications", + "pushNotificationsSubtitle": "Receive push notifications", + "paymentRemindersTitle": "Payment reminders", + "paymentRemindersSubtitle": "Reminders for upcoming payments", + "drawNotificationsTitle": "Draw notifications", + "drawNotificationsSubtitle": "Alerts for lottery draws", + "upiIdTitle": "UPI ID", + "loading": "Loading…", + "active": "Active", + "notConfigured": "Not configured", + "configureBackend": "Configure in backend/.env", + "copyUpiIdTooltip": "Copy UPI ID", + "upiCopiedClipboard": "UPI ID copied to clipboard", + "paymentStatisticsTitle": "Payment statistics", + "paymentStatisticsSubtitle": "View payment insights", + "transactionFeesTitle": "Transaction fees", + "transactionFeesSubtitle": "0% fees • Save lakhs per year!", + "free": "FREE", + "appVersionTitle": "App version", + "privacyPolicyTitle": "Privacy policy", + "termsOfServiceTitle": "Terms of service", + "logout": "Logout", + "logoutConfirmTitle": "Logout", + "logoutConfirmMessage": "Are you sure you want to logout?", + "cancel": "Cancel", + "close": "Close", + "copied": "Copied!", + "profileComingSoon": "Profile page coming soon", + "changePasswordComingSoon": "Change password feature coming soon", + "notificationComingSoon": "Notification settings coming soon", + "paymentStatsComingSoon": "Payment statistics coming soon", + "privacyComingSoon": "Privacy policy page coming soon", + "termsComingSoon": "Terms of service page coming soon", + "loggedOutSuccess": "Logged out successfully", + "upiPaymentSettingsTitle": "UPI payment settings", + "currentUpiId": "Current UPI ID", + "upiNotConfiguredMessage": "UPI ID not configured. Update backend/.env file.", + "howToUpdateUpi": "How to update UPI ID", + "stepOpenEnv": "Open backend/.env file", + "stepUpdatePhonepe": "Update PHONEPE_UPI_ID=your_upi@paytm", + "stepRestartBackend": "Restart backend server", + "stepRefreshScreen": "Refresh this screen", + "proTipTitle": "Pro tip", + "proTipBody": "Members can pay using ANY UPI app (PhonePe, GPay, Paytm) directly to your personal UPI ID with 0% transaction fees!" +} diff --git a/luckychit/lib/l10n/app_localizations.dart b/luckychit/lib/l10n/app_localizations.dart new file mode 100644 index 0000000..bd7b859 --- /dev/null +++ b/luckychit/lib/l10n/app_localizations.dart @@ -0,0 +1,694 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:intl/intl.dart' as intl; + +import 'app_localizations_en.dart'; +import 'app_localizations_te.dart'; + +// ignore_for_file: type=lint + +/// Callers can lookup localized strings with an instance of AppLocalizations +/// returned by `AppLocalizations.of(context)`. +/// +/// Applications need to include `AppLocalizations.delegate()` in their app's +/// `localizationDelegates` list, and the locales they support in the app's +/// `supportedLocales` list. For example: +/// +/// ```dart +/// import 'l10n/app_localizations.dart'; +/// +/// return MaterialApp( +/// localizationsDelegates: AppLocalizations.localizationsDelegates, +/// supportedLocales: AppLocalizations.supportedLocales, +/// home: MyApplicationHome(), +/// ); +/// ``` +/// +/// ## Update pubspec.yaml +/// +/// Please make sure to update your pubspec.yaml to include the following +/// packages: +/// +/// ```yaml +/// dependencies: +/// # Internationalization support. +/// flutter_localizations: +/// sdk: flutter +/// intl: any # Use the pinned version from flutter_localizations +/// +/// # Rest of dependencies +/// ``` +/// +/// ## iOS Applications +/// +/// iOS applications define key application metadata, including supported +/// locales, in an Info.plist file that is built into the application bundle. +/// To configure the locales supported by your app, you’ll need to edit this +/// file. +/// +/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file. +/// Then, in the Project Navigator, open the Info.plist file under the Runner +/// project’s Runner folder. +/// +/// Next, select the Information Property List item, select Add Item from the +/// Editor menu, then select Localizations from the pop-up menu. +/// +/// Select and expand the newly-created Localizations item then, for each +/// locale your application supports, add a new item and select the locale +/// you wish to add from the pop-up menu in the Value field. This list should +/// be consistent with the languages listed in the AppLocalizations.supportedLocales +/// property. +abstract class AppLocalizations { + AppLocalizations(String locale) : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + + final String localeName; + + static AppLocalizations? of(BuildContext context) { + return Localizations.of(context, AppLocalizations); + } + + static const LocalizationsDelegate delegate = _AppLocalizationsDelegate(); + + /// A list of this localizations delegate along with the default localizations + /// delegates. + /// + /// Returns a list of localizations delegates containing this delegate along with + /// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, + /// and GlobalWidgetsLocalizations.delegate. + /// + /// Additional delegates can be added by appending to this list in + /// MaterialApp. This list does not have to be used at all if a custom list + /// of delegates is preferred or required. + static const List> localizationsDelegates = >[ + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; + + /// A list of this localizations delegate's supported locales. + static const List supportedLocales = [ + Locale('en'), + Locale('te') + ]; + + /// No description provided for @appTitle. + /// + /// In en, this message translates to: + /// **'LuckyChit'** + String get appTitle; + + /// No description provided for @settingsTitle. + /// + /// In en, this message translates to: + /// **'Settings'** + String get settingsTitle; + + /// No description provided for @sectionAppearance. + /// + /// In en, this message translates to: + /// **'Appearance'** + String get sectionAppearance; + + /// No description provided for @sectionLanguage. + /// + /// In en, this message translates to: + /// **'Language'** + String get sectionLanguage; + + /// No description provided for @sectionAccount. + /// + /// In en, this message translates to: + /// **'Account'** + String get sectionAccount; + + /// No description provided for @sectionPaymentSettings. + /// + /// In en, this message translates to: + /// **'Payment Settings'** + String get sectionPaymentSettings; + + /// No description provided for @sectionNotifications. + /// + /// In en, this message translates to: + /// **'Notifications'** + String get sectionNotifications; + + /// No description provided for @sectionAbout. + /// + /// In en, this message translates to: + /// **'About'** + String get sectionAbout; + + /// No description provided for @languageTitle. + /// + /// In en, this message translates to: + /// **'App language'** + String get languageTitle; + + /// No description provided for @languageSubtitle. + /// + /// In en, this message translates to: + /// **'English or Telugu'** + String get languageSubtitle; + + /// No description provided for @languageEnglish. + /// + /// In en, this message translates to: + /// **'English'** + String get languageEnglish; + + /// No description provided for @languageTelugu. + /// + /// In en, this message translates to: + /// **'Telugu (తెలుగు)'** + String get languageTelugu; + + /// No description provided for @chooseLanguageTitle. + /// + /// In en, this message translates to: + /// **'Choose language'** + String get chooseLanguageTitle; + + /// No description provided for @themeTitle. + /// + /// In en, this message translates to: + /// **'Theme'** + String get themeTitle; + + /// No description provided for @themeLight. + /// + /// In en, this message translates to: + /// **'Light'** + String get themeLight; + + /// No description provided for @themeDark. + /// + /// In en, this message translates to: + /// **'Dark'** + String get themeDark; + + /// No description provided for @themeSystem. + /// + /// In en, this message translates to: + /// **'System default'** + String get themeSystem; + + /// No description provided for @chooseThemeTitle. + /// + /// In en, this message translates to: + /// **'Choose theme'** + String get chooseThemeTitle; + + /// No description provided for @darkModeTitle. + /// + /// In en, this message translates to: + /// **'Dark mode'** + String get darkModeTitle; + + /// No description provided for @darkModeSubtitle. + /// + /// In en, this message translates to: + /// **'Override system settings'** + String get darkModeSubtitle; + + /// No description provided for @changePasswordTitle. + /// + /// In en, this message translates to: + /// **'Change password'** + String get changePasswordTitle; + + /// No description provided for @changePasswordSubtitle. + /// + /// In en, this message translates to: + /// **'Update your password'** + String get changePasswordSubtitle; + + /// No description provided for @pushNotificationsTitle. + /// + /// In en, this message translates to: + /// **'Push notifications'** + String get pushNotificationsTitle; + + /// No description provided for @pushNotificationsSubtitle. + /// + /// In en, this message translates to: + /// **'Receive push notifications'** + String get pushNotificationsSubtitle; + + /// No description provided for @paymentRemindersTitle. + /// + /// In en, this message translates to: + /// **'Payment reminders'** + String get paymentRemindersTitle; + + /// No description provided for @paymentRemindersSubtitle. + /// + /// In en, this message translates to: + /// **'Reminders for upcoming payments'** + String get paymentRemindersSubtitle; + + /// No description provided for @drawNotificationsTitle. + /// + /// In en, this message translates to: + /// **'Draw notifications'** + String get drawNotificationsTitle; + + /// No description provided for @drawNotificationsSubtitle. + /// + /// In en, this message translates to: + /// **'Alerts for lottery draws'** + String get drawNotificationsSubtitle; + + /// No description provided for @upiIdTitle. + /// + /// In en, this message translates to: + /// **'UPI ID'** + String get upiIdTitle; + + /// No description provided for @loading. + /// + /// In en, this message translates to: + /// **'Loading…'** + String get loading; + + /// No description provided for @active. + /// + /// In en, this message translates to: + /// **'Active'** + String get active; + + /// No description provided for @notConfigured. + /// + /// In en, this message translates to: + /// **'Not configured'** + String get notConfigured; + + /// No description provided for @configureBackend. + /// + /// In en, this message translates to: + /// **'Configure in backend/.env'** + String get configureBackend; + + /// No description provided for @copyUpiIdTooltip. + /// + /// In en, this message translates to: + /// **'Copy UPI ID'** + String get copyUpiIdTooltip; + + /// No description provided for @upiCopiedClipboard. + /// + /// In en, this message translates to: + /// **'UPI ID copied to clipboard'** + String get upiCopiedClipboard; + + /// No description provided for @paymentStatisticsTitle. + /// + /// In en, this message translates to: + /// **'Payment statistics'** + String get paymentStatisticsTitle; + + /// No description provided for @paymentStatisticsSubtitle. + /// + /// In en, this message translates to: + /// **'View payment insights'** + String get paymentStatisticsSubtitle; + + /// No description provided for @transactionFeesTitle. + /// + /// In en, this message translates to: + /// **'Transaction fees'** + String get transactionFeesTitle; + + /// No description provided for @transactionFeesSubtitle. + /// + /// In en, this message translates to: + /// **'0% fees • Save lakhs per year!'** + String get transactionFeesSubtitle; + + /// No description provided for @free. + /// + /// In en, this message translates to: + /// **'FREE'** + String get free; + + /// No description provided for @appVersionTitle. + /// + /// In en, this message translates to: + /// **'App version'** + String get appVersionTitle; + + /// No description provided for @privacyPolicyTitle. + /// + /// In en, this message translates to: + /// **'Privacy policy'** + String get privacyPolicyTitle; + + /// No description provided for @termsOfServiceTitle. + /// + /// In en, this message translates to: + /// **'Terms of service'** + String get termsOfServiceTitle; + + /// No description provided for @logout. + /// + /// In en, this message translates to: + /// **'Logout'** + String get logout; + + /// No description provided for @logoutConfirmTitle. + /// + /// In en, this message translates to: + /// **'Logout'** + String get logoutConfirmTitle; + + /// No description provided for @logoutConfirmMessage. + /// + /// In en, this message translates to: + /// **'Are you sure you want to logout?'** + String get logoutConfirmMessage; + + /// No description provided for @cancel. + /// + /// In en, this message translates to: + /// **'Cancel'** + String get cancel; + + /// No description provided for @close. + /// + /// In en, this message translates to: + /// **'Close'** + String get close; + + /// No description provided for @copied. + /// + /// In en, this message translates to: + /// **'Copied!'** + String get copied; + + /// No description provided for @profileComingSoon. + /// + /// In en, this message translates to: + /// **'Profile page coming soon'** + String get profileComingSoon; + + /// No description provided for @changePasswordComingSoon. + /// + /// In en, this message translates to: + /// **'Change password feature coming soon'** + String get changePasswordComingSoon; + + /// No description provided for @notificationComingSoon. + /// + /// In en, this message translates to: + /// **'Notification settings coming soon'** + String get notificationComingSoon; + + /// No description provided for @paymentStatsComingSoon. + /// + /// In en, this message translates to: + /// **'Payment statistics coming soon'** + String get paymentStatsComingSoon; + + /// No description provided for @privacyComingSoon. + /// + /// In en, this message translates to: + /// **'Privacy policy page coming soon'** + String get privacyComingSoon; + + /// No description provided for @termsComingSoon. + /// + /// In en, this message translates to: + /// **'Terms of service page coming soon'** + String get termsComingSoon; + + /// No description provided for @loggedOutSuccess. + /// + /// In en, this message translates to: + /// **'Logged out successfully'** + String get loggedOutSuccess; + + /// No description provided for @upiPaymentSettingsTitle. + /// + /// In en, this message translates to: + /// **'UPI payment settings'** + String get upiPaymentSettingsTitle; + + /// No description provided for @currentUpiId. + /// + /// In en, this message translates to: + /// **'Current UPI ID'** + String get currentUpiId; + + /// No description provided for @upiNotConfiguredMessage. + /// + /// In en, this message translates to: + /// **'UPI ID not configured. Update backend/.env file.'** + String get upiNotConfiguredMessage; + + /// No description provided for @howToUpdateUpi. + /// + /// In en, this message translates to: + /// **'How to update UPI ID'** + String get howToUpdateUpi; + + /// No description provided for @stepOpenEnv. + /// + /// In en, this message translates to: + /// **'Open backend/.env file'** + String get stepOpenEnv; + + /// No description provided for @stepUpdatePhonepe. + /// + /// In en, this message translates to: + /// **'Update PHONEPE_UPI_ID=your_upi@paytm'** + String get stepUpdatePhonepe; + + /// No description provided for @stepRestartBackend. + /// + /// In en, this message translates to: + /// **'Restart backend server'** + String get stepRestartBackend; + + /// No description provided for @stepRefreshScreen. + /// + /// In en, this message translates to: + /// **'Refresh this screen'** + String get stepRefreshScreen; + + /// No description provided for @proTipTitle. + /// + /// In en, this message translates to: + /// **'Pro tip'** + String get proTipTitle; + + /// No description provided for @proTipBody. + /// + /// In en, this message translates to: + /// **'Members can pay using ANY UPI app (PhonePe, GPay, Paytm) directly to your personal UPI ID with 0% transaction fees!'** + String get proTipBody; + + String get ok; + String get save; + String get retry; + String get delete; + String get userLabel; + String get managerFallbackName; + + String get snackTitleError; + String get snackTitleSuccess; + String get operationFailedShort; + + String get failedLoadChitGroups; + String get chitfundCreatedSuccess; + String get failedCreateChitGroup; + String get failedUpdateChitGroup; + String get chitGroupDeletedSuccess; + String get failedDeleteChitGroup; + String get failedLoadGroupDetails; + String get failedLoadGroupMembers; + String get memberAddedSuccess; + String get failedAddMember; + String get memberRemovedSuccess; + String get failedRemoveMember; + String get memberStatusUpdatedSuccess; + String get failedUpdateMemberStatus; + String get failedLoadPayments; + String get paymentRecordedSuccess; + String get failedRecordPayment; + String get failedLoadGroupStatistics; + String get chitfundStartedSuccess; + String get failedStartChitGroup; + String get failedLoadMonthlyDraws; + String get failedCreateMonthlyDraw; + String get failedLoadDrawStatistics; + String get failedLoadFinancialData; + + String get signupFailedTitle; + String get signupFailedGeneric; + String get loginFailedTitle; + String get loginFailedGeneric; + String get passwordChangedSuccess; + String get failedChangePassword; + + String get stateSomethingWentWrong; + + String get emptyNoGroupsTitle; + String get emptyNoGroupsMessage; + String get emptyNoGroupsAction; + String get emptyNoMembersTitle; + String get emptyNoMembersMessage; + String get emptyNoMembersAction; + String get emptyNoPaymentsTitle; + String get emptyNoPaymentsMessage; + String get emptyNoPaymentsAction; + String get emptyNoActivitiesTitle; + String get emptyNoActivitiesMessage; + String get emptyNoActivitiesAction; + String get emptyNoResultsTitle; + String get emptyNoResultsMessage; + String get emptyNoResultsAction; + String get emptyErrorTitle; + String get emptyErrorMessage; + String get emptyErrorAction; + String get emptyNoInternetTitle; + String get emptyNoInternetMessage; + String get emptyNoInternetAction; + + String get dashboardTitle; + String get notificationsTooltip; + String get recordingsTooltip; + String get testDrawTooltip; + String get chitFundManagerRole; + String get menuDashboard; + String get menuMyChitfunds; + String get menuMembers; + String get menuPayments; + String get menuLotteryDraws; + String get menuReports; + String get welcomeBackTitle; + String get welcomeBackSubtitle; + String get quickActionsTitle; + String get qaCreateChitfundTitle; + String get qaCreateChitfundSubtitle; + String get qaImportChitfundTitle; + String get qaImportChitfundSubtitle; + String get qaViewAllChitfundsTitle; + String get qaViewAllChitfundsSubtitle; + String get qaManageMembersTitle; + String get qaManageMembersSubtitle; + String get qaPaymentRecordsTitle; + String get qaPaymentRecordsSubtitle; + String get sectionMyChitfunds; + String get viewAll; + String get noChitFundsYetShort; + String get groupStatusActive; + String get groupStatusForming; + String get groupStatusCompleted; + String get unnamedGroup; + String get actionRecord; + String get actionDraw; + String get actionView; + String get actionManageGroup; + String get groupImportedMessage; + String get groupImportedTitle; + String get paymentsPageComingSoon; + String get comingSoonTitle; + + String get pageMyChitfunds; + String get createNewGroupMenu; + String get importExistingGroupMenu; + + String get appDisplayName; + String get authLoginTagline; + String get authSignupScreenTitle; + String get authSignupTagline; + String get labelMobileNumber; + String get labelMobileNumberRequired; + String get labelPassword; + String get labelPasswordRequired; + String get labelFullNameRequired; + String get labelEmailOptional; + String get labelAddressOptional; + String get labelEmergencyContactOptional; + String get labelConfirmPasswordRequired; + String get validatorEnterMobile; + String get validatorMobileTenDigits; + String get validatorMobileDigitsOnly; + String get validatorEnterFullName; + String get validatorValidEmail; + String get validatorEmergencyTenDigits; + String get validatorEmergencyDigitsOnly; + String get validatorEnterPasswordAuth; + String get validatorPasswordMinSixAuth; + String get validatorConfirmPassword; + String get validatorPasswordsMismatch; + String get tooltipShowPassword; + String get tooltipHidePassword; + String get signInButton; + String get createAccountButton; + String get alreadyHaveAccount; + String get loginLink; + String get loginInvalidCredentials; + String get signupSuccessWelcome; + String get signupFailedGenericUi; + String get featureComingSoonMessage; + + String memberWelcomeGreeting(String name); + String get memberFallbackName; + String get memberSubtitleEmpty; + String get memberSubtitleHasGroups; + String get navHome; + String get navPayments; + String get navNotifications; + String get navProfile; + String get memberEmptyChitTitle; + String get memberEmptyChitBody; + String get memberHowToStartTitle; + String get memberHowToStartBody; + String get unnamedGroupLong; + String get labelTotalValue; + String get labelDuration; + String get monthsSuffix; + String get labelInstallment; + String get labelStatus; + String get groupStatusPending; + String get payNowButton; + String get detailsButton; + String get memberInfoNotFound; +} + +class _AppLocalizationsDelegate extends LocalizationsDelegate { + const _AppLocalizationsDelegate(); + + @override + Future load(Locale locale) { + return SynchronousFuture(lookupAppLocalizations(locale)); + } + + @override + bool isSupported(Locale locale) => ['en', 'te'].contains(locale.languageCode); + + @override + bool shouldReload(_AppLocalizationsDelegate old) => false; +} + +AppLocalizations lookupAppLocalizations(Locale locale) { + + + // Lookup logic when only language code is specified. + switch (locale.languageCode) { + case 'en': return AppLocalizationsEn(); + case 'te': return AppLocalizationsTe(); + } + + throw FlutterError( + 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' + 'an issue with the localizations generation tool. Please file an issue ' + 'on GitHub with a reproducible sample app and the gen-l10n configuration ' + 'that was used.' + ); +} diff --git a/luckychit/lib/l10n/app_localizations_en.dart b/luckychit/lib/l10n/app_localizations_en.dart new file mode 100644 index 0000000..d0b0db6 --- /dev/null +++ b/luckychit/lib/l10n/app_localizations_en.dart @@ -0,0 +1,706 @@ +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for English (`en`). +class AppLocalizationsEn extends AppLocalizations { + AppLocalizationsEn([String locale = 'en']) : super(locale); + + @override + String get appTitle => 'LuckyChit'; + + @override + String get settingsTitle => 'Settings'; + + @override + String get sectionAppearance => 'Appearance'; + + @override + String get sectionLanguage => 'Language'; + + @override + String get sectionAccount => 'Account'; + + @override + String get sectionPaymentSettings => 'Payment Settings'; + + @override + String get sectionNotifications => 'Notifications'; + + @override + String get sectionAbout => 'About'; + + @override + String get languageTitle => 'App language'; + + @override + String get languageSubtitle => 'English or Telugu'; + + @override + String get languageEnglish => 'English'; + + @override + String get languageTelugu => 'Telugu (తెలుగు)'; + + @override + String get chooseLanguageTitle => 'Choose language'; + + @override + String get themeTitle => 'Theme'; + + @override + String get themeLight => 'Light'; + + @override + String get themeDark => 'Dark'; + + @override + String get themeSystem => 'System default'; + + @override + String get chooseThemeTitle => 'Choose theme'; + + @override + String get darkModeTitle => 'Dark mode'; + + @override + String get darkModeSubtitle => 'Override system settings'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get changePasswordSubtitle => 'Update your password'; + + @override + String get pushNotificationsTitle => 'Push notifications'; + + @override + String get pushNotificationsSubtitle => 'Receive push notifications'; + + @override + String get paymentRemindersTitle => 'Payment reminders'; + + @override + String get paymentRemindersSubtitle => 'Reminders for upcoming payments'; + + @override + String get drawNotificationsTitle => 'Draw notifications'; + + @override + String get drawNotificationsSubtitle => 'Alerts for lottery draws'; + + @override + String get upiIdTitle => 'UPI ID'; + + @override + String get loading => 'Loading…'; + + @override + String get active => 'Active'; + + @override + String get notConfigured => 'Not configured'; + + @override + String get configureBackend => 'Configure in backend/.env'; + + @override + String get copyUpiIdTooltip => 'Copy UPI ID'; + + @override + String get upiCopiedClipboard => 'UPI ID copied to clipboard'; + + @override + String get paymentStatisticsTitle => 'Payment statistics'; + + @override + String get paymentStatisticsSubtitle => 'View payment insights'; + + @override + String get transactionFeesTitle => 'Transaction fees'; + + @override + String get transactionFeesSubtitle => '0% fees • Save lakhs per year!'; + + @override + String get free => 'FREE'; + + @override + String get appVersionTitle => 'App version'; + + @override + String get privacyPolicyTitle => 'Privacy policy'; + + @override + String get termsOfServiceTitle => 'Terms of service'; + + @override + String get logout => 'Logout'; + + @override + String get logoutConfirmTitle => 'Logout'; + + @override + String get logoutConfirmMessage => 'Are you sure you want to logout?'; + + @override + String get cancel => 'Cancel'; + + @override + String get close => 'Close'; + + @override + String get copied => 'Copied!'; + + @override + String get profileComingSoon => 'Profile page coming soon'; + + @override + String get changePasswordComingSoon => 'Change password feature coming soon'; + + @override + String get notificationComingSoon => 'Notification settings coming soon'; + + @override + String get paymentStatsComingSoon => 'Payment statistics coming soon'; + + @override + String get privacyComingSoon => 'Privacy policy page coming soon'; + + @override + String get termsComingSoon => 'Terms of service page coming soon'; + + @override + String get loggedOutSuccess => 'Logged out successfully'; + + @override + String get upiPaymentSettingsTitle => 'UPI payment settings'; + + @override + String get currentUpiId => 'Current UPI ID'; + + @override + String get upiNotConfiguredMessage => 'UPI ID not configured. Update backend/.env file.'; + + @override + String get howToUpdateUpi => 'How to update UPI ID'; + + @override + String get stepOpenEnv => 'Open backend/.env file'; + + @override + String get stepUpdatePhonepe => 'Update PHONEPE_UPI_ID=your_upi@paytm'; + + @override + String get stepRestartBackend => 'Restart backend server'; + + @override + String get stepRefreshScreen => 'Refresh this screen'; + + @override + String get proTipTitle => 'Pro tip'; + + @override + String get proTipBody => 'Members can pay using ANY UPI app (PhonePe, GPay, Paytm) directly to your personal UPI ID with 0% transaction fees!'; + + @override + String get ok => 'OK'; + + @override + String get save => 'Save'; + + @override + String get retry => 'Retry'; + + @override + String get delete => 'Delete'; + + @override + String get userLabel => 'User'; + + @override + String get managerFallbackName => 'Manager'; + + @override + String get snackTitleError => 'Error'; + + @override + String get snackTitleSuccess => 'Success'; + + @override + String get operationFailedShort => 'Failed'; + + @override + String get failedLoadChitGroups => 'Failed to load chit groups'; + + @override + String get chitfundCreatedSuccess => 'Chitfund created successfully'; + + @override + String get failedCreateChitGroup => 'Failed to create chit group'; + + @override + String get failedUpdateChitGroup => 'Failed to update chit group'; + + @override + String get chitGroupDeletedSuccess => 'Chit group deleted successfully'; + + @override + String get failedDeleteChitGroup => 'Failed to delete chit group'; + + @override + String get failedLoadGroupDetails => 'Failed to load group details'; + + @override + String get failedLoadGroupMembers => 'Failed to load group members'; + + @override + String get memberAddedSuccess => 'Member added successfully'; + + @override + String get failedAddMember => 'Failed to add member'; + + @override + String get memberRemovedSuccess => 'Member removed successfully'; + + @override + String get failedRemoveMember => 'Failed to remove member'; + + @override + String get memberStatusUpdatedSuccess => 'Member status updated successfully'; + + @override + String get failedUpdateMemberStatus => 'Failed to update member status'; + + @override + String get failedLoadPayments => 'Failed to load payments'; + + @override + String get paymentRecordedSuccess => 'Payment recorded successfully'; + + @override + String get failedRecordPayment => 'Failed to record payment'; + + @override + String get failedLoadGroupStatistics => 'Failed to load group statistics'; + + @override + String get chitfundStartedSuccess => 'Chitfund started successfully'; + + @override + String get failedStartChitGroup => 'Failed to start chit group'; + + @override + String get failedLoadMonthlyDraws => 'Failed to load monthly draws'; + + @override + String get failedCreateMonthlyDraw => 'Failed to create monthly draw'; + + @override + String get failedLoadDrawStatistics => 'Failed to load draw statistics'; + + @override + String get failedLoadFinancialData => 'Failed to load financial data'; + + @override + String get signupFailedTitle => 'Signup Failed'; + + @override + String get signupFailedGeneric => 'Signup failed. Please try again.'; + + @override + String get loginFailedTitle => 'Login Failed'; + + @override + String get loginFailedGeneric => 'Login failed. Please try again.'; + + @override + String get passwordChangedSuccess => 'Password changed successfully'; + + @override + String get failedChangePassword => 'Failed to change password'; + + @override + String get stateSomethingWentWrong => 'Something went wrong'; + + @override + String get emptyNoGroupsTitle => 'No Chit Groups Yet'; + + @override + String get emptyNoGroupsMessage => + 'You haven\'t created any chit groups yet.\nCreate your first group or import an existing one!'; + + @override + String get emptyNoGroupsAction => 'Create Group'; + + @override + String get emptyNoMembersTitle => 'No Members Yet'; + + @override + String get emptyNoMembersMessage => + 'This group doesn\'t have any members yet.\nAdd members to get started!'; + + @override + String get emptyNoMembersAction => 'Add Members'; + + @override + String get emptyNoPaymentsTitle => 'No Payments Yet'; + + @override + String get emptyNoPaymentsMessage => + 'No payment records found.\nPayments will appear here once recorded.'; + + @override + String get emptyNoPaymentsAction => 'Record Payment'; + + @override + String get emptyNoActivitiesTitle => 'No Recent Activities'; + + @override + String get emptyNoActivitiesMessage => + 'Your recent activities will appear here.\nStart using the app to see updates!'; + + @override + String get emptyNoActivitiesAction => 'Refresh'; + + @override + String get emptyNoResultsTitle => 'No Results Found'; + + @override + String get emptyNoResultsMessage => + 'We couldn\'t find what you\'re looking for.\nTry adjusting your search or filters.'; + + @override + String get emptyNoResultsAction => 'Clear Filters'; + + @override + String get emptyErrorTitle => 'Oops! Something Went Wrong'; + + @override + String get emptyErrorMessage => + 'We encountered an error while loading data.\nPlease try again.'; + + @override + String get emptyErrorAction => 'Retry'; + + @override + String get emptyNoInternetTitle => 'No Internet Connection'; + + @override + String get emptyNoInternetMessage => + 'Please check your internet connection\nand try again.'; + + @override + String get emptyNoInternetAction => 'Retry'; + + @override + String get dashboardTitle => 'Dashboard'; + + @override + String get notificationsTooltip => 'Notifications'; + + @override + String get recordingsTooltip => 'View draw recordings'; + + @override + String get testDrawTooltip => 'Test animated draw'; + + @override + String get chitFundManagerRole => 'Chit Fund Manager'; + + @override + String get menuDashboard => 'Dashboard'; + + @override + String get menuMyChitfunds => 'My Chitfunds'; + + @override + String get menuMembers => 'Members'; + + @override + String get menuPayments => 'Payments'; + + @override + String get menuLotteryDraws => 'Lottery Draws'; + + @override + String get menuReports => 'Reports'; + + @override + String get welcomeBackTitle => 'Welcome back!'; + + @override + String get welcomeBackSubtitle => + 'Here\'s what\'s happening with your chit funds today.'; + + @override + String get quickActionsTitle => 'Quick Actions'; + + @override + String get qaCreateChitfundTitle => 'Create New Chitfund'; + + @override + String get qaCreateChitfundSubtitle => 'Start a new chit fund group'; + + @override + String get qaImportChitfundTitle => 'Import Existing Chitfund'; + + @override + String get qaImportChitfundSubtitle => 'Add a group that already started'; + + @override + String get qaViewAllChitfundsTitle => 'View All Chitfunds'; + + @override + String get qaViewAllChitfundsSubtitle => 'Manage your existing groups'; + + @override + String get qaManageMembersTitle => 'Manage Members'; + + @override + String get qaManageMembersSubtitle => 'Add or remove members'; + + @override + String get qaPaymentRecordsTitle => 'Payment Records'; + + @override + String get qaPaymentRecordsSubtitle => 'Track all transactions'; + + @override + String get sectionMyChitfunds => 'My Chitfunds'; + + @override + String get viewAll => 'View All'; + + @override + String get noChitFundsYetShort => 'No chit funds yet'; + + @override + String get groupStatusActive => 'Active'; + + @override + String get groupStatusForming => 'Forming'; + + @override + String get groupStatusCompleted => 'Completed'; + + @override + String get unnamedGroup => 'Unnamed'; + + @override + String get actionRecord => 'Record'; + + @override + String get actionDraw => 'Draw'; + + @override + String get actionView => 'View'; + + @override + String get actionManageGroup => 'Manage Group'; + + @override + String get groupImportedMessage => + 'Group imported! Now add members and backfill past data.'; + + @override + String get groupImportedTitle => 'Success'; + + @override + String get paymentsPageComingSoon => 'Payments page will be implemented next'; + + @override + String get comingSoonTitle => 'Coming Soon'; + + @override + String get pageMyChitfunds => 'My Chitfunds'; + + @override + String get createNewGroupMenu => 'Create New Group'; + + @override + String get importExistingGroupMenu => 'Import Existing Group'; + + @override + String get appDisplayName => 'LuckyChit'; + + @override + String get authLoginTagline => + 'Chit fund management that feels effortless.'; + + @override + String get authSignupScreenTitle => 'Create account'; + + @override + String get authSignupTagline => 'Set up your profile in under a minute.'; + + @override + String get labelMobileNumber => 'Mobile number'; + + @override + String get labelMobileNumberRequired => 'Mobile number *'; + + @override + String get labelPassword => 'Password'; + + @override + String get labelPasswordRequired => 'Password *'; + + @override + String get labelFullNameRequired => 'Full name *'; + + @override + String get labelEmailOptional => 'Email (optional)'; + + @override + String get labelAddressOptional => 'Address (optional)'; + + @override + String get labelEmergencyContactOptional => 'Emergency contact (optional)'; + + @override + String get labelConfirmPasswordRequired => 'Confirm password *'; + + @override + String get validatorEnterMobile => 'Please enter mobile number'; + + @override + String get validatorMobileTenDigits => 'Mobile number must be 10 digits'; + + @override + String get validatorMobileDigitsOnly => + 'Mobile number must contain only digits'; + + @override + String get validatorEnterFullName => 'Please enter your full name'; + + @override + String get validatorValidEmail => 'Please enter a valid email address'; + + @override + String get validatorEmergencyTenDigits => + 'Emergency contact must be 10 digits'; + + @override + String get validatorEmergencyDigitsOnly => + 'Emergency contact must contain only digits'; + + @override + String get validatorEnterPasswordAuth => 'Please enter password'; + + @override + String get validatorPasswordMinSixAuth => + 'Password must be at least 6 characters'; + + @override + String get validatorConfirmPassword => 'Please confirm password'; + + @override + String get validatorPasswordsMismatch => 'Passwords do not match'; + + @override + String get tooltipShowPassword => 'Show password'; + + @override + String get tooltipHidePassword => 'Hide password'; + + @override + String get signInButton => 'Sign in'; + + @override + String get createAccountButton => 'Create account'; + + @override + String get alreadyHaveAccount => 'Already have an account? '; + + @override + String get loginLink => 'Login'; + + @override + String get loginInvalidCredentials => + 'Invalid mobile number or password. Please try again.'; + + @override + String get signupSuccessWelcome => + 'Account created successfully! Welcome to LuckyChit.'; + + @override + String get signupFailedGenericUi => + 'Failed to create account. Please try again.'; + + @override + String get featureComingSoonMessage => 'Feature coming soon'; + + @override + String memberWelcomeGreeting(String name) => 'Welcome, $name!'; + + @override + String get memberFallbackName => 'Member'; + + @override + String get memberSubtitleEmpty => + 'Join a chit fund to start managing your investments.'; + + @override + String get memberSubtitleHasGroups => + 'Manage your chit fund investments and track your payments.'; + + @override + String get navHome => 'Home'; + + @override + String get navPayments => 'Payments'; + + @override + String get navNotifications => 'Notifications'; + + @override + String get navProfile => 'Profile'; + + @override + String get memberEmptyChitTitle => 'No Chit Funds Yet'; + + @override + String get memberEmptyChitBody => + 'You haven\'t joined any chit funds yet.\nContact your manager to get started!'; + + @override + String get memberHowToStartTitle => 'How to get started?'; + + @override + String get memberHowToStartBody => + '1. Your manager will add you to a chit group\n' + '2. You\'ll receive a notification\n' + '3. Start managing your payments here!'; + + @override + String get unnamedGroupLong => 'Unnamed Group'; + + @override + String get labelTotalValue => 'Total Value'; + + @override + String get labelDuration => 'Duration'; + + @override + String get monthsSuffix => 'months'; + + @override + String get labelInstallment => 'Installment'; + + @override + String get labelStatus => 'Status'; + + @override + String get groupStatusPending => 'Pending'; + + @override + String get payNowButton => 'Pay Now'; + + @override + String get detailsButton => 'Details'; + + @override + String get memberInfoNotFound => 'Member information not found'; +} diff --git a/luckychit/lib/l10n/app_localizations_te.dart b/luckychit/lib/l10n/app_localizations_te.dart new file mode 100644 index 0000000..8cf9489 --- /dev/null +++ b/luckychit/lib/l10n/app_localizations_te.dart @@ -0,0 +1,714 @@ +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Telugu (`te`). +class AppLocalizationsTe extends AppLocalizations { + AppLocalizationsTe([String locale = 'te']) : super(locale); + + @override + String get appTitle => 'లక్కీచిట్'; + + @override + String get settingsTitle => 'సెట్టింగ్స్'; + + @override + String get sectionAppearance => 'రూపం'; + + @override + String get sectionLanguage => 'భాష'; + + @override + String get sectionAccount => 'ఖాతా'; + + @override + String get sectionPaymentSettings => 'చెల్లింపు సెట్టింగ్స్'; + + @override + String get sectionNotifications => 'నోటిఫికేషన్లు'; + + @override + String get sectionAbout => 'గురించి'; + + @override + String get languageTitle => 'యాప్ భాష'; + + @override + String get languageSubtitle => 'ఇంగ్లీషు లేదా తెలుగు'; + + @override + String get languageEnglish => 'ఇంగ్లీషు'; + + @override + String get languageTelugu => 'తెలుగు'; + + @override + String get chooseLanguageTitle => 'భాష ఎంచుకోండి'; + + @override + String get themeTitle => 'థీమ్'; + + @override + String get themeLight => 'లైట్'; + + @override + String get themeDark => 'డార్క్'; + + @override + String get themeSystem => 'సిస్టమ్ డిఫాల్ట్'; + + @override + String get chooseThemeTitle => 'థీమ్ ఎంచుకోండి'; + + @override + String get darkModeTitle => 'డార్క్ మోడ్'; + + @override + String get darkModeSubtitle => 'సిస్టమ్ సెట్టింగ్స్‌ను ఓవర్‌రైడ్ చేయండి'; + + @override + String get changePasswordTitle => 'పాస్‌వర్డ్ మార్చండి'; + + @override + String get changePasswordSubtitle => 'మీ పాస్‌వర్డ్‌ను నవీకరించండి'; + + @override + String get pushNotificationsTitle => 'పుష్ నోటిఫికేషన్లు'; + + @override + String get pushNotificationsSubtitle => 'పుష్ నోటిఫికేషన్లు అందుకోండి'; + + @override + String get paymentRemindersTitle => 'చెల్లింపు రిమైండర్లు'; + + @override + String get paymentRemindersSubtitle => 'రాబోయే చెల్లింపుల కోసం రిమైండర్లు'; + + @override + String get drawNotificationsTitle => 'డ్రా నోటిఫికేషన్లు'; + + @override + String get drawNotificationsSubtitle => 'లాటరీ డ్రాల కోసం అలర్ట్‌లు'; + + @override + String get upiIdTitle => 'UPI ID'; + + @override + String get loading => 'లోడ్ అవుతోంది…'; + + @override + String get active => 'సక్రియం'; + + @override + String get notConfigured => 'కాన్ఫిగర్ చేయలేదు'; + + @override + String get configureBackend => 'backend/.env లో కాన్ఫిగర్ చేయండి'; + + @override + String get copyUpiIdTooltip => 'UPI ID కాపీ చేయండి'; + + @override + String get upiCopiedClipboard => 'UPI ID క్లిప్‌బోర్డ్‌కు కాపీ అయింది'; + + @override + String get paymentStatisticsTitle => 'చెల్లింపు గణాంకాలు'; + + @override + String get paymentStatisticsSubtitle => 'చెల్లింపు అంతర్దృష్టులను చూడండి'; + + @override + String get transactionFeesTitle => 'లావాదేవీ ఫీజులు'; + + @override + String get transactionFeesSubtitle => '0% ఫీజులు • సంవత్సరానికి లక్షలు ఆదా!'; + + @override + String get free => 'ఉచితం'; + + @override + String get appVersionTitle => 'యాప్ వెర్షన్'; + + @override + String get privacyPolicyTitle => 'గోప్యతా విధానం'; + + @override + String get termsOfServiceTitle => 'సేవా నిబంధనలు'; + + @override + String get logout => 'లాగ్ అవుట్'; + + @override + String get logoutConfirmTitle => 'లాగ్ అవుట్'; + + @override + String get logoutConfirmMessage => 'మీరు ఖచ్చితంగా లాగ్ అవుట్ కావాలనుకుంటున్నారా?'; + + @override + String get cancel => 'రద్దు'; + + @override + String get close => 'మూసివేయి'; + + @override + String get copied => 'కాపీ అయింది!'; + + @override + String get profileComingSoon => 'ప్రొఫైల్ పేజీ త్వరలో వస్తుంది'; + + @override + String get changePasswordComingSoon => 'పాస్‌వర్డ్ మార్పు ఫీచర్ త్వరలో వస్తుంది'; + + @override + String get notificationComingSoon => 'నోటిఫికేషన్ సెట్టింగ్స్ త్వరలో వస్తాయి'; + + @override + String get paymentStatsComingSoon => 'చెల్లింపు గణాంకాలు త్వరలో వస్తాయి'; + + @override + String get privacyComingSoon => 'గోప్యతా విధానం పేజీ త్వరలో వస్తుంది'; + + @override + String get termsComingSoon => 'సేవా నిబంధనల పేజీ త్వరలో వస్తుంది'; + + @override + String get loggedOutSuccess => 'విజయవంతంగా లాగ్ అవుట్ అయ్యారు'; + + @override + String get upiPaymentSettingsTitle => 'UPI చెల్లింపు సెట్టింగ్స్'; + + @override + String get currentUpiId => 'ప్రస్తుత UPI ID'; + + @override + String get upiNotConfiguredMessage => 'UPI ID కాన్ఫిగర్ చేయలేదు. backend/.env ఫైల్‌ను నవీకరించండి.'; + + @override + String get howToUpdateUpi => 'UPI ID ఎలా నవీకరించాలి'; + + @override + String get stepOpenEnv => 'backend/.env ఫైల్‌ను తెరవండి'; + + @override + String get stepUpdatePhonepe => 'PHONEPE_UPI_ID=your_upi@paytm ను నవీకరించండి'; + + @override + String get stepRestartBackend => 'బ్యాకెండ్ సర్వర్‌ను రీస్టార్ట్ చేయండి'; + + @override + String get stepRefreshScreen => 'ఈ స్క్రీన్‌ను రిఫ్రెష్ చేయండి'; + + @override + String get proTipTitle => 'ప్రో టిప్'; + + @override + String get proTipBody => 'సభ్యులు ఏ UPI యాప్‌తోనైనా (PhonePe, GPay, Paytm) మీ వ్యక్తిగత UPI IDకు నేరుగా 0% లావాదేవీ ఫీజుతో చెల్లించవచ్చు!'; + + @override + String get ok => 'సరే'; + + @override + String get save => 'సేవ్'; + + @override + String get retry => 'మళ్లీ ప్రయత్నించండి'; + + @override + String get delete => 'తొలగించు'; + + @override + String get userLabel => 'వినియోగదారు'; + + @override + String get managerFallbackName => 'మేనేజర్'; + + @override + String get snackTitleError => 'లోపం'; + + @override + String get snackTitleSuccess => 'విజయం'; + + @override + String get operationFailedShort => 'విఫలమైంది'; + + @override + String get failedLoadChitGroups => 'చిట్ గ్రూప్‌లను లోడ్ చేయడంలో విఫలమైంది'; + + @override + String get chitfundCreatedSuccess => 'చిట్‌ఫండ్ విజయవంతంగా సృష్టించబడింది'; + + @override + String get failedCreateChitGroup => 'చిట్ గ్రూప్ సృష్టించడంలో విఫలమైంది'; + + @override + String get failedUpdateChitGroup => 'చిట్ గ్రూప్ నవీకరించడంలో విఫలమైంది'; + + @override + String get chitGroupDeletedSuccess => 'చిట్ గ్రూప్ విజయవంతంగా తొలగించబడింది'; + + @override + String get failedDeleteChitGroup => 'చిట్ గ్రూప్ తొలగించడంలో విఫలమైంది'; + + @override + String get failedLoadGroupDetails => 'గ్రూప్ వివరాలను లోడ్ చేయడంలో విఫలమైంది'; + + @override + String get failedLoadGroupMembers => 'గ్రూప్ సభ్యులను లోడ్ చేయడంలో విఫలమైంది'; + + @override + String get memberAddedSuccess => 'సభ్యుడు విజయవంతంగా జోడించబడ్డారు'; + + @override + String get failedAddMember => 'సభ్యుడిని జోడించడంలో విఫలమైంది'; + + @override + String get memberRemovedSuccess => 'సభ్యుడు విజయవంతంగా తొలగించబడ్డారు'; + + @override + String get failedRemoveMember => 'సభ్యుడిని తొలగించడంలో విఫలమైంది'; + + @override + String get memberStatusUpdatedSuccess => 'సభ్య స్థితి విజయవంతంగా నవీకరించబడింది'; + + @override + String get failedUpdateMemberStatus => 'సభ్య స్థితిని నవీకరించడంలో విఫలమైంది'; + + @override + String get failedLoadPayments => 'చెల్లింపులను లోడ్ చేయడంలో విఫలమైంది'; + + @override + String get paymentRecordedSuccess => 'చెల్లింపు విజయవంతంగా నమోదు చేయబడింది'; + + @override + String get failedRecordPayment => 'చెల్లింపు నమోదు చేయడంలో విఫలమైంది'; + + @override + String get failedLoadGroupStatistics => 'గ్రూప్ గణాంకాలను లోడ్ చేయడంలో విఫలమైంది'; + + @override + String get chitfundStartedSuccess => 'చిట్‌ఫండ్ విజయవంతంగా ప్రారంభమైంది'; + + @override + String get failedStartChitGroup => 'చిట్ గ్రూప్ ప్రారంభించడంలో విఫలమైంది'; + + @override + String get failedLoadMonthlyDraws => 'నెలవారీ డ్రాలను లోడ్ చేయడంలో విఫలమైంది'; + + @override + String get failedCreateMonthlyDraw => 'నెలవారీ డ్రా సృష్టించడంలో విఫలమైంది'; + + @override + String get failedLoadDrawStatistics => 'డ్రా గణాంకాలను లోడ్ చేయడంలో విఫలమైంది'; + + @override + String get failedLoadFinancialData => 'ఆర్థిక డేటాను లోడ్ చేయడంలో విఫలమైంది'; + + @override + String get signupFailedTitle => 'నమోదు విఫలమైంది'; + + @override + String get signupFailedGeneric => 'నమోదు విఫలమైంది. దయచేసి మళ్లీ ప్రయత్నించండి.'; + + @override + String get loginFailedTitle => 'లాగిన్ విఫలమైంది'; + + @override + String get loginFailedGeneric => 'లాగిన్ విఫలమైంది. దయచేసి మళ్లీ ప్రయత్నించండి.'; + + @override + String get passwordChangedSuccess => 'పాస్‌వర్డ్ విజయవంతంగా మార్చబడింది'; + + @override + String get failedChangePassword => 'పాస్‌వర్డ్ మార్చడంలో విఫలమైంది'; + + @override + String get stateSomethingWentWrong => 'ఏదో తప్పు జరిగింది'; + + @override + String get emptyNoGroupsTitle => 'ఇంకా చిట్ గ్రూప్‌లు లేవు'; + + @override + String get emptyNoGroupsMessage => + 'మీరు ఇంకా చిట్ గ్రూప్‌లు సృష్టించలేదు.\nమొదటి గ్రూప్ సృష్టించండి లేదా ఉన్నదాన్ని దిగుమతి చేయండి!'; + + @override + String get emptyNoGroupsAction => 'గ్రూప్ సృష్టించు'; + + @override + String get emptyNoMembersTitle => 'ఇంకా సభ్యులు లేరు'; + + @override + String get emptyNoMembersMessage => + 'ఈ గ్రూప్‌కు ఇంకా సభ్యులు లేరు.\nప్రారంభించడానికి సభ్యులను జోడించండి!'; + + @override + String get emptyNoMembersAction => 'సభ్యులను జోడించు'; + + @override + String get emptyNoPaymentsTitle => 'ఇంకా చెల్లింపులు లేవు'; + + @override + String get emptyNoPaymentsMessage => + 'చెల్లింపు రికార్డ్‌లు కనుగొనబడలేదు.\nనమోదు చేసిన తర్వాత ఇక్కడ కనిపిస్తాయి.'; + + @override + String get emptyNoPaymentsAction => 'చెల్లింపు నమోదు'; + + @override + String get emptyNoActivitiesTitle => 'ఇటీవలి కార్యకలాపాలు లేవు'; + + @override + String get emptyNoActivitiesMessage => + 'మీ ఇటీవలి కార్యకలాపాలు ఇక్కడ కనిపిస్తాయి.\nనవీకరణల కోసం యాప్‌ను ఉపయోగించండి!'; + + @override + String get emptyNoActivitiesAction => 'రిఫ్రెష్'; + + @override + String get emptyNoResultsTitle => 'ఫలితాలు లేవు'; + + @override + String get emptyNoResultsMessage => + 'మీరు వెతుకుతున్నది కనుగొనలేకపోయాము.\nశోధన లేదా ఫిల్టర్‌లను మార్చి ప్రయత్నించండి.'; + + @override + String get emptyNoResultsAction => 'ఫిల్టర్‌లు క్లియర్'; + + @override + String get emptyErrorTitle => 'అయ్యో! ఏదో తప్పు జరిగింది'; + + @override + String get emptyErrorMessage => + 'డేటా లోడ్ చేస్తూ లోపం వచ్చింది.\nదయచేసి మళ్లీ ప్రయత్నించండి.'; + + @override + String get emptyErrorAction => 'మళ్లీ ప్రయత్నించు'; + + @override + String get emptyNoInternetTitle => 'ఇంటర్నెట్ కనెక్షన్ లేదు'; + + @override + String get emptyNoInternetMessage => + 'దయచేసి మీ ఇంటర్నెట్ కనెక్షన్‌ను తనిఖీ చేసి\nమళ్లీ ప్రయత్నించండి.'; + + @override + String get emptyNoInternetAction => 'మళ్లీ ప్రయత్నించు'; + + @override + String get dashboardTitle => 'డాష్‌బోర్డ్'; + + @override + String get notificationsTooltip => 'నోటిఫికేషన్‌లు'; + + @override + String get recordingsTooltip => 'డ్రా రికార్డింగ్‌లు చూడండి'; + + @override + String get testDrawTooltip => 'యానిమేటెడ్ డ్రా పరీక్ష'; + + @override + String get chitFundManagerRole => 'చిట్ ఫండ్ మేనేజర్'; + + @override + String get menuDashboard => 'డాష్‌బోర్డ్'; + + @override + String get menuMyChitfunds => 'నా చిట్‌ఫండ్‌లు'; + + @override + String get menuMembers => 'సభ్యులు'; + + @override + String get menuPayments => 'చెల్లింపులు'; + + @override + String get menuLotteryDraws => 'లాటరీ డ్రాలు'; + + @override + String get menuReports => 'నివేదికలు'; + + @override + String get welcomeBackTitle => 'మళ్లీ స్వాగతం!'; + + @override + String get welcomeBackSubtitle => + 'ఈ రోజు మీ చిట్ ఫండ్‌లతో ఏమి జరుగుతోందో ఇక్కడ ఉంది.'; + + @override + String get quickActionsTitle => 'త్వరిత చర్యలు'; + + @override + String get qaCreateChitfundTitle => 'కొత్త చిట్‌ఫండ్ సృష్టించు'; + + @override + String get qaCreateChitfundSubtitle => 'కొత్త చిట్ ఫండ్ గ్రూప్ ప్రారంభించండి'; + + @override + String get qaImportChitfundTitle => 'ఉన్న చిట్‌ఫండ్ దిగుమతి'; + + @override + String get qaImportChitfundSubtitle => 'ఇప్పటికే ప్రారంభమైన గ్రూప్ జోడించండి'; + + @override + String get qaViewAllChitfundsTitle => 'అన్ని చిట్‌ఫండ్‌లు చూడండి'; + + @override + String get qaViewAllChitfundsSubtitle => 'మీ గ్రూప్‌లను నిర్వహించండి'; + + @override + String get qaManageMembersTitle => 'సభ్యులను నిర్వహించు'; + + @override + String get qaManageMembersSubtitle => 'సభ్యులను జోడించండి లేదా తొలగించండి'; + + @override + String get qaPaymentRecordsTitle => 'చెల్లింపు రికార్డ్‌లు'; + + @override + String get qaPaymentRecordsSubtitle => 'అన్ని లావాదేవీలను ట్రాక్ చేయండి'; + + @override + String get sectionMyChitfunds => 'నా చిట్‌ఫండ్‌లు'; + + @override + String get viewAll => 'అన్నీ చూడండి'; + + @override + String get noChitFundsYetShort => 'ఇంకా చిట్ ఫండ్‌లు లేవు'; + + @override + String get groupStatusActive => 'సక్రియం'; + + @override + String get groupStatusForming => 'ఏర్పాటులో'; + + @override + String get groupStatusCompleted => 'పూర్తయింది'; + + @override + String get unnamedGroup => 'పేరులేని'; + + @override + String get actionRecord => 'నమోదు'; + + @override + String get actionDraw => 'డ్రా'; + + @override + String get actionView => 'చూడండి'; + + @override + String get actionManageGroup => 'గ్రూప్ నిర్వహణ'; + + @override + String get groupImportedMessage => + 'గ్రూప్ దిగుమతి అయ్యింది! ఇప్పుడు సభ్యులను జోడించి గత డేటా నింపండి.'; + + @override + String get groupImportedTitle => 'విజయం'; + + @override + String get paymentsPageComingSoon => + 'చెల్లింపుల పేజీ తదుపరి అమలు చేయబడుతుంది'; + + @override + String get comingSoonTitle => 'త్వరలో'; + + @override + String get pageMyChitfunds => 'నా చిట్‌ఫండ్‌లు'; + + @override + String get createNewGroupMenu => 'కొత్త గ్రూప్ సృష్టించు'; + + @override + String get importExistingGroupMenu => 'ఉన్న గ్రూప్ దిగుమతి'; + + @override + String get appDisplayName => 'LuckyChit'; + + @override + String get authLoginTagline => + 'చిట్ ఫండ్ నిర్వహణ సులభంగా అనిపించేలా.'; + + @override + String get authSignupScreenTitle => 'ఖాతా సృష్టించు'; + + @override + String get authSignupTagline => + 'ఒక నిమిషంలో మీ ప్రొఫైల్ సెటప్ చేయండి.'; + + @override + String get labelMobileNumber => 'మొబైల్ నంబర్'; + + @override + String get labelMobileNumberRequired => 'మొబైల్ నంబర్ *'; + + @override + String get labelPassword => 'పాస్‌వర్డ్'; + + @override + String get labelPasswordRequired => 'పాస్‌వర్డ్ *'; + + @override + String get labelFullNameRequired => 'పూర్తి పేరు *'; + + @override + String get labelEmailOptional => 'ఇమెయిల్ (ఐచ్ఛికం)'; + + @override + String get labelAddressOptional => 'చిరునామా (ఐచ్ఛికం)'; + + @override + String get labelEmergencyContactOptional => + 'అత్యవసర సంప్రదింపు (ఐచ్ఛికం)'; + + @override + String get labelConfirmPasswordRequired => 'పాస్‌వర్డ్ నిర్ధారించు *'; + + @override + String get validatorEnterMobile => 'దయచేసి మొబైల్ నంబర్ నమోదు చేయండి'; + + @override + String get validatorMobileTenDigits => + 'మొబైల్ నంబర్ 10 అంకెలు ఉండాలి'; + + @override + String get validatorMobileDigitsOnly => + 'మొబైల్ నంబర్‌లో అంకెలు మాత్రమే ఉండాలి'; + + @override + String get validatorEnterFullName => 'దయచేసి మీ పూర్తి పేరు నమోదు చేయండి'; + + @override + String get validatorValidEmail => + 'దయచేసి సరైన ఇమెయిల్ చిరునామా నమోదు చేయండి'; + + @override + String get validatorEmergencyTenDigits => + 'అత్యవసర సంప్రదింపు 10 అంకెలు ఉండాలి'; + + @override + String get validatorEmergencyDigitsOnly => + 'అత్యవసర సంప్రదింపులో అంకెలు మాత్రమే ఉండాలి'; + + @override + String get validatorEnterPasswordAuth => + 'దయచేసి పాస్‌వర్డ్ నమోదు చేయండి'; + + @override + String get validatorPasswordMinSixAuth => + 'పాస్‌వర్డ్ కనీసం 6 అక్షరాలు ఉండాలి'; + + @override + String get validatorConfirmPassword => + 'దయచేసి పాస్‌వర్డ్ నిర్ధారించండి'; + + @override + String get validatorPasswordsMismatch => + 'పాస్‌వర్డ్‌లు సరిపోలడం లేదు'; + + @override + String get tooltipShowPassword => 'పాస్‌వర్డ్ చూపు'; + + @override + String get tooltipHidePassword => 'పాస్‌వర్డ్ దాచు'; + + @override + String get signInButton => 'సైన్ ఇన్'; + + @override + String get createAccountButton => 'ఖాతా సృష్టించు'; + + @override + String get alreadyHaveAccount => 'ఇప్పటికే ఖాతా ఉందా? '; + + @override + String get loginLink => 'లాగిన్'; + + @override + String get loginInvalidCredentials => + 'తప్పు మొబైల్ నంబర్ లేదా పాస్‌వర్డ్. మళ్లీ ప్రయత్నించండి.'; + + @override + String get signupSuccessWelcome => + 'ఖాతా విజయవంతంగా సృష్టించబడింది! LuckyChitకు స్వాగతం.'; + + @override + String get signupFailedGenericUi => + 'ఖాతా సృష్టించడంలో విఫలమైంది. మళ్లీ ప్రయత్నించండి.'; + + @override + String get featureComingSoonMessage => 'ఫీచర్ త్వరలో వస్తుంది'; + + @override + String memberWelcomeGreeting(String name) => 'స్వాగతం, $name!'; + + @override + String get memberFallbackName => 'సభ్యుడు'; + + @override + String get memberSubtitleEmpty => + 'మీ పెట్టుబడులను నిర్వహించడానికి చిట్ ఫండ్‌లో చేరండి.'; + + @override + String get memberSubtitleHasGroups => + 'మీ చిట్ ఫండ్ పెట్టుబడులు మరియు చెల్లింపులను ట్రాక్ చేయండి.'; + + @override + String get navHome => 'హోమ్'; + + @override + String get navPayments => 'చెల్లింపులు'; + + @override + String get navNotifications => 'నోటిఫికేషన్‌లు'; + + @override + String get navProfile => 'ప్రొఫైల్'; + + @override + String get memberEmptyChitTitle => 'ఇంకా చిట్ ఫండ్‌లు లేవు'; + + @override + String get memberEmptyChitBody => + 'మీరు ఇంకా ఏ చిట్ ఫండ్‌లోనూ చేరలేదు.\nప్రారంభించడానికి మీ మేనేజర్‌ను సంప్రదించండి!'; + + @override + String get memberHowToStartTitle => 'ఎలా ప్రారంభించాలి?'; + + @override + String get memberHowToStartBody => + '1. మీ మేనేజర్ మిమ్మల్ని చిట్ గ్రూప్‌కు జోడిస్తారు\n' + '2. మీకు నోటిఫికేషన్ వస్తుంది\n' + '3. ఇక్కడ మీ చెల్లింపులను నిర్వహించండి!'; + + @override + String get unnamedGroupLong => 'పేరులేని గ్రూప్'; + + @override + String get labelTotalValue => 'మొత్తం విలువ'; + + @override + String get labelDuration => 'కాలపరిమితి'; + + @override + String get monthsSuffix => 'నెలలు'; + + @override + String get labelInstallment => 'వాయిదా'; + + @override + String get labelStatus => 'స్థితి'; + + @override + String get groupStatusPending => 'పెండింగ్'; + + @override + String get payNowButton => 'ఇప్పుడు చెల్లించు'; + + @override + String get detailsButton => 'వివరాలు'; + + @override + String get memberInfoNotFound => 'సభ్య సమాచారం కనుగొనబడలేదు'; +} diff --git a/luckychit/lib/l10n/app_te.arb b/luckychit/lib/l10n/app_te.arb new file mode 100644 index 0000000..837fb27 --- /dev/null +++ b/luckychit/lib/l10n/app_te.arb @@ -0,0 +1,69 @@ +{ + "@@locale": "te", + "appTitle": "లక్కీచిట్", + "settingsTitle": "సెట్టింగ్స్", + "sectionAppearance": "రూపం", + "sectionLanguage": "భాష", + "sectionAccount": "ఖాతా", + "sectionPaymentSettings": "చెల్లింపు సెట్టింగ్స్", + "sectionNotifications": "నోటిఫికేషన్లు", + "sectionAbout": "గురించి", + "languageTitle": "యాప్ భాష", + "languageSubtitle": "ఇంగ్లీషు లేదా తెలుగు", + "languageEnglish": "ఇంగ్లీషు", + "languageTelugu": "తెలుగు", + "chooseLanguageTitle": "భాష ఎంచుకోండి", + "themeTitle": "థీమ్", + "themeLight": "లైట్", + "themeDark": "డార్క్", + "themeSystem": "సిస్టమ్ డిఫాల్ట్", + "chooseThemeTitle": "థీమ్ ఎంచుకోండి", + "darkModeTitle": "డార్క్ మోడ్", + "darkModeSubtitle": "సిస్టమ్ సెట్టింగ్స్‌ను ఓవర్‌రైడ్ చేయండి", + "changePasswordTitle": "పాస్‌వర్డ్ మార్చండి", + "changePasswordSubtitle": "మీ పాస్‌వర్డ్‌ను నవీకరించండి", + "pushNotificationsTitle": "పుష్ నోటిఫికేషన్లు", + "pushNotificationsSubtitle": "పుష్ నోటిఫికేషన్లు అందుకోండి", + "paymentRemindersTitle": "చెల్లింపు రిమైండర్లు", + "paymentRemindersSubtitle": "రాబోయే చెల్లింపుల కోసం రిమైండర్లు", + "drawNotificationsTitle": "డ్రా నోటిఫికేషన్లు", + "drawNotificationsSubtitle": "లాటరీ డ్రాల కోసం అలర్ట్‌లు", + "upiIdTitle": "UPI ID", + "loading": "లోడ్ అవుతోంది…", + "active": "Active", + "notConfigured": "కాన్ఫిగర్ చేయలేదు", + "configureBackend": "backend/.env లో కాన్ఫిగర్ చేయండి", + "copyUpiIdTooltip": "UPI ID కాపీ చేయండి", + "upiCopiedClipboard": "UPI ID క్లిప్‌బోర్డ్‌కు కాపీ అయింది", + "paymentStatisticsTitle": "చెల్లింపు గణాంకాలు", + "paymentStatisticsSubtitle": "చెల్లింపు అంతర్దృష్టులను చూడండి", + "transactionFeesTitle": "లావాదేవీ ఫీజులు", + "transactionFeesSubtitle": "0% ఫీజులు • సంవత్సరానికి లక్షలు ఆదా!", + "free": "ఉచితం", + "appVersionTitle": "యాప్ వెర్షన్", + "privacyPolicyTitle": "గోప్యతా విధానం", + "termsOfServiceTitle": "సేవా నిబంధనలు", + "logout": "లాగ్ అవుట్", + "logoutConfirmTitle": "లాగ్ అవుట్", + "logoutConfirmMessage": "మీరు ఖచ్చితంగా లాగ్ అవుట్ కావాలనుకుంటున్నారా?", + "cancel": "రద్దు", + "close": "మూసివేయి", + "copied": "కాపీ అయింది!", + "profileComingSoon": "ప్రొఫైల్ పేజీ త్వరలో వస్తుంది", + "changePasswordComingSoon": "పాస్‌వర్డ్ మార్పు ఫీచర్ త్వరలో వస్తుంది", + "notificationComingSoon": "నోటిఫికేషన్ సెట్టింగ్స్ త్వరలో వస్తాయి", + "paymentStatsComingSoon": "చెల్లింపు గణాంకాలు త్వరలో వస్తాయి", + "privacyComingSoon": "గోప్యతా విధానం పేజీ త్వరలో వస్తుంది", + "termsComingSoon": "సేవా నిబంధనల పేజీ త్వరలో వస్తుంది", + "loggedOutSuccess": "విజయవంతంగా లాగ్ అవుట్ అయ్యారు", + "upiPaymentSettingsTitle": "UPI చెల్లింపు సెట్టింగ్స్", + "currentUpiId": "ప్రస్తుత UPI ID", + "upiNotConfiguredMessage": "UPI ID కాన్ఫిగర్ చేయలేదు. backend/.env ఫైల్‌ను నవీకరించండి.", + "howToUpdateUpi": "UPI ID ఎలా నవీకరించాలి", + "stepOpenEnv": "backend/.env ఫైల్‌ను తెరవండి", + "stepUpdatePhonepe": "PHONEPE_UPI_ID=your_upi@paytm ను నవీకరించండి", + "stepRestartBackend": "బ్యాకెండ్ సర్వర్‌ను రీస్టార్ట్ చేయండి", + "stepRefreshScreen": "ఈ స్క్రీన్‌ను రిఫ్రెష్ చేయండి", + "proTipTitle": "ప్రో టిప్", + "proTipBody": "సభ్యులు ఏ UPI యాప్‌తోనైనా (PhonePe, GPay, Paytm) మీ వ్యక్తిగత UPI IDకు నేరుగా 0% లావాదేవీ ఫీజుతో చెల్లించవచ్చు!" +} diff --git a/luckychit/lib/l10n/l10n_x.dart b/luckychit/lib/l10n/l10n_x.dart new file mode 100644 index 0000000..a3131c4 --- /dev/null +++ b/luckychit/lib/l10n/l10n_x.dart @@ -0,0 +1,26 @@ +import 'package:flutter/widgets.dart'; +import 'package:get/get.dart'; + +import 'package:luckychit/l10n/app_localizations.dart'; +import 'package:luckychit/l10n/app_localizations_en.dart'; + +/// Localizations from a [BuildContext] (widgets). +extension AppL10n on BuildContext { + AppLocalizations get l10n { + final l = AppLocalizations.of(this); + assert(l != null, 'AppLocalizations not found — check GetMaterialApp delegates'); + return l!; + } +} + +/// Localizations when there is no [BuildContext] (GetX services, etc.). +/// Falls back to English if [Get.context] is missing or not localized yet. +final class L10nSvc { + L10nSvc._(); + + static AppLocalizations of() { + final ctx = Get.context; + final l = ctx != null ? AppLocalizations.of(ctx) : null; + return l ?? AppLocalizationsEn(); + } +} diff --git a/luckychit/lib/main.dart b/luckychit/lib/main.dart index 957e10a..152fe3a 100644 --- a/luckychit/lib/main.dart +++ b/luckychit/lib/main.dart @@ -4,11 +4,14 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'app.dart'; import 'core/themes/app_theme.dart'; import 'core/controllers/theme_controller.dart'; +import 'core/controllers/locale_controller.dart'; +import 'l10n/app_localizations.dart'; -void main() { - // Initialize theme controller +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); Get.put(ThemeController()); - + final localeController = Get.put(LocaleController()); + await localeController.ensureLoaded(); runApp(const LuckyChitApp()); } @@ -18,17 +21,22 @@ class LuckyChitApp extends StatelessWidget { @override Widget build(BuildContext context) { return ScreenUtilInit( - designSize: const Size(375, 812), // Mobile-first design size (iPhone X) + designSize: const Size(375, 812), minTextAdapt: true, splitScreenMode: true, builder: (context, child) { - return GetMaterialApp( - title: 'LuckyChit', - debugShowCheckedModeBanner: false, - theme: AppTheme.lightTheme, - darkTheme: AppTheme.darkTheme, - themeMode: ThemeController.to.themeMode, - home: const App(), + return Obx( + () => GetMaterialApp( + title: 'LuckyChit', + debugShowCheckedModeBanner: false, + theme: AppTheme.lightTheme, + darkTheme: AppTheme.darkTheme, + themeMode: ThemeController.to.themeMode, + locale: LocaleController.to.locale.value, + supportedLocales: AppLocalizations.supportedLocales, + localizationsDelegates: AppLocalizations.localizationsDelegates, + home: const App(), + ), ); }, ); diff --git a/luckychit/lib/shared/widgets/empty_state_widget.dart b/luckychit/lib/shared/widgets/empty_state_widget.dart index d21fa7c..181a673 100644 --- a/luckychit/lib/shared/widgets/empty_state_widget.dart +++ b/luckychit/lib/shared/widgets/empty_state_widget.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import '../../l10n/l10n_x.dart'; + enum EmptyStateType { noGroups, noMembers, @@ -31,7 +33,7 @@ class EmptyStateWidget extends StatelessWidget { @override Widget build(BuildContext context) { - final config = _getEmptyStateConfig(type); + final config = _getEmptyStateConfig(context, type); return Center( child: SingleChildScrollView( @@ -164,17 +166,17 @@ class EmptyStateWidget extends StatelessWidget { ); } - _EmptyStateConfig _getEmptyStateConfig(EmptyStateType type) { + _EmptyStateConfig _getEmptyStateConfig(BuildContext context, EmptyStateType type) { + final l = context.l10n; switch (type) { case EmptyStateType.noGroups: return _EmptyStateConfig( icon: Icons.group_add_rounded, iconColor: Colors.green.shade600, backgroundColor: Colors.green.shade50, - title: 'No Chit Groups Yet', - message: - 'You haven\'t created any chit groups yet.\nStart by creating your first group!', - actionLabel: 'Create Group', + title: l.emptyNoGroupsTitle, + message: l.emptyNoGroupsMessage, + actionLabel: l.emptyNoGroupsAction, actionIcon: Icons.add_circle_outline, buttonColor: Colors.green.shade600, ); @@ -184,10 +186,9 @@ class EmptyStateWidget extends StatelessWidget { icon: Icons.people_outline_rounded, iconColor: Colors.blue.shade600, backgroundColor: Colors.blue.shade50, - title: 'No Members Yet', - message: - 'This group doesn\'t have any members yet.\nAdd members to get started!', - actionLabel: 'Add Members', + title: l.emptyNoMembersTitle, + message: l.emptyNoMembersMessage, + actionLabel: l.emptyNoMembersAction, actionIcon: Icons.person_add, buttonColor: Colors.blue.shade600, ); @@ -197,10 +198,9 @@ class EmptyStateWidget extends StatelessWidget { icon: Icons.payment_rounded, iconColor: Colors.orange.shade600, backgroundColor: Colors.orange.shade50, - title: 'No Payments Yet', - message: - 'No payment records found.\nPayments will appear here once recorded.', - actionLabel: 'Record Payment', + title: l.emptyNoPaymentsTitle, + message: l.emptyNoPaymentsMessage, + actionLabel: l.emptyNoPaymentsAction, actionIcon: Icons.add, buttonColor: Colors.orange.shade600, ); @@ -210,10 +210,9 @@ class EmptyStateWidget extends StatelessWidget { icon: Icons.history_rounded, iconColor: Colors.purple.shade600, backgroundColor: Colors.purple.shade50, - title: 'No Recent Activities', - message: - 'Your recent activities will appear here.\nStart using the app to see updates!', - actionLabel: 'Refresh', + title: l.emptyNoActivitiesTitle, + message: l.emptyNoActivitiesMessage, + actionLabel: l.emptyNoActivitiesAction, actionIcon: Icons.refresh, buttonColor: Colors.purple.shade600, ); @@ -223,10 +222,9 @@ class EmptyStateWidget extends StatelessWidget { icon: Icons.search_off_rounded, iconColor: Colors.grey.shade600, backgroundColor: Colors.grey.shade100, - title: 'No Results Found', - message: - 'We couldn\'t find what you\'re looking for.\nTry adjusting your search or filters.', - actionLabel: 'Clear Filters', + title: l.emptyNoResultsTitle, + message: l.emptyNoResultsMessage, + actionLabel: l.emptyNoResultsAction, actionIcon: Icons.clear_all, buttonColor: Colors.grey.shade600, ); @@ -236,10 +234,9 @@ class EmptyStateWidget extends StatelessWidget { icon: Icons.error_outline_rounded, iconColor: Colors.red.shade600, backgroundColor: Colors.red.shade50, - title: 'Oops! Something Went Wrong', - message: - 'We encountered an error while loading data.\nPlease try again.', - actionLabel: 'Retry', + title: l.emptyErrorTitle, + message: l.emptyErrorMessage, + actionLabel: l.emptyErrorAction, actionIcon: Icons.refresh, buttonColor: Colors.red.shade600, ); @@ -249,10 +246,9 @@ class EmptyStateWidget extends StatelessWidget { icon: Icons.wifi_off_rounded, iconColor: Colors.red.shade600, backgroundColor: Colors.red.shade50, - title: 'No Internet Connection', - message: - 'Please check your internet connection\nand try again.', - actionLabel: 'Retry', + title: l.emptyNoInternetTitle, + message: l.emptyNoInternetMessage, + actionLabel: l.emptyNoInternetAction, actionIcon: Icons.refresh, buttonColor: Colors.red.shade600, ); diff --git a/luckychit/pubspec.lock b/luckychit/pubspec.lock index 78e5e15..6513355 100644 --- a/luckychit/pubspec.lock +++ b/luckychit/pubspec.lock @@ -270,6 +270,11 @@ packages: url: "https://pub.dev" source: hosted version: "7.2.0" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" flutter_plugin_android_lifecycle: dependency: transitive description: diff --git a/luckychit/pubspec.yaml b/luckychit/pubspec.yaml index c4885ed..4bd774f 100644 --- a/luckychit/pubspec.yaml +++ b/luckychit/pubspec.yaml @@ -30,6 +30,8 @@ environment: dependencies: flutter: sdk: flutter + flutter_localizations: + sdk: flutter # UI and Icons