Saltar al contenido

Añadir características de i18n

En esta receta, aprenderás cómo usar content collections y enrutamiento dinámico para construir tu propia solución de internacionalización (i18n) y servir tu contenido en diferentes idiomas.

Este ejemplo sirve cada idioma en su propio subpath, ej. example.com/en/blog para inglés y example.com/fr/blog para francés.

Si prefieres que el idioma por defecto no sea visible en la URL a diferencia de otros idiomas, hay instrucciones para ocultar el idioma por defecto más abajo.

Consulta la sección de recursos para enlaces externos a temas relacionados como estilos right-to-left (RTL) y elección de etiquetas de idioma.
  1. Crea un directorio para cada idioma que quieras soportar. Por ejemplo, en/ y fr/ si estás soportando inglés y francés:

    • Directoriosrc/
      • Directoriopages/
        • Directorioen/
          • about.astro
          • index.astro
        • Directoriofr/
          • about.astro
          • index.astro
        • index.astro
  2. Configura src/pages/index.astro para redirigir a tu idioma por defecto.

    src/pages/index.astro
    <meta http-equiv="refresh" content="0;url=/en/" />

    Este enfoque usa un meta refresh y funcionará sin importar cómo despliegues tu sitio. Algunos hosts estáticos también te permiten configurar redirecciones de servidor con un archivo de configuración personalizado. Consulta la documentación de tu plataforma de despliegue para más detalles.

  1. Crea una carpeta en src/content/ para cada tipo de contenido que quieras incluir y añade subdirectorios para cada idioma soportado. Por ejemplo, para soportar blog posts en inglés y francés:

    • Directoriosrc/
      • Directoriocontent/
        • Directorioblog/
          • Directorioen/ Blog posts en inglés
            • post-1.md
            • post-2.md
          • Directoriofr/ Blog posts en francés
            • post-1.md
            • post-2.md
  2. Crea un archivo src/content.config.ts y exporta una colección para cada tipo de contenido.

    src/content.config.ts
    import { defineCollection } from 'astro:content';
    import { z } from 'astro/zod';
    const blogCollection = defineCollection({
    schema: z.object({
    title: z.string(),
    author: z.string(),
    date: z.date()
    })
    });
    export const collections = {
    'blog': blogCollection
    };
    Lee más sobre Content Collections.
  3. Usa rutas dinámicas para obtener y renderizar contenido basado en un parámetro lang y un slug.

    En modo de renderizado estático, usa getStaticPaths para mapear cada entrada de contenido a una página:

    src/pages/[lang]/blog/[...slug].astro
    ---
    import { getCollection, render } from 'astro:content';
    export async function getStaticPaths() {
    const pages = await getCollection('blog');
    const paths = pages.map(page => {
    const [lang, ...slug] = page.id.split('/');
    return { params: { lang, slug: slug.join('/') || undefined }, props: page };
    });
    return paths;
    }
    const { lang, slug } = Astro.params;
    const page = Astro.props;
    const formattedDate = page.data.date.toLocaleString(lang);
    const { Content } = await render(page);
    ---
    <h1>{page.data.title}</h1>
    <p>by {page.data.author}{formattedDate}</p>
    <Content/>
    Lee más sobre enrutamiento dinámico.

