Qwik — Internacionalización (i18n) — I

Guía completa para poder crear aplicaciones en diferentes idiomas mediante i18n

Anartz Mugika Ledo🤗
23 min readMar 13, 2023

Comenzamos con un nuevo artículo en el que vamos a dar todos los pasos necesarios desde 0 con el proceso de instalación, configuración y puesta en marcha, para poder crear una aplicación en Qwik en varios idiomas, viendo todos los entresijos de esta librería y así aprovechar todo su potencial.

Voy a estructurar este contenido en dos partes, por un lado comenzaremos con lo básico, para poder añadir los textos básicos mediante el enrutamiento y sin ello.

Empezaremos con la instalación y configuración hasta llegar a completar esas dos formas de gestionar los textos de traducción.

En la segunda parte ya haremos más hincapié a detalles más profundos como el trabajo con la función translate y en una tercera parte (después de unas semanas) como configurar para que coja las traducciones de los metadatos, imprescindible para indexar bien los textos en el idioma seleccionado en los buscadores.

Esto es algo habitual en proyectos que queremos mostrar a un publico que se comunica en diferentes idiomas.

En este artículo vamos a trabajar en una aplicación sencilla, una página principal donde tendremos información básica con varios textos de diferentes maneras plasmados y los idiomas que vamos a tratar son Inglés, Español e Euskera.

Os recomiendo leer los artículos anteriores en orden y si practicáis, ¡¡mucho mejor!!.

Recordad, que si hay dudas, aun leyéndolo, podéis preguntarlo en los comentarios y sin dudas me gustaría que me comentéis también, para tener feedback.

Todos los artículos publicados del curso los encontraréis en la siguiente lista que iré actualizando semanalmente y estableciendo el orden natural recomendado:

Qwik paso a paso desde 0 al detalle

23 stories

Los requisitos a tener en cuenta son los mismos que en todos los artículos y para trabajar en este artículo crearemos un nuevo proyecto en una versión más actual de la que hemos estado trabajando hasta este mismo instante.

Ya sabéis como se hace, a día de hoy (13/03/2023) usaremos la versión 0.19.2 de Qwik, que es la que es compatible con la librería que usaremos para aplicar i18n.

Esto es lo que vamos a ver en este artículo:

  • Introducción a Qwik Speak, librería para trabajar con i18n en Qwik.
  • Instalación y configuración inicial.
  • Cambiar configuración regional (local) desde la app.
  • i18n aplicando selección del idioma mediante ruta.
  • (PARTE 2) Opciones con la función $translate
  • (PARTE 2) Traducir los textos de metadatos.

Introducción Qwik Speak

Esta es la librería que vamos a usar para implementar la funcionalidad de la internacionalización en nuestros proyectos de Qwik.

Vamos a seguir la siguiente referencia y aquí os indicaré en que punto nos encontramos, para que si avanza a versiones futuras, podamos saber correctamente como aplicarlo.

Hay algunos aspectos que personalmente les faltan unos matices para entenderlo bien y mi idea es dejarlo todo matizado para que se pueda trabajar sin problemas.

Actualmente existe la versión 0.7.2 de qwik-speak pero como no funciona correctamente con la versión más actual que es la 0.21.0 de Qwik, investigando he conseguido encontrar la compatibilidad entre la versión 0.6.2 de Qwik Speak y la versión 0.19.2 de Qwik.

Por lo tanto, debemos de empezar creando el proyecto de Qwik en la versión 0.19.2 para estar preparados para el siguiente punto:

npm create qwik@0.19.2

Instalación y configuración inicial

Una vez que ya tenemos creado y preparado para trabajar en la versión 0.19.2 (marzo 2023), vamos a empezar a seguir los pasos de instalación y configuración de la librería qwik-speak. Seguimos las indicaciones que vienen a continuación.

Instalamos el paquete (en la versión 0.6.2 aunque a día de hoy exista la 0.7.2):

npm install qwik-speak@0.6.2 --save-dev

Esperamos un instante a que complete el proceso de instalación y cuando esté listo, aparecerá algo de este estilo:

Bien, ahora vamos a pasar a implementar la configuración del fichero para gestionar las traducciones.

Configuración del fichero de traducciones

Creamos un fichero nuevo en src/config/speak-i18n.ts e iremos añadiendo el código por partes que os pueda ir explicando y así tenerlo todo claro con todo en conjunto:

import { $ } from '@builder.io/qwik';
...

La función $ va a indicar a Qwik que vamos a realizar una carga diferida de una referencia que aplicaremos a una función. Esto lo aplicaremos unas líneas más abajo a la hora de cargar los textos de traducción de manera diferida.

...
// (2)
import { isServer } from '@builder.io/qwik/build';
...

