Fixes #20479

## Description
Redirects to the site creation failure flow when the endpoint fails instead of an unhandled crash

-----

## To Test:
Since I was not able to reproduce this issue I would recommend a sanity check of the site creation flow

-----

## Regression Notes

1. Potential unintended areas of impact

    - Site Creation

2. What I did to test those areas of impact (or what existing automated tests I relied on)

    - Manual testing

3. What automated tests I added (or what prevented me from doing so)

    - Added a test case

-----

## PR Submission Checklist:

- [x] I have completed the Regression Notes.
- [x] I have considered adding accessibility improvements for my changes.
- [x] I have considered if this change warrants user-facing release notes and have added them to `RELEASE-NOTES.txt` if necessary.

-----

## Testing Checklist (strike-out the not-applying and unnecessary ones):

- [ ] WordPress.com sites and self-hosted Jetpack sites.
- [ ] Portrait and landscape orientations.
- [ ] Light and dark modes.
- [ ] Fonts: Larger, smaller and bold text.
- [ ] High contrast.
- [ ] Talkback.
- [ ] Languages with large words or with letters/accents not frequently used in English.
- [ ] Right-to-left languages. (Even if translation isn’t complete, formatting should still respect the right-to-left layout)
- [ ] Large and small screen sizes. (Tablet and smaller phones)
- [ ] Multi-tasking: Split screen and Pop-up view. (Android 10 or higher)

