【问题标题】:Mulitlingual Data Annotation in .NET 4.6.2.NET 4.6.2 中的多语言数据注释
【发布时间】:2017-02-20 00:01:48
【问题描述】:

我有以下视图模型:

public class LoginViewModel
    {
        [DisplayName("Email Address")]
        [Required(ErrorMessage = "PleaseEnterYourEmail")]
        public string EmailAddress { get; set; }

    }

我有以下资源文件,名为:DataAnnotation.Localization.de-DE.resx,位于 App_LocalResources 文件夹内

具有以下属性:

现在根据blog post announcing.net 4.6.2,这应该可以正常工作,因为我应该将我的消息的本地化版本返回到视图中。

但它只是显示:

我检查了我当前的文化并将其设置为:de-DE,以便应用了解它需要显示的语言。 Target 框架也是 4.6.2。

这里有什么我遗漏的吗?

【问题讨论】:

  • 您使用的是 asp.net 项目吗?由于改进是指 ASP.NET
  • @Jehof 是的,这是一个 ASP.NET 项目。
  • 你是如何设置文化的?
  • @jle 使用用户当前的文化设置文化,或者我们覆盖它。但是,当我在视图中获得文化时,它会以 de-DE 的形式返回,因此它应该符合我提出的要求。
  • .net framework 4.6.2 中为 WebForm 添加了此功能。不能在 asp.net core 项目或 MVC 项目中使用。

标签: .net asp.net-mvc data-annotations .net-4.6.2


【解决方案1】:

新的 DataAnnotations 本地化功能,如 blog post announcing .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。此提供程序尝试在指定网站的Temporary ASP.NET Files 文件夹中查找“App_LocalResources.root”dll。

但是,有一个预先检查是否应该使用该功能:

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) &&
            string.IsNullOrEmpty(Attribute.ErrorMessageResourceName) &&
            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。它没有实现新的本地化功能。

最重要的是,MVC 和 WebForms 的 ASP.NET 动态编译输出不同,因此 WebForms 使用的编译资源(例如:de\App_LocalResources.root.q_wjw-ce.resources.dll")甚至不存在用于 ASP.NET MVC 应用程序的App_LocalResources

MVC 和 WebForms 之间编译输出的这种差异排除了围绕 WebForms 实现编写包装器并在 MVC 应用程序中“按原样”使用它的可能性。

MVC 6 工作,但略有不同...

MVC 6 使用第三个实现,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.csConfigure(...)方法中需要请求本地化,例如:

// 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; }
    }
}

我们必须在WebApplication 的根目录中添加Models.User.{culture}.resx 文件,并带有“First name is required”键。和本地化的验证错误消息。

尽管 MVC 6 有不同的实现,但同样的条件适用于 ValidationAttribute,就像在 WebForms 中一样。 ErrorMessage 必须定义,而 ErrorMessageResourceNameErrorMessageResourceType 不应使用。

【讨论】:

  • 我只是想知道您在哪里发现它不适用于 MVC 5?我在博文中看不到?
  • @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.resxModels/AccountViewModels/RegisterViewModel.sv-SE.resx在通过调用services.AddLocalization(options =&gt; options.ResourcesPath = "Resources")设置的Resources文件夹中)然后你准备好了。 TagHelpers 和 HtmlHelpers 将开始工作并翻译错误消息。

  • 另外,这将适用于开箱即用的 DisplayAttribute.Name(v1.1.0-preview1-final + .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: 如果有人希望代码与 App_LocalResourses 文件夹中的 DataAnnotation.Localization 一起工作,我已经更新了 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());

【讨论】:

  • 不客气@RyanMcDonough 很高兴我能帮上忙。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-10-22
  • 2012-02-05
  • 2010-09-07
  • 2016-01-03
  • 1970-01-01
相关资源
最近更新 更多