From f17af21f8f083970cbf9ee3a59aaf2a6fd45b9ed Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Wed, 1 Jul 2026 14:27:01 -0400 Subject: [PATCH] CMM-2132: Send the app language slug when fetching blogging prompts The blogging prompts fetch relied on the framework's default _locale, which is region-qualified (e.g. tr_TR). WP.com content endpoints resolve translations by the clean language slug (tr), so pass PerAppLocaleManager's language code explicitly and disable the framework locale for this request, matching how Reader and Stats already localize content. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../FetchBloggingPromptsListUseCase.kt | 9 ++++++- .../BloggingPromptCardViewModelSlice.kt | 11 ++++++-- .../FetchBloggingPromptsListUseCaseTest.kt | 16 +++++++++--- .../BloggingPromptCardViewModelSliceTest.kt | 7 ++++- .../rest/wpcom/BaseWPComRestClient.java | 2 +- .../rest/wpcom/WPComGsonRequestBuilder.kt | 5 ++-- .../BloggingPromptsRestClient.kt | 10 +++++-- .../bloggingprompts/BloggingPromptsStore.kt | 5 ++-- .../BloggingPromptsRestClientTest.kt | 26 ++++++++++++++----- .../BloggingPromptsStoreTest.kt | 24 +++++++++++------ 10 files changed, 85 insertions(+), 30 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/bloggingprompts/promptslist/usecase/FetchBloggingPromptsListUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/bloggingprompts/promptslist/usecase/FetchBloggingPromptsListUseCase.kt index 5ad5195c362f..859a5b317852 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/bloggingprompts/promptslist/usecase/FetchBloggingPromptsListUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/bloggingprompts/promptslist/usecase/FetchBloggingPromptsListUseCase.kt @@ -3,6 +3,7 @@ package org.wordpress.android.ui.bloggingprompts.promptslist.usecase import org.wordpress.android.fluxc.model.bloggingprompts.BloggingPromptModel import org.wordpress.android.fluxc.store.bloggingprompts.BloggingPromptsStore import org.wordpress.android.ui.mysite.SelectedSiteRepository +import org.wordpress.android.util.PerAppLocaleManager import java.time.LocalDate import java.time.ZoneId import java.util.Date @@ -11,6 +12,7 @@ import javax.inject.Inject class FetchBloggingPromptsListUseCase @Inject constructor( private val bloggingPromptsStore: BloggingPromptsStore, private val selectedSiteRepository: SelectedSiteRepository, + private val perAppLocaleManager: PerAppLocaleManager, ) { suspend fun execute(): Result { return fetchBloggingPrompts() @@ -35,7 +37,12 @@ class FetchBloggingPromptsListUseCase @Inject constructor( .let { Date.from(it) } return selectedSiteRepository.getSelectedSite()?.let { site -> - bloggingPromptsStore.fetchPrompts(site, NUMBER_OF_PROMPTS, fromDate) + bloggingPromptsStore.fetchPrompts( + site, + NUMBER_OF_PROMPTS, + fromDate, + perAppLocaleManager.getCurrentLocaleLanguageCode() + ) .takeUnless { it.isError } ?.model } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/bloggingprompts/BloggingPromptCardViewModelSlice.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/bloggingprompts/BloggingPromptCardViewModelSlice.kt index da6e61de3fa1..6bf635515021 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/bloggingprompts/BloggingPromptCardViewModelSlice.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/bloggingprompts/BloggingPromptCardViewModelSlice.kt @@ -25,6 +25,7 @@ import org.wordpress.android.ui.mysite.SiteNavigationAction import org.wordpress.android.ui.pages.SnackbarMessageHolder import org.wordpress.android.ui.prefs.AppPrefsWrapper import org.wordpress.android.ui.utils.UiString +import org.wordpress.android.util.PerAppLocaleManager import org.wordpress.android.viewmodel.Event import java.time.LocalDate import java.time.ZoneId @@ -43,7 +44,8 @@ class BloggingPromptCardViewModelSlice @Inject constructor( private val bloggingPromptsCardTrackHelper: BloggingPromptsCardTrackHelper, private val bloggingPromptsPostTagProvider: BloggingPromptsPostTagProvider, private val bloggingPromptCardBuilder: BloggingPromptCardBuilder, - private val promptsStore: BloggingPromptsStore + private val promptsStore: BloggingPromptsStore, + private val perAppLocaleManager: PerAppLocaleManager ) { private val _onSnackbarMessage = MutableLiveData>() val onSnackbarMessage = _onSnackbarMessage as LiveData> @@ -91,7 +93,12 @@ class BloggingPromptCardViewModelSlice @Inject constructor( isSinglePromptRefresh: Boolean = false ) { val numOfPromptsToFetch = if (isSinglePromptRefresh) 1 else NUM_PROMPTS_TO_REQUEST - val result = promptsStore.fetchPrompts(selectedSite, numOfPromptsToFetch, Date()) + val result = promptsStore.fetchPrompts( + selectedSite, + numOfPromptsToFetch, + Date(), + perAppLocaleManager.getCurrentLocaleLanguageCode() + ) when { result.isError -> postLastState() else -> { diff --git a/WordPress/src/test/java/org/wordpress/android/ui/bloggingprompts/promptslist/usecase/FetchBloggingPromptsListUseCaseTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/bloggingprompts/promptslist/usecase/FetchBloggingPromptsListUseCaseTest.kt index c9449aa100b8..981fb04d6a93 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/bloggingprompts/promptslist/usecase/FetchBloggingPromptsListUseCaseTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/bloggingprompts/promptslist/usecase/FetchBloggingPromptsListUseCaseTest.kt @@ -17,6 +17,7 @@ import org.wordpress.android.ui.bloggingprompts.promptslist.BloggingPromptsListF import org.wordpress.android.ui.bloggingprompts.promptslist.usecase.FetchBloggingPromptsListUseCase.Result.Failure import org.wordpress.android.ui.bloggingprompts.promptslist.usecase.FetchBloggingPromptsListUseCase.Result.Success import org.wordpress.android.ui.mysite.SelectedSiteRepository +import org.wordpress.android.util.PerAppLocaleManager import java.util.Date @ExperimentalCoroutinesApi @@ -26,13 +27,17 @@ class FetchBloggingPromptsListUseCaseTest : BaseUnitTest() { @Mock lateinit var bloggingPromptsStore: BloggingPromptsStore + + @Mock + lateinit var perAppLocaleManager: PerAppLocaleManager lateinit var useCase: FetchBloggingPromptsListUseCase @Before fun setUp() { useCase = FetchBloggingPromptsListUseCase( bloggingPromptsStore = bloggingPromptsStore, - selectedSiteRepository = selectedSiteRepository + selectedSiteRepository = selectedSiteRepository, + perAppLocaleManager = perAppLocaleManager ) } @@ -48,7 +53,8 @@ class FetchBloggingPromptsListUseCaseTest : BaseUnitTest() { @Test fun `given fetching prompts fails, when execute is called, then return failure`() = test { whenever(selectedSiteRepository.getSelectedSite()).thenReturn(SiteModel().apply { id = 1 }) - whenever(bloggingPromptsStore.fetchPrompts(any(), any(), any())) + whenever(perAppLocaleManager.getCurrentLocaleLanguageCode()).thenReturn("en") + whenever(bloggingPromptsStore.fetchPrompts(any(), any(), any(), any())) .thenReturn(BloggingPromptsResult(error = mock())) val result = useCase.execute() @@ -62,7 +68,8 @@ class FetchBloggingPromptsListUseCaseTest : BaseUnitTest() { var initialDate: Date = now var count: Int = 0 whenever(selectedSiteRepository.getSelectedSite()).thenReturn(SiteModel().apply { id = 1 }) - whenever(bloggingPromptsStore.fetchPrompts(any(), any(), any())) + whenever(perAppLocaleManager.getCurrentLocaleLanguageCode()).thenReturn("en") + whenever(bloggingPromptsStore.fetchPrompts(any(), any(), any(), any())) .doSuspendableAnswer { count = it.getArgument(1) initialDate = it.getArgument(2) @@ -97,7 +104,8 @@ class FetchBloggingPromptsListUseCaseTest : BaseUnitTest() { var initialDate: Date = now var count: Int = 0 whenever(selectedSiteRepository.getSelectedSite()).thenReturn(SiteModel().apply { id = 1 }) - whenever(bloggingPromptsStore.fetchPrompts(any(), any(), any())) + whenever(perAppLocaleManager.getCurrentLocaleLanguageCode()).thenReturn("en") + whenever(bloggingPromptsStore.fetchPrompts(any(), any(), any(), any())) .doSuspendableAnswer { count = it.getArgument(1) initialDate = it.getArgument(2) diff --git a/WordPress/src/test/java/org/wordpress/android/ui/mysite/cards/dashboard/bloggingprompts/BloggingPromptCardViewModelSliceTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/mysite/cards/dashboard/bloggingprompts/BloggingPromptCardViewModelSliceTest.kt index 4103820b3e7a..a7bcc5696712 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/mysite/cards/dashboard/bloggingprompts/BloggingPromptCardViewModelSliceTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/mysite/cards/dashboard/bloggingprompts/BloggingPromptCardViewModelSliceTest.kt @@ -29,6 +29,7 @@ import org.wordpress.android.ui.mysite.SiteNavigationAction import org.wordpress.android.ui.pages.SnackbarMessageHolder import org.wordpress.android.ui.prefs.AppPrefsWrapper import org.wordpress.android.ui.utils.UiString +import org.wordpress.android.util.PerAppLocaleManager @Suppress("LargeClass") @ExperimentalCoroutinesApi @@ -58,6 +59,9 @@ class BloggingPromptCardViewModelSliceTest : BaseUnitTest() { @Mock lateinit var promptsStore: BloggingPromptsStore + @Mock + lateinit var perAppLocaleManager: PerAppLocaleManager + private lateinit var viewModelSlice: BloggingPromptCardViewModelSlice private lateinit var navigationActions: MutableList @@ -83,7 +87,8 @@ class BloggingPromptCardViewModelSliceTest : BaseUnitTest() { bloggingPromptsCardTrackHelper, bloggingPromptsPostTagProvider, bloggingPromptCardBuilder, - promptsStore + promptsStore, + perAppLocaleManager ) whenever(selectedSiteRepository.getSelectedSite()).thenReturn(site) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/BaseWPComRestClient.java b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/BaseWPComRestClient.java index 8123c11f9137..75b148ea8c01 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/BaseWPComRestClient.java +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/BaseWPComRestClient.java @@ -73,7 +73,7 @@ public Request add(WPComGsonRequest request) { return add(request, true); } - protected Request add(WPComGsonRequest request, boolean addLocaleParameter) { + public Request add(WPComGsonRequest request, boolean addLocaleParameter) { if (addLocaleParameter) { addLocaleToRequest(request); } diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/WPComGsonRequestBuilder.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/WPComGsonRequestBuilder.kt index 13f06c9c5f18..cdd7ba7aa8ce 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/WPComGsonRequestBuilder.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/WPComGsonRequestBuilder.kt @@ -99,7 +99,8 @@ class WPComGsonRequestBuilder type: Type, enableCaching: Boolean = false, cacheTimeToLive: Int = BaseRequest.DEFAULT_CACHE_LIFETIME, - forced: Boolean = false + forced: Boolean = false, + addLocaleParameter: Boolean = true ) = suspendCancellableCoroutine> { cont -> val request = WPComGsonRequest.buildGetRequest(url, params, type, { cont.resume(Success(it)) @@ -113,7 +114,7 @@ class WPComGsonRequestBuilder if (forced) { request.setShouldForceUpdate() } - restClient.add(request) + restClient.add(request, addLocaleParameter) } /** diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/bloggingprompts/BloggingPromptsRestClient.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/bloggingprompts/BloggingPromptsRestClient.kt index 1a9779905e76..612e69870575 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/bloggingprompts/BloggingPromptsRestClient.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/bloggingprompts/BloggingPromptsRestClient.kt @@ -38,12 +38,17 @@ class BloggingPromptsRestClient @Inject constructor( site: SiteModel, perPage: Int, after: Date, + locale: String, ignoresYear: Boolean = true, ): BloggingPromptsPayload { val url = WPCOMV3.sites.site(site.siteId).blogging_prompts.url val params = mutableMapOf( "per_page" to perPage.toString(), - "after" to BloggingPromptsUtils.dateToString(after, ignoresYear) + "after" to BloggingPromptsUtils.dateToString(after, ignoresYear), + // Send the app's language slug (e.g. "tr") explicitly so prompt content is localized. + // The framework's default _locale is region-qualified (e.g. "tr_TR"), which the endpoint + // doesn't resolve, so we disable it below (addLocaleParameter = false) and set our own. + "_locale" to locale ) if (ignoresYear) { // when ignoring the year we force the response to be for the current year @@ -56,7 +61,8 @@ class BloggingPromptsRestClient @Inject constructor( this, url, params, - BloggingPromptsListResponseTypeToken.type + BloggingPromptsListResponseTypeToken.type, + addLocaleParameter = false ) return when (response) { is Success -> BloggingPromptsPayload(response.data) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/bloggingprompts/BloggingPromptsStore.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/bloggingprompts/BloggingPromptsStore.kt index fca34231413b..53a1f5a44aaa 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/bloggingprompts/BloggingPromptsStore.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/bloggingprompts/BloggingPromptsStore.kt @@ -29,10 +29,11 @@ class BloggingPromptsStore @Inject constructor( suspend fun fetchPrompts( site: SiteModel, number: Int, - from: Date + from: Date, + locale: String ): BloggingPromptsResult> { return coroutineEngine.withDefaultContext(AppLog.T.API, this, "fetchPrompts") { - val payload = restClient.fetchPrompts(site, number, from) + val payload = restClient.fetchPrompts(site, number, from, locale) storePrompts(site, payload) } } diff --git a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/network/rest/wpcom/bloggingprompts/BloggingPromptsRestClientTest.kt b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/network/rest/wpcom/bloggingprompts/BloggingPromptsRestClientTest.kt index b1bb8245fdc8..1862e5a1f7ec 100644 --- a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/network/rest/wpcom/bloggingprompts/BloggingPromptsRestClientTest.kt +++ b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/network/rest/wpcom/bloggingprompts/BloggingPromptsRestClientTest.kt @@ -103,6 +103,7 @@ class BloggingPromptsRestClientTest { private val siteId: Long = 1 private val numberOfPromptsToFetch: Int = 40 + private val locale: String = "en" @Before fun setUp() { @@ -123,7 +124,7 @@ class BloggingPromptsRestClientTest { val json = UnitTestUtils.getStringFromResourceFile(javaClass, PROMPTS_JSON) initFetchPrompts(data = getPromptsResponseFromJsonString(json)) - restClient.fetchPrompts(site, numberOfPromptsToFetch, Date()) + restClient.fetchPrompts(site, numberOfPromptsToFetch, Date(), locale) assertEquals( urlCaptor.firstValue, @@ -131,13 +132,23 @@ class BloggingPromptsRestClientTest { ) } + @Test + fun `when fetch prompts gets triggered, then the given locale is sent as the _locale param`() = test { + val json = UnitTestUtils.getStringFromResourceFile(javaClass, PROMPTS_JSON) + initFetchPrompts(data = getPromptsResponseFromJsonString(json)) + + restClient.fetchPrompts(site, numberOfPromptsToFetch, Date(), locale) + + assertEquals(locale, paramsCaptor.firstValue["_locale"]) + } + @Test fun `given success call, when fetch prompts gets triggered, then prompts response is returned`() = test { val json = UnitTestUtils.getStringFromResourceFile(javaClass, PROMPTS_JSON) initFetchPrompts(data = getPromptsResponseFromJsonString(json)) - val result = restClient.fetchPrompts(site, numberOfPromptsToFetch, Date()) + val result = restClient.fetchPrompts(site, numberOfPromptsToFetch, Date(), locale) assertSuccess(PROMPTS_RESPONSE, result) } @@ -147,7 +158,7 @@ class BloggingPromptsRestClientTest { test { initFetchPrompts(error = WPComGsonNetworkError(BaseNetworkError(GenericErrorType.UNKNOWN))) - val result = restClient.fetchPrompts(site, numberOfPromptsToFetch, Date()) + val result = restClient.fetchPrompts(site, numberOfPromptsToFetch, Date(), locale) assertError(GENERIC_ERROR, result) } @@ -157,7 +168,7 @@ class BloggingPromptsRestClientTest { test { initFetchPrompts(error = WPComGsonNetworkError(BaseNetworkError(GenericErrorType.TIMEOUT))) - val result = restClient.fetchPrompts(site, numberOfPromptsToFetch, Date()) + val result = restClient.fetchPrompts(site, numberOfPromptsToFetch, Date(), locale) assertError(TIMEOUT, result) } @@ -167,7 +178,7 @@ class BloggingPromptsRestClientTest { test { initFetchPrompts(error = WPComGsonNetworkError(BaseNetworkError(GenericErrorType.NETWORK_ERROR))) - val result = restClient.fetchPrompts(site, numberOfPromptsToFetch, Date()) + val result = restClient.fetchPrompts(site, numberOfPromptsToFetch, Date(), locale) assertError(API_ERROR, result) } @@ -177,7 +188,7 @@ class BloggingPromptsRestClientTest { test { initFetchPrompts(error = WPComGsonNetworkError(BaseNetworkError(GenericErrorType.INVALID_RESPONSE))) - val result = restClient.fetchPrompts(site, numberOfPromptsToFetch, Date()) + val result = restClient.fetchPrompts(site, numberOfPromptsToFetch, Date(), locale) assertError(INVALID_RESPONSE, result) } @@ -187,7 +198,7 @@ class BloggingPromptsRestClientTest { test { initFetchPrompts(error = WPComGsonNetworkError(BaseNetworkError(GenericErrorType.NOT_AUTHENTICATED))) - val result = restClient.fetchPrompts(site, numberOfPromptsToFetch, Date()) + val result = restClient.fetchPrompts(site, numberOfPromptsToFetch, Date(), locale) assertError(AUTHORIZATION_REQUIRED, result) } @@ -212,6 +223,7 @@ class BloggingPromptsRestClientTest { eq(BloggingPromptsListResponseTypeToken.type), eq(false), any(), + eq(false), eq(false) ) ).thenReturn(response) diff --git a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/bloggingprompts/BloggingPromptsStoreTest.kt b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/bloggingprompts/BloggingPromptsStoreTest.kt index f297dab6811f..7a8214b481cc 100644 --- a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/bloggingprompts/BloggingPromptsStoreTest.kt +++ b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/store/bloggingprompts/BloggingPromptsStoreTest.kt @@ -168,6 +168,7 @@ class BloggingPromptsStoreTest { private val numberOfPromptsToFetch = 40 private val requestedPromptDate = Date() + private val locale = "en" @Before fun setUp() { @@ -190,11 +191,12 @@ class BloggingPromptsStoreTest { restClient.fetchPrompts( siteModel, numberOfPromptsToFetch, - requestedPromptDate + requestedPromptDate, + locale ) ).thenReturn(payload) - promptsStore.fetchPrompts(siteModel, numberOfPromptsToFetch, requestedPromptDate) + promptsStore.fetchPrompts(siteModel, numberOfPromptsToFetch, requestedPromptDate, locale) verify(dao).insertForSite(siteModel.id, PROMPT_MODELS) } @@ -207,7 +209,8 @@ class BloggingPromptsStoreTest { restClient.fetchPrompts( siteModel, numberOfPromptsToFetch, - requestedPromptDate + requestedPromptDate, + locale ) ).thenReturn( payload @@ -216,7 +219,8 @@ class BloggingPromptsStoreTest { val result = promptsStore.fetchPrompts( siteModel, numberOfPromptsToFetch, - requestedPromptDate + requestedPromptDate, + locale ) assertThat(result.model).isEqualTo(PROMPT_MODELS) @@ -231,7 +235,8 @@ class BloggingPromptsStoreTest { restClient.fetchPrompts( siteModel, numberOfPromptsToFetch, - requestedPromptDate + requestedPromptDate, + locale ) ).thenReturn( payload @@ -246,7 +251,8 @@ class BloggingPromptsStoreTest { val result = promptsStore.fetchPrompts( siteModel, numberOfPromptsToFetch, - requestedPromptDate + requestedPromptDate, + locale ) assertThat(result.model).isNull() @@ -311,14 +317,16 @@ class BloggingPromptsStoreTest { restClient.fetchPrompts( siteModel, numberOfPromptsToFetch, - requestedPromptDate + requestedPromptDate, + locale ) ).thenReturn(payload) val result = promptsStore.fetchPrompts( siteModel, numberOfPromptsToFetch, - requestedPromptDate + requestedPromptDate, + locale ) assertThat(result.model).isNull()