Crea diccionarios de términos para traducir las etiquetas de los elementos de UI alrededor de tu sitio. Esto permite que tus visitantes experimenten tu sitio completamente en su idioma.

  1. Crea un archivo src/i18n/ui.ts para almacenar tus cadenas de traducción:

    src/i18n/ui.ts
    export const languages = {
    en: 'English',
    fr: 'Français',
    };
    export const defaultLang = 'en';
    export const ui = {
    en: {
    'nav.home': 'Home',
    'nav.about': 'About',
    'nav.twitter': 'Twitter',
    },
    fr: {
    'nav.home': 'Accueil',
    'nav.about': 'À propos',
    },
    } as const;
  2. Crea dos funciones helper: una para detectar el idioma de la página basándose en la URL actual, y otra para obtener cadenas de traducción para diferentes partes de la UI en src/i18n/utils.ts:

    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];
    }
    }
  3. Importa los helpers donde sean necesarios y úsalos para elegir la cadena de UI que corresponda al idioma actual. Por ejemplo, un componente de nav podría verse así:

    src/components/Nav.astro
    ---
    import { getLangFromUrl, useTranslations } from '../i18n/utils';
    const lang = getLangFromUrl(Astro.url);
    const t = useTranslations(lang);
    ---
    <ul>
    <li>
    <a href={`/${lang}/home/`}>
    {t('nav.home')}
    </a>
    </li>
    <li>
    <a href={`/${lang}/about/`}>
    {t('nav.about')}
    </a>
    </li>
    <li>
    <a href="https://twitter.com/astrodotbuild">
    {t('nav.twitter')}
    </a>
    </li>
    </ul>
  4. Cada página debe tener un atributo lang en el elemento <html> que coincida con el idioma de la página. En este ejemplo, un layout reutilizable extrae el idioma de la ruta actual:

    src/layouts/Base.astro
    ---
    import { getLangFromUrl } from '../i18n/utils';
    const lang = getLangFromUrl(Astro.url);
    ---
    <html lang={lang}>
    <head>
    <meta charset="utf-8" />
    <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
    <meta name="viewport" content="width=device-width" />
    <title>Astro</title>
    </head>
    <body>
    <slot />
    </body>
    </html>

    Luego puedes usar este layout base para asegurar que las páginas usen el atributo lang correcto automáticamente.

    src/pages/en/about.astro
    ---
    import Base from '../../layouts/Base.astro';
    ---
    <Base>
    <h1>About me</h1>
    ...
    </Base>

Crea enlaces a los diferentes idiomas que soportas para que los usuarios puedan elegir el idioma en el que quieren leer tu sitio.

  1. Crea un componente para mostrar un enlace para cada idioma:

    src/components/LanguagePicker.astro
    ---
    import { languages } from '../i18n/ui';
    ---
    <ul>
    {Object.entries(languages).map(([lang, label]) => (
    <li>
    <a href={`/${lang}/`}>{label}</a>
    </li>
    ))}
    </ul>
  2. Añade <LanguagePicker /> a tu sitio para que se muestre en cada página. El ejemplo siguiente lo añade al footer del sitio en un layout base:

    src/layouts/Base.astro
    ---
    import LanguagePicker from '../components/LanguagePicker.astro';
    import { getLangFromUrl } from '../i18n/utils';
    const lang = getLangFromUrl(Astro.url);
    ---
    <html lang={lang}>
    <head>
    <meta charset="utf-8" />
    <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
    <meta name="viewport" content="width=device-width" />
    <title>Astro</title>
    </head>
    <body>
    <slot />
    <footer>
    <LanguagePicker />
    </footer>
    </body>
    </html>
  1. Crea un directorio para cada idioma excepto el idioma por defecto. Por ejemplo, almacena las páginas de tu idioma por defecto directamente en pages/, y tus páginas traducidas en fr/:

    • Directoriosrc/
      • Directoriopages/
        • about.astro
        • index.astro
        • Directoriofr/
          • about.astro
          • index.astro
  2. Añade otra línea al archivo src/i18n/ui.ts para activar la funcionalidad:

    src/i18n/ui.ts
    export const showDefaultLang = false;
  3. Añade una función helper a src/i18n/utils.ts, para traducir rutas basándose en el idioma actual:

    src/i18n/utils.ts
    import { ui, defaultLang, showDefaultLang } from './ui';
    export function useTranslatedPath(lang: keyof typeof ui) {
    return function translatePath(path: string, l: string = lang) {
    return !showDefaultLang && l === defaultLang ? path : `/${l}${path}`
    }
    }
  4. Importa el helper donde sea necesario. Por ejemplo, un componente nav podría verse así:

    src/components/Nav.astro
    ---
    import { getLangFromUrl, useTranslations, useTranslatedPath } from '../i18n/utils';
    const lang = getLangFromUrl(Astro.url);
    const t = useTranslations(lang);
    const translatePath = useTranslatedPath(lang);
    ---
    <ul>
    <li>
    <a href={translatePath('/home/')}>
    {t('nav.home')}
    </a>
    </li>
    <li>
    <a href={translatePath('/about/')}>
    {t('nav.about')}
    </a>
    </li>
    <li>
    <a href="https://twitter.com/astrodotbuild">
    {t('nav.twitter')}
    </a>
    </li>
    </ul>
  5. La función helper también puede usarse para traducir rutas para un idioma específico. Por ejemplo, cuando los usuarios cambian entre idiomas:

    src/components/LanguagePicker.astro
    ---
    import { languages } from '../i18n/ui';
    import { getLangFromUrl, useTranslatedPath } from '../i18n/utils';
    const lang = getLangFromUrl(Astro.url);
    const translatePath = useTranslatedPath(lang);
    ---
    <ul>
    {Object.entries(languages).map(([lang, label]) => (
    <li>
    <a href={translatePath('/', lang)}>{label}</a>
    </li>
    ))}
    </ul>

