Setting up culture detection

A critical step when setting up multilingual projects is to ensure that the system knows the correct content culture when processing page requests.

Culture detection greatly depends on the routing scheme of your website:

  • If your site uses content tree-based routing, the system automatically detects and sets the current culture for each request.
  • If your site uses conventional ASP.NET routing, you need to set up and handle the functionality for detecting and setting the current culture manually.

With culture detection set up correctly, you can assign cultures to your site in Xperience, and localize the content displayed on the site’s pages. Finally, content editors can then edit and translate the content of individual pages.

Projects with content tree-based routing

Once you have enabled and set up content tree-based routing, the functionality for detecting and setting the current culture for each request is handled automatically by the system.

Once you have enabled and set up content tree-based routing, the current culture for each request is detected and set automatically. The only requirement is for the system’s localization middleware to be registered in your project’s middleware pipeline. The middleware is registered as part of the UseKentico call, which should always be present in your application’s pipeline.

Application startup class



public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // Registers the localization middleware
    app.UseKentico();
    ...
}


By default, the middleware ensures the correct culture for requests handled by the system (preview URLs, content tree-based routing, form and page builder features).

You can then:

In addition to the routing handled by the system, you can also set up routing for pages not represented in the content tree by registering routes using conventional routing provided by ASP.NET. That is, pages not matched by the system router can always be matched by custom routes.

For more information about how to set up custom routing, see Combining content tree-based and ASP.NET routing and the section below.

Projects with custom routing

To serve content in multiple languages on sites that use a custom routing scheme (i.e., conventional ASP.NET routing) or for features that you define manually in your live site application, you need to set up functionality that detects and sets the current culture for each request.

Register and configure the .NET framework’s Request culture providers, which tell the system where to search for the culture under which the current request should be served.

Add culture providers via KenticoRequestLocalizationOptions – the class behaves identically to RequestLocalizationOptions provided by ASP.NET Core, but hides configuration handled for the Core application by Xperience. For example, you do not need to provide the list of supported application and UI cultures at compile-time, since you control UI cultures from the connected Xperience administration application.

For example, the following code snippet registers a RouteDataRequestCultureProvider that looks for the culture parameter in a request’s “culture” route data parameter.

Application startup class



public void ConfigureServices(IServiceCollection services)
{
    services.Configure<KenticoRequestLocalizationOptions>(options =>
    {
        // Registers a culture provider that sets the request culture from the "culture" route data parameter
        // (e.g., for routes in the following format: {culture}/{controller}/{action})
        options.RequestCultureProviders.Add(new RouteDataRequestCultureProvider
        {
            RouteDataStringKey = "culture",
            UIRouteDataStringKey = "culture"
        });
    });

    ...
}


At the beginning of every request, you need to:

  1. Retrieve or determine the correct culture to be used in the current request.

    • The way you detect the culture depends on the implementation of your multilingual site. For example, you can use language prefixes in URLs, culture-specific domains, or custom cookies.
  2. Set the Thread.CurrentThread.CurrentUICulture and Thread.CurrentThread.CurrentCulture properties of the current thread (available in the System.Threading namespace).

    If you detect culture using language prefixes in URLs or using culture-specific domains, we recommend using the SiteCultureConstraint (Kentico.Content.Web.Mvc namespace) provided by the Xperience API. The constraint supplies the culture for the current thread automatically based on the following process:

    • detect and validate the culture against site cultures,
    • determine default culture according to the following priorities (first that applies):
    • set the CurrentUICulture and CurrentCulture properties of the current thread.Moreover, the constraint allows you to hide the language prefix in URLs for the default content culture:
    1. Set the constraint’s HideLanguagePrefixForDefaultCulture property to true.
    2. Register a custom route without a culture parameter in the URL pattern for the default culture URLs.
    3. Manually set the CurrentUICulture and CurrentCulture properties for the custom route.See the example below.

Note: Thread.CurrentThread.CurrentUICulture and Thread.CurrentThread.CurrentCulture are properties of the .NET framework and use the System.Globalization.CultureInfo type. They are not directly comparable with the CMS.Localization.CultureInfo type, or the CurrentUICulture and CurrentCulture properties of the CMS.Localization.LocalizationContext class provided by the Xperience API.

The Xperience localization API then automatically works with the given culture (for example when resolving resource strings).

You can now:

Example

One common way to determine the culture of requests is to use language prefixes in your site’s routes. For example:

  • English – www.example.com/en-us/path
  • Spanish – www.example.com/es-es/path

This example shows how to parse the culture from the route prefix and set the current culture for the MVC application using the system constraint SiteCultureConstraint. It also demonstrates how to hide language prefixes in URLs for the site’s default content culture (e.g. if English is set as the default culture, the URL www.example.com/en-us/path becomes www.example.com/path).

Example - RouteConfig.cs



using System.Web.Mvc;
using System.Web.Routing;

public static void RegisterRoutes(RouteCollection routes)
{
    ...

    // Matches a URL containing a culture route prefix
    routes.MapRoute(
        name: "Default",
        url: "{culture}/{controller}/{action}",
        defaults: new { controller = "Home", action = "Index" },
        constraints: new { culture = new SiteCultureConstraint() { HideLanguagePrefixForDefaultCulture = true} }
    );

    // Matches a URL with a hidden culture prefix
    // This route needs to be registered AFTER the route which strips the culture parameter from the URL ("Default" in this example)
    routes.MapRoute(
        name: "DefaultWithoutCulturePrefix",
        url: "{controller}/{action}",
        defaults: new { },
        constraints: new { culture = new SetDefaultCultureConstraint() }
     );

}



Example - SetDefaultCultureConstraint



using System.Globalization;
using System.Threading;
using System.Web;
using System.Web.Routing;

using CMS.Helpers;
using CMS.SiteProvider;

// Constraint for a route that parses URLs with a hidden culture prefix for the default culture
public class SetDefaultCultureConstraint : IRouteConstraint
{
    // Propagates default culture into the current thread
    public bool Match(HttpContextBase httpContext, 
                    Route route, 
                    string parameterName, 
                    RouteValueDictionary values, 
                    RouteDirection routeDirection)
    {
        var defaultCultureCode = CultureHelper.GetDefaultCultureCode(SiteContext.CurrentSiteName);
        var culture = new CultureInfo(defaultCultureCode);

        Thread.CurrentThread.CurrentUICulture = culture;
        Thread.CurrentThread.CurrentCulture = culture;

        return true; 
    }
}



You can use the SiteCultureConstraintconstraint equally for when you detect cultures using culture-specific domains. When the constraint parameter (“culture” in this case) is not found in the URL scheme, the SiteCultureConstraintensures the culture will be detected from the URL’s domain.

SiteCultureConstraint with culture-specific domains



...

    var route = routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}",
        defaults: new { controller = "Home", action = "Index" },
        constraints: new { culture = new SiteCultureConstraint() }
    );

...