Etiquetas Hreflang en Next.js: Cómo Distribuimos 30 Idiomas en 118 Páginas
Comparte un número que me mantuvo despierto por la noche: 3,540. Eso es 30 idiomas multiplicados por 118 páginas. Si hubiéramos lanzado todo eso simultáneamente en Deluxe Astrology, Google habría indexado miles de páginas delgadas, sin traducir o con basura de máquina. Nuestros rankings se habrían desplomado. En su lugar, construimos un sistema de compuerta de traducción de dos niveles que implementa idiomas progresivamente, utiliza Claude Haiku para traducción por lotes a aproximadamente $22 por idioma, y controla la calidad con puntuación de Winston AI. Todo funciona en Next.js con middleware de next-intl, URLs canónicas conscientes de locale, y etiquetas hreflang tanto en el encabezado HTML como en los sitemaps XML. Este es el desglose completo de cómo lo hicimos -- cada configuración de middleware, cada entrada de sitemap, cada cálculo de costo.
Tabla de Contenidos
- Por Qué Las Etiquetas Hreflang Todavía Importan en 2025
- El Problema de las 3,540 Páginas
- Sistema de Compuerta de Traducción de Dos Niveles
- Configuración de Middleware Next-intl
- Implementación de Hreflang en el Encabezado HTML
- Generación de Sitemap con Entradas Hreflang
- Pipeline de Traducción: Claude Haiku + Winston AI
- Comparación de Costos: Nuestro Enfoque vs Alternativas
- Marcado Schema Consciente de Locale
- Errores Comunes Que Hundirán Tus Rankings
- Preguntas Frecuentes

Por Qué Las Etiquetas Hreflang Todavía Importan en 2025
La detección de idioma de Google ha mejorado. Les doy eso. Pero "mejor" no significa "resuelto". Si estás ejecutando variantes regionales -- piensa en pt-BR vs pt-PT, o zh-CN vs zh-TW -- Google todavía necesita señales explícitas. Sin hreflang, verás tus páginas en portugués de Brasil canibalizando tu contenido dirigido a Portugal, y viceversa.
Aquí es lo que dicen los datos:
- Más del 60% de sitios web multilingües tienen errores de configuración de hreflang (fuente: estudios de Ahrefs sobre auditorías de SEO internacional)
- La implementación adecuada de hreflang puede aumentar las tasas de clics en 20-30% en mercados específicos dentro de 4-6 semanas
- Los sitios sin hreflang experimentan rotaciones de ranking impredecibles entre versiones de idioma, haciendo que el seguimiento de rendimiento sea casi imposible
Para Deluxe Astrology, estamos dirigiéndonos a 30 idiomas con contenido distinto. No variantes regionales -- idiomas reales diferentes. Eso son 30 audiencias diferentes que necesitan encontrar la versión correcta en los resultados de búsqueda. Hreflang no es opcional aquí. Es la base.
Lo que la mayoría de guías pierden: necesitas hreflang en tanto el <head> HTML como tu sitemap XML. No uno u otro. Ambos. Google ha confirmado que procesan hreflang de múltiples fuentes, y la redundancia aquí no es desperdicio -- es seguros.
El Problema de las 3,540 Páginas
Déjame caminar a través de la matemática que moldeó toda nuestra arquitectura.
Deluxe Astrology tiene:
- 118 páginas (páginas de contenido principal)
- 41 espacios de nombres de traducción (agrupaciones lógicas de cadenas traducibles)
- 39 rutas API conscientes de locale
- 30 idiomas objetivo
30 × 118 = 3,540 variantes de página total.
Si lanzáramos todas las 3,540 páginas el primer día, esto es lo que sucedería:
- La mayoría de páginas contendría texto alternativo en inglés con una ruta de URL en otro idioma. Google lo ve como contenido delgado/duplicado.
- Googlebot quemaría presupuesto de rastreo indexando miles de páginas de baja calidad.
- La señal de calidad general del sitio se desplomaría, arrastrando incluso las buenas páginas en inglés.
- Los usuarios que lleguen a páginas sin traducir saltarían inmediatamente.
Esto no es teórico. He visto suceder esto en sitios de clientes que conectaron Weglot o herramientas similares y activaron el interruptor para 20 idiomas durante la noche. El tráfico bajó, no subió.
La solución: no lances todos los idiomas a la vez. Controlarlos.
Sistema de Compuerta de Traducción de Dos Niveles
Dividimos nuestros 30 idiomas en dos niveles con estrategias de lanzamiento fundamentalmente diferentes.
Nivel 1: TRANSLATED_LOCALES
Estos son 15 idiomas con páginas principales completamente traducidas, revisadas manualmente por hablantes nativos o bilingües verificados.
// config/locales.ts
export const TRANSLATED_LOCALES = [
'en', 'es', 'fr', 'de', 'it', 'pt-BR', 'ja', 'ko',
'zh-CN', 'zh-TW', 'ru', 'ar', 'hi', 'tr', 'nl'
] as const;
Estos 15 idiomas obtienen:
- Etiquetas hreflang completas en las 118 páginas
- Inclusión en el sitemap XML
- URLs canónicas indexables
- Marcado schema consciente de locale
Nivel 2: DYNAMIC_TRANSLATED_LOCALES
Los 15 idiomas restantes comienzan como marcadores de posición solo en inglés. No están indexados. No obtienen entradas de hreflang. No existen en el sitemap.
export const DYNAMIC_TRANSLATED_LOCALES = [
'pl', 'sv', 'da', 'fi', 'no', 'cs', 'ro', 'hu',
'el', 'th', 'vi', 'id', 'ms', 'uk', 'bg'
] as const;
export const ALL_LOCALES = [
...TRANSLATED_LOCALES,
...DYNAMIC_TRANSLATED_LOCALES
] as const;
Cuando un idioma del Nivel 2 completa el pipeline de traducción -- traducción por lotes de Claude Haiku, compuerta de calidad de Winston AI, revisión humana opcional -- se gradúa al Nivel 1. Las entradas de hreflang, la inclusión de sitemap y las directivas de indexación se actualizan automáticamente.
// utils/locale-status.ts
export function isLocaleReady(locale: string): boolean {
// Comprueba si todos los espacios de nombres requeridos tienen traducciones
// con puntuaciones de Winston AI >= 95%
const status = getTranslationStatus(locale);
return status.completedNamespaces >= REQUIRED_NAMESPACES
&& status.minQualityScore >= 0.95;
}
export function getIndexableLocales(): string[] {
return ALL_LOCALES.filter(isLocaleReady);
}
Esta es la perspectiva clave: tu implementación de hreflang necesita ser dinámica. No puede ser una lista estática codificada en tiempo de compilación (bueno, puede serlo si reconstruyes cuando los locales se gradúan, que es lo que hacemos con ISR).

