This article shows some of the ways in which localization can be used in an ASP.NET Core MVC application.
Code: https://github.com/damienbod/AspNetCoreLocalization/
Posts in this series
- ASP.NET Core MVC Localization
- Using DataAnnotations and Localization in ASP.NET Core MVC
- ASP.NET Core using SQL Localization
2018-12-05: Updated to ASP.NET Core 2.2 and EF Core 2.2
2017-08-19: Updated to ASP.NET Core 2.0
2017-02-10: Updated to VS2017 msbuild
2016-06-28: Updated to ASP.NET Core 1.0 RTM
2016-05-16: Updated to ASP.NET Core 1.0 RC2 dotnet
2015-11-20: 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; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.AspNetCore.Localization; using System.Globalization; using Microsoft.Extensions.Options; using Localization.SqlLocalizer.DbStringLocalizer; namespace AspNet5Localization { using System.IO; using Localization.SqlLocalizer; public class Startup { public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true); if (env.IsDevelopment()) { builder.AddUserSecrets(); } builder.AddEnvironmentVariables(); Configuration = builder.Build(); } public IConfigurationRoot Configuration { get; set; } public void ConfigureServices(IServiceCollection services) { services.AddLocalization(options => options.ResourcesPath = "Resources"); services.AddMvc() .AddViewLocalization() .AddDataAnnotationsLocalization(); services.AddScoped<LanguageActionFilter>(); services.Configure<RequestLocalizationOptions>( options => { var supportedCultures = new List<CultureInfo> { new CultureInfo("en-US"), new CultureInfo("de-CH"), new CultureInfo("fr-CH"), new CultureInfo("it-CH") }; options.DefaultRequestCulture = new RequestCulture(culture: "en-US", uiCulture: "en-US"); options.SupportedCultures = supportedCultures; options.SupportedUICultures = supportedCultures; }); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(); loggerFactory.AddDebug(); var locOptions = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>(); app.UseRequestLocalization(locOptions.Value); app.UseStaticFiles(); app.UseMvc(); } public static void Main(string[] args) { var host = new WebHostBuilder() .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) .UseIISIntegration() .UseStartup<Startup>() .Build(); host.Run(); } } }
The localization can be used for example in an ASP.NET Core MVC 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 Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Localization; using Microsoft.Extensions.Localization; namespace AspNet5Localization.Controllers { [Route("api/[controller]")] public class AboutController : Controller { private readonly IStringLocalizer<SharedResource> _localizer; private readonly IStringLocalizer<AboutController> _aboutLocalizerizer; public AboutController(IStringLocalizer<SharedResource> localizer, IStringLocalizer<AboutController> aboutLocalizerizer) { _localizer = localizer; _aboutLocalizerizer = aboutLocalizerizer; } [HttpGet] public string Get() { // _localizer["Name"] return _aboutLocalizerizer["AboutTitle"]; } } }
Setting the culture in the Query
The culture required by the client application can be set in the query using the ?culture=de-CH.
The application can be run from the commandline, open the application in the src folder and enter:
dotnet restore dotnet run
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.Extensions.Logging; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.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 NET451 System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo(culture); System.Threading.Thread.CurrentThread.CurrentUICulture = new CultureInfo(culture); #elif NET46 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 Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.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 readonly IStringLocalizer<SharedResource> _localizer; public AboutWithCultureInRouteController(IStringLocalizer<SharedResource> localizer) { _localizer = localizer; } [HttpGet] public string Get() { return _localizer["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 ASP.NET Core RC2 dotnet supports most of the requirements for implementing localization in a web application. It is flexible and can be extended as required.
Links:
Microsoft Docs: Globalization and localization
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
[…] ASP.NET 5 MVC 6 Localization – Damienbod explores localization techniques in ASP.NET 5 / MVC6, walking through the configuration of the various aspects of the configuration […]
The current ASP.NET 4 way of using an external resource file everywhere via the auto-generated typesafe wrapper (e.g. AccountResources.UsernameLabel) seems a lot nicer than this pass a string approach (_htmlLocalizer[“UsernameLabel”]).
I assume since it is an interface, what if I want to replace the localization implementation entirely (not use resource files)? I ask because I have worked on sites with interesting localization requirements. A couple examples:
* Localization pulls from external CMS system.
* White label sites, where you pass in a context that tells the system to use one set of localization strings versus another.
* Localization has an override feature, where you have a base set of strings, and then for certain scenarios based on context you have an override set of strings. This for pages that share 90% of the localization, but have some special considerations based on context.
I am hoping that instead of just custom rolling my own localization that ignores MVC way of doing things, that using your new interface I can replace that implementation and all these features can be supported in a transparent way.
Hi Cleve
Thanks for your reply. Using different src resources is an important feature for me as well. Maybe you could ask this question on gitHub aspnet/localization. Don’t think this is possible yet apart from database resources. A lot of translations are delivered as excel, so a type of conversion would also be wished.
The 90% should work fine as you can use parameters in the resource requests.
The white labels feature can be solved by using your IoC, (named resolve)
Greetings Damien
[…] Faire de la localisation (gestion des langues) avec votre application ASP.NET 5. […]
Hi,
Thanks for this item.
I have found an issue in visual studio 2015, when you use vs 2015 for execute web site, the localization fail.
Environnement culture is correct but resources file don’t change.
it’s reproduce with your solution when you use this url : http://localhost:5000/api/About?culture=en-US
Hi
thanks
I have created an issue for this.
https://github.com/aspnet/Tooling/issues/236
Greetings Damien
Hi Damian, how does one actually add a RESX file to an ASP.NET 5 project?
I cannot find the template when I right-click on the project and select Add > New Item
Hi Jerrie
It’s not possible yet in VS2015 for ASP.NET5, I created one in an old project and copied it to the ASP.NET 5 project. A gitHub issue exists for this in aspnet/tooling
Also, every time you add a new resource to the file, the gen. class is compiled to an internal class. You need to change this to a public class for it to work in MVC6. A gitHub issue also exists for this.
Greetings Damien
Thanks Damien. I came across the issue you logged in GH soon after I commented here, so I’ve got it sorted out now 🙂
Hi Jerriep
try to install the new RC1 update for VS 2015 it will sort the issue of adding new resources files to the solution; you have to select add new item and pick resource file from the list of available files
Regards
Hi Jerrie
I’ve just created a global issue for this, could you give your views here?
https://github.com/aspnet/Home/issues/1142
Greetings Damien
Hi Damien,
I’m reading all of your localization posts and github issues, but still don’t know what to do in my project. I have a webapplication which has to be localized for displaying localized labels on the views. And the validations are in the separate libraries. These validations should be localized too.
What should I do? Should I implement my own IStringLocalizerFactory?
Regards,
Alex
Hi Alex
Yes, it looks like this. Damian Edwards is updating a lot of the localization now, and it will change or work in rc2. He’s also creating an example which shows how view localization is done.
At present, if the validations are in a separate library, it causes problems. This should be ok in rc2, check his issues…
Hope this helps
Greetings Damien
Hi! In this moment, the RC1, AddViewLocalization is not working. Now you have AddLocalization, right? I have tested your example, but in this moment, I can’t make it works… My main problem is with DI in my HomeController with IHtmlLocalizer… Any ideas? Thank you so much!!
HI Damien
Thanks for all the help you give us lowly programmers.
I’ve downloaded your AspNet5Localization sample and it is not working for me. I’m using rc1 and the dnx web command to no avail.
Any idea why it’s not working?
Paul
Hi Paul
Thanks for the comment. The latest version uses rc2. Your using rc1. I have created a rc1 branch which you can use. Try switching to this branch and it should work. (With all the rc1 known issues.)
Greetings Damien
Hi Damien, I can solve my problem, I still have the same error… All compiled fine (rc1 tree), but the translation is not working ( running outside VS, dnx web command). What is wrong? I’m using rc1, and dnx45 in my project json. I have created a question in stackoverflow, http://stackoverflow.com/questions/35433059/asp-net-5-mvc-6-resources-localization Thanks!!
Hi Damien
Thank you for the good work you do for us lowly programmers.
I’ve downloaded and tried your sample AspNet5Localization and I can’t get it to work. I’m using rc1 and the dnx web command.
Any ideas why it isn’t working?
Regards
Paul
Hi Damien, I have updated your example to ASP.NET 5 MVC 6 (dnx451). You can change the language, using cookie 😉 You can check it here:
https://github.com/chemitaxis/Localization.StackOverflow
Hi Chema
nice, thanks
Greetings Damien
Hi Damien, thank you for your work on Localization! I tried to run a clone of your repo with RC2 preview but dotnet-ef as dependency is not found.
See output:
Errors in /Users/david/Projects/AspNet5Localization/AspNet5Localization/src/AspNet5Localization/project.json
Unable to resolve ‘dotnet-ef (>= 1.0.0)’ for ‘.NETCoreApp,Version=v1.0’.
NuGet Config files used:
/Users/david/Projects/AspNet5Localization/AspNet5Localization/NuGet.Config
/Users/david/.nuget/NuGet/NuGet.Config
Feeds used:
https://www.myget.org/F/aspnetrc2/api/v3/index.json
https://api.nuget.org/v3/index.json
https://www.myget.org/F/aspnetvnext/api/v3/index.json
## Info
$ dotnet –info
.NET Command Line Tools (1.0.0-preview1-002702)
Product Information:
Version: 1.0.0-preview1-002702
Commit Sha: 6cde21225e
Runtime Environment:
OS Name: Mac OS X
OS Version: 10.11
OS Platform: Darwin
Which feeds do you have additionally?
Thanks
Hi David, I just got the tooling, fixed the code, the latest version should run without problems.
Greetings Damien
Hi David, thanks
I’m using the following feeds:
https://www.myget.org/F/aspnetrc2/api/v3/index.json
https://api.nuget.org/v3/index.json
https://www.myget.org/F/aspnetvnext/api/v3/index.json
https://dotnet.myget.org/F/dotnet-core/api/v3/index.json
and the latest dotnet installed.
From what I’ve read, the RC2 release will be tonight, so I can test the preview then. I cannot test from VS as it is not released yet.
I will update this then to the default release feeds.
Hope this helps
Greetings Damien
“The runtime doesn’t look up localized strings for non-validation attributes”
from the official documentation
so how we should localize the display attribute
Thanks a lot
Hi Damien, thank you for your work on Localization with Core 1 ! ,
I just study your project, and I have some questions :
1) Is it possiblo to localize also on .cshtml pages ?
2) I see all inizialization is on startup, but if I need to change language ?
Thank for your work
Mark
Hi Mark,
Did you get your answer from Damien? I appreciate the Brilliant work done by Damien, but poor documentation is a big headache and better not to use such things.
[…] oggetto si ottiene il valore 12 aprile 2017. Ho cercato di aggiungere solo in tedesco utilizzando questo articolo e anche questo, ma senza […]
[…] date obtient la valeur 12 avril 2017. J'ai essayé d'ajouter en allemand uniquement à l'aide de cette article et également cette, mais sans […]
[…] de objetos obtiene el valor de 12 de abril de 2017. He intentado añadir alemán sólo mediante este artículo y también este, pero sin […]
[…] ASP.NET Core MVC Localization […]