Overview of Globalization in ASP.NET Core
Globalisation is the preparation of an application to cater for different
languages based on the user's preference. Localisation is the process of adapting the site
content for different countries, regions or cultures. The starting point
to globalisation of a web application is being able to determine the language or culture for each
request. Following that, a mechanism is required to select the content based on
the current request culture. In this article, I look at the role that the
CultureInfo
class plays in localisation, and how to implement a view
component to enable users to select their preferred culture for requests.
The steps below add basic localisation to a Razor Pages application which is generated from the standard ASP.NET Core 3.0 Web Application template with no authentication configured. I called my app "Localisation". You may want to name yours differently. If you do, be careful with namespaces if you want to copy and paste code from this article.
-
Start by opening the Startup.cs file and adding the following
using
directives:using System.Globalization; using Microsoft.AspNetCore.Localization; using Microsoft.Extensions.Options;
-
Localisation is an opt-in feature. It is not enabled by default. Amend the
ConfigureServices
method to includeAddLocalization
, which makes the various supporting localisation services available to the dependency injection system. Then add the code to configureRequestLocalizationOptions
for the application.services.Configure<RequestLocalizationOptions>(options => { var supportedCultures = new[] { new CultureInfo("en"), new CultureInfo("de"), new CultureInfo("fr"), new CultureInfo("es"), new CultureInfo("ru"), new CultureInfo("ja"), new CultureInfo("ar"), new CultureInfo("zh"), new CultureInfo("en-GB") }; options.DefaultRequestCulture = new RequestCulture("en-GB"); options.SupportedCultures = supportedCultures; options.SupportedUICultures = supportedCultures; });
You need to specify the languages or cultures that you plan to support in your application. Cultures are represented in .NET by the
CultureInfo
class, which holds information about number and date formatting, calendars, writing systems, sort orders and other locale-specific matters. The overload of theCultureInfo
class constructor used here takes a string representing the name of the culture. Permitted values are ISO 639-1 codes that represent the language (e.g. "en" for English) optionally with an ISO 3166 subculture code that represents the country or dialect (e.g "en-GB" for Great Britain, or "en-ZA" for South Africa). In the example above a number of languages are supported, including one subculture - British English which has been set as the default culture. -
Now that the
RequestLocalizationOptions
have been configured, they can be applied to the request localization middleware, which needs to be added in theConfigure
method afterapp.UseRouting()
:app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); var localizationOptions = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>().Value; app.UseRequestLocalization(localizationOptions);
Setting the Request Culture
The request localization middleware makes use of components called RequestCultureProviders to determine the culture of the current request. Three of these are added by default:
QueryStringRequestCultureProvider
, which gets the culture from the query stringCookieRequestCultureProvider
, which gets the culture from a cookieAcceptHeadersRequestCultureProvider
, which gets the culture from the browser's Accept-Language header
The request culture providers are called one after the other until one is able to determine the culture for the request. It is also possible to create your own request culture provider, which I plan to look at in a future article. In the meantime, I will show how to create a View Component that enables the user to set the culture for the current request.
-
The first step is to create a folder named Models, and within that add a class file named CultureSwitcherModel.cs.
using System.Collections.Generic; using System.Globalization; namespace Localisation.Models { public class CultureSwitcherModel { public CultureInfo CurrentUICulture { get; set; } public List<CultureInfo> SupportedCultures { get; set; } } }
-
Add a folder named ViewComponents to the project, and within it, add a new C# class file named CultureSwitcherViewcomponent.cs. Then replace the content with the following code:
using Localisation.Models; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Localization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using System.Linq; namespace Localisation.ViewComponents { public class CultureSwitcherViewComponent : ViewComponent { private readonly IOptions<RequestLocalizationOptions> localizationOptions; public CultureSwitcherViewComponent(IOptions<RequestLocalizationOptions> localizationOptions) => this.localizationOptions = localizationOptions; public IViewComponentResult Invoke() { var cultureFeature = HttpContext.Features.Get<IRequestCultureFeature>(); var model = new CultureSwitcherModel { SupportedCultures = localizationOptions.Value.SupportedUICultures.ToList(), CurrentUICulture = cultureFeature.RequestCulture.UICulture }; return View(model); } } }
-
Add a new folder to the Pages folder and name it Components. Within that, add another folder named CultureSwitcher. Then add a Razor View to that named default.cshtml, and replace the existing content with the following:
@model CultureSwitcherModel <div> <form id="culture-switcher"> <select name="culture" id="culture-options"> <option></option> @foreach (var culture in Model.SupportedCultures) { <option value="@culture.Name" selected="@(Model.CurrentUICulture.Name == culture.Name)">@culture.DisplayName</option> } </select> </form> </div> <script> document.getElementById("culture-options").addEventListener("change", () => { document.getElementById("culture-switcher").submit(); }); </script>
The view component is a simple
select
element, populated with the supported cultures that were configured inStartup
. The form that it sits within uses the defaultget
method, which means that the submitted value will appear in the query string with a name ofculture
. TheQueryStringRequestCultureProvider
is designed to look for an item in the query string with a key ofculture
(and/orui-culture
).The
CurrentCulture
for the request determines the locale-specific formatting to be used for such things as dates and numbers. TheCurrentUICulture
is used to select the correct resource containing translated strings. I will look at how to use resource files for localising static content in the next article in the series. It is possible to set different values for theCurrentCulture
and theCurrentUICulture
, but more often it makes sense for both to have the same value. If only one is set (e.g via a single query string value), then the value is assigned to both properties. -
At this stage, you probably have some red wavy lines in the view that you just created, so open the _ViewImports.cshtml file and add the second and third
using
directives below, along with the last line that enables you to use a tag helper to render the view component:@using Localisation @using Localisation.Models @using System.Globalization @namespace Localisation.Pages @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @addTagHelper *, Localisation
-
Include the culture switcher view component within the layout page, using the tag helper approach as shown in the last line here.
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse"> <ul class="navbar-nav flex-grow-1"> <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a> </li> <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a> </li> </ul> </div> <vc:culture-switcher/>
-
Alter Index.cshtml to include the code in the code block and the HTML for the table that displays various bits of data:
@page @using Microsoft.AspNetCore.Localization @model IndexModel @{ ViewData["Title"] = "Home page"; var requestCultureFeature = HttpContext.Features.Get<IRequestCultureFeature>(); var requestCulture = requestCultureFeature.RequestCulture; } <div class="text-center"> <h1 class="display-4">Welcome</h1> <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p> <table class="table culture-table"> <tr> <td style="width:50%;">Culture</td> <td>@requestCulture.Culture.DisplayName {@requestCulture.Culture.Name}</td> </tr> <tr> <td>UI Culture</td> <td>@requestCulture.UICulture.Name</td> </tr> <tr> <td>UICulture Parent</td> <td>@requestCulture.UICulture.Parent</td> </tr> <tr> <td>Date</td> <td>@DateTime.Now.ToLongDateString()</td> </tr> <tr> <td>Currency</td> <td> @(12345.00.ToString("c")) </td> </tr> <tr> <td>Number</td> <td> @(123.45m.ToString("F2")) </td> </tr> </table> </div>
When you first run the application, the cuture for the request is the set by
the AcceptHeadersCultureRequestProvider
. When you use the the dropdown to select different cultures,
the culture is set by the QueryStringCultureRequestProvider
. Try adding a
ui-culture
key to the query string with a different value to the
culture
key (e.g. https://localhost:xxxxx/?culture=es&ui-culture=de
) to see
what effect that has.
Summary
This article is an introduction to localisation in Razor Pages. It covered configuration of the cultures that you wish to support in your application, and how to pass that configuration to localisation middleware. It shows how the culture for the current request works in terms of formatting content for display. It also shows how you can enable your visitors to change the culture to suit their preference.
You may have noticed that while names of days and months are automatically translated, based on the calendar associated with the current culture, other static content remains in English. In the next article, I will show how to use Resource files (.resx) to manage multiple translations for this type of content.