DisplayNameAttribute 的本地化

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/356464/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 00:20:58  来源:igfitidea点击:

Localization of DisplayNameAttribute

c#visual-studio-2008localizationattributes

提问by PowerKiKi

I am looking for a way to localize properties names displayed in a PropertyGrid. The property's name may be "overriden" using the DisplayNameAttribute attribute. Unfortunately attributes can not have non constant expressions. So I can not use strongly typed resources such as:

我正在寻找一种方法来本地化 PropertyGrid 中显示的属性名称。可以使用 DisplayNameAttribute 属性“覆盖”属性的名称。不幸的是,属性不能有非常量表达式。所以我不能使用强类型资源,例如:

class Foo
{
   [DisplayAttribute(Resources.MyPropertyNameLocalized)]  // do not compile
   string MyProperty {get; set;}
}

I had a look around and found some suggestion to inherit from DisplayNameAttribute to be able to use resource. I would end up up with code like:

我环顾四周,发现了一些从 DisplayNameAttribute 继承以便能够使用资源的建议。我最终会得到如下代码:

class Foo
{
   [MyLocalizedDisplayAttribute("MyPropertyNameLocalized")] // not strongly typed
   string MyProperty {get; set;}
}

However I lose strongly typed resource benefits which is definitely not a good thing. Then I came across DisplayNameResourceAttributewhich may be what I'm looking for. But it's supposed to be in Microsoft.VisualStudio.Modeling.Design namespace and I can't find what reference I am supposed to add for this namespace.

然而,我失去了强类型资源的好处,这绝对不是一件好事。然后我遇到了DisplayNameResourceAttribute这可能是我正在寻找的。但它应该在 Microsoft.VisualStudio.Modeling.Design 命名空间中,我找不到应该为此命名空间添加的引用。

Anybody know if there's a easier way to achieve DisplayName localization in a good way ? or if there is as way to use what Microsoft seems to be using for Visual Studio ?

有人知道是否有一种更简单的方法可以很好地实现 DisplayName 本地化?或者是否有使用 Microsoft 似乎用于 Visual Studio 的方法?

采纳答案by PowerKiKi

Here is the solution I ended up with in a separate assembly (called "Common" in my case):

这是我在一个单独的程序集中最终得到的解决方案(在我的例子中称为“通用”):

   [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event)]
   public class DisplayNameLocalizedAttribute : DisplayNameAttribute
   {
      public DisplayNameLocalizedAttribute(Type resourceManagerProvider, string resourceKey)
         : base(Utils.LookupResource(resourceManagerProvider, resourceKey))
      {
      }
   }

with the code to look up the resource:

使用代码查找资源:

  internal static string LookupResource(Type resourceManagerProvider, string resourceKey)
  {
     foreach (PropertyInfo staticProperty in  resourceManagerProvider.GetProperties(BindingFlags.Static | BindingFlags.NonPublic))
     {
        if (staticProperty.PropertyType == typeof(System.Resources.ResourceManager))
        {
           System.Resources.ResourceManager resourceManager = (System.Resources.ResourceManager)staticProperty.GetValue(null, null);
           return resourceManager.GetString(resourceKey);
        }
     }

     return resourceKey; // Fallback with the key name
  }

Typical usage would be:

典型用法是:

class Foo
{
      [Common.DisplayNameLocalized(typeof(Resources.Resource), "CreationDateDisplayName"),
      Common.DescriptionLocalized(typeof(Resources.Resource), "CreationDateDescription")]
      public DateTime CreationDate
      {
         get;
         set;
      }
}

What is pretty much ugly as I use literal strings for resource key. Using a constant there would mean to modify Resources.Designer.cs which is probably not a good idea.

当我使用文字字符串作为资源键时,这非常难看。使用常量意味着修改 Resources.Designer.cs,这可能不是一个好主意。

Conclusion: I am not happy with that, but I am even less happy about Microsoft who can't provide anything useful for such a common task.

结论:我对此并不满意,但对于 Microsoft 无法为此类常见任务提供任何有用的东西,我更不满意。

回答by configurator

Well, the assembly is Microsoft.VisualStudio.Modeling.Sdk.dll. which comes with the Visual Studio SDK (With Visual Studio Integration Package).