isServer se usa para comprobar si ese componente se está montando en el servidor o no. En caso de ser afirmativo entrará dentro del condicional del resultado verdadero que sale de esta operación para coger el path absoluto de este y usarlo a la hora de cargar los textos requeridos.

...
// (3)
import type {
LoadTranslationFn,
SpeakConfig,
TranslationFn
} from 'qwik-speak';
...

Elementos que vamos a usar de la librería qwik-speak para primero con SpeakConfig implementar las configuraciones de idiomas soportados, por defecto y más aspectos, LoadTranslationFn para hacer la carga de los textos de i18n que necesitaremos de manera diferida y finalmente TranslationFn para obtener el resultado de los textos cargados de manera diferida.

Tranquilidad, ahora os explico esto 3 últimos puntos de manera individual.

...
// 4
export const config: SpeakConfig = {
// 4.1.
defaultLocale: { lang: 'en', currency: 'USD', timeZone: 'America/Los_Angeles' },
// 4.2
supportedLocales: [
{ lang: 'es', currency: 'EUR', timeZone: 'Europe/Madrid' },
{ lang: 'en', currency: 'USD', timeZone: 'America/Los_Angeles' },
{ lang: 'eu', currency: 'EUR', timeZone: 'Europe/Madrid' }
],
// 4.3
assets: [
'app'
// Directorio de traducciones que va a estar disponible para compartir textos entre diferentes rutas
]
};
...

Configuración principal de los idiomas que soportará nuestra app. Vamos a tener en cuenta estas tres propiedades que son muy importantes: defaultLocale, supportedLocales y assets.

  • defaultLocale: Es el idioma (lang) que vamos a añadir a nuestra app por defecto, para que cargue los textos acordes a ese idiomas seleccionado. Aparte de esto, le añadimos la configuración de la divisa mediante currency y la zona horaria con timeZone, para que cuando apliquemos fechas, aplique bien el formato de fecha y hora acorde al huso horario.

Os dejo disponible la referencia a la información para poder trabajar correctamente con los idiomas, es decir, con la propiedad lang.

Esta será una lista que contiene los códigos ISO639–1, donde tenemos idiomas como Español (es), Euskera (eu), Inglés (en), Italiano (it), Portugués (pt),… Trabajaremos con los tres primeros aunque esto lo podemos ampliar a la lista de idiomas que necesitemos.

Para trabajar con la propiedad currency de manera correcta, usaremos esta referencia

Y finalmente, para asignar correctamente las zonas horarias, con timeZone, usaremos esta referencia.

Os invito a que le echéis un vistazo y probéis con diferentes.

  • supportedLocales: Similar a defaultLocale pero en este caso será un array de idiomas, donde añadiremos aparte del idioma que hemos puesto por defecto, todos los idiomas que vamos a usar en nuestra aplicación.
  • assets: Listado de nombres de directorios que vamos a tener disponibles de manera global en nuestra aplicación. Usaremos app para compartir los textos de las páginas, ya que en muchos casos habrá textos duplicados y si quisiéramos hacer una carga dinámica mediante claves o parámetros, usaremos runtime (esto lo veremos cuando trabajemos con la carga de idiomas en los metadatos)

En este último punto había bastantes aspectos a tener en cuenta y cosas que debemos de aplicar bien, aunque no son complicadas, pasamos al apartado donde cargamos los textos de la selección realizada.

...
// (5)
export const loadTranslationData$: LoadTranslationFn = $(async (lang: string, asset: string, origin?: string) => {
let url = '';
// URL Absoluta del servidor
// Esto por si hacemos la carga desde el apartado del servidor
if (isServer && origin) {
url = origin;
}
url += `/i18n/${lang}/${asset}.json`;
const response = await fetch(url);
if (response.ok) {
return response.json();
}
else if (response.status === 404) {
console.warn(`loadTranslation$: ${url} not found`);
}
});
...

En este punto vamos a obtener las referencias para la carga de los textos, mediante el idioma (lang), la referencia del directorio (asset, como puede ser app o una página concreta que veremos como home) y origin, que será una referencia por si viene un componente montado en el servidor para obtener su ruta absoluta.

Esto todo lo va a cargar de manera diferida ya que hemos añadido el símbolo $ al inicio de la función, para no dejarnos de lado ninguna traducción en el camino.

...
// (6)
export const translationFn: TranslationFn = {
loadTranslation$: loadTranslationData$
};

Esta función usará la función de la carga de los textos de manera diferida del punto anterior. Esto todo que hemos visto es lo relacionado al fichero que hemos creado en src/config/speak-i18n.ts cuyo código completo será lo siguiente:

// (1)
import { $ } from '@builder.io/qwik';
// (2)
import { isServer } from '@builder.io/qwik/build';
// (3)
import type {
LoadTranslationFn,
SpeakConfig,
TranslationFn
} from 'qwik-speak';


// 4
export const config: SpeakConfig = {
// 4.1
defaultLocale: { lang: 'en', currency: 'USD', timeZone: 'America/Los_Angeles' },
// 4.2
supportedLocales: [
{ lang: 'es', currency: 'EUR', timeZone: 'Europe/Madrid' },
{ lang: 'en', currency: 'USD', timeZone: 'America/Los_Angeles' },
{ lang: 'eu', currency: 'EUR', timeZone: 'Europe/Madrid' }
],
assets: [
'app' // Directorio de traducciones que va a estar disponible para compartir textos entre diferentes rutas
]
};
// 5
export const loadTranslationData$: LoadTranslationFn = $(async (lang: string, asset: string, origin?: string) => {
let url = '';
// Absolute urls on server - Esto por si hacemos la carga desde el apartado del servidor
if (isServer && origin) {
url = origin;
}
url += `/i18n/${lang}/${asset}.json`;
const response = await fetch(url);
if (response.ok) {
return response.json();
}
else if (response.status === 404) {
console.warn(`loadTranslation$: ${url} not found`);
}
});
// 6
export const translationFn: TranslationFn = {
loadTranslation$: loadTranslationData$
};

Implementar Proveedor para aplicar i18n con Qwik Speak

Para implementar bien las configuraciones y obtener los textos en nuestra app, tenemos que integrar el proveedor que nos proporciona la librería aplicando QwikSpeakProvider como principal componente y dentro de el todo lo que viene dentro de src/root.tsx

Pasaremos de lo siguiente:

import { component$ } from '@builder.io/qwik';
import { QwikCityProvider, RouterOutlet, ServiceWorkerRegister } from '@builder.io/qwik-city';
import { RouterHead } from './components/router-head/router-head';

import './global.css';
export default component$(() => {
return (
<QwikCityProvider>
...
</QwikCityProvider>
);
});

A esto nuevo con todos lo cambios:

import { component$ } from '@builder.io/qwik';
import { QwikCityProvider, RouterOutlet, ServiceWorkerRegister } from '@builder.io/qwik-city';
import { RouterHead } from './components/router-head/router-head';

import './global.css';
// Estos imports son nuevos
// Proveedor de Qwik Speak
import { QwikSpeakProvider } from 'qwik-speak';
// Configuración de los idiomas y función de carga de los textos
import { config, translationFn } from './config/speak-i18n';
export default component$(() => {
// Aplicando el cambio añadiendo QwikSpeakProvider
return (
<QwikSpeakProvider config={config} translationFn={translationFn}>
<QwikCityProvider>
...
</QwikCityProvider>
</QwikSpeakProvider>
);
});

Hasta este punto es la configuración que debemos de hacer siempre si o si, apliquemos el uso de idiomas con enrutamiento o sin el.

Voy a dejar en este punto lo desarrollado para que tengáis como base esto y así poder empezar desde este punto aplicando los siguientes apartados de manera individual.

Cambiar de idioma aplicando configuración regional

Empezamos con la primera forma de gestionar los idiomas dentro de nuestra aplicación Qwik.

Podemos seleccionar el idioma resolviendo la configuración regional mediante dos maneras: pasando el parámetro de configuración regional al componente QwikSpeakProvider o asignándolo a la configuración regional manejada por Qwik.

En src/layout.tsx, después del componente predeterminado (component$), añadimos el siguiente código:

export const onRequest: RequestHandler = ({ request, locale }) => {
// 1
const cookie = request.headers?.get('cookie');
// 2
const acceptLanguage = request.headers?.get('accept-language');

let lang: string | null = null;
// 3
if (cookie) {
const result = new RegExp('(?:^|; )' + encodeURIComponent('locale') + '=([^;]*)').exec(cookie);
if (result) {
lang = JSON.parse(result[1])['lang'];
}
}
// 4
if (!lang) {
if (acceptLanguage) {
lang = acceptLanguage.split(';')[0]?.split(',')[0];
}
}
// 5
locale(lang || config.defaultLocale.lang);
};

