DisplayNameAttribute का स्थानीयकरण


120

मैं एक प्रॉपर्टीग्रिड में प्रदर्शित प्रॉपर्टी के नामों का स्थानीयकरण करने का तरीका ढूंढ रहा हूं। 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 के लिए उपयोग किया जा रहा है, तो उपयोग करने का तरीका क्या है?


2
डिस्प्ले (रिसोर्स टाइप = टाइपऑफ़ (रिसोर्सस्ट्रीम), नाम = "MyProperty") के बारे में क्या देखें msdn.microsoft.com/en-us/library/…
पीटर

@ खिलाड़ी ने पोस्ट को ध्यान से पढ़ा, वह सटीक विपरीत चाहता है, रिसोर्सस्ट्रीम और संकलन समय का उपयोग करके हार्ड कोडित स्ट्रिंग्स की जांच न करे ...
Marko

जवाबों:


113

नहीं है प्रदर्शन विशेषता System.ComponentModel.DataAnnotations से नेट 4 में यह MVC 3 पर काम करता है PropertyGrid

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

यह UserNameआपके MyResources.resx फ़ाइल में नामित संसाधन को देखता है ।


मैंने इस पृष्ठ को खोजने से पहले सभी को देखा ... यह एक ऐसा जीवन रक्षक है। धन्यवाद! मेरे लिए MVC5 पर अच्छा काम करता है।
क्रिस्च

यदि कंपाइलर शिकायत कर रहा है typeof(MyResources), तो आपको अपनी रिसोर्स फाइल एक्सेस मोडिफ़ायर को सार्वजनिक करने की आवश्यकता हो सकती है ।
वह वेजीगुए

80

हम कई भाषाओं का समर्थन करने के लिए कई विशेषताओं के लिए ऐसा कर रहे हैं। हमने 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";
}

जब मैं इस दृष्टिकोण की कोशिश करता हूं, तो मुझे एक त्रुटि संदेश मिलता है जिसमें कहा गया है "एक विशेषता तर्क एक स्थिर अभिव्यक्ति, टाइपो अभिव्यक्ति या विशेषता पैरामीटर प्रकार की सरणी निर्माण अभिव्यक्ति होना चाहिए"। हालाँकि, मान स्ट्रिंग स्ट्रिंग के रूप में LocalizedDisplayName करने के लिए। काश यह दृढ़ता से टाइप किया जाता।
अज़ुरे एसएमई

1
@Andy: रिसोर्सस्ट्रीम में मूल्य स्थिर होना चाहिए, जैसा कि उत्तर में इंगित किया गया है, गुण या आसानी से मान नहीं। उन्हें कांस्ट के रूप में चिह्नित किया जाना चाहिए और संसाधनों के नामों को संदर्भित करना चाहिए, अन्यथा आपको एक त्रुटि मिलेगी।
जेफ येट्स

1
मेरे अपने प्रश्न का उत्तर दिया, इस बारे में था कि आपके पास संसाधन कहां थे। स्रोत प्रबंधक, मेरे मामले में रेक्स फाइलें सार्वजनिक रेक्स उत्पन्न होती हैं इसलिए यह था[MyNamespace].[MyResourceFile].ResourceManager.GetString("MyString");
ट्रिस्टन वार्नर-स्मिथ

कहते हैं, मैं संसाधनों की एक उदाहरण की जरूरत है। स्रोत
प्रबंधक इस

1
@LTR: कोई बात नहीं। मुझे खुशी है कि आप इस मुद्दे की तह तक गए। मदद करने के लिए खुश, अगर मैं कर सकता हूँ
जेफ येट्स

41

