- Modern Web Development with ASP.NET Core 3
- Ricardo Peres
- 1134字
- 2021-06-18 18:36:02
Globalization and localization
If you need to build an application that will be used by people in different countries, you may want to have all of it, or at least parts of it, translated. It's not just that, though: you may also want to have decimal numbers and currency symbols presented in a way that users would expect. The process by which an application is made to support different cultures is called globalization, and localization is the process of adapting it to a specific culture—for example, by presenting it with text in a specific language.
ASP.NET Core, like previous versions, fully supports these two entwined concepts by applying a specific culture to a request and letting it flow, and by having the ability to serve string resources according to the language of the requester.
We first need to add support for globalization and localization, and we do this by adding the Microsoft.AspNetCore.Localization.Routing package to the project. As far as this chapter is concerned, we want to be able to do the following:
- Set the culture for the current request
- Hand resource strings that match the current culture
Let's configure localization in the ConfigureServices method with a call to AddLocalization. We'll pick the Resources folder as the source for resource files, as we'll see in a minute:
services.AddLocalization(options =>
{
options.ResourcesPath = "Resources";
});
We create this Resources folder and inside it, we create a Controllers folder. Using Visual Studio, let's also create two resource files, one called HomeController.en.resx and the other called HomeController.pt.resx. The resx extension is a standard extension for resource files that are basically XML files containing key–value pairs. On each of these files, add an entry with the key Hello and the following value:

It should look like the following screenshot. Note that each file has the name of the controller class plus a two-letter culture identifier:
Now, let's define a range of cultures and languages to support. To make it simple, let's say that we will support Portuguese (pt) and English (en):
var supportedCultures = new List<CultureInfo>
{
new CultureInfo("pt"),
new CultureInfo("en")
};
We then configure RequestLocalizationOptions in order to have a default language:
services.Configure<RequestLocalizationOptions>(options =>
{
options.DefaultRequestCulture =
new RequestCulture(supportedCultures.First().Name,
supportedCultures.First().Name);
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
options.RequestCultureProviders = new[] {
new AcceptLanguageHeaderRequestCultureProvider { Options =
options } };
});
The process by which a culture is obtained from the browser is based upon a provider model. The following providers are available:
- AcceptLanguageHeaderRequestCultureProvider gets the culture from the Accept-Language header.
- CookieRequestCultureProvider gets the culture from a cookie.
- QueryStringRequestCultureProvider gets the culture from a query string parameter.
- RouteDataRequestCultureProvider gets the culture from a route parameter.
Just replace the RequestCultureProviders assignments in the previous code with the ones you want. As you can see, there are many options available, each featuring the different features that you need to set, such as the cookie name, the query string parameter, the route parameter name, and so on:
new CookieRequestCultureProvider { CookieName = "culture" }
new QueryStringRequestCultureProvider { QueryStringKey = "culture" }
new RouteDataRequestCultureProvider { RouteDataStringKey = "culture" }
In the second chapter, we looked at route constraints, so here we will introduce the culture route constraint:
public sealed class CultureRouteConstraint : IRouteConstraint
{
public const string CultureKey = "culture";
public bool Match(
HttpContext httpContext,
IRouter route,
string routeKey,
RouteValueDictionary values,
RouteDirection routeDirection)
{
if ((!values.ContainsKey(CultureKey)) || (values
[CultureKey] == null))
{
return false;
}
var lang = values[CultureKey].ToString();
var requestLocalizationOptions = httpContext
.RequestServices
.GetRequiredService<IOptions<RequestLocalization
Options>>();
if ((requestLocalizationOptions.Value.SupportedCultures
== null)
|| (requestLocalizationOptions.Value.SupportedCultures.
Count == 0))
{
try
{
new System.Globalization.CultureInfo(lang);
//if invalid, throws an exception
return true;
}
catch
{
//an invalid culture was supplied
return false;
}
}
//checks if any of the configured supported cultures matches the
//one requested
return requestLocalizationOptions.Value.SupportedCultures
.Any(culture => culture.Name.Equals(lang, StringComparison
.CurrentCultureIgnoreCase));
}
}
The Match method only operates if there is a value specified for the culture key; if so, it extracts its value and checks the RequestLocalizationOptions to see if it is a supported culture or if it is a valid one. Essentially, what this does is allow the verification of route values, such as {language:culture}, and if the value is not a valid culture, you will get an exception. This route constraint needs to be registered before it can be used, as follows:
services.Configure<RouteOptions>(options =>
{
options.ConstraintMap.Add(CultureRouteConstraint.CultureKey, typeof
(CultureRouteConstraint));
});
Now, we want our controller to respond to the browser's language settings. For example, in Chrome, we will configure this in Settings | Languages | Language and input settings:
What this setting does is configure the Accept-Language HTTP header that the browser will send upon each request. We are going to take advantage of this to decide what language we will present.
Each controller that we wish to make localization-aware needs to be changed as follows:
- Add a middleware filter attribute in order to inject a middleware component.
- Inject a string localizer that we can use to fetch appropriately translated resources.
Here is what that should look like:
[MiddlewareFilter(typeof(LocalizationPipeline))]
public class HomeController
{
private readonly IStringLocalizer<HomeController> _localizer;
public HomeController(IStringLocalizer<HomeController> localizer)
{
this._localizer = localizer;
}
}
The LocalizationPipeline is actually an OWIN middleware component, and should look as follows:
public class LocalizationPipeline
{
public static void Configure(
IApplicationBuilder app,
IOptions<RequestLocalizationOptions> options)
{
app.UseRequestLocalization(options.Value);
}
}
Now, if we want to access a specific resource in a culture-specific way, all we need to do is the following:
var hello = this._localizer["Hello"];
The returned string will come from the right resource file, based on the current culture, as originated from the browser. You can check this by looking at the CultureInfo.CurrentCulture and CultureInfo.CurrentUICulture properties.
There are a couple of final things to note:
- You can have several resource files per language, or more accurately, per specific (for example, en, pt) and generic language (for example, en-gb, en-us); if the browser requests a specific language (for example, en-gb, en-us), then the localizer will try to find a resource file with that as a suffix, and if it cannot find one, it will try the generic language (for example, en). If this also fails, it will return the resource key provided (for example, Hello)
- The localizer never returns an error or a null value, but you can check whether the value exists for the current language with the following:
var exists = this._localizer["Hello"].ResourceNotFound;
The topics discussed here are very important if you are going to implement sites that need to support multiple cultures or languages, but you should also consider using it if you would like to have the text in your site in files, such as resources, so that they can be easily edited and replaced.
- 深度實踐OpenStack:基于Python的OpenStack組件開發(fā)
- Java應用與實戰(zhàn)
- Python 深度學習
- C#完全自學教程
- 編程卓越之道(卷3):軟件工程化
- Python數(shù)據(jù)分析(第2版)
- Hands-On RESTful Web Services with Go
- Java應用開發(fā)技術實例教程
- Jupyter數(shù)據(jù)科學實戰(zhàn)
- 高級語言程序設計(C語言版):基于計算思維能力培養(yǎng)
- Android系統(tǒng)原理及開發(fā)要點詳解
- Node.js:來一打 C++ 擴展
- Learning JavaScript Data Structures and Algorithms(Second Edition)
- Python Machine Learning Blueprints:Intuitive data projects you can relate to
- Python物理建模初學者指南(第2版)