Y este será su funcionamiento:

  • 1 — Obtenemos el valor de las cookies almacenándolo en cookie, en el caso de que existan. Lo lógico es que aunque tengamos algo almacenado de primeras a menos que hayamos trabajado con algo similar, no tengamos la información del valor locale.
  • 2 — Obtenemos el valor de las preferencias del usuario que tiene configurado en su navegador.
  • 3 — Si tenemos información de las cookies almacenado en cookie, accede y si dentro de esta información encuentra el valor del locale, seleccionará la propiedad lang que nos dará el idioma que está almacenado asignándolo al valor lang. Si no encuentra el valor del locale, lo ignora y sigue para abajo y accederá al 4.
  • 4 — Si no hemos conseguido obtener información de las cookie con el idioma, probará si tenemos la información de las preferencias del usuario en el navegador. Si lo encuentra, lo almacena en lang.
  • 5 — Resuelve el locale con el valor lang a partir de la información de las cookies / preferencias del usuario en el navegador y en el caso de que no haya un valor asignado, cogerá el valor locale por defecto asignado en src/config/speak-i18n.ts visto anteriormente.

El código en ese fichero se reflejará de la siguiente forma:

import { component$, Slot } from '@builder.io/qwik';
import { loader$, RequestHandler } from '@builder.io/qwik-city';
import { config } from '~/config/speak-i18n';

import Header from '../components/header/header';
export const useServerTimeLoader = loader$(() => {
return {
date: new Date().toISOString(),
};
});
export default component$(() => {
const serverTime = useServerTimeLoader();
return (
<>
....
</>
);
});
export const onRequest: RequestHandler = ({ request, locale }) => {
// 1
const cookie = request.headers?.get('cookie');
// 2
const acceptLanguage = request.headers?.get('accept-language');
let lang: string | null = null;
// 3
if (cookie) {
const result = new RegExp('(?:^|; )' + encodeURIComponent('locale') + '=([^;]*)').exec(cookie);
if (result) {
lang = JSON.parse(result[1])['lang'];
}
}
// 4
if (!lang) {
if (acceptLanguage) {
lang = acceptLanguage.split(';')[0]?.split(',')[0];
}
}
// 5
locale(lang || config.defaultLocale.lang);
};

Internamente, Qwik Speak intentará tomar la configuración regional (locale) de Qwik, antes de volver a la configuración regional (locale) predeterminada si no está en las configuraciones regionales compatibles (supportedLocales).

Añadiendo algunas traducciones en la ruta principal

Una vez que hemos completado el paso de configurar la gestión de la configuración regional tenemos en cuenta lo siguiente:

  • Si existe el valor de la cookie cogemos su valor lang
  • Si no existe el valor cookie, iremos por las preferencias de usuario del navegador, cuyo valor si no existe cogerá el valor por defecto asignado en defaultLocale de la configuración especificada en src/config/speak-i18n.ts, vamos a añadir algunas traducciones para empezar a trabajar con diferentes idiomas.

Vamos a src/routes/index.tsx y añadimos un nuevo componente llamado <Home /> con este código, por el momento no hace nada especial:

export const Home = component$(() => {
return (
<>
Texto que añadiremos
</>
);
});

Y dentro del componente por defecto, donde tenemos todo el contenido por defecto que nos viene al crear una app de Qwik, lo reemplazamos por el siguiente código añadiendo una novedad, el uso del componente <Speak />:

import {
Speak,
} from 'qwik-speak';

export default component$(() => {
return (
/**
* Añadimos los textos relacionado al componente Home
* (Solo se aplicará en componentes hijos)
*/
<Speak assets={['home']}>
<Home />
</Speak>
);
});

Aquí estamos utilizando el componente <Speak/> pasándole la referencia del directorio para agregar traducciones específicas a la página de inicio y su nombre de clave será home y estaremos aplicando i18n en el hijo, que es <Home />.

Para agregar un texto para el componente <Home /> hacemos lo siguiente:

import {
$translate as t, // <==== Para ejecutar la traducción de una clave
Speak,
} from 'qwik-speak';

export const Home = component$(() => {
useStylesScoped$(`
span {
background-color: #d4cfcf
}
`)
return (
<>
<h1>{t('app.title')}</h1> (1)
<p>Coge de la carpeta "app" en el idioma seleccionado y la propiedad <b>title</b></p>
<p>Esto hará referencia al fichero que generamos posteriormente en <span>public/i18n/_locale_/app.json</span></p>
<h3>{t('home.intro')}</h3> (2)
<p>Coge de la carpeta "home" en el idioma seleccionado y la propiedad <b>intro</b></p>
<p>Esto hará referencia al fichero que generamos posteriormente en <span>public/i18n/_locale_/home.json</span></p>
</>
);
});

De esta manera, sin tocar nada (no creéis los ficheros a mano porque os voy a enseñar a generarlos con un comando) iniciamos el proyecto, a ver que pasa.

Ejecutamos el comando para iniciar:

npm start

