Repository navigation
fix(ui): stop MudBlazor from pinning the culture on MAUI hybrid heads (ADR-027) - #411
Merged
Merged
Conversation
… (ADR-027) MudBlazor 9.7+ reads its built-in English strings by assigning CurrentUICulture to the invariant culture and then assigning the previous value back. Both writes land in an AsyncLocal, so the calling thread leaves the read carrying an explicit culture. On a MAUI Blazor Hybrid head the renderer dispatches on the main thread, which nothing resets, so the first MudBlazor chrome string a page rendered pinned the app to its launch language: MauiCultureApplier set the thread defaults and reloaded the WebView, and the pinned AsyncLocal still won. Signed-in pages render that chrome (pagers, the bell badge) while the anonymous landing pages do not, which is why it surfaced as "works signed out, not signed in". AddUIShared now installs InvariantMudLocalizationInterceptor via MudBlazor's AddLocalizationInterceptor (replace semantics, order-independent): same resolution order as the default, with the built-ins read through a ResourceManager under an explicit invariant culture and no culture assignment. A canary test pins the upstream behaviour so the replacement is retired when MudBlazor stops assigning the culture. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On the ADC Android app (MAUI Blazor Hybrid), switching the language does nothing once a signed-in page has rendered: the preference PUT succeeds and the WebView reloads (visible in prod telemetry as five
PUT auth/preferences204s each followed by a fresh hub connection), yet every page still renders in the launch language. The web heads are unaffected.Root cause
MudBlazor9.7+ (AbstractLocalizationInterceptor.InvariantLanguageResourceLocalizer.ReadInvariant, introduced by MudBlazor/MudBlazor#13466) reads its built-in English strings by assigningCultureInfo.CurrentUICulture = InvariantCultureand then assigning the previous value back. Both writes go to anAsyncLocal, so the calling thread leaves the read carrying an explicit culture that beatsDefaultThreadCurrentUICulturefrom then on. On a hybrid head the renderer dispatches on the process's main thread, which nothing ever resets, so the first MudBlazor chrome string a page renders (pager label, bell badge, dialog close button) pins the app to its launch language:MauiCultureAppliersets the defaults toes, reloads, and every render still resolvesen-US. This is exactly the trap ADR-027 Decision 10 already forbids for our own code, re-introduced by the MudBlazor bump (9.6.0 -> 9.9.0, #220/#288). Verified:MudBlazor.dllis the only non-BCL assembly in the trimmed Android Release output that referencesset_CurrentUICulture, and a standalone repro shows the defaults switch no longer reaching the thread after the swap.Anonymous landing pages render almost no MudBlazor chrome while signed-in pages do, which is why it read as "works signed out, not signed in". Web heads never see it: request localization sets the culture per request, and a browser reload discards the WASM runtime.
Fix
AddUISharedinstallsInvariantMudLocalizationInterceptorthrough MudBlazor'sAddLocalizationInterceptor(replace semantics, soAddMudServicesorder does not matter). Same resolution order as MudBlazor's default (English UI culture or noMudLocalizerreads the built-ins; other cultures go throughResxMudLocalizerwith a built-in fallback), but the built-ins are read through aResourceManagerunder an explicit invariant culture, which is what MudBlazor's swap was for, with no culture assignment.Tests
InvariantMudLocalizationInterceptorTests(6): DI replacement, built-in read leaves theExecutionContextinstance unchanged, Spanish viaMudTranslationsplus built-in fallback for unknown keys, argument formatting, neutralenhandling.DefaultLocalizationInterceptorstill writes theAsyncLocal; when it starts failing, upstream fixed it and the replacement can be retired.MMCA.Common.UI.Testssuite: 970 passed. FACTS drift check: up to date.Docs
ADR-027 Decision 10 amendment is on a Website branch (
docs/adr-027-mudblazor-culture-pin), pending push.CHANGELOG.mdUnreleased entry included here.Follow-ups
/push-release); the ADC 1.8.5 Play build should wait for it.CurrentUICultureon hybrid hosts.🤖 Generated with Claude Code