Globalización y localización en ASP.NET CoreGlobalization and localization in ASP.NET Core

By Rick Anderson, Damien Bowden, Bart Calixto, Nadeem Afana y Hisham Bin AteyaBy Rick Anderson, Damien Bowden, Bart Calixto, Nadeem Afana, and Hisham Bin Ateya

El hecho de crear un sitio web multilingüe con ASP.NET Core permite que este llegue a un público más amplio.Creating a multilingual website with ASP.NET Core will allow your site to reach a wider audience. ASP.NET Core proporciona servicios y software intermedio para la localización en diferentes idiomas y referencias culturales.ASP.NET Core provides services and middleware for localizing into different languages and cultures.

La internacionalización conlleva globalización y localización.Internationalization involves Globalization and Localization. La globalización es el proceso de diseñar aplicaciones que admiten diferentes referencias culturales.Globalization is the process of designing apps that support different cultures. La globalización agrega compatibilidad con la entrada, la visualización y la salida de un conjunto definido de scripts de lenguaje relacionados con áreas geográficas específicas.Globalization adds support for input, display, and output of a defined set of language scripts that relate to specific geographic areas.

La localización es el proceso de adaptar una aplicación globalizada, que ya se ha procesado para la localizabilidad, a una determinada referencia cultural o configuración regional.Localization is the process of adapting a globalized app, which you have already processed for localizability, to a particular culture/locale. Para más información, vea Términos relacionados con la globalización y la localización al final de este documento.For more information see Globalization and localization terms near the end of this document.

La localización de la aplicación implica lo siguiente:App localization involves the following:

  1. Hacer que el contenido de la aplicación sea localizableMake the app's content localizable

  2. Proporcionar recursos localizados para los idiomas y las referencias culturales admitidosProvide localized resources for the languages and cultures you support

  3. Implementar una estrategia para seleccionar el idioma o la referencia cultural de cada solicitudImplement a strategy to select the language/culture for each request

Vea o descargue el código de ejemplo (cómo descargarlo)View or download sample code (how to download)

Hacer que el contenido de la aplicación sea localizableMake the app's content localizable

IStringLocalizer y IStringLocalizer<T>, introducidos en ASP.NET Core, están diseñados para mejorar la productividad al desarrollar aplicaciones localizadas.Introduced in ASP.NET Core, IStringLocalizer and IStringLocalizer<T> were architected to improve productivity when developing localized apps. IStringLocalizer usa ResourceManager y ResourceReader para proporcionar recursos específicos de la referencia cultural en tiempo de ejecución.IStringLocalizer uses the ResourceManager and ResourceReader to provide culture-specific resources at run time. La interfaz simple tiene un indizador y un IEnumerable para devolver las cadenas localizadas.The simple interface has an indexer and an IEnumerable for returning localized strings. IStringLocalizer no necesita que se almacenen las cadenas de idioma predeterminado en un archivo de recursos.IStringLocalizer doesn't require you to store the default language strings in a resource file. Puede desarrollar una aplicación destinada a la localización sin necesidad de crear archivos de recursos al principio de la fase de desarrollo.You can develop an app targeted for localization and not need to create resource files early in development. En el código siguiente se muestra cómo ajustar la cadena "About Title" para la localización.The code below shows how to wrap the string "About Title" for localization.

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.In the code above, the IStringLocalizer<T> implementation comes from Dependency Injection. Si no se encuentra el valor localizado de "About Title, se devuelve la clave de indizador, es decir, la cadena "About Title".If the localized value of "About Title" isn't found, then the indexer key is returned, that is, the string "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.You can leave the default language literal strings in the app and wrap them in the localizer, so that you can focus on developing the app. Desarrolle la 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.You develop your app with your default language and prepare it for the localization step without first creating a default resource file. También puede seguir el método tradicional y proporcionar una clave para recuperar la cadena de idioma predeterminado.Alternatively, you can use the traditional approach and provide a key to retrieve the default language string. 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.For many developers the new workflow of not having a default language .resx file and simply wrapping the string literals can reduce the overhead of localizing an app. Otros desarrolladores preferirán el flujo de trabajo tradicional, ya que facilita el trabajo con literales de cadena más largos, así como la actualización de las cadenas localizadas.Other developers will prefer the traditional work flow as it can make it easier to work with longer string literals and make it easier to update localized strings.

Use la implementación de IHtmlLocalizer<T> para los recursos que contienen HTML.Use the IHtmlLocalizer<T> implementation for resources that contain HTML. El HTML de 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.IHtmlLocalizer HTML encodes arguments that are formatted in the resource string, but doesn't HTML encode the resource string itself. En el ejemplo que se muestra a continuación, solo está codificado en HTML el valor del parámetro name.In the sample highlighted below, only the value of name parameter is HTML encoded.

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: Generalmente solo le interesa localizar texto, no HTML.Note: You generally want to only localize text and not HTML.

En el nivel más bajo, puede obtener IStringLocalizerFactory de la inserción de dependencias:At the lowest level, you can get IStringLocalizerFactory out of Dependency Injection:

