Finally I found a solution for this problem.
Here I will share my solution for others who are having the same problem.
Add this class to your asp.net core application:
using System;
using Microsoft.Extensions.Localization;
namespace App.Utilities
{
public static class StringLocalizerFactoryExtensions
{
public static IStringLocalizer CreateConventional<T>(this IStringLocalizerFactory factory)
{
return factory.CreateConventional(typeof(T));
}
public static IStringLocalizer CreateConventional(this IStringLocalizerFactory factory, Type type)
{
if (type.Module.ScopeName != "CommonLanguageRuntimeLibrary")
{
string[] parts = type.FullName.Split(new[] { type.Assembly.FullName.Split(',')[0] }, StringSplitOptions.None);
string name = parts[parts.Length - 1].Trim('.');
return factory.CreateConventional(name);
}
else
{
return factory.Create(type);
}
}
public static IStringLocalizer CreateConventional(this IStringLocalizerFactory factory, string resourceName)
{
return factory.Create(resourceName, null);
}
public static IStringLocalizer CreateDataAnnotation(this IStringLocalizerFactory factory)
{
if (type.Module.ScopeName != "CommonLanguageRuntimeLibrary")
{
return factory.Create("DataAnnotation.Localization", "App_LocalResources");
}
else
{
return factory.Create(type);
}
}
}
}
... and in your Startup.cs file replace the following part:
services.AddLocalization(options => options.ResourcesPath = "Resources");
services.AddMvc()
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
.AddDataAnnotationsLocalization();
... with this code:
services.AddLocalization(options => options.ResourcesPath = "Resources");
services.AddMvc()
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
//The following part includes the change:
.AddDataAnnotationsLocalization(options => options.DataAnnotationLocalizerProvider = (type, factory) => factory.CreateConventional(type));
The code is treating your view model localization resources like the one used for views or any other place where the default IStringLocalizerFactory
can be used.
Therefore, no more DataAnnotation.Localization.de-DE.resx
resources and App_LocalResources
folder are needed.
Just, create a series of resource files with the conventional naming (Models.AccountViewModels.RegisterViewModel.en-US.resx
or Models/AccountViewModels/RegisterViewModel.sv-SE.resx
in the Resources
folder which is set by calling services.AddLocalization(options => options.ResourcesPath = "Resources")
) and you are ready to go. The TagHelpers and HtmlHelpers will start working and translating the error messages.
Also, this will work for DisplayAttribute.Name
out of the box. (v1.1.0-preview1-final + .net v4.6.2)
Update 1:
Here is my project.json
:
{
"userSecretsId": "...",
"dependencies": {
"Microsoft.NETCore.Platforms": "1.1.0-preview1-*",
"Microsoft.AspNetCore.Authentication.Cookies": "1.1.0-preview1-final",
"Microsoft.AspNetCore.Diagnostics": "1.1.0-preview1-final",
"Microsoft.AspNetCore.DataProtection": "1.1.0-preview1-final",
"Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore": "1.1.0-preview1-final",
"Microsoft.AspNetCore.Identity.EntityFrameworkCore": "1.1.0-preview1-final",
"Microsoft.AspNetCore.Mvc": "1.1.0-preview1-final",
"Microsoft.AspNetCore.Razor.Tools": "1.0.0-preview3-final",
"Microsoft.ApplicationInsights.AspNetCore": "1.0.2",
"Microsoft.AspNetCore.Mvc.Localization": "1.1.0-preview1-final",
"Microsoft.AspNetCore.Mvc.Razor": "1.1.0-preview1-final",
"Microsoft.AspNetCore.Mvc.TagHelpers": "1.1.0-preview1-final",
"Microsoft.AspNetCore.Mvc.DataAnnotations": "1.1.0-preview1-final",
"Microsoft.Extensions.Configuration.CommandLine": "1.1.0-preview1-final",
"Microsoft.Extensions.Configuration.FileExtensions": "1.1.0-preview1-final",
"Microsoft.AspNet.WebApi.Client": "5.2.3",
"Microsoft.AspNetCore.Routing": "1.1.0-preview1-final",
"Microsoft.AspNetCore.Server.IISIntegration": "1.1.0-preview1-final",
"Microsoft.AspNetCore.Server.Kestrel": "1.1.0-preview1-final",
"Microsoft.AspNetCore.StaticFiles": "1.1.0-preview1-final",
"Microsoft.EntityFrameworkCore": "1.1.0-preview1-final",
"Microsoft.EntityFrameworkCore.SqlServer.Design": "1.1.0-preview1-final",
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0-preview1-final",
"Microsoft.Extensions.Configuration.Json": "1.1.0-preview1-final",
"Microsoft.Extensions.Configuration.UserSecrets": "1.1.0-preview1-final",
"Microsoft.Extensions.Logging": "1.1.0-preview1-final",
"Microsoft.Extensions.Logging.Console": "1.1.0-preview1-final",
"Microsoft.Extensions.Logging.Debug": "1.1.0-preview1-final",
"Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0-preview1-final",
"Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0",
"Microsoft.VisualStudio.Web.CodeGeneration.Tools": "1.0.0-preview3-final",
"Microsoft.VisualStudio.Web.CodeGenerators.Mvc": "1.0.0-preview3-final",
"Microsoft.AspNetCore.Hosting": "1.1.0-preview1-final",
"Microsoft.AspNetCore.Hosting.WindowsServices": "1.1.0-preview1-final",
"Loggr.Extensions.Logging": "1.0.0",
"Microsoft.EntityFrameworkCore.SqlServer": "1.1.0-preview1-final",
"Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview3-final",
"BundlerMinifier.Core": "2.2.296"
},
"tools": {
"Microsoft.AspNetCore.Razor.Tools": "1.0.0-preview3-final",
"Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview3-final",
"Microsoft.EntityFrameworkCore.Tools.DotNet": "1.0.0-preview3-final",
"Microsoft.Extensions.SecretManager.Tools": "1.0.0-preview3-final",
"Microsoft.VisualStudio.Web.CodeGeneration.Tools": {
"version": "1.0.0-preview3-final",
"imports": [
"portable-net45+win8"
]
}
},
"frameworks": {
"net462": {}
},
"buildOptions": {
"emitEntryPoint": true,
"preserveCompilationContext": true
},
"runtimeOptions": {
"configProperties": {
"System.GC.Server": true
}
},
"publishOptions": {
"include": [
"wwwroot",
"**/*.cshtml",
"appsettings.json",
"web.config"
]
},
"scripts": {
"prepublish": [ "bower install", "dotnet bundle" ],
"postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
}
}
Update 2: In case someone wants the code to just work as promised with the DataAnnotation.Localization
in the App_LocalResourses
folder, I have updated the StringLocalizerFactoryExtensions
code. Use the updated class and the following code in Startup.cs
class instead and it should work.
services.AddLocalization(options => options.ResourcesPath = "Resources");
services.AddMvc()
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
//The following part includes the change:
.AddDataAnnotationsLocalization(options => options.DataAnnotationLocalizerProvider = (type, factory) => factory.CreateDataAnnotation());