Configuración de Middleware Next-intl
El middleware es donde la detección de locale, enrutamiento y la lógica de compuerta convergen. Aquí está nuestro middleware.ts real:
// middleware.ts
import createMiddleware from 'next-intl/middleware';
import { NextRequest, NextResponse } from 'next/server';
import { ALL_LOCALES, TRANSLATED_LOCALES } from './config/locales';
const intlMiddleware = createMiddleware({
locales: ALL_LOCALES,
defaultLocale: 'en',
localePrefix: 'always',
localeDetection: true
});
export default function middleware(request: NextRequest) {
const pathname = request.nextUrl.pathname;
// Extrae locale del path
const pathnameLocale = ALL_LOCALES.find(
(locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
);
// Si locale está en Nivel 2 y aún no está listo,
// sirve contenido pero añade encabezado noindex
if (
pathnameLocale &&
!TRANSLATED_LOCALES.includes(pathnameLocale) &&
!isLocaleReady(pathnameLocale)
) {
const response = intlMiddleware(request);
response.headers.set('X-Robots-Tag', 'noindex, nofollow');
return response;
}
return intlMiddleware(request);
}
export const config = {
matcher: ['/((?!api|_next|_vercel|.*\\..*).*)']
};
Algunas cosas a tener en cuenta aquí:
localePrefix: 'always'-- Cada URL obtiene un prefijo de locale./en/horoscope,/de/horoskop, etc. Sin ambigüedad. Esto es crítico para hreflang porque cada URL alternativa debe ser distinta y predecible.Noindex del Nivel 2 -- Los locales sin traducir todavía se renderizan (los usuarios de esas regiones todavía pueden navegar), pero obtienen un encabezado
noindex. Google no desperdiciará presupuesto de rastreo en ellos.El matcher -- Excluimos rutas API, internos de Next.js y archivos estáticos. Las 39 rutas API conscientes de locale tienen su propio manejo de locale.
Si estás construyendo algo similar, hemos escrito más sobre nuestro enfoque de desarrollo de Next.js y cómo el middleware se ajusta a la arquitectura.
Implementación de Hreflang en el Encabezado HTML
Next.js 14+ con el App Router nos da la función generateMetadata. Aquí es donde van las etiquetas hreflang en el <head> HTML.
// app/[locale]/[...slug]/page.tsx
import { getIndexableLocales } from '@/utils/locale-status';
import { getLocalizedSlug } from '@/utils/slugs';
type Props = {
params: { locale: string; slug: string[] };
};
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { locale, slug } = params;
const baseUrl = 'https://deluxeastrology.com';
const pagePath = slug ? `/${slug.join('/')}` : '';
const indexableLocales = getIndexableLocales();
// Construye alternativas de idioma -- solo para locales indexables
const languages: Record<string, string> = {};
for (const loc of indexableLocales) {
const localizedSlug = await getLocalizedSlug(pagePath, loc);
languages[loc] = `${baseUrl}/${loc}${localizedSlug}`;
}
// x-default apunta a inglés
languages['x-default'] = `${baseUrl}/en${pagePath}`;
return {
title: await getLocalizedTitle(pagePath, locale),
alternates: {
canonical: `${baseUrl}/${locale}${pagePath}`,
languages
}
};
}
Esto genera HTML como:
<link rel="canonical" href="https://deluxeastrology.com/de/horoskop" />
<link rel="alternate" hreflang="en" href="https://deluxeastrology.com/en/horoscope" />
<link rel="alternate" hreflang="de" href="https://deluxeastrology.com/de/horoskop" />
<link rel="alternate" hreflang="fr" href="https://deluxeastrology.com/fr/horoscope" />
<!-- ... 12 más locales indexables ... -->
<link rel="alternate" hreflang="x-default" href="https://deluxeastrology.com/en/horoscope" />
Dos detalles críticos:
- La URL canónica es específica de locale. La página alemana canónica es la URL alemana, no la inglesa. Cada versión de idioma es su propia página canónica.
- x-default siempre está presente. Apunta a inglés. Si Google no puede coincidir el idioma de un usuario con ninguna de tus entradas de hreflang, x-default es el respaldo.
Generación de Sitemap con Entradas Hreflang
El hreflang en <head> HTML es necesario pero no suficiente. Para un sitio con 3,540 variantes de página potenciales, también necesitas hreflang en tu sitemap XML. Aquí está el por qué: Google puede descubrir relaciones de hreflang desde el sitemap sin rastrear cada página primero.
// app/sitemap.ts
import { MetadataRoute } from 'next';
import { getIndexableLocales } from '@/utils/locale-status';
import { getAllPages } from '@/utils/pages';
import { getLocalizedSlug } from '@/utils/slugs';
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const baseUrl = 'https://deluxeastrology.com';
const indexableLocales = getIndexableLocales();
const pages = await getAllPages(); // Devuelve 118 definiciones de página
const entries: MetadataRoute.Sitemap = [];
for (const page of pages) {
for (const locale of indexableLocales) {
const localizedSlug = await getLocalizedSlug(page.path, locale);
const url = `${baseUrl}/${locale}${localizedSlug}`;
// Construye alternativas para esta página específica
const alternates: Record<string, string> = {};
for (const altLocale of indexableLocales) {
const altSlug = await getLocalizedSlug(page.path, altLocale);
alternates[altLocale] = `${baseUrl}/${altLocale}${altSlug}`;
}
alternates['x-default'] = `${baseUrl}/en${page.path}`;
entries.push({
url,
lastModified: page.updatedAt,
changeFrequency: page.changeFreq || 'weekly',
priority: locale === 'en' ? 0.9 : 0.8,
alternates: {
languages: alternates
}
});
}
}
return entries;
}
Esto genera XML como:
<url>
<loc>https://deluxeastrology.com/de/horoskop</loc>
<lastmod>2025-01-15</lastmod>
<xhtml:link rel="alternate" hreflang="en" href="https://deluxeastrology.com/en/horoscope"/>
<xhtml:link rel="alternate" hreflang="de" href="https://deluxeastrology.com/de/horoskop"/>
<xhtml:link rel="alternate" hreflang="fr" href="https://deluxeastrology.com/fr/horoscope"/>
<xhtml:link rel="alternate" hreflang="x-default" href="https://deluxeastrology.com/en/horoscope"/>
</url>
Con 15 locales indexables y 118 páginas, eso son 1,770 entradas de sitemap. Manejable. Cuando todos los 30 idiomas estén listos, serán 3,540. Todavía dentro del límite de 50,000 URLs del sitemap de Google, pero de todos modos dividimos en sitemaps por locale para un monitoreo más limpio de Google Search Console.
Pipeline de Traducción: Claude Haiku + Winston AI
Aquí es donde la economía se vuelve interesante. Necesitábamos traducir 118 páginas en 41 espacios de nombres a 30 idiomas. La traducción profesional humana sería el estándar de oro, pero la matemática del presupuesto es brutal.
El Pipeline
- Extraer -- Extrae todas las cadenas traducibles de 41 espacios de nombres en JSON estructurado
- Traducir -- Procesa por lotes a través de Claude Haiku (el modelo rápido y barato de Anthropic) con contexto sobre el dominio (astrología), tono y audiencia objetivo
- Compuerta de Calidad -- Ejecuta contenido traducido a través de detección de contenido de Winston AI y puntuación de calidad. Umbral: 95%+ o rechaza.
- Revisión Humana -- Páginas de alto valor (página de inicio, páginas de destino, páginas de dinero) obtienen revisión manual por hablantes nativos
- Graduar -- Una vez que todos los espacios de nombres pasen compuertas de calidad, el locale se mueve de
DYNAMIC_TRANSLATED_LOCALESaTRANSLATED_LOCALES
// scripts/translate-locale.ts
async function translateLocale(targetLocale: string) {
const namespaces = await getNamespaces(); // 41 espacios de nombres
for (const ns of namespaces) {
const sourceStrings = await loadNamespace('en', ns);
const translated = await claude.messages.create({
model: 'claude-3-haiku-20240307',
max_tokens: 4096,
system: `Eres un traductor profesional especializado en contenido de astrología.
Traduce del inglés al ${getLanguageName(targetLocale)}.
Mantén precisión de terminología astrológica.
Preserva todas las variables de interpolación como {name} y {date}.`,
messages: [{
role: 'user',
content: `Traduce estos pares clave-valor JSON. Devuelve solo JSON válido:\n${JSON.stringify(sourceStrings, null, 2)}`
}]
});
const qualityScore = await winstonAI.analyze(translated.content);
if (qualityScore >= 0.95) {
await saveNamespace(targetLocale, ns, translated.content);
} else {
await flagForReview(targetLocale, ns, translated.content, qualityScore);
}
}
}
El costo por idioma con Claude Haiku se calcula aproximadamente en $22 para todas las 118 páginas en 41 espacios de nombres. La mayoría son costos de tokens -- Haiku es increíblemente barato a $0.25 por millón de tokens de entrada y $1.25 por millón de tokens de salida (precios 2025).
Comparación de Costos: Nuestro Enfoque vs Alternativas
Esta es la tabla que convenció al equipo de Deluxe Astrology:
| Enfoque | Costo para 30 Idiomas | Costo Continuo | Calidad | Tiempo para Lanzar |
|---|---|---|---|---|
| Claude Haiku + Winston AI | ~$660 total ($22/idioma) | $0 (una sola vez) | Compuerta de calidad 95%+, revisión humana para páginas clave | 2-3 semanas en forma progresiva |
| Weglot | $0 configuración | $699/mes ($8,388/año) | Traducción por máquina, editable | Instantáneo pero arriesgado |
| Traductores Profesionales | $150K-$300K ($5K-10K/idioma) | $2K-5K/idioma para actualizaciones | Calidad más alta | 3-6 meses |
| DeepL API | ~$400 total | $0 (una sola vez) | Bueno pero sin compuerta de calidad | 1-2 semanas |
| API de Google Translate | ~$300 total | $0 (una sola vez) | Calidad más baja para contenido de nicho | 1 semana |
Seamos honestos: el $660 total para traducción de Claude Haiku de 30 idiomas es casi sospechosamente barato. El truco es que necesitas la compuerta de calidad (Winston AI) y la capa de revisión humana para hacerlo listo para producción. Incluso con esos costos incluidos -- quizás $50-100 para llamadas API de Winston AI y $500-1,000 para revisión humana de páginas de alto valor -- todavía estás bajo $2,000 total. Compáralo con los $699/mes de Weglot. Te recuperarías en menos de 3 meses.
El verdadero problema con Weglot y servicios similares: traducen todo de una vez. Sin compuertas. Sin control de calidad por página. Activas un interruptor y de repente Google ve 3,540 páginas, muchas de las cuales son mediocres traducciones de máquina. Nuestro enfoque nos permite ser quirúrgicos al respecto.
Hablamos más sobre cómo abordamos proyectos como este en nuestra página de precios -- el pipeline de traducción es un componente de una arquitectura de desarrollo de CMS headless más grande.
Marcado Schema Consciente de Locale
Este es el que sorprende a casi todos. Tus datos estructurados necesitan coincidir con el idioma de la página. Una página alemana con schema de FAQ en inglés confunde la comprensión de Google sobre la página.
// utils/schema.ts
export function generateFAQSchema(
faqs: Array<{ question: string; answer: string }>,
locale: string
) {
return {
'@context': 'https://schema.org',
'@type': 'FAQPage',
'inLanguage': locale, // Crítico: debe coincidir con locale de página
'mainEntity': faqs.map((faq) => ({
'@type': 'Question',
'name': faq.question, // Debe estar en idioma objetivo
'acceptedAnswer': {
'@type': 'Answer',
'text': faq.answer // Debe estar en idioma objetivo
}
}))
};
}
Cada tipo de schema que soporta inLanguage debe usarlo. Para Deluxe Astrology, eso incluye:
- FAQPage -- Preguntas y respuestas en el idioma objetivo
- Article --
inLanguagecoincidiendo con el locale - WebPage -- Propiedad
inLanguage - BreadcrumbList -- Nombres de migas de pan en el idioma objetivo
No solo traduzcas el contenido visible y olvides los datos estructurados. Google lee ambos.
Errores Comunes Que Hundirán Tus Rankings
Falta hreflang x-default
Lo veo constantemente. Los sitios implementan hreflang para todos sus idiomas pero olvidan x-default. Sin él, Google no tiene respaldo para usuarios cuyo idioma no coincida con ninguna de tus versiones. Siempre inclúyelo. Siempre apunta a tu idioma principal (generalmente inglés).
Locale inconsistente en URL vs contenido
Si tu URL dice /fr/horoscope pero el contenido de la página está en inglés porque la traducción no ha cargado o ha retrocedido, Google lo marcará como un 404 suave o contenido delgado. Esto es exactamente por qué construimos el sistema de compuerta de dos niveles -- una página no obtiene una URL francesa hasta que tiene contenido francés.
Lanzar todos los idiomas a la vez
He repetido este punto ya, pero vale la pena repetir. Lanzar 30 idiomas simultáneamente es el error más común en SEO internacional. Incluso si tus traducciones son perfectas, estás pidiendo a Google que rastree, indexe y evalúe miles de páginas nuevas durante la noche. Lanza en lotes de 3-5 idiomas. Monitorea la indexación en GSC. Luego añade más.
Etiquetas hreflang no recíprocas
Si la página A (inglés) apunta a la página B (alemán) vía hreflang, la página B debe apuntar de vuelta a la página A. Si este enlace recíproco falta, Google ignora completamente el hreflang. Cuando generas estos dinámicamente (como hacemos), la reciprocidad es automática. Pero si los administras manualmente, audita regularmente.
Hreflang auto-referenciador faltante
Cada página debe incluirse a sí misma en su propio conjunto de hreflang. La página alemana debe listar hreflang="de" apuntando a sí misma. Esto es fácil de perder en implementaciones manuales.
Hreflang en solo una ubicación
Poner hreflang solo en el <head> o solo en el sitemap es un error. Usa ambos. Cinturón y tirantes. Google procesa ambas fuentes, y si una no logra ser rastreada, la otra actúa como respaldo.
Para proyectos de esta escala, tener un equipo experimentado ayuda a evitar estos escollos. Si estás planeando una compilación multilingüe, estamos felices de hablar sobre el enfoque.
Preguntas Frecuentes
¿Necesito etiquetas hreflang si solo tengo diferencias de idioma (no regionales)? Sí. Aunque la detección de idioma de Google ha mejorado en 2025, hreflang sigue siendo la señal definitiva para indicar a los motores de búsqueda qué versión de idioma servir. Sin ellas, corre el riesgo de que Google muestre tu página en inglés a usuarios que hablan francés simplemente porque la versión en inglés tiene más backlinks. Hreflang se vuelve aún más crítica cuando tienes 10+ idiomas -- la probabilidad de canibalización entre idiomas aumenta dramáticamente con la escala.
¿Cuántas entradas de hreflang son demasiadas para una sola página?
Google no ha publicado un límite oficial, pero las pruebas prácticas muestran que más allá de 50 variantes de idioma por página, comienzas a ver rendimientos decrecientes y ocasionales problemas de análisis. Para nuestra configuración de 30 idiomas, cada página tiene 31 entradas de hreflang (30 idiomas + x-default), lo cual está bien dentro de la zona segura. Si estás tratando con 50+ combinaciones regionales e idiomáticas, considera usar solo el enfoque de sitemap XML para mantener el tamaño de <head> manejable.
¿Debo usar hreflang en el encabezado HTML, sitemap XML o encabezados HTTP?
Para aplicaciones Next.js, usa tanto <head> HTML como sitemap XML. Los encabezados HTTP son principalmente útiles para recursos no HTML como PDFs. El enfoque de <head> HTML se procesa en tiempo de rastreo y da la señal más rápida. El sitemap actúa como respaldo y ayuda a Google a descubrir páginas alternas que aún no ha rastreado. No recomendamos confiar en solo un método.
¿Cuál es el costo de traducir un sitio web completo con IA en 2025? Usando Claude Haiku, traducimos 118 páginas en 41 espacios de nombres por aproximadamente $22 por idioma. Para 30 idiomas, eso es alrededor de $660 total. Añade compuerta de calidad de Winston AI a aproximadamente $50-100 para llamadas API, y revisión humana opcional para páginas de alto valor a $500-1,000, y tu costo total es bajo $2,000. Compáralo con Weglot a $699/mes o servicios de traducción profesional a $5,000-10,000 por idioma.
¿Por qué usar un sistema de compuerta de traducción de dos niveles en lugar de traducir todo a la vez? Google trata el contenido delgado como una señal de calidad negativa que puede arrastrarte a todo tu dominio. Si lanzas 30 idiomas pero solo 15 tienen traducciones de calidad, esos 15 idiomas mal traducidos crean aproximadamente 1,770 páginas de baja calidad. El sistema de dos niveles asegura que solo las páginas que cumplan un umbral de calidad de 95%+ sean indexadas. Los idiomas se gradúan del Nivel 2 al Nivel 1 a medida que las traducciones pasan compuertas de calidad, protegiendo tu autoridad de dominio durante el lanzamiento.
¿Cómo manejo páginas sin traducir para un locale parcialmente traducido?
Para locales donde algunos espacios de nombres están traducidos pero otros no, retrocedemos a contenido en inglés y añadimos una etiqueta meta noindex vía middleware. La URL todavía se resuelve (los usuarios pueden acceder a ella), pero Google no indexará la página de idioma mixto. Una vez que todos los espacios de nombres requeridos pasen compuertas de calidad, la etiqueta noindex se elimina y se añaden entradas de hreflang. Esto previene que traducciones parciales contaminen tu índice.
¿Qué umbral de puntuación de calidad debo usar para traducciones de IA? Usamos Winston AI con un umbral de puntuación de calidad de 95%+. Cualquier cosa por debajo se marca para revisión humana o re-traducción con prompts ajustados. En la práctica, Claude Haiku logra 95%+ en aproximadamente el 85% de lotes de espacios de nombres en la primera pasada. El 15% restante típicamente falla debido a terminología específica del dominio (términos de astrología que no se traducen directamente) o estructuras oracionales complejas. Un umbral del 90% dejaría pasar una redacción notablemente incómoda.
¿Puedo usar Astro en lugar de Next.js para sitios multilingües con hreflang? Absolutamente. Astro tiene excelente soporte i18n integrado a partir de Astro 4.0+, y su modelo de generación estática en realidad simplifica la implementación de hreflang ya que todas las URLs son conocidas en tiempo de compilación. Hemos construido proyectos multilingües con ambos frameworks. Para sitios con contenido dinámico pesado y rutas API (como los 39 puntos finales conscientes de locale de Deluxe Astrology), Next.js es el mejor ajuste. Para sitios ricos en contenido con menos interactividad, el desarrollo de Astro puede ser más rápido y performante. Los principios de hreflang son idénticos independientemente del framework.