Una aplicación web multilingue permite que llegue a un público mas amplio. Net Core proporciona servicios y middleware para la localización de diferentes idiomas. En el siguiente post mostraremos crearemos una página que permitirá leerse en español y en inglés.

Para realizar este post se utilizará Net Core 3.1. No se necesita ningún Nuget y se utilizará un proyecto web Asp Net Core MVC.

LOCALIZACIÓN EN VISTAS

Creacción de archivos de traducción

En primer lugar, vamos a crear una carpeta donde guardaremos todos los archivos de traducción. Esta carpeta se llamará Resources y se creará a nivel de proyecto, en el mismo nivel que Views o Models.

CreacionCarpetaResources
Creacción de carpeta Resources

Dentro de esta carpeta se crearán los archivos de traducción. Estos archivos son de tipo Resource y se crean pulsando botón derecho desde la carpeta Resources -> Add -> New File -> Panel Misc -> Resource File.

CrearResourceFile
Crear Archivo Resource

Como vemos en la imagen, este archivo tiene una nomenclatura peculiar. Para que haga referencia a una vista concreta, debemos darle un nombre que haga referencia a dicha vista seguido del idioma al que vamos a traducir. En este caso hacemos referencia a la vista Index, esta vista está en la carpeta Views/Home, además vamos a crear un archivo que traduzca la página a español, por lo que la nomenclatura de nuestro archivo será: Views.Home.Index.es.resx.

Existe la posibilidad de hacer lo mismo a través de un sistema de carpetas dentro de la carpeta Resources. Para ello se creará una carpeta Views, dentro una carpeta llamada Home y dentro un archivo llamado Index.es.resx.

Para que todo funcione correctamente, dichos archivos deben tener una configuración concreta. Debe ser de tipo public en la pestaña de Access Modifier. Tambien debemos acceder a las properties del archivo y aseguranos de que la pestaña Build action tenga seleccionada la option EmbeddedResource.

BuildActions
Properties del archivo

Este archivo es de tipo XML por lo que funciona a base de etiquetas. Visual Studio provee un editor específico para este tipo de archivos que facilita su uso. Vamos a crear un registro al que llamaremos «saludo» y le daremos el valor «Hola!».

<data name="saludo" xml:space="preserve">
    <value>Hola!</value>
</data>

Configuración de las vistas

Para insertar esos strings dentro de nuestras vistas, usaremos la interfaz IViewLocalizer. Dicha interfáz proviene del espacio de nombre Microsoft.AspNetCore.Mvc.Localization, por lo que tendremos que añadirlo a nuestro archivo _ViewImports.

@using Microsoft.AspNetCore.Mvc.Localization

Una vez hemos hecho el using, podemos utilizar la interfaz en nuestro archivo Index.

@inject IViewLocalizer Localizer
@{
    ViewData["Title"] = Localizer["saludo"];
}
<div class="text-center mt-5">
    <h1 class="display-4">@ViewData["Title"]</h1>
</div>

En el ejemplo anterior hacemos referencia a nuestro string «saludo» utilizando la interfaz.

Además podemos pasar parámetros a dichos strings para hacer dinámicas nuestras traducciones. Crearemos un nuevo string llamado «saludoCompleto» que recibirá un nombre como parámetro:

<data name="saludoCompleto" xml:space="preserve">
    <value>Hola {0}!</value>
</data>

Los parámetros se indican con las llaves y el número de parámetro que recibirá. Si recibe varios parámetros, se colocarán en orden con dichos números.

Para utilizar estos strings en nuestro código Index haremos prácticamente lo mismo que en el código anterior, pero añadiendo el nombre como parámetro:

@inject IViewLocalizer Localizer
@{
    ViewData["Title"] = Localizer["saludoCompleto", "Maria"];
}
<div class="text-center mt-5">
    <h1 class="display-4">@ViewData["Title"]</h1>
</div>

Configuración del Startup

El método ConfigureServices de nuestro archivo Startup tendrá el siguiente código:

public void ConfigureServices (IServiceCollection services) {
      services.AddLocalization(options => 
                   options.ResourcesPath = "Resources");
      
      services.AddMvc().AddViewLocalization(
            LanguageViewLocationExpanderFormat.Suffix)
            .AddDataAnnotationsLocalization();

      services.Configure<RequestLocalizationOptions>(
            options => {
                var suportedCultures = new List<CultureInfo> {
                    new CultureInfo("en-US"),
                    new CultureInfo("es-ES"),
                    new CultureInfo("en"),
                    new CultureInfo("es")
                };
            options.DefaultRequestCulture = 
                          new RequestCulture("en-US");
            options.SupportedCultures = suportedCultures;
            options.SupportedUICultures = suportedCultures;
       });
       services.AddControllersWithViews(
            options => options.EnableEndpointRouting = false)
            .AddSessionStateTempDataProvider();
}

A continuación, en el archivo configure, añadiremos el siguiente código:

public void Configure (IApplicationBuilder app,
     IWebHostEnvironment env) {
            if (env.IsDevelopment()) {
                app.UseDeveloperExceptionPage();
            } else {
                app.UseExceptionHandler("/Home/Error");
                app.UseHsts();
            }
            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseRouting();

            var supportedCultures = 
                    new[] { "en-US", "es-ES", "es", "en" };
            var localizationOptions = 
                    new RequestLocalizationOptions()
                    .SetDefaultCulture(supportedCultures[0])
                    .AddSupportedCultures(supportedCultures)
                    .AddSupportedUICultures(supportedCultures);
            
            app.UseRequestLocalization(localizationOptions);
            app.UseAuthorization();
            app.UseMvc(routes => {
                routes.MapRoute(
                    name: "default",
                    template: 
                     "{controller=Home}/{action=Index}/{id?}");
            });
}