嗯,大会是Microsoft.VisualStudio.Modeling.Sdk.dll。它随 Visual Studio SDK(带有 Visual Studio 集成包)一起提供。

But it would be used in pretty much the same way as your attribute; there is no way to use strongly types resources in attributes simply because they are not constant.

但它的使用方式与您的属性几乎相同;不能仅仅因为它们不是常量就在属性中使用强类型资源。

回答by Marc Gravell

You can subclass DisplayNameAttribute to provide i18n, by overriding one of the methods. Like so. edit:You might have to settle for using a constant for the key.

您可以通过覆盖其中一种方法来继承 DisplayNameAttribute 以提供 i18n。像这样。编辑:您可能不得不接受为密钥使用常量。

using System;
using System.ComponentModel;
using System.Windows.Forms;

class Foo {
    [MyDisplayName("bar")] // perhaps use a constant: SomeType.SomeResName
    public string Bar {get; set; }
}

public class MyDisplayNameAttribute : DisplayNameAttribute {
    public MyDisplayNameAttribute(string key) : base(Lookup(key)) {}

    static string Lookup(string key) {
        try {
            // get from your resx or whatever
            return "le bar";
        } catch {
            return key; // fallback
        }
    }
}

class Program {
    [STAThread]
    static void Main() {
        Application.Run(new Form { Controls = {
            new PropertyGrid { SelectedObject =
                new Foo { Bar = "abc" } } } });
    }
}

回答by Vincent Van Den Berghe

I apologize for the VB.NET code, my C# is a bit rusty... But you'll get the idea, right?

我为 VB.NET 代码道歉,我的 C# 有点生疏...但你会明白的,对吧?

First of all, create a new class: LocalizedPropertyDescriptor, which inherits PropertyDescriptor. Override the DisplayNameproperty like this:

首先,创建一个新类:LocalizedPropertyDescriptor,它继承了PropertyDescriptor. DisplayName像这样覆盖属性:

Public Overrides ReadOnly Property DisplayName() As String
         Get
            Dim BaseValue As String = MyBase.DisplayName
            Dim Translated As String = Some.ResourceManager.GetString(BaseValue)
            If String.IsNullOrEmpty(Translated) Then
               Return MyBase.DisplayName
            Else
               Return Translated
           End If
    End Get
End Property

Some.ResourceManageris the ResourceManager of the resource file that contains your translations.

Some.ResourceManager是包含您的翻译的资源文件的 ResourceManager。

Next, implement ICustomTypeDescriptorin the class with the localized properties, and override the GetPropertiesmethod:

接下来,ICustomTypeDescriptor在具有本地化属性的类中实现,并覆盖该GetProperties方法:

Public Function GetProperties() As PropertyDescriptorCollection Implements System.ComponentModel.ICustomTypeDescriptor.GetProperties
    Dim baseProps As PropertyDescriptorCollection = TypeDescriptor.GetProperties(Me, True)
    Dim LocalizedProps As PropertyDescriptorCollection = New PropertyDescriptorCollection(Nothing)

    Dim oProp As PropertyDescriptor
    For Each oProp In baseProps
        LocalizedProps.Add(New LocalizedPropertyDescriptor(oProp))
    Next
    Return LocalizedProps
End Function

You can now use the 'DisplayName` attribute to store a reference to a value in a resource file...

您现在可以使用“DisplayName”属性来存储对资源文件中值的引用...

<DisplayName("prop_description")> _
Public Property Description() As String

prop_descriptionis the key in the resource file.

prop_description是资源文件中的key。

回答by Jeff Yates

We are doing this for a number of attributes in order to support multiple language. We have taken a similar approach to Microsoft, where they override their base attributes and pass a resource name rather than the actual string. The resource name is then used to perform a lookup in the DLL resources for the actual string to return.

我们正在为许多属性执行此操作,以支持多种语言。我们采用了与 Microsoft 类似的方法,它们覆盖了它们的基本属性并传递了资源名称而不是实际的字符串。然后使用资源名称在 DLL 资源中查找要返回的实际字符串。

For example:

例如:

class LocalizedDisplayNameAttribute : DisplayNameAttribute
{
    private readonly string resourceName;
    public LocalizedDisplayNameAttribute(string resourceName)
        : base()
    {
      this.resourceName = resourceName;
    }

