[MVC] Simple Translation System

Hola! hoy les voy a mostrar una serie de Clases de C# que utilizo para la traducción de mis aplicaciones MVC.




Hay muchísimas formas de traducir una View de MVC, la más conocida es la de los archivos RESX. Un tutorial de ello aquí.

Personalmente el uso de RESX no me agrada, porque por cada idioma y cultura hay que crear un archivo. Esto con el paso del tiempo se hace muy difícil de mantener. Es por ello que hoy les presento mi sistema de traducción: "Simple CSV-Based Translation System".

Este sistema lo que hace es cargar desde un archivo CSV los diferentes lenguajes en Memoria, y así hace veloces traducciones.

La ventaja de utilizar un archivo CSV es que dentro de él se encuentran todos los idiomas, por lo que es imposible olvidarse de traducir una palabra.



Para realizar una traducción en una página MVC, utilizo tres clases:

  • Lang: contiene un listado de constantes que asocian una palabra a traducir con las "key" del archivo CSV.
  • LocalizationService: se encarga de leer el archivo de traducciones, y de establecer y leer el idioma seleccionado (almacenado en sesión).
  • L: Utiliza LocalizationService de forma abreviada. ¿Por qué "L"? para no tener que escribir mucho en cada traducción. L tiene dos métodos: L y F. L traduce una palabra, y F traduce una palabra con argumentos (como String.Format lo hace).
Ejemplo de traducción:
    <h3>@L.T(Lang.Settings)</h3>

Resultado en español:
    <h3>Configuración</h3>

Resultado en inglés:
    <h3>Settings</h3>

Código


using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Web;
using Localization;

namespace Localization
{
    public static class LocalizationService
    {
        public static string Current()
        {
            return (HttpContext.Current.Session["Lang"]??"en").ToString();
        }

        public static string Get(string lang, string key, string def = "")
        {
            try
            {
                var wordKey = string.Format("Lang-{0}-{1}", lang, key);
                return (HttpContext.Current.Application[wordKey]??def).ToString();
            }
            catch(Exception ex)
            {
                return ex.Message;
            }
        }

        //
        //    Carga el archivo de traducción ubicado en App_Data (o en el path que se
        //    especifique.
        //
        //    Recomendable usar en Application_Start de global.asax
        //
        public static void Load(string path = "")
        {
            HttpContext.Current.Application.Lock();

            try
            {
                if (string.IsNullOrWhiteSpace(path))
                {
                    path = Path.Combine(System.Web.HttpContext.Current.Server.MapPath(@"~/App_Data"), "Lang.csv");
                }

                var linesList = new List<string>();

                using (StreamReader sr = new StreamReader(path, Encoding.Default))
                {
                    while (!sr.EndOfStream)
                    {
                        linesList.Add(sr.ReadLine());
                    }
                }

                var lines = linesList.ToArray();//File.ReadAllLines(path);

                var langsLine = lines[0].Substring(lines[0].IndexOf(';') + 1);
                var langs = langsLine.Split(';');

                HttpContext.Current.Application["Langs"] = langs;

                for (int i = 1; i < lines.Length; i++)
                {
                    if (string.IsNullOrWhiteSpace(lines[i]))
                        continue;

                    var words = lines[i].Split(';');
                    var key = words[0];

                    for (int j = 1; j < words.Length; j++)
                    {
                        HttpContext.Current.Application[string.Format("Lang-{0}-{1}", langs[j-1], key)] = words[j];
                    }

                }
            }
            finally
            {
                HttpContext.Current.Application.UnLock();
            }


        }

        public static void SetLanguage(string lang)
        {
            HttpContext.Current.Session["Lang"] = lang;
        }
    }
}


public static class L
{
    public static string T(string key, string def = "")
    {
        var lang = LocalizationService.Current().ToString();
        var result =  LocalizationService.Get(lang, key, def);

        return result;
    }
    public static string F(string key, params object[] args)
    {
        var lang = LocalizationService.Current().ToString();
        var text = LocalizationService.Get(lang, key);

        return string.Format(text, args);
    }
}



Uso

  1. Incluir clases en proyecto.
  2. Crear archivo CSV preferentemente en APP_DATA.
  3. Cargar archivo en App_Start de Global.asax.
  4. Listo para su uso!

Integración con Entity Framework

  1. Agregar a la clase UserProfile el atributo Lang (de tipo String). En él guardaremos el idioma elegido por el usuario.
  2. Actualizar la base de datos.
  3. Agregar en AccountController el método LoadUserLanguage (ver *3).
  4. Agregar LoadUserLanguage a Login(LoginModel model, string returnUrl) (ver *4).
  5. Modificar método Register (ver *5).
  6. Hacer un botón para modificar el idioma. Para ello se puede hacer que el botón sea un link a la acción Home/Language/ en el cual es el ID del lenguaje. En esa acción utilizamos LocalizationService para seleccionar el nuevo idioma, y podemos luego hacer un redirect hacia la página principal.

*3. LoadUserLanguage

    [Authorize]
    [InitializeSimpleMembership]
    public class AccountController : Controller
        
        /* ... */

        private void LoadUserLanguage(string userName)
        {
            using (UsersContext db = new UsersContext())
            {
                UserProfile profile = db.UserProfiles.FirstOrDefault(u => u.UserName.ToLower() == userName.ToLower());
                LocalizationService.SetLanguage(profile.Lang);
            }
        }
    }

*4. Login
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult Login(LoginModel model, string returnUrl)
        {
            if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
            {
                LoadUserLanguage(model.UserName);   //  Loads the user language
                return RedirectToLocal(returnUrl);
            }

            // If we got this far, something failed, redisplay form
            ModelState.AddModelError("", "The user name or password provided is incorrect.");
            return View(model);
        }

*5. Register
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult Register(RegisterModel model)
        {
            if (ModelState.IsValid)
            {
                // Attempt to register the user
                try
                {

                    WebSecurity.CreateUserAndAccount(model.UserName, model.Password,
                        new {
                            UserName = model.UserName,
                            Lang = LocalizationService.Current()
                        });
                    WebSecurity.Login(model.UserName, model.Password);

                    return RedirectToAction("Index", "Home");
                }
                catch (MembershipCreateUserException e)
                {
                    ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
                }
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }


Bueno, espero que les haya sido útil este post!

Saludos!

Comentarios

Entradas populares de este blog

Ordenar lista en python - Super fácil!