Autor en Google+
Saltar al contenido

Variable not found. Artículos, noticias, curiosidades, reflexiones... sobre el mundo del desarrollo de software, internet, u otros temas relacionados con la tecnología. C#, ASP.NET, ASP.NET MVC, HTML, Javascript, CSS, jQuery, Ajax, VB.NET, componentes, herramientas...

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, noticias, curiosidades, reflexiones... sobre el mundo del desarrollo
de software, internet, u otros temas relacionados con la tecnología

¡Microsoft MVP!
martes, 15 de marzo de 2011
ASP.NET MVCComo sabemos, en MVC 2 y MVC 3 el atributo [DisplayName] nos permite especificar en cada propiedad de las entidades del Modelo un nombre descriptivo que, entre otras cosas, es utilizado como etiqueta en formularios si empleamos el helper Html.LabelFor().

Así, dada una clase del Modelo como la siguiente:
public class Persona
{
    [DisplayName("Nombre completo")]
    public string Name { get; set; }
 
    [DisplayName("Edad actual")]
    public int Age { get; set; }
}
si implementamos una vista de edición sobre ella similar a la mostrada a continuación, vemos el resultado que obtendríamos en tiempo de ejecución:

Vista de edición y resultado en tiempo de ejecución
Este mecanismo resulta muy cómodo y útil la mayoría de las veces, sin embargo, cuando estamos desarrollando sitios multiidioma, rápidamente vamos a encontrarnos con que este enfoque no es suficiente, puesto que asocia una única descripción textual a la propiedad independientemente de la cultura activa.

A diferencia de otros atributos como las anotaciones de validación, DisplayName no incorpora ninguna forma para indicar una referencia hacia el recurso localizado, es decir, identificar el tipo (la clase) que representa al archivo de recursos (.resx) donde se encuentra la traducción del término, así como la propiedad o clave en la que lo encontraremos. Por ejemplo, el data annotation [Required] permite hacerlo de la siguiente forma:
[Required(
    ErrorMessageResourceName="NombreObligatorio", 
    ErrorMessageResourceType=typeof(Resources.Textos)
)]
public string Name { get; set; }
Ahora bastaría con crear la carpeta App_GlobalResources un archivo de recursos llamado “Textos.resx” (obviamente el nombre puede ser cualquiera, siempre que coincida con el referenciado en el parámetro ErrorMessageResourceType anterior), e introducir en él la clave “NombreObligatorio” con su correspondiente valor. Para añadir la traducción al inglés, bastaría con hacer lo mismo sobre el archivo Textos.en.resx, y así sucesivamente.

Archivos de recursos

Afortunadamente, a partir de ASP.NET MVC 3 podemos utilizar la anotación Display, disponible en System.ComponentModel.DataAnnotations a partir de la versión 4 del framework, de la siguiente forma:
[Display(Name="Nombre", ResourceType=typeof(Resources.Textos))]
public string Name { get; set; }
Como podréis intuir, el parámetro Name indica la clave del recurso incluido en la clase especificada en ResourceType.

Sin embargo, el uso de este atributo requiere que los elementos del archivo de recursos sean de acceso público, y no interno, como son por defecto cuando creamos los .resx en App_GlobalResources. Si no tenemos en cuenta este aspecto, podemos encontrarnos con un error como el siguiente:

Cannot retrieve property 'Name' because localization failed.  Type 'Resources.Textos' is not public or does not contain a public static string property with the name 'Nombre'.

Ante este error, tenemos varias fórmulas para conseguir acceso a los recursos localizados:
  • la primera de ellos a lo bestia, accediendo a la clase generada desde el archivo de recursos (por ejemplo Textos.Designer.cs) y cambiando los modificadores de la clase y sus miembros de “internal” a “public”. Obviamente, este cambio sólo nos vale hasta que se vuelva a generar el código , cosa que ocurrirá al modificar o añadir nuevas cadenas de texto, por lo que no es nada recomendable.
  • otra, sacar los recursos de la carpeta App_GlobalResources y moverlos a cualquier otra carpeta del proyecto (por ejemplo, /Recursos). Tras ello, en las propiedades del archivo .resx podemos modificar la “Herramienta personalizada” y establecerla a “PublicResXFileCodeGenerator” para asegurar su acceso público.
  • una última sería crear ensamblados satélite, lo cual requiere la añadir un nuevo proyecto de biblioteca de clases en la solución, en el que introduciremos exclusivamente los archivos de recursos localizados.
Pero si ninguna de ellas nos convence, o bien estamos todavía trabajando con MVC 2 y .NET 3.5SP1, todavía tenemos otra posibilidad:

[LocalizedDisplayNameAtribute]

Es realmente sencillo implementar un atributo que nos permita conseguir algo parecido a lo descrito anteriormente. Simplemente heredando de DisplayNameAttribute y sobrescribiendo su propiedad DisplayName podemos hacer que su valor sea tomado desde los recursos de la aplicación:
public class LocalizedDisplayNameAttribute : DisplayNameAttribute
{
    private readonly string _resourceName;
    public LocalizedDisplayNameAttribute(string resourceName)
    {
        _resourceName = resourceName;
    }
 
    public override string DisplayName
    {
        get { return Resources.Textos.ResourceManager.GetString(_resourceName); }
    }
}
Archivos de recursosObservad que para simplificar la sintaxis de uso y su implementación, estamos asumiendo que los recursos se encuentran en Resources.Textos, la clase generada automáticamente a partir del archivo Textos.resx.

De esta forma, ahora decoraremos las propiedades del modelo no con el texto que queremos que aparezca en pantalla, sino con la clave del recurso, y dado que la clase de recursos está definida en el atributo, no será necesario indicarla:
public class Persona
{
    [LocalizedDisplayName("Nombre")]
    public string Name { get; set; }
 
    [LocalizedDisplayName("Edad")]
    public int Age { get; set; }
}
Y por si os resulta de interés, he dejado un proyecto de demostración de este atributo en Skydrive.

Publicado en: Variable not found.

Estos contenidos se publican bajo una licencia de Creative Commons Licencia Reconocimiento-No comercial-Compartir bajo la misma licencia 3.0 España de Creative Commons

3 Comentarios:

yense dijo...

Hola amigo en caso de ese ejemplo veo que lo estas usando mediante una clase que creastes pero si se da en el caso que solo queremos cambiar el contenido de la pagina me explico

tengo una pagina que no tiene formulario alguno solo muestra noticias y algunas imagenes como puedo hacer ese modelo

o mejor en su ejemplo como UD lo cambia el

-legend- Persona/ -legend-

me puedes explicar

gracias

José M. Aguilar dijo...

Buenas, yense.

Si lo que quieres es que la vista sea diferente dependiendo del idioma actual, mejor que sigas el procedimiento descrito en este otro post:

http://www.variablenotfound.com/2011/03/retornar-vistas-dependiendo-de-la.html

Saludos.

Anaya.-www.microzep.com dijo...

Muy buen Aporte!! Me sacastes de un apuro..

Saludos,

Salvador Anaya