Sitio web multilenguaje con Vuejs y Quasar en modo SSR.

Dom 24 Nov 2019 | Por: Roylan Suarez

Hola queridos lectores, hoy desde CódigoJS - Desarrollo web en Sevilla vamos a compartir con vosotros la experiencia de cómo 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 cómo logramos hacer la configuración para que el sitio se renderice 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.

Tabla de contenidos


¿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 inglés
  • 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 es 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 cómo 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 cómo 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 inglés. Listo, nosotros creíamos que lo teníamos todo resuelto y estuvimos a punto de celebrarlo. Todo funcionaba perfectamente 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 inglés. 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. ¿Por qué 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. ¿Cómo 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 inicialice 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 cómo los mensajes se establecen en español, prueba con los demás dominios y verás cómo se muestran los textos en el idioma del dominio. Si quieres asegurarte y hacer una prueba más ve 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?



Déjanos un comentario

Artículos relacionados