Y esto es lo que nos indica en el terminal cuando ejecutamos el comando mencionado:

  • 1 — Ejecución normal de la aplicación de Qwik
  • 2 — Ha seleccionado el idioma mediante la referencia que se le especifica dentro de la configuración de src/config/speak-i18n.ts
  • 3 — No encuentra los ficheros de traducciones de los nombres clave app y home, lo normal, porque no los hemos creado.

Como no coge las traducciones, al no haberse generado los ficheros, tenemos el siguiente estado en la ruta principal con el componente <Home /> donde muestra la referencia de las claves asociadas a los textos de traducción que queremos añadir, sin añadir ningún texto traducido (es lógico, porque no existe ahora mismo).

Podemos crear a mano los ficheros en public/i18n/<locale>/app.jsonypublic/i18n/<locale>/home.json

Aunque podamos hacerlo mejor lo hacemos mediante un comando que configuraremos mediante el Qwik Speak Extract.

Este va a ser más rápido y si se diese el caso que aparte de app y home, tuviésemos referencias de más componentes, sería muy costoso por tiempo y seguramente cometeremos algún error, ya que con el script este cualquier nuevas claves se extraen y añaden automáticamente.

Configurando y ejecutando Qwik Speak Extract

Teniendo en cuenta esta referencia vamos a configurar el script basándonos en los idiomas que va a soportar nuestra aplicación.

Acudimos al ficheros package.json y en el apartado de scripts añadimos un nuevo script, que lo llamaremos qwik-speak-extract y lo añadiremos siguiendo este formato:

"qwik-speak-extract": "qwik-speak-extract --supportedLangs=<IDIOMAS_SOPORTADOS>"

Teniendo en cuenta que los idiomas que va a soportar son:

  • Español: es
  • Euskera: eu
  • Inglés: en

El script deberá de configurarse de la siguiente forma:

"qwik-speak-extract": "qwik-speak-extract --supportedLangs=es,eu,en"

Una vez configurado, ejecutamos el script para generar los ficheros con todas sus claves.

npm run qwik-speak-extract

Por el momento no tenemos mucho contenido, pero podremos ejecutar este comando para recibir actualizaciones, por lo que no nos preocupemos y este será el resultado:

  • 1 — Ejecutar el script
  • 2 — Se ejecuta el comando establecido en el script
  • 3 — Se generan y se extraen todas las claves. En este caso como tenemos dos nombres clave como app y home, junto con tres idiomas, se generan 6 ficheros. Se puede ver a continuación y abriendo uno de ellos se ve que podemos escribir el contenido que deseamos, que está en blanco por no añadir un valor por defecto (esto lo veremos más adelante como asignarlo)

Vamos a rellenar los textos de los 6 ficheros, acorde a lo que vamos a poner. Yo añadiré los siguientes contenidos, en todos los ficheros:

Inglés (public/i18n/en):

  • app.json
{
"app": {
"title": "App Title"
}
}
  • home.json
{
"home": {
"intro": "Home Intro"
}
}

Español (public/i18n/es):

  • app.json
{
"app": {
"title": "Título de la aplicación"
}
}
  • home.json
{
"home": {
"intro": "Entrada de Home"
}
}

Euskera (public/i18n/eu):

  • app.json
{
"app": {
"title": "Aplikazioaren titulua"
}
}
  • home.json
{
"home": {
"intro": "Home-ren hasiera"
}
}

Cuando lo tengamos, ejecutamos de nuevo para iniciar el proyecto

npm start

Al iniciar el proyecto, cogerá el idioma por defecto (defaultLocale) y coge los textos asociados como se puede ver, sin mostrar las referencias de las claves como anteriormente.

¡Bien! Ya hemos conseguido coger los textos de las traducciones. Lo que nos queda es implementar la funcionalidad para ir cambiando la configuración regional (locale) desde la aplicación.

Cambiar configuración regional (locale) desde la app

Ya hemos conseguido cargar los textos de traducción, pero solo lo hemos conseguido con el idioma Inglés, que es el idioma que se ha configurado por defecto en defaultLocale.

Lo que necesitamos es una opción para poder cambiar y visualizar los textos asociados al idioma que queremos utilizar en la aplicación en cualquier momento sin necesidad de cambio de ruta ni recarga de la página.

Vamos a implementar varios botones dentro un componente llamado <ChangeLocale /> que se añadirá dentro del componente header (src/components/header) para tenerlo de manera accesible en todas las rutas.

Crear componente ChangeLocale

Creamos el componente <ChangeLocale /> en la siguiente ruta src/components/header/change-locale.tsx e iré paso por paso añadiendo lo comentarios de lo que hará cada apartado

// 1 Los import necesarios para trabajar con los elementos para el cambio de idiomas
</strong><strong>import { component$, $} from '@builder.io/qwik';
</strong>import { changeLocale,
$translate as t,
useSpeakContext,
useSpeakConfig,
SpeakLocale } from 'qwik-speak';

