This article shows some of the ways in which localization can be used in a MVC 6 ASP.NET Core 1.0 application.
Code: https://github.com/damienbod/AspNet5Localization
20.11.2015: ASP.NET Core 1.0 rc1 version
Localization Setup
The localization is configured in the setup class and can be used throughout the application per dependency injection. The AddLocalization method is used in the ConfigureServices to define the resources and localization. This can then be used in the Configure method. Here, the RequestLocalizationOptions can be defined and is added to the stack using the UseRequestLocalization method. You can also define different options as required, for example the Accept-Header provider could be removed or a custom provider could be added.
using System.Collections.Generic; using System.Globalization; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Hosting; using Microsoft.AspNet.Localization; using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.Logging; namespace AspNet5Localization { public class Startup { public Startup(IHostingEnvironment env) { } public void ConfigureServices(IServiceCollection services) { services.AddLocalization(options => options.ResourcesPath = "Resources"); services.AddMvc() .AddViewLocalization() .AddDataAnnotationsLocalization(); services.AddScoped<LanguageActionFilter>(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.MinimumLevel = LogLevel.Information; loggerFactory.AddConsole(); loggerFactory.AddDebug(); var requestLocalizationOptions = new RequestLocalizationOptions { DefaultRequestCulture = new RequestCulture(new CultureInfo("en-US")), SupportedCultures = new List<CultureInfo> { new CultureInfo("en-US"), new CultureInfo("de-CH"), new CultureInfo("fr-CH"), new CultureInfo("it-CH") }, SupportedUICultures = new List<CultureInfo> { new CultureInfo("en-US"), new CultureInfo("de-CH"), new CultureInfo("fr-CH"), new CultureInfo("it-CH") } }; app.UseRequestLocalization(requestLocalizationOptions); app.UseIISPlatformHandler(); app.UseStaticFiles(); app.UseMvc(); } } }
The localization can be used for example in a MVC 6 controller. This is done by defining the IHtmlLocalizer with the name of your resx file(s). The resx files are defined in the folder defined in the Startup class AddLocalization method. The IHtmlLocalizer can then be used to return localized properties. A shared resource requires an emtpy class and special resource naming so that the magic string conventions work and the resource can be found. See the code for this.
using System.Globalization; using System.Threading; using AspNet5Localization.Resources; using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Mvc.Localization; namespace AspNet5Localization.Controllers { [Route("api/[controller]")] public class AboutController : Controller { private IHtmlLocalizer<AmazingResource> _htmlLocalizer; public AboutController(IHtmlLocalizer<AmazingResource> localizer) { _htmlLocalizer = localizer; } [HttpGet] public string Get() { return _htmlLocalizer["Name"]; } } }
Setting the culture in the Query
The culture required by the client application can be set in the query using the ?culture=de-CH.
To test this, the application needs to be started in the console due to a Visual Studio Tooling bug.
Open the application in the src folder and
dnu restore dnx web
Now the query localization can be tested or used as follows:
http://localhost:5000/api/About?culture=de-CH http://localhost:5000/api/About?culture=it-CH
Setting the culture in the Accept Header
The HTTP Request Accept-Language header can also be used to request from the server the required culture.
This is implemented as follows for the de-CH culture
GET http://localhost:5000/api/About HTTP/1.1 Accept: */* Accept-Language: de-CH Host: localhost:5000
Or implemented as follows for the it-CH culture
GET http://localhost:5000/api/About HTTP/1.1 Accept: */* Accept-Language: it-CH Host: localhost:5000
Setting the culture in the Request URL
The culture can also be set in the URL. This is not supported out of the box and you must implement this yourself for example using an action filter.
The action filter can be implemented as follows:
using System.Globalization; using Microsoft.AspNet.Mvc.Filters; using Microsoft.Framework.Logging; namespace AspNet5Localization { public class LanguageActionFilter : ActionFilterAttribute { private readonly ILogger _logger; public LanguageActionFilter(ILoggerFactory loggerFactory) { _logger = loggerFactory.CreateLogger("LanguageActionFilter"); } public override void OnActionExecuting(ActionExecutingContext context) { string culture = context.RouteData.Values["culture"].ToString(); _logger.LogInformation($"Setting the culture from the URL: {culture}"); #if DNX451 System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo(culture); System.Threading.Thread.CurrentThread.CurrentUICulture = new CultureInfo(culture); #else CultureInfo.CurrentCulture = new CultureInfo(culture); CultureInfo.CurrentUICulture = new CultureInfo(culture); #endif base.OnActionExecuting(context); } } }
The culture value is defined as route data and this is then used to set the culture.
The action filter is then registered in the Startup ConfigureServices method.
public void ConfigureServices(IServiceCollection services) { services.AddLocalization(options => options.ResourcesPath = "Resources"); services.AddMvc() .AddViewLocalization() .AddDataAnnotationsLocalization(); services.AddScoped<LanguageActionFilter>(); }
This can then be used in a controller using an attribute routing parameter and applying the action filter to the controller class.
using System.Globalization; using System.Threading; using AspNet5Localization.Resources; using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Mvc.Localization; namespace AspNet5Localization.Controllers { [ServiceFilter(typeof(LanguageActionFilter))] [Route("api/{culture}/[controller]")] public class AboutWithCultureInRouteController : Controller { // http://localhost:5000/api/it-CH/AboutWithCultureInRoute // http://localhost:5000/api/fr-CH/AboutWithCultureInRoute private IHtmlLocalizer<AmazingResource> _htmlLocalizer; public AboutWithCultureInRouteController(IHtmlLocalizer<AmazingResource> localizer) { _htmlLocalizer = localizer; } [HttpGet] public string Get() { return _htmlLocalizer["Name"]; } } }
This can then be used as follows:
http://localhost:5000/api/it-CH/AboutWithCultureInRoute http://localhost:5000/api/fr-CH/AboutWithCultureInRoute
This is very useful if you cannot rely on the browser culture.
Notes
Localization in MVC 6 is not flexible enough for most requirements. At present, the Visual Studio Tooling does not work and some of the implementation is not yet polished. The usage of magic strings is a pain. If you do not want to implement the IStringLocalizerFactory and/or IStringLocalizer, you need to follow this magic string convention.
It is also not possible to add resx files to a MVC 6 application using Visual Studio. This will be supported. It is also possible to use the localization in Razor views if your not implementing a Javascript client. See the links underneath for further reading on this.
Links:
https://github.com/aspnet/Localization
https://github.com/aspnet/Tooling/issues/236
http://www.jerriepelser.com/blog/how-aspnet5-determines-culture-info-for-localization
https://github.com/WeebDo/WeebDo.CMF
https://github.com/joeaudette/cloudscribe
https://github.com/aspnet/Mvc/tree/dev/test/WebSites/LocalizationWebSite
Example of localization middleware for culture in route
http://weblogs.asp.net/jeff/beating-localization-into-submission-on-asp-net-5
https://github.com/joeaudette/experiments
https://github.com/avodovnik/Cancel/tree/master/Source/Demo06.ViewLocationExpanders