{
    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.The code above demonstrates each of the two factory create methods.

Puede dividir las cadenas localizadas por controlador o por área, o bien tener un solo contenedor.You can partition your localized strings by controller, area, or have just one container. En la aplicación de ejemplo, se usa una clase ficticia denominada SharedResource para los recursos compartidos.In the sample app, a dummy class named SharedResource is used for shared resources.

// Dummy class to group shared resources

namespace Localization
{
    public class SharedResource
    {
    }
}

Algunos programadores usan la clase Startup para contener cadenas globales o compartidas.Some developers use the Startup class to contain global or shared strings. En el ejemplo siguiente, se usan los localizadores InfoController y SharedResource:In the sample below, the InfoController and the SharedResource localizers are used:

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 vistaView localization

El servicio IViewLocalizer proporciona cadenas localizadas para una vista.The IViewLocalizer service provides localized strings for a view. La clase ViewLocalizer implementa esta interfaz y busca la ubicación del recurso en la ruta de acceso del archivo de vista.The ViewLocalizer class implements this interface and finds the resource location from the view file path. En el código siguiente se muestra cómo usar la implementación predeterminada de IViewLocalizer:The following code shows how to use the default implementation of 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.The default implementation of IViewLocalizer finds the resource file based on the view's file name. No se puede usar un archivo de recursos compartidos global.There's no option to use a global shared resource file. ViewLocalizer implementa el localizador mediante IHtmlLocalizer, por lo que Razor no codifica como HTML la cadena localizada.ViewLocalizer implements the localizer using IHtmlLocalizer, so Razor doesn't HTML encode the localized string. Puede parametrizar las cadenas de recursos y IViewLocalizer codificará como HTML los parámetros, pero no la cadena de recursos.You can parameterize resource strings and IViewLocalizer will HTML encode the parameters, but not the resource string. Observe el siguiente marcado de Razor:Consider the following Razor markup:

@Localizer["<i>Hello</i> <b>{0}!</b>", UserManager.GetUserName(User)]

Un archivo de recursos en francés podría contener lo siguiente:A French resource file could contain the following:

KeyKey ValorValue
<i>Hello</i> <b>{0}!</b> <i>Bonjour</i> <b>{0} !</b>

La vista representada contendría el marcado HTML del archivo de recursos.The rendered view would contain the HTML markup from the resource file.

Nota: Generalmente solo le interesa localizar texto, no HTML.Note: You generally want to only localize text and not HTML.

Para usar un archivo de recursos compartido en una vista, inserte IHtmlLocalizer<T>:To use a shared resource file in a view, inject 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 DataAnnotationsDataAnnotations localization

Los mensajes de error de DataAnnotations se localizan con IStringLocalizer<T>.DataAnnotations error messages are localized with 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:Using the option ResourcesPath = "Resources", the error messages in RegisterViewModel can be stored in either of the following paths:

  • Resources/ViewModels.Account.RegisterViewModel.fr.resxResources/ViewModels.Account.RegisterViewModel.fr.resx
  • Resources/ViewModels/Account/RegisterViewModel.fr.resxResources/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.In ASP.NET Core MVC 1.1.0 and higher, non-validation attributes are localized. ASP.NET Core MVC 1.0 no busca cadenas localizadas para los atributos que no son de validación.ASP.NET Core MVC 1.0 does not look up localized strings for non-validation attributes.

Uso de una cadena de recursos para varias clasesUsing one resource string for multiple classes

En el código siguiente se muestra cómo usar una cadena de recursos para atributos de validación con varias clases:The following code shows how to use one resource string for validation attributes with multiple classes:

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.In the preceding code, SharedResource is the class corresponding to the resx where your validation messages are stored. Según este método, DataAnnotations solo usará SharedResource, en lugar del recurso de cada clase.With this approach, DataAnnotations will only use SharedResource, rather than the resource for each class.

Proporcionar recursos localizados para los idiomas y las referencias culturales admitidosProvide localized resources for the languages and cultures you support

SupportedCultures y SupportedUICulturesSupportedCultures and SupportedUICultures

ASP.NET Core permite especificar dos valores de referencia cultural, SupportedCultures y SupportedUICultures.ASP.NET Core allows you to specify two culture values, SupportedCultures and SupportedUICultures. El objeto CultureInfo para SupportedCultures determina los resultados de funciones dependientes de la referencia cultural, como el formato de fecha, hora, número y moneda.The CultureInfo object for SupportedCultures determines the results of culture-dependent functions, such as date, time, number, and currency formatting. SupportedCultures también determina el criterio de ordenación del texto, las convenciones sobre el uso de mayúsculas y minúsculas, y las comparaciones de cadenas.SupportedCultures also determines the sorting order of text, casing conventions, and string comparisons. Vea CultureInfo.CurrentCulture para obtener más información sobre la manera en que el servidor obtiene la referencia cultural.See CultureInfo.CurrentCulture for more info on how the server gets the Culture. SupportedUICultures determina qué cadenas traducidas buscará ResourceManager (en archivos .resx).The SupportedUICultures determines which translates strings (from .resx files) are looked up by the ResourceManager. ResourceManager simplemente busca cadenas específicas de referencias culturales determinadas por CurrentUICulture.The ResourceManager simply looks up culture-specific strings that's determined by CurrentUICulture. Todos los subprocesos de .NET tienen objetos CurrentCulture y CurrentUICulture.Every thread in .NET has CurrentCulture and CurrentUICulture objects. ASP.NET Core inspecciona estos valores al representar funciones dependientes de la referencia cultural.ASP.NET Core inspects these values when rendering culture-dependent functions. Por ejemplo, si la referencia cultural del subproceso actual está establecida en "en-US" (inglés (Estados Unidos)), DateTime.Now.ToLongDateString() mostrará "Thursday, February 18, 2016". En cambio, si CurrentCulture está establecido en "es-ES" (español (España)), la salida será "jueves, 18 de febrero de 2016".For example, if the current thread's culture is set to "en-US" (English, United States), DateTime.Now.ToLongDateString() displays "Thursday, February 18, 2016", but if CurrentCulture is set to "es-ES" (Spanish, Spain) the output will be "jueves, 18 de febrero de 2016".

Archivos de recursosResource files

Un archivo de recursos es un mecanismo útil para separar del código las cadenas localizables.A resource file is a useful mechanism for separating localizable strings from code. Las cadenas traducidas para el idioma no predeterminado son archivos de recursos .resx aislados.Translated strings for the non-default language are isolated .resx resource files. Pongamos por caso que quiere crear un archivo de recursos de español denominado Welcome.es.resx que contenga cadenas traducidas.For example, you might want to create Spanish resource file named Welcome.es.resx containing translated strings. "es" es el código de idioma para español."es" is the language code for Spanish. Para crear este archivo de recursos en Visual Studio:To create this resource file in Visual Studio:

  1. En el Explorador de soluciones, haga clic con el botón derecho en la carpeta que contendrá el archivo de recursos > Agregar > Nuevo elemento.In Solution Explorer, right click on the folder which will contain the resource file > Add > New Item.

    Menú contextual anidado: en el Explorador de soluciones, se abre un menú contextual para los recursos.

  2. En el cuadro para buscar plantillas instaladas, escriba "recurso" y asigne un nombre al archivo.In the Search installed templates box, enter "resource" and name the file.

    Cuadro de diálogo Agregar nuevo elemento

  3. Escriba el valor de clave (cadena nativa) en la columna Nombre y la cadena traducida en la columna Valor.Enter the key value (native string) in the Name column and the translated string in the Value column.

    Archivo Welcome.es.resx (archivo de recursos de bienvenida para el idioma español) con la palabra Hello en la columna Nombre y la palabra Hola en la columna Valor

    El archivo Welcome.es.resx aparece en Visual Studio.Visual Studio shows the Welcome.es.resx file.

    Archivo de recursos de bienvenida en español (es) en el Explorador de soluciones

Nomenclatura de los archivos de recursosResource file naming

El nombre de los recursos es el nombre del tipo completo de su clase menos el nombre del ensamblado.Resources are named for the full type name of their class minus the assembly name. Por ejemplo, un recurso francés de un proyecto cuyo ensamblado principal sea LocalizationWebsite.Web.dll para la clase LocalizationWebsite.Web.Startup se denominará Startup.fr.resx.For example, a French resource in a project whose main assembly is LocalizationWebsite.Web.dll for the class LocalizationWebsite.Web.Startup would be named Startup.fr.resx. Un recurso para la clase LocalizationWebsite.Web.Controllers.HomeController se denominará Controllers.HomeController.fr.resx.A resource for the class LocalizationWebsite.Web.Controllers.HomeController would be named Controllers.HomeController.fr.resx. Si el espacio de nombres de la clase de destino no es igual que el nombre del ensamblado, necesitará el nombre de tipo completo.If your targeted class's namespace isn't the same as the assembly name you will need the full type name. Por ejemplo, en el proyecto de ejemplo, un recurso para el tipo ExtraNamespace.Tools se denominará ExtraNamespace.Tools.fr.resx.For example, in the sample project a resource for the type ExtraNamespace.Tools would be named ExtraNamespace.Tools.fr.resx.

En el proyecto de ejemplo, el método ConfigureServices establece ResourcesPath en "Resources", por lo que la ruta de acceso relativa del proyecto para el archivo de recursos en francés del controlador de inicio es Resources/Controllers.HomeController.fr.resx.In the sample project, the ConfigureServices method sets the ResourcesPath to "Resources", so the project relative path for the home controller's French resource file is Resources/Controllers.HomeController.fr.resx. También puede usar carpetas para organizar los archivos de recursos.Alternatively, you can use folders to organize resource files. Para el controlador de inicio, la ruta de acceso sería Resources/Controllers/HomeController.fr.resx.For the home controller, the path would be Resources/Controllers/HomeController.fr.resx. Si no usa la opción ResourcesPath, el archivo .resx estará en el directorio base del proyecto.If you don't use the ResourcesPath option, the .resx file would go in the project base directory. El archivo de recursos para HomeController se denominará Controllers.HomeController.fr.resx.The resource file for HomeController would be named Controllers.HomeController.fr.resx. La opción de usar la convención de nomenclatura de punto o ruta de acceso depende de la manera en que quiera organizar los archivos de recursos.The choice of using the dot or path naming convention depends on how you want to organize your resource files.

Nombre del recursoResource name Nomenclatura de punto o ruta de accesoDot or path naming
Resources/Controllers.HomeController.fr.resxResources/Controllers.HomeController.fr.resx PuntoDot
Resources/Controllers/HomeController.fr.resxResources/Controllers/HomeController.fr.resx Ruta de accesoPath

Los archivos de recursos que usan @inject IViewLocalizer en las vistas de Razor siguen un patrón similar.Resource files using @inject IViewLocalizer in Razor views follow a similar pattern. Para asignar un nombre al archivo de recursos de una vista, se puede usar la nomenclatura de punto o de ruta de acceso.The resource file for a view can be named using either dot naming or path naming. Los archivos de recursos de la vista de Razor imitan la ruta de acceso de su archivo de vista asociado.Razor view resource files mimic the path of their associated view file. Supongamos que establecemos ResourcesPath en "Resources". En ese caso, el archivo de recursos de francés asociado a la vista Views/Home/About.cshtml podría ser uno de los siguientes:Assuming we set the ResourcesPath to "Resources", the French resource file associated with the Views/Home/About.cshtml view could be either of the following:

  • Resources/Views/Home/About.fr.resxResources/Views/Home/About.fr.resx

  • Resources/Views.Home.About.fr.resxResources/Views.Home.About.fr.resx

Si no usa la opción ResourcesPath, el archivo .resx de una vista se ubicará en la misma carpeta que la vista.If you don't use the ResourcesPath option, the .resx file for a view would be located in the same folder as the view.

RootNamespaceAttributeRootNamespaceAttribute

El atributo RootNamespace proporciona el espacio de nombres raíz de un ensamblado cuando el espacio de nombres raíz del ensamblado es diferente del nombre de ensamblado.The RootNamespace attribute provides the root namespace of an assembly when the root namespace of an assembly is different than the assembly name.

Advertencia

Esto puede ocurrir cuando el nombre de un proyecto no es un identificador de .NET válido.This can occur when a project's name is not a valid .NET identifier. Por ejemplo my-project-name.csproj usará el espacio de nombres raíz my_project_name y el nombre de ensamblado my-project-name, lo que lleva a este error.For instance my-project-name.csproj will use the root namespace my_project_name and the assembly name my-project-name leading to this error.

Si el espacio de nombres raíz de un ensamblado es diferente del nombre de ensamblado:If the root namespace of an assembly is different than the assembly name:

  • La localización no funciona de forma predeterminada.Localization does not work by default.
  • Se produce un error en la localización debido a la manera en que se buscan los recursos dentro del ensamblado.Localization fails due to the way resources are searched for within the assembly. RootNamespace es un valor en tiempo de compilación que no está disponible para el proceso en ejecución.RootNamespace is a build-time value which is not available to the executing process.

Si RootNamespace es diferente de AssemblyName, incluya lo siguiente en AssemblyInfo.cs (con los valores de parámetro reemplazados por los valores reales):If the RootNamespace is different from the AssemblyName, include the following in AssemblyInfo.cs (with parameter values replaced with the actual values):

using System.Reflection;
using Microsoft.Extensions.Localization;

[assembly: ResourceLocation("Resource Folder Name")]
[assembly: RootNamespace("App Root Namespace")]

El código anterior permite la resolución correcta de los archivos resx.The preceding code enables the successful resolution of resx files.

Comportamiento de reserva de la referencia culturalCulture fallback behavior

Cuando se busca un recurso, la localización entra en un proceso conocido como "reserva de la referencia cultural".When searching for a resource, localization engages in "culture fallback". Partiendo de la referencia cultural solicitada, si esta no se encuentra, se revierte a su referencia cultural principal correspondiente.Starting from the requested culture, if not found, it reverts to the parent culture of that culture. Como inciso, decir que la propiedad CultureInfo.Parent representa la referencia cultural principal.As an aside, the CultureInfo.Parent property represents the parent culture. Eso suele conllevar (aunque no siempre) la eliminación del significante nacional del código ISO.This usually (but not always) means removing the national signifier from the ISO. Así, por ejemplo, el dialecto de español hablado en México es "es-MX".For example, the dialect of Spanish spoken in Mexico is "es-MX". Contiene el elemento principal "es", que corresponde a español no específico de ningún país en concreto.It has the parent "es"—Spanish non-specific to any country.

Imagine que su sitio recibe una solicitud sobre un recurso "Welcome" donde se emplea la referencia cultural "fr-CA".Imagine your site receives a request for a "Welcome" resource using culture "fr-CA". El sistema de localización busca los recursos siguientes (en orden) y selecciona la primera coincidencia:The localization system looks for the following resources, in order, and selects the first match:

  • Welcome.fr-CA.resxWelcome.fr-CA.resx
  • Welcome.fr.resxWelcome.fr.resx
  • Welcome.resx (si NeutralResourcesLanguage es "fr-CA")Welcome.resx (if the NeutralResourcesLanguage is "fr-CA")

Consideremos, por ejemplo, que quita el designador de referencia cultural ".fr" y la referencia cultural está establecida en francés. En ese caso, se lee el archivo de recursos predeterminado y se localizan las cadenas.As an example, if you remove the ".fr" culture designator and you have the culture set to French, the default resource file is read and strings are localized. El Administrador de recursos designa un recurso predeterminado o de reserva para los casos en que nada coincida con la referencia cultural solicitada.The Resource manager designates a default or fallback resource for when nothing meets your requested culture. Si quiere simplemente devolver la clave cuando falte un recurso para la referencia cultural solicitada, no debe tener un archivo de recursos predeterminado.If you want to just return the key when missing a resource for the requested culture you must not have a default resource file.

Generar archivos de recursos con Visual StudioGenerate resource files with Visual Studio

Si crea un archivo de recursos en Visual Studio sin una referencia cultural en el nombre de archivo (por ejemplo, Welcome.resx), Visual Studio creará una clase de C# con una propiedad para cada cadena.If you create a resource file in Visual Studio without a culture in the file name (for example, Welcome.resx), Visual Studio will create a C# class with a property for each string. Normalmente esto no interesa con ASP.NET Core;That's usually not what you want with ASP.NET Core. por lo general, no tendrá un archivo de recursos .resx predeterminado (un archivo .resx sin el nombre de la referencia cultural).You typically don't have a default .resx resource file (a .resx file without the culture name). Se recomienda que cree el archivo .resx con un nombre de referencia cultural (por ejemplo, Welcome.fr.resx).We suggest you create the .resx file with a culture name (for example Welcome.fr.resx). Cuando cree un archivo .resx con un nombre de referencia cultural, Visual Studio no generará el archivo de clase.When you create a .resx file with a culture name, Visual Studio won't generate the class file.

Agregar otras referencias culturalesAdd other cultures

Cada combinación de idioma y referencia cultural (que no sea el idioma predeterminado) requiere un archivo de recursos único.Each language and culture combination (other than the default language) requires a unique resource file. Para crear archivos de recursos para otras referencias culturales y configuraciones regionales, debe crear archivos de recursos en los que los códigos de idioma ISO formen parte del nombre de archivo (por ejemplo, en-us, fr-ca y en-gb).You create resource files for different cultures and locales by creating new resource files in which the ISO language codes are part of the file name (for example, en-us, fr-ca, and en-gb). Estos códigos ISO se colocan entre el nombre de archivo y la extensión de archivo .resx, como en Welcome.es-MX.resx (español [México]).These ISO codes are placed between the file name and the .resx file extension, as in Welcome.es-MX.resx (Spanish/Mexico).

Implementar una estrategia para seleccionar el idioma o la referencia cultural de cada solicitudImplement a strategy to select the language/culture for each request

Configurar la localizaciónConfigure localization

La localización se configura en el método Startup.ConfigureServices:Localization is configured in the Startup.ConfigureServices method:

services.AddLocalization(options => options.ResourcesPath = "Resources");

services.AddMvc()
    .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
    .AddDataAnnotationsLocalization();
  • AddLocalization agrega los servicios de localización al contenedor de servicios.AddLocalization Adds the localization services to the services container. El código anterior también establece la ruta de acceso a los recursos en "Resources".The code above also sets the resources path to "Resources".

  • AddViewLocalization agrega compatibilidad con los archivos de vista localizados.AddViewLocalization Adds support for localized view files. En este ejemplo, la localización de vista se basa en el sufijo del archivo de vista.In this sample view localization is based on the view file suffix. Por ejemplo, "fr" en el archivo Index.fr.cshtml.For example "fr" in the Index.fr.cshtml file.

  • AddDataAnnotationsLocalization agrega compatibilidad con mensajes de validación de DataAnnotations localizados mediante abstracciones IStringLocalizer.AddDataAnnotationsLocalization Adds support for localized DataAnnotations validation messages through IStringLocalizer abstractions.

Software intermedio de localizaciónLocalization middleware

La referencia cultural actual de una solicitud se establece en el software intermedio de localización.The current culture on a request is set in the localization Middleware. El software intermedio de localización se habilita en el método Startup.Configure.The localization middleware is enabled in the Startup.Configure method. El software intermedio de localización debe configurarse antes que cualquier software intermedio que pueda comprobar la referencia cultural de la solicitud (por ejemplo, app.UseMvcWithDefaultRoute()).The localization middleware must be configured before any middleware which might check the request culture (for example, app.UseMvcWithDefaultRoute()).

var supportedCultures = new[]
{
    new CultureInfo("en-US"),
    new CultureInfo("fr"),
};

app.UseRequestLocalization(new RequestLocalizationOptions
{
    DefaultRequestCulture = new RequestCulture("en-US"),
    // Formatting numbers, dates, etc.
    SupportedCultures = supportedCultures,
    // UI strings that we have localized.
    SupportedUICultures = supportedCultures
});

app.UseStaticFiles();
// To configure external authentication, 
// see: http://go.microsoft.com/fwlink/?LinkID=532715
app.UseAuthentication();
app.UseMvcWithDefaultRoute();

UseRequestLocalization inicializa un objeto RequestLocalizationOptions.UseRequestLocalization initializes a RequestLocalizationOptions object. En todas las solicitudes, se enumera la lista de RequestCultureProvider en RequestLocalizationOptions y se usa el primer proveedor que puede determinar correctamente la referencia cultural de la solicitud.On every request the list of RequestCultureProvider in the RequestLocalizationOptions is enumerated and the first provider that can successfully determine the request culture is used. Los proveedores predeterminados proceden de la clase RequestLocalizationOptions:The default providers come from the RequestLocalizationOptions class:

  1. QueryStringRequestCultureProvider
  2. CookieRequestCultureProvider
  3. AcceptLanguageHeaderRequestCultureProvider

La lista predeterminada va de más específico a menos específico.The default list goes from most specific to least specific. Más adelante en el artículo veremos cómo puede cambiar el orden e incluso agregar un proveedor de referencia cultural personalizado.Later in the article we'll see how you can change the order and even add a custom culture provider. Si ninguno de los proveedores puede determinar la referencia cultural de la solicitud, se usa DefaultRequestCulture.If none of the providers can determine the request culture, the DefaultRequestCulture is used.

QueryStringRequestCultureProviderQueryStringRequestCultureProvider

Algunas aplicaciones usarán una cadena de consulta para establecer la referencia cultural y la referencia cultural de la interfaz de usuario.Some apps will use a query string to set the culture and UI culture. Para las aplicaciones que usan el método de cookie o de encabezado Accept-Language, es útil agregar una cadena de consulta a la dirección URL para depurar y probar el código.For apps that use the cookie or Accept-Language header approach, adding a query string to the URL is useful for debugging and testing code. De forma predeterminada, QueryStringRequestCultureProvider está registrado como primer proveedor de localización en la lista RequestCultureProvider.By default, the QueryStringRequestCultureProvider is registered as the first localization provider in the RequestCultureProvider list. Debe pasar los parámetros de cadena de consulta culture y ui-culture.You pass the query string parameters culture and ui-culture. En el ejemplo siguiente, la referencia cultural específica (idioma y región) se establece en español (México):The following example sets the specific culture (language and region) to Spanish/Mexico:

http://localhost:5000/?culture=es-MX&ui-culture=es-MX

Si solo pasa uno de los dos parámetros (culture o ui-culture), el proveedor de la cadena de consulta usará el valor que usted ha pasado para establecer ambos valores.If you only pass in one of the two (culture or ui-culture), the query string provider will set both values using the one you passed in. Por ejemplo, si solo establece la referencia cultural, se establecerán Culture y UICulture:For example, setting just the culture will set both the Culture and the UICulture:

http://localhost:5000/?culture=es-MX

CookieRequestCultureProviderCookieRequestCultureProvider

Las aplicaciones de producción suelen proporcionar un mecanismo para establecer la referencia cultural con la cookie de la referencia cultural de ASP.NET Core.Production apps will often provide a mechanism to set the culture with the ASP.NET Core culture cookie. Use el método MakeCookieValue para crear una cookie.Use the MakeCookieValue method to create a cookie.

CookieRequestCultureProvider DefaultCookieName devuelve el nombre de cookie predeterminado usado para realizar un seguimiento de la información de la referencia cultural preferida del usuario.The CookieRequestCultureProvider DefaultCookieName returns the default cookie name used to track the user's preferred culture information. El nombre de cookie predeterminado es .AspNetCore.Culture.The default cookie name is .AspNetCore.Culture.

El formato de la cookie es c=%LANGCODE%|uic=%LANGCODE%, donde c es Culture y uic es UICulture, por ejemplo:The cookie format is c=%LANGCODE%|uic=%LANGCODE%, where c is Culture and uic is UICulture, for example:

c=en-UK|uic=en-US

Si solo especifica uno de los dos valores, ya sea la información de la referencia cultural o la referencia cultural de la interfaz de usuario, la referencia cultural especificada se usará tanto para la información de la referencia cultural como para la referencia cultural de la interfaz de usuario.If you only specify one of culture info and UI culture, the specified culture will be used for both culture info and UI culture.

Encabezado HTTP Accept-LanguageThe Accept-Language HTTP header

El encabezado Accept-Language se puede establecer en la mayoría de los exploradores y está diseñado inicialmente para especificar el idioma del usuario.The Accept-Language header is settable in most browsers and was originally intended to specify the user's language. Este valor indica qué debe enviar el explorador o qué ha heredado del sistema operativo subyacente.This setting indicates what the browser has been set to send or has inherited from the underlying operating system. El encabezado HTTP Accept-Language de una solicitud del explorador no es un método infalible para detectar el idioma preferido del usuario (vea Setting language preferences in a browser [Establecer las preferencias de idioma en un explorador]).The Accept-Language HTTP header from a browser request isn't an infallible way to detect the user's preferred language (see Setting language preferences in a browser). Una aplicación de producción debe ofrecer al usuario una manera de personalizar su opción de referencia cultural.A production app should include a way for a user to customize their choice of culture.

Establecer el encabezado HTTP Accept-Language en Internet ExplorerSet the Accept-Language HTTP header in IE

  1. En el icono de engranaje, pulse Opciones de Internet.From the gear icon, tap Internet Options.

  2. Haga clic en Lenguajes.Tap Languages.

    Opciones de Internet

  3. Haga clic en Establecer preferencias de idioma.Tap Set Language Preferences.

  4. Haga clic en Agregar un idioma.Tap Add a language.

  5. Agregue el idioma.Add the language.

  6. Haga clic en el idioma y, después, en Subir.Tap the language, then tap Move Up.

El encabezado HTTP Content-LanguageThe Content-Language HTTP header

El encabezado de entidad Content-Language:The Content-Language entity header:

  • Se usa para describir los lenguajes destinados a la audiencia.Is used to describe the language(s) intended for the audience.
  • Permite a un usuario diferenciar según el propio idioma preferido de los usuarios.Allows a user to differentiate according to the users' own preferred language.

Los encabezados de entidad se usan en las solicitudes y respuestas HTTP.Entity headers are used in both HTTP requests and responses.

Se puede agregar el encabezado Content-Language mediante el establecimiento de la propiedad ApplyCurrentCultureToResponseHeaders.The Content-Language header can be added by setting the property ApplyCurrentCultureToResponseHeaders.

La adición del encabezado Content-Language:Adding the Content-Language header:

  • Permite que RequestLocalizationMiddleware establezca el encabezado Content-Language con CurrentUICulture.Allows the RequestLocalizationMiddleware to set the Content-Language header with the CurrentUICulture.
  • Elimina la necesidad de establecer el encabezado de respuesta Content-Language explícitamente.Eliminates the need to set the response header Content-Language explicitly.
app.UseRequestLocalization(new RequestLocalizationOptions
{
    ApplyCurrentCultureToResponseHeaders = true
});

Usar un proveedor personalizadoUse a custom provider

Imagine que quiere permitir que los clientes almacenen su idioma y su referencia cultural en las bases de datos.Suppose you want to let your customers store their language and culture in your databases. Puede escribir un proveedor para que busque estos valores para el usuario.You could write a provider to look up these values for the user. En el código siguiente se muestra cómo agregar un proveedor personalizado:The following code shows how to add a custom provider:

private const string enUSCulture = "en-US";

services.Configure<RequestLocalizationOptions>(options =>
{
    var supportedCultures = new[]
    {
        new CultureInfo(enUSCulture),
        new CultureInfo("fr")
    };

    options.DefaultRequestCulture = new RequestCulture(culture: enUSCulture, uiCulture: enUSCulture);
    options.SupportedCultures = supportedCultures;
    options.SupportedUICultures = supportedCultures;

    options.RequestCultureProviders.Insert(0, new CustomRequestCultureProvider(async context =>
    {
        // My custom request culture logic
        return new ProviderCultureResult("en");
    }));
});
private const string enUSCulture = "en-US";

services.Configure<RequestLocalizationOptions>(options =>
{
    var supportedCultures = new[]
    {
        new CultureInfo(enUSCulture),
        new CultureInfo("fr")
    };

    options.DefaultRequestCulture = new RequestCulture(culture: enUSCulture, uiCulture: enUSCulture);
    options.SupportedCultures = supportedCultures;
    options.SupportedUICultures = supportedCultures;

    options.AddInitialRequestCultureProvider(new CustomRequestCultureProvider(async context =>
    {
        // My custom request culture logic
        return new ProviderCultureResult("en");
    }));
});

Use RequestLocalizationOptions para agregar o quitar proveedores de localización.Use RequestLocalizationOptions to add or remove localization providers.

Establecer la referencia cultural mediante programaciónSet the culture programmatically

Este proyecto Localization.StarterWeb de ejemplo de GitHub contiene una interfaz de usuario para establecer el valor Culture.This sample Localization.StarterWeb project on GitHub contains UI to set the Culture. El archivo Views/Shared/_SelectLanguagePartial.cshtml le permite seleccionar la referencia cultural de la lista de referencias culturales admitidas:The Views/Shared/_SelectLanguagePartial.cshtml file allows you to select the culture from the list of supported cultures:

@using Microsoft.AspNetCore.Builder
@using Microsoft.AspNetCore.Http.Features
@using Microsoft.AspNetCore.Localization
@using Microsoft.AspNetCore.Mvc.Localization
@using Microsoft.Extensions.Options

@inject IViewLocalizer Localizer
@inject IOptions<RequestLocalizationOptions> LocOptions

@{
    var requestCulture = Context.Features.Get<IRequestCultureFeature>();
    var cultureItems = LocOptions.Value.SupportedUICultures
        .Select(c => new SelectListItem { Value = c.Name, Text = c.DisplayName })
        .ToList();
    var returnUrl = string.IsNullOrEmpty(Context.Request.Path) ? "~/" : $"~{Context.Request.Path.Value}";
}

<div title="@Localizer["Request culture provider:"] @requestCulture?.Provider?.GetType().Name">
    <form id="selectLanguage" asp-controller="Home" 
          asp-action="SetLanguage" asp-route-returnUrl="@returnUrl" 
          method="post" class="form-horizontal" role="form">
        <label asp-for="@requestCulture.RequestCulture.UICulture.Name">@Localizer["Language:"]</label> <select name="culture"
          onchange="this.form.submit();"
          asp-for="@requestCulture.RequestCulture.UICulture.Name" asp-items="cultureItems">
        </select>
    </form>
</div>

El archivo Views/Shared/_SelectLanguagePartial.cshtml se agrega a la sección footer del archivo de diseño para que esté disponible para todas las vistas:The Views/Shared/_SelectLanguagePartial.cshtml file is added to the footer section of the layout file so it will be available to all views:

<div class="container body-content" style="margin-top:60px">
    @RenderBody()
    <hr>
    <footer>
        <div class="row">
            <div class="col-md-6">
                <p>&copy; @System.DateTime.Now.Year - Localization</p>
            </div>
            <div class="col-md-6 text-right">
                @await Html.PartialAsync("_SelectLanguagePartial")
            </div>
        </div>
    </footer>
</div>

El método SetLanguage establece la cookie de la referencia cultural.The SetLanguage method sets the culture cookie.

[HttpPost]
public IActionResult SetLanguage(string culture, string returnUrl)
{
    Response.Cookies.Append(
        CookieRequestCultureProvider.DefaultCookieName,
        CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)),
        new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) }
    );

    return LocalRedirect(returnUrl);
}