यहाँ मैं एक अलग विधानसभा (जिसे मेरे मामले में "सामान्य" कहा जाता है) के साथ समाप्त हुआ समाधान है:

   [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 एक अच्छा समाधान प्रस्तुत करता है जो संसाधनों को संदर्भित करने का दृढ़ता से टाइप किया गया तरीका प्रदान करता है।
जॉनी ओशिका

फिर यह स्ट्रिंग सामग्री वास्तव में कड़ी मेहनत से बेकार है :( यदि आप गुण का उपयोग करने वाली संपत्ति का नाम प्राप्त कर सकते हैं, तो आप इसे कॉन्फ़िगरेशन तरीके से कन्वेंशन में कर सकते हैं, लेकिन ऐसा संभव नहीं लगता है। Enumerations, आप उपयोग कर सकते हैं, यह भी वास्तव में बनाए रखने योग्य नहीं है: /
Rookian

यह एक अच्छा उपाय है। मैं अभी ResourceManagerसंपत्तियों के संग्रह के माध्यम से पुनरावृत्ति नहीं करूंगा । इसके बजाय आप बस पैरामीटर में प्रदान किए गए प्रकार से सीधे संपत्ति प्राप्त कर सकते हैं:PropertyInfo property = resourceManagerProvider.GetProperty(resourceKey, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
मैक्सिमिलियन मेजर

1
संसाधन कुंजी को स्वतः उत्पन्न करने के लिए @ zielu1 के T4 टेम्पलेट के साथ इसे मिलाएं, और आपके पास एक योग्य विजेता है!
डेविड कीवेनी

19

C # 6 में प्रदर्शन विशेषता (System.ComponentModel.DataAnnotations) और nameof () अभिव्यक्ति का उपयोग करके , आपको एक स्थानीय और दृढ़ता से टाइप किया गया समाधान मिलेगा।

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

1
इस उदाहरण में, "MyResources" क्या है? एक जोरदार टाइप रेक्स फ़ाइल? एक कस्टम वर्ग?
ग्रेग

14

आप स्थिरांक उत्पन्न करने के लिए 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#>";
  <# } #>
  }
 }
}

आउटपुट कैसा होगा?
इरफंदर

9

यह एक पुराना प्रश्न है, लेकिन मुझे लगता है कि यह एक बहुत ही सामान्य समस्या है, और यहाँ MVC 3 में मेरा समाधान है।

सबसे पहले, गंदा तार से बचने के लिए स्थिरांक उत्पन्न करने के लिए एक टी 4 टेम्पलेट की आवश्यकता होती है। हमारे पास एक संसाधन फ़ाइल है '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;
        }
    }
}

'DisplayName' विशेषता को 'Labels.resx' से स्वचालित रूप से पढ़ने के लिए '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' के माध्यम से सेट करते हैं, तो यह इसके बजाय इसका उपयोग करेगा।


6

आप तरीकों में से एक को ओवरराइड करके, 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" } } } });
    }
}

2

मैं अपने मामले में इस तरह से हल का उपयोग करता हूं

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

}

1

खैर, विधानसभा है Microsoft.VisualStudio.Modeling.Sdk.dll। जो विजुअल स्टूडियो SDK (विजुअल स्टूडियो इंटीग्रेशन पैकेज के साथ) के साथ आता है।

लेकिन यह आपकी विशेषता के रूप में उसी तरह से उपयोग किया जाएगा; विशेषताओं में दृढ़ता से प्रकार संसाधनों का उपयोग करने का कोई तरीका नहीं है, क्योंकि वे निरंतर नहीं हैं।


0

मैं VB.NET कोड के लिए माफी माँगता हूँ, मेरा C # थोड़ा कठोर है ... लेकिन आपको यह विचार सही लगेगा?

सबसे पहले, एक नया वर्ग बनाएं: LocalizedPropertyDescriptorजो विरासत में मिला है PropertyDescriptorDisplayNameसंपत्ति को इस तरह से ओवरराइड करें:

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 संसाधन फ़ाइल का संसाधन प्रबंधक है जिसमें आपके अनुवाद शामिल हैं।

अगला, 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

अब आप किसी संसाधन फ़ाइल में मान को संदर्भित करने के लिए 'DisplayName` विशेषता का उपयोग कर सकते हैं ...

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

prop_description संसाधन फ़ाइल में कुंजी है।


आपके समाधान का पहला भाग जो मैंने किया है ... जब तक मुझे हल नहीं करना था "What is Some.ResourceManager?" सवाल। क्या मैं "MyAssembly.Resource.Resource" जैसे दूसरे शाब्दिक स्ट्रिंग को देने वाला हूं ? रास्ता बहुत खतरनाक है! दूसरे भाग (ICustomTypeDescriptor) के अनुसार मुझे नहीं लगता कि यह वास्तव में उपयोगी है
PowerKiKi

मार्क ग्रेवेल का समाधान एक ऐसा तरीका है जब आपको किसी अनुवादित डिस्प्लेनेम के अलावा किसी और चीज की आवश्यकता नहीं है - मैं अन्य सामान के लिए कस्टम डिस्क्रिप्टर का उपयोग करता हूं, और यह मेरा समाधान था। यद्यपि किसी प्रकार की कुंजी की आपूर्ति किए बिना ऐसा करने का कोई तरीका नहीं है।
विन्सेन्ट वान डेन बर्गे १०’०
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.