Skip to content

Commit a70ba2a

Browse files
committed
feat: Localize app configuration page
- Added localization support - Updated text to use l10n
1 parent 2300f49 commit a70ba2a

File tree

1 file changed

+66
-76
lines changed

1 file changed

+66
-76
lines changed

lib/app_configuration/view/app_configuration_page.dart

Lines changed: 66 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
33
import 'package:ht_dashboard/app_configuration/bloc/app_configuration_bloc.dart';
44
import 'package:ht_dashboard/shared/constants/app_spacing.dart';
55
import 'package:ht_dashboard/shared/widgets/widgets.dart';
6+
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
67
import 'package:ht_shared/ht_shared.dart'; // For AppConfig and its nested models
78

89
/// {@template app_configuration_page}
@@ -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

Comments
 (0)