    public override string DisplayName
    {
        get
        {
            return Resources.ResourceManager.GetString(this.resourceName);
        }
    }
}

You can take this a step further when actually using the attribute, and specify your resource names as constants in a static class. That way, you get declarations like.

在实际使用属性时,您可以更进一步,并将您的资源名称指定为静态类中的常量。这样,你会得到类似的声明。

[LocalizedDisplayName(ResourceStrings.MyPropertyName)]
public string MyProperty
{
  get
  {
    ...
  }
}

Update
ResourceStringswould look something like (note, each string would refer to the name of a resource that specifies the actual string):

更新
ResourceStrings看起来像(注意,每个字符串将引用指定实际字符串的资源名称):

public static class ResourceStrings
{
    public const string ForegroundColorDisplayName="ForegroundColorDisplayName";
    public const string FontSizeDisplayName="FontSizeDisplayName";
}

回答by zielu1

You could use T4 to generate constants. I wrote one:

您可以使用 T4 生成常量。我写了一篇:

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Xml.dll" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Xml.XPath" #>
using System;
using System.ComponentModel;


namespace Bear.Client
{
 /// <summary>
 /// Localized display name attribute
 /// </summary>
 public class LocalizedDisplayNameAttribute : DisplayNameAttribute
 {
  readonly string _resourceName;

  /// <summary>
  /// Initializes a new instance of the <see cref="LocalizedDisplayNameAttribute"/> class.
  /// </summary>
  /// <param name="resourceName">Name of the resource.</param>
  public LocalizedDisplayNameAttribute(string resourceName)
   : base()
  {
   _resourceName = resourceName;
  }

  /// <summary>
  /// Gets the display name for a property, event, or public void method that takes no arguments stored in this attribute.
  /// </summary>
  /// <value></value>
  /// <returns>
  /// The display name.
  /// </returns>
  public override String DisplayName
  {
   get
   {
    return Resources.ResourceManager.GetString(this._resourceName);
   }
  }
 }

 partial class Constants
 {
  public partial class Resources
  {
  <# 
   var reader = XmlReader.Create(Host.ResolvePath("resources.resx"));
   var document = new XPathDocument(reader);
   var navigator = document.CreateNavigator();
   var dataNav = navigator.Select("/root/data");
   foreach (XPathNavigator item in dataNav)
   {
    var name = item.GetAttribute("name", String.Empty);
  #>
   public const String <#= name#> = "<#= name#>";
  <# } #>
  }
 }
}

回答by RandomEngy

There is the Display attributefrom System.ComponentModel.DataAnnotations in .NET 4. It works on the MVC 3 PropertyGrid.

.NET 4 中有来自 System.ComponentModel.DataAnnotations的Display 属性。它适用于 MVC 3 PropertyGrid

[Display(ResourceType = typeof(MyResources), Name = "UserName")]
public string UserName { get; set; }

This looks up a resource named UserNamein your MyResources.resx file.

这将查找UserName在您的MyResources.resx 文件中命名的资源。

回答by YYFish

This is an old question, but I think this is a very common problem, and here is my solution in MVC 3.

这是一个老问题,但我认为这是一个非常普遍的问题,这是我在 MVC 3 中的解决方案。

Firstly, a T4 template is needed to generate constants to avoid nasty strings. We have a resource file ‘Labels.resx' holds all the label strings. Therefore the T4 template uses the resource file directly,

首先,需要一个 T4 模板来生成常量以避免讨厌的字符串。我们有一个资源文件 'Labels.resx' 包含所有标签字符串。所以T4模板直接使用资源文件,

<#@ template debug="True" hostspecific="True" language="C#" #>
<#@ output extension=".cs" #>
<#@ Assembly Name="C:\Project\trunk\Resources\bin\Development\Resources.dll" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Globalization" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Resources" #>
<#
  var resourceStrings = new List<string>();
  var manager = Resources.Labels.ResourceManager;

