AspNetCore Internationalization Fundamentals

one-pagers csharp

Terms

  • Globalization - The process of making an app support different languages and regions.

  • Localization - The process of customizing a globalized app for specific languages and regions.

  • Internationalization(I18N) - Both globalization and localization.

  • Culture - A language and, optionally, a region.

  • Neutral Culture - A culture that has a specified language, but not a region (for example "en", "es").

  • Specific Culture - A culture that has a specified language and region (for example, "en-US", "en-GB", "es-CL").

  • Parent Culture - The neutral culture that contains a specific culture (for example, "en" is the parent culture of "en-US" and "en-GB").

IStringLocalizer

  • An interface with an indexer and an IEnumerable for returning localized strings.

  • It does not require storing the default language strings in a resource file.

  • Useful when you are early in development and you do not need to create resource files.

  • Code Sample

IHtmlLocalizer

  • Useful for resources that contain HTML.

  • It HTML-encodes arguments that are formatted in the resource string,

  • Code Sample

IStringLocalizerFactory

  • At the lowest level, IStringLocalizerFactory can be retrieved from the DI framework.

  • Code Sample

Shared Resources

  • You can partition your localized strings by controller or area, or have just one container.

  • Code Sample

IViewLocalizer

  • It provides localized strings for a view.

  • It finds the resource location from the view file path.

  • It finds the resource file based on the view’s file name.

  • No option to use a global shared resource file.

  • It implements the localizer using IHtmlLocalizer. It does not html-encode the localized string.

  • It html encodes the parameters and not the resource string itself.

  • To use a shared resource file in a view, inject IHtmlLocalizer<T>.

  • Code Sample

DataAnnotations Localization

  • DataAnnotations error messages are localized with IStringLocalizer<T>.

  • You can store the error messages in a ResourcesPath as shown in the code sample.

  • A shared resource can also be used for DataAnnotations error message.

        services.AddMvc()
            .AddDataAnnotationsLocalization(options => {
                options.DataAnnotationLocalizerProvider = (type, factory) =>
                    factory.Create(typeof(SharedResource));
            });

    With the above code, DataAnnotations only uses the SharedResource class for error messages rather than the resource for each class.

  • A SharedResource is the class corresponding to the .resx file where the validation messages are stored.

Configuring Localization Services

builder.Services.AddLocalization(options => options.ResourcesPath = "Resources"); (1)

builder.Services.AddMvc()
    .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix) (2)
    .AddDataAnnotationsLocalization();  (3)
1 Adds the localization services to the services container, including implementations of IStringLocalizer<T> and IStringLocalizerFactory.
2 Support for localized view files. In this sample, view localization is based on the view suffix file. Eg- Index.fr.cshtml.
3 Adds support for localized DataAnnotations validation messages through IStringLocalizer abstractions.

SupportedCultures and SupportedUICultures

  • AspNetCore has two collections of culture values - SupportedCultures and SupportedUICultures.

    • SupportedCultures

      • Determines the results of culture-dependent functions - date, time, number and currency formatting.

      • Determines the sorting order of text, casing-conventions, and string comparisons.

    • SuoportedUICultures

      • Determines which translated strings(from .resx files) are looked up by the ResourceManager.

      • The ResourceManager uses the CurrentUICulture object to lookup culture-specific string.

  • Every thread in .NET has CurrentCulture and CurrentUICulture objects.

  • More details

Resource Files

  • A resource file helps separate localizable strings from code.

  • Non-default language strings are isolated in .resx resource files.

  • Welcome.es.resx is the file containing translated strings for Spanish.

  • More details

RootNamespace Behaviour

  • If the root namespace of an assembly is different than the assembly name.

    • Localization does not work by default.

    • It fails because of the way resource files are searched for within the assembly.

  • If the RootNamespace is different from the AssemblyName, include the following in Assembly.cs

    using System.Reflection;
    using Microsoft.Extensions.Localization;
    
    [assembly: ResourceLocation("Resource Folder Name")]
    [assembly: RootNamespace("App Root Namespace")]
  • More details

Culture fallback behaviour

  • This means that if a request comes in for es-gb and the site does not have a resource file for es-gb, it fallsback to the parent culture which is es and serves the request.

  • More details

Configure Middleware

  • The localization middleware must be configured before any middleware that might check the request culture. For example, app.UseMvcWithDefaultRoute().

    builder.Services.Configure<RequestLocalizationOptions>(options =>
    {
        var supportedCultures = new[] { "en-US", "fr" };
        options.SetDefaultCulture(supportedCultures[0])
            .AddSupportedCultures(supportedCultures)
            .AddSupportedUICultures(supportedCultures);
    });
  • UseRequestLocalization intializes a RequestLocalizationOptions object.

  • On every request, a list of RequestCultureProviders in the RequestLocalizationOptions is checked to determine the culture to be used by the request.

  • More details

