Hi Bram
When you make AJAX requests to regular controllers, you don't utilize Episerver routing and the URL does not include the language segment. Episerver looks at this segment when attempting to set the value of PreferredCulture
for content requests.
When you make requests to a custom controller, without setting the PreferredCulture
, Episerver will use the master language when loading content for your code. However, you can easily detect and set the language code for custom controllers.
I usually do something like this:
PreferredCulture
to that language.Accept-Language
header of the AJAX request to the current language code (as written to the markup).I once wrote a piece about above solution, including sample code, here.
While reading your question a second time, I remembered another solution.
It sounds like you are requesting HTML using AJAX. If true, you could implement the filtering in your existing Episerver page controller (the page with your form), with the following code at the end of your action method.
return Request.IsAjaxRequest()
? PartialView(viewModel)
: View(viewModel);
Your front end code would then request the page itself as an AJAX request. Then Episerver will figure out the language for you.
Hi Stefan,
We've managed to partially translate the page using your 1st solution. However, this when we try to add blocks to the page, it will only translate the 1st one. All other blocks added below this one in the view are rendered in the master language. Upon closer inspection we noticed that the PreferredCulture was set to the master language directly after rendering the first block. This only happens with views replaced using javascript. Do you have an idea why the rendering of blocks would cause the PreferredCulture to reset?
Let me understand this right, Bram.
The custom controller renders an Episerver page that contains several blocks? And you implemented my attribute solution on the custom controller class?
If you did that, then the PreferredCulture
should be set and remain for that entire request. Neither pages nor blocks generally change that value. It is normally set before loading any routable content (not including blocks).
Are you sure that no other changes the PreferredCulture
property? Do you load those blocks yourself (using IContentLoader
), or do you render using the PropertyFor
(or similar) method?
We load the page with IContentLoader. This is done in a controller where the attribute is active. There are multiple blocks on this page that are loaded in the view using the PropertyFor method. I'm testing the block controllers right now, but I doubt they change the PreferredCulture. I'll update the post with my findings.
Update: the blocks do revert to the master language. Adding the attribute to the block controllers does not help. The language settings seem off though. It recognizes the 2nd language as the master language, and the master language as language to show.
In your controller, the PreferredCulture is set correctly? And you call IContentLoader.Get<T> with no language parameter (it will pick up the current language, itself)?
I would maybe try loading the blocks from the page, using the content loader with a forced language parameter, to verify that the content can in fact be loaded in the selected language.
PreferredCulture is set correctly. The content retrieved with IContentLoader.Get<T> is retrieved in the correct language as well. The blocks are shown in the correct language when I do a soft refresh, but shows incorrect data on a hard refresh. From what I've seen, we might've an incorrect implementation of the IUpdateCurrentLanguage interface, where we store the current language in a cookie. However, this cookie is overwritten with the master language through multiple other parts of code that also call this implementation. I haven't been able to find where Episerver uses this implementation though.
Okay, then you probably don't want to call the IUpdateCurrentLanguage
logic for these AJAX requests.
You can try changing my code sample, so it directly sets the content language and the UI culture (for things like date formatting and translations).
// Remove this:
_updateCurrentLanguage.Service.UpdateLanguage(culture.Name);
// Add these:
ContentLanguage.Instance.SetCulture(culture.Name);
UserInterfaceLanguage.Instance.SetCulture(culture.Name);
Generally the IUpdateCurrentLanguage
interface seems to be called from deep inside Episerver's routing. But not when using custom controllers, custom routing or WebAPI.
That didn't seem to make any difference. In our implementation of the IUpdateCurrentLanguage interface, somehow the master language is passed as a parameter. I've tried to force this to the 2nd language, which resulted in all pages being translated correctly. What we don't know is why the master language is passed as the parameter and how we can isolate the correct language in this method.
Update: seems like incorrect language is supplied by Episerver. Called from EPiServer.Cms.AspNet.dll!EPiServer.Web.Routing.Segments.Internal.RequestSegmentContext.Language.set(string value)
I've been going through the IUpdateCurrentLanguage implementation today and found out that images and bundles (css/js) also pass through this interface and try to update the language. As they do not have a 2nd language or even a language at all, they are defaulted to the master language or even null. By filtering on the accept-types of the incoming request, we were able to isolate the language update when actual pages were loaded, instead of images and bundles. It works as it should now. Thank you very much for your help!
I noticed when using this approach on Ajax.BeginForm and using the OnBegin method sending accros the Accept-Language using JavaScript, that the ModelState doesn't have the right Culture. Is there a way to make sure that also the MVC ModelState is getting the right culture?
You mean that your ModelState error messages are in wrong language, Jeroen?
In your controller action, can you verify that both Thread.CurrentThread.CurrentCulture
and Thread.CurrentThread.CurrentUICulture
are set to the requested language?
@Stefan, That is set correctly already using that custom attribute approach you suggested, but it seems that the ModelState is already filled earlier in the request pipeline with the culture. Because when in the controller I clear ModelState and revalidate it again it respects the right language.
That sounds right, Jeroen. OnActionExecuting
seems to run just after the model binding and validation.
This is not a scenario I considered when implementing the solution. But you can probably amend it to fit in earlier in the pipeline.
this is nice overview of mvc pipeline. we had the same problem back in days..
I implemented an IHttpModule and this logic seems to do the trick. The Ajax requests are still working and there is no need for a custom filter attribute on controller level. Also the ModelState is now using the correct language. The code I've used below:
public class LocalizationHttpModule : IHttpModule
{
private readonly Injected<EPiServer.Configuration.Settings> _settings;
private readonly Injected<IUpdateCurrentLanguage> _updateCurrentLanguage;
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(context_BeginRequest);
}
private void context_BeginRequest(object sender, EventArgs e)
{
HttpRequest request = ((HttpApplication)sender)?.Request;
if (_settings.Service.PageUseBrowserLanguagePreferences)
{
// If the PageUseBrowserLanguagePreferences setting is true, this is already handled everywhere.
return;
}
if (request == null)
{
return;
}
HttpRequestBase httpRequest = new HttpRequestWrapper(request);
if (!httpRequest.IsAjaxRequest())
{
return;
}
string[] acceptedLanguages = httpRequest.UserLanguages;
if (acceptedLanguages == null || acceptedLanguages.Length == 0)
{
return;
}
var languagePreferenceList = new LanguagePreferenceList();
languagePreferenceList.ConditionalAddRange(acceptedLanguages);
var culture = ContentLanguage.Instance.DetermineCulture(languagePreferenceList);
_updateCurrentLanguage.Service.UpdateLanguage(culture.Name);
ContentLanguage.Instance.SetCulture(culture.Name);
UserInterfaceLanguage.Instance.SetCulture(culture.Name);
}
}
Hi,
Because of filter functionality we have created a form that is posted asynchronously to retrieve all items that match the given criteria. This is done using regular MVC controllers, but this raises a problem.
Upon entering the controller, the current culture and the PreferredCulture of ContentLanguage is reset to the master language. Multiple languages are available, so we've tried to force it to a different language. However, when one ContentArea is rendered (with Html.PropertyFor) the PreferredCulture of ContentLanguage was reset to the master language once more. The first block of the page has been rendered in the second language and the rest of the page in the master language.
I'd like to add that this only happens when the language is changed for this first time, or when the page is reloaded without cache (ctrl + f5).
Is there anyone who could explain why this is happening?
Kind regards,
Bram van der Zee