#.net #asp.net-mvc #data-annotations #.net-4.6.2
#.net #asp.net-mvc #данные-аннотации #.net-4.6.2
Вопрос:
У меня есть следующая модель представления:
public class LoginViewModel
{
[DisplayName("Email Address")]
[Required(ErrorMessage = "PleaseEnterYourEmail")]
public string EmailAddress { get; set; }
}
У меня есть следующий файл ресурсов с именем: DataAnnotation.Localization.de-DE.resx, это внутри папки App_LocalResources
Со следующими свойствами:
Теперь, согласно сообщению в блоге announcing.net 4.6.2 это должно просто сработать, поскольку я должен вернуть локализованную версию моего сообщения в представление.
Однако это просто показывает:
Я проверил свою текущую культуру, и для нее установлено значение: de-DE, чтобы приложение знало язык, который оно должно показывать. Целевой платформой также является 4.6.2.
Есть ли что-то, чего я здесь не хватает?
Комментарии:
1. вы используете asp.net проект? Поскольку улучшения относятся к ASP.NET
2. @Jehof Да, это ASP.NET проект.
3. Как вы настраиваете культуру?
4. @jle установка культуры либо с использованием текущей культуры пользователя, либо мы переопределяем ее. Однако, когда я получаю культуру во время просмотра, она возвращается как de-DE, поэтому она должна соответствовать запросу, который я делаю.
5. Эта функция добавлена для WebForm в .net framework 4.6.2. Вы не можете использовать ее в asp.net основной проект или проект MVC.
Ответ №1:
Новая функция локализации аннотаций данных, как описано в сообщении в блоге, объявляющем, что .NET Framework 4.6.2 работает «из коробки» только для ASP.NET WebForms
.
Веб-формы…
В классе .NET Framework 4.6.2 реализована новая функция локализации System.Web.ModelBinding.DataAnnotationsModelValidator
, которая используется StringLocalizerProviders.DataAnnotationStringLocalizerProvider.GetLocalizedString
для разрешения локализованной строки.
System.Web.Mvc.DataAnnotationsModelValidator
по умолчанию установлено System.Web.Globalization.ResourceFileStringLocalizerProvider
значение . Этот поставщик пытается найти DLL «App_LocalResources.root» в Temporary ASP.NET Files
папке для указанного веб-сайта.
Однако существует предварительная проверка, следует ли вообще использовать эту функцию:
private bool UseStringLocalizerProvider {
get {
// if developer already uses existing localization feature,
// then we don't opt in the new localization feature.
return (!string.IsNullOrEmpty(Attribute.ErrorMessage) amp;amp;
string.IsNullOrEmpty(Attribute.ErrorMessageResourceName) amp;amp;
Attribute.ErrorMessageResourceType == null);
}
}
Вышеуказанная проверка означает, что новая функция локализации будет работать только в случае, подобном:
[Required(ErrorMessage = "FirstName is required")]
public string FirstName { get; set; }
где ErrorMessage
задается значение. Для наиболее распространенного варианта использования:
[Required]
public string FirstName { get; set; }
он возвращается к разрешению устаревших имен.
В MVC 5 функция не работает, потому что…
MVC 5 использует свою собственную, специфическую реализацию System.Web.Mvc.DataAnnotationsModelValidator
. Эта реализация берет свое начало с Microsoft.AspNet.Mvc
версии 5.x.x и предшествует .NET Framework 4.6.2. В ней не реализована новая функция локализации.
Кроме того, ASP.NET выходные данные динамической компиляции для MVC и WebForms различаются, поэтому скомпилированный ресурс, используемый WebForms (например: deApp_LocalResources.root.q_wjw-ce.resources.dll
«), даже не существует для App_LocalResources
ASP.NET Приложение MVC.
Это различие результатов компиляции между MVC и WebForms исключает возможность написания оболочки вокруг внедрения WebForms и использования ее «как есть» в приложении MVC.
MVC 6 работает, но немного по-другому…
MVC 6 использует 3-ю реализацию, Microsoft.AspNetCore.Mvc.DataAnnotations.Internal.DataAnnotationsModelValidator
. Эта реализация принимает IStringLocalizer stringLocalizer
в качестве параметра конструктора.
Конфигурация локализации по умолчанию может быть добавлена в Startup.cs
качестве:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc()
.AddDataAnnotationsLocalization();
}
И необходимая локализация запроса в Startup.cs
Configure(...)
методе, например:
// Configure the localization options
app.UseRequestLocalization(new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture(new CultureInfo("de-AT")),
SupportedCultures = new List<CultureInfo>
{
new CultureInfo("de")
},
SupportedUICultures = new List<CultureInfo>
{
new CultureInfo("de-AT")
}
});
Если мы создадим ViewModel:
using System.ComponentModel.DataAnnotations;
namespace WebApplication1.Models
{
public class User
{
[Required(ErrorMessage = "First name is required.")]
public string FirstName { get; set; }
}
}
Мы должны добавить Models.User.{culture}.resx
файл в корень WebApplication
с ключом «Требуется имя». и локализованная проверка — сообщение об ошибке.
Несмотря на то, что MVC 6 имеет другую реализацию, для WebForms применяется то же условие, что и для ValidationAttribute
WebForms. ErrorMessage
должно быть определено, в то время ErrorMessageResourceName
как и ErrorMessageResourceType
не должно использоваться.
Комментарии:
1. Мне просто интересно, где вы обнаружили, что это не работает для MVC 5? Я не вижу этого в сообщении в блоге?
2. @RyanMcDonough, я добавил больше объяснений для MVC 5, но просто, если вы посмотрите на исходный код реализации MVC, части кода для этой новой функции там вообще нет.
Ответ №2:
Наконец-то я нашел решение этой проблемы.
Здесь я поделюсь своим решением для других, у кого такая же проблема.
Добавьте этот класс в свой asp.net основное приложение:
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);
}
}
}
}
… и в вашем файле Startup.cs замените следующую часть:
services.AddLocalization(options => options.ResourcesPath = "Resources");
services.AddMvc()
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
.AddDataAnnotationsLocalization();
… с помощью этого кода:
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));
Код обрабатывает ресурсы локализации вашей модели представления, подобные тем, которые используются для представлений, или в любом другом месте, где IStringLocalizerFactory
можно использовать значение по умолчанию.
Поэтому больше никаких DataAnnotation.Localization.de-DE.resx
ресурсов и App_LocalResources
папок не требуется.
-
Просто создайте серию файлов ресурсов с обычным наименованием (
Models.AccountViewModels.RegisterViewModel.en-US.resx
илиModels/AccountViewModels/RegisterViewModel.sv-SE.resx
вResources
папке, которая задается вызовомservices.AddLocalization(options => options.ResourcesPath = "Resources")
), и все готово. TagHelpers и HtmlHelpers начнут работать и переводить сообщения об ошибках. -
Кроме того, это будет работать
DisplayAttribute.Name
«из коробки». (версия v1.1.0-предварительный просмотр1-окончательный .net v4.6.2)
Обновление 1: вот мой 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%" ]
}
}
Обновление 2: на случай, если кто-то хочет, чтобы код работал, как и было обещано, с DataAnnotation.Localization
in the App_LocalResourses
folder, я обновил StringLocalizerFactoryExtensions
код. Вместо этого используйте обновленный класс и следующий код в Startup.cs
классе, и он должен работать.
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());
Комментарии:
1. Добро пожаловать @RyanMcDonough рад, что я смог помочь.