DisplayName atributo de los recursos?

160

Tengo una aplicación localizada y me pregunto si es posible DisplayNameestablecer la propiedad de un determinado modelo a partir de un Recurso.

Me gustaría hacer algo como esto:

public class MyModel {
  [Required]
  [DisplayName(Resources.Resources.labelForName)]
  public string name{ get; set; }
}

Pero no puedo hacerlo, como dice el compilador: "Un argumento de atributo debe ser una expresión constante, tipo de expresión o expresión de creación de matriz de un tipo de parámetro de atributo" :(

¿Hay alguna solución? Estoy enviando etiquetas manualmente, ¡pero las necesito para la salida del validador!

Palantir
fuente

Respuestas:

112

¿Qué tal escribir un atributo personalizado:

public class LocalizedDisplayNameAttribute: DisplayNameAttribute
{
    public LocalizedDisplayNameAttribute(string resourceId) 
        : base(GetMessageFromResource(resourceId))
    { }

    private static string GetMessageFromResource(string resourceId)
    {
        // TODO: Return the string from the resource file
    }
}

que podría usarse así:

public class MyModel 
{
    [Required]
    [LocalizedDisplayName("labelForName")]
    public string Name { get; set; }
}
Darin Dimitrov
fuente
Sí, trabajé en una solución similar esta mañana y es factible. Luego encontré esta publicación que tiene el mismo enfoque: adamyan.blogspot.com/2010/02/…
Palantir
23
@Gunder su publicación, debajo de esta (con más votos), es una solución mucho mejor. Solo para personas que solo leen las publicaciones aceptadas
321X
1
En realidad, esto NO funciona para acceder a diferentes traducciones, ya que devolverá el mismo valor para todos los usuarios sin importar qué. Almacenar resourceid en una variable local y anular DisplayName en su lugar
Fischer
55
Sugerencia para TODO: return Resources.Language.ResourceManager.GetString (resourceId);
Leonel Sanches da Silva
44
Debe usar: [Display (Name = "labelForName", ResourceType = typeof (Resources.Resources))] como se describe a continuación ...
Frank Boucher
375

Si usa MVC 3 y .NET 4, puede usar el nuevo Displayatributo en el System.ComponentModel.DataAnnotationsespacio de nombres. Este atributo reemplaza al DisplayNameatributo y proporciona mucha más funcionalidad, incluido el soporte de localización.

En su caso, lo usaría así:

public class MyModel
{
    [Required]
    [Display(Name = "labelForName", ResourceType = typeof(Resources.Resources))]
    public string name{ get; set; }
}

Como nota al margen, este atributo no funcionará con recursos dentro App_GlobalResourceso App_LocalResources. Esto tiene que ver con la herramienta personalizada ( GlobalResourceProxyGenerator) que utilizan estos recursos. En su lugar, asegúrese de que su archivo de recursos esté configurado en 'Recurso incorporado' y use la herramienta personalizada 'ResXFileCodeGenerator'.

(Como nota adicional, no deberías usar App_GlobalResourceso App_LocalResourcescon MVC. Puedes leer más sobre por qué este es el caso aquí )

René
fuente
Esto es bueno para el ejemplo específico utilizado aquí, pero no funcionará para la mayoría de los establecedores de propiedades dinámicas que desean cadenas.
kingdango
1
Esto es bueno cuando tenemos todo nuestro entorno local con nosotros antes de implementarlo en producción. Pero si quiero llamar a db para cadenas de db, este tipo de enfoque no es apropiado. También la desventaja del archivo de recursos, es que requiere compilación nuevamente para efectuar los cambios, eso significa que necesita lanzar otra versión al cliente. No digo esto como un mal enfoque, por favor no se sienta así
kbvishnu
Solo un problema: tenemos que saber el tipo de recurso en el modelo. Tengo un modelo DLL y un sitio web en dos proyectos distintos. Me gustaría poder establecer nombres para mostrar en el modelo y establecer el tipo de recurso en el sitio web ...
Kek
1
Obtenga Resharper y cree el archivo de recursos en su proyecto, y se lo recordará automáticamente y luego lo ayudará a mover el Nombre al archivo de recursos.
anIBMer
14
Con C # 6, en lugar de Name = "labelForName"usted también puede usar Name = nameof(Resources.Resources.labelForName).
Uwe Keim
35

Si abre su archivo de recursos y cambia el modificador de acceso a público o interno, generará una clase a partir de su archivo de recursos que le permitirá crear referencias de recursos fuertemente tipadas.

Opción para generar código de archivo de recursos

Lo que significa que puede hacer algo como esto (usando C # 6.0). Entonces no tienes que recordar si el primer nombre estaba en minúsculas o en camello. Y puede ver si otras propiedades usan el mismo valor de recurso con buscar todas las referencias.

[Display(Name = nameof(PropertyNames.FirstName), ResourceType = typeof(PropertyNames))]
public string FirstName { get; set; }
Tikall
fuente
¿Funcionará esto con Winforms? Tengo una clase que agregué Mostrar anotación de recursos y utilicé el método GetAttributeFrom del enlace para obtener el nombre de la propiedad, ¡pero no muestra la localizada!
Dark_Knight
2
¿Está utilizando attribute.Name o attribute.GetName () para obtener el texto localizado? La documentación para .Name dice "No use esta propiedad para obtener el valor de la propiedad Name. Utilice el método GetName en su lugar". msdn.microsoft.com/en-us/library/…
Tikall
Sí, descubrí que tenía que usar GetName (). Gracias
Dark_Knight
20

Actualizar:

Sé que es demasiado tarde, pero me gustaría agregar esta actualización:

Estoy usando el proveedor de metadatos de modelos convencionales que presentado por Phil Haacked es más potente y fácil de aplicar. Míralo : ConventionalModelMetadataProvider


Vieja respuesta

Aquí, si desea admitir muchos tipos de recursos:

public class LocalizedDisplayNameAttribute : DisplayNameAttribute
{
    private readonly PropertyInfo nameProperty;

    public LocalizedDisplayNameAttribute(string displayNameKey, Type resourceType = null)
        : base(displayNameKey)
    {
        if (resourceType != null)
        {
            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);
        }
    }
}

Entonces úsalo así:

    [LocalizedDisplayName("Password", typeof(Res.Model.Shared.ModelProperties))]
    public string Password { get; set; }

Para ver el tutorial completo de localización, consulte esta página .

Wahid Bitar
fuente
1
+1. La solución de Haack es definitivamente la más elegante en comparación con las demás aquí. Se adapta muy bien al estilo de programación basado en convenciones en ASP.NET MVC y se implementa fácilmente a través de un solo comando nuget y una sola línea de código en Global.asax.cs.
Nilzor
11

Obtuve la respuesta de Gunders al trabajar con mi App_GlobalResources eligiendo las propiedades de los recursos y cambié "Herramienta personalizada" a "PublicResXFileCodeGenerator" y creé la acción a "Recursos integrados". Observe el comentario de Gunders a continuación.

ingrese la descripción de la imagen aquí

Funciona de maravilla :)

