Bits of Xperience: Localizing XHR Requests in Kentico Xperience 13.0
Tags: xperience, kentico, aspnetcore, dotnet
Published: 2021-01-18 15:13:43

---
title: Bits of Xperience: Localizing XHR Requests in Kentico Xperience 13.0
published: true
description: Learn how to pass the Page's culture to any XHR requests it makes back to your Kentico Xperience API
tags: xperience, kentico, aspnetcore, dotnet
series: Bits of Xperience
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/91a47vt9de7x1jkcfrl8.jpg
---

Application and content localization can be tricky, but fortunately, Kentico Xperience 13.0 makes it easy 🤗!

Using Xperience's [Content Tree-based routing](https://docs.xperience.io/multilingual-websites/setting-up-multilingual-websites/configuring-urls-for-multilingual-websites), we let the platform generate our URLs, inserting the culture code as part of the path.

When our application handles a localized URL, assuming we use the `CultureInfo.CurrentCulture` (or more likely `IPageRetriever`) to retrieve our Page's content, we'll be sure display the localized content 💪🏿!

In this post I want to talk about a different scenario...

How do we make sure that any XHR requests made to an API, running in our Xperience application, are also localized, working with and returning the correct content for the current page 🤔?

## 📚 What Will We Learn?

- What is Localization?
- How does Xperience set the Page's request culture?
- How do our custom API endpoints find the request culture?
- What tools can we use to pass the Page's culture on to the API requests?

## 🌍 What is Localization?

Good ol' [Wikipedia](https://en.wikipedia.org/wiki/Language_localisation) says that localization "is the process of adapting a product's translation to a specific country or region."

When we "localize" a response to an HTTP request for a web page, we are making sure that the content returned with that response is correct for the country or region associated with that request. This is often simplified to be the language associated with the request.

> For the rest of this post I'm going to use this simplified definition of localization and refer to the language, but there are plenty of other bits of content that need to be localized, like [number](https://en.wikipedia.org/wiki/Decimal_separator) and [date](https://en.wikipedia.org/wiki/Date_format_by_country) formatting.

