Back to all posts

Real World Localization Implementation ASP.NET Core 5

Posted on Dec 30, 2020

Posted in category:
Development
ASP.NET

As with many other concepts in the ASP.NET Core world, expansive documentation exists regarding Globalization & Localization within applications. However, the documentation is in the form of a user's guide and a simple implementation example. Rarely do our final projects end up matching the single-project solution structures that are used as examples. After recently setting up a new project to support localization, I thought I would share a more "real-world" example from the implementation.

The Goal

The goal is simple, we have a multi-project ASP.NET Core application that needs to support localization. We are primarily concerned with the following features.

  • Localization of DataAnnotations & Validations in View Models
  • Localization of static content in views
  • Localization of messages/information from controllers
  • Standard support for language switching/detection

Our Project Structure

For simplicity we will only look at the two application tiers involved in this example:

  • MyProject.Services - This contains View Models & Service Implementation
  • MyProject.Web - This contains the actual ASP.NET Core web project.

Although in this example we are working with two assemblies, in the end, we can repeat this pattern regardless of the number of assemblies or complexity of your project.

Enabling Localization

Before we update any of our existing View Models, or Views, we begin by configuring our application to support localization. This is completed with two small changes to the Startup.cs file within our web project.

Changes to ConfigureServices

Your configure services method might have a lot of complexity depending on the features you have enabled, however, our starting point is a default project setup, with MVC enabled and looks like the following.

Default ConfigureServices Method
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
}    

We need to add three statements to this method; the first enables Localization support and configures where to find the resources. The second and third statements configure the application to add localization support to our data models & views automatically.

Updated ConfigureServices Method
public void ConfigureServices(IServiceCollection services)
{
    services.AddLocalization(options => options.ResourcesPath = "Resources");

    services.AddControllersWithViews()
        .AddViewLocalization()
        .AddDataAnnotationsLocalization();
}
    

The order of these elements is important. The first statement's definition of a ResourcePath is the most critical for downstream processing. The path specified creates a pattern where resource files can be identified based on the object's fully qualified name within each assembly; more on this later.

The remaining two elements add the required elements to wire-up the needed components for automatic localization support.

Configure Method Changes

Next, we need to configure the application to enable language selection based on the incoming pipeline information, this is completed in the Configure method of Startup.cs. You should add this BEFORE any app.UseEndpoints() or other element that will need to be aware of the requested locale.

Additions to Startup.cs
//Setup what we will support
var supportedCultures = new[] { "en-US", "es" };
var localizationOptions = new RequestLocalizationOptions().SetDefaultCulture(supportedCultures[0])
    .AddSupportedCultures(supportedCultures)
    .AddSupportedUICultures(supportedCultures);
app.UseRequestLocalization(localizationOptions);
    

At this point, we have completed all necessary configuration elements, we can now start adding resource files to our projects and handling localization on a case-by-case basis.

Localizing ViewModels

In our theoretical sample project, we have a folder called "ViewModels" within the service project that contains a very simple Model

SimpleModel.cs
public class SimpleModel
{
    [Display(Name = "ApplicationName")]
    public string ApplicationName { get; set; }
}
    

As you can see from this snippet we have added a Display attribute with a name. All we need to do to support localization is to add a new Resources file for each of our languages in the format SimpleModel.{Language}.resx. This file should be created at the same nesting level, within a "Resources" folder inside of our current project for proper identification. This means our final tree structure will be.

  • Resources
    • ViewModels
      • SampleModel.en.resx
      • SampleModel.es.resx
  • ViewModels
    • SampleModel.cs

This is the critical pathway, we can continue to distribute our resources with our individual libraries if we follow this structure. Inside of the resource file, we can have a single key/value pair for the actual text, English example below.

Name Value
ApplicationName ApplicationName

This method is supported for data annotations used for display names, validators and similar without any additional support on your side.

Enable Localization of Views

Localization of views is even easier, you may simply add additional files alongside of any of your views using the same suffix format utilized for the resource files.

If you have a view "Sample.cshtml" you can create a Spanish version using "Sample.es.cshtml" and simply update with the proper information.

Language Detection?

Using default configuration as outlined in this posting your application will now support automatic language switching using one of three different methods.

  1. Querystring - Utilizing the culture & ui-culture query string parameters. (?culture=es_es for example)
  2. Cookie - Utilizing a default .AspNetCore.Culture. named cookie with c and/or uic values (c=es_es for example)
  3. Accept-Header - This will use the user's browser default language.

You will need to build into your application a language switcher or otherwise if desired. Often times, user preferences are used to store these values.

In Summary

Following these quick steps we setup our ASP.NET Core application to support localization. There are still further methods of customization and implementation, however, this gets off the ground running! For existing applications, it is important to note that any missing language values will fall-back gracefully to the end-user which lessens the burden of beginning the transition.