// 2 Constantes para hacer uso de la duración de las cookies
const ONE_HOUR_SECONDS = 3600;
const ONE_DAY_SECONDS = 24 * ONE_HOUR_SECONDS; // 86400
const ONE_WEEK_SECONDS = 7 * ONE_DAY_SECONDS; // 604800
const ONE_MONTH_SECONDS = 30 * ONE_DAY_SECONDS; // 2592000
const ONE_YEAR_SECONDS = 365 * ONE_DAY_SECONDS; // 31536000

// 3 Componente para el cambio
export const ChangeLocale = component$(() => {

// 4 - Funciones hook que se usan para el contexto de los idiomas
const ctx = useSpeakContext();

// 5 - configuración de los idiomas para obtener la lista de idiomas disponibles en este caso
const config = useSpeakConfig();

// 6 - Función para realizar el cambio de configuración regional
const changeLocale$ = $(async (newLocale: SpeakLocale) => {
await changeLocale(newLocale, ctx);
// 7 - Almacenamos el valor seleccionado con una duración de una semana (mirad bien las constantes)
document.cookie = `locale=${JSON.stringify(newLocale)};max-age=${ONE_WEEK_SECONDS};path=/`;
});

// 8 - Botones para trabajar con los cambios de configuración regional
return (
<div>
<div>{t('app.changeLocale')} ({ctx.locale.lang})</div>
{config.supportedLocales.map(value => (
<button onClick$={async () => await changeLocale$(value)}>
{value.lang}
</button>
))}
</div>
);
});

Dentro de este componente, tenemos una nueva referencia para traducir:

<div>{t('app.changeLocale')}</div>

Para actualizar en los ficheros de traducción (app.json) de los tres idiomas debemos de ejecutar el comando del script que hemos añadido anteriormente mostrando que se han extraído tres claves (antes eran 2):

Actualizamos la clave changeLocale en los tres ficheros app.json de nuestra aplicación

Añadir componente ChangeLocale en el componente Header

Añadimos este componente dentro del componente <Header/> que lo encontramos en src/components/header/header.tsx con el siguiente contenido:

import { component$ } from '@builder.io/qwik';
import { ChangeLocale } from './change-locale';

export default component$(() => {
return (
<header>
<ChangeLocale />
</header>
);
});

Comprobando la aplicación que carga los textos en diferentes idiomas

Teniendo todo esto, vamos a probar nuestra app y ver como se visualizan los textos traducidos.

Ejecutamos:

npm start

Al iniciar la aplicación, tenemos lo siguiente, con el idioma por defecto seleccionado:

Si cambiamos a Español:

Si cambiamos a Euskera:

¿Qué pasaría si ahora refrescamos la aplicación con F5 o si se reinicia?

Tanto de una forma como la otra, se mantiene el idioma que hemos seleccionado al final. Esto se mantiene debido a que estamos almacenando durante una semana esta configuración en las cookies.

Accedemos a las herramientas de desarrollador (1) y accedemos a las cookies (2) y podemos ver que tenemos el valor eu asignado a lang y que expira en una semana (3 <-> 4) comparando con la fecha del ordenador:

Con esto tenemos todo lo necesario para trabajar con los textos i18n en una aplicación de Qwik con opción de cambiar (y almacenar la selección) el idioma a usar durante lo que nosotros establezcamos.

Recordad, para generar más claves, primero las añadimos en el código JSX y cuando las tengamos, ejecutamos el script para extraer esos textos y llevarlos a los ficheros JSON dentro del directorio i18n correspondiente a su idioma. Os animo a que lo hagáis en los textos que he dejado fijo el contenido.

El código de lo trabajado hasta este punto lo tenemos a continuación:

i18n aplicando selección del idioma mediante ruta

Después de haber trabajado con la opción de manejar el idioma seleccionado mediante la selección de estos con un botón, consiguiendo que se guarde su configuración, vamos a trabajar en este caso seleccionando el idioma en base a la ruta que introducimos.

Para trabajar con este punto, nos vamos a orientar basándonos en a referencia que tenemos aquí.

Vamos a comenzar partiendo de la rama donde ya hemos realizado la instalación y las primeras configuraciones de los idiomas soportados y por defecto en src/config/speak-i18n.ts.

Configurar enrutamiento para recibir valores por ruta

Lo primero que vamos a hacer es configurar el apartado para el enrutamiento mediante los conocimientos que hemos adquirido en un artículo anterior.

Nos centramos en el punto donde vamos a obtener todas las posibilidades, que en este caso serían los códigos de lenguaje (es, en, eu) vistos anteriormente.

