Cómo añadir internacionalización básica (i18n) a tu App.

Traducción de un interesante artículo de Dana Woodman explicando cómo añadir traducciones y localización sencilla a una app Svelte. El método se basa en un post de Matthias Stahl, pero Dana Woodman explica cómo hacerlo sin necesidad de crear un repositorio adicional para las traducciones.

i18ninternacionalizaciontraduccion app
10 diciembre, 2021 Internacionalización de Apps Svelte
10 diciembre, 2021 Internacionalización de Apps Svelte

Texto original escrito por Dana Woodman, explicando cómo internacionalizar una App y traducir los textos de un idioma a otro de forma automática, sin necesidad de recurrir a una traductor o una agencia de traducción.

Disclaimer: Esta es una traducción gratuita de un texto original de Nicolas Bertagnolli realizada gratuitamente por un estudiante de traducción para la empresa de traducción Ibidem Group. Si necesitas servicios de traducción profesional, es mejor que contactes con una agencia de traducción en Barcelona o una agencia de traducción en Madrid.

***

Svelte Quick Tips (6 Part Series)
 1 🚀 Svelte Quick Tip: Styling slot content with :global
 2 🚀 Svelte Quick Tip: Styling conditional named slots
 3 🚀 Svelte Quick Tip: Create a tooltip action using Tippy.js
 4 🚀 Svelte Quick Tip: Adding basic internationalization (i18n) to you app
 5 🚀 Svelte Quick Tip: Connect a store to local storage
 6 🚀 Svelte Quick Tip: Creating a toast notification system 

👋 ¡Hola, Mundo!

Hace poco me topé con un gran vídeo del Dr. Matthias Stahl (tweet aquí, vídeo de YouTube aquí, código aquí) en el canal de YouTube Svelte, que ideó un enfoque sencillo para añadir traducciones i18n básicas a una aplicación Svelte.

Pensé que sería divertido y útil recrearlo, y aprovechar para hacer algunas optimizaciones y mejoras menores por el camino… 🤗

Queremos crear algo así:

translations in action

¡La mayor parte del crédito en este post va a Matthias aquí, así que asegúrate de echar un ojo a su trabajo y seguirle! 🙇

📒 Nota: esta no es una solución de internacionalización completa como i18next, por lo podría no ser la solución perfecta para lo que estés buscando.

¿Impaciente? Consulta el REPL de Svelte con todo el código

El objeto de las traducciones

En el ejemplo de Matthias, utiliza un objeto profundamente anidado para almacenar las cadenas de traducción. Esto funciona, pero es ligeramente ineficiente ya que tendrás que recorrer el objeto, especialmente si tienes múltiples capas anidadas (piensa en app => page => section => component => label).

En su lugar, he optado por un objeto plano cuya clave es la subetiqueta de localización de internacionalización (por ejemplo, en y no en-US) y una cadena con espacios separados por puntos para los valores traducidos. Si tenemos que gestionar muchas traducciones, esto sería una buena ventaja a nivel rendimiento.

Además, admitiremos variables incrustadas y HTML en nuestras cadenas de traducción:

// translations.js
export default {
en: {
"homepage.title": "Hello, World!",
"homepage.welcome": "Hi <strong>{{name}}</strong>, how are you?", "homepage.time": "The current time is: {{time}}",
},
es: {
"homepage.title": "¡Hola Mundo!",
"homepage.welcome": "Hola, <strong>{{name}}</strong>, ¿cómo estás?",
"homepage.time": "La hora actual es: {{time}}",
},
};

Esto nos permitirá tener claves con espacios de nombres, así como soportar formatos enriquecidos e inyectar valores (por ejemplo, cadenas, números, fechas, etc.).

El componente

Ahora crearemos nuestro componente Svelte, ¡huzzah! 👏

Este componente es bastante sencillo y consistirá en un desplegable de selección para elegir el idioma que el usuario desea utilizar, así como en la visualización de algunos textos de traducción, ¡incluyendo uno con HTML y variables personalizadas!

 
<!-- App.svelte -->
<script>
import { t, locale, locales } from "./i18n";

// Create a locale specific timestamp
$: time = new Date().toLocaleDateString($locale, {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric",
});
</script>

