11import 'package:flutter/material.dart' ;
22import 'package:flutter_bloc/flutter_bloc.dart' ;
33import 'package:ht_dashboard/app_configuration/bloc/app_configuration_bloc.dart' ;
4+ import 'package:ht_dashboard/l10n/l10n.dart' ;
45import 'package:ht_dashboard/shared/constants/app_spacing.dart' ;
56import 'package:ht_dashboard/shared/widgets/widgets.dart' ;
67import 'package:ht_shared/ht_shared.dart' ; // For AppConfig and its nested models
@@ -45,18 +46,18 @@ class _AppConfigurationPageState extends State<AppConfigurationPage>
4546 return Scaffold (
4647 appBar: AppBar (
4748 title: Text (
48- 'App Configuration' ,
49+ context.l10n.appConfigurationPageTitle ,
4950 style: Theme .of (context).textTheme.headlineSmall,
5051 ),
5152 bottom: TabBar (
5253 controller: _tabController,
5354 isScrollable: true ,
54- tabs: const [
55- Tab (text: 'User Content Limits' ),
56- Tab (text: 'Ad Settings' ),
57- Tab (text: 'In-App Prompts' ),
58- Tab (text: 'App Operational Status' ),
59- Tab (text: 'Force Update' ),
55+ tabs: [
56+ Tab (text: context.l10n.userContentLimitsTab ),
57+ Tab (text: context.l10n.adSettingsTab ),
58+ Tab (text: context.l10n.inAppPromptsTab ),
59+ Tab (text: context.l10n.appOperationalStatusTab ),
60+ Tab (text: context.l10n.forceUpdateTab ),
6061 ],
6162 ),
6263 ),
@@ -69,7 +70,7 @@ class _AppConfigurationPageState extends State<AppConfigurationPage>
6970 ..showSnackBar (
7071 SnackBar (
7172 content: Text (
72- 'App configuration saved successfully!' ,
73+ context.l10n.appConfigSaveSuccessMessage ,
7374 style: Theme .of (context).textTheme.bodyMedium? .copyWith (
7475 color: Theme .of (context).colorScheme.onPrimary,
7576 ),
@@ -87,7 +88,9 @@ class _AppConfigurationPageState extends State<AppConfigurationPage>
8788 ..showSnackBar (
8889 SnackBar (
8990 content: Text (
90- 'Error: ${state .errorMessage ?? "Unknown error" }' ,
91+ context.l10n.appConfigSaveErrorMessage (
92+ state.errorMessage ?? context.l10n.unknownError,
93+ ),
9194 style: Theme .of (context).textTheme.bodyMedium? .copyWith (
9295 color: Theme .of (context).colorScheme.onError,
9396 ),
@@ -100,14 +103,16 @@ class _AppConfigurationPageState extends State<AppConfigurationPage>
100103 builder: (context, state) {
101104 if (state.status == AppConfigurationStatus .loading ||
102105 state.status == AppConfigurationStatus .initial) {
103- return const LoadingStateWidget (
106+ return LoadingStateWidget (
104107 icon: Icons .settings_applications_outlined,
105- headline: 'Loading Configuration' ,
106- subheadline: 'Please wait while settings are loaded...' ,
108+ headline: context.l10n.loadingConfigurationHeadline ,
109+ subheadline: context.l10n.loadingConfigurationSubheadline ,
107110 );
108111 } else if (state.status == AppConfigurationStatus .failure) {
109112 return FailureStateWidget (
110- message: state.errorMessage ?? 'Failed to load configuration.' ,
113+ message:
114+ state.errorMessage ??
115+ context.l10n.failedToLoadConfigurationMessage,
111116 onRetry: () {
112117 context.read <AppConfigurationBloc >().add (
113118 const AppConfigurationLoaded (),
@@ -128,10 +133,10 @@ class _AppConfigurationPageState extends State<AppConfigurationPage>
128133 ],
129134 );
130135 }
131- return const InitialStateWidget (
136+ return InitialStateWidget (
132137 icon: Icons .settings_applications_outlined,
133- headline: 'App Configuration' ,
134- subheadline: 'Load application settings from the backend.' ,
138+ headline: context.l10n.appConfigurationPageTitle ,
139+ subheadline: context.l10n.loadAppSettingsSubheadline ,
135140 ); // Fallback
136141 },
137142 ),
@@ -162,7 +167,7 @@ class _AppConfigurationPageState extends State<AppConfigurationPage>
162167 );
163168 }
164169 : null ,
165- child: const Text ('Discard Changes' ),
170+ child: Text (context.l10n.discardChangesButton ),
166171 ),
167172 const SizedBox (width: AppSpacing .md),
168173 ElevatedButton (
@@ -176,7 +181,7 @@ class _AppConfigurationPageState extends State<AppConfigurationPage>
176181 }
177182 }
178183 : null ,
179- child: const Text ('Save Changes' ),
184+ child: Text (context.l10n.saveChangesButton ),
180185 ),
181186 ],
182187 ),
@@ -190,17 +195,17 @@ class _AppConfigurationPageState extends State<AppConfigurationPage>
190195 builder: (BuildContext dialogContext) {
191196 return AlertDialog (
192197 title: Text (
193- 'Confirm Configuration Update' ,
198+ context.l10n.confirmConfigUpdateDialogTitle ,
194199 style: Theme .of (dialogContext).textTheme.titleLarge,
195200 ),
196201 content: Text (
197- 'Are you sure you want to apply these changes to the live application configuration? This is a critical operation.' ,
202+ context.l10n.confirmConfigUpdateDialogContent ,
198203 style: Theme .of (dialogContext).textTheme.bodyMedium,
199204 ),
200205 actions: < Widget > [
201206 TextButton (
202207 onPressed: () => Navigator .of (dialogContext).pop (false ),
203- child: const Text ('Cancel' ),
208+ child: Text (context.l10n.cancelButton ),
204209 ),
205210 ElevatedButton (
206211 onPressed: () => Navigator .of (dialogContext).pop (true ),
@@ -210,7 +215,7 @@ class _AppConfigurationPageState extends State<AppConfigurationPage>
210215 dialogContext,
211216 ).colorScheme.onError,
212217 ),
213- child: const Text ('Confirm Save' ),
218+ child: Text (context.l10n.confirmSaveButton ),
214219 ),
215220 ],
216221 );
@@ -231,25 +236,22 @@ class _AppConfigurationPageState extends State<AppConfigurationPage>
231236 crossAxisAlignment: CrossAxisAlignment .start,
232237 children: [
233238 Text (
234- 'User Content Limits' ,
239+ context.l10n.userContentLimitsTab ,
235240 style: Theme .of (context).textTheme.headlineSmall,
236241 ),
237242 const SizedBox (height: AppSpacing .md),
238243 Text (
239- 'These settings define the maximum number of countries, news sources, '
240- 'categories, and saved headlines a user can follow or save. '
241- 'Limits vary by user type (Guest, Standard, Premium) and directly '
242- 'impact what content users can curate.' ,
244+ context.l10n.userContentLimitsDescription,
243245 style: Theme .of (context).textTheme.bodySmall? .copyWith (
244246 color: Theme .of (context).colorScheme.onSurface.withOpacity (0.7 ),
245247 ),
246248 ),
247249 const SizedBox (height: AppSpacing .lg),
248250 TabBar (
249- tabs: const [
250- Tab (text: 'Guest' ),
251- Tab (text: 'Authenticated' ),
252- Tab (text: 'Premium' ),
251+ tabs: [
252+ Tab (text: context.l10n.guestUserTab ),
253+ Tab (text: context.l10n.authenticatedUserTab ),
254+ Tab (text: context.l10n.premiumUserTab ),
253255 ],
254256 labelColor: Theme .of (context).colorScheme.primary,
255257 unselectedLabelColor: Theme .of (
@@ -316,26 +318,22 @@ class _AppConfigurationPageState extends State<AppConfigurationPage>
316318 crossAxisAlignment: CrossAxisAlignment .start,
317319 children: [
318320 Text (
319- 'Ad Settings' ,
321+ context.l10n.adSettingsTab ,
320322 style: Theme .of (context).textTheme.headlineSmall,
321323 ),
322324 const SizedBox (height: AppSpacing .md),
323325 Text (
324- 'These settings control how advertisements are displayed within '
325- 'the app\' s news feed, with different rules for Guest, Standard, '
326- 'and Premium users. "Ad Frequency" determines how often an ad '
327- 'can appear, while "Ad Placement Interval" sets how many news '
328- 'items must be shown before the very first ad appears.' ,
326+ context.l10n.adSettingsDescription,
329327 style: Theme .of (context).textTheme.bodySmall? .copyWith (
330328 color: Theme .of (context).colorScheme.onSurface.withOpacity (0.7 ),
331329 ),
332330 ),
333331 const SizedBox (height: AppSpacing .lg),
334332 TabBar (
335- tabs: const [
336- Tab (text: 'Guest' ),
337- Tab (text: 'Standard User' ),
338- Tab (text: 'Premium' ),
333+ tabs: [
334+ Tab (text: context.l10n.guestUserTab ),
335+ Tab (text: context.l10n.standardUserAdTab ),
336+ Tab (text: context.l10n.premiumUserTab ),
339337 ],
340338 labelColor: Theme .of (context).colorScheme.primary,
341339 unselectedLabelColor: Theme .of (
@@ -405,25 +403,21 @@ class _AppConfigurationPageState extends State<AppConfigurationPage>
405403 crossAxisAlignment: CrossAxisAlignment .start,
406404 children: [
407405 Text (
408- 'In-App Prompts' ,
406+ context.l10n.inAppPromptsTab ,
409407 style: Theme .of (context).textTheme.headlineSmall,
410408 ),
411409 const SizedBox (height: AppSpacing .md),
412410 Text (
413- 'These settings control how often special in-app messages or '
414- '"prompts" are shown to users in their news feed. These prompts '
415- 'encourage actions like linking an account (for guests) or '
416- 'upgrading to a premium subscription (for authenticated users). '
417- 'The frequency varies by user type.' ,
411+ context.l10n.inAppPromptsDescription,
418412 style: Theme .of (context).textTheme.bodySmall? .copyWith (
419413 color: Theme .of (context).colorScheme.onSurface.withOpacity (0.7 ),
420414 ),
421415 ),
422416 const SizedBox (height: AppSpacing .lg),
423417 TabBar (
424- tabs: const [
425- Tab (text: 'Guest' ),
426- Tab (text: 'Standard User' ),
418+ tabs: [
419+ Tab (text: context.l10n.guestUserTab ),
420+ Tab (text: context.l10n.standardUserAdTab ),
427421 ],
428422 labelColor: Theme .of (context).colorScheme.primary,
429423 unselectedLabelColor: Theme .of (
@@ -476,12 +470,12 @@ class _AppConfigurationPageState extends State<AppConfigurationPage>
476470 crossAxisAlignment: CrossAxisAlignment .start,
477471 children: [
478472 Text (
479- 'App Operational Status' ,
473+ context.l10n.appOperationalStatusTab ,
480474 style: Theme .of (context).textTheme.headlineSmall,
481475 ),
482476 const SizedBox (height: AppSpacing .md),
483477 Text (
484- 'WARNING: Changing the app \' s operational status can affect all users. Use with extreme caution.' ,
478+ context.l10n.appOperationalStatusWarning ,
485479 style: Theme .of (context).textTheme.bodyMedium? .copyWith (
486480 color: Theme .of (context).colorScheme.error,
487481 fontWeight: FontWeight .bold,
@@ -490,9 +484,8 @@ class _AppConfigurationPageState extends State<AppConfigurationPage>
490484 const SizedBox (height: AppSpacing .lg),
491485 _buildDropdownField <RemoteAppStatus >(
492486 context,
493- label: 'App Operational Status' ,
494- description:
495- 'The current operational status of the app (e.g., active, maintenance, disabled).' ,
487+ label: context.l10n.appOperationalStatusLabel,
488+ description: context.l10n.appOperationalStatusDescription,
496489 value: appConfig.appOperationalStatus,
497490 items: RemoteAppStatus .values,
498491 itemLabelBuilder: (status) => status.name,
@@ -509,9 +502,8 @@ class _AppConfigurationPageState extends State<AppConfigurationPage>
509502 if (appConfig.appOperationalStatus == RemoteAppStatus .maintenance)
510503 _buildTextField (
511504 context,
512- label: 'Maintenance Message' ,
513- description:
514- 'Message displayed when the app is in maintenance mode.' ,
505+ label: context.l10n.maintenanceMessageLabel,
506+ description: context.l10n.maintenanceMessageDescription,
515507 value: appConfig.maintenanceMessage,
516508 onChanged: (value) {
517509 context.read <AppConfigurationBloc >().add (
@@ -524,9 +516,8 @@ class _AppConfigurationPageState extends State<AppConfigurationPage>
524516 if (appConfig.appOperationalStatus == RemoteAppStatus .disabled)
525517 _buildTextField (
526518 context,
527- label: 'Disabled Message' ,
528- description:
529- 'Message displayed when the app is permanently disabled.' ,
519+ label: context.l10n.disabledMessageLabel,
520+ description: context.l10n.disabledMessageDescription,
530521 value: appConfig.disabledMessage,
531522 onChanged: (value) {
532523 context.read <AppConfigurationBloc >().add (
@@ -548,22 +539,21 @@ class _AppConfigurationPageState extends State<AppConfigurationPage>
548539 crossAxisAlignment: CrossAxisAlignment .start,
549540 children: [
550541 Text (
551- 'Force Update Configuration' ,
542+ context.l10n.forceUpdateConfigurationTitle ,
552543 style: Theme .of (context).textTheme.headlineSmall,
553544 ),
554545 const SizedBox (height: AppSpacing .md),
555546 Text (
556- 'These settings control app version enforcement. Users on versions below the minimum allowed will be forced to update.' ,
547+ context.l10n.forceUpdateDescription ,
557548 style: Theme .of (context).textTheme.bodySmall? .copyWith (
558549 color: Theme .of (context).colorScheme.onSurface.withOpacity (0.7 ),
559550 ),
560551 ),
561552 const SizedBox (height: AppSpacing .lg),
562553 _buildTextField (
563554 context,
564- label: 'Minimum Allowed App Version' ,
565- description:
566- 'The lowest app version allowed to run (e.g., "1.2.0").' ,
555+ label: context.l10n.minAllowedAppVersionLabel,
556+ description: context.l10n.minAllowedAppVersionDescription,
567557 value: appConfig.minAllowedAppVersion,
568558 onChanged: (value) {
569559 context.read <AppConfigurationBloc >().add (
@@ -575,8 +565,8 @@ class _AppConfigurationPageState extends State<AppConfigurationPage>
575565 ),
576566 _buildTextField (
577567 context,
578- label: 'Latest App Version' ,
579- description: 'The latest available app version (e.g., "1.5.0").' ,
568+ label: context.l10n.latestAppVersionLabel ,
569+ description: context.l10n.latestAppVersionDescription ,
580570 value: appConfig.latestAppVersion,
581571 onChanged: (value) {
582572 context.read <AppConfigurationBloc >().add (
@@ -588,8 +578,8 @@ class _AppConfigurationPageState extends State<AppConfigurationPage>
588578 ),
589579 _buildTextField (
590580 context,
591- label: 'Update Required Message' ,
592- description: 'Message displayed when a force update is required.' ,
581+ label: context.l10n.updateRequiredMessageLabel ,
582+ description: context.l10n.updateRequiredMessageDescription ,
593583 value: appConfig.updateRequiredMessage,
594584 onChanged: (value) {
595585 context.read <AppConfigurationBloc >().add (
@@ -601,8 +591,8 @@ class _AppConfigurationPageState extends State<AppConfigurationPage>
601591 ),
602592 _buildTextField (
603593 context,
604- label: 'Update Optional Message' ,
605- description: 'Message displayed for an optional update.' ,
594+ label: context.l10n.updateOptionalMessageLabel ,
595+ description: context.l10n.updateOptionalMessageDescription ,
606596 value: appConfig.updateOptionalMessage,
607597 onChanged: (value) {
608598 context.read <AppConfigurationBloc >().add (
@@ -614,8 +604,8 @@ class _AppConfigurationPageState extends State<AppConfigurationPage>
614604 ),
615605 _buildTextField (
616606 context,
617- label: 'iOS Store URL' ,
618- description: 'URL to the app on the Apple App Store.' ,
607+ label: context.l10n.iosStoreUrlLabel ,
608+ description: context.l10n.iosStoreUrlDescription ,
619609 value: appConfig.iosStoreUrl,
620610 onChanged: (value) {
621611 context.read <AppConfigurationBloc >().add (
@@ -627,8 +617,8 @@ class _AppConfigurationPageState extends State<AppConfigurationPage>
627617 ),
628618 _buildTextField (
629619 context,
630- label: 'Android Store URL' ,
631- description: 'URL to the app on the Google Play Store.' ,
620+ label: context.l10n.androidStoreUrlLabel ,
621+ description: context.l10n.androidStoreUrlDescription ,
632622 value: appConfig.androidStoreUrl,
633623 onChanged: (value) {
634624 context.read <AppConfigurationBloc >().add (
0 commit comments