Dentro del proyecto creamos un nuevo directorio en la raíz de src/routes llamado [...lang] e introducimos el fichero index.tsx que se encuentra en src/routes dentro de este directorio, quedando de la siguiente manera:

Ahora vamos a src/routes/layout.tsx y añadimos el contenido de la función onRequest que usábamos en el modo de configurar anterior implementando lo siguiente:

// 1
export const onRequest: RequestHandler = ({ params, locale }) => {
// 2
const lang = params.lang;

// 3
locale(lang || config.defaultLocale.lang);
};

Y lo que hará paso a paso será lo siguiente:

  • 1 — Obtenemos el valor de los parámetros y en este caso será lang, que lo hemos definido con [...lang] como directorio dinámico a la hora de configurar el enrutamiento. Aparte de ello, obtenemos la función locale que la usamos para asignar la configuración del idioma.
  • 2 — Asignar el valor de params.lang, para añadirlo en la configuración regional del idioma siempre y cuando sea uno de los idiomas soportados (supportedLanguages).
  • 3 — Asignamos el idioma a usar.

Quedando el fichero de la siguiente forma:

import { component$, Slot } from '@builder.io/qwik';
import { loader$, RequestHandler } from '@builder.io/qwik-city';
import { config } from '~/config/speak-i18n';

import Header from '../components/header/header';
export const useServerTimeLoader = loader$(() => {
return {
date: new Date().toISOString(),
};
});
export default component$(() => {
const serverTime = useServerTimeLoader();
return (
<>
....
</>
);
});
export const onRequest: RequestHandler = ({ params, locale }) => {
const lang = params.lang;
locale(lang || config.defaultLocale.lang);
};

Creando el componente Home con los textos

Esto lo hemos visto anteriormente, vamos a añadir lo siguiente creando el componente <Home /> dentro del index.tsx que se encuentra en src/routes/[...lang]/index.tsx:

import { component$, useStylesScoped$ } from '@builder.io/qwik';
import { DocumentHead } from '@builder.io/qwik-city';
import {
$translate as t, // <==== Para ejecutar la traducción de una clave
Speak,
} from 'qwik-speak';

export const Home = component$(() => {
useStylesScoped$(`
span {
background-color: #d4cfcf
}
`)
return (
<>
<h1>{t('app.title')}</h1> (1)
<p>Coge de la carpeta "app" en el idioma seleccionado y la propiedad <b>title</b></p>
<p>Esto hará referencia al fichero que generamos posteriormente en <span>public/i18n/_locale_/app.json</span></p>
<h3>{t('home.intro')}</h3> (2)
<p>Coge de la carpeta "home" en el idioma seleccionado y la propiedad <b>intro</b></p>
<p>Esto hará referencia al fichero que generamos posteriormente en <span>public/i18n/_locale_/home.json</span></p>
</>
);
});
export default component$(() => {
return (
/**
* Añadimos los textos relacionado al componente Home
* (Solo se aplicará en componentes hijos)
*/
<Speak assets={['home']}>
<Home />
</Speak>
);
});
export const head: DocumentHead = {
title: 'Welcome to Qwik',
meta: [
{
name: 'description',
content: 'Qwik site description',
},
],
};

Script para extraer los textos traducidos

No olvidéis de añadir el script.

El script deberá de configurarse de la siguiente forma:

"qwik-speak-extract": "qwik-speak-extract --supportedLangs=es,eu,en"

Una vez configurado, ejecutamos el script para generar los ficheros con todas sus claves.

npm run qwik-speak-extract

Rellenad los textos como hemos hecho anteriormente.

Comprobad QwikSpeakProvider

Tal y como hemos realizado anteriormente, en src/root.tsx debemos de tener añadido el elemento QwikSpeakProvider, de la siguiente forma:

import { component$ } from '@builder.io/qwik';
import { QwikCityProvider, RouterOutlet, ServiceWorkerRegister } from '@builder.io/qwik-city';
import { RouterHead } from './components/router-head/router-head';

import './global.css';
import { QwikSpeakProvider } from 'qwik-speak';
import { config, translationFn } from './config/speak-i18n';
export default component$(() => {
return (
<QwikSpeakProvider config={config} translationFn={translationFn}>
<QwikCityProvider>
...
</QwikCityProvider>
</QwikSpeakProvider>
);
});

Si no lo está de esta manera, deberíais de volver al punto (Implementar Proveedor para aplicar i18n con Qwik Speak) donde se habla más al detalle sobre esto, en los primeros pasos que se han dado al inicio del artículo.

Probando la selección de idioma mediante ruta

En este caso vamos a ir introduciendo las rutas a mano. Sin ningún valor de parámetros o añadimos /en nos aparece seleccionado el idioma Inglés.

