Sitio web multilenguaje con Vuejs y Quasar en modo SSR.

Sitio web multilenguaje con Vuejs y Quasar en modo SSR.

Hola queridos lectores, hoy desde CodigoJS – Desarrollo web en Sevilla vamos a compartir con vosotros la experiencia de como logramos configurar un sitio web multilenguaje y conseguimos establecer el idioma de cada dominio utilizando Quasar Framework en modo SSR.

Ya sabemos que cuando trabajamos con frameworks javascript en sitios que son importantes para el SEO, debemos renderizar el contenido en el servidor para que Google pueda rastrear de forma correcta el sitio.

En este artículo vamos a contar nuestra experiencia sobre como logramos hacer la configuración para que el sitio se renderizada en el idioma correcto, según el dominio por el cual se estaba accediendo en el lado del servidor. Estamos seguros que este no es el único método que existe para hacer esto, pero en Internet existe muy poca documentación sobre el tema.

¿Cuál era el problema ?

Actualmente nos encontramos en proceso de migración de una web multilenguaje con varios dominios:

  • www.midominio.com – En idioma ingles
  • www.midominio.es – En idioma español
  • www.midominio.fr – En idioma francés

Necesitamos que al acceder a www.dominio.com la web se renderice en idioma inglés y así con el resto de dominios, dado que es un proyecto online y muy importante que conserve las posiciones que tiene en el buscador.

El nuevo sitio se está desarrollando con Quasar Framework en el frontend y Django Rest Framework en el backend. En el frontend utilizamos vue-i18n para las traducciones y Vuex para manejar el estado de las variables.

Obtener el idioma según el dominio

La primera tarea era detectar el idioma según el dominio por el cual se accedía, pero en modo SSR, así que el objeto windows de Javascript no funcionaría.

NOTA: Asumimos que tienes conocimientos de vue-i18n. Si aún no sabes como aplicar traducciones a un sitio web con Vue I18n lo puedes ver aquí.

Para resolver este problema creamos un boot de Quasar que se ejecutará solo en el servidor.

$ quasar new b gethost

Ahora editamos el archivo creado en src/boot/gethost.js y pegamos el siguiente código.