No se puede conectar el archivo _SelectLanguagePartial.cshtml con código de ejemplo para este proyecto.You can't plug in the _SelectLanguagePartial.cshtml to sample code for this project. El proyecto Localization.StarterWeb de GitHub tiene código para hacer fluir RequestLocalizationOptions a una vista parcial de Razor a través del contenedor de inserción de dependencias.The Localization.StarterWeb project on GitHub has code to flow the RequestLocalizationOptions to a Razor partial through the Dependency Injection container.

Datos de ruta y cadenas de consulta de enlace de modelosModel binding route data and query strings

Vea Comportamiento de globalización de los datos de ruta de enlace de modelos y las cadenas de consulta.See Globalization behavior of model binding route data and query strings.

Términos relacionados con la globalización y la localizaciónGlobalization and localization terms

Para localizar una aplicación también es necesario contar con unos conocimientos básicos sobre los juegos de caracteres pertinentes que se usan en el desarrollo de software moderno y sobre los problemas asociados.The process of localizing your app also requires a basic understanding of relevant character sets commonly used in modern software development and an understanding of the issues associated with them. Aunque todos los equipos almacenan texto como números (códigos), cada sistema almacena el mismo texto con números diferentes.Although all computers store text as numbers (codes), different systems store the same text using different numbers. El proceso de localización consiste en traducir la interfaz de usuario (IU) de la aplicación a una referencia cultural o configuración regional específica.The localization process refers to translating the app user interface (UI) for a specific culture/locale.