Si en vez de en, añadimos es(http://localhost:5173/es),tendremos el Español:

Con eu (http://localhost:5173/eu), tendremos el Euskera:

Si en la ruta añadimos cualquier valor que no sea el código correcto, selecciona el idioma por defecto. Por ejemplo, probad a acceder a esta URL: http://localhost:5173/eeoeoeoee/

Añadir funcionalidad de los botones de selección de idioma

Vamos a añadir la configuración regional sin recargar la página, solo volviendo a representar los componentes que usan traducciones. Vamos a crear el componente <ChangeLocale /> como hemos hecho antes dentro de src/components/header con el nombre change-locale.tsx:

import { component$, useTask$, $ } from "@builder.io/qwik";
import { useLocation, useNavigate } from "@builder.io/qwik-city";
import { changeLocale, SpeakLocale, useSpeakConfig, useSpeakContext, useSpeakLocale, $translate as t } from "qwik-speak";

export const ChangeLocale = component$(() => {
// Para obtener las configuraciones de localización
const loc = useLocation();
// Para navegación
const nav = useNavigate();
// Para trabajar con las configuraciones de la librería
const ctx = useSpeakContext();
const locale = useSpeakLocale();
const config = useSpeakConfig();
// Manejamos la ruta de localización, antes posibles cambios
useTask$(async ({ track }) => {
// Observamos posibles cambios
track(() => loc.params.lang);
// Por cada cambio evalua si es diferente el idioma seleccionado para cambiarlo
const newLocale = config.supportedLocales.find(value => value.lang === loc.params.lang) || config.defaultLocale;
if (newLocale.lang !== locale.lang) {
await changeLocale(newLocale, ctx);
}
});
// para reemplazar la URL del navegador, y así no tener que recargar la página
const localizeUrl$ = $(async (newLocale: SpeakLocale) => {
let pathname = loc.url.pathname;
if (loc.params.lang) {
if (newLocale.lang !== config.defaultLocale.lang) {
pathname = pathname.replace(loc.params.lang, newLocale.lang);
} else {
pathname = pathname.replace(new RegExp(`(/${loc.params.lang}/)|(/${loc.params.lang}$)`), '/');
}
} else if (newLocale.lang !== config.defaultLocale.lang) {
pathname = `/${newLocale.lang}${pathname}`;
}
// No recarga la página, solo cambia la URL en el navegador
nav(pathname);
});
return (
<div>
<div>{t('app.changeLocale')} ({ locale.lang })</div>
{config.supportedLocales.map(value => (
<button onClick$={async () => await localizeUrl$(value)}>
{value.lang}
</button>
))}
</div>
);
});

Recordad, ejecutar el comando para extraer las claves (en este caso app.changeLocale) y añadirle las traducciones correctamente

Lo añadimos en <Header />, como hemos hecho anteriormente:

import { component$, useStylesScoped$ } from '@builder.io/qwik';
import { ChangeLocale } from './change-locale';
import styles from './header.css?inline';

export default component$(() => {
useStylesScoped$(styles);
return (
<header>
<ChangeLocale />
</header>
);
});

Ejecutamos el proyecto de nuevo y probamos, haciendo click en las tres opciones y visualizando como cambia la URL:

Llegados a este punto, os proporciono el código final correspondiente a este apartado:

Conclusión

Llegados a este punto hemos aprendido como aplicar i18n con Qwik de manera básica, para poder dar los primeros pasos.

Ya conocemos las claves para hacer la configuración general y las dos variantes posibles, como extraer textos para añadir las traducciones de manera fácil.

Llevamos aprendido muchísimo, os animo a que repaséis si hiciese falta.

Me gustaría que en los comentarios dejaseis resultados de cosas que vayáis practicando, ya que cuanta más variedad, todo el mundo nos beneficiamos de ello.

Finalizamos el artículo y seguiremos con la continuación de este artículo y en el nos centraremos en la función $translate más al detalle y como trabajar con la gestión de las descripciones de la metadata de nuestras páginas

Todos los artículos publicados del curso los encontraréis en la siguiente lista que iré actualizando semanalmente y estableciendo el orden natural recomendado:

Qwik paso a paso desde 0 al detalle

23 stories

Todo lo que hemos trabajado lo podéis encontrar en el siguiente repositorio con el resultado en código:

Presencia en redes sociales

Podéis encontrarme en las siguientes redes.

--

--

Anartz Mugika Ledo🤗

[{#frontend:[#mobile:{#android, #kotlin, #ionic}}, {#web:{#angular, #qwik, #bootstrap}}],{#backend: [{#graphql, #nestjs,#express, #mongodb, #mysql}]}]