Многоязычная аннотация данных в .NET 4.6.2

#.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

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 с ключом «Требуется имя». и локализованная проверка — сообщение об ошибке.

Файл Model.User.resx.

Несмотря на то, что 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 рад, что я смог помочь. 🙂