En este código indicamos el idioma base de nuestra aplicación además de los lenguajes soportados. En nuestro caso soportamos español e inglés, por lo que aparecen en nuestra lista suportedCultures.

Configuración del Layout

El último paso es el archivo _Layout, que se encuentra dentro de Views/Shared. Este archivo contiene, entre otras cosas, la barra de navegación de nuestra aplicación. Queremos añadir un select que permita seleccionar el idioma en todo momento, por lo que lo añadiremos ahi.

<nav class="navbar navbar-expand-lg navbar-light bg-light">
        <div class="container">
            <a class="navbar-brand" href="#">Localization</a>
            <button class="navbar-toggler" type="button" 
             data-bs-toggle="collapse" 
             data-bs-target="#navbarSupportedContent" 
             aria-controls="navbarSupportedContent" 
             aria-expanded="false" 
             aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse"
             id="navbarSupportedContent">
                <ul class="navbar-nav me-auto mb-2 mb-lg-0">
                    <li class="nav-item">
                        <a class="nav-link active" 
                         aria-current="page" href="#">Home</a>
                    </li>
                </ul>
                <div class="ms-auto">
                    <select class="form-control" 
                     id="langSelect">
                        <option default>Language</option>
                        <option value="ES">Español</option>
                        <option value="EN">Ingles</option>
                    </select>
                </div>
            </div>
        </div>
    </nav>

En el código anterior creamos una barra de navegación simple de Bootstrap a la que añadiremos el select del final. La funcionalidad que buscamos es que al pulsar, la página cambie de idioma. La página es consciente del lenguaje a utilizar a través de su url. Si su url es: https://localhost:5001/?culture=es-ES sabremos que la página está en español, y si es: https://localhost:5001/?culture=en-US, sabremos que está en ingles.

Para ello utilizaremos un poco de JavaScript:

<script type="text/javascript">
   $(document).ready(function () {
        var culture = 
             window.location.href.split("?").length > 1 ? 
             window.location.href.split("?")[1] : "";
            
        if (culture != "") {
             var lang = culture.split("=")[1];
             if (lang == "es-ES") {
                $("#langSelect").val("ES");
             } else {
                $("#langSelect").val("EN");
             }
        }
       
        $("#langSelect").change(function () {
           var redirect = 
                $("#langSelect").val() == "ES" ? 
                "?culture=es-ES" : "?culture=en-US";
                window.location.href = redirect;
        });
   })
</script>

En el script anterior, en primer lugar consultamos la url para poner la opcion correcta en el select cuando se carga la página. En segundo lugar, en el método change modificamos la url de la página en función del valor seleccionado en nuestro select añadiendo el sufijo adecuado para la página en español (?culture=es-ES) y en inglés (?culture=en-US).

Con los pasos anteriores el resultado es el siguiente:

PaginaTerminada
Resultado

LOCALIZACIÓN EN CONTROLADORES

Para realizar esta acción en un controlador utilizaremos la interfáz IStringLocalizer que nos permitirá acceder de la misma manera a nuestros strings. Para ello habrá que inyectarlo en el constructor, pero no habrá que hacer ninguna modificación a nuesto archivo Startup:

public class HomeController : Controller {
        private IStringLocalizer<HomeController> _localizer;

        public HomeController (IStringLocalizer<HomeController> 
          local) {
            this._localizer = local;
        }

        public IActionResult Index () {
            ViewData["despedida"] = 
                 this._localizer["despedida"];
            return View();
        }
}

Como hemos mencionado anteriormente, nuestros archivos de localización deben tener la ruta del fichero al que hacemos referencia, de tal manera que en la carpeta Resources crearemos un nuevo archivo con el siguiente nombre: Controllers.HomeController.es.resx

Dicho archivo será exactamente igual que los anteriores. Crearemos un string de despedida:

<data name="despedida" xml:space="preserve">
    <value>Adios Controller!</value>
</data>

En el controlador ya hemos hecho referencia a este string y lo hemos enviado a la vista a través de un ViewData, por lo que nuestra vista Index quedará:

@inject IViewLocalizer Localizer
@{
    ViewData["Title"] = Localizer["saludoCompleto", "Maria"];
}
<div class="text-center mt-5">
    <h1 class="display-4">@ViewData["Title"]</h1>

    <h1 class="display-4">@ViewData["despedida"]</h1>
</div>

Y el resultado será:

ResultadoController
Uso de Localizacion en controladores

De esta manera podemos traducir páginas a varios idiomas de manera sencilla. Hay muchas formas distintas de hacerlo dependiendo del tamaño de la aplicación o incluso utilizando Nugets que permiten la traducción utilizando Google Translator como XLocalizer. Sin embargo, esta es la manera mas sencilla de hacerlo para proyectos pequeños ya que no requiere instalación de Nugets ni una configuración compleja.

Teneis todo el código en el siguiente repositorio de github.

Autor/a: Susana Santos Moreno

Curso: Desarrollo Web Full Stack, MultiCloud y Multiplataforma

Centro: Tajamar

Año académico: 2020 – 2021

Código/ recursos utilizados/ Otros datos de interés: enlace a Github y Linkedin.

Leave a Comment

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.