export default async ({ store, ssrContext }) => {
  let lang = 'en-us' // Idioma por defecto es EN
  let host = ssrContext.req.headers.host
  const ext = host.split('.')
  if (ext[2] === 'es:8080') {
    lang = 'es'
  } else if (ext[2] === 'fr:8080') {
    lang = 'fr'
  } else {
    lang = 'en-us'
  }
  store.commit('main/setLang', lang)
  1. Aquí estamos creando un boot al que le pasamos como parámetros el store de Vuex y ssrContext, este último parámetro solo está disponible en modo SSR, más adelante veremos como establecemos que el boot se ejecute solo en el servidor.
  2. Declaramos una variable lang con el idioma por defecto. En este caso Inglés.
  3. Declaramos otra variable host, para obtener el dominio por el cual estamos accediendo.
  4. Creamos la lógica para dividir el dominio y obtener la extensión.(recuerda eliminar :8080 en producción)
  5. Por último utilizamos una mutación de Vuex para guardar el valor de la variable lang en una variable llamada language en el State de Vuex:
// main/mutations.js
export function setLang ( state, lang ) {
    state.language = lang //Establecemos el valor de la variable language
}
// main/state.js
export default {
   language: 'en-us', // Idioma por defecto
}

Ahora nos queda establecer la ejecución del boot en modo servidor. Editamos el archivo quasar.conf.js, localizamos la sección boot y añadimos lo siguiente:

boot: [
  { path: 'gethost', client: false },
  …
]

Le estamos diciendo al boot que no se ejecute en modo cliente, solo en el servidor.

IMPORTANTE: El boot gethost debe ser la primera opción. Pensábamos que el orden de los boots no era importante y esto nos llevó casi todo un día investigando.

Configurando VueI18n

Ahora nos vamos a la configuración de vue-i18n que se encuentra en boot src/boot/i18n.js. Lo editamos y reemplazamos el código por el siguiente:

import VueI18n from 'vue-i18n'
import messages from 'src/i18n'

export default async ({ app, Vue, store }) => {
  Vue.use(VueI18n)
  let lang = store.state.main.language
  app.i18n = new VueI18n({
    locale: lang,
    fallbackLocale: 'en-us',
    messages
  })
}
  1. Importamos vue-i18n.
  2. importamos los mensajes de traducción creados por nosotros.
  3. Creamos una función asincronica y obtenemos los parámetros app, Vue, store.
  4. Usamos el plugin VueI18n.
  5. Creamos la variable lang y le asignamos el valor de la variable language del store.
  6. Por último creamos una nueva instancia de VueI18n y le establecemos el idioma obtenido en la variable lang y los mensajes de traducción de nuestra aplicación.

Para hacer las pruebas en local es necesario añadir tres lineas al archivo /etc/hosts con el siguiente contenido:

127.0.0.1 www.midominio.com
127.0.0.1 www.midominio.es
127.0.0.1 www.midominio.fr

Ejecutamos el proyecto en modo SSR:

$ quasar dev -m ssr

Nos vamos al navegador y llamamos la siguiente URL: www.midominio.com:8080 y la web debería salir en ingles.

Listo, nosotros creíamos que lo teníamos todo resuelto y estuvimos a punto de celebrarlo. Todo funcionaba perfecto mientras lo utilizábamos con el idioma por defecto, el Inglés.

Ahora, si llamamos a www.midominio.es:8080 veremos rápidamente como la web carga en idioma español y después se cambia a ingles. Si no te das cuenta porque ocurre muy rápido puedes irte a la consola de linux y escribir:

wget http://www.midominio.es:8080/

Abres el archivo descargado y podrás ver como la página se ha descargado con los mensajes en español. Lo mismo sucede con el dominio .fr.

¿Porqué sucedía esto? Porque la variable language que establecíamos en el boot gethost.js se creaba en el servidor y cuando la web se cargaba en el cliente (navegador) obtienía el valor por defecto: en-us. Es decir no se estaba pasando el valor de la variable desde el servidor al cliente porque en modo SSR no hay acceso al LocalStore.

¿Como resolvimos este problema? En el foro de Quasar había una usuario que tenía un problema similar y que lo había logrado con Vuex Persisted State. Fue cuando nos enteramos que podíamos hacer persistentes los valores del State de Vuex.

Persistir la información de Vuex.

Lo instalamos

$ npm install --save vuex-persistedstate'

Comenzamos a configurar según la documentación y nos encontramos otro problema, Vuex Persisted State tampoco funciona en modo SSR con Quasar.

En el foro de Quasar nos recomendaron como crear un boot que nos permitiera persistir la información a través de las Cookies, le echamos un ojo y nos llevamos la idea.

Lo primero fue crear el boot:

$ quasar new b persist-data

Lo editamos y añadimos el siguiente código:

import { Cookies } from 'quasar'
import createPersistedState from 'vuex-persistedstate'

export default async ({ store, ssrContext }) => {
  const cookies = Cookies.parseSSR(ssrContext)
  const options = { path: '/' }
  createPersistedState({
    storage: {
      getItem: key => { JSON.stringify(cookies.get(key)) },
      setItem: (key, value) => { cookies.set(key, value, options) },
      removeItem: key => cookies.remove(key)
    }
  })(store)
}
  1. Importamos el plugin Cookies de quasar.
  2. Importamos el plugin vuex-persistedstate.
  3. Creamos una función que tome los parámetros store y ssrContext.
  4. Inicializamos el plugin Cookies.
  5. Creamos una constante option para establecer el path de las cookies.
  6. Por ultimo inicializamos el plugin vuex-persistedstate con las opciones especificadas y lo ligamos al store de Vuex.

Ahora debemos activar el boot en el archivo quasar.conf.js de forma tal que quede así:

boot: [
  { path: 'gethost', client: false },
  { path: 'persist-data', client: false },
  'i18n',
],

Este boot solo se ejecutará en el servidor.

Por último debemos añadir el plugin Cookies al archivo quasar.conf.js

// Quasar plugins
plugins: [
  'Meta',
  'Cookies'
]

De esta forma estamos pasando los valores del state de Vuex a través de las Cookies al navegador. Ahora solo nos falta obtener esos valores en el navegador.

Creamos un nuevo boot llamado persist-client:

$ quasar new b persist-client

Lo editamos y añadimos el siguiente código:

import { Cookies } from 'quasar'

export default async ({ store }) => {
  let vuex = Cookies.get('vuex')
  store.commit('main/setLang', vuex.main.language)
}
  1. Importamos el plugin Cookies de quasar.
  2. Creamos una función y le pasamos el store.
  3. Obtenemos el valor de la cookie vuex que es el nombre que vue-persistenddata establece por defecto cuando pasamos los valores del store desde el servidor.
  4. Por último le pasamos a la variable language del state de Vuex el valor pasado desde el servidor en la cookie. Esto hace que cuando se cargue la configuración de vue-i18n en el cliente se inicialize la variable language con el mismo idioma que tenia en el servidor.

Ultimo paso. Lo prometo. Activamos el boot en el archivo quasar.conf.js:

boot: [
  { path: 'gethost', client: false },
  { path: 'persist-data', client: false },
  { path: 'persist-client', server: false },
  'i18n',
],

Este boot se ejecutará solamente en el cliente.

Ahora sí. Después de varios días lo logramos. Corremos el proyecto en modo ssr:

$ quasar dev -m ssr

Espera que el proyecto se compile y en la barra de dirección llama al dominio www.midominio.es:8080 y  verás como los mensajes se establecen en español, prueba con los demás dominios y verás como se muestran los textos en el idioma del dominio.

Si quieres asegurarte y hacer una prueba más ves a la consola de linux y escribe:

$ wget http://www.midominio.es:8080/

y comprueba que la página descargada está en el idioma del dominio.

Conclusiones

Como dije al principio, puede que exista otra solución más fácil, nosotros no la encontramos, creamos la nuestra y la estamos compartiendo con la comunidad de Quasar. Espero que os sea tan útil como a nosotros.

El código del proyecto está disponible en GitHub. Lo puedes clonar y ejecutar en tu computadora para que compruebes todos los pasos.

Agradecimientos

Quiero agradecer a los usuarios del Foro de Quasar en inglés y a mi amigo y compañero de trabajo Daniel Lopez que me ayudaron a encontrar esta solución.

Recuerda suscribirte más abajo a nuestro boletín para que no te pierdas ninguna entrada.

¿Conoces alguna otra forma de trabajar con sitios multilenguajes con SSR en Quasar?

Suscríbete a nuestro boletín y mantente informado de todas nuestras publicaciones

Deja un comentario

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