From 30c29a9a6136132b52c3db63eabe5615291a873c Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 16:12:08 +0100 Subject: [PATCH 1/6] feat(l10n): add new localization strings - Added labels and descriptions for: - Followed items limit - Saved headlines limit - Ad frequency - Ad placement interval - Articles before interstitial ads --- lib/l10n/app_localizations.dart | 82 ++++++++++++++++++++++++++++-- lib/l10n/app_localizations_ar.dart | 49 ++++++++++++++++-- lib/l10n/app_localizations_en.dart | 48 ++++++++++++++++- lib/l10n/arb/app_ar.arb | 57 +++++++++++++++++++++ lib/l10n/arb/app_en.arb | 65 +++++++++++++++++++++-- 5 files changed, 287 insertions(+), 14 deletions(-) diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 75ab7f39..b3ec87ba 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -716,11 +716,11 @@ abstract class AppLocalizations { /// **'Android Store URL'** String get androidStoreUrlLabel; - /// Description for Android Store URL + /// Description for Android Update URL /// /// In en, this message translates to: /// **'URL to the app on the Google Play Store.'** - String get androidStoreUrlDescription; + String get androidUpdateUrlDescription; /// Label for Guest Days Between In-App Prompts /// @@ -1496,11 +1496,83 @@ abstract class AppLocalizations { /// **'Android Update URL'** String get androidUpdateUrlLabel; - /// Description for Android Update URL + /// Label for Followed Items Limit /// /// In en, this message translates to: - /// **'URL for Android app updates.'** - String get androidUpdateUrlDescription; + /// **'Followed Items Limit'** + String get followedItemsLimitLabel; + + /// Description for Followed Items Limit + /// + /// In en, this message translates to: + /// **'Maximum number of countries, news sources, or categories this user role can follow (each type has its own limit).'** + String get followedItemsLimitDescription; + + /// Label for Saved Headlines Limit + /// + /// In en, this message translates to: + /// **'Saved Headlines Limit'** + String get savedHeadlinesLimitLabel; + + /// Description for Saved Headlines Limit + /// + /// In en, this message translates to: + /// **'Maximum number of headlines this user role can save.'** + String get savedHeadlinesLimitDescription; + + /// Label for Ad Frequency + /// + /// In en, this message translates to: + /// **'Ad Frequency'** + String get adFrequencyLabel; + + /// Description for Ad Frequency + /// + /// In en, this message translates to: + /// **'How often an ad can appear for this user role (e.g., a value of 5 means an ad could be placed after every 5 news items).'** + String get adFrequencyDescription; + + /// Label for Ad Placement Interval + /// + /// In en, this message translates to: + /// **'Ad Placement Interval'** + String get adPlacementIntervalLabel; + + /// Description for Ad Placement Interval + /// + /// In en, this message translates to: + /// **'Minimum number of news items that must be shown before the very first ad appears for this user role.'** + String get adPlacementIntervalDescription; + + /// Label for Articles Before Interstitial Ads + /// + /// In en, this message translates to: + /// **'Articles Before Interstitial Ads'** + String get articlesBeforeInterstitialAdsLabel; + + /// Description for Articles Before Interstitial Ads + /// + /// In en, this message translates to: + /// **'Number of articles this user role needs to read before a full-screen interstitial ad is shown.'** + String get articlesBeforeInterstitialAdsDescription; + + /// Suffix for number of days in prompt descriptions + /// + /// In en, this message translates to: + /// **'Days'** + String get daysSuffix; + + /// Description for days between in-app prompts + /// + /// In en, this message translates to: + /// **'Minimum number of days before showing the {actionType} prompt.'** + String daysBetweenPromptDescription(String actionType); + + /// Text for the retry button + /// + /// In en, this message translates to: + /// **'Retry'** + String get retryButtonText; } class _AppLocalizationsDelegate diff --git a/lib/l10n/app_localizations_ar.dart b/lib/l10n/app_localizations_ar.dart index 6f823816..abe2b849 100644 --- a/lib/l10n/app_localizations_ar.dart +++ b/lib/l10n/app_localizations_ar.dart @@ -371,8 +371,7 @@ class AppLocalizationsAr extends AppLocalizations { String get androidStoreUrlLabel => 'رابط متجر Android'; @override - String get androidStoreUrlDescription => - 'رابط التطبيق على متجر Google Play Store.'; + String get androidUpdateUrlDescription => 'رابط تحديثات تطبيق Android.'; @override String get guestDaysBetweenInAppPromptsLabel => @@ -785,5 +784,49 @@ class AppLocalizationsAr extends AppLocalizations { String get androidUpdateUrlLabel => 'رابط تحديث Android'; @override - String get androidUpdateUrlDescription => 'رابط تحديثات تطبيق Android.'; + String get followedItemsLimitLabel => 'حد العناصر المتابعة'; + + @override + String get followedItemsLimitDescription => + 'الحد الأقصى لعدد البلدان أو مصادر الأخبار أو المواضيع التي يمكن لهذا الدور المستخدم متابعتها (لكل نوع حد خاص به).'; + + @override + String get savedHeadlinesLimitLabel => 'حد العناوين المحفوظة'; + + @override + String get savedHeadlinesLimitDescription => + 'الحد الأقصى لعدد العناوين الرئيسية التي يمكن لهذا الدور المستخدم حفظها.'; + + @override + String get adFrequencyLabel => 'تكرار الإعلان'; + + @override + String get adFrequencyDescription => + 'عدد مرات ظهور الإعلان لهذا الدور المستخدم (على سبيل المثال، قيمة 5 تعني أنه يمكن وضع إعلان بعد كل 5 عناصر إخبارية).'; + + @override + String get adPlacementIntervalLabel => 'فترة وضع الإعلان'; + + @override + String get adPlacementIntervalDescription => + 'الحد الأدنى لعدد عناصر الأخبار التي يجب عرضها قبل ظهور الإعلان الأول لهذا الدور المستخدم.'; + + @override + String get articlesBeforeInterstitialAdsLabel => + 'مقالات قبل الإعلانات البينية'; + + @override + String get articlesBeforeInterstitialAdsDescription => + 'عدد المقالات التي يحتاج هذا الدور المستخدم لقراءتها قبل عرض إعلان بيني بملء الشاشة.'; + + @override + String get daysSuffix => 'أيام'; + + @override + String daysBetweenPromptDescription(String actionType) { + return 'الحد الأدنى لعدد الأيام قبل عرض تنبيه $actionType.'; + } + + @override + String get retryButtonText => 'إعادة المحاولة'; } diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 16f9b59c..54189838 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -368,7 +368,7 @@ class AppLocalizationsEn extends AppLocalizations { String get androidStoreUrlLabel => 'Android Store URL'; @override - String get androidStoreUrlDescription => + String get androidUpdateUrlDescription => 'URL to the app on the Google Play Store.'; @override @@ -783,5 +783,49 @@ class AppLocalizationsEn extends AppLocalizations { String get androidUpdateUrlLabel => 'Android Update URL'; @override - String get androidUpdateUrlDescription => 'URL for Android app updates.'; + String get followedItemsLimitLabel => 'Followed Items Limit'; + + @override + String get followedItemsLimitDescription => + 'Maximum number of countries, news sources, or categories this user role can follow (each type has its own limit).'; + + @override + String get savedHeadlinesLimitLabel => 'Saved Headlines Limit'; + + @override + String get savedHeadlinesLimitDescription => + 'Maximum number of headlines this user role can save.'; + + @override + String get adFrequencyLabel => 'Ad Frequency'; + + @override + String get adFrequencyDescription => + 'How often an ad can appear for this user role (e.g., a value of 5 means an ad could be placed after every 5 news items).'; + + @override + String get adPlacementIntervalLabel => 'Ad Placement Interval'; + + @override + String get adPlacementIntervalDescription => + 'Minimum number of news items that must be shown before the very first ad appears for this user role.'; + + @override + String get articlesBeforeInterstitialAdsLabel => + 'Articles Before Interstitial Ads'; + + @override + String get articlesBeforeInterstitialAdsDescription => + 'Number of articles this user role needs to read before a full-screen interstitial ad is shown.'; + + @override + String get daysSuffix => 'Days'; + + @override + String daysBetweenPromptDescription(String actionType) { + return 'Minimum number of days before showing the $actionType prompt.'; + } + + @override + String get retryButtonText => 'Retry'; } diff --git a/lib/l10n/arb/app_ar.arb b/lib/l10n/arb/app_ar.arb index 027f35a6..133516a8 100644 --- a/lib/l10n/arb/app_ar.arb +++ b/lib/l10n/arb/app_ar.arb @@ -961,5 +961,62 @@ "androidUpdateUrlDescription": "رابط تحديثات تطبيق Android.", "@androidUpdateUrlDescription": { "description": "وصف رابط تحديث Android" + }, + "followedItemsLimitLabel": "حد العناصر المتابعة", + "@followedItemsLimitLabel": { + "description": "تسمية حد العناصر المتابعة" + }, + "followedItemsLimitDescription": "الحد الأقصى لعدد البلدان أو مصادر الأخبار أو المواضيع التي يمكن لهذا الدور المستخدم متابعتها (لكل نوع حد خاص به).", + "@followedItemsLimitDescription": { + "description": "وصف حد العناصر المتابعة" + }, + "savedHeadlinesLimitLabel": "حد العناوين المحفوظة", + "@savedHeadlinesLimitLabel": { + "description": "تسمية حد العناوين المحفوظة" + }, + "savedHeadlinesLimitDescription": "الحد الأقصى لعدد العناوين الرئيسية التي يمكن لهذا الدور المستخدم حفظها.", + "@savedHeadlinesLimitDescription": { + "description": "وصف حد العناوين المحفوظة" + }, + "adFrequencyLabel": "تكرار الإعلان", + "@adFrequencyLabel": { + "description": "تسمية تكرار الإعلان" + }, + "adFrequencyDescription": "عدد مرات ظهور الإعلان لهذا الدور المستخدم (على سبيل المثال، قيمة 5 تعني أنه يمكن وضع إعلان بعد كل 5 عناصر إخبارية).", + "@adFrequencyDescription": { + "description": "وصف تكرار الإعلان" + }, + "adPlacementIntervalLabel": "فترة وضع الإعلان", + "@adPlacementIntervalLabel": { + "description": "تسمية فترة وضع الإعلان" + }, + "adPlacementIntervalDescription": "الحد الأدنى لعدد عناصر الأخبار التي يجب عرضها قبل ظهور الإعلان الأول لهذا الدور المستخدم.", + "@adPlacementIntervalDescription": { + "description": "وصف فترة وضع الإعلان" + }, + "articlesBeforeInterstitialAdsLabel": "مقالات قبل الإعلانات البينية", + "@articlesBeforeInterstitialAdsLabel": { + "description": "تسمية مقالات قبل الإعلانات البينية" + }, + "articlesBeforeInterstitialAdsDescription": "عدد المقالات التي يحتاج هذا الدور المستخدم لقراءتها قبل عرض إعلان بيني بملء الشاشة.", + "@articlesBeforeInterstitialAdsDescription": { + "description": "وصف مقالات قبل الإعلانات البينية" + }, + "daysSuffix": "أيام", + "@daysSuffix": { + "description": "لاحقة لعدد الأيام في أوصاف التنبيهات" + }, + "daysBetweenPromptDescription": "الحد الأدنى لعدد الأيام قبل عرض تنبيه {actionType}.", + "@daysBetweenPromptDescription": { + "description": "وصف الأيام بين التنبيهات داخل التطبيق", + "placeholders": { + "actionType": { + "type": "String" + } + } + }, + "retryButtonText": "إعادة المحاولة", + "@retryButtonText": { + "description": "نص زر إعادة المحاولة" } } diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 5e72a003..0ca252d1 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -426,9 +426,9 @@ "@androidStoreUrlLabel": { "description": "Label for Android Store URL" }, - "androidStoreUrlDescription": "URL to the app on the Google Play Store.", - "@androidStoreUrlDescription": { - "description": "Description for Android Store URL" + "androidUpdateUrlDescription": "URL to the app on the Google Play Store.", + "@androidUpdateUrlDescription": { + "description": "Description for Android Update URL" }, "guestDaysBetweenInAppPromptsLabel": "Guest Days Between In-App Prompts", "@guestDaysBetweenInAppPromptsLabel": { @@ -958,8 +958,65 @@ "@androidUpdateUrlLabel": { "description": "Label for Android Update URL" }, - "androidUpdateUrlDescription": "URL for Android app updates.", + "androidUpdateUrlDescription": "URL to the app on the Google Play Store.", "@androidUpdateUrlDescription": { "description": "Description for Android Update URL" + }, + "followedItemsLimitLabel": "Followed Items Limit", + "@followedItemsLimitLabel": { + "description": "Label for Followed Items Limit" + }, + "followedItemsLimitDescription": "Maximum number of countries, news sources, or categories this user role can follow (each type has its own limit).", + "@followedItemsLimitDescription": { + "description": "Description for Followed Items Limit" + }, + "savedHeadlinesLimitLabel": "Saved Headlines Limit", + "@savedHeadlinesLimitLabel": { + "description": "Label for Saved Headlines Limit" + }, + "savedHeadlinesLimitDescription": "Maximum number of headlines this user role can save.", + "@savedHeadlinesLimitDescription": { + "description": "Description for Saved Headlines Limit" + }, + "adFrequencyLabel": "Ad Frequency", + "@adFrequencyLabel": { + "description": "Label for Ad Frequency" + }, + "adFrequencyDescription": "How often an ad can appear for this user role (e.g., a value of 5 means an ad could be placed after every 5 news items).", + "@adFrequencyDescription": { + "description": "Description for Ad Frequency" + }, + "adPlacementIntervalLabel": "Ad Placement Interval", + "@adPlacementIntervalLabel": { + "description": "Label for Ad Placement Interval" + }, + "adPlacementIntervalDescription": "Minimum number of news items that must be shown before the very first ad appears for this user role.", + "@adPlacementIntervalDescription": { + "description": "Description for Ad Placement Interval" + }, + "articlesBeforeInterstitialAdsLabel": "Articles Before Interstitial Ads", + "@articlesBeforeInterstitialAdsLabel": { + "description": "Label for Articles Before Interstitial Ads" + }, + "articlesBeforeInterstitialAdsDescription": "Number of articles this user role needs to read before a full-screen interstitial ad is shown.", + "@articlesBeforeInterstitialAdsDescription": { + "description": "Description for Articles Before Interstitial Ads" + }, + "daysSuffix": "Days", + "@daysSuffix": { + "description": "Suffix for number of days in prompt descriptions" + }, + "daysBetweenPromptDescription": "Minimum number of days before showing the {actionType} prompt.", + "@daysBetweenPromptDescription": { + "description": "Description for days between in-app prompts", + "placeholders": { + "actionType": { + "type": "String" + } + } + }, + "retryButtonText": "Retry", + "@retryButtonText": { + "description": "Text for the retry button" } } From 63a3ae088ec7d89db80cc9173032ebcc6100d8dc Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 16:12:21 +0100 Subject: [PATCH 2/6] refactor(app_config): Use l10n for labels and descriptions - Replaced hardcoded labels with localized strings. - Replaced hardcoded descriptions with localized strings. - Improved readability and maintainability. - Simplified code by using localized strings. - Updated UI to reflect localized text. --- .../view/app_configuration_page.dart | 42 +++++++++---------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/lib/app_configuration/view/app_configuration_page.dart b/lib/app_configuration/view/app_configuration_page.dart index bdbe45bf..2770e305 100644 --- a/lib/app_configuration/view/app_configuration_page.dart +++ b/lib/app_configuration/view/app_configuration_page.dart @@ -766,15 +766,14 @@ class _UserPreferenceLimitsFormState extends State<_UserPreferenceLimitsForm> { @override Widget build(BuildContext context) { final userPreferenceConfig = widget.remoteConfig.userPreferenceConfig; + final l10n = context.l10n; return Column( children: [ widget.buildIntField( context, - label: 'Followed Items Limit', - description: - 'Maximum number of countries, news sources, or categories this ' - 'user role can follow (each type has its own limit).', + label: l10n.followedItemsLimitLabel, + description: l10n.followedItemsLimitDescription, value: _getFollowedItemsLimit(userPreferenceConfig), onChanged: (value) { widget.onConfigChanged( @@ -790,8 +789,8 @@ class _UserPreferenceLimitsFormState extends State<_UserPreferenceLimitsForm> { ), widget.buildIntField( context, - label: 'Saved Headlines Limit', - description: 'Maximum number of headlines this user role can save.', + label: l10n.savedHeadlinesLimitLabel, + description: l10n.savedHeadlinesLimitDescription, value: _getSavedHeadlinesLimit(userPreferenceConfig), onChanged: (value) { widget.onConfigChanged( @@ -990,15 +989,14 @@ class _AdConfigFormState extends State<_AdConfigForm> { @override Widget build(BuildContext context) { final adConfig = widget.remoteConfig.adConfig; + final l10n = context.l10n; return Column( children: [ widget.buildIntField( context, - label: 'Ad Frequency', - description: - 'How often an ad can appear for this user role (e.g., a value ' - 'of 5 means an ad could be placed after every 5 news items).', + label: l10n.adFrequencyLabel, + description: l10n.adFrequencyDescription, value: _getAdFrequency(adConfig), onChanged: (value) { widget.onConfigChanged( @@ -1011,10 +1009,8 @@ class _AdConfigFormState extends State<_AdConfigForm> { ), widget.buildIntField( context, - label: 'Ad Placement Interval', - description: - 'Minimum number of news items that must be shown before the ' - 'very first ad appears for this user role.', + label: l10n.adPlacementIntervalLabel, + description: l10n.adPlacementIntervalDescription, value: _getAdPlacementInterval(adConfig), onChanged: (value) { widget.onConfigChanged( @@ -1027,10 +1023,8 @@ class _AdConfigFormState extends State<_AdConfigForm> { ), widget.buildIntField( context, - label: 'Articles Before Interstitial Ads', - description: - 'Number of articles this user role needs to read before a ' - 'full-screen interstitial ad is shown.', + label: l10n.articlesBeforeInterstitialAdsLabel, + description: l10n.articlesBeforeInterstitialAdsDescription, value: _getArticlesBeforeInterstitial(adConfig), onChanged: (value) { widget.onConfigChanged( @@ -1198,27 +1192,29 @@ class _AccountActionConfigFormState extends State<_AccountActionConfigForm> { super.dispose(); } - String _formatLabel(String enumName) { + String _formatLabel(String enumName, AppLocalizations l10n) { // Converts camelCase to Title Case final spaced = enumName.replaceAllMapped( RegExp('([A-Z])'), (match) => ' ${match.group(1)}', ); - return '${spaced[0].toUpperCase()}${spaced.substring(1)} Days'; + return '${spaced[0].toUpperCase()}${spaced.substring(1)} ${l10n.daysSuffix}'; } @override Widget build(BuildContext context) { final accountActionConfig = widget.remoteConfig.accountActionConfig; final relevantActionTypes = _getDaysMap(accountActionConfig).keys.toList(); + final l10n = context.l10n; return Column( children: relevantActionTypes.map((actionType) { return widget.buildIntField( context, - label: _formatLabel(actionType.name), - description: - 'Minimum number of days before showing the ${actionType.name} prompt.', + label: _formatLabel(actionType.name, l10n), + description: l10n.daysBetweenPromptDescription( + actionType: actionType.name, + ), value: _getDaysMap(accountActionConfig)[actionType] ?? 0, onChanged: (value) { final currentMap = _getDaysMap(accountActionConfig); From 719d7366525f4da9b22a65c3e729f1d32286d789 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 16:12:27 +0100 Subject: [PATCH 3/6] fix(failure_state): use localized retry button text - Imported l10n package. - Used localized string for retry button. --- lib/shared/widgets/failure_state_widget.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/shared/widgets/failure_state_widget.dart b/lib/shared/widgets/failure_state_widget.dart index 7848c5a4..95d157a6 100644 --- a/lib/shared/widgets/failure_state_widget.dart +++ b/lib/shared/widgets/failure_state_widget.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:ht_shared/ht_shared.dart'; import 'package:ht_ui_kit/ht_ui_kit.dart'; +import 'package:ht_dashboard/l10n/l10n.dart'; // Import l10n /// A widget to display an error message and an optional retry button. class FailureStateWidget extends StatelessWidget { @@ -32,6 +33,7 @@ class FailureStateWidget extends StatelessWidget { @override Widget build(BuildContext context) { final friendlyMessage = exception.toFriendlyMessage(context); + final l10n = context.l10n; // Get l10n instance return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -47,7 +49,7 @@ class FailureStateWidget extends StatelessWidget { padding: const EdgeInsets.only(top: 16), child: ElevatedButton( onPressed: onRetry, - child: Text(retryButtonText ?? 'Retry'), + child: Text(retryButtonText ?? l10n.retryButtonText), ), ), ], From efb34847a36d124e6d1e3946d72c8af389948032 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 17:54:42 +0100 Subject: [PATCH 4/6] refactor(app_config): Improve user preference limits display - Use AppLocalizations for labels/descriptions - Added helper methods for better readability - Improved code structure and organization - Simplified descriptions in some cases - Handle different user roles consistently --- .../view/app_configuration_page.dart | 57 ++++++++++++++++--- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/lib/app_configuration/view/app_configuration_page.dart b/lib/app_configuration/view/app_configuration_page.dart index 2770e305..28e3202f 100644 --- a/lib/app_configuration/view/app_configuration_page.dart +++ b/lib/app_configuration/view/app_configuration_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:ht_dashboard/app_configuration/bloc/app_configuration_bloc.dart'; +import 'package:ht_dashboard/l10n/app_localizations.dart'; import 'package:ht_dashboard/l10n/l10n.dart'; import 'package:ht_dashboard/shared/constants/app_spacing.dart'; import 'package:ht_dashboard/shared/widgets/widgets.dart'; @@ -772,8 +773,8 @@ class _UserPreferenceLimitsFormState extends State<_UserPreferenceLimitsForm> { children: [ widget.buildIntField( context, - label: l10n.followedItemsLimitLabel, - description: l10n.followedItemsLimitDescription, + label: _getFollowedItemsLimitLabel(l10n), + description: _getFollowedItemsLimitDescription(l10n), value: _getFollowedItemsLimit(userPreferenceConfig), onChanged: (value) { widget.onConfigChanged( @@ -789,8 +790,8 @@ class _UserPreferenceLimitsFormState extends State<_UserPreferenceLimitsForm> { ), widget.buildIntField( context, - label: l10n.savedHeadlinesLimitLabel, - description: l10n.savedHeadlinesLimitDescription, + label: _getSavedHeadlinesLimitLabel(l10n), + description: _getSavedHeadlinesLimitDescription(l10n), value: _getSavedHeadlinesLimit(userPreferenceConfig), onChanged: (value) { widget.onConfigChanged( @@ -808,6 +809,50 @@ class _UserPreferenceLimitsFormState extends State<_UserPreferenceLimitsForm> { ); } + String _getFollowedItemsLimitLabel(AppLocalizations l10n) { + switch (widget.userRole) { + case AppUserRole.guestUser: + return l10n.guestFollowedItemsLimitLabel; + case AppUserRole.standardUser: + return l10n.standardUserFollowedItemsLimitLabel; + case AppUserRole.premiumUser: + return l10n.premiumFollowedItemsLimitLabel; + } + } + + String _getFollowedItemsLimitDescription(AppLocalizations l10n) { + switch (widget.userRole) { + case AppUserRole.guestUser: + return l10n.guestFollowedItemsLimitDescription; + case AppUserRole.standardUser: + return l10n.standardUserFollowedItemsLimitDescription; + case AppUserRole.premiumUser: + return l10n.premiumFollowedItemsLimitDescription; + } + } + + String _getSavedHeadlinesLimitLabel(AppLocalizations l10n) { + switch (widget.userRole) { + case AppUserRole.guestUser: + return l10n.guestSavedHeadlinesLimitLabel; + case AppUserRole.standardUser: + return l10n.standardUserSavedHeadlinesLimitLabel; + case AppUserRole.premiumUser: + return l10n.premiumSavedHeadlinesLimitLabel; + } + } + + String _getSavedHeadlinesLimitDescription(AppLocalizations l10n) { + switch (widget.userRole) { + case AppUserRole.guestUser: + return l10n.guestSavedHeadlinesLimitDescription; + case AppUserRole.standardUser: + return l10n.standardUserSavedHeadlinesLimitDescription; + case AppUserRole.premiumUser: + return l10n.premiumSavedHeadlinesLimitDescription; + } + } + int _getFollowedItemsLimit(UserPreferenceConfig config) { switch (widget.userRole) { case AppUserRole.guestUser: @@ -1212,9 +1257,7 @@ class _AccountActionConfigFormState extends State<_AccountActionConfigForm> { return widget.buildIntField( context, label: _formatLabel(actionType.name, l10n), - description: l10n.daysBetweenPromptDescription( - actionType: actionType.name, - ), + description: l10n.daysBetweenPromptDescription(actionType.name), value: _getDaysMap(accountActionConfig)[actionType] ?? 0, onChanged: (value) { final currentMap = _getDaysMap(accountActionConfig); From 36b64cbca23ca119b115c3912de83f43f63262e6 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 18:22:28 +0100 Subject: [PATCH 5/6] feat(l10n): Add feed action types to localization - Added "Link Account" - Added "Rate App" - Added "Follow Topics" - Added "Follow Sources" - Added "Enable Notifications" --- lib/l10n/app_localizations.dart | 36 ++++++++++++++++++++++++++++++ lib/l10n/app_localizations_ar.dart | 18 +++++++++++++++ lib/l10n/app_localizations_en.dart | 18 +++++++++++++++ lib/l10n/arb/app_ar.arb | 24 ++++++++++++++++++++ lib/l10n/arb/app_en.arb | 26 ++++++++++++++++++++- 5 files changed, 121 insertions(+), 1 deletion(-) diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index b3ec87ba..18664028 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -1573,6 +1573,42 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'Retry'** String get retryButtonText; + + /// Feed action type for linking an account + /// + /// In en, this message translates to: + /// **'Link Account'** + String get feedActionTypeLinkAccount; + + /// Feed action type for rating the app + /// + /// In en, this message translates to: + /// **'Rate App'** + String get feedActionTypeRateApp; + + /// Feed action type for following topics + /// + /// In en, this message translates to: + /// **'Follow Topics'** + String get feedActionTypeFollowTopics; + + /// Feed action type for following sources + /// + /// In en, this message translates to: + /// **'Follow Sources'** + String get feedActionTypeFollowSources; + + /// Feed action type for upgrading + /// + /// In en, this message translates to: + /// **'Upgrade'** + String get feedActionTypeUpgrade; + + /// Feed action type for enabling notifications + /// + /// In en, this message translates to: + /// **'Enable Notifications'** + String get feedActionTypeEnableNotifications; } class _AppLocalizationsDelegate diff --git a/lib/l10n/app_localizations_ar.dart b/lib/l10n/app_localizations_ar.dart index abe2b849..f5414485 100644 --- a/lib/l10n/app_localizations_ar.dart +++ b/lib/l10n/app_localizations_ar.dart @@ -829,4 +829,22 @@ class AppLocalizationsAr extends AppLocalizations { @override String get retryButtonText => 'إعادة المحاولة'; + + @override + String get feedActionTypeLinkAccount => 'ربط الحساب'; + + @override + String get feedActionTypeRateApp => 'تقييم التطبيق'; + + @override + String get feedActionTypeFollowTopics => 'متابعة المواضيع'; + + @override + String get feedActionTypeFollowSources => 'متابعة المصادر'; + + @override + String get feedActionTypeUpgrade => 'ترقية'; + + @override + String get feedActionTypeEnableNotifications => 'تفعيل الإشعارات'; } diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 54189838..df9bb499 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -828,4 +828,22 @@ class AppLocalizationsEn extends AppLocalizations { @override String get retryButtonText => 'Retry'; + + @override + String get feedActionTypeLinkAccount => 'Link Account'; + + @override + String get feedActionTypeRateApp => 'Rate App'; + + @override + String get feedActionTypeFollowTopics => 'Follow Topics'; + + @override + String get feedActionTypeFollowSources => 'Follow Sources'; + + @override + String get feedActionTypeUpgrade => 'Upgrade'; + + @override + String get feedActionTypeEnableNotifications => 'Enable Notifications'; } diff --git a/lib/l10n/arb/app_ar.arb b/lib/l10n/arb/app_ar.arb index 133516a8..78402ef5 100644 --- a/lib/l10n/arb/app_ar.arb +++ b/lib/l10n/arb/app_ar.arb @@ -1018,5 +1018,29 @@ "retryButtonText": "إعادة المحاولة", "@retryButtonText": { "description": "نص زر إعادة المحاولة" + }, + "feedActionTypeLinkAccount": "ربط الحساب", + "@feedActionTypeLinkAccount": { + "description": "نوع إجراء الموجز لربط حساب" + }, + "feedActionTypeRateApp": "تقييم التطبيق", + "@feedActionTypeRateApp": { + "description": "نوع إجراء الموجز لتقييم التطبيق" + }, + "feedActionTypeFollowTopics": "متابعة المواضيع", + "@feedActionTypeFollowTopics": { + "description": "نوع إجراء الموجز لمتابعة المواضيع" + }, + "feedActionTypeFollowSources": "متابعة المصادر", + "@feedActionTypeFollowSources": { + "description": "نوع إجراء الموجز لمتابعة المصادر" + }, + "feedActionTypeUpgrade": "ترقية", + "@feedActionTypeUpgrade": { + "description": "نوع إجراء الموجز للترقية" + }, + "feedActionTypeEnableNotifications": "تفعيل الإشعارات", + "@feedActionTypeEnableNotifications": { + "description": "نوع إجراء الموجز لتفعيل الإشعارات" } } diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 0ca252d1..5e2f455b 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -1018,5 +1018,29 @@ "retryButtonText": "Retry", "@retryButtonText": { "description": "Text for the retry button" + }, + "feedActionTypeLinkAccount": "Link Account", + "@feedActionTypeLinkAccount": { + "description": "Feed action type for linking an account" + }, + "feedActionTypeRateApp": "Rate App", + "@feedActionTypeRateApp": { + "description": "Feed action type for rating the app" + }, + "feedActionTypeFollowTopics": "Follow Topics", + "@feedActionTypeFollowTopics": { + "description": "Feed action type for following topics" + }, + "feedActionTypeFollowSources": "Follow Sources", + "@feedActionTypeFollowSources": { + "description": "Feed action type for following sources" + }, + "feedActionTypeUpgrade": "Upgrade", + "@feedActionTypeUpgrade": { + "description": "Feed action type for upgrading" + }, + "feedActionTypeEnableNotifications": "Enable Notifications", + "@feedActionTypeEnableNotifications": { + "description": "Feed action type for enabling notifications" } -} +} \ No newline at end of file From a09d30ef7662f6e98df264dfb01afc68b60ed41c Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 14 Jul 2025 18:22:38 +0100 Subject: [PATCH 6/6] refactor(app_config): Improve label formatting - Removed redundant formatting function. - Use switch statement for localized labels. - Improved clarity and readability. - Localized description using localized action type. - Simplified label generation. --- .../view/app_configuration_page.dart | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/app_configuration/view/app_configuration_page.dart b/lib/app_configuration/view/app_configuration_page.dart index 28e3202f..41e40e5c 100644 --- a/lib/app_configuration/view/app_configuration_page.dart +++ b/lib/app_configuration/view/app_configuration_page.dart @@ -1237,15 +1237,6 @@ class _AccountActionConfigFormState extends State<_AccountActionConfigForm> { super.dispose(); } - String _formatLabel(String enumName, AppLocalizations l10n) { - // Converts camelCase to Title Case - final spaced = enumName.replaceAllMapped( - RegExp('([A-Z])'), - (match) => ' ${match.group(1)}', - ); - return '${spaced[0].toUpperCase()}${spaced.substring(1)} ${l10n.daysSuffix}'; - } - @override Widget build(BuildContext context) { final accountActionConfig = widget.remoteConfig.accountActionConfig; @@ -1254,10 +1245,21 @@ class _AccountActionConfigFormState extends State<_AccountActionConfigForm> { return Column( children: relevantActionTypes.map((actionType) { + final localizedActionType = switch (actionType) { + FeedActionType.linkAccount => l10n.feedActionTypeLinkAccount, + FeedActionType.rateApp => l10n.feedActionTypeRateApp, + FeedActionType.followTopics => l10n.feedActionTypeFollowTopics, + FeedActionType.followSources => l10n.feedActionTypeFollowSources, + FeedActionType.upgrade => l10n.feedActionTypeUpgrade, + FeedActionType.enableNotifications => + l10n.feedActionTypeEnableNotifications, + }; return widget.buildIntField( context, - label: _formatLabel(actionType.name, l10n), - description: l10n.daysBetweenPromptDescription(actionType.name), + label: '$localizedActionType ${l10n.daysSuffix}', + description: l10n.daysBetweenPromptDescription( + localizedActionType, + ), value: _getDaysMap(accountActionConfig)[actionType] ?? 0, onChanged: (value) { final currentMap = _getDaysMap(accountActionConfig);