Tutorial: useLocales y Traducciones
El proyecto tiene dos sistemas de i18n que coexisten:
| Sistema | Archivo | Uso |
|---|---|---|
vue-i18n (useI18n) | i18n/es.json, en.json, pt-BR.json | Strings cortas: botones, labels, mensajes de error |
| useLocales | app/locales/inspiria/{lang}/{page}.json | Contenido de páginas completas: títulos, secciones, textos largos |
useLocales — Carga de JSONs por página
Section titled “useLocales — Carga de JSONs por página”Cómo funciona internamente
Section titled “Cómo funciona internamente”useLocales<T>('webinars') 1. Detecta idioma actual (useI18n().locale ó languageStore) 2. import(`~/locales/inspiria/${lang}/webinars.json`) 3. import(`~/locales/inspiria/es/webinars.json`) ← siempre carga fallback 4. mergeWithFallback(data, fallbackData) ← merge profundo 5. return { data: T }Uso en componentes
Section titled “Uso en componentes”<script setup lang="ts">interface WebinarsPage { title: string subtitle: string filters: { label: string; value: string }[]}
const { data: pageData } = await useLocales<WebinarsPage>('webinars')</script>
<template> <h1>{{ pageData.title }}</h1> <p>{{ pageData.subtitle }}</p></template>Merge profundo (la magia del fallback)
Section titled “Merge profundo (la magia del fallback)”El composable useLocales implementa mergeWithFallback() que resuelve traducciones parciales:
Ejemplo práctico
Section titled “Ejemplo práctico”// locales/inspiria/es/webinars.json (fallback - completo){ "title": "Webinars", "subtitle": "Formación en directo", "filters": [ { "id": 1, "label": "Todos" }, { "id": 2, "label": "Próximos" }, { "id": 3, "label": "Grabados" } ]}// locales/inspiria/en/webinars.json (parcial - solo title traducido){ "title": "Webinars", "subtitle": "", "filters": [ { "id": 1, "label": "All" }, { "id": 2, "label": "" } ]}Resultado del merge (lo que recibe el componente):
{ "title": "Webinars", // ← inglés (tiene valor) "subtitle": "Formación en directo", // ← español (inglés está vacío) "filters": [ { "id": 1, "label": "All" }, // ← inglés { "id": 2, "label": "Próximos" }, // ← español (inglés vacío) { "id": 3, "label": "Grabados" } // ← español (no existe en inglés) ]}Reglas del merge
Section titled “Reglas del merge”| Caso | Resultado |
|---|---|
| Campo con valor en ambos | Usa el idioma primario |
| Campo vacío/null en primario | Usa el fallback (español) |
Array con objetos que tienen id | Merge por ID (no por posición) |
| Array sin IDs | Merge por posición (índice) |
| Objeto falta en primario | Usa el del fallback completo |
| Archivo entero falta en primario | Usa fallback completo |
Cuándo usar useLocales vs useI18n
Section titled “Cuándo usar useLocales vs useI18n”| Necesitas… | Usa |
|---|---|
| Label de botón (“Comprar”, “Cancel”) | useI18n() → t('common.buy') |
| Mensaje de error genérico | useI18n() → t('errors.network') |
| Contenido completo de una página | useLocales<T>('pageName') |
| Datos que podrían no estar traducidos | useLocales (tiene fallback) |
| Texto en un componente reutilizable | useI18n() |
Agregar un nuevo archivo de traducciones
Section titled “Agregar un nuevo archivo de traducciones”-
Crea el JSON en español (es el fallback y debe estar completo):
app/locales/inspiria/es/mi-pagina.json -
Crea versiones para otros idiomas (pueden estar parcialmente traducidas):
app/locales/inspiria/en/mi-pagina.jsonapp/locales/inspiria/pt-BR/mi-pagina.json -
Usa en el componente:
const { data } = await useLocales<MiPaginaData>('mi-pagina')