  IDictionaryEnumerator enumerator = manager.GetResourceSet(CultureInfo.CurrentCulture,  true, true)
                                             .GetEnumerator();
  while (enumerator.MoveNext())
  {
        resourceStrings.Add(enumerator.Key.ToString());
  }
#>     

// This file is generated automatically. Do NOT modify any content inside.

namespace Lib.Const{
        public static class LabelNames{
<#
            foreach (String label in resourceStrings){
#>                    
              public const string <#=label#> =     "<#=label#>";                    
<#
           }    
#>
    }
}

Then, an extension method get created to localize the ‘DisplayName',

然后,创建一个扩展方法来本地化“DisplayName”,

using System.ComponentModel.DataAnnotations;
using Resources;

namespace Web.Extensions.ValidationAttributes
{
    public static class ValidationAttributeHelper
    {
        public static ValidationContext LocalizeDisplayName(this ValidationContext    context)
        {
            context.DisplayName = Labels.ResourceManager.GetString(context.DisplayName) ?? context.DisplayName;
            return context;
        }
    }
}

‘DisplayName' attribute is replaced by ‘DisplayLabel' attribute in order to read from ‘Labels.resx' automatically,

'DisplayName' 属性被 'DisplayLabel' 属性替换,以便自动从 'Labels.resx' 中读取,

namespace Web.Extensions.ValidationAttributes
{

    public class DisplayLabelAttribute :System.ComponentModel.DisplayNameAttribute
    {
        private readonly string _propertyLabel;

        public DisplayLabelAttribute(string propertyLabel)
        {
            _propertyLabel = propertyLabel;
        }

        public override string DisplayName
        {
            get
            {
                return _propertyLabel;
            }
        }
    }
}

After all those preparation work, time to touch those default validation attributes. I am using ‘Required' attribute as an example,

在所有这些准备工作之后,是时候接触那些默认的验证属性了。我以“必需”属性为例,

using System.ComponentModel.DataAnnotations;
using Resources;

namespace Web.Extensions.ValidationAttributes
{
    public class RequiredAttribute : System.ComponentModel.DataAnnotations.RequiredAttribute
    {
        public RequiredAttribute()
        {
          ErrorMessageResourceType = typeof (Errors);
          ErrorMessageResourceName = "Required";
        }

        protected override ValidationResult IsValid(object value, ValidationContext  validationContext)
        {
            return base.IsValid(value, validationContext.LocalizeDisplayName());
        }

    }
}

Now, We can apply those attributes in our model,

现在,我们可以在我们的模型中应用这些属性,

using Web.Extensions.ValidationAttributes;

namespace Web.Areas.Foo.Models
{
    public class Person
    {
        [DisplayLabel(Lib.Const.LabelNames.HowOldAreYou)]
        public int Age { get; set; }

        [Required]
        public string Name { get; set; }
    }
}

By default, property name is used as the key to look up ‘Label.resx', but if you set it through ‘DisplayLabel', it will use that instead.

默认情况下,属性名称用作查找 'Label.resx' 的键,但如果您通过 'DisplayLabel' 设置它,它将改为使用该名称。

回答by HaikMnatsakanyan

I use this way solve in my case

我用这种方式解决我的情况

[LocalizedDisplayName("Age", NameResourceType = typeof(RegistrationResources))]
 public bool Age { get; set; }

With the code

随着代码

public sealed class LocalizedDisplayNameAttribute : DisplayNameAttribute
{
    private PropertyInfo _nameProperty;
    private Type _resourceType;


    public LocalizedDisplayNameAttribute(string displayNameKey)
        : base(displayNameKey)
    {

    }

    public Type NameResourceType
    {
        get
        {
            return _resourceType;
        }
        set
        {
            _resourceType = value;
            _nameProperty = _resourceType.GetProperty(base.DisplayName, BindingFlags.Static | BindingFlags.Public);
        }
    }

    public override string DisplayName
    {
        get
        {
            if (_nameProperty == null)
            {
                return base.DisplayName;
            }

            return (string)_nameProperty.GetValue(_nameProperty.DeclaringType, null);
        }
    }

}

回答by dionoid

Using the Displayattribute (from System.ComponentModel.DataAnnotations) and the nameof()expression in C# 6, you'll get a localized and strongly typed solution.

使用Display属性(来自 System.ComponentModel.DataAnnotations)和C# 6 中的nameof()表达式,您将获得本地化的强类型解决方案。

[Display(ResourceType = typeof(MyResources), Name = nameof(MyResources.UserName))]
public string UserName { get; set; }