Traduciendo metadatos de página

Configurando etiquetas <title> y <description> localizadas

Problema

Un usuario visualiza una página en español, y todo el contenido visible está correctamente traducido. Sin embargo, la pestaña del navegador y el fragmento de resultados del motor de búsqueda todavía muestran el título y la descripción en inglés. Esta discrepancia en los metadatos crea una experiencia de usuario confusa y perjudica el SEO al presentar información irrelevante en las búsquedas.

Solución

Utiliza la función generateMetadata de Next.js dentro de tus páginas y layouts. Esta función del lado del servidor puede cargar las traducciones correctas basadas en el parámetro lang (usando la misma lógica de carga de diccionarios que tus componentes) y devolver un objeto metadata dinámico con el título y la descripción localizados.

Pasos

1. Crear una función para cargar diccionarios

Necesitas una forma de cargar tus archivos de traducción planos en el servidor. Crea una función auxiliar para esto.

// app/get-dictionary.ts
import 'server-only';

type Messages = Record<string, string>;

const dictionaries: { [key: string]: () => Promise<Messages> } = {
  en: () => import('@/dictionaries/en.json').then((module) => module.default),
  es: () => import('@/dictionaries/es.json').then((module) => module.default),
};

export const getDictionary = async (lang: string) => {
  const load = dictionaries[lang];
  if (load) {
    return load();
  }
  return dictionaries.en();
};

2. Definir metadatos en una página

En tu archivo de página (por ejemplo, app/[lang]/about/page.tsx), exporta una función async llamada generateMetadata. Next.js la llamará automáticamente al renderizar la página.

// app/[lang]/about/page.tsx
import { getDictionary } from '@/app/get-dictionary';
import type { Metadata } from 'next';

type Props = {
  params: { lang: string };
};

// Esta función genera metadatos
export async function generateMetadata({ params }: Props): Promise<Metadata> {
  // Carga el diccionario para esta página
  const dict = await getDictionary(params.lang);

  return {
    title: dict['about.title'], // por ejemplo, "About Us" o "Sobre Nosotros"
    description: dict['about.description'],
  };
}

// El resto de tu componente de página
export default function AboutPage() {
  return (
    <div>
      {/* Contenido de la página */}
      <h1>...</h1>
    </div>
  );
}

3. Establece una plantilla de título en el layout raíz

Para evitar repetir el nombre de tu sitio en cada título, puedes establecer una plantilla en tu layout raíz.

// app/[lang]/layout.tsx
import { getDictionary } from '@/app/get-dictionary';
import type { Metadata } from 'next';

type Props = {
  params: { lang: string };
  children: React.ReactNode;
};

// También puedes generar metadatos en los layouts
export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const dict = await getDictionary(params.lang);

  return {
    // Esto proporciona un título base y una plantilla
    title: {
      default: dict['site.name'], // por ejemplo, "Mi Sitio Increíble"
      template: `%s | ${dict['site.name']}`, // por ejemplo, "Sobre Nosotros | Mi Sitio Increíble"
    },
    description: dict['site.description'],
  };
}

export default async function RootLayout({ children, params }: Props) {
  // ... resto de tu layout (carga de proveedores, etc.)
  return (
    <html lang={params.lang}>
      <body>{children}</body>
    </html>
  );
}