Traduce las rutas de tus páginas para cada idioma.

  1. Añade mapeos de rutas a src/i18n/ui.ts:

    src/i18n/ui.ts
    export const routes = {
    de: {
    'services': 'leistungen',
    },
    fr: {
    'services': 'prestations-de-service',
    },
    }
  2. Actualiza la función helper useTranslatedPath en src/i18n/utils.ts para añadir lógica de traducción de rutas.

    src/i18n/utils.ts
    import { ui, defaultLang, showDefaultLang, routes } from './ui';
    export function useTranslatedPath(lang: keyof typeof ui) {
    return function translatePath(path: string, l: string = lang) {
    const pathName = path.replaceAll('/', '')
    const hasTranslation = defaultLang !== l && routes[l] !== undefined && routes[l][pathName] !== undefined
    const translatedPath = hasTranslation ? '/' + routes[l][pathName] : path
    return !showDefaultLang && l === defaultLang ? translatedPath : `/${l}${translatedPath}`
    }
    }
  3. Crea una función helper para obtener la ruta, si existe basándose en la URL actual, en src/i18n/utils.ts:

    src/i18n/utils.ts
    import { ui, defaultLang, showDefaultLang, routes } from './ui';
    export function getRouteFromUrl(url: URL): string | undefined {
    const pathname = new URL(url).pathname;
    const parts = pathname?.split('/');
    const path = parts.pop() || parts.pop();
    if (path === undefined) {
    return undefined;
    }
    const currentLang = getLangFromUrl(url);
    if (defaultLang === currentLang) {
    const route = Object.values(routes)[0];
    return route[path] !== undefined ? route[path] : undefined;
    }
    const getKeyByValue = (obj: Record<string, string>, value: string): string | undefined => {
    return Object.keys(obj).find((key) => obj[key] === value);
    }
    const reversedKey = getKeyByValue(routes[currentLang], path);
    if (reversedKey !== undefined) {
    return reversedKey;
    }
    return undefined;
    }
  4. La función helper puede usarse para obtener una ruta traducida. Por ejemplo, cuando no se define una ruta traducida, el usuario será redirigido a la página principal:

    src/components/LanguagePicker.astro
    ---
    import { languages } from '../i18n/ui';
    import { getRouteFromUrl, useTranslatedPath } from '../i18n/utils';
    const route = getRouteFromUrl(Astro.url);
    ---
    <ul>
    {Object.entries(languages).map(([lang, label]) => {
    const translatePath = useTranslatedPath(lang);
    return (
    <li>
    <a href={translatePath(`/${route ? route : ''}`)}>{label}</a>
    </li>
    )
    })}
    </ul>

Encuentra utilidades de i18n construidas por la comunidad que puedes añadir a tu proyecto de Astro en nuestro directorio de integraciones.

Contribuir Comunidad Patrocinar