IllegalStateException: Site already exists - seems like an issue with domain suggestions endpoint
Sentry Issue: [JETPACK-ANDROID-DZ0](https://a8c.sentry.io/issues/4584356763/?referrer=github_integration)

```
IllegalStateException: Site already exists - seems like an issue with domain suggestions endpoint
    at org.wordpress.android.ui.sitecreation.services.SiteCreationServiceManager$createSite$1.invokeSuspend(SiteCreationServiceManager.kt:139)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.CoroutineContextKt.withContinuationContext
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:90)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108)
...
(4 additional frame(s) were not displayed)
```

Thanks for reporting! 👍 <!-- generated_by_dangermattic -->

Sentry Issue: [JETPACK-ANDROID-1RW](https://a8c.sentry.io/issues/3857462344/?referrer=github_integration)
Checking the sentry logs all of them contain the following error:
```
e/WordPress-SITE_CREATION: Site already exists - seems like an issue with domain suggestions endpoint

StackTrace: com.android.volley.ClientError
	at com.android.volley.toolbox.NetworkUtility.shouldRetryException(NetworkUtility.java:193)
	at com.android.volley.toolbox.BasicNetwork.performRequest(BasicNetwork.java:145)
	at com.android.volley.NetworkDispatcher.processRequest(NetworkDispatcher.java:132)
	at com.android.volley.NetworkDispatcher.processRequest(NetworkDispatcher.java:111)
	at com.android.volley.NetworkDispatcher.run(NetworkDispatcher.java:90)
Volley error on https://public-api.wordpress.com/rest/v1.1/sites/new/?locale=en_GB Status Code: 400 - exception: null
```
[Checking the Volley code](https://github.com/google/volley/blob/master/core/src/main/java/com/android/volley/toolbox/NetworkUtility.java#L191) the above should indicate that no retry is possible.

Checking the stats ~2 users per day experience this crash

![Screenshot 2024-03-15 at 1 07 54 PM](https://github.com/wordpress-mobile/WordPress-Android/assets/304044/ff71f77b-ac3f-4d89-937f-eb8625f90d5c)
![Screenshot 2024-03-15 at 1 18 55 PM](https://github.com/wordpress-mobile/WordPress-Android/assets/304044/6bb14303-d964-4103-9012-520475cd494d)

diff --git a/WordPress/src/main/java/org/wordpress/android/ui/sitecreation/services/SiteCreationServiceManager.kt b/WordPress/src/main/java/org/wordpress/android/ui/sitecreation/services/SiteCreationServiceManager.kt
index 86a62720f401..b4dc3824e639 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/sitecreation/services/SiteCreationServiceManager.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/sitecreation/services/SiteCreationServiceManager.kt
@@ -136,7 +136,7 @@ class SiteCreationServiceManager @Inject constructor(
                          */
                         val errorMsg = "Site already exists - seems like an issue with domain suggestions endpoint"
                         AppLog.e(T.SITE_CREATION, errorMsg)
-                        throw IllegalStateException(errorMsg)
+                        executePhase(FAILURE)
                     }
                 } else {
                     executePhase(FAILURE)

diff --git a/WordPress/src/test/java/org/wordpress/android/ui/sitecreation/services/SiteCreationServiceManagerTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/sitecreation/services/SiteCreationServiceManagerTest.kt
index a70333b10838..ed8ecd68a3a2 100644
--- a/WordPress/src/test/java/org/wordpress/android/ui/sitecreation/services/SiteCreationServiceManagerTest.kt
+++ b/WordPress/src/test/java/org/wordpress/android/ui/sitecreation/services/SiteCreationServiceManagerTest.kt
@@ -134,6 +134,22 @@ class SiteCreationServiceManagerTest : BaseUnitTest() {
         }
     }

+    @Test
+    fun verifyFailureFlowWhenTheSiteExistsAndIsNotARetry() = test {
+        setSiteExistsErrorResponses()
+        val stateBeforeFailure = CREATE_SITE_STATE
+        whenever(serviceListener.getCurrentState()).thenReturn(stateBeforeFailure)
+
+        startFlow()
+
+        argumentCaptor<SiteCreationServiceState>().apply {
+            verify(serviceListener, times(3)).updateState(capture())
+            assertThat(allValues[0]).isEqualTo(IDLE_STATE)
+            assertThat(allValues[1]).isEqualTo(CREATE_SITE_STATE)
+            assertThat(allValues[2]).isEqualTo(FAILURE_STATE.copy(payload = stateBeforeFailure))
+        }
+    }
+
     @Test
     fun verifyRetryWorksWhenCreateSiteRequestFailed() = test {
         setGenericErrorResponses()


[Reader] Fix `Reader` showing posts from a language different than the selected one

Fixes #19969

-----

There are many testing steps, but basically the goal of this PR is to make sure the content shown in the Reader matches the app selected language. If you see any `Reader` content using the wrong language, please let me know.

## To Test:

1. Install Jetpack and sign in.
2. Open "Me" screen.
3. Tap "App Settings".
4. Change the "Interface Language" to a language **different than English and different than the one selected in `wordpress.com`** (it seems like when the language query parameter is missing, the response defaults to the language selected in web. Thanks @thomashorta  for the tip).
5. Open "Reader".
6. Make sure you're following at least one tag and open "Discover" feed.
7. 🔍 Verify the posts list: they should be posts matching the selected language in step 4.
8. Open the search screen (tap on 🔍).
9. 🔍 Search for something: the results should match the selected language in step 4 even if you search use words from a different language (e.g. searching for the word "football" while having Portuguese selected). **Ideally this step should be tested using a version before and after Android O (see [here](https://github.com/wordpress-mobile/WordPress-Android/blob/ac56c495d4f5cecf2a50f349576f23515679d8b1/WordPress/src/main/java/org/wordpress/android/ui/reader/services/search/ReaderSearchServiceStarter.java#L26))**.
10. Open "My Site" tab with a blog selected.
11. Look for the daily prompts card and tap "View all responses".
12. 🔍 The posts should match the selected language in step 4. **Ideally this step should be tested using a version before and after Android O (see [here](https://github.com/wordpress-mobile/WordPress-Android/blob/7843d4e88d4b6ded2656df769e305c9c7f326d0b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/post/ReaderPostServiceStarter.java#L42))**
13. Open "Reader" and select "Discover".
14. Scroll until you find a tags card and select any tag.
15. 🔍 The posts should match the selected language in step 4. **Ideally this step should be tested using a version before and after Android O (see [here](https://github.com/wordpress-mobile/WordPress-Android/blob/7843d4e88d4b6ded2656df769e305c9c7f326d0b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/post/ReaderPostServiceStarter.java#L42))**
16. Unfollow all tags using "Subscriptions" feed.
17. Open "Discover" feed: the empty state should be triggered and you'll see a button (the label in English is "Get Started")
<img width="343" alt="image" src="https://github.com/wordpress-mobile/WordPress-Android/assets/14964993/e00fb1ee-207f-4166-886b-2884f2b2a64b">

18. 🔍  Tap the button: the suggested tags should match the language selected in step 4.

Feel free to change to pick language at any point: things should work as expected. Just be sure that the selected language is not the same as the one in `wordpress.com`.

-----

## Regression Notes

1. Potential unintended areas of impact

    - Language of "Reader" results

20. What I did to test those areas of impact (or what existing automated tests I relied on)

    - Manual testing

21. What automated tests I added (or what prevented me from doing so)

    - Had to update legacy code, which is not testable without a significant refactor.

-----

## PR Submission Checklist:

- [x] I have completed the Regression Notes.
- [x] I have considered adding accessibility improvements for my changes.
- [x] I have considered if this change warrants user-facing release notes and have added them to `RELEASE-NOTES.txt` if necessary.

-----

## Testing Checklist (strike-out the not-applying and unnecessary ones):

- [ ] WordPress.com sites and self-hosted Jetpack sites.
- [ ] Portrait and landscape orientations.
- [ ] Light and dark modes.
- [ ] Fonts: Larger, smaller and bold text.
- [ ] High contrast.
- [ ] Talkback.
- [ ] Languages with large words or with letters/accents not frequently used in English.
- [ ] Right-to-left languages. (Even if translation isn’t complete, formatting should still respect the right-to-left layout)
- [ ] Large and small screen sizes. (Tablet and smaller phones)
- [ ] Multi-tasking: Split screen and Pop-up view. (Android 10 or higher)

[Reader][Bug] Locale not working properly on some Reader requests
### Expected behavior
An appropriate locale-related parameter should be sent on Reader requests, based on the current language of the System/App to provide relevant results and content to the user.

### Actual behavior
Several Reader screens/requests are not using the locale-related parameter expected by the backend, which causes the backend to return results using defaults (which are either English or the Interface language set in the WordPress.com account on web).

Examples:
- Requests to `/rest/v1.2/read` path (such as search and tags) send a `locale` query parameter, but the endpoints on that path seem to expect a `lang` parameter. When they return, it seems to be using the interface language from the WP.com account.
- Requests to `/wpcom/v2/read` path (such as discover) send a `locale` query parameter, but the endpoints on that path seem to expect a `_locale` parameter. When they return, it seems to be using English as default. (not 100% sure)

### Steps to reproduce the behavior
1. Install and log into Jetpack
2. Set the app language to something other than English (also, preferably not matching the interface language setting of the WP.com account)
3. Go to the Reader
4. Go to Discover Feed
5. Verify that Discover results don't match the app language
6. Tap on a suggested tag
7. Verify that the tag results don't match the app language
8. Go to Search
9. Search for anything, ideally a generic word used in several languages (ex: Android, iPhone)
10. Verify that the search results don't match the app language

##### Tested on OnePlus 5T, Android 10, JPAndroid 24.0-rc-1 (also `trunk` and `feature/reader-ia`)

diff --git a/WordPress/src/main/java/org/wordpress/android/models/ReaderPost.java b/WordPress/src/main/java/org/wordpress/android/models/ReaderPost.java
index cbe82cda4cf3..8f8f10a92f5d 100644
--- a/WordPress/src/main/java/org/wordpress/android/models/ReaderPost.java
+++ b/WordPress/src/main/java/org/wordpress/android/models/ReaderPost.java
@@ -185,9 +185,11 @@ public static ReaderPost fromJson(JSONObject json) {
         // if there's no featured image, check if featured media has been set to an image
         if (!post.hasFeaturedImage() && json.has("featured_media")) {
             JSONObject jsonMedia = json.optJSONObject("featured_media");
-            String type = JSONUtils.getString(jsonMedia, "type");
-            if (type.equals("image")) {
-                post.mFeaturedImage = JSONUtils.getString(jsonMedia, "uri");
+            if (jsonMedia != null) {
+                String type = JSONUtils.getString(jsonMedia, "type");
+                if (type.equals("image")) {
+                    post.mFeaturedImage = JSONUtils.getString(jsonMedia, "uri");
+                }
             }
         }

diff --git a/WordPress/src/main/java/org/wordpress/android/models/recommend/RecommendApiCallsProvider.kt b/WordPress/src/main/java/org/wordpress/android/models/recommend/RecommendApiCallsProvider.kt
index d59d2c1f6119..e324f64e5679 100644
--- a/WordPress/src/main/java/org/wordpress/android/models/recommend/RecommendApiCallsProvider.kt
+++ b/WordPress/src/main/java/org/wordpress/android/models/recommend/RecommendApiCallsProvider.kt
@@ -51,7 +51,7 @@ class RecommendApiCallsProvider @Inject constructor(
                 cont.resume(Failure(errorMessage))
             }

-            restClientProvider.getRestClientUtilsV2().get(
+            restClientProvider.getRestClientUtilsV2().getWithLocale(
                 endPointPath,
                 listener,
                 errorListener
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/utils/NotificationsUtils.java b/WordPress/src/main/java/org/wordpress/android/ui/notifications/utils/NotificationsUtils.java
index 5653112f7af5..5f5634bcba7b 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/utils/NotificationsUtils.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/utils/NotificationsUtils.java
@@ -87,7 +87,7 @@ public static void getPushNotificationSettings(Context context, RestRequest.List
         if (!TextUtils.isEmpty(deviceID)) {
             settingsEndpoint += "?device_id=" + deviceID;
         }
-        WordPress.getRestClientUtilsV1_1().get(settingsEndpoint, listener, errorListener);
+        WordPress.getRestClientUtilsV1_1().getWithLocale(settingsEndpoint, listener, errorListener);
     }

     public static void registerDeviceForPushNotifications(final Context ctx, String token) {
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/people/InviteLinksApiCallsProvider.kt b/WordPress/src/main/java/org/wordpress/android/ui/people/InviteLinksApiCallsProvider.kt
index 0d349d3a307f..6ea4dd44eb67 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/people/InviteLinksApiCallsProvider.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/people/InviteLinksApiCallsProvider.kt
@@ -39,7 +39,7 @@ class InviteLinksApiCallsProvider @Inject constructor(
             cont.resume(Failure(error))
         }

-        WordPress.getRestClientUtilsV1_1().get(
+        WordPress.getRestClientUtilsV1_1().getWithLocale(
             endPointPath,
             listener,
             errorListener
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/people/utils/PeopleUtils.java b/WordPress/src/main/java/org/wordpress/android/ui/people/utils/PeopleUtils.java
index 5709f56c62a4..418d586a4077 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/people/utils/PeopleUtils.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/people/utils/PeopleUtils.java
@@ -62,7 +62,7 @@ public void onErrorResponse(VolleyError volleyError) {
         params.put("order_by", "display_name");
         params.put("order", "ASC");
         String path = String.format(Locale.US, "sites/%d/users", site.getSiteId());
-        WordPress.getRestClientUtilsV1_1().get(path, params, null, listener, errorListener);
+        WordPress.getRestClientUtilsV1_1().getWithLocale(path, params, null, listener, errorListener);
     }

     public static void fetchAuthors(final SiteModel site, final int offset, final FetchUsersCallback callback) {
@@ -95,7 +95,7 @@ public static void fetchAuthors(final SiteModel site, final int offset, final Fe
         params.put("order", "ASC");
         params.put("authors_only", "true");
         String path = String.format(Locale.US, "sites/%d/users", site.getSiteId());
-        WordPress.getRestClientUtilsV1_1().get(path, params, null, listener, errorListener);
+        WordPress.getRestClientUtilsV1_1().getWithLocale(path, params, null, listener, errorListener);
     }

     public static void fetchRevisionAuthorsDetails(final SiteModel site, List<String> authors,
@@ -144,7 +144,7 @@ public void onErrorResponse(VolleyError volleyError) {
                             site.getSiteId(), authors.get(i)));
         }

-        WordPress.getRestClientUtilsV1_1().get("batch/", batchParams, null, listener, errorListener);
+        WordPress.getRestClientUtilsV1_1().getWithLocale("batch/", batchParams, null, listener, errorListener);
     }

     public static void fetchFollowers(final SiteModel site, final int page, final FetchFollowersCallback callback) {
@@ -195,7 +195,7 @@ public void onErrorResponse(VolleyError volleyError) {
         params.put("page", Integer.toString(page));
         params.put("type", isEmailFollower ? "email" : "wp_com");
         String path = String.format(Locale.US, "sites/%d/stats/followers", site.getSiteId());
-        WordPress.getRestClientUtilsV1_1().get(path, params, null, listener, errorListener);
+        WordPress.getRestClientUtilsV1_1().getWithLocale(path, params, null, listener, errorListener);
     }

     public static void fetchViewers(final SiteModel site, final int offset, final FetchViewersCallback callback) {
@@ -233,7 +233,7 @@ public void onErrorResponse(VolleyError volleyError) {
         params.put("number", Integer.toString(FETCH_LIMIT));
         params.put("page", Integer.toString(page));
         String path = String.format(Locale.US, "sites/%d/viewers", site.getSiteId());
-        WordPress.getRestClientUtilsV1_1().get(path, params, null, listener, errorListener);
+        WordPress.getRestClientUtilsV1_1().getWithLocale(path, params, null, listener, errorListener);
     }

     public static void updateRole(final SiteModel site, long personID, String newRole, final int localTableBlogId,
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/publicize/PublicizeActions.java b/WordPress/src/main/java/org/wordpress/android/ui/publicize/PublicizeActions.java
index 8fad36d11e09..307e118fbd78 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/publicize/PublicizeActions.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/publicize/PublicizeActions.java
@@ -153,7 +153,7 @@ public void onErrorResponse(VolleyError volleyError) {
         };

         String path = "/me/keyring-connections";
-        WordPress.getRestClientUtilsV1_1().get(path, listener, errorListener);
+        WordPress.getRestClientUtilsV1_1().getWithLocale(path, listener, errorListener);
     }

     /*
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/publicize/services/PublicizeUpdateServicesV2.kt b/WordPress/src/main/java/org/wordpress/android/ui/publicize/services/PublicizeUpdateServicesV2.kt
index ed549f49e226..ce089cdb7ca7 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/publicize/services/PublicizeUpdateServicesV2.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/publicize/services/PublicizeUpdateServicesV2.kt
@@ -27,7 +27,7 @@ class PublicizeUpdateServicesV2 @Inject constructor(
         }
         val errorListener = RestRequest.ErrorListener { volleyError -> failure(volleyError) }
         val path = "sites/$siteId/external-services?type=publicize"
-        restClientProvider.getRestClientUtilsV2().get(path, listener, errorListener)
+        restClientProvider.getRestClientUtilsV2().getWithLocale(path, listener, errorListener)
     }

     /*
@@ -45,6 +45,6 @@ class PublicizeUpdateServicesV2 @Inject constructor(
         }
         val errorListener = RestRequest.ErrorListener { volleyError -> failure(volleyError) }
         val path = String.format(Locale.ROOT, "sites/%d/publicize-connections", siteId)
-        restClientProvider.getRestClientUtilsV1_1().get(path, listener, errorListener)
+        restClientProvider.getRestClientUtilsV1_1().getWithLocale(path, listener, errorListener)
     }
 }
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/actions/ReaderBlogActions.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/actions/ReaderBlogActions.java
index 2451b933cb18..21f2a01c547f 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/actions/ReaderBlogActions.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/actions/ReaderBlogActions.java
@@ -398,10 +398,11 @@ public void onErrorResponse(VolleyError volleyError) {
         };

         if (hasBlogId) {
-            WordPress.getRestClientUtilsV1_1().get("read/sites/" + blogId, listener, errorListener);
+            WordPress.getRestClientUtilsV1_1().getWithLocale("read/sites/" + blogId, listener, errorListener);
         } else {
             WordPress.getRestClientUtilsV1_1()
-                     .get("read/sites/" + UrlUtils.urlEncode(UrlUtils.getHost(blogUrl)), listener, errorListener);
+                     .getWithLocale("read/sites/" + UrlUtils.urlEncode(UrlUtils.getHost(blogUrl)), listener,
+                             errorListener);
         }
     }

@@ -438,7 +439,7 @@ public void onErrorResponse(VolleyError volleyError) {
         } else {
             path = "read/feed/" + UrlUtils.urlEncode(feedUrl);
         }
-        WordPress.getRestClientUtilsV1_1().get(path, listener, errorListener);
+        WordPress.getRestClientUtilsV1_1().getWithLocale(path, listener, errorListener);
     }

     private static void handleUpdateBlogInfoResponse(JSONObject jsonObject, UpdateBlogInfoListener infoListener) {
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/actions/ReaderPostActions.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/actions/ReaderPostActions.java
index ee49b07f0a92..7b6e9527bba4 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/actions/ReaderPostActions.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/actions/ReaderPostActions.java
@@ -159,7 +159,7 @@ public void onErrorResponse(VolleyError volleyError) {
             }
         };
         AppLog.d(T.READER, "updating post");
-        WordPress.getRestClientUtilsV1_2().get(path, null, null, listener, errorListener);
+        WordPress.getRestClientUtilsV1_2().getWithLocale(path, null, null, listener, errorListener);
     }

     private static void handleUpdatePostResponse(@NonNull final ReaderPost localPost,
@@ -320,7 +320,7 @@ public void onErrorResponse(VolleyError volleyError) {
         };

         AppLog.d(T.READER, "requesting post");
-        restClientUtils.get(path, null, null, listener, errorListener);
+        restClientUtils.getWithLocale(path, null, null, listener, errorListener);
     }

     private static String getTrackingPixelForPost(@NonNull ReaderPost post) {
@@ -417,7 +417,7 @@ public void onErrorResponse(VolleyError volleyError) {
                       + "?size_local=" + NUM_RELATED_POSTS_TO_REQUEST
                       + "&size_global=" + NUM_RELATED_POSTS_TO_REQUEST
                       + "&fields=" + ReaderSimplePost.SIMPLE_POST_FIELDS;
-        WordPress.getRestClientUtilsV1_2().get(path, null, null, listener, errorListener);
+        WordPress.getRestClientUtilsV1_2().getWithLocale(path, null, null, listener, errorListener);
     }

     private static void handleRelatedPostsResponse(final ReaderPost sourcePost,
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/repository/usecases/ParseDiscoverCardsJsonUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/reader/repository/usecases/ParseDiscoverCardsJsonUseCase.kt
index 9197e5e635c2..1e5806799c47 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/repository/usecases/ParseDiscoverCardsJsonUseCase.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/repository/usecases/ParseDiscoverCardsJsonUseCase.kt
@@ -78,7 +78,7 @@ class ParseDiscoverCardsJsonUseCase @Inject constructor(
     }

     fun parseNextPageHandle(jsonObject: JSONObject): String =
-        jsonObject.getString(ReaderConstants.JSON_NEXT_PAGE_HANDLE)
+        jsonObject.optString(ReaderConstants.JSON_NEXT_PAGE_HANDLE)

     fun convertListOfJsonArraysIntoSingleJsonArray(jsons: List<String>): JSONArray {
         val arrays = jsons.map { JSONArray(it) }
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/comment/ReaderCommentService.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/comment/ReaderCommentService.java
index 56487cc3f0bc..748577aafb40 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/comment/ReaderCommentService.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/comment/ReaderCommentService.java
@@ -201,7 +201,7 @@ public void onErrorResponse(VolleyError volleyError) {
             }
         };
         AppLog.d(AppLog.T.READER, "updating comments");
-        WordPress.getRestClientUtilsV1_1().get(path, null, null, listener, errorListener);
+        WordPress.getRestClientUtilsV1_1().getWithLocale(path, null, null, listener, errorListener);
     }

     private static void handleUpdateCommentsResponse(final JSONObject jsonObject,
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/discover/ReaderDiscoverLogic.kt b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/discover/ReaderDiscoverLogic.kt
index 8e384e3626a5..8678093db272 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/discover/ReaderDiscoverLogic.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/discover/ReaderDiscoverLogic.kt
@@ -46,6 +46,7 @@ import org.wordpress.android.ui.reader.services.discover.ReaderDiscoverLogic.Dis
 import org.wordpress.android.ui.reader.services.discover.ReaderDiscoverLogic.DiscoverTasks.REQUEST_MORE
 import org.wordpress.android.util.AppLog
 import org.wordpress.android.util.AppLog.T.READER
+import org.wordpress.android.util.LocaleManagerWrapper
 import org.wordpress.android.util.config.ReaderDiscoverNewEndpointFeatureConfig
 import javax.inject.Inject

@@ -59,6 +60,7 @@ class ReaderDiscoverLogic @Inject constructor(
     private val getDiscoverCardsUseCase: GetDiscoverCardsUseCase,
     private val appPrefsWrapper: AppPrefsWrapper,
     private val readerDiscoverNewEndpointFeatureConfig: ReaderDiscoverNewEndpointFeatureConfig,
+    private val localeManagerWrapper: LocaleManagerWrapper,
 ) {
     enum class DiscoverTasks {
         REQUEST_MORE, REQUEST_FIRST_PAGE
@@ -118,12 +120,13 @@ class ReaderDiscoverLogic @Inject constructor(
                 AppLog.e(READER, volleyError)
                 resultListener.onUpdateResult(FAILED)
             }
+            params["_locale"] = localeManagerWrapper.getLanguage()
             val endpoint = if (readerDiscoverNewEndpointFeatureConfig.isEnabled()) {
                 "read/streams/discover"
             } else {
                 "read/tags/cards"
             }
-            WordPress.getRestClientUtilsV2()[endpoint, params, null, listener, errorListener]
+            WordPress.getRestClientUtilsV2().get(endpoint, params, null, listener, errorListener)
         }
     }

@@ -150,7 +153,9 @@ class ReaderDiscoverLogic @Inject constructor(
             insertCardsJsonIntoDb(simplifiedCardsJson)

             val nextPageHandle = parseDiscoverCardsJsonUseCase.parseNextPageHandle(json)
-            appPrefsWrapper.readerCardsPageHandle = nextPageHandle
+            if (nextPageHandle.isNotEmpty()) {
+                appPrefsWrapper.readerCardsPageHandle = nextPageHandle
+            }

             if (cards.isEmpty()) {
                 readerTagTableWrapper.clearTagLastUpdated(ReaderTag.createDiscoverPostCardsTag())
@@ -238,7 +243,11 @@ class ReaderDiscoverLogic @Inject constructor(
         // If we've received a recommended tags or blogs card as the first element,
         // it should be displayed as the third card.
         if (firstRecommendationCard != null) {
-            simplifiedJsonList.add(2, firstRecommendationCard)
+            if (simplifiedJsonList.size >=2) {
+                simplifiedJsonList.add(2, firstRecommendationCard)
+            } else {
+                simplifiedJsonList.add(firstRecommendationCard)
+            }
         }

         return JSONArray(simplifiedJsonList)
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/post/ReaderPostJobService.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/post/ReaderPostJobService.java
index 3eb45bc78936..70a2ed80efb8 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/post/ReaderPostJobService.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/post/ReaderPostJobService.java
@@ -10,6 +10,11 @@
 import org.wordpress.android.ui.reader.ReaderEvents;
 import org.wordpress.android.ui.reader.services.ServiceCompletionListener;
 import org.wordpress.android.util.AppLog;
+import org.wordpress.android.util.LocaleManagerWrapper;
+
+import javax.inject.Inject;
+
+import dagger.hilt.android.AndroidEntryPoint;

 import static org.wordpress.android.ui.reader.services.post.ReaderPostServiceStarter.ARG_ACTION;
 import static org.wordpress.android.ui.reader.services.post.ReaderPostServiceStarter.ARG_BLOG_ID;
@@ -26,9 +31,12 @@
  * EventBus to alert of update status
  */

+@AndroidEntryPoint
 public class ReaderPostJobService extends JobService implements ServiceCompletionListener {
     private ReaderPostLogic mReaderPostLogic;

+    @Inject LocaleManagerWrapper mLocaleManagerWrapper;
+
     @Override public boolean onStartJob(JobParameters params) {
         AppLog.i(AppLog.T.READER, "reader post job service > started");
         UpdateAction action;
@@ -66,7 +74,7 @@ public class ReaderPostJobService extends JobService implements ServiceCompletio
     @Override
     public void onCreate() {
         super.onCreate();
-        mReaderPostLogic = new ReaderPostLogic(this);
+        mReaderPostLogic = new ReaderPostLogic(this, mLocaleManagerWrapper);
         AppLog.i(AppLog.T.READER, "reader post job service > created");
     }

diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/post/ReaderPostLogic.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/post/ReaderPostLogic.java
index ca1619ae3788..212d32230fab 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/post/ReaderPostLogic.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/post/ReaderPostLogic.java
@@ -25,15 +25,19 @@
 import org.wordpress.android.ui.reader.services.post.ReaderPostServiceStarter.UpdateAction;
 import org.wordpress.android.ui.reader.utils.ReaderUtils;
 import org.wordpress.android.util.AppLog;
+import org.wordpress.android.util.LocaleManagerWrapper;
 import org.wordpress.android.util.StringUtils;
 import org.wordpress.android.util.UrlUtils;

 public class ReaderPostLogic {
     private ServiceCompletionListener mCompletionListener;
+    private final LocaleManagerWrapper mLocaleManagerWrapper;
     private Object mListenerCompanion;

-    public ReaderPostLogic(ServiceCompletionListener listener) {
+    public ReaderPostLogic(@NonNull final ServiceCompletionListener listener,
+                           @NonNull final LocaleManagerWrapper localeManagerWrapper) {
         mCompletionListener = listener;
+        mLocaleManagerWrapper = localeManagerWrapper;
     }

     public void performTask(Object companion, UpdateAction action,
@@ -87,7 +91,7 @@ public void onUpdateResult(ReaderActions.UpdateResult result) {
         requestPostsForFeed(feedId, action, listener);
     }

-    private static void requestPostsWithTag(final ReaderTag tag,
+    private void requestPostsWithTag(final ReaderTag tag,
                                             final UpdateAction updateAction,
                                             final ReaderActions.UpdateResultListener resultListener) {
         String path = getRelativeEndpointForTag(tag);
@@ -126,22 +130,18 @@ private static void requestPostsWithTag(final ReaderTag tag,

         sb.append("&meta=site,likes");

-        com.wordpress.rest.RestRequest.Listener listener = new RestRequest.Listener() {
-            @Override
-            public void onResponse(JSONObject jsonObject) {
-                // remember when this tag was updated if newer posts were requested
-                if (updateAction == UpdateAction.REQUEST_NEWER || updateAction == UpdateAction.REQUEST_REFRESH) {
-                    ReaderTagTable.setTagLastUpdated(tag);
-                }
-                handleUpdatePostsResponse(tag, jsonObject, updateAction, resultListener);
+        sb.append("&lang=").append(mLocaleManagerWrapper.getLanguage());
+
+        com.wordpress.rest.RestRequest.Listener listener = jsonObject -> {
+            // remember when this tag was updated if newer posts were requested
+            if (updateAction == UpdateAction.REQUEST_NEWER || updateAction == UpdateAction.REQUEST_REFRESH) {
+                ReaderTagTable.setTagLastUpdated(tag);
             }
+            handleUpdatePostsResponse(tag, jsonObject, updateAction, resultListener);
         };
-        RestRequest.ErrorListener errorListener = new RestRequest.ErrorListener() {
-            @Override
-            public void onErrorResponse(VolleyError volleyError) {
-                AppLog.e(AppLog.T.READER, volleyError);
-                resultListener.onUpdateResult(ReaderActions.UpdateResult.FAILED);
-            }
+        RestRequest.ErrorListener errorListener = volleyError -> {
+            AppLog.e(AppLog.T.READER, volleyError);
+            resultListener.onUpdateResult(ReaderActions.UpdateResult.FAILED);
         };

         WordPress.getRestClientUtilsV1_2().get(sb.toString(), null, null, listener, errorListener);
@@ -174,7 +174,7 @@ public void onErrorResponse(VolleyError volleyError) {
             }
         };
         AppLog.d(AppLog.T.READER, "updating posts in blog " + blogId);
-        WordPress.getRestClientUtilsV1_2().get(path, null, null, listener, errorListener);
+        WordPress.getRestClientUtilsV1_2().getWithLocale(path, null, null, listener, errorListener);
     }

     private static void requestPostsForFeed(final long feedId,
@@ -203,7 +203,7 @@ public void onErrorResponse(VolleyError volleyError) {
         };

         AppLog.d(AppLog.T.READER, "updating posts in feed " + feedId);
-        WordPress.getRestClientUtilsV1_2().get(path, null, null, listener, errorListener);
+        WordPress.getRestClientUtilsV1_2().getWithLocale(path, null, null, listener, errorListener);
     }

     /*
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/post/ReaderPostService.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/post/ReaderPostService.java
index f00bcf4f3445..5753d9ce4cb7 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/post/ReaderPostService.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/post/ReaderPostService.java
@@ -9,6 +9,11 @@
 import org.wordpress.android.ui.reader.ReaderEvents;
 import org.wordpress.android.ui.reader.services.ServiceCompletionListener;
 import org.wordpress.android.util.AppLog;
+import org.wordpress.android.util.LocaleManagerWrapper;
+
+import javax.inject.Inject;
+
+import dagger.hilt.android.AndroidEntryPoint;

 import static org.wordpress.android.ui.reader.services.post.ReaderPostServiceStarter.ARG_ACTION;
 import static org.wordpress.android.ui.reader.services.post.ReaderPostServiceStarter.ARG_BLOG_ID;
@@ -21,9 +26,12 @@
  * EventBus to alert of update status
  */

+@AndroidEntryPoint
 public class ReaderPostService extends Service implements ServiceCompletionListener {
     private ReaderPostLogic mReaderPostLogic;

+    @Inject LocaleManagerWrapper mLocaleManagerWrapper;
+
     @Override
     public IBinder onBind(Intent intent) {
         return null;
@@ -32,7 +40,7 @@ public IBinder onBind(Intent intent) {
     @Override
     public void onCreate() {
         super.onCreate();
-        mReaderPostLogic = new ReaderPostLogic(this);
+        mReaderPostLogic = new ReaderPostLogic(this, mLocaleManagerWrapper);
         AppLog.i(AppLog.T.READER, "reader post service > created");
     }

diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/search/ReaderSearchJobService.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/search/ReaderSearchJobService.java
index e4f9db580dbb..f5fd3f46892d 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/search/ReaderSearchJobService.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/search/ReaderSearchJobService.java
@@ -5,6 +5,11 @@

 import org.wordpress.android.ui.reader.services.ServiceCompletionListener;
 import org.wordpress.android.util.AppLog;
+import org.wordpress.android.util.LocaleManagerWrapper;
+
+import javax.inject.Inject;
+
+import dagger.hilt.android.AndroidEntryPoint;

 import static org.wordpress.android.ui.reader.services.search.ReaderSearchServiceStarter.ARG_OFFSET;
 import static org.wordpress.android.ui.reader.services.search.ReaderSearchServiceStarter.ARG_QUERY;
@@ -13,9 +18,12 @@
  * service which searches for reader posts on wordpress.com
  */

+@AndroidEntryPoint
 public class ReaderSearchJobService extends JobService implements ServiceCompletionListener {
     private ReaderSearchLogic mReaderSearchLogic;

+    @Inject LocaleManagerWrapper mLocaleManagerWrapper;
+
     @Override public boolean onStartJob(JobParameters params) {
         if (params.getExtras() != null && params.getExtras().getString(ARG_QUERY) != null) {
             String query = params.getExtras().getString(ARG_QUERY);
@@ -34,7 +42,7 @@ public class ReaderSearchJobService extends JobService implements ServiceComplet
     @Override
     public void onCreate() {
         super.onCreate();
-        mReaderSearchLogic = new ReaderSearchLogic(this);
+        mReaderSearchLogic = new ReaderSearchLogic(this, mLocaleManagerWrapper);
         AppLog.i(AppLog.T.READER, "reader search job service > created");
     }

diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/search/ReaderSearchLogic.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/search/ReaderSearchLogic.java
index 0002fe3febce..b865a00e28ec 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/search/ReaderSearchLogic.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/search/ReaderSearchLogic.java
@@ -14,16 +14,21 @@
 import org.wordpress.android.ui.reader.ReaderEvents;
 import org.wordpress.android.ui.reader.services.ServiceCompletionListener;
 import org.wordpress.android.util.AppLog;
+import org.wordpress.android.util.LocaleManagerWrapper;
 import org.wordpress.android.util.UrlUtils;

 import static org.wordpress.android.ui.reader.utils.ReaderUtils.getTagForSearchQuery;

 public class ReaderSearchLogic {
-    private ServiceCompletionListener mCompletionListener;
+    private final ServiceCompletionListener mCompletionListener;
+
+    private final LocaleManagerWrapper mLocaleManagerWrapper;
     private Object mListenerCompanion;

-    public ReaderSearchLogic(ServiceCompletionListener listener) {
+    public ReaderSearchLogic(@NonNull final ServiceCompletionListener listener,
+                             final @NonNull LocaleManagerWrapper localeManagerWrapper) {
         mCompletionListener = listener;
+        mLocaleManagerWrapper = localeManagerWrapper;
     }

     public void startSearch(@NonNull final String query, final int offset, Object companion) {
@@ -32,16 +37,14 @@ public void startSearch(@NonNull final String query, final int offset, Object co
                       + UrlUtils.urlEncode(query)
                       + "&number=" + ReaderConstants.READER_MAX_SEARCH_RESULTS_TO_REQUEST
                       + "&offset=" + offset
-                      + "&meta=site,likes";
+                      + "&meta=site,likes"
+                      + "&lang=" + mLocaleManagerWrapper.getLanguage();

-        RestRequest.Listener listener = new RestRequest.Listener() {
-            @Override
-            public void onResponse(JSONObject jsonObject) {
-                if (jsonObject != null) {
-                    handleSearchResponse(query, offset, jsonObject);
-                } else {
-                    EventBus.getDefault().post(new ReaderEvents.SearchPostsEnded(query, offset, false));
-                }
+        RestRequest.Listener listener = jsonObject -> {
+            if (jsonObject != null) {
+                handleSearchResponse(query, offset, jsonObject);
+            } else {
+                EventBus.getDefault().post(new ReaderEvents.SearchPostsEnded(query, offset, false));
             }
         };
         RestRequest.ErrorListener errorListener = new RestRequest.ErrorListener() {
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/search/ReaderSearchService.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/search/ReaderSearchService.java
index ddbf822e4100..200bd615e43c 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/search/ReaderSearchService.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/search/ReaderSearchService.java
@@ -6,18 +6,26 @@

 import org.wordpress.android.ui.reader.services.ServiceCompletionListener;
 import org.wordpress.android.util.AppLog;
+import org.wordpress.android.util.LocaleManagerWrapper;
 import org.wordpress.android.util.StringUtils;

+import javax.inject.Inject;
+
+import dagger.hilt.android.AndroidEntryPoint;
+
 /**
  * service which searches for reader posts on wordpress.com
  */

+@AndroidEntryPoint
 public class ReaderSearchService extends Service implements ServiceCompletionListener {
     private static final String ARG_QUERY = "query";
     private static final String ARG_OFFSET = "offset";

     private ReaderSearchLogic mReaderSearchLogic;

+    @Inject LocaleManagerWrapper mLocaleManagerWrapper;
+
     @Override
     public IBinder onBind(Intent intent) {
         return null;
@@ -26,7 +34,7 @@ public IBinder onBind(Intent intent) {
     @Override
     public void onCreate() {
         super.onCreate();
-        mReaderSearchLogic = new ReaderSearchLogic(this);
+        mReaderSearchLogic = new ReaderSearchLogic(this, mLocaleManagerWrapper);
         AppLog.i(AppLog.T.READER, "reader search service > created");
     }

diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/update/ReaderUpdateLogic.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/update/ReaderUpdateLogic.java
index 151bdebd5101..9af7b113c7db 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/update/ReaderUpdateLogic.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/update/ReaderUpdateLogic.java
@@ -120,7 +120,7 @@ public void onErrorResponse(VolleyError volleyError) {
         HashMap<String, String> params = new HashMap<>();
         params.put("locale", mLanguage);
         mClientUtilsProvider.getRestClientForTagUpdate()
-                            .get("read/menu", params, null, listener, errorListener);
+                            .getWithLocale("read/menu", params, null, listener, errorListener);
     }

     /**
@@ -316,7 +316,8 @@ public void onErrorResponse(VolleyError volleyError) {
         AppLog.d(AppLog.T.READER, "reader service > updating followed blogs. Page requested: " + page);
         // request using ?meta=site,feed to get extra info
         WordPress.getRestClientUtilsV1_2()
-                 .get("read/following/mine?number=100&page=" + page + "&meta=site%2Cfeed", listener, errorListener);
+                 .getWithLocale("read/following/mine?number=100&page=" + page + "&meta=site%2Cfeed", listener,
+                         errorListener);
     }

     private void handleFollowedBlogsResponse(final ReaderBlogList serverBlogs, final JSONObject jsonObject) {
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/utils/PostSubscribersApiCallsProvider.kt b/WordPress/src/main/java/org/wordpress/android/ui/reader/utils/PostSubscribersApiCallsProvider.kt
index 40f2d8aefe89..a0887da7d86b 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/utils/PostSubscribersApiCallsProvider.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/utils/PostSubscribersApiCallsProvider.kt
@@ -38,7 +38,7 @@ class PostSubscribersApiCallsProvider @Inject constructor(
             cont.resume(false)
         }

-        WordPress.getRestClientUtilsV1_1().get(
+        WordPress.getRestClientUtilsV1_1().getWithLocale(
             endPointPath,
             listener,
             errorListener
@@ -64,7 +64,7 @@ class PostSubscribersApiCallsProvider @Inject constructor(
             cont.resume(Failure(error))
         }

-        WordPress.getRestClientUtilsV1_1().get(
+        WordPress.getRestClientUtilsV1_1().getWithLocale(
             endPointPath,
             listener,
             errorListener
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/suggestion/service/SuggestionService.java b/WordPress/src/main/java/org/wordpress/android/ui/suggestion/service/SuggestionService.java
index 7853bb5dba14..0c215405c511 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/suggestion/service/SuggestionService.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/suggestion/service/SuggestionService.java
@@ -91,7 +91,7 @@ private void updateSuggestions(final long siteId) {

         AppLog.d(AppLog.T.SUGGESTION, "suggestion service > updating suggestions for siteId: " + siteId);
         String path = "/users/suggest" + "?site_id=" + siteId;
-        WordPress.getRestClientUtils().get(path, listener, errorListener);
+        WordPress.getRestClientUtils().getWithLocale(path, listener, errorListener);
     }

     private void handleSuggestionsUpdatedResponse(final long siteId, final JSONObject jsonObject) {
@@ -136,7 +136,7 @@ private void updateTags(final long siteId) {

         AppLog.d(AppLog.T.SUGGESTION, "suggestion service > updating tags for siteId: " + siteId);
         String path = "/sites/" + siteId + "/tags";
-        WordPress.getRestClientUtils().get(path, listener, errorListener);
+        WordPress.getRestClientUtils().getWithLocale(path, listener, errorListener);
     }

     private void handleTagsUpdatedResponse(final long siteId, final JSONObject jsonObject) {
diff --git a/libs/networking/src/main/java/org/wordpress/android/networking/RestClientUtils.java b/libs/networking/src/main/java/org/wordpress/android/networking/RestClientUtils.java
index 2e62fea38810..148a2674e399 100644
--- a/libs/networking/src/main/java/org/wordpress/android/networking/RestClientUtils.java
+++ b/libs/networking/src/main/java/org/wordpress/android/networking/RestClientUtils.java
@@ -103,7 +103,7 @@ public RestClient getRestClient() {

     public void getCategories(long siteId, Listener listener, ErrorListener errorListener) {
         String path = String.format(Locale.US, "sites/%d/categories", siteId);
-        get(path, null, null, listener, errorListener);
+        getWithLocale(path, null, null, listener, errorListener);
     }

     /**
@@ -112,7 +112,7 @@ public void getCategories(long siteId, Listener listener, ErrorListener errorLis
      * <a href="https://developer.wordpress.com/docs/api/1/get/notifications/">api/1/get/notifications</a>
      */
     public void getNotifications(Map<String, String> params, Listener listener, ErrorListener errorListener) {
-        get("notifications", params, null, listener, errorListener);
+        getWithLocale("notifications", params, null, listener, errorListener);
     }

     /**
@@ -123,7 +123,7 @@ public void getNotification(Map<String, String> params, String noteId,
                                 Listener listener, ErrorListener errorListener) {
         params.put("fields", NOTIFICATION_FIELDS);
         String path = String.format(Locale.US, "notifications/%s/", noteId);
-        get(path, params, null, listener, errorListener);
+        getWithLocale(path, params, null, listener, errorListener);
     }

     /**
@@ -135,7 +135,7 @@ public void getNotification(String noteId, Listener listener, ErrorListener erro
         HashMap<String, String> params = new HashMap<>();
         params.put("fields", NOTIFICATION_FIELDS);
         String path = String.format("notifications/%s", noteId);
-        get(path, params, null, listener, errorListener);
+        getWithLocale(path, params, null, listener, errorListener);
     }

     /**
@@ -166,23 +166,23 @@ public void decrementUnreadCount(String noteId, String decrementAmount,

     public void getJetpackSettings(long siteId, Listener listener, ErrorListener errorListener) {
         String path = String.format(Locale.US, "jetpack-blogs/%d/rest-api/?path=/jetpack/v4/settings", siteId);
-        get(path, listener, errorListener);
+        getWithLocale(path, listener, errorListener);
     }

     public void getGeneralSettings(long siteId, Listener listener, ErrorListener errorListener) {
         String path = String.format(Locale.US, "sites/%d/settings", siteId);
-        get(path, listener, errorListener);
+        getWithLocale(path, listener, errorListener);
     }

     public void getJetpackMonitorSettings(long siteId, Listener listener, ErrorListener errorListener) {
         String path = String.format(Locale.US, "jetpack-blogs/%d", siteId);
-        get(path, listener, errorListener);
+        getWithLocale(path, listener, errorListener);
     }

     public void getJetpackModuleSettings(long siteId, Listener listener, ErrorListener errorListener) {
         String path = String.format(Locale.US, "sites/%d/jetpack/modules", siteId);
-        get(path, listener, errorListener);
+        getWithLocale(path, listener, errorListener);
     }

     public void setGeneralSiteSettings(long siteId, JSONObject params, Listener listener, ErrorListener errorListener) {
@@ -228,7 +228,7 @@ public void setJetpackModuleSettings(long siteId, String module, boolean active,

     public void getSitePurchases(long siteId, Listener listener, ErrorListener errorListener) {
         String path = String.format(Locale.US, "sites/%d/purchases", siteId);
-        get(path, listener, errorListener);
+        getWithLocale(path, listener, errorListener);
     }

     public void exportContentAll(long siteId, Listener listener, ErrorListener errorListener) {
@@ -238,7 +238,7 @@ public void exportContentAll(long siteId, Listener listener, ErrorListener error

     public void getSharingButtons(String siteId, Listener listener, ErrorListener errorListener) {
         String path = String.format("sites/%s/sharing-buttons", siteId);
-        get(path, listener, errorListener);
+        getWithLocale(path, listener, errorListener);
     }

     public void setSharingButtons(String siteId, JSONObject params, Listener listener, ErrorListener errorListener) {
@@ -249,29 +249,44 @@ public void setSharingButtons(String siteId, JSONObject params, Listener listene
     /**
      * Make GET request
      */
-    public Request<JSONObject> get(String path, Listener listener, ErrorListener errorListener) {
-        return get(path, null, null, listener, errorListener);
+    public Request<JSONObject> getWithLocale(final String path, final Listener listener,
+                                             final ErrorListener errorListener) {
+        return getWithLocale(path, null, null, listener, errorListener);
     }

     /**
-     * Make GET request with params
+     * Make GET request with params. Locale param is added automatically.
      */
-    public Request<JSONObject> get(String path, Map<String, String> params, RetryPolicy retryPolicy, Listener listener,
-                    ErrorListener errorListener) {
+    public Request<JSONObject> getWithLocale(final String path, final Map<String, String> params,
+                                             RetryPolicy retryPolicy, final Listener listener,
+                                             final ErrorListener errorListener) {
         // turn params into query string
         HashMap<String, String> paramsWithLocale = getRestLocaleParams(mContext);
         if (params != null) {
             paramsWithLocale.putAll(params);
         }
+        return get(path, paramsWithLocale, retryPolicy, listener, errorListener);
+    }

+    /**
+     * Make GET request with params.
+     */
+    public Request<JSONObject> get(final String path, final Map<String, String> params, RetryPolicy retryPolicy,
+                                   final Listener listener, final ErrorListener errorListener) {
+        final Map<String, String> paramsWithPath = new HashMap<>();
+        if (params != null) {
+            paramsWithPath.putAll(params);
+        }
         String realPath = getSanitizedPath(path);
         if (TextUtils.isEmpty(realPath)) {
             realPath = path;
         }
-        paramsWithLocale.putAll(getSanitizedParameters(path));
+        if (path != null) {
+            paramsWithPath.putAll(getSanitizedParameters(path));
+        }

         RestRequest request = mRestClient.makeRequest(Method.GET, mRestClient
-                        .getAbsoluteURL(realPath, paramsWithLocale), null, listener, errorListener);
+                .getAbsoluteURL(realPath, paramsWithPath), null, listener, errorListener);

         if (retryPolicy == null) {
             retryPolicy = new DefaultRetryPolicy(REST_TIMEOUT_MS, REST_MAX_RETRIES_GET, REST_BACKOFF_MULT);

diff --git a/WordPress/src/test/java/org/wordpress/android/models/recommend/RecommendApiCallsProviderTest.kt b/WordPress/src/test/java/org/wordpress/android/models/recommend/RecommendApiCallsProviderTest.kt
index 9132c83d0598..ddf347895251 100644
--- a/WordPress/src/test/java/org/wordpress/android/models/recommend/RecommendApiCallsProviderTest.kt
+++ b/WordPress/src/test/java/org/wordpress/android/models/recommend/RecommendApiCallsProviderTest.kt
@@ -109,7 +109,13 @@ class RecommendApiCallsProviderTest : BaseUnitTest() {
         val error = "Invalid response received"
         whenever(jsonObject.toString()).thenReturn("{name:\"wordpress\",message=[]}")
         whenever(context.getString(R.string.recommend_app_bad_format_response)).thenReturn(error)
-        whenever(restClientUtils.get(anyString(), listenerCaptor.capture(), errorListenerCaptor.capture())).doAnswer {
+        whenever(
+            restClientUtils.getWithLocale(
+                anyString(),
+                listenerCaptor.capture(),
+                errorListenerCaptor.capture()
+            )
+        ).doAnswer {
             listenerCaptor.lastValue.onResponse(jsonObject)
             null
         }
@@ -127,7 +133,13 @@ class RecommendApiCallsProviderTest : BaseUnitTest() {
         val error = "Invalid response received"
         whenever(jsonObject.optString("name")).thenReturn("jetpack")
         whenever(context.getString(R.string.recommend_app_bad_format_response)).thenReturn(error)
-        whenever(restClientUtils.get(anyString(), listenerCaptor.capture(), errorListenerCaptor.capture())).doAnswer {
+        whenever(
+            restClientUtils.getWithLocale(
+                anyString(),
+                listenerCaptor.capture(),
+                errorListenerCaptor.capture()
+            )
+        ).doAnswer {
             listenerCaptor.lastValue.onResponse(jsonObject)
             null
         }
@@ -144,7 +156,13 @@ class RecommendApiCallsProviderTest : BaseUnitTest() {
     fun `error is tracked on null net response`() = test {
         val error = "No response received"
         whenever(context.getString(R.string.recommend_app_null_response)).thenReturn(error)
-        whenever(restClientUtils.get(anyString(), listenerCaptor.capture(), errorListenerCaptor.capture())).doAnswer {
+        whenever(
+            restClientUtils.getWithLocale(
+                anyString(),
+                listenerCaptor.capture(),
+                errorListenerCaptor.capture()
+            )
+        ).doAnswer {
             listenerCaptor.lastValue.onResponse(null)
             null
         }
@@ -161,7 +179,13 @@ class RecommendApiCallsProviderTest : BaseUnitTest() {
     fun `error is tracked on volley error`() = test {
         val error = "Unknown error fetching recommend app template"
         whenever(context.getString(R.string.recommend_app_generic_get_template_error)).thenReturn(error)
-        whenever(restClientUtils.get(anyString(), listenerCaptor.capture(), errorListenerCaptor.capture())).doAnswer {
+        whenever(
+            restClientUtils.getWithLocale(
+                anyString(),
+                listenerCaptor.capture(),
+                errorListenerCaptor.capture()
+            )
+        ).doAnswer {
             errorListenerCaptor.lastValue.onErrorResponse(mock())
             null
         }


[Reader][0 blogs/0 tags] Fix Subscription feed infinite loading and fetch when tapping filters

Fixes #20009
Fixes #20010

The biggest change in this PR is fixing the logic inside `ReaderUpdateLogic` to avoid unnecessary DB writes which were happening always, even when the server tag list was the same as the local tag list, which in turn made the `FollowTagsChanged` stop being triggered every single time a fetch was done.

Fixing that issue made the `FollowTagsChanged` event only be triggered when we, in fact, had changes in the local tag DB, which solved the issues referenced above BUT broke a lot of other things, since much of the existing code listening to that event was relying on `FollowTagsChanged` being triggered at the end of all tag fetching tasks.

To solve that I renamed the `FollowTagsChanged` to `FollowTagsFetched` and made it trigger in the same situation as it was before (regardless of changes being made to the local DB), added a new field `didChange` to propagate the information if a change was done locally or not, and updated the logic in `ReaderPostListFragment` event listener that caused the 2 bugs above to happen, by guarding with the specific conditions in which we really want fetching to happen

-----

## To Test:

Before running each test scenarios below make sure you have 0 blogs and 0 tags followed.

### Infinite loading after subscribing to tags
1. Install and log into Jetpack on a `feature/reader-ia` branch build
2. Go to reader
3. Go to the Subscriptions feed
4. Tap "0 tags" filter pill
5. Tap "Suggested tags"
6. Choose zero or more tags and tap "Done"/back
7. **Verify** it goes back to Subscription Feed and the page has no infinite loading indicator

### Tapping the blog/tag filter pills causes the empty list to refresh
1. Install and log into Jetpack on a `feature/reader-ia` branch build
2. Go to reader
3. Go to the Subscriptions feed
4. Tap either the "0 blogs" or "0 tags" filter pill
5. **Verify** the feed does not try to refresh since there are no subscribed blogs

-----

## Regression Notes

1. Potential unintended areas of impact
    - Normal scenarios of reader (with tags/blogs subscriptions)

2. What I did to test those areas of impact (or what existing automated tests I relied on)
    - Manual testing

3. What automated tests I added (or what prevented me from doing so)
    - Updated existing unit tests

-----

## PR Submission Checklist:

- [x] I have completed the Regression Notes.
- [x] I have considered adding accessibility improvements for my changes.
- [x] I have considered if this change warrants user-facing release notes and have added them to `RELEASE-NOTES.txt` if necessary.

-----

## Testing Checklist:

- [ ] WordPress.com sites and self-hosted Jetpack sites.
- [ ] Portrait and landscape orientations.
- [ ] Light and dark modes.
- [ ] Fonts: Larger, smaller and bold text.
- [ ] High contrast.
- [ ] Talkback.
- [ ] Languages with large words or with letters/accents not frequently used in English.
- [ ] Right-to-left languages. (Even if translation isn’t complete, formatting should still respect the right-to-left layout)
- [ ] Large and small screen sizes. (Tablet and smaller phones)
- [ ] Multi-tasking: Split screen and Pop-up view. (Android 10 or higher)

[Reader IA][Bug][0 tags/blogs] Infinite loading indicator on Subscription feed after subscribing to tags
### Expected behavior
After going to "Suggested tags" flow inside the Tag filter bottom and returning to the Subscriptions feed it should still show the empty state (as there are still 0 blogs being followed) and not show a loading indicator indefinitely.

### Actual behavior
After going to "Suggested tags" flow inside the Tag filter bottom and returning to the Subscriptions feed it shows a loading indicator indefinitely on top of a blank feed.

https://github.com/wordpress-mobile/WordPress-Android/assets/5091503/5e88ec3f-7199-49e1-a5e8-eafcadecf4c0

### Steps to reproduce the behavior
Make sure to unsubscribe from all tags and blogs.

1. Install and log into Jetpack on a `feature/reader-ia` branch build
2. Go to reader
3. Go to the Subscriptions feed
4. Tap "0 tags" filter pill
5. Tap "Suggested tags"
6. Choose zero or more tags and tap "Done"/back
7. **Verify** it goes back to Subscription Feed in an infinite loading state
[Reader IA][Bug][0 blogs/tags] Subscription feed tries refreshing when tapping filter pill
### Expected behavior
When tapping a filter pill (either blogs or tags) the Subscription post feed should not try to refresh, unless the fetch for the filters updates the subscribed blog list and it now has at least 1 blog.

### Actual behavior
When tapping a filter pill (either blogs or tags) the Subscription post feed always tries to refresh.

### Steps to reproduce the behavior
Make sure to unsubscribe from all tags and blogs.

1. Install and log into Jetpack on a `feature/reader-ia` branch build
2. Go to reader
3. Go to the Subscriptions feed
4. Tap either the "0 blogs" or "0 tags" filter pill
5. **Verify** the feed tries to refresh (show the refresh indicator)

diff --git a/WordPress/src/main/java/org/wordpress/android/datasets/ReaderTagTable.java b/WordPress/src/main/java/org/wordpress/android/datasets/ReaderTagTable.java
index 977f7eaf0f2e..7c7530756b12 100644
--- a/WordPress/src/main/java/org/wordpress/android/datasets/ReaderTagTable.java
+++ b/WordPress/src/main/java/org/wordpress/android/datasets/ReaderTagTable.java
@@ -232,6 +232,10 @@ public static ReaderTagList getBookmarkTags() {
         return getTagsOfType(ReaderTagType.BOOKMARKED);
     }

+    public static ReaderTagList getDiscoverPostCardsTags() {
+        return getTagsOfType(ReaderTagType.DISCOVER_POST_CARDS);
+    }
+
     private static ReaderTagList getTagsOfType(ReaderTagType tagType) {
         String[] args = {Integer.toString(tagType.toInt())};
         Cursor c = ReaderDatabase.getReadableDb()
diff --git a/WordPress/src/main/java/org/wordpress/android/models/ReaderTagList.java b/WordPress/src/main/java/org/wordpress/android/models/ReaderTagList.java
index 6a9633c44441..12da31783833 100644
--- a/WordPress/src/main/java/org/wordpress/android/models/ReaderTagList.java
+++ b/WordPress/src/main/java/org/wordpress/android/models/ReaderTagList.java
@@ -44,6 +44,8 @@ public boolean isSameList(ReaderTagList otherList) {
                 return false;
             } else if (!otherTag.getTagTitle().equals(this.get(i).getTagTitle())) {
                 return false;
+            } else if (!otherTag.getTagDisplayName().equals(this.get(i).getTagDisplayName())) {
+                return false;
             }
         }

diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderEvents.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderEvents.java
index e107db26f932..de72a22e2b40 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderEvents.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderEvents.java
@@ -26,16 +26,27 @@ private ReaderEvents() {
         throw new AssertionError();
     }

-    public static class FollowedTagsChanged {
+    public static class FollowedTagsFetched {
         private final boolean mDidSucceed;
+        private final boolean mDidChange;

-        public FollowedTagsChanged(boolean didSucceed) {
+        public FollowedTagsFetched(boolean didSucceed) {
             mDidSucceed = didSucceed;
+            mDidChange = true;
+        }
+
+        public FollowedTagsFetched(boolean didSucceed, boolean didChange) {
+            mDidSucceed = didSucceed;
+            mDidChange = didChange;
         }

         public boolean didSucceed() {
             return mDidSucceed;
         }
+
+        public boolean didChange() {
+            return mDidChange;
+        }
     }

     public static class TagAdded {
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java
index 2da410a95ab6..287204bb16d1 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java
@@ -84,6 +84,7 @@
 import org.wordpress.android.ui.mysite.jetpackbadge.JetpackPoweredBottomSheetFragment;
 import org.wordpress.android.ui.pages.SnackbarMessageHolder;
 import org.wordpress.android.ui.prefs.AppPrefs;
+import org.wordpress.android.ui.reader.ReaderEvents.FollowedTagsFetched;
 import org.wordpress.android.ui.reader.ReaderEvents.TagAdded;
 import org.wordpress.android.ui.reader.ReaderTypes.ReaderPostListType;
 import org.wordpress.android.ui.reader.actions.ReaderActions;
@@ -934,14 +935,16 @@ private void resetPostAdapter(ReaderPostListType postListType) {

     @SuppressWarnings("unused")
     @Subscribe(threadMode = ThreadMode.MAIN)
-    public void onEventMainThread(ReaderEvents.FollowedTagsChanged event) {
+    public void onEventMainThread(FollowedTagsFetched event) {
         if (getPostListType() == ReaderPostListType.TAG_FOLLOWED) {
-            // reload the tag filter since tags have changed
-            reloadTags();
+            if (event.didChange()) {
+                // reload the tag filter since tags have changed or we just opened the fragment
+                reloadTags();
+            }

             // update the current tag if the list fragment is empty - this will happen if
             // the tag table was previously empty (ie: first run)
-            if (isPostAdapterEmpty()) {
+            if (isPostAdapterEmpty() && (ReaderBlogTable.hasFollowedBlogs() || !mHasUpdatedPosts)) {
                 updateCurrentTag();
             }
         }
@@ -1724,9 +1727,6 @@ private void setEmptyTitleDescriptionAndButton(boolean requestFailed) {
         // Ensure the default image is reset for empty views before applying logic
         mActionableEmptyView.image.setImageResource(R.drawable.illustration_reader_empty);

-        // TODO thomashortadev
-        //  try to quickly hack some way of making the button black
-
         if (shouldShowEmptyViewForSelfHostedCta()) {
             setEmptyTitleAndDescriptionForSelfHostedCta();
             return;
@@ -1940,7 +1940,9 @@ private void showBookmarksSavedLocallyDialog(ShowBookmarkedSavedOnlyLocallyDialo
                 .setCancelable(false)
                 .create();
         mBookmarksSavedLocallyDialog.show();
-    }    /*
+    }
+
+    /*
      * called by post adapter when data has been loaded
      */
     private final ReaderInterfaces.DataLoadedListener mDataLoadedListener = new ReaderInterfaces.DataLoadedListener() {
@@ -2333,7 +2335,7 @@ public void run() {
                             mActionableEmptyView.button.setVisibility(View.GONE);
                             mActionableEmptyView.subtitle.setVisibility(View.GONE);
                             showEmptyView();
-                        } else {
+                        } else if (!isPostAdapterEmpty()) {
                             hideEmptyView();
                         }
                     });
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderSubsActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderSubsActivity.java
index 955a08936ce5..3e8914829792 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderSubsActivity.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderSubsActivity.java
@@ -41,6 +41,7 @@
 import org.wordpress.android.ui.LocaleAwareActivity;
 import org.wordpress.android.ui.RequestCodes;
 import org.wordpress.android.ui.prefs.AppPrefs;
+import org.wordpress.android.ui.reader.ReaderEvents.FollowedTagsFetched;
 import org.wordpress.android.ui.reader.actions.ReaderActions;
 import org.wordpress.android.ui.reader.actions.ReaderBlogActions;
 import org.wordpress.android.ui.reader.actions.ReaderTagActions;
@@ -203,7 +204,7 @@ protected void onResume() {

     @SuppressWarnings("unused")
     @Subscribe(threadMode = ThreadMode.MAIN)
-    public void onEventMainThread(ReaderEvents.FollowedTagsChanged event) {
+    public void onEventMainThread(FollowedTagsFetched event) {
         AppLog.d(AppLog.T.READER, "reader subs > followed tags changed");
         getPageAdapter().refreshFollowedTagFragment();
     }
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/actions/ReaderTagActions.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/actions/ReaderTagActions.java
index 9577553148fa..8e9ec0895f73 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/actions/ReaderTagActions.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/actions/ReaderTagActions.java
@@ -13,7 +13,7 @@
 import org.wordpress.android.models.ReaderTagList;
 import org.wordpress.android.models.ReaderTagType;
 import org.wordpress.android.ui.reader.ReaderConstants;
-import org.wordpress.android.ui.reader.ReaderEvents;
+import org.wordpress.android.ui.reader.ReaderEvents.FollowedTagsFetched;
 import org.wordpress.android.ui.reader.actions.ReaderActions.ActionListener;
 import org.wordpress.android.ui.reader.utils.ReaderUtils;
 import org.wordpress.android.util.AppLog;
@@ -51,7 +51,7 @@ public static boolean deleteTag(final ReaderTag tag,
     private static boolean deleteTagsLocallyOnly(ActionListener actionListener, ReaderTag tag) {
         ReaderTagTable.deleteTag(tag);
         ReaderActions.callActionListener(actionListener, true);
-        EventBus.getDefault().post(new ReaderEvents.FollowedTagsChanged(true));
+        EventBus.getDefault().post(new FollowedTagsFetched(true));

         return true;
     }
@@ -130,7 +130,7 @@ public static boolean addTags(@NonNull final List<ReaderTag> tags,
     private static boolean saveTagsLocallyOnly(ActionListener actionListener, ReaderTagList newTags) {
         ReaderTagTable.addOrUpdateTags(newTags);
         ReaderActions.callActionListener(actionListener, true);
-        EventBus.getDefault().post(new ReaderEvents.FollowedTagsChanged(true));
+        EventBus.getDefault().post(new FollowedTagsFetched(true));

         return true;
     }
@@ -147,7 +147,7 @@ private static boolean saveTagsLocallyAndRemotely(ActionListener actionListener,
             if (actionListener != null) {
                 ReaderActions.callActionListener(actionListener, true);
             }
-            EventBus.getDefault().post(new ReaderEvents.FollowedTagsChanged(true));
+            EventBus.getDefault().post(new FollowedTagsFetched(true));
         };

         RestRequest.ErrorListener errorListener = volleyError -> {
@@ -159,7 +159,7 @@ private static boolean saveTagsLocallyAndRemotely(ActionListener actionListener,
                 if (actionListener != null) {
                     ReaderActions.callActionListener(actionListener, true);
                 }
-                EventBus.getDefault().post(new ReaderEvents.FollowedTagsChanged(true));
+                EventBus.getDefault().post(new FollowedTagsFetched(true));
                 return;
             }

@@ -171,7 +171,7 @@ private static boolean saveTagsLocallyAndRemotely(ActionListener actionListener,
             if (actionListener != null) {
                 ReaderActions.callActionListener(actionListener, false);
             }
-            EventBus.getDefault().post(new ReaderEvents.FollowedTagsChanged(false));
+            EventBus.getDefault().post(new FollowedTagsFetched(false));
         };

         ReaderTagTable.addOrUpdateTags(newTags);
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/repository/ReaderDiscoverDataProvider.kt b/WordPress/src/main/java/org/wordpress/android/ui/reader/repository/ReaderDiscoverDataProvider.kt
index a1a20185e26d..27b8cb35b031 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/repository/ReaderDiscoverDataProvider.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/repository/ReaderDiscoverDataProvider.kt
@@ -14,7 +14,7 @@ import org.wordpress.android.models.discover.ReaderDiscoverCards
 import org.wordpress.android.modules.IO_THREAD
 import org.wordpress.android.modules.UI_THREAD
 import org.wordpress.android.ui.reader.ReaderEvents.FetchDiscoverCardsEnded
-import org.wordpress.android.ui.reader.ReaderEvents.FollowedTagsChanged
+import org.wordpress.android.ui.reader.ReaderEvents.FollowedTagsFetched
 import org.wordpress.android.ui.reader.actions.ReaderActions.UpdateResult.CHANGED
 import org.wordpress.android.ui.reader.actions.ReaderActions.UpdateResult.FAILED
 import org.wordpress.android.ui.reader.actions.ReaderActions.UpdateResult.HAS_NEW
@@ -211,7 +211,7 @@ class ReaderDiscoverDataProvider @Inject constructor(

     @Suppress("unused", "UNUSED_PARAMETER")
     @Subscribe(threadMode = BACKGROUND)
-    fun onFollowedTagsChanged(event: FollowedTagsChanged) {
+    fun onFollowedTagsFetched(event: FollowedTagsFetched) {
         launch {
             refreshCards()
         }
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/repository/usecases/tags/FetchFollowedTagsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/reader/repository/usecases/tags/FetchFollowedTagsUseCase.kt
index 3afd6a0d2cf2..1d109666337d 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/repository/usecases/tags/FetchFollowedTagsUseCase.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/repository/usecases/tags/FetchFollowedTagsUseCase.kt
@@ -2,7 +2,7 @@ package org.wordpress.android.ui.reader.repository.usecases.tags

 import org.greenrobot.eventbus.Subscribe
 import org.greenrobot.eventbus.ThreadMode
-import org.wordpress.android.ui.reader.ReaderEvents.FollowedTagsChanged
+import org.wordpress.android.ui.reader.ReaderEvents.FollowedTagsFetched
 import org.wordpress.android.ui.reader.repository.ReaderRepositoryCommunication
 import org.wordpress.android.ui.reader.repository.ReaderRepositoryCommunication.Error.NetworkUnavailable
 import org.wordpress.android.ui.reader.repository.ReaderRepositoryCommunication.Error.RemoteRequestFailure
@@ -48,7 +48,7 @@ class FetchFollowedTagsUseCase @Inject constructor(

     @Suppress("unused")
     @Subscribe(threadMode = ThreadMode.BACKGROUND)
-    fun onFollowedTagsChanged(event: FollowedTagsChanged) {
+    fun onFollowedTagsFetched(event: FollowedTagsFetched) {
         val result = if (event.didSucceed()) {
             Success
         } else {
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/repository/usecases/tags/FollowInterestTagsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/reader/repository/usecases/tags/FollowInterestTagsUseCase.kt
index 3002e258d450..8e171e262261 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/repository/usecases/tags/FollowInterestTagsUseCase.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/repository/usecases/tags/FollowInterestTagsUseCase.kt
@@ -4,7 +4,7 @@ import org.greenrobot.eventbus.Subscribe
 import org.greenrobot.eventbus.ThreadMode
 import org.wordpress.android.fluxc.store.AccountStore
 import org.wordpress.android.models.ReaderTag
-import org.wordpress.android.ui.reader.ReaderEvents.FollowedTagsChanged
+import org.wordpress.android.ui.reader.ReaderEvents.FollowedTagsFetched
 import org.wordpress.android.ui.reader.actions.ReaderTagActions
 import org.wordpress.android.ui.reader.repository.ReaderRepositoryCommunication
 import org.wordpress.android.ui.reader.repository.ReaderRepositoryCommunication.Error.NetworkUnavailable
@@ -43,7 +43,7 @@ class FollowInterestTagsUseCase @Inject constructor(

     @Suppress("unused")
     @Subscribe(threadMode = ThreadMode.BACKGROUND)
-    fun onFollowedTagsChanged(event: FollowedTagsChanged) {
+    fun onFollowedTagsFetched(event: FollowedTagsFetched) {
         val result = if (event.didSucceed()) {
             Success
         } else {
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/update/ReaderUpdateLogic.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/update/ReaderUpdateLogic.java
index cde09194380f..e743d7f52bfa 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/update/ReaderUpdateLogic.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/update/ReaderUpdateLogic.java
@@ -24,6 +24,7 @@
 import org.wordpress.android.ui.prefs.AppPrefs;
 import org.wordpress.android.ui.reader.ReaderConstants;
 import org.wordpress.android.ui.reader.ReaderEvents;
+import org.wordpress.android.ui.reader.ReaderEvents.FollowedTagsFetched;
 import org.wordpress.android.ui.reader.ReaderEvents.InterestTagsFetchEnded;
 import org.wordpress.android.ui.reader.services.ServiceCompletionListener;
 import org.wordpress.android.util.AppLog;
@@ -122,24 +123,21 @@ public void onErrorResponse(VolleyError volleyError) {
                             .get("read/menu", params, null, listener, errorListener);
     }

-    private boolean displayNameUpdateWasNeeded(ReaderTagList serverTopics) {
-        boolean updateDone = false;
-
+    /**
+     * Update the display names of the default tags (such as Subscribed and Discover) in the serverTopics list.
+     *
+     * @param serverTopics The list of default tags.
+     */
+    private void updateDisplayNamesIfNeeded(@NonNull ReaderTagList serverTopics) {
         for (ReaderTag tag : serverTopics) {
-            String tagNameBefore = tag.getTagDisplayName();
             if (tag.isFollowedSites()) {
                 tag.setTagDisplayName(mContext.getString(R.string.reader_subscribed_display_name));
-                if (!tagNameBefore.equals(tag.getTagDisplayName())) updateDone = true;
             } else if (tag.isDiscover()) {
                 tag.setTagDisplayName(mContext.getString(R.string.reader_discover_display_name));
-                if (!tagNameBefore.equals(tag.getTagDisplayName())) updateDone = true;
             } else if (tag.isPostsILike()) {
                 tag.setTagDisplayName(mContext.getString(R.string.reader_my_likes_display_name));
-                if (!tagNameBefore.equals(tag.getTagDisplayName())) updateDone = true;
             }
         }
-
-        return updateDone;
     }

     private void handleUpdateTagsResponse(final JSONObject jsonObject) {
@@ -151,7 +149,7 @@ public void run() {
                 ReaderTagList serverTopics = new ReaderTagList();
                 serverTopics.addAll(parseTags(jsonObject, "default", ReaderTagType.DEFAULT));

-                boolean displayNameUpdateWasNeeded = displayNameUpdateWasNeeded(serverTopics);
+                updateDisplayNamesIfNeeded(serverTopics);

                 serverTopics.addAll(parseTags(jsonObject, "subscribed", ReaderTagType.FOLLOWED));

@@ -176,13 +174,11 @@ public void run() {
                 localTopics.addAll(ReaderTagTable.getFollowedTags());
                 localTopics.addAll(ReaderTagTable.getBookmarkTags());
                 localTopics.addAll(ReaderTagTable.getCustomListTags());
+                localTopics.addAll(ReaderTagTable.getDiscoverPostCardsTags());

-                if (
-                        !localTopics.isSameList(serverTopics)
-                        || displayNameUpdateWasNeeded
-                ) {
-                    AppLog.d(AppLog.T.READER, "reader service > followed topics changed "
-                                              + "updatedDisplayNames [" + displayNameUpdateWasNeeded + "]");
+                boolean didChangeFollowedTags = false;
+                if (!localTopics.isSameList(serverTopics)) {
+                    AppLog.d(AppLog.T.READER, "reader service > followed topics changed");

                     if (!mAccountStore.hasAccessToken()) {
                         // Do not delete locally saved tags for logged out user
@@ -195,8 +191,9 @@ public void run() {
                         ReaderTagTable.replaceTags(serverTopics);
                     }
                     // broadcast the fact that there are changes
-                    EventBus.getDefault().post(new ReaderEvents.FollowedTagsChanged(true));
+                    didChangeFollowedTags = true;
                 }
+                EventBus.getDefault().post(new FollowedTagsFetched(true, didChangeFollowedTags));
                 AppPrefs.setReaderTagsUpdatedTimestamp(new Date().getTime());

                 taskCompleted(UpdateTask.TAGS);
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/subfilter/SubFilterViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/reader/subfilter/SubFilterViewModel.kt
index e331ecaeb231..a9dd37639a36 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/subfilter/SubFilterViewModel.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/subfilter/SubFilterViewModel.kt
@@ -393,7 +393,7 @@ class SubFilterViewModel @Inject constructor(

     @Suppress("unused", "UNUSED_PARAMETER")
     @Subscribe(threadMode = ThreadMode.MAIN)
-    fun onEventMainThread(event: ReaderEvents.FollowedTagsChanged) {
+    fun onEventMainThread(event: ReaderEvents.FollowedTagsFetched) {
         AppLog.d(T.READER, "Subfilter bottom sheet > followed tags changed")
         loadSubFilters()
     }
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/viewmodels/ReaderViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/reader/viewmodels/ReaderViewModel.kt
index a6e26e3f648a..0e13b2f17e0b 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/viewmodels/ReaderViewModel.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/viewmodels/ReaderViewModel.kt
@@ -135,7 +135,8 @@ class ReaderViewModel @Inject constructor(
 //        _showJetpackPoweredBottomSheet.value = Event(true)
     }

-    private fun loadTabs(savedInstanceState: Bundle? = null) {
+    @JvmOverloads
+    fun loadTabs(savedInstanceState: Bundle? = null) {
         launch {
             val tagList = loadReaderTabsUseCase.loadTabs()
             if (tagList.isNotEmpty() && readerTagsList != tagList) {
@@ -215,7 +216,7 @@ class ReaderViewModel @Inject constructor(

     @Suppress("unused", "UNUSED_PARAMETER")
     @Subscribe(threadMode = MAIN)
-    fun onTagsUpdated(event: ReaderEvents.FollowedTagsChanged) {
+    fun onTagsUpdated(event: ReaderEvents.FollowedTagsFetched) {
         loadTabs()
     }

diff --git a/WordPress/src/test/java/org/wordpress/android/ui/reader/repository/FetchFollowedTagsUseCaseTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/reader/repository/FetchFollowedTagsUseCaseTest.kt
index a581bc43584f..5c0c086000e0 100644
--- a/WordPress/src/test/java/org/wordpress/android/ui/reader/repository/FetchFollowedTagsUseCaseTest.kt
+++ b/WordPress/src/test/java/org/wordpress/android/ui/reader/repository/FetchFollowedTagsUseCaseTest.kt
@@ -9,7 +9,7 @@ import org.mockito.Mock
 import org.mockito.junit.MockitoJUnitRunner
 import org.mockito.kotlin.whenever
 import org.wordpress.android.BaseUnitTest
-import org.wordpress.android.ui.reader.ReaderEvents.FollowedTagsChanged
+import org.wordpress.android.ui.reader.ReaderEvents.FollowedTagsFetched
 import org.wordpress.android.ui.reader.repository.ReaderRepositoryCommunication.Error.NetworkUnavailable
 import org.wordpress.android.ui.reader.repository.ReaderRepositoryCommunication.Error.RemoteRequestFailure
 import org.wordpress.android.ui.reader.repository.ReaderRepositoryCommunication.Success
@@ -61,12 +61,12 @@ class FetchFollowedTagsUseCaseTest : BaseUnitTest() {
     }

     @Test
-    fun `Success returned when FollowedTagsChanged event is posted with true`() = test {
+    fun `Success returned when FollowedTagsFetched event is posted with success`() = test {
         // Given
         whenever(networkUtilsWrapper.isNetworkAvailable()).thenReturn(true)
-        val event = FollowedTagsChanged(true)
+        val event = FollowedTagsFetched(true)
         whenever(readerUpdateServiceStarterWrapper.startService(contextProvider.getContext(), EnumSet.of(TAGS)))
-            .then { useCase.onFollowedTagsChanged(event) }
+            .then { useCase.onFollowedTagsFetched(event) }

         // When
         val result = useCase.fetch()
@@ -76,12 +76,12 @@ class FetchFollowedTagsUseCaseTest : BaseUnitTest() {
     }

     @Test
-    fun `RemoteRequestFailure returned when FollowedTagsChanged event is posted with false`() = test {
+    fun `RemoteRequestFailure returned when FollowedTagsFetched event is posted with failure`() = test {
         // Given
         whenever(networkUtilsWrapper.isNetworkAvailable()).thenReturn(true)
-        val event = FollowedTagsChanged(false)
+        val event = FollowedTagsFetched(false)
         whenever(readerUpdateServiceStarterWrapper.startService(contextProvider.getContext(), EnumSet.of(TAGS)))
-            .then { useCase.onFollowedTagsChanged(event) }
+            .then { useCase.onFollowedTagsFetched(event) }

         // When
         val result = useCase.fetch()
diff --git a/WordPress/src/test/java/org/wordpress/android/ui/reader/repository/ReaderDiscoverDataProviderTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/reader/repository/ReaderDiscoverDataProviderTest.kt
index 18f0d5704887..3a87558edee4 100644
--- a/WordPress/src/test/java/org/wordpress/android/ui/reader/repository/ReaderDiscoverDataProviderTest.kt
+++ b/WordPress/src/test/java/org/wordpress/android/ui/reader/repository/ReaderDiscoverDataProviderTest.kt
@@ -16,7 +16,7 @@ import org.wordpress.android.models.ReaderPost
 import org.wordpress.android.models.discover.ReaderDiscoverCard.ReaderPostCard
 import org.wordpress.android.models.discover.ReaderDiscoverCards
 import org.wordpress.android.ui.reader.ReaderEvents.FetchDiscoverCardsEnded
-import org.wordpress.android.ui.reader.ReaderEvents.FollowedTagsChanged
+import org.wordpress.android.ui.reader.ReaderEvents.FollowedTagsFetched
 import org.wordpress.android.ui.reader.actions.ReaderActions.UpdateResult.FAILED
 import org.wordpress.android.ui.reader.actions.ReaderActions.UpdateResult.HAS_NEW
 import org.wordpress.android.ui.reader.actions.ReaderActions.UpdateResult.UNCHANGED
@@ -249,7 +249,11 @@ class ReaderDiscoverDataProviderTest : BaseUnitTest() {
     @Test
     fun `when followed tags change the discover feed gets refreshed`() = test {
         // Act
-        dataProvider.onFollowedTagsChanged(FollowedTagsChanged(true))
+        dataProvider.onFollowedTagsFetched(
+            FollowedTagsFetched(
+                true
+            )
+        )
         // Assert
         verify(fetchDiscoverCardsUseCase).fetch(REQUEST_FIRST_PAGE)
     }
diff --git a/WordPress/src/test/java/org/wordpress/android/ui/reader/viewmodels/ReaderViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/reader/viewmodels/ReaderViewModelTest.kt
index 29780c984015..2f437f24465c 100644
--- a/WordPress/src/test/java/org/wordpress/android/ui/reader/viewmodels/ReaderViewModelTest.kt
+++ b/WordPress/src/test/java/org/wordpress/android/ui/reader/viewmodels/ReaderViewModelTest.kt
@@ -177,7 +177,7 @@ class ReaderViewModelTest : BaseUnitTest() {
     }

     @Test
-    fun `Tags are reloaded when FollowedTagsChanged event is received`() = testWithNonEmptyTags {
+    fun `Tags are reloaded when FollowedTagsFetched event is received`() = testWithNonEmptyTags {
         // Arrange
         var state: ReaderUiState? = null
         viewModel.uiState.observeForever {