RequestCultureProviders

  • QueryStringRequestCultureProvider`

    • Some apps use a query string to set the CultureInfo.

    • It is the first localization provider checked.

    • Pass the query string parameters culture and ui-culture.

    • If only one value is passed, both Culture and UICulture use that same value.

  • CookieRequestCultureProvider

    • Some apps set the culture with the aspnetcore culture cookie.

    • The default cookie name is .AspNetCore.Culture.

    • The cookie format is c=en-UK|uic=en-US.

  • AcceptLanguageHeaderRequestCultureProvider

    • This setting indicates what the browser has been set to send or has inherited from the OS.

    • A production app should include a way for a user to customize their choice of culture.

A CustomProvider using CustomRequestCultureProvider

  • Suppose you want to let your customers store their language and culture in your databases.

  • You could write a provider to look up these values for the user.

  • Use RequestLocalizationOptions to add or remove localization providers.

    private const string enUSCulture = "en-US";
    
    services.Configure<RequestLocalizationOptions>(options =>
    {
        var supportedCultures = new[]
        {
            new CultureInfo(enUSCulture),
            new CultureInfo("fr")
        };
    
        options.DefaultRequestCulture = new RequestCulture(culture: enUSCulture, uiCulture: enUSCulture);
        options.SupportedCultures = supportedCultures;
        options.SupportedUICultures = supportedCultures;
    
        options.AddInitialRequestCultureProvider(new CustomRequestCultureProvider(async context =>
        {
            // Custom request culture logic
            return await Task.FromResult(new ProviderCultureResult("en"));
        }));
    });

A CustomProvider using RequestCultureProvider

  • A new implementation of RequestCultureProvider can be created to determine the request culture information from a custom source.

  • Code Sample

Change the Call Order of CultureProvider

  • RequestLocalizationOptions has three default request culture providers and they are called in the following order:

    1. QueryStringRequestCultureProvider

    2. CookieRequestCultureProvider

    3. AcceptLanguageHeaderRequestCultureProvider

  • The following code helps change the order. CookieRequestCultureProvider is moved to the top and QueryStringRequestCultureProvider is moved to second spot.

    app.UseRequestLocalization(options =>
        {
            var questStringCultureProvider = options.RequestCultureProviders[0];
            options.RequestCultureProviders.RemoveAt(0);
            options.RequestCultureProviders.Insert(1, questStringCultureProvider);
        });
  • A custom provider via AddInitialRequestCultureProvider moves to the top, by default.

Set the Culture Programmatically

  • Setting the culture cookie.

    [HttpPost]
    public IActionResult SetLanguage(string culture, string returnUrl)
    {
        Response.Cookies.Append(
            CookieRequestCultureProvider.DefaultCookieName,
            CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)),
            new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) }
        );
    
        return LocalRedirect(returnUrl);
    }
  • Code Sample

What is a PO file?

  • PO files are distributed as text files containing the translated strings for a given language.

  • Benefits of using PO instead of resx:

    • Support pluralization, resx do not support pluralization

    • Not compiled like resx. So no special tooling or build step required.

    • Work well with collaborative online editing tools.

Sample - fr.po
#: Pages/Index.cshtml:13
msgid "Hello world!"
msgstr "Bonjour le monde!"

msgid "There is one item."
msgid_plural "There are {0} items."
msgstr[0] "Il y a un élément."
msgstr[1] "Il y a {0} éléments."
  1. # is a comment indicating the context of the string to be translated.

  2. msgid - The untranslated string.

  3. msgstr - The transalated string.

  4. msgid_plural - The untranslated plural string.

  5. msgstr[0] - The translated string for the case 0.

  6. msgstr[1] - The translated string for the case N.

Configuring PO for AspNetCore

  1. Reference the package:

    <PackageReference Include="OrchardCore.Localization.Core" Version="1.5.0" />

  2. Register the service:

    builder.Services
        .AddRazorPages()
        .AddViewLocalization();
  3. Create the po file with <culture code>.po in the application root folder.

    msgid "Hello world!"
    msgstr "Bonjour le monde!"
  4. Add razor page

    @inject IViewLocalizer Localizer
    <p>@Localizer["Hello world!"]</p>

Change PO file location

Localization Resources

  • AspNetCore provides ResourceManagerStringLocalizer.

  • ResourceManagerStringLocalizer is an implementation of IStringLocalizer that uses resx to store localization resources.

  • By implementing, IStringLocalizer any data source can be used.

  • The following example projects implement IStringLocalizer:-

References