La localizabilidad es un proceso intermedio para comprobar que una aplicación globalizada está preparada para la localización.Localizability is an intermediate process for verifying that a globalized app is ready for localization.

El formato RFC 4646 para el nombre de la referencia cultural es <languagecode2>-<country/regioncode2>, donde <languagecode2> es el código de idioma y <country/regioncode2> es el código de la referencia cultural secundaria.The RFC 4646 format for the culture name is <languagecode2>-<country/regioncode2>, where <languagecode2> is the language code and <country/regioncode2> is the subculture code. Por ejemplo, es-CL para español (Chile), en-US para inglés (Estados Unidos) y en-AU para inglés (Australia).For example, es-CL for Spanish (Chile), en-US for English (United States), and en-AU for English (Australia). RFC 4646 es una combinación de un código de referencia cultural ISO 639 de dos letras en minúsculas asociado con un idioma y un código de referencia cultural secundaria ISO 3166 de dos letras en mayúsculas asociado con un país o región.RFC 4646 is a combination of an ISO 639 two-letter lowercase culture code associated with a language and an ISO 3166 two-letter uppercase subculture code associated with a country or region. Vea Language Culture Name (Nombre de la referencia cultural del idioma).See Language Culture Name.

"Internacionalización" suele abreviarse como "I18N".Internationalization is often abbreviated to "I18N". La abreviatura toma la primera y la última letra de la palabra, y el número de letras que hay entre ellas, es decir, el 18 representa el número de letras que hay entre la "I" inicial y la "N" final.The abbreviation takes the first and last letters and the number of letters between them, so 18 stands for the number of letters between the first "I" and the last "N". Lo mismo se puede decir de "globalización" (G11N) y "localización" (L10N).The same applies to Globalization (G11N), and Localization (L10N).

