El espacio de nombres
System.ComponentModel.DataAnnotations proporciona clases de atributos que se usan para definir los metadatos para ASP.NET MVC y los controles de ASP.NET.
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