Magnus Karlsson
fuente
Esto da como resultado un archivo de recursos que se compilará como si hubiera agregado un archivo de recursos fuera de App_GlobalResources. Por lo tanto, su archivo de recursos ya no se comportará como un archivo de recursos "App_GlobalResources", lo cual está absolutamente bien. Pero deberías ser consciente de ello. Por lo tanto, ya no tiene los "beneficios" de colocar el archivo de recursos en App_GlobalResources. Podrías haberlo puesto en otro lugar.
René
5
public class Person
{
    // Before C# 6.0
    [Display(Name = "Age", ResourceType = typeof(Testi18n.Resource))]
    public string Age { get; set; }

    // After C# 6.0
    // [Display(Name = nameof(Resource.Age), ResourceType = typeof(Resource))]
}
  1. Defina ResourceType del atributo para que busque un recurso
  2. Defina el nombre del atributo que se usa para la clave del recurso, después de C # 6.0, puede usarlo nameofpara obtener un soporte de tipo seguro en lugar de codificar la clave.

  3. Establece la cultura del hilo actual en el controlador.

Resource.Culture = CultureInfo.GetCultureInfo("zh-CN");

  1. Establecer la accesibilidad del recurso al público

  2. Mostrar la etiqueta en cshtml como este

@Html.DisplayNameFor(model => model.Age)

code4j
fuente