Open guardrex opened 6 months ago
Please let me know all your requirements, hopefully I will do quick one when the time permits
Blazor Web App that allows the user to set the culture via a drop-down list from a set of selected cultures ... the app must persist the culture across SSR and CSR transitions between SSR/CSR/prerendering components.
If it's quicker for you, you can start with the one that I created and just modify it. It has the server/WASM/Auto components already in it and preconfigures for en-US
and es-CR
(Costa Rica), which work well for demo because the number and date formats change the same way between SSR and CSR glob/loc.
https://github.com/guardrex/BlazorCulturePerComponentInteractivity
Is the plan to add the sample and link it to our docs or refer to an actual blog post?
I'll place a sample app in the Blazor samples repo, and the article will explain basically how it works. You only need to provide the sample app. My problem is just that I'm too dumb of a 🦖 to figure it out 😆. I can write it up for the article and place the sample if I can just see how to set it up.
I can add you as a byline author "By ..." and link it to whatever you want if that's helpful to you.
@hishamco ... I take it that you're still busy there? You could let me know what your idea is for CSR and the loc cookie to make it work? I can try to implement your idea.
Is your idea to interact directly with the loc cookie in the .Client
project for CSR via JS interop? That's the only thing that I could think of to get CSR components to pick up the correct culture from the cookie. I tried it and failed to get that approach to work 🙈 ... but I could try again with fresh eyes and see if I can make it work.
If you have a different idea that doesn't rely on JS interop, what's your idea to make it work?
I tried it and failed to get that approach to work 🙈 ... but I could try again with fresh eyes and see if I can make it work.
Do you have your demo in a public repo, so I can start from where you stop instead of doing it from scratch
BTW I was sick for week or so, that's why I'm active at that time
I think the sample works well for both SSR & CSR, right? What I remember from our last conversation is trying the cookie approach, right?
After a quick look seems you are using both cookies & local storage
Yes, it works by keeping both the loc cookie and local storage up-to-date. That way, when it's rendering a component server-side, it goes off the loc cookie; and when it's rendering client-side, it goes off of local storage. Due to the differences in the availability of naming server- versus client-side, I use a dictionary for the dropdown list text so that it appears the same way to the user.
It doesn't rely on the loc cookie for CSR. When I investigated trying to get the loc cookie to be used via CSR, the only approach that seemed like it might work would be to interact with the cookie directly via JS interop. However, I tried to make that work and couldn't get it working properly.
Let me check the CSR and make the cookie works as well
Shall I submit a PR to your repo?
Sure ... that's cool.
Check the PR https://github.com/guardrex/BlazorCulturePerComponentInteractivity/pull/1 and let me know if you have comments
Yes ... that was about what I was trying to do and didn't quite get it working.
I'll take a closer look on Monday. If it's all good, I'll update the article section from the example.
If you want an author byline on the topic, let me know what you want it linked to.
It will be either Tuesday or Wednesday. I'm bogged down at the moment in security bits.
If there's anything to help please let me know
@hishamco ... I just looked it over.
So, the change you made was to basically swap interacting directly with the cookie for local storage.
However, this seems less elegant to me. The cookie approach ...
Microsoft.AspNetCore.Localization
).c=en-US|uic=en-US
, not just en-US
?Why would the cookie approach be better than local storage? If there isn't a compelling reason to change, I'd kind'a like to stick with local storage for CSR, as the topic already covers ...
Local Storage approach | Cookie approach |
---|---|
window.blazorCulture = { |
window.blazorCulture = { |
const string defaultCulture = "en-US"; |
CultureInfo culture; |
Requires more code (and messy code because it's more work to deal with the cookie), as shown below.
Not much :)
It requires that the client app reference an additional package (Microsoft.AspNetCore.Localization).
You can avoid it by providing the localization cookie name or get the value directly from the server. FYI the cookie APIs is able to read any cookie value
It seems to use values for the loc cookie for CSR that are different from the server-based loc cookie. For English, wouldn't the normal server-side cookie value be c=en-US|uic=en-US, not just en-US?
I just used the default values provided by CookieRequestCultureProvider
Will make the CSR parts of a BWA less like what we're pitching for pure standalone WASM, which uses local storage.
Doesn't help shed use of the controller under CSR to set the cookie server-side so that server-side loc can pick up its value.
I didn't know that your requirement to use the controller
We're still stuck having to run code locally via JS interop and toss back and forth the server, so there's no improvement for that aspect.
What's the issue here? Could you please elaborate?
Not much :)
It's still messy 🤮😆 compared to local storage, and it still departs from our standalone WASM approach.
You can avoid it by providing the localization cookie name
That's fair, and I think that would be wise.
I just used the default values provided by CookieRequestCultureProvider
I'm not sure what you mean. You used just the name value of the culture (e.g., en-US
) for the cookie value, but I thought that such loc cookies use the c=...|uic=...
format for the value. I don't really know much about the cookie. I just noticed a delta on that when I was looking at what the ASP.NET Core loc bits were doing compared to what you had for the cookie values. So, it looks like there's this delta in cookie values between the two, and that seems less elegant. Because local storage is a completely independent system, it can be different ... use different values ... and it doesn't seem inconsistent.
I didn't know that your requirement to use the controller
What's the issue here? Could you please elaborate?
In the CultureSelector
component to make this work, the controller still has to be hit. Otherwise, the system breaks down and culture can't be updated. Using the cookie approach didn't get rid of that. It didn't simplify anything really in that regard. I was hoping that there was a magic way ✨ to perhaps drop the controller request for CSR and make this whole system still work. However, it seems like the controller has to be hit both for SSR and CSR to make this work. The direct cookie use approach didn't do anything to help that situation out and make it simpler with less code and with dropping a server request for components on CSR.
All that this cookie approach did was result in nasty JS cookie code 😆 over sweet JS local storage code 😎. I just felt that the loc storage code is a lot nicer to see and work with ... and going back to what we have earlier in the article, it's exactly the approach taken for standalone WASM (i.e., the true, real CSR Blazor app type). It feels more like a natural extension of what we're telling users to do in a standalone WASM app.
Anyway ... these are just impressions. If you really think that the cookie approach is better, let me know why you think so. I think Dan and Artak will decide which way the article should go. If you don't have a good reason for this approach tho, I still like the local storage approach the best, and that's what I'd use, unless there are some 🐉 or 😈 that I'm not aware of that could break it 💥 in some way.
Here's the controller code that I'm referring to .....................
In the server project ...
[Route("[controller]/[action]")]
public class CultureController : Controller
{
public IActionResult Set(string culture, string redirectUri)
{
if (culture != null)
{
HttpContext.Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
CookieRequestCultureProvider.MakeCookieValue(
new RequestCulture(culture, culture)));
}
return LocalRedirect(redirectUri);
}
}
In the CultureSelector
component ...
var uri = new Uri(Navigation.Uri)
.GetComponents(UriComponents.PathAndQuery, UriFormat.Unescaped);
var cultureEscaped = Uri.EscapeDataString(value.Name);
var uriEscaped = Uri.EscapeDataString(uri);
Navigation.NavigateTo(
$"Culture/Set?culture={cultureEscaped}&redirectUri={uriEscaped}",
forceLoad: true);
BTW ... one more thing ... the CultureSelector
isn't invoking blazorCulture.set
the same way. It invokes it like this ...
JS.InvokeVoidAsync("blazorCulture.set", value.Name);
... without the cookie name.
UPDATE: Actually, it looks like that line can be removed 🔪 for the cookie approach, just leaving the controller request there.
@MackinnonBuck looks like @guardrex has some questions with this. But I know you've a ton on your plate already. So please keep this on the backlog of your todo list. I think it'll be ok to tackle closer to preview 6/7 timeframe.
Description
cc: @hishamco ... I'll work off this issue. Please post your sample cross-link here and let me know if you want a byline (and what cross-link to use for it if so).
Page URL
https://learn.microsoft.com/en-us/aspnet/core/blazor/globalization-localization?view=aspnetcore-8.0
Content source URL
https://github.com/dotnet/AspNetCore.Docs/blob/main/aspnetcore/blazor/globalization-localization.md
Document ID
d6f07538-228e-9f96-680f-6c324caf11d6
Article author
@guardrex