From 0292b2bbc7fc23d9db576592259c5edfc83c45a7 Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 30 Mar 2026 10:43:21 +0800 Subject: [PATCH] fix: Update fallback culture handling in AbpCultureMenuItemUrlProvider to use CurrentCulture instead of CurrentUICulture --- .../2026-03-29-Url-Based-Localization/POST.md | 2 +- .../fundamentals/url-based-localization.md | 2 +- .../AbpCultureMenuItemUrlProvider.cs | 7 ++++--- .../AbpCultureMenuItemUrlProvider_Tests.cs | 18 ++++++++++-------- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/docs/en/Community-Articles/2026-03-29-Url-Based-Localization/POST.md b/docs/en/Community-Articles/2026-03-29-Url-Based-Localization/POST.md index c97753b54f..ec70ba02c7 100644 --- a/docs/en/Community-Articles/2026-03-29-Url-Based-Localization/POST.md +++ b/docs/en/Community-Articles/2026-03-29-Url-Based-Localization/POST.md @@ -93,7 +93,7 @@ Blazor WebAssembly (WebApp) is similar. The server renders the first page via SS |---|---| | **Culture detection** | `RouteDataRequestCultureProvider` reads `{culture}` from the URL on the initial HTTP request (SSR). | | **Cookie persistence** | The middleware saves the detected culture to the `.AspNetCore.Culture` cookie, which persists across the WebSocket connection. | -| **Menu URLs** | `AbpCultureMenuItemUrlProvider` prepends the culture prefix. In Blazor interactive circuits (where `HttpContext` is unavailable), it falls back to `CultureInfo.CurrentUICulture`. | +| **Menu URLs** | `AbpCultureMenuItemUrlProvider` prepends the culture prefix. In Blazor interactive circuits (where `HttpContext` is null — no active HTTP request), it falls back to `CultureInfo.CurrentCulture`. | | **Language switching** | The built-in `LanguageSwitch` component navigates to `/Abp/Languages/Switch` with `forceLoad: true`, triggering a full HTTP reload. The culture segment in the return URL is automatically replaced. | ### Important: Blazor component route limitation diff --git a/docs/en/framework/fundamentals/url-based-localization.md b/docs/en/framework/fundamentals/url-based-localization.md index 25f1c853b5..66e3f43cd2 100644 --- a/docs/en/framework/fundamentals/url-based-localization.md +++ b/docs/en/framework/fundamentals/url-based-localization.md @@ -111,7 +111,7 @@ Blazor Server uses SignalR (WebSocket) for the interactive circuit. The HTTP mid |---|---| | **Culture detection** | `RouteDataRequestCultureProvider` reads `{culture}` from the URL on the initial HTTP request (SSR). | | **Cookie persistence** | The middleware automatically saves the detected culture to `.AspNetCore.Culture` cookie, which persists across the WebSocket connection. | -| **Menu URLs** | `AbpCultureMenuItemUrlProvider` prepends the culture prefix. In the interactive circuit (where `HttpContext` has no route values), it falls back to `CultureInfo.CurrentUICulture`. | +| **Menu URLs** | `AbpCultureMenuItemUrlProvider` prepends the culture prefix. In the interactive circuit (where `HttpContext` is null — no active HTTP request), it falls back to `CultureInfo.CurrentCulture`. | | **Language switching** | The built-in `LanguageSwitch` component navigates to `/Abp/Languages/Switch` with `forceLoad: true`, triggering a full HTTP reload. The culture segment in the return URL is automatically replaced. | ### What requires manual changes diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/AbpCultureMenuItemUrlProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/AbpCultureMenuItemUrlProvider.cs index b821883ecd..cd069f7c04 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/AbpCultureMenuItemUrlProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/AbpCultureMenuItemUrlProvider.cs @@ -62,9 +62,10 @@ public class AbpCultureMenuItemUrlProvider : IMenuItemUrlProvider } // Blazor interactive circuits: HttpContext is null because there is - // no active HTTP request. Fall back to CultureInfo.CurrentUICulture - // which was set by the middleware during SSR and persisted in the circuit. - var currentCulture = CultureInfo.CurrentUICulture.Name; + // no active HTTP request. Fall back to CultureInfo.CurrentCulture + // (set by the middleware during SSR and persisted in the circuit). + // CurrentCulture corresponds to the {culture} route segment, not ui-culture. + var currentCulture = CultureInfo.CurrentCulture.Name; var isKnownCulture = AbpLocalizationOptions.Value.Languages .Any(l => string.Equals(l.CultureName, currentCulture, StringComparison.OrdinalIgnoreCase)); diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/AbpCultureMenuItemUrlProvider_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/AbpCultureMenuItemUrlProvider_Tests.cs index 781f6dfbda..cd8296a352 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/AbpCultureMenuItemUrlProvider_Tests.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/AbpCultureMenuItemUrlProvider_Tests.cs @@ -51,24 +51,26 @@ public class AbpCultureMenuItemUrlProvider_Tests } [Fact] - public async Task Should_Use_CurrentUICulture_Fallback_When_No_HttpContext() + public async Task Should_Use_CurrentCulture_Fallback_When_No_HttpContext() { - // Simulates Blazor interactive circuit: no HttpContext, but CurrentUICulture is set + // Simulates Blazor interactive circuit: no HttpContext, but CurrentCulture is set. + // CurrentCulture (not CurrentUICulture) is used because {culture} route segments + // represent the culture, not the UI culture. var provider = CreateProviderWithoutHttpContext( useRouteBasedCulture: true, knownLanguages: new[] { "en", "zh-Hans", "tr" }); var menu = CreateMenuWithItems("/home", "/about"); - var previousCulture = CultureInfo.CurrentUICulture; + var previousCulture = CultureInfo.CurrentCulture; try { - CultureInfo.CurrentUICulture = new CultureInfo("zh-Hans"); + CultureInfo.CurrentCulture = new CultureInfo("zh-Hans"); await provider.HandleAsync(new MenuItemUrlProviderContext(menu)); } finally { - CultureInfo.CurrentUICulture = previousCulture; + CultureInfo.CurrentCulture = previousCulture; } menu.Items[0].Url.ShouldBe("/zh-Hans/home"); @@ -85,15 +87,15 @@ public class AbpCultureMenuItemUrlProvider_Tests var menu = CreateMenuWithItems("/home", "/about"); - var previousCulture = CultureInfo.CurrentUICulture; + var previousCulture = CultureInfo.CurrentCulture; try { - CultureInfo.CurrentUICulture = new CultureInfo("fr"); + CultureInfo.CurrentCulture = new CultureInfo("fr"); await provider.HandleAsync(new MenuItemUrlProviderContext(menu)); } finally { - CultureInfo.CurrentUICulture = previousCulture; + CultureInfo.CurrentCulture = previousCulture; } menu.Items[0].Url.ShouldBe("/home");