Estos atributos nos permiten especificar las cadenas a utilizar en la interfaz de la aplicación cuando se hace referencia al campo, se detectan errores de validación, etc. Esto resulta muy útil para tener definidas estas cadenas en un único lugar de la aplicación y que los mensajes al usuario sean uniformes a lo largo de toda nuestra aplicación.
Sin embargo, en este mundo cada vez más globalizado, cada vez son menos las aplicaciones cuyo interfaz se define en un único idioma. Entonces, ¿cómo utilizamos los atributos del namespace DataAnnotations de forma que nuestros textos se adapten al idioma del usuario?
En la serie de artículos que comienzo aquí trataré de explicar las diferentes opciones que tenemos a la hora de abordar este problema.
Creando la aplicación de ejemplo
Para mostrar las diferentes soluciones al problema vamos a crear un nueva Aplicación ASP.NET LocalizeDataAnnotations en nuestro Visual Studio, seleccionando la plantilla vacía y marcando el check para incluir las referencias a MVC.
Vamos a crear una nueva Interfaz IPersona.cs/IPersona.vb en nuestra carpeta Model en la que definiremos las propiedades que tendrán los objetos de nuestro modelo. Como veremos, el sentido de crear esta interfaz es el de poder utilizar una misma Vista para todos los ejemplos.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace LocalizeDataAnnotations.Models { public interface IPersona { string Nombre { get; set; } string Apellido { get; set; } string Ciudad { get; set; } string Email { get; set; } } }
Public Interface IPersona Property Nombre As String Property Apellido As String Property Ciudad As String Property Email As String End Interface
A continuación crearemos una Vista Index en la carpeta Views/Home que utilizaremos para ver los resultados de nuestros ejemplos. Esta vista utilizará como Modelo una instancia de la interfaz IPersona y simplemente muestra un formulario que nos permite asignar valores a las propiedades y enviarlos al servidor para su validación:
@model LocalizeDataAnnotations.Models.IPersona @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Index</title> </head> <body> <div> @using (Html.BeginForm()) { @Html.EditorForModel() <br /> <input type="submit" value="Validar" /> } </div> </body> </html>
@ModelType LocalizeDataAnnotations.IPersona @Code Layout = Nothing End Code <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Index</title> </head> <body> <div> @Using (Html.BeginForm()) @Html.EditorForModel() @:<br /> @:<input type="submit" value="Validar" /> End Using </div> </body> </html>
Añadiendo los atributos DataAnnotations
Para poder probar nuestra vista y ver la funcionalidad de los atributos del Namespace DataAnnotations creamos una nueva clase Persona que implementa la interfaz IPersona en la carpeta Model. Para el ejemplo vamos a establecer el nombre de cada propiedad a través del atributo DisplayNameAttribute y dos atributos de validación: un RequiredAttribute con mensaje de error personalizado para establecer la propiedad Nombre como obligatoria, y un StringLengthAttribute para establecer que la longitud de la propiedad Ciudad debe estar entre 4 y 20 caracteres.
using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Web; namespace LocalizeDataAnnotations.Models { public class Persona: IPersona { [Required(ErrorMessage = "Debe introducir un nombre")] [DisplayName("Nombre")] public string Nombre { get; set; } [DisplayName("Apellido")] public string Apellido { get; set; } [DisplayName("Ciudad")] [StringLength(20, MinimumLength=4, ErrorMessage="El nombre de Ciudad debe tener una longitud entre 4 y 20 caracteres")] public string Ciudad { get; set; } [DisplayName("Correo electrónico")] public string Email { get; set; } } }
Imports System.ComponentModel Imports System.ComponentModel.DataAnnotations Public Class Persona Implements IPersona <Required(ErrorMessage:="Debe introducir un nombre")> _ <DisplayName("Nombre")> _ Public Property Nombre As String Implements IPersona.Nombre <DisplayName("Apellido")> _ Public Property Apellido As String Implements IPersona.Apellido <DisplayName("Ciudad")> _ <StringLength(20, MinimumLength:=4, ErrorMessage:="El nombre de Ciudad debe tener una longitud entre 4 y 20 caracteres")> _ Public Property Ciudad As String Implements IPersona.Ciudad <DisplayName("Correo electrónico")> _ Public Property Email As String Implements IPersona.Email End Class
Para finalizar y probar el ejemplo añadimos un controlador HomeController en la carpeta Controllers. En el controlador crearemos una única acción Index que recibe un parámetro del tipo Persona y lo pasa a nuestra vista Index.
using LocalizeDataAnnotations.Models; using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace LocalizeDataAnnotations.Controllers { public class HomeController : Controller { public ViewResult Index(Persona persona) { return View(persona); } } }
Imports System.Web.Mvc Namespace Controllers Public Class HomeController Inherits Controller Function Index(persona As Persona) As ViewResult Return View(persona) End Function End Class End Namespace
Al arrancar nuestra aplicación podremos ver en el navegador un formulario para introducir los valores de la clase Persona con etiquetas que toman el texto de los atributos DisplayNameAttribute al hacer click en el botón Validar la información se manda al servidor y, en caso de existir errores de validación nos aparecerán, tomando los textos de los mensajes de los atributos de validación.
Usar archivos de recursos
Hemos definido los textos del modelo a partir de los atributos Data Annotations, ahora veremos qué podemos hacer para adaptar estos mensajes al idioma del usuario.
La solución más sencilla, y que viene ya implementada en los atributos, es la de usar archivos de recursos. Para nuestro ejemplo crearemos una carpeta Content y añadiremos dos archivos de recursos:
- El archivo Textos.resx que tendrá los mensajes en español y que será el que utilice nuestra aplicación por defecto
- El archivo Textos.en.resx que tendrá los mensajes en inglés y que utilizará nuestra aplicación en caso de que el usuario tenga definida una cultura con idioma inglés
No debemos olvidar establecer el Modificador de acceso de los archivos a Public.
He modificado algunos de los textos que utilizábamos en el ejemplo anterior para que se vea la diferencia cuando probemos la solución.
A continuación crearemos una nueva clase PersonaResource que herede de nuestra interfaz IPersona y en la que estableceremos los textos de los atributos a través de los archivos de recursos recién creados.
using LocalizeDataAnnotations.Content; using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Web; namespace LocalizeDataAnnotations.Models { public class PersonaResource: IPersona { [Required(ErrorMessageResourceName = "NombreObligatorio", ErrorMessageResourceType = typeof(Textos))] [Display(Name="Nombre", ResourceType=typeof(Textos))] public string Nombre { get; set; } [Display(Name = "Apellido", ResourceType = typeof(Textos))] public string Apellido { get; set; } [Display(Name = "Ciudad", ResourceType = typeof(Textos))] [StringLength(20, MinimumLength = 4, ErrorMessageResourceName="CiudadErrorLongitud", ErrorMessageResourceType=typeof(Textos))] public string Ciudad { get; set; } [Display(Name = "Email", ResourceType = typeof(Textos))] public string Email { get; set; } } }
Imports System.ComponentModel.DataAnnotations Imports LocalizeDataAnnotations.My.Resources Public Class PersonaResource Implements IPersona <Required(ErrorMessageResourceName:="NombreObligatorio", ErrorMessageResourceType:=GetType(Textos))> _ <Display(Name:="Nombre", ResourceType:=GetType(Textos))> _ Public Property Nombre As String Implements IPersona.Nombre <Display(Name:="Apellido", ResourceType:=GetType(Textos))> _ Public Property Apellido As String Implements IPersona.Apellido <Display(Name:="Ciudad", ResourceType:=GetType(Textos))> _ <StringLength(20, MinimumLength:=4, ErrorMessageResourceName:="CiudadErrorLongitud", ErrorMessageResourceType:=GetType(Textos))> _ Public Property Ciudad As String Implements IPersona.Ciudad <Display(Name:="Email", ResourceType:=GetType(Textos))> _ Public Property Email As String Implements IPersona.Email End Class
Por último añadimos una nueva acción Resource a nuestro controlador HomeController que pasa un objeto del tipo PersonaResource a la vista Index.
public ViewResult Resource(PersonaResource persona) { return View("Index", persona); }
Function Resource(persona As PersonaResource) As ViewResult Return View("Index", persona) End Function
Para probar nuestra aplicación, establecemos la cultura del interfaz al idioma español en el archivo Web.config añadiendo la entrada globalization en la sección system.web.
<system.web> <compilation debug="true" targetFramework="4.5"/> <httpRuntime targetFramework="4.5"/> <globalization culture="auto" uiCulture="es" /> </system.web>
Ahora, si arrancamos la aplicación y accedemos a la ruta http://<rutapublicacion>/Home/Resource podremos ver como los textos han cambiado y ahora se muestran los del archivo de recursos.
Si modificamos el Web.config para establecer la cultura del interfaz al idioma inglés, podremos ver cómo nuestra aplicación utiliza las cadenas en inglés.
<system.web> <compilation debug="true" targetFramework="4.5"/> <httpRuntime targetFramework="4.5"/> <globalization culture="auto" uiCulture="en" /> </system.web>
En las próximas entradas abordaré otras posibles soluciones repasando los pros y los contras de cada una de ellas.
Continúa con:
ASP.NET. Localizando DataAnnotations (II) - Utilizar una plantilla T4 para conectar a cualquier origen
ASP.NET. Localizando Data Annotations (y III) - Personalizar Atributos de Validación
El código completo de los ejemplos mostrados a lo largo de los 3 artículos, tanto en C# como en VB.Net, puede descargarse de:
Códigos de muestra - Ejemplos MSDN
No hay comentarios:
Publicar un comentario