> Also, the [.NET docs](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization?view=aspnetcore-5.0#globalization-and-localization-terms-2) have a great discussion on Globalization (building an app so it can support multiple languages) and Localization (adding support for a specific language(s)).

## 🔍 How does Xperience Figure Out a Request's Culture?

Xperience, like any globalized platform/website, needs to decide on a culture by looking at the incoming HTTP request, because that culture determines the localization of the Page.

There's a lot of places this information could be stored, but the most common are cookies 🍪 or the URL itself.

Xperience's Content Tree-based routing let's developers pick from 2 options:

- Using the host: `https://site.es/products` - Spanish language top level domain
- Using the path: `https://site.com/es-MX/products` - Mexican Spanish language path prefix

The scenario I'm interested in is the latter because it supports having 2 cultures on the same domain.

---

When looking for the culture in the URL path, Xperience uses route constraints and tokens to pull the culture value out. It has a specific URL pattern, using the path prefix (`https://stuff/en-US/more-stuff`) which makes it easy to pinpoint the culture 🧐.

In the Xperience ASP.NET Core integration, Xperience has some [middleware](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/write?view=aspnetcore-5.0) 🤓 that looks at the incoming request context and figures out what culture to associate with it (based on the [settings](https://docs.xperience.io/multilingual-websites/setting-up-multilingual-websites/configuring-urls-for-multilingual-websites) in the application).

Once it figures out the culture for the request URL, it then sets the culture for the thread/context:

```csharp
CultureInfo.CurrentCulture = requestCulture.Culture;
CultureInfo.CurrentUICulture = requestCulture.UICulture;
```

## 🧭 Finding the Request Culture in API Endpoints

When routing to an ASP.NET Controller or View associated with a Page in the content tree, Xperience does all the traffic management.

But our custom API endpoints aren't associated with anything in the content tree, which means Xperience is hands off 👐!

---

Let's assume we have a content tree route to a Product Page that is handled by Xperience and we are visiting the (Mexican) Spanish language version of that Page:

```bash
GET https://site.com/es-MX/product/apples
```

We rendered the product id for "apples" into the Razor View for this page, and we use that value to make an XHR request back to our API:

```html
@model ProductViewModel

<h1>@Model.Name</h1> @* This will be "Manzanas" *@

<script>
    const productId = @Model.ProductId;

    fetch(`/api/products/${productId}`)
        .then(resp => resp.json())
        .then(resp => console.log(resp));
</script>
```

Here's the code that makes up our `ProductsApiController`:

```csharp
[ApiController]
[Route("api/[controller]")]
public class ProductsApiController : ControllerBase
{
    [HttpGet("{productId:int}")]
    public ActionResult GetProduct(int productId)
    {
        var culture = CultureInfo.CurrentCulture.Name;

        // ...
    }
}
```

Our original Razor View and HTML document were rendered using the Spanish language content and the URL this page is loaded from has the `/es-MX` path prefix.

When this `GetProduct` method is called from the XHR made by our JavaScript `fetch()` request, what will the value of `culture` be 🤔😕?

...

It's a trick question 🤦🏽‍♂️! I haven't told you what the [default culture](https://docs.xperience.io/multilingual-websites/setting-up-multilingual-websites/setting-default-languages-for-users-and-visitors) is for our site.

Since there's no culture code in the XHR request's URL, Xperience will associate it with whatever the default culture is for the site that the domain is associated with 😮.

In my specific use-case, it's English. This means that the `culture` variable will have the value `en-US`.

How can we get that to be `es-MX` so that any data we retrieve from the Xperience database and return from our API endpoint will be localized to the Page's culture 🤷🏼‍♀️?

## 🤝🏻🤝🤝🏾 Passing the Culture

There are many ways to pass the culture information in a URL and they would all work - really all we need to do is get the culture code to be a parameter (probably query string) that our `GetProduct()` method can directly access.

Then we can the `CultureInfo` constructor and the code pattern that Xperience already uses to set the culture on the current thread 👍🏾:

```csharp
public ActionResult GetProduct(int productId, [FromQuery] string culture)
{
    var cultureInfo = CultureInfo.GetCultureInfo(culture);

    CultureInfo.CurrentCulture = cultureInfo;
    CultureInfo.CurrentUICulture = cultureInfo;

    // ...
}
```

The problem with this approach is we are now cluttering up our API action method with assigning the thread culture and we have this weird query string parameter we have to remember to include in all our other API requests and action methods.

> We also don't have access to the correct culture until our Controller action method executes, which means Action filters and anything else that executes before the action are using our default culture 😣.

A better approach is to use the same feature that Xperience relies on for parsing out and assigning a culture to a Page's HTTP request.

We just need to make sure our API URL includes a special `{culture}` token in the route definition:

```csharp
[ApiController]
[Route("api/{culture}/[controller]")] // <- new route token
public class ProductsApiController : ControllerBase
{
    [HttpGet("{productId:int}")]
    public ActionResult GetProduct(int productId)
    {
        var culture = CultureInfo.CurrentCulture.Name;

        // ^ culture will have the correct value now
    }
}
```

And, then include that in the `fetch()` request URL (we will source the culture value from `CultureInfo.CurrentCulture.Name` which is correctly set when rendering our Page):

```html
<script>
    const productId = @Model.ProductId;
    const culture = '@CultureInfo.CurrentCulture.Name';

    fetch(`/api/${culture}/products/${productId}`)
        .then(resp => resp.json())
        .then(resp => console.log(resp));
</script>
```

I think this is a much nicer solution 👏🏽!

Our API URLs match the same culture path prefix pattern that our Page URLs have and we don't have to worry about extra culture related values and logic in our API action methods 👍🏼.

## 🧠 Conclusion

Globalization and localization are important features when they're needed, but they tend to create new problems that need solutions, especially if we're used to not worrying about other languages (like me!).

Kentico Xperience supports localization as a first class feature and, for the most part, handles it all for us 😉.

However, when we start to come up with clever technical solutions for our application requirements, we need to be sure to carry all the existing features forward.

Fortunately, culture-aware XHR requests can be enabled in an elegant and simple way with Kentico Xperience 13.0 sites.

As always, thanks for reading 🙏!

## References

- [.NET Documentation on Localization and Globalization](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization)
- [Xperience 13.0 Documentation on Setting up Multilingual Websites](https://docs.xperience.io/multilingual-websites/setting-up-multilingual-websites/)

---

We've put together a list over on [Kentico's GitHub account](https://github.com/Kentico/Home/blob/master/RESOURCES.md) of developer resources. Go check it out!

If you are looking for additional Kentico content, checkout the Kentico or Xperience tags here on DEV.

{% tag kentico %}

{% tag xperience %}

Or my [Kentico Xperience blog series](https://dev.to/seangwright/series), like:

- [Kentico Xperience Xplorations](https://dev.to/seangwright/series/8185)
- [Kentico Xperience MVC Widget Experiments](https://dev.to/seangwright/series/9483)
- [Kentico 12 - Design Patterns](https://dev.to/seangwright/series/1046)