Términos:Terms:

  • Globalización (G11N): es el proceso de hacer que una aplicación admita diferentes idiomas y regiones.Globalization (G11N): The process of making an app support different languages and regions.
  • Localización (L10N): es el proceso de personalizar una aplicación para un idioma y región determinados.Localization (L10N): The process of customizing an app for a given language and region.
  • Internacionalización (I18N): hace referencia a la globalización y la localización.Internationalization (I18N): Describes both globalization and localization.
  • Referencia cultural: es un idioma y, opcionalmente, una región.Culture: It's a language and, optionally, a region.
  • Referencia cultural neutra: se trata de una referencia cultural que tiene un idioma especificado, pero no una región.Neutral culture: A culture that has a specified language, but not a region. (por ejemplo, "en" y "es").(for example "en", "es")
  • Referencia cultural específica: es una referencia cultural que tiene un idioma y una región especificadosSpecific culture: A culture that has a specified language and region. (por ejemplo, "en-US", "en-GB" y "es-CL").(for example "en-US", "en-GB", "es-CL")
  • Referencia cultural principal: se trata de la referencia cultural neutra que contiene una referencia cultural específica.Parent culture: The neutral culture that contains a specific culture. (por ejemplo, "en" es la referencia cultural principal de "en-US" y "en-GB").(for example, "en" is the parent culture of "en-US" and "en-GB")
  • Configuración regional: la configuración regional es lo mismo que la referencia cultural.Locale: A locale is the same as a culture.

Nota

Es posible que no pueda escribir comas decimales en campos decimales.You may not be able to enter decimal commas in decimal fields. 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.To support jQuery validation for non-English locales that use a comma (",") for a decimal point, and non US-English date formats, you must take steps to globalize your app. Consulte este problema 4076 de GitHub para instrucciones sobre cómo agregar la coma decimal.See this GitHub issue 4076 for instructions on adding decimal comma.

Nota

Antes de ASP.NET Core 3.0, las aplicaciones escribían un registro de tipo LogLevel.Warning por solicitud si no se admitía la referencia cultural solicitada.Prior to ASP.NET Core 3.0 web apps write one log of type LogLevel.Warning per request if the requested culture is unsupported. El registro de un valor LogLevel.Warning por solicitud puede crear archivos de registro de gran tamaño con información redundante.Logging one LogLevel.Warning per request is can make large log files with redundant information. Este comportamiento ha cambiado en ASP.NET 3.0.This behavior has been changed in ASP.NET 3.0. RequestLocalizationMiddleware escribe un registro de tipo LogLevel.Debug, lo que reduce el tamaño de los registros de producción.The RequestLocalizationMiddleware writes a log of type LogLevel.Debug, which reduces the size of production logs.

Recursos adicionalesAdditional resources