La localizzazione di DisplayNameAttribute

Sto cercando un modo per localizzare proprietà di nomi visualizzati in un PropertyGrid. La proprietà del nome potrebbe essere “sostituiti” utilizzando il DisplayNameAttribute attributo. Purtroppo gli attributi non possono avere non costante espressioni. Quindi non posso usare fortemente tipizzato risorse, quali:

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

Ho dato un’occhiata in giro e ho trovato qualche suggerimento per ereditare da DisplayNameAttribute per essere in grado di utilizzare la risorsa. Vorrei finire con un codice simile:

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

Tuttavia perdo di risorse fortemente tipizzate benefici che sicuramente non è una buona cosa. Poi mi sono imbattuto in DisplayNameResourceAttribute che potrebbe essere quello che sto cercando. Ma si suppone di essere in Microsoft.VisualStudio.Modellazione.La progettazione dello spazio dei nomi e non riesco a trovare quello di riferimento dovrei aggiungere per questo spazio dei nomi.

Qualcuno sa se c’è un modo più semplice per ottenere DisplayName localizzazione in un buon modo ? o se c’è modo per utilizzare ciò che Microsoft sembra essere utilizzando per Visual Studio ?

  • Che dire di Visualizzazione(ResourceType=typeof(ResourceStrings),Nome=”MyProperty”) vedi msdn.microsoft.com/en-us/library/…
  • leggi il post con attenzione, vuole esattamente l’opposto, utilizzando ResourceStrings e la compilazione, il controllo del tempo non sono codificati corde…
InformationsquelleAutor PowerKiKi | 2008-12-10



10 Replies
  1. 111

    C’è il Attributo di visualizzazione dal Sistema.ComponentModel.DataAnnotations in .Rete 4. Funziona su MVC 3 PropertyGrid.

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

    Questo cerca una risorsa di nome UserName nel MyResources .file resx.

    • Funziona in ASP.NET MVC 3, grazie!
    • Non funziona su ASP.NET MVC 4
    • Ho guardato tutto il mondo prima di trovare questa pagina… questo è un risparmiatore di vita. Grazie! Funziona bene su di MVC5 per me.
    • Se il compilatore si lamenta typeof(MyResources), potrebbe essere necessario impostare il file di risorse modificatore di accesso a Pubblico.
  2. 79

    Stiamo facendo questo per un certo numero di attributi per il supporto di più lingue. Abbiamo adottato un approccio simile a Microsoft, dove sostituiscono i loro attributi di base e passare il nome di una risorsa piuttosto che la stringa effettiva. Il nome della risorsa che viene poi utilizzato per eseguire una ricerca nella DLL di risorse per la stringa effettiva restituzione.

    Per esempio:

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

    Si può prendere questo un ulteriore passo avanti quando in realtà utilizzando l’attributo, e specificare i nomi delle risorse come costanti in una classe statica. In questo modo, si ottiene dichiarazioni come.

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

    Aggiornamento

    ResourceStrings sarebbe qualcosa di simile (nota, ogni stringa si riferisce al nome di una risorsa che consente di specificare la stringa effettiva):

    public static class ResourceStrings
    {
        public const string ForegroundColorDisplayName="ForegroundColorDisplayName";
        public const string FontSizeDisplayName="FontSizeDisplayName";
    }
    • Quando provo questo approccio, ricevo un messaggio di errore che dice “Un argomento dell’attributo deve essere una costante espressione, typeof espressione o la creazione di array di espressione di un attributo del tipo di parametro”. Tuttavia, passando il valore di Nomevisualizzatolocalizzato come una stringa. Vorrei sarebbe fortemente tipizzato.
    • I valori in ResourceStrings devono essere costanti, come indicato nella risposta, non di proprietà o di sola lettura valori. Deve essere contrassegnati come const e vedere i nomi delle risorse, altrimenti si otterrà un errore.
    • Risposto alla mia domanda, era sul punto in cui si aveva le Risorse.ResourceManager, nel mio caso i file resx sono pubblici resx generato così è stato [MyNamespace].[MyResourceFile].ResourceManager.GetString("MyString");
    • dice che ho bisogno di un’istanza di Risorse.ResourceManager in ordine di chiamata ottenere stringa su di esso
    • Probabilmente non hanno un uso dichiarazione per il vostro spazio dei nomi è di default per il using System e la chiamata alla classe effettiva, piuttosto che l’istanza di fuori del generata automaticamente Resources classe.
    • Che strano. Avrebbero dovuto lavorare sodo per questo. Mi propongono di creare una Connessione bug, nel caso in cui trovare il tempo per affrontare questo. Puoi dare maggiori dettagli su cosa si intende? Quale parte del DataGridView è alla ricerca di DisplayNameAttribute?
    • Spiacenti, non importa. Ho mescolato la Descrizione e DisplayName attributi.
    • Nessun problema. Sono contento che sei arrivato fino in fondo al problema. Felice di aiutare, se posso.
    • grazie per l’approccio. Sto eseguendo la query sul db invece di usare file di risorse. Nella mia applicazione, ci sarà una dropdownlist che contiene lingue supportate, e l’utente può scegliere uno qualsiasi di essi. Quindi come posso cambiare la lingua ? Utilizzando questo approccio.
    • anche in questo approccio come faccio a passare il locale ?
    • Il locale per la risorsa di look-up viene da CultureInfo.CurrentUICulture. Il gestore delle risorse già ha la cultura impostato su di esso.
    • Ok Grazie @JeffYates per il vostro aiuto. Ci sono un certo numero di etichette e controlli come bottoni,tooltip, etc. Il modo di cui sopra discutere circa le proprietà di una classe. C’è un modo per implementare la localizzazione di questo tipo di oggetti ?
    • .NET fornisce un supporto integrato per la localizzazione di WinForms. Basta impostare Localizzabile al vero, in forma di design e di dirgli cosa è la cultura il layout attuale è per. In WPF, dovrete cercare LocBaml.
    • Oggi con C# non potremmo usare nameof invece di mantenere un ResourceStrings classe? Dovrebbe essere simile nameof(Resources.MyResource),

  3. 41

    Ecco la soluzione che ho finito in un assembly separato (chiamato “Comune” nel mio caso):

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

    con il codice per cercare di risorse:

      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
      }

    Utilizzo tipico potrebbe essere:

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

    Cosa è molto brutta come utilizzare le stringhe letterali per la chiave di risorsa. L’utilizzo di una costante c’significherebbe modificare le Risorse.Designer.cs, che è probabilmente una buona idea.

    Conclusione: io non sono felice con quello, ma sono anche meno felice per Microsoft che non può fornire qualcosa di utile per un’attività comune.

    • Molto utile. Grazie. In futuro, spero che Microsoft si presenta con una bella soluzione che offre fortemente tipizzati di riferimento, le risorse.
    • ya questa stringa roba fa schifo davvero difficile 🙁 Se si potrebbe ottenere la proprietà nome della proprietà che utilizza l’attributo, si potrebbe fare in convention over configuration modo, ma questo non sembra essere possibile. La cura per la “fortemente tpyed” Enumerazioni, si potrebbe utilizzare, inoltre, non è davvero manutenibile :/
    • Questa è una buona soluzione. Ho appena non iterare attraverso la raccolta di ResourceManager proprietà. Invece si può semplicemente ottenere direttamente la struttura dal tipo del parametro: PropertyInfo property = resourceManagerProvider.GetProperty(resourceKey, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
    • Combinate questo con @zielu1 del modello T4 di auto-generare le chiavi di risorsa, e si dispone di un degno vincitore!
  4. 19

    Utilizzando il Display attributo (dal Sistema.ComponentModel.DataAnnotations) e il nomedi() espressione in C# 6, si otterrà un localizzata e fortemente tipizzato soluzione.

    [Display(ResourceType = typeof(MyResources), Name = nameof(MyResources.UserName))]
    public string UserName { get; set; }
    • In questo esempio, ciò che è “MyResources”? Fortemente tipizzato file resx? Una classe personalizzata?
    • Sì, hai ragione.
  5. 14

    Si potrebbe utilizzare T4 per generare costanti. Ho scritto:

    <#@ 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#>";
      <# } #>
      }
     }
    }
    • Che cosa sarebbe l’output essere come?
  6. 9

    Questa è una vecchia questione, ma credo che questo sia un problema molto comune, e qui è la mia soluzione in MVC 3.

    In primo luogo, un modello T4 è necessario per generare costanti per evitare brutte stringhe. Abbiamo un file di risorse Etichette.resx’ contiene tutte le etichette stringhe. Pertanto il modello T4 utilizza il file di risorse direttamente,

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

    Quindi, un metodo di estensione creata per localizzare il “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” attributo è sostituito da ” DisplayLabel’ attributo per leggere le Etichette.resx’ automaticamente,

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

    Dopo tutti quei lavori di preparazione, tempo di toccare quelli di default attributi di convalida. Io sto usando il ‘Necessario’ attributo come un esempio,

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

    Ora, Siamo in grado di applicare tali attributi, nel nostro modello,

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

    Per impostazione predefinita, il nome della proprietà è utilizzata come chiave per cercare ‘Etichetta.resx’, ma se si imposta attraverso ‘DisplayLabel’, utilizzerà invece.

  7. 6

    È possibile sottoclasse DisplayNameAttribute per fornire i18n, sostituendo uno dei metodi. Come così. edit: Si potrebbe avere a stabilirsi per l’utilizzo di una costante per la chiave.

    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" } } } });
        }
    }
    • La soluzione migliore!
    • Codice simile per la Categoria attributo come bene.
  8. 2

    Io uso questo modo di risolvere nel mio caso

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

    Con il codice

    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);
            }
        }
    
    }
  9. 1

    Bene, l’assemblea è Microsoft.VisualStudio.Modeling.Sdk.dll. che viene fornito con Visual Studio SDK (Con Integrazione di Visual Studio Pacchetto).

    Ma che sarebbe stato utilizzato in molto lo stesso modo come attributo; non c’è modo di utilizzare fortemente tipi di risorse attributi semplicemente perché non sono costante.

  10. 0

    Mi scuso per l’VB.NET codice, il C# è un po ‘ arrugginito… Ma avrai un’idea, giusto?

    Prima di tutto, creare una nuova classe: LocalizedPropertyDescriptor, che eredita PropertyDescriptor. Ignorare il DisplayName proprietà come questo:

    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 è ResourceManager del file di risorse che contiene le traduzioni.

    Avanti, a implementare ICustomTypeDescriptor in classe con il localizzate proprietà, e ignorare il GetProperties metodo:

    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

    È ora possibile utilizzare il “DisplayName” attributo per memorizzare un riferimento a un valore in un file di risorse…

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

    prop_description è la chiave nel file di risorse.

    • La prima parte della soluzione, è quello che ho fatto… fino a quando ho dovuto risolvere il “che cosa sono Alcune.ResourceManager ?” domanda. Dovrei dare un seconda stringa letterale come “MyAssembly.Risorse.Risorsa” ? troppo pericoloso! Come per la seconda parte (ICustomTypeDescriptor) io non credo che sia effettivamente utile
    • Marc Gravell la soluzione è la strada da percorrere se non hai bisogno di altro che di una traduzione DisplayName — io uso personalizzato descrittore per altre cose, e questa è stata la mia soluzione. Non c’è modo di farlo senza fornire una sorta di chiave, però.

Lascia un commento