Angela Sofíá Osorio
Tiempo de lectura 3 minutes
Fecha de publicación
Añadir múltiples idiomas a un proyecto web suele ser sinónimo de instalar librerías monolíticas que terminan inflando el peso total del sitio. Afortunadamente, trabajas con Astro.
Aquí no necesitamos complicarnos la existencia con dependencias kilométricas. Construiremos un sistema de internacionalización puramente basado en TypeScript, combinando diccionarios simples y el enrutamiento nativo del framework.
Arquitectura del proyecto
Antes de tirar líneas de código, visualicemos la estructura. Aislar la lógica de traducción de nuestras vistas es fundamental para no terminar con un desastre inmanejable a medida que el proyecto crezca.
Nuestra arquitectura se verá exactamente así:
src/
├── i18n/
│ ├── ui.ts # Diccionarios y configuración base
│ └── utils.ts # Lógica de enrutamiento y traducción
├── layouts/
│ └── Layout.astro # Plantilla principal del sitio
└── pages/
├── en/
│ └── index.astro # Ruta secundaria (inglés)
└── index.astro # Ruta principal (español por defecto)Plaintext
Paso 1: Configurar la fuente de la verdad (ui.ts)
Necesitamos un lugar seguro y predecible para almacenar nuestros textos de interfaz. Nada de bases de datos complejas para esta capa; un archivo de configuración tipado será nuestro núcleo.
Crea el archivo ui.ts dentro de tu directorio i18n.
// src/i18n/ui.ts
// Definimos los idiomas soportados para uso global
export const languages = {
es: "Español",
en: "English",
};
// Establecemos el idioma de respaldo
export const defaultLang = 'es';
// Nuestro diccionario centralizado
export const ui = {
es: {
title: "Ejercicios Astro",
description: "Explora los primeros 151 Pokémon y su información detallada.",
cta: "Ver Pokémons",
pokemons: "Pokemones",
},
en: {
title: "Astro Exercises",
description: "Explore the first 151 Pokémon and their detailed information.",
cta: "View Pokémons",
pokemons: "Pokemons",
}
} as const; TypeScriptEse as const al final del objeto no es una sugerencia decorativa. Es una directiva estricta que obliga a TypeScript a inferir los valores exactos como literales, salvándote de errores tipográficos catastróficos más adelante en el desarrollo.
Paso 2: La lógica de enrutamiento y traducción (utils.ts)
Astro es rápido, pero no lee mentes. Dependemos de la ruta de la URL visitada para determinar qué idioma procesar en el servidor. Si el visitante navega a /en/, servimos inglés.
Crearemos utils.ts con dos funciones estructurales que manejan todo el peso dinámico.
// src/i18n/utils.ts
import { ui, defaultLang } from "./ui";
export function getLangFromUrl(url: URL) {
const [, lang] = url.pathname.split('/');
if (lang in ui) return lang as keyof typeof ui;
return defaultLang;
}
export function useTranslations(lang: keyof typeof ui) {
return function t(key: keyof typeof ui[typeof defaultLang]) {
return ui[lang][key] || ui[defaultLang][key];
}
}TypeScriptAnatomía de getLangFromUrl
Esta función intercepta la URL actual del usuario y devuelve el código de idioma validado.
Desestructuración: Al ejecutar split('/') sobre un string como /en/blog, obtenemos ['', 'en', 'blog']. La sintaxis [, lang] ignora inteligentemente el primer elemento vacío y captura directamente nuestro código de idioma.
Validación: El condicional if (lang in ui) asegura que el usuario no esté manipulando la URL con idiomas inexistentes. Si el idioma detectado figura en nuestros diccionarios, lo validamos.
Aserción estricta: Al usar as keyof typeof ui, le garantizamos al compilador que la salida no es un string cualquiera, sino estrictamente "es" o "en". Si la validación falla, devolvemos silenciosamente defaultLang.
Anatomía de useTranslations
Esta es una función factoría clásica. Le inyectas el idioma actual una sola vez y te devuelve un asistente listo para imprimir textos en tus vistas.
Seguridad de tipos: Observa el parámetro key: keyof typeof ui[typeof defaultLang]. Esta regla bloquea la compilación si intentas traducir una clave que no existe en tu idioma base. Disfruta del autocompletado en tu editor.
Sistema de rescate: La línea ui[lang][key] || ui[defaultLang][key] es un seguro de vida. Si olvidaste traducir una frase específica al inglés, la interfaz no colapsará; mostrará la versión en el idioma por defecto.
Paso 3: Inyección de dependencias en componentes
Con el motor listo, conectemos los cables en nuestras vistas. Abre tu página base index.astro e importa las herramientas recién fabricadas.
Fragmento de código
---
// src/pages/index.astro
import Layout from "@layout/Layout.astro";
import { getLangFromUrl, useTranslations } from "src/i18n/utils";
// 1. Extraemos el idioma dinámicamente de la URL actual de Astro
const lang = getLangFromUrl(Astro.url);
// 2. Inicializamos nuestro traductor para la vista actual
const t = useTranslations(lang);
---
<Layout {lang} title={t('title')} description={t('description')}>
<main class="text-center mt-20">
<h1 class="text-6xl font-bold text-blue-600">
{t('title')}
</h1>
<p class="text-xl text-gray-500 mt-4">
{t('description')}
</p>
<a href={`/${lang === defaultLang ? '' : lang + '/'}pokemons`} class="bg-blue-600 text-white px-6 py-3 mt-8 inline-block rounded-full">
{t('cta')}
</a>
</main>
</Layout>AstroEl error más común de quienes migran desde HTML estático es olvidar la sintaxis de evaluación. Si escribes un texto plano como <h1>t('title')</h1>, el navegador mostrará literalmente eso. Siempre envuelve la función en llaves {}.
Paso 4: Escalabilidad a rutas secundarias
Para soportar la versión en inglés, el proceso carece de fricción técnica. Replicas tu estructura de archivos dentro de una carpeta /en/.
El código de tu componente queda exactamente igual que el anterior.
Fragmento de código
---
// src/pages/en/index.astro
import Layout from "@layout/Layout.astro";
import { getLangFromUrl, useTranslations } from "src/i18n/utils";
// Aquí getLangFromUrl leerá el '/en/' de la ruta sin que toques nada
const lang = getLangFromUrl(Astro.url);
const t = useTranslations(lang);
---
<Layout {lang} title={t('title')} description={t('description')}>
<main class="text-center mt-20">
<h1 class="text-6xl font-bold text-blue-600">{t('title')}</h1>
<p class="text-xl text-gray-500 mt-4">{t('description')}</p>
</main>
</Layout>AstroAstro compilará ambas rutas. La utilidad getLangFromUrl hará el trabajo pesado evaluando el contexto de la ruta generada, asignando automáticamente el diccionario inglés durante el proceso de build o en la renderización del servidor.
Contents