Programing

DisplayNameAttribute 지역화

lottogame 2020. 7. 15. 07:36
반응형

DisplayNameAttribute 지역화


PropertyGrid에 표시되는 속성 이름을 지역화하는 방법을 찾고 있습니다. 속성 이름은 DisplayNameAttribute 특성을 사용하여 "재 지정"될 수 있습니다. 불행하게도 속성은 상수가 아닌 표현식을 가질 수 없습니다. 따라서 다음과 같은 강력한 형식의 리소스를 사용할 수 없습니다.

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

주변을 둘러보고 리소스를 사용할 수 있도록 DisplayNameAttribute에서 상속 할 제안을 찾았습니다. 나는 다음과 같은 코드로 끝날 것이다.

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

그러나 나는 분명히 좋은 유형이 아닌 강력한 유형의 리소스 이점을 잃습니다. 그런 다음 내가 찾고있는 DisplayNameResourceAttribute 를 발견했습니다. 그러나 Microsoft.VisualStudio.Modeling.Design 네임 스페이스에 있어야 하며이 네임 스페이스에 추가 할 참조를 찾을 수 없습니다.

누구나 DisplayName 지역화를 쉽게 수행 할 수있는 방법이 있는지 알고 있습니까? 또는 Microsoft가 Visual Studio에 사용하는 것으로 보이는 것을 사용하는 방법이 있습니까?


.NET 4에는 System.ComponentModel.DataAnnotations Display 속성이 있습니다 PropertyGrid. MVC 3에서 작동합니다 .

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

라는 이름의 자원까지이 모양 UserName당신에 MyResources된 .resx 파일.


우리는 여러 언어를 지원하기 위해 여러 속성에 대해이 작업을 수행하고 있습니다. 우리는 기본 속성을 무시하고 실제 문자열이 아닌 리소스 이름을 전달하는 Microsoft와 비슷한 접근 방식을 취했습니다. 그런 다음 리소스 이름은 실제 문자열을 반환하기 위해 DLL 리소스에서 조회를 수행하는 데 사용됩니다.

예를 들면 다음과 같습니다.

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

실제로 속성을 사용할 때이 단계를 한 단계 더 나아가서 정적 클래스에서 자원 이름을 상수로 지정할 수 있습니다. 그렇게하면 선언을 얻을 수 있습니다.

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

업데이트
ResourceStrings 는 다음과 같습니다 (각 문자열은 실제 문자열을 지정하는 리소스 이름을 나타냅니다).

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

다음은 별도의 어셈블리 (내 경우에는 "공통"이라고 함)로 끝내는 솔루션입니다.

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

리소스를 찾는 코드와 함께 :

  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
  }

일반적인 사용법은 다음과 같습니다.

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

리소스 키에 리터럴 문자열을 사용하면 상당히 추한 것입니다. 상수를 사용하면 Resources.Designer.cs를 수정하는 것이 좋지 않을 수 있습니다.

결론 : 나는 그 점에 만족하지 않지만, 일반적인 작업에 유용한 것을 제공 할 수없는 Microsoft에 대해서는 훨씬 덜 행복합니다.


C # 6 Display 특성 (System.ComponentModel.DataAnnotations) 및 nameof () 식을 사용하면 지역화 된 강력한 형식의 솔루션을 얻게됩니다.

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

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#>";
  <# } #>
  }
 }
}

이것은 오래된 질문이지만 이것이 매우 일반적인 문제라고 생각하며 여기 MVC 3의 솔루션이 있습니다.

먼저, 불쾌한 문자열을 피하기 위해 상수를 생성하려면 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#>";                    
<#
           }    
#>
    }
}

그런 다음 '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;
        }
    }
}

'Labels.resx'에서 자동으로 읽으려면 'DisplayName'속성이 'DisplayLabel'속성으로 대체됩니다.

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

모든 준비 작업이 끝나면 기본 유효성 검사 속성을 만질 시간입니다. 예를 들어 '필수'속성을 사용하고 있습니다.

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());
        }

    }
}

이제이 속성을 모델에 적용 할 수 있습니다.

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

기본적으로 속성 이름은 'Label.resx'를 찾는 키로 사용되지만 'DisplayLabel'을 통해 설정하면 대신 사용됩니다.


메소드 중 하나를 대체하여 i18n을 제공하도록 DisplayNameAttribute를 서브 클래스화할 수 있습니다. 그렇게 편집 : 키에 상수를 사용하기 위해 정착해야 할 수도 있습니다.

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" } } } });
    }
}

나는이 방법을 사용하여 내 경우에 해결

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

코드로

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

}

글쎄요, 어셈블리는 Microsoft.VisualStudio.Modeling.Sdk.dll입니다. Visual Studio SDK (Visual Studio 통합 패키지 포함)와 함께 제공됩니다.

그러나 속성과 거의 같은 방식으로 사용됩니다. 속성이 강력하지 않기 때문에 속성에 강력한 유형의 리소스를 사용하는 방법은 없습니다.


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

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

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.ResourceManager is the ResourceManager of the resource file that contains your translations.

Next, implement ICustomTypeDescriptor in the class with the localized properties, and override the GetProperties method:

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("prop_description")> _
Public Property Description() As String

prop_description is the key in the resource file.

참고URL : https://stackoverflow.com/questions/356464/localization-of-displaynameattribute

반응형