Ensuring the right AJAX content language using a filter attribute

When we implement AJAX interactions in a website, for asynchronous interactions between browser and server, such AJAX responses can also return content from the CMS.

Now, what if our EPiServer site is multilingual and we need to return texts (error messages, label texts, product descriptions) or images in the AJAX responses? How can we make sure that the server returns content in the correct language?

Solution outline

When requesting webpages from EPiServer, figuring out which language to look up is already taken care of by the routing engine of the CMS. Usually the path to the page is prepended with a language code, or the page could be located on a subdomain (read about localization on EPiServer World).

When making AJAX request we should not implement and call localized URLs to AJAX endpoints. And it should not be necessary to embed language codes in the URLs, since the HTTP protocol has other built-in means to request specific languages.

It is not impossible, though, to add a language parameter to an MVC or a WebAPI route. That parameter could then be used to get content in that specific language. But I think there is a much nicer solution than that. One that utilizes the HTTP headers and controls the language generically for all AJAX requests.

I propose implementing a generic ActionFilterAttribute to make MVC and WebAPI set the requested language in the EPiServer context. A solution that lies outside of specific controllers. This way we can encapsulate the relatively simple logic into class, thereby eliminating repeated implementation of the same logic.

The logic should be like this:

  1. Check if EPiServer is configured to consider the Accept-Language header, indicating the browser's preferred content language. This is not enabled in EPiServer by default, as it can override the language routing (where the URL contains a language indication). But if it is enabled, skip our logic.
  2. Ensure that the current request is an AJAX request. If the browser requests a regular page, skip our logic and let EPiServer control it.
  3. Ensure that the browser has indicated at least one language code In the Accept-Language header. If not, skip our logic and let EPiServer use the master language.
  4. Pass the list of all accepted/preferred language codes to EPiServer and let it decide from the top of the list which language can be supported. Set the content language of the current request to that specific language.
  5. In MVC or API controllers: get the content items like usual. 

On the frontend side, we need to pass the language of the current page to the AJAX service, overriding the browser's list of preferred languages. Otherwise, the server could potentially send page and AJAX content in different languages. We want to serve AJAX content in the exact same language branch as the page was served in.

Implementation

The following is my code showing an implementation of the beforementioned solution. Add it as an attribute to a base class or to specific implementation classes, to invoke the logic at run-time.

This first class is a version for ASP.Net MVC:

And this class is a version for ASP.Net WebAPI: 

Now switch to the frontend side of the solution. To make the language code of the current page available for any frontend AJAX logic, store it in the HTML markup. Simply store the language of the current page in the root HTML tag like the following (this is generally a good SEO practice): 

Then in the JavaScript doing the AJAX call, look up the language code and add it to the collection of headers. Here is an example: