remarks generate example c# visual-studio-2008 localization attributes

generate - params comments c#



LocalizaciĆ³n de DisplayNameAttribute (10)

Aquí está la solución que terminé en una asamblea separada (llamada "Común" en mi 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 el código para buscar el recurso:

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 }

El uso típico sería:

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

Lo que es bastante feo ya que uso cadenas literales para la clave de recursos. Usar una constante significaría modificar Resources.Designer.cs, lo que probablemente no sea una buena idea.

Conclusión: no estoy contento con eso, pero estoy aún menos contento con Microsoft que no puede proporcionar nada útil para una tarea tan común.

Estoy buscando una forma de localizar los nombres de propiedades que se muestran en un PropertyGrid. El nombre de la propiedad puede ser "anulado" utilizando el atributo DisplayNameAttribute. Lamentablemente, los atributos no pueden tener expresiones no constantes. Por lo tanto, no puedo usar recursos fuertemente tipados, como:

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

Eché un vistazo y encontré una sugerencia para heredar de DisplayNameAttribute para poder usar el recurso. Terminaría con un código como:

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

Sin embargo, pierdo los beneficios de recursos fuertemente tipados, lo que definitivamente no es algo bueno. Luego me encontré DisplayNameResourceAttribute que puede ser lo que estoy buscando. Pero se supone que debe estar en el espacio de nombres Microsoft.VisualStudio.Modeling.Design y no puedo encontrar la referencia que se supone que debo agregar para este espacio de nombres.

¿Alguien sabe si hay una manera más fácil de lograr la localización DisplayName de una buena manera? o si hay una forma de usar lo que Microsoft parece estar usando para Visual Studio?


Bueno, el ensamblado es Microsoft.VisualStudio.Modeling.Sdk.dll . que viene con Visual Studio SDK (con el paquete de integración de Visual Studio).

Pero se usaría de la misma manera que tu atributo; no hay forma de usar fuertemente los recursos en atributos simplemente porque no son constantes.


Esta es una vieja pregunta, pero creo que este es un problema muy común, y aquí está mi solución en MVC 3.

En primer lugar, se necesita una plantilla T4 para generar constantes para evitar cadenas desagradables. Tenemos un archivo de recursos ''Labels.resx'' contiene todas las cadenas de etiquetas. Por lo tanto, la plantilla T4 usa el archivo de recursos directamente,

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

Luego, se crea un método de extensión para localizar el ''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; } } }

El atributo ''DisplayName'' se reemplaza por el atributo ''DisplayLabel'' para poder leer automáticamente ''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; } } } }

Después de todo el trabajo de preparación, es momento de tocar esos atributos de validación predeterminados. Estoy usando el atributo ''Requerido'' como un ejemplo,

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

Ahora, podemos aplicar esos atributos en nuestro modelo,

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

De forma predeterminada, el nombre de propiedad se usa como la clave para buscar ''Label.resx'', pero si lo configura a través de ''DisplayLabel'', usará eso en su lugar.


Estamos haciendo esto para una serie de atributos con el fin de admitir múltiples idiomas. Hemos adoptado un enfoque similar al de Microsoft, donde anulan sus atributos básicos y pasan el nombre de un recurso en lugar de la cadena real. El nombre del recurso se usa para realizar una búsqueda en los recursos de la DLL para que la cadena actual regrese.

Por ejemplo:

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

Puede llevar esto un paso más allá al usar el atributo y especificar los nombres de recursos como constantes en una clase estática. De esa manera, obtienes declaraciones como.

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

Actualizar
ResourceStrings se vería algo así como (tenga en cuenta que cada cadena se referiría al nombre de un recurso que especifica la cadena real):

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


Existe el msdn.microsoft.com/en-us/library/… de System.ComponentModel.DataAnnotations en .NET 4. Funciona en MVC 3 PropertyGrid .

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

Esto busca un recurso llamado MyResources usuario en su archivo MyResources MyResources.


Me disculpo por el código de VB.NET, mi C # está un poco oxidado ... Pero entenderás la idea, ¿verdad?

Antes que nada, crea una nueva clase: LocalizedPropertyDescriptor , que hereda PropertyDescriptor . Anule la propiedad DisplayName esta manera:

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 es el ResourceManager del archivo de recursos que contiene sus traducciones.

A continuación, implemente ICustomTypeDescriptor en la clase con las propiedades localizadas y anule el método 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

Ahora puede usar el atributo ''DisplayName` para almacenar una referencia a un valor en un archivo de recursos ...

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

prop_description es la clave en el archivo de recursos.


Podría usar T4 para generar constantes. Yo escribí uno:

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


Puede subclase DisplayNameAttribute para proporcionar i18n, anulando uno de los métodos. Al igual que. editar: puede que tenga que conformarse con usar una constante para la clave.

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


Usando el atributo Display (de System.ComponentModel.DataAnnotations) y el nameof() la nameof() en C # 6, obtendrá una solución localizada y fuertemente tipada.

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


Yo uso esta forma de resolver en mi caso

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

Con el código

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