Add i18n features

Astro doesn’t have built-in internationalization (i18n) support, but you can build your own i18n solution. In this recipe, you’ll learn how to use content collections and dynamic routing to serve content for different languages.

This example serves each language at its own subpath, e.g. example.com/en/blog for English and example.com/fr/blog for French.

Set up pages for each language

Section titled Set up pages for each language
  1. Create a directory for each language you want to support. For example, en/ and fr/ if you are supporting English and French:

    • ディレクトリsrc/
      • ディレクトリpages/
        • ディレクトリen/
          • about.astro
          • index.astro
        • ディレクトリfr/
          • about.astro
          • index.astro
        • index.astro
  2. Set up src/pages/index.astro to redirect to your default language.

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

    Copied!

    This approach uses a meta refresh and will work however you deploy your site. Some static hosts also let you configure server redirects with a custom configuration file. See your deploy platform’s documentation for more details.

Use collections for translated content

Section titled Use collections for translated content
  1. Create a folder in src/content/ for each type of content you want to include and add subdirectories for each supported language. For example, to support English and French blog posts:

    • ディレクトリsrc/
      • ディレクトリcontent/
        • ディレクトリblog/
          • ディレクトリen/ Blog posts in English
            • post-1.md
            • post-2.md
          • ディレクトリfr/ Blog posts in French
            • post-1.md
            • post-2.md
  2. Create a src/content/config.ts file and export a collection for each type of content.

    src/content/config.ts
    import { defineCollection, z } from 'astro:content';
    
    const blogCollection = defineCollection({
      schema: z.object({
        title: z.string(),
        author: z.string(),
        date: z.date()
      })
    });
    
    export const collections = {
      'blog': blogCollection
    };

    Copied!

    📚 Read more about Content Collections.

  3. Use dynamic routes to fetch and render content based on a lang and a slug parameter.

    In static rendering mode, use getStaticPaths to map each content entry to a page:

    src/pages/[lang]/blog/[...slug].astro
    ---
    import { getCollection } from 'astro:content'
    
    export async function getStaticPaths() {
      const pages = await getCollection('blog')
    
      const paths = pages.map(page => {
        const [lang, ...slug] = page.slug.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 page.render();
    ---
    <h1>{page.data.title}</h1>
    <p>by {page.data.author}{formattedDate}</p>
    <Content/>

    Copied!

    📚 Read more about dynamic routing.

Create dictionaries of terms to translate the labels for UI elements around your site. This allows your visitors to experience your site fully in their language.

  1. Create a src/i18n/ui.ts file to store your translation strings:

    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': 'Acceuil',
        'nav.about': 'À propos',
      },
    } as const;

    Copied!

  2. Create two helper functions: one to detect the page language based on the current URL, and one to get translations strings for different parts of the UI in 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];
      }
    }

    Copied!

  3. Import the helpers where needed and use them to choose the UI string that corresponds to the current language. For example, a nav component might look like:

    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>

    Copied!

  4. Each page must have a lang attribute on the <html> element that matches the language on the page. In this example, a reusable layout extracts the language from the current route:

    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>

    Copied!

    You can then use this base layout to ensure that pages use the correct lang attribute automatically.

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

    Copied!

Let users switch between languages

Section titled Let users switch between languages

Create links to the different languages you support so users can choose the language they want to read your site in.

  1. Create a component to show a link for each language:

    src/components/LanguagePicker.astro
    ---
    import { languages } from '../i18n/ui';
    ---
    <ul>
      {Object.entries(languages).map(([lang, label]) => (
        <li>
          <a href={`/${lang}/`}>{label}</a>
        </li>
      ))}
    </ul>

    Copied!

  2. Add <LanguagePicker /> to your site so it is shown on every page. The example below adds it to the site footer in a base layout:

    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>

    Copied!

  • astro-i18next — An Astro integration for i18next including some utility components.
  • astro-i18n — A TypeScript-first internationalization library for Astro.

その他のレシピ