<main>
<p>
<select bind:value={$locale}>
{#each locales as l}
<option value={l}>{l}</option>
{/each}
</select>
</p>

<h1>{$t("homepage.title")}!</h1>
<p>{@html $t("homepage.welcome", { name: "Jane Doe" })}!</p>
<p>{$t("homepage.time", { time })}!</p>
</main>

Lo que estamos haciendo aquí es conectar un elemento < select> a un almacén Svelte (que crearemos en un momento) y también utilizar un método mágico $t() que nos permitirá hacer búsquedas de traducción.

Notarás que estamos creando una marca de tiempo específica de la localización para mostrar al usuario usando toLocaleDateString a la que pasamos el valor de la tienda $locale.

Si esto aún no tiene sentido para tí, no te preocupes, ¡sigue leyendo!

La tienda

Ahora viene la parte divertida, ¡vamos a crear nuestra tienda Svelte! 👯♂️

El repositorio es bastante simple: almacenamos primero el valor de la configuración regional (por ejemplo, en, es, etc) en un repositorio, y luego creamos un repositorio derivado de la configuración regional y el objeto de traducciones creado antes.

 
import { derived, writable } from "svelte/store";
import translations from "./translations";

export const locale = writable("en");
export const locales = Object.keys(translations);

function translate(locale, key, vars) {
// Let's throw some errors if we're trying to use keys/locales that don't exist.
// We could improve this by using Typescript and/or fallback values.
if (!key) throw new Error("no key provided to $t()");
if (!locale) throw new Error(`no translation for key "${key}"`);
// Grab the translation from the translations object.
let text = translations[locale][key];

if (!text) throw new Error(`no translation found for ${locale}.${key}`);
// Replace any passed in variables in the translation string.
Object.keys(vars).map((k) => {
const regex = new RegExp(`{{${k}}}`, "g");
text = text.replace(regex, vars[k]);
});

return text;
}

export const t = derived(locale, ($locale) => (key, vars = {}) =>
translate($locale, key, vars)
);

La mayor parte de la lógica está en el método translate que busca las claves e inyecta las variables, si están presentes.

El almacén derivado se mantendrá sincronizado con la configuración regional actual y por lo tanto nuestro método translate siempre recibirá la configuración regional actual cuando sea llamado. Cuando se actualice la configuración regional, las llamadas a $t() se volverán a calcular y así se actualizará todo nuestro texto en nuestro componente Svelte cuando el usuario cambie su configuración regional. ¡Genial! 😎

Esto se aleja un poco de la versión de Matthias, ya que no requiere la creación de un repositorio adicional para la traducción, algo que no es estrictamente necesario y es un poco más eficiente si lo omitimos.

Cómo montarlo todo

Ahora que tenemos nuestra tienda, tenemos todas las piezas para crear un sistema básico de internacionalización en Svelte, felicidades 🎉

Si quieres ver este código en acción, echa un vistazo al REPL de Svelte

🛰 Para ir un poco más allá

Ahora bien, esta opción no es adecuada para todo el mundo. Si estás construyendo una aplicación grande, robusta y con mucho contenido con muchas traducciones, entonces quizás quieras considerar algo como Locize en combinación con i18next. Siempre puedes integrar sus librerías JS con Svelte de forma similar.

Tampoco estamos desinfectando el contenido HTML, así que si estás inyectando datos suministrados por el usuario en tus cadenas de traducción, tendrás que asegurarte de desinfectar/eliminar la entrada para no crear una vulnerabilidad XSS. 🔐

Otro problema con este enfoque es que no se define un comportamiento alternativo para textos que no tienen traducción (son carencias de este sistema que igual no quieres oír ahora…).

Dicho esto, una solución como ésta puede ser útil cuando no se necesita una plataforma de traducción completa y bastan traducciones de cadenas relativamente básicas.

Se podría ampliar este ejemplo manteniendo el valor de la configuración regional en el almacenamiento local y por defecto en el idioma preferido del navegador, por ejemplo, utilizando la propiedad navigator.languages. Pero eso lo abordaremos en otro post..

🎬 Fin

Rate this post

Articulos relacionados


Traducción de Inglés a Español de un interesantísimo artículo de Matt Layman, ingeniero de software experto en open source y Python, que nos muestra en esta guía cómo hacer la internacionalización (i18n) para una aplicación de Python.

Traducción de Inglés a Español de un interesantísimo artículo de Dawei Ma, desarrollador de Xian, que nos explica detalladamente cómo implementar un proyecto de traducción de software, incluyendo tanto internacionalización (varios entornos lingüísticos) como localización...

Traducción de un interesantísimo tutorial de Jérôme W. sobre cómo crear un sistema de traducciones en un proyecto Laravel de forma que se pueda cambiar de idioma simplemente escogiéndolo del menu desplegable.