Construyendo una app multi-lenguaje con Vue JS. 🌐
Tags: vue, typescript, tutorial, webdev
Published: 2024-01-13 15:57:55

Hoy les presentare un forma fácil de crear aplicaciones que soporten varios idiomas, usando Vue JS

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gosnzodfu6yu4r8iwmsk.png)

## Tecnologías a utilizar.

- Vue **3.3.11**
- @tolgee/vue **5.19.0**
- Vite JS **5.0.8**
- TypeScript **5.2.2**
- Tailwind CSS **3.4.0**

## Creando el proyecto

Para crear el proyecto con Vue, usaremos **vite js**
Al proyecto le colocaremos el nombre de: **`multi-lang-app`** (opcional, tu le puedes poner el nombre que gustes).

```bash
npm create vite@latest
```

Después de colocar el nombre, seleccionamos la opción de Vue y luego la opción de TypeScript (aunque no es necesario para esta ocasión)
Creamos el proyecto con Vite JS y seleccionamos React con TypeScript.  
 Luego ejecutamos el siguiente comando para navegar al directorio que se acaba de crear.

```bash
cd multi-lang-app
```

Luego instalamos las dependencias.

```bash
npm install
```

Después abrimos el proyecto en un editor de código (en mi caso VS code).

```bash
code .
```

### Instalando Tailwind

Solamente seguimos los pasos que indica la [documentación de Vite](https://tailwindcss.com/docs/guides/vite#vue)
Primero instalamos las dependencias.

```bash
npm install -D tailwindcss postcss autoprefixer
```

Después ejecutamos este comando para crear los archivos de configuración de tailwind.

```bash
npx tailwindcss init -p
```

Luego en nuestro archivo `tailwind.config.js` agregamos la siguiente configuración

```tsx
/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./index.html",
    "./src/**/*.{vue,js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
```

Y en nuestro archivo `style.css` agregamos:

```css
@tailwind base;
@tailwind components;
@tailwind utilities;
```

Y eso es suficiente.

## 🈵 Primeros pasos.

Primero vamos a crear una TO-DO list sencillo.

Creamos un nuevo archivo `src/components/MainContent.vue`
En el cual definiremos dos nuevos estados:

- **task**: Es la descripción de la tarea.
- **collectionTasks**: un arreglo de tareas (y una tarea se compone de objeto que contiene la descripción y si esta completada).

```vue
<script setup lang="ts">
import { ref } from 'vue'

interface Task {
  task: string
  isCompleted: boolean
}

const task = ref('')
const collectionTasks = ref<Task[]>([])
</script>

<template>
</template>
```

Seguido de eso, creamos dos simples funciones.

- **createTask**: agrega una nueva tarea a la lista y reinicia el input donde ingresas dicha tarea.
- **handleToggleComplete**: marca la tarea como completada

```vue
<script setup lang="ts">

// ...

const createTask = () => {

  if(task.value.trim().length === 0) return

  collectionTasks.value.push({
    task: task.value,
    isCompleted: false
  })

  task.value = ''
}

const handleToggleComplete = (index:number) => {
  const taskSelected = collectionTasks.value.find( (_,i) => i === index )
  if(taskSelected) {
    taskSelected.isCompleted = !taskSelected?.isCompleted
  }
}
</script>

<template>
</template>
```

Ahora seguimos con el HTML.

> Nota: En este punto se supone que deberías tener nociones de como usar Vue, asi que no se te debe hacer raro dichas características que usa Vue, como el @submit.prevent, @click, v-model, v-show, v-for, v-bind (usando su forma corta ":") o la sintaxis de "Mustache" para la interpolación de texto.

```vue
<script setup lang="ts">
// ...
</script>

<template>
  <div class="flex items-center flex-col gap-2 p-5">

    <h1 class="text-6xl font-black mt-28 mb-10 text-wrap">
      THIS IS THE MAIN TITLE
    </h1>

    <main class="max-w-2xl ">
      <form
        @submit.prevent="createTask"
        class="flex items-center gap-5"
      >
        <input
          type="text"
          placeholder="TASK PLACEHOLDER"
          autofocus
          v-model="task"
          class="p-3 px-4 text-xl rounded-lg flex-1 min-w-[420px]"
        />
        <button class="bg-blue-500 p-3 px-4 rounded-md hover:bg-blue-600">
          ADD NEW TASK
        </button>
      </form>

      <section
        class="bg-[#272932] p-5 rounded-md my-10"
        v-show="collectionTasks.length !== 0"
      >
        <h2
          class="mt-2 mb-5 text-3xl font-bold text-white/50 border-b pb-3 border-white/50"
        >
          SECTION LIST TASK TITLE
        </h2>

        <div
          v-for="(item, index) in collectionTasks"
          :key="index"
          class="flex items-center gap-5"
        >
          <span
            :class="['p-2 text-xl cursor-pointer', item.isCompleted && 'line-through']"
            @click="handleToggleComplete(index)"
          >
            👉 {{ item.task }}</span
          >

          <span
            v-show="item.isCompleted"
            class="p-2 rounded-lg bg-green-500/20 text-green-300 border-green-500 border text-xs"
          >
            COMPLETED
          </span>
        </div>
      </section>
    </main>
  </div>
</template>
```

Nota que en el código de arriba puse ciertas palabras en mayúsculas, esto es porque es ahí donde queremos aplicar el cambio de idioma.
Y con esto terminamos nuestra mini aplicación, solo falta mostrarla en `App.vue`

Dentro del archivo `src/App.vue`, borramos todo y colocamos lo siguiente:

```vue
<script setup lang="ts">
import MainContent from './components/MainContent.vue';
</script>

<template>
  <MainContent />
</template>
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/80r9r163au2j961n6azj.png)

Los estilos pueden cambiar, **checa el repositorio al final de este post**

## Configurando las traducciones para la app.

Antes de seguir con el código, vamos a configurar las traducciones, para ello, usaremos Tolgee.

> "Tolgee es una plataforma de localización que te permite traducir tu aplicación a cualquier idioma sin modificar tu código. Está diseñada para su uso con aplicaciones web, pero puede utilizarse también con apps para móviles y aplicaciones de escritorio."

Puedes saber más hacer de esta plataforma en su web [Tolgee](https://tolgee.io/)

1. Primero creamos una cuenta en [https://app.tolgee.io/sign_up](https://app.tolgee.io/sign_up).

Una vez creada la cuenta te llevara a tu proyectos, el cual no tendrá ninguno creado aun.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/25m8rzhc0qe8awpjmwwd.png)

2. Agregar un proyecto nuevo.

- Agregas un nombre al proyecto (ejemplo: demo-vue)
- Agregas los idiomas que quieras configurar.
- Seleccionas un lenguaje base.
- Finalmente das click en el botón "Save"

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4whakz1ndyu5dpqfabi0.png)

Una vez creado el proyecto, te llevara al dashboard de dicho proyecto.
Ahora dirígete a la parte de "Translations" que debe estar en el menú lateral izquierdo.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hui65vajmn0pe8tuobev.png)

3. Agregar traducciones.
   Una vez en la sección de "Translations", presiona el botón de "+" para agregar una nueva traducción. Te pedirá agregar los siguiente:

- key: es la palabra clave que identifica la traducción y **debe ser única**. Normalmente siguen una convección que se usa "\_" por ejemplo: "about_page_title", mira más sobre eso en la [documentación](https://tolgee.io/platform/translation_keys/keys#naming-conventions)
- namespaces (opcional): es una llave que ayuda a separar las traducciones en multiples archivos, es util cuando tienes una app grande que contenga muchas traducciones (más de 1000 keys)
- tags: que pueden ser usadas para agrupar keys.
- languages text(s): El texto que debería ser desplegado en cada idioma.
  - Puede seleccionar los idiomas que desea mostrar aquí seleccionándolos en el menú desplegable de la parte superior. Si no especifica el texto para un idioma, la tecla se marcará como "Untranslated" para ese idioma.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tv3df9lcqrqngewo2l4u.png)

> Revisa bien que se muestren los lenguajes en los que quieras agregar la traducción. **En la parte superior derecha puedes seleccionar que lenguajes quieres traducir**.

Nota que cuando agregas el texto en en campo del idioma que seleccionaste como "base language" y luego te colocas en otro campo para agregar otro idioma, se te despliega un panel con dos secciones

- **TRANSLATION MEMORY**: busca textos similares en tu proyecto y te los sugiere. Esto te ayuda a reutilizar traducciones y ahorrar tiempo. mira más en la sección [translation memory](https://tolgee.io/platform/translation_process/translation_memory)
- **MACHINE TRANSLATION**: es una forma de traducir contenidos utilizando servicios externos y obtener una traducción aproximada del contenido.

Solamente selecciona una opción, si no conoces la traducción exacta.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qmba9snfe1vtaf1ebvcr.png)

Finalmente presiona "Save" para agregar la traducción

Para este proyecto agregare 3 idiomas "español mexicano", "inglés" y "francés".

## Agregando Tolgee a nuestro proyecto.

Para agregar Tolgee a nuestra proyecto es muy sencillo, ejecutamos:

```bash
npm install @tolgee/vue
```

Ahora vamos a crear un nuevo archivo `src/lib/tolgee.ts` y agregaremos la siguiente configuración.

Primero vamos a llamar la función Tolgee.

```ts
import { Tolgee } from '@tolgee/vue';

export const tolgee = Tolgee()
```

Después vamos a configurar algunos plugins para Tolgee.

- [DevTools](https://tolgee.io/js-sdk/api/web_package/plugins#devtools): combinación de varios plugins que nos ayudaran en modo desarrollo, como por ejemplo comunicar nuestra app con la plataforma de Tolgee. Este plugin es omitido en producción por defecto.
- [FormatSimple](https://tolgee.io/js-sdk/api/core_package/format-simple): Es un formateador que nos habilita poder pasar variables dentro las traducciones.

Para definir un plugin usamos el método **use()**

```ts
import { Tolgee, DevTools, FormatSimple, BackendFetch } from '@tolgee/vue';

export const tolgee = Tolgee()
  .use(DevTools())
  .use(FormatSimple())
```

Luego llamamos el método init, pasándole un objeto con las siguientes propiedades.

- **language**: el lenguaje inicial de tu app.
- **apiUrl**: una URL que te proporciona tolgee, para conectarse a la plataforma.
- **apiKey**: una KEY que te proporciona tolgee, para conectarse a la plataforma.

Esta función init() tiene mas [propiedades](https://tolgee.io/js-sdk/api/core_package/options), pero las que mencionamos son suficientes por el momento.

```ts
import { Tolgee, DevTools, FormatSimple, BackendFetch } from '@tolgee/vue';

export const tolgee = Tolgee()
  .use(DevTools())
  .use(FormatSimple())
  .init({
    language: 'es-MX',
    apiUrl: '',
    apiKey: ''
  });
```

### Obteniendo la API KEY.

Volvemos a la plataforma Tolgee, buscamos en el menú lateral la sección de **integrate**.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jktk2kcp5j4q5nrh515c.png)

Te aparecerá una serie de pasos:

1. Escoger la tecnología que vas a usar para tu proyecto.
2. Después te pedirá seleccionar una **API KEY**. Si es que ya tienes una creada, si no, solo selecciona la opción de "**Create new +**". Ahi mismo puedes configurar el nombre, expiración y el alcance.
3. Te mostrara un breve Quick start. En esa parte te mostrara tanto la variable **API_URL** y **API_KEY**.

Solo copia las variables en un archivo **.env** dentro de tu proyecto. Y finalmente hacemos referencia a dichas variables de entorno.

```ts
import { Tolgee, DevTools, FormatSimple, BackendFetch } from '@tolgee/vue';

export const tolgee = Tolgee()
  .use(DevTools())
  .use(FormatSimple())
  .init({
    language: 'es-MX',
    apiUrl: import.meta.env.VITE_APP_TOLGEE_API_URL,
    apiKey: import.meta.env.VITE_APP_TOLGEE_API_KEY
  });
```

### Terminando la configuración de Tolgee

Ahora en nuestro archivo `src/main.ts` usamos el plugin de tolgee que hemos creado en la app de Vue de la siguiente manera:

```tsx
import { createApp } from 'vue';
import './style.css';
import App from './App.vue';

import { VueTolgee } from '@tolgee/vue';
import { tolgee } from './lib/tolgee';

const app = createApp(App);

app.use(VueTolgee, { tolgee });

app.mount('#app');
```

### Agregando el proveedor

Ahora necesitamos agregar un proveedor en `src/App.vue` y envolvemos nuestro componente **<MainContent/>**

```vue
<script setup lang="ts">
import { TolgeeProvider } from '@tolgee/vue';
import MainContent from './components/MainContent.vue';
</script>

<template>
  <TolgeeProvider>
    <MainContent />
  </TolgeeProvider>
</template>
```

Ahora dentro del proveedor necesitamos especificar un componente fallback que se mostrará mientras se carga las traducciones del idioma que esta por defecto.

> Nota que cree un componente por separado llamado Loading en `src/components/Loading.vue`

```vue
<script setup lang="ts">
import { TolgeeProvider } from '@tolgee/vue';
import MainContent from './components/MainContent.vue';
import Loading from './components/Loading.vue';
</script>

<template>
  <TolgeeProvider>

    <template v-slot:fallback>
      <Loading />
    </template>

    <MainContent />
  </TolgeeProvider>
</template>
```

Y ahora ya estamos listos para traducir.

## Traduciendo los textos

Una vez terminada la configuración, tenemos acceso a varias formas de traducir los textos, en este caso usare la función global [**$t**](https://tolgee.io/js-sdk/integrations/vue/translating#global-t-function), porque no necesito importar nada gracias a la configuración que hicimos.
También existen otras formas, como usar un [componente T ](https://tolgee.io/js-sdk/integrations/react/api#t-component)o un [composable useTranslate](https://tolgee.io/js-sdk/integrations/react/api#hook-usetranslate)

Devuelta en el archivo `src/components/MainContent.vue`, usamos la función global **$t** de esta manera:

- Dentro de la función, le mandamos la key que definimos en la plataforma de Tolgee.

```vue
<h1 class="text-6xl font-black mt-28 mb-10 text-wrap">
  {{ $t('title_page') }}
</h1>
```

Y de esta manera vemos como se muestra correctamente el titulo en el lenguaje por defecto que configuramos.

Ahora vamos a remplazar los demás textos de los elementos:

- el **placeholder** del input

```vue
<template>
<!-- all the rest ... -->
  <input
    type="text"
    :placeholder="$t('placeholder_task_input')"
    autofocus
    v-model="task"
    class="p-3 px-4 text-xl rounded-lg flex-1 min-w-[420px]"
  />
  <!-- all the rest ... -->
</template>
```

- el label del botón

```vue
<button class="bg-blue-500 p-3 px-4 rounded-md hover:bg-blue-600">
  {{ $t('button_add_task') }}
</button>
```

el titulo de la sección del listado

```vue
<h2
  class="mt-2 mb-5 text-3xl font-bold text-white/50 border-b pb-3 border-white/50"
>
  {{ $t('title_section_list_tasks') }}
</h2>
```

Y asi sucesivamente lo que te gustaría traducir.

## Cambiando el idioma.

Ya podemos mostrar los textos, ahora necesitamos seleccionar otro idioma para mostrar las otras traducciones.
Para ello crearemos un componente `src/components/SelectLang.vue`

Primero crearemos la funcionalidad.
Estaremos usando un composable de tolgee, [**useTolgee**](https://tolgee.io/js-sdk/integrations/react/api#hook-usetolgee).
Dicho composable nos retorna una instancia de Tolgee permitiendo suscribirnos a diferentes [eventos](https://tolgee.io/js-sdk/api/core_package/events)

> Nota como especificamos "language", lo que significa que solamente queremos escuchar el evento cuando un idioma cambia.
> También nota que estamos pasándole un arreglo a useTolgee, debido a que podemos escuchar varios eventos.

Después creamos una función que recibirá un evento () y vamos a ejecutar la función **changeLanguage** de tolgee, mandando el valor del input.
Básicamente usaremos un elemento **select** y cada vez que se haga un cambio se ejecutara el **changeLanguage**, pasando el valor que hemos seleccionado.

```vue
<script setup lang="ts">
import { useTolgee } from '@tolgee/vue';

const tolgee = useTolgee(['language']);

const changeLanguage = (e: Event) => {
  tolgee.value.changeLanguage((e.target as HTMLSelectElement).value);
};
</script>
```

Finalmente solo implementamos el template.

- Nota que en el atributo value del select, estamos llamando la función getLanguage() de tolgee, el cual nos provee el idioma actual que hemos configurado (**NO** es el idioma de tu navegador), que en es 'es-MX'.

- Nota también que en en evento @change del select estamos llamando la función que creamos **"changeLanguage"**.
- Por último nota que en cada elemento option debe llevar un atributo **value** que hace referencia al lenguaje, justo como esta en la plataforma de Tolgee.

```vue
<script setup lang="ts">
// ---
</script>

<template>
  <select
    :value="tolgee.getLanguage()"
    @change="changeLanguage"
    class="p-1 rounded-md text-lg font-semibold"
  >
    <option value="es-MX">🇲🇽 Spanish</option>
    <option value="en">🇺🇲 English</option>
    <option value="fr">🇫🇷 French</option>
  </select>
</template>
```

Listo, tenemos nuestro componente para cambiar de idioma nuestra app.

Ahora vamos a crear un pequeño NavBar para colocarlo.

Dentro de `src/components/NavBar.vue` y colocamos lo siguiente.

```vue
<script setup lang="ts">
import SelectLang from './SelectLang.vue';
</script>

<template>
  <nav
    class="flex justify-between items-center gap-5 bg-black/50 backdrop-blur-md fixed top-0 left-0 w-full p-7 px-10"
  >
    <div class="flex gap-5 items-center">
      <span>{{ $t('label_select_idiom') }}</span>

      <SelectLang />

    </div>
  </nav>
</template>
```

> No olvides colocar el componente _Navbar.vue_ en `src/components/MainContent.vue`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rodam6o6uaegtcwrivu4.png)

## Usando variables en las traducciones

Para usar variables tienes hacer configuraciones en la plataforma de Tolgee.

> Tolgee siguen el formato de mensaje [ICU](https://tolgee.io/platform/translation_process/icu_message_format) aprende más de este formato para aprender más reglas, ya que aquí solo te voy a mostrar la más básica.

Supongamos que quieres crear algo como esto:

"Hola, 'VAR_NAME' es mi nombre"

Siendo VAR_NAME la variable que quieres mostrar y puede ser cualquier nombre.

Bueno cuando estés creando las traducciones en Tolgee, y quieras agregar una variable, lo harías de esta forma en cada traducción:

- Se encierra la variable entre estos símbolos '{}'.
- Nota que dentro de los corchetes esta el nombre de la variable "name". El nombre que le coloques debe ser el mismo en cada traducción y el el mismo que usaremos en nuestra app para hacer referencia a esa variable.

```txt
Hola, {name} es mi nombre.
```

Ahora vamos al `src/components/NavBar.vue`
Vamos a crear una simple variable.

```vue
<script setup lang="ts">
// ....
const userName = 'Barry Allen'
</script>

<template>
  <!-- .... -->
</template>
```

Y ahora para colocar una variable lo haremos de esta manera:

```vue
<span>{{ $t('title_navbar',{ name: userName })  }}</span>
```

Seguimos mandando la key como primer parámetro, después un objeto con el nombre de la propiedad exactamente como la definiste en la plataforma de Tolgee, que en mi caso le puse '{name}'.

Y asi podemos usar variables en nuestras traducciones.

```vue
<script setup lang="ts">
import SelectLang from './SelectLang.vue';

const name = 'Barry Allen'
</script>

<template>
  <nav
    class="flex justify-between items-center gap-5 bg-black/50 backdrop-blur-md fixed top-0 left-0 w-full p-7 px-10"
  >
    <span>{{ $t('title_navbar',{ name })  }}</span>

    <div class="flex gap-5 items-center">
      <span>{{ $t('label_select_idiom') }}</span
      ><SelectLang />
    </div>
  </nav>
</template>
```

## Preparando para producción

Cuando vayas a desplegar tu app a producción, **NO vas a incluir las variables de entorno**. Esto es porque el SDK y las variables de entorno solo deben ser usadas en el desarrollo y no en producción.

Para producción debes usar los [datos exportados de la plataforma de Tolgee](https://tolgee.io/platform/projects_and_organizations/export).

Tienes estas opciones:

1. Exportar los datos manualmente de la plataforma Tolgee.

En la plataforma de Tolgee, en el menu lateral hay una opción "Export", que te lleva a configurar que lenguajes quieres exportar y otras configuraciones.
Una vez que configuras y exportas las traducciones en archivos como por ejemplo archivos formato JSON, los colocas en la carpeta `public/i18n`.

Y haces unas configuraciones en el archivo `src/lib/tolgee.ts` algo asi.

```ts
export const tolgee = Tolgee()
  // ...
  .init({
    // ...
    staticData: {
      'es-MX': () => import('./i18n/es-MX.json'),
      fr: () => import('./i18n/fr.json'),
      en: () => import('./i18n/en.json'),
    },
  });
```

2. Usar plugin **BackendFetch**

Existe otra forma más fácil si es que no quieres descargar esos archivos. Y es usando el plugin **BackendFetch**

En la plataforma de Tolgee, ve al menú lateral y busca la sección de "Developer settings".
Una vez ahí, en la parte de ["CONTENT DELIVERY"](https://tolgee.io/platform/projects_and_organizations/content_delivery) le darás al botón "+ CONTENT DELIVERY". Básicamente es para desplegar tus archivos de traducción en un almacenamiento en la nube y puedas usarlos en tu app.

Al presionar el botón te pedirá un par de configuraciones como el nombre, que lenguajes vas a desplegar, en que formato, entre otros.
Una vez que terminas de configurar eso, y presionas "SAVE", te creara una CDN la cual usaras en tu app.

Mediante el plugin de BackendFetch, le pasamos un objeto con la propiedad prefix y colocamos la CDN que creamos.

```ts
export const tolgee = Tolgee()
// ...
.use(
    BackendFetch({
      prefix: 'https://cdn.tolg.ee/19c2f74a3835f11cab85f',
    })
)
.init({
  // ....
});
```

Lo mejor es que, con el BackendFetch y "Content delivery", cuando actualices tus traducciones en Tolgee, estos se reflejaran en tu app.

Ahora si puedes desplegar tu app.

### 🈵 Demostración simple.

[https://vue-tolgee-multi-lang.netlify.app/](https://vue-tolgee-multi-lang.netlify.app/)

### 🈵 Código fuente.

[https://github.com/Franklin361/vue-tolgee](https://github.com/Franklin361/vue-tolgee)