Hacer que el contenido de una aplicación de ASP.NET Core sea localizable
Nota:
Esta no es la versión más reciente de este artículo. Para la versión actual, consulta la versión .NET 8 de este artículo.
Advertencia
Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulta la Directiva de soporte técnico de .NET y .NET Core. Para la versión actual, consulta la versión .NET 8 de este artículo.
Importante
Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.
Para la versión actual, consulte la versión .NET 8 de este artículo.
Por Hisham Bin Ateya, Damien Bowden, Bart Calixto y Nadeem Afana
Una de las tareas para localizar una aplicación es encapsular el contenido localizable con código, lo que facilita reemplazar ese contenido por distintas referencias culturales.
IStringLocalizer
IStringLocalizer y IStringLocalizer<T> se diseñaron para mejorar la productividad al desarrollar aplicaciones localizadas. IStringLocalizer
usa ResourceManager y ResourceReader para proporcionar recursos específicos de la referencia cultural en tiempo de ejecución. La interfaz tiene un indizador y un IEnumerable
para devolver las cadenas localizadas. IStringLocalizer
no necesita que se almacenen las cadenas de idioma predeterminado en un archivo de recursos. Puede desarrollar una aplicación destinada a la localización sin necesidad de crear archivos de recursos al principio de la fase de desarrollo.
En el ejemplo de código siguiente se muestra cómo encapsular la cadena "About Title" para la localización.
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
namespace Localization.Controllers;
[Route("api/[controller]")]
public class AboutController : Controller
{
private readonly IStringLocalizer<AboutController> _localizer;
public AboutController(IStringLocalizer<AboutController> localizer)
{
_localizer = localizer;
}
[HttpGet]
public string Get()
{
return _localizer["About Title"];
}
}
En el código anterior, la implementación de IStringLocalizer<T>
procede de la inserción de dependencias. Si no se encuentra el valor localizado de "About Title, se devuelve la clave de indizador, es decir, la cadena "About Title".
Puede dejar las cadenas literales del idioma predeterminado en la aplicación y ajustarlas en el localizador, de modo que se pueda centrar en el desarrollo de la aplicación. Desarrolle una aplicación con el idioma predeterminado y prepárela para el proceso de localización sin necesidad de crear primero un archivo de recursos predeterminado.
También puede seguir el método tradicional y proporcionar una clave para recuperar la cadena de idioma predeterminado. El nuevo flujo de trabajo, que carece de archivo .resx de idioma predeterminado y simplemente ajusta los literales de cadena, puede ahorrar a muchos desarrolladores la sobrecarga de localizar una aplicación. Otros desarrolladores prefieren el flujo de trabajo tradicional, ya que puede facilitar el trabajo con literales de cadena largos, así como la actualización de las cadenas localizadas.
IHtmlLocalizer
Use la implementación de IHtmlLocalizer<TResource> para los recursos que contienen HTML. El HTML IHtmlLocalizer codifica los argumentos a los que se da formato en la cadena de recursos, pero no codifica como HTML la cadena de recursos en sí misma. En el código resaltado siguiente, solo el valor del parámetro name
está codificado en HTML.
using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Localization;
namespace Localization.Controllers;
public class BookController : Controller
{
private readonly IHtmlLocalizer<BookController> _localizer;
public BookController(IHtmlLocalizer<BookController> localizer)
{
_localizer = localizer;
}
public IActionResult Hello(string name)
{
ViewData["Message"] = _localizer["<b>Hello</b><i> {0}</i>", name];
return View();
}
NOTA: Por lo general, solo se localiza texto, no HTML.
IStringLocalizerFactory
En el nivel mínimo, IStringLocalizerFactory se puede recuperar de la inserción de dependencias:
public class TestController : Controller
{
private readonly IStringLocalizer _localizer;
private readonly IStringLocalizer _localizer2;
public TestController(IStringLocalizerFactory factory)
{
var type = typeof(SharedResource);
var assemblyName = new AssemblyName(type.GetTypeInfo().Assembly.FullName);
_localizer = factory.Create(type);
_localizer2 = factory.Create("SharedResource", assemblyName.Name);
}
public IActionResult About()
{
ViewData["Message"] = _localizer["Your application description page."]
+ " loc 2: " + _localizer2["Your application description page."];
return View();
}
En el código anterior se muestran los dos métodos factory.Create.
Recursos compartidos
Puede dividir las cadenas localizadas por controlador o área, o bien tener un solo contenedor. En la aplicación de ejemplo, se usa una clase de marcador denominada SharedResource
para los recursos compartidos. Nunca se llama a la clase de marcador:
// Dummy class to group shared resources
namespace Localization;
public class SharedResource
{
}
En el ejemplo siguiente, se usan los localizadores InfoController
y SharedResource
:
public class InfoController : Controller
{
private readonly IStringLocalizer<InfoController> _localizer;
private readonly IStringLocalizer<SharedResource> _sharedLocalizer;
public InfoController(IStringLocalizer<InfoController> localizer,
IStringLocalizer<SharedResource> sharedLocalizer)
{
_localizer = localizer;
_sharedLocalizer = sharedLocalizer;
}
public string TestLoc()
{
string msg = "Shared resx: " + _sharedLocalizer["Hello!"] +
" Info resx " + _localizer["Hello!"];
return msg;
}
Localización de vista
El servicio IViewLocalizer proporciona cadenas localizadas para una vista. La clase ViewLocalizer
implementa esta interfaz y busca la ubicación del recurso en la ruta de acceso del archivo de vista. En el código siguiente se muestra cómo usar la implementación predeterminada de IViewLocalizer
:
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@{
ViewData["Title"] = Localizer["About"];
}
<h2>@ViewData["Title"].</h2>
<h3>@ViewData["Message"]</h3>
<p>@Localizer["Use this area to provide additional information."]</p>
La implementación predeterminada de IViewLocalizer
busca el archivo de recursos según el nombre del archivo de vista. No se puede usar un archivo de recursos compartidos global. ViewLocalizer
implementa el localizador mediante IHtmlLocalizer
, por lo que Razor no codifica como HTML la cadena localizada. Puede parametrizar las cadenas de recursos y IViewLocalizer
codifica como HTML los parámetros, pero no la cadena de recursos. Observe el siguiente marcado Razor:
@Localizer["<i>Hello</i> <b>{0}!</b>", UserManager.GetUserName(User)]
Un archivo de recursos en francés puede contener los valores siguientes:
Clave | Valor |
---|---|
<i>Hello</i> <b>{0}!</b> |
<i>Bonjour</i> <b>{0} !</b> |
La vista representada contendría el marcado HTML del archivo de recursos.
Por lo general, solo se localiza texto, no HTML.
Para usar un archivo de recursos compartido en una vista, inserte IHtmlLocalizer<T>
:
@using Microsoft.AspNetCore.Mvc.Localization
@using Localization.Services
@inject IViewLocalizer Localizer
@inject IHtmlLocalizer<SharedResource> SharedLocalizer
@{
ViewData["Title"] = Localizer["About"];
}
<h2>@ViewData["Title"].</h2>
<h1>@SharedLocalizer["Hello!"]</h1>
Localización de DataAnnotations
Los mensajes de error de DataAnnotations se localizan con IStringLocalizer<T>
. Mediante la opción ResourcesPath = "Resources"
, es posible almacenar los mensajes de error en RegisterViewModel
en cualquiera de las rutas de acceso siguientes:
- Resources/ViewModels.Account.RegisterViewModel.fr.resx
- Resources/ViewModels/Account/RegisterViewModel.fr.resx
using System.ComponentModel.DataAnnotations;
namespace Localization.ViewModels.Account;
public class RegisterViewModel
{
[Required(ErrorMessage = "The Email field is required.")]
[EmailAddress(ErrorMessage = "The Email field is not a valid email address.")]
[Display(Name = "Email")]
public string Email { get; set; }
[Required(ErrorMessage = "The Password field is required.")]
[StringLength(8, ErrorMessage = "The {0} must be at least {2} characters long.",
MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage =
"The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
Los atributos que no son de validación están localizados.
Cómo usar una cadena de recursos para varias clases
En el código siguiente se muestra cómo usar una cadena de recursos para atributos de validación con varias clases:
services.AddMvc()
.AddDataAnnotationsLocalization(options => {
options.DataAnnotationLocalizerProvider = (type, factory) =>
factory.Create(typeof(SharedResource));
});
En el código anterior, SharedResource
es la clase correspondiente al archivo .resx donde se almacenan los mensajes de validación. Según este enfoque, DataAnnotations solo usa SharedResource
, en lugar del recurso de cada clase.
Configuración de los servicios de localización
Los servicios de localización se configuran en Program.cs
:
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
builder.Services.AddMvc()
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
.AddDataAnnotationsLocalization();
AddLocalization agrega los servicios de localización al contenedor de servicios, incluidas las implementaciones de
IStringLocalizer<T>
yIStringLocalizerFactory
. El código anterior también establece la ruta de acceso a los recursos en "Resources".AddViewLocalization agrega compatibilidad con los archivos de vista localizados. En este ejemplo, la localización de vista se basa en el sufijo del archivo de vista. Por ejemplo, "fr" en el archivo
Index.fr.cshtml
.AddDataAnnotationsLocalization agrega compatibilidad con mensajes de validación de
DataAnnotations
localizados mediante abstraccionesIStringLocalizer
.
Nota
Es posible que no pueda escribir comas decimales en campos decimales. Para que la validación de jQuery sea compatible con configuraciones regionales distintas del inglés que usan una coma (",") en lugar de un punto decimal y formatos de fecha distintos del de Estados Unidos, debe seguir unos pasos para globalizar la aplicación. Consulte este comentario 4076 de GitHub para instrucciones sobre cómo agregar la coma decimal.
Pasos siguientes
La localización de una aplicación también implica las tareas siguientes:
- Proporcionar recursos localizados para los idiomas y las referencias culturales que admite la aplicación
- Implementar una estrategia para seleccionar el idioma o la referencia cultural de cada solicitud
Recursos adicionales
- Proveedor de referencia cultural de direcciones URL mediante middleware como filtros en ASP.NET Core
- Aplicación global de RouteDataRequest CultureProvider con middleware como filtros
- Globalización y localización en ASP.NET Core
- Proporcionar recursos localizados para idiomas y referencias culturales en una aplicación de ASP.NET Core
- Estrategias para seleccionar el idioma y la referencia cultural en una aplicación de ASP.NET Core localizada
- Solución de problemas de localización de ASP.NET Core
- Globalización y localización de aplicaciones .NET
- Proyecto Localization.StarterWeb usado en el artículo
- Recursos en archivos .resx
- Kit de herramientas de aplicaciones multilingüe de Microsoft
- Localización y genéricos
By Rick Anderson, Damien Bowden, Bart Calixto, Nadeem Afana y Hisham Bin Ateya
Una de las tareas para localizar una aplicación es encapsular el contenido localizable con código, lo que facilita reemplazar ese contenido por distintas referencias culturales.
IStringLocalizer
IStringLocalizer y IStringLocalizer<T> se diseñaron para mejorar la productividad al desarrollar aplicaciones localizadas. IStringLocalizer
usa ResourceManager y ResourceReader para proporcionar recursos específicos de la referencia cultural en tiempo de ejecución. La interfaz tiene un indizador y un IEnumerable
para devolver las cadenas localizadas. IStringLocalizer
no necesita que se almacenen las cadenas de idioma predeterminado en un archivo de recursos. Puede desarrollar una aplicación destinada a la localización sin necesidad de crear archivos de recursos al principio de la fase de desarrollo.
En el ejemplo de código siguiente se muestra cómo encapsular la cadena "About Title" para la localización.
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
namespace Localization.Controllers
{
[Route("api/[controller]")]
public class AboutController : Controller
{
private readonly IStringLocalizer<AboutController> _localizer;
public AboutController(IStringLocalizer<AboutController> localizer)
{
_localizer = localizer;
}
[HttpGet]
public string Get()
{
return _localizer["About Title"];
}
}
}
En el código anterior, la implementación de IStringLocalizer<T>
procede de la inserción de dependencias. Si no se encuentra el valor localizado de "About Title, se devuelve la clave de indizador, es decir, la cadena "About Title".
Puede dejar las cadenas literales del idioma predeterminado en la aplicación y ajustarlas en el localizador, de modo que se pueda centrar en el desarrollo de la aplicación. Desarrolle una aplicación con el idioma predeterminado y prepárela para el proceso de localización sin necesidad de crear primero un archivo de recursos predeterminado.
También puede seguir el método tradicional y proporcionar una clave para recuperar la cadena de idioma predeterminado. El nuevo flujo de trabajo, que carece de archivo .resx de idioma predeterminado y simplemente ajusta los literales de cadena, puede ahorrar a muchos desarrolladores la sobrecarga de localizar una aplicación. Otros desarrolladores prefieren el flujo de trabajo tradicional, ya que puede facilitar el trabajo con literales de cadena largos, así como la actualización de las cadenas localizadas.
IHtmlLocalizer
Use la implementación de IHtmlLocalizer<T>
para los recursos que contienen HTML. El HTML IHtmlLocalizer
codifica los argumentos a los que se da formato en la cadena de recursos, pero no codifica como HTML la cadena de recursos en sí misma. En el código resaltado siguiente, solo el valor del parámetro name
está codificado en HTML.
using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Localization;
namespace Localization.Controllers
{
public class BookController : Controller
{
private readonly IHtmlLocalizer<BookController> _localizer;
public BookController(IHtmlLocalizer<BookController> localizer)
{
_localizer = localizer;
}
public IActionResult Hello(string name)
{
ViewData["Message"] = _localizer["<b>Hello</b><i> {0}</i>", name];
return View();
}
Nota
Por lo general, solo se localiza texto, no HTML.
IStringLocalizerFactory
En el nivel más bajo, puede obtener IStringLocalizerFactory
de la inserción de dependencias:
{
public class TestController : Controller
{
private readonly IStringLocalizer _localizer;
private readonly IStringLocalizer _localizer2;
public TestController(IStringLocalizerFactory factory)
{
var type = typeof(SharedResource);
var assemblyName = new AssemblyName(type.GetTypeInfo().Assembly.FullName);
_localizer = factory.Create(type);
_localizer2 = factory.Create("SharedResource", assemblyName.Name);
}
public IActionResult About()
{
ViewData["Message"] = _localizer["Your application description page."]
+ " loc 2: " + _localizer2["Your application description page."];
En el código anterior se muestran los dos métodos factory.Create.
Recursos compartidos
Puede dividir las cadenas localizadas por controlador o área, o bien tener un solo contenedor. En la aplicación de ejemplo, se usa una clase ficticia denominada SharedResource
para los recursos compartidos.
// Dummy class to group shared resources
namespace Localization
{
public class SharedResource
{
}
}
Algunos programadores usan la clase Startup
para contener cadenas globales o compartidas. En el ejemplo siguiente, se usan los localizadores InfoController
y SharedResource
:
public class InfoController : Controller
{
private readonly IStringLocalizer<InfoController> _localizer;
private readonly IStringLocalizer<SharedResource> _sharedLocalizer;
public InfoController(IStringLocalizer<InfoController> localizer,
IStringLocalizer<SharedResource> sharedLocalizer)
{
_localizer = localizer;
_sharedLocalizer = sharedLocalizer;
}
public string TestLoc()
{
string msg = "Shared resx: " + _sharedLocalizer["Hello!"] +
" Info resx " + _localizer["Hello!"];
return msg;
}
Localización de vista
El servicio IViewLocalizer
proporciona cadenas localizadas para una vista. La clase ViewLocalizer
implementa esta interfaz y busca la ubicación del recurso en la ruta de acceso del archivo de vista. En el código siguiente se muestra cómo usar la implementación predeterminada de IViewLocalizer
:
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@{
ViewData["Title"] = Localizer["About"];
}
<h2>@ViewData["Title"].</h2>
<h3>@ViewData["Message"]</h3>
<p>@Localizer["Use this area to provide additional information."]</p>
La implementación predeterminada de IViewLocalizer
busca el archivo de recursos según el nombre del archivo de vista. No se puede usar un archivo de recursos compartidos global. ViewLocalizer
implementa el localizador mediante IHtmlLocalizer
, por lo que Razor no codifica como HTML la cadena localizada. Puede parametrizar las cadenas de recursos y IViewLocalizer
codifica como HTML los parámetros, pero no la cadena de recursos. Observe el siguiente marcado Razor:
@Localizer["<i>Hello</i> <b>{0}!</b>", UserManager.GetUserName(User)]
Un archivo de recursos en francés puede contener los valores siguientes:
Clave | Valor |
---|---|
<i>Hello</i> <b>{0}!</b> |
<i>Bonjour</i> <b>{0} !</b> |
La vista representada contendría el marcado HTML del archivo de recursos.
Nota
Por lo general, solo se localiza texto, no HTML.
Para usar un archivo de recursos compartido en una vista, inserte IHtmlLocalizer<T>
:
@using Microsoft.AspNetCore.Mvc.Localization
@using Localization.Services
@inject IViewLocalizer Localizer
@inject IHtmlLocalizer<SharedResource> SharedLocalizer
@{
ViewData["Title"] = Localizer["About"];
}
<h2>@ViewData["Title"].</h2>
<h1>@SharedLocalizer["Hello!"]</h1>
Localización de DataAnnotations
Los mensajes de error de DataAnnotations se localizan con IStringLocalizer<T>
. Mediante la opción ResourcesPath = "Resources"
, es posible almacenar los mensajes de error en RegisterViewModel
en cualquiera de las rutas de acceso siguientes:
- Resources/ViewModels.Account.RegisterViewModel.fr.resx
- Resources/ViewModels/Account/RegisterViewModel.fr.resx
public class RegisterViewModel
{
[Required(ErrorMessage = "The Email field is required.")]
[EmailAddress(ErrorMessage = "The Email field is not a valid email address.")]
[Display(Name = "Email")]
public string Email { get; set; }
[Required(ErrorMessage = "The Password field is required.")]
[StringLength(8, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
En ASP.NET Core MVC 1.1.0 y versiones posteriores, los atributos que no son de validación están localizados.
Cómo usar una cadena de recursos para varias clases
En el código siguiente se muestra cómo usar una cadena de recursos para atributos de validación con varias clases:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddDataAnnotationsLocalization(options => {
options.DataAnnotationLocalizerProvider = (type, factory) =>
factory.Create(typeof(SharedResource));
});
}
En el código anterior, SharedResource
es la clase correspondiente al archivo .resx donde se almacenan los mensajes de validación. Según este enfoque, DataAnnotations solo usa SharedResource
, en lugar del recurso de cada clase.
Configuración de los servicios de localización
Los servicios de localización se configuran en el método Startup.ConfigureServices
:
services.AddLocalization(options => options.ResourcesPath = "Resources");
services.AddMvc()
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
.AddDataAnnotationsLocalization();
AddLocalization
agrega los servicios de localización al contenedor de servicios, incluidas las implementaciones deIStringLocalizer<T>
yIStringLocalizerFactory
. El código anterior también establece la ruta de acceso a los recursos en "Resources".AddViewLocalization
agrega compatibilidad con los archivos de vista localizados. En este ejemplo, la localización de vista se basa en el sufijo del archivo de vista. Por ejemplo, "fr" en el archivoIndex.fr.cshtml
.AddDataAnnotationsLocalization
agrega compatibilidad con mensajes de validación deDataAnnotations
localizados mediante abstraccionesIStringLocalizer
.
Nota
Es posible que no pueda escribir comas decimales en campos decimales. Para que la validación de jQuery sea compatible con configuraciones regionales distintas del inglés que usan una coma (",") en lugar de un punto decimal y formatos de fecha distintos del de Estados Unidos, debe seguir unos pasos para globalizar la aplicación. Consulte este comentario 4076 de GitHub para instrucciones sobre cómo agregar la coma decimal.
Pasos siguientes
La localización de una aplicación también implica las tareas siguientes:
- Proporcionar recursos localizados para los idiomas y las referencias culturales que admite la aplicación
- Implementar una estrategia para seleccionar el idioma o la referencia cultural de cada solicitud
Recursos adicionales
- Globalización y localización en ASP.NET Core
- Proporcionar recursos localizados para idiomas y referencias culturales en una aplicación de ASP.NET Core
- Estrategias para seleccionar el idioma y la referencia cultural en una aplicación de ASP.NET Core localizada
- Solución de problemas de localización de ASP.NET Core
- Globalización y localización de aplicaciones .NET
- Proyecto Localization.StarterWeb usado en el artículo
- Recursos en archivos .resx
- Kit de herramientas de aplicaciones multilingüe de Microsoft
- Localización y genéricos