根据文化本地化数据注释错误消息

Localize dataannotation error messages as per culture

在我的 WPF 应用程序中,我 属性 定义如下

[Required(AllowEmptyStrings =false, ErrorMessageResourceName ="Msg1", ErrorMessageResourceType =typeof(*<AssemblyName>*.Resources.*<ResourceFileName>*))]
public string Name
{
    get
    {
        return _name;
    }

    set
    {
        if (_name == value)
        {
            return;
        }
        _name = value;
    }
}

我在单独的程序集中定义了我的错误消息,该程序集具有针对不同文化的资源文件,例如Resources.resx、Resources.en-GB.Resx、Resources.fr-FR.Resx、Resources.en-US.Resx等

使用上面的代码,我能够从我的附属程序集中的默认资源文件中检索错误消息,但我没有看到任何从区域性特定资源文件中查找字符串资源的规定。我的意思是,如果我的 CurrentUICluture 设置为英语(英国),那么我想从文件 "Resources.en-GB.Resx" 而不是默认文件(即 Resources.Resx)中检索资源值。

我看不出有什么方法可以传递必需属性定义中的区域性信息。另外,我已经测试过它不是固有地根据当前文化集查看特定于文化的资源文件。

我想要的是某种让资源检索机制具有文化意识的方法。

谢谢,

如果您想让您的网站具有文化意识,您需要修改 system.web 元素下 web.config 中的全球化属性:

<globalization fileEncoding="utf-8" requestEncoding="utf-8" culture="auto" uiCulture="auto" enableClientBasedCulture="true" />

然后它将根据浏览器的首选语言设置寻找资源。

或者,您可以明确指定它:

<globalization fileEncoding="utf-8" requestEncoding="utf-8" responseEncoding="" culture="en-US" uiCulture="en-US" />

我终于从 this link

那里得到了解决问题的方法

在我的应用程序中,我通过将以下代码放入 app.xaml.cs 文件来在启动应用程序时设置区域性。

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        InitializeCultures();
    }

    private static void InitializeCultures()
    {
        var culture = ConfigurationManager.AppSettings.Get("Culture");
        if (!String.IsNullOrEmpty(culture))
        {
            Thread.CurrentThread.CurrentCulture =  new CultureInfo(culture);
        }

        var UICulture = ConfigurationManager.AppSettings.Get("UICulture");
        if (!String.IsNullOrEmpty(UICulture))
        {
            Thread.CurrentThread.CurrentUICulture = new CultureInfo(UICulture);
        }
    }

然而问题是即使我最初设置了文化,并不是所有的线程都会使用我最初设置的相同文化。因此,为我读取资源值的线程仍在使用默认文化,这导致始终从默认文化资源文件而不是文化特定资源文件中读取资源字符串。

所以诀窍是将我的应用程序中的所有线程设置为使用我最初设置的相同区域性。这个有两个属性

  1. CultureInfo.DefaultThreadCurrentCulture
  2. CultureInfo.DefaultThreadCurrentUICulture

最初为这两个属性设置所需的文化值将确保应用程序中使用的所有后续线程将具有与用户设置的相同的文化。这两个属性设置当前应用程序域中所有线程的区域性。

一旦文化被正确设置,资源值的读取自然成为文化感知并从特定于文化的资源文件中读取资源值。

下面是 InitializeCulture 方法的更新版本:

    private static void InitializeCultures()
    {
        var culture = ConfigurationManager.AppSettings.Get("Culture");
        if (!String.IsNullOrEmpty(culture))
        {
            Thread.CurrentThread.CurrentCulture = CultureInfo.DefaultThreadCurrentCulture =  new CultureInfo(culture);
        }

        var UICulture = ConfigurationManager.AppSettings.Get("UICulture");
        if (!String.IsNullOrEmpty(UICulture))
        {
            Thread.CurrentThread.CurrentUICulture = CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo(UICulture);
        }
    }

P.S。在 Web 应用程序中使用此修复程序时要小心。请阅读原文 link.

希望这对您有所帮助...

我想出了一个简单的主意,使我无需单独装饰所有属性。 确实是一个令人讨厌的解决方案,因为它涉及反射但有效。

注意:如果 .NET Core 团队决定重命名字段或 类 或者内部内容发生变化,这可能会中断,因此请自行决定使用。

首先在您的应用程序中创建一个 resx 文件,要么从头开始创建一个文件,要么复制 this or this 一个文件,然后用所需的翻译替换字符串,在与它们出现的格式相同(注意 {0} 占位符及其顺序)。
显然,您随后可以添加类似 DataAnnotations.en-UK.resx 的文件并将它们全部翻译,只要您的应用 properly set 能够以所需的文化运行,它就可以工作。

在您的项目中,在早期启动时调用以下命令:

void InitializeDataAnnotationsCulture()
{
  var sr = 
    typeof(ValidationAttribute)
      .Assembly
      .DefinedTypes
      .Single(t => t.FullName == "System.SR");

  var resourceManager = 
    sr
      .DeclaredFields
      .Single(f => f.IsStatic && f.Name == "s_resourceManager");

  resourceManager
    .SetValue(null, 
      DataAnnotationsResources.ResourceManager, /* The generated RESX class in my proj */
      BindingFlags.NonPublic | BindingFlags.Static, null, null);

  var injected = resourceManager.GetValue(null) == DataAnnotationsResources.ResourceManager;
  Debug.Assert(injected);
}

它的作用是利用 this 字段将默认的 DataAnnotations 资源管理器替换为您自己的。

它已经过测试并在 .NET Core 3.1 WPF 中工作,但您可以浏览任何 .NET 的 .NET 源代码以查找资源位置并注入您自己的资源。