Browse Source

fix: Update fallback culture handling in AbpCultureMenuItemUrlProvider to use CurrentCulture instead of CurrentUICulture

pull/25174/head
maliming 1 day ago
parent
commit
0292b2bbc7
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 2
      docs/en/Community-Articles/2026-03-29-Url-Based-Localization/POST.md
  2. 2
      docs/en/framework/fundamentals/url-based-localization.md
  3. 7
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/AbpCultureMenuItemUrlProvider.cs
  4. 18
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/AbpCultureMenuItemUrlProvider_Tests.cs

2
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

2
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

7
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));

18
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");

Loading…
Cancel
Save