言語を切り替える
テーマを切り替える

Astro i18n 設定ガイド:30分で実装する多言語サイト(言語切り替え機能付き)

はじめに

「よし、素敵なAstroブログができた!次は世界に向けて発信だ!」と意気込んで多言語対応(i18n)を調べ始めたものの、挫折しそうになったことはありませんか?
astro.config.mjslocales だの prefixDefaultLocale だの設定項目は多いし、ディレクトリ構造はどうすればいいか迷うし、言語切り替えボタンの実装方法もよくわからない…。

正直、私も最初は prefixDefaultLocaletrue にすべきか false にすべきかで半日悩みました。でも、一度理解してしまえば、Astroのi18n機能は非常にシンプルで強力なことに気づきます。

この記事では、公式ドキュメントよりも噛み砕いて、Astroサイトを多言語化する全手順を解説します。基本設定から、コピペで使える言語切り替えコンポーネント、SEO対策まで、これさえ読めば30分であなたのサイトがグローバル対応になります。

Astro i18n 基本設定(10分で完了)

astro.config.mjs の設定

まずは心臓部である設定ファイルです。Astro v4.0からi18nは標準機能になりました。astro.config.mjs に以下の設定を追加します。

// astro.config.mjs
import { defineConfig } from 'astro/config';

export default defineConfig({
  i18n: {
    // サイトでサポートする全言語リスト
    locales: ['en', 'zh-cn', 'ja'],

    // デフォルト言語(localesに含まれている必要あり)
    defaultLocale: 'ja',

    // デフォルト言語のURLにプレフィックス(/ja/)をつけるかどうか
    // false にすると、日本語は /about 、英語は /en/about になります(推奨)
    prefixDefaultLocale: false,
  }
});

設定項目の意味は以下の通りです:

locales: サポートする言語の配列です。'en'(英語)、'zh-cn'(簡体字中国語)、'ja'(日本語)のように標準的な言語コードを使います。地域まで指定したい場合は 'en-US' のように書きます。

defaultLocale: ユーザーがルートURL(/)にアクセスしたときに表示される言語(デフォルト言語)です。

prefixDefaultLocale: ここが悩みどころです。

  • false(推奨): デフォルト言語(例:日本語)のURLは /about のようにスッキリします。他の言語は /en/about になります。
  • true: 全ての言語にプレフィックスがつきます。日本語は /ja/about、英語は /en/about になります。URLの構造を統一したい場合や、SEO上の理由がある場合はこちらを選びます。

3つのルーティング戦略

どのURL構造にするか、プロジェクトに合わせて選びましょう。

戦略設定URL例おすすめシーンメリット・デメリット
戦略1:デフォルト言語プレフィックスなしprefixDefaultLocale: false/about
/en/about
一般的なブログ、公式サイト(推奨)✅ デフォルト言語のURLが短い
❌ URL構造が不統一に見えるかも
戦略2:全言語プレフィックスありprefixDefaultLocale: true/ja/about
/en/about
SEO要件が厳しい場合
URL構造を統一したい場合
✅ 構造が統一されて管理しやすい
✅ 言語切り替えロジックが単純
❌ デフォルト言語のURLが長くなる
戦略3:マニュアルモードrouting: 'manual'(完全カスタム)複雑な要件がある場合✅ 自由自在
❌ 設定と実装が大変

個人のブログやドキュメントサイトなら、戦略1false)で十分です。もし中英日3ヶ国語対応のブログにするなら、locales: ['ja', 'en', 'zh-cn']defaultLocale: 'ja' でOKです。

Astro i18n 多言語設定

Astroサイトを多言語化するための30分ステップバイステップガイド

  1. 1

    Step1: 設定ファイルの編集

    astro.config.mjs に i18n オブジェクトを追加:

    • locales:対応言語リスト(例:['ja', 'en'])
    • defaultLocale:デフォルト言語(例:ja)
    • prefixDefaultLocale:URLプレフィックス設定
    - false:デフォルト言語はプレフィックスなし(推奨)
    - true:全言語にプレフィックス付与
  2. 2

    Step2: コンテンツの構造化

    コンテンツ管理方法を選択:

    方法1:言語別フォルダ(初心者向け)
    • src/pages/以下に ja/ や en/ フォルダを作成
    • 直感的で分かりやすい

    方法2:動的ルーティング(中級者向け)
    • [lang] パラメータを使用
    • コード量を削減できる

    ブログの場合:
    • Content Collections を使用
    • Markdownファイルを言語ごとに管理
  3. 3

    Step3: UI翻訳辞書の作成

    src/i18n/ui.ts を作成し、ナビゲーションやボタンなどの定型文を定義:

    {
    'ja': {
    nav: { home: 'ホーム', about: '概要' },
    button: { submit: '送信' }
    },
    'en': {
    nav: { home: 'Home', about: 'About' },
    button: { submit: 'Submit' }
    }
    }
  4. 4

    Step4: 言語切り替え機の実装

    getRelativeLocaleUrl と Astro.currentLocale を活用して言語切り替えコンポーネントを作成:

    • 現在のパスを維持したまま言語部分だけ置換
    • ドロップダウンやボタンリストで表示
  5. 5

    Step5: SEO対策

    Layoutファイルに hreflang タグを追加し、sitemap設定を行うことで検索エンジンに多言語対応を通知する

コンテンツの管理方法(2パターン)

設定の次は「ファイルをどう置くか」です。Astroには主に2つのアプローチがあります。

パターン1:言語ごとにフォルダを分ける(初心者向け)

一番わかりやすい方法です。言語ごとに物理的なフォルダを作ります。

src/pages/
├── about.astro        # デフォルト言語(日本語)
├── blog.astro
├── index.astro
├── en/                # 英語版
│   ├── about.astro
│   ├── blog.astro
│   └── index.astro
└── zh-cn/             # 中国語版
    ├── about.astro
    ├── blog.astro
    └── index.astro

prefixDefaultLocale: false の場合、デフォルト言語のファイルは src/pages/ 直下に置きます。
この方法は構造が単純で、ページごとにレイアウトを大胆に変えたい場合などに便利です。ただし、全言語共通の修正が入った場合、全てのファイルを修正する必要があります。

パターン2:動的ルーティング(中級者向け)

ファイルを1つにまとめたい場合は動的ルーティングを使います。

src/pages/
└── [lang]/
    └── [...slug].astro

[lang] 部分が言語コード(en, jaなど)になります。

---
// src/pages/[lang]/[...slug].astro
export function getStaticPaths() {
  const locales = ['ja', 'en', 'zh-cn'];
  const slugs = ['about', 'blog', 'contact'];

  return locales.flatMap((lang) =>
    slugs.map((slug) => ({
      params: { lang, slug },
    }))
  );
}

const { lang, slug } = Astro.params;
// langとslugに基づいてコンテンツをロード
---

コードの重複が減りますが、Astroの動的ルーティングの知識が必要です。最初はパターン1で慣れてから移行するのもありです。

Content Collections を使う(ブログならこれ!)

ブログ記事のようなコンテンツは、Content Collections で管理するのがベストプラクティスです。

ディレクトリ構成:

src/content/
└── blog/
    ├── en/
    │   ├── post-1.md
    │   └── post-2.md
    ├── ja/
    │   ├── post-1.md
    │   └── post-2.md
    └── zh-cn/

設定ファイル src/content/config.ts

import { defineCollection, z } from 'astro:content';

const blogCollection = defineCollection({
  schema: z.object({
    title: z.string(),
    lang: z.enum(['en', 'zh-cn', 'ja']), // 言語フィールドを追加
    // ...他のフィールド
  }),
});

export const collections = {
  blog: blogCollection,
};

ページ側での取得:

---
import { getCollection } from 'astro:content';

const currentLang = Astro.currentLocale; // 現在の言語を取得
const posts = await getCollection('blog', ({ data }) => {
  return data.lang === currentLang; // 現在の言語の記事だけフィルタリング
});
---

Astro公式サイトもこの方式を採用しています。スケーラブルで管理しやすいです。

UI翻訳辞書の管理

記事の中身以外の、ナビゲーションメニューやボタンのテキスト(「ホーム」「次へ」など)の翻訳管理方法です。
シンプルな辞書ファイルを作りましょう。

// src/i18n/ui.ts
export const ui = {
  'en': {
    'nav.home': 'Home',
    'nav.about': 'About',
    'btn.readMore': 'Read More',
  },
  'ja': {
    'nav.home': 'ホーム',
    'nav.about': '概要',
    'btn.readMore': '続きを読む',
  },
  'zh-cn': {
    'nav.home': '首页',
    'nav.about': '关于',
    'btn.readMore': '阅读更多',
  },
} as const;

ヘルパー関数を作成:

// src/i18n/utils.ts
import { ui } from './ui';

export function getLangFromUrl(url: URL) {
  const [, lang] = url.pathname.split('/');
  if (lang in ui) return lang as keyof typeof ui;
  return 'ja'; // デフォルト言語
}

export function useTranslations(lang: keyof typeof ui) {
  return function t(key: keyof typeof ui[typeof lang]) {
    return ui[lang][key] || ui['ja'][key];
  }
}

コンポーネントでの使用:

---
import { getLangFromUrl, useTranslations } from '@/i18n/utils';
const lang = getLangFromUrl(Astro.url);
const t = useTranslations(lang);
---
<nav>
  <a href="/">{t('nav.home')}</a> <!-- "ホーム" or "Home" -->
</nav>

言語切り替え機能の実装(完全コード)

ここが最難関だと思われがちですが、Astroのヘルパー関数を使えば簡単です。

便利なヘルパー関数

getRelativeLocaleUrl(locale, path): 指定した言語での相対パスを生成します。
例:getRelativeLocaleUrl('en', 'about')/en/about

Astro.currentLocale: 現在のページの言語コードを取得します。

言語切り替えコンポーネント

コピペで使えるコンポーネントです。現在見ているページを維持したまま、言語だけ切り替えるリンクを生成します。

---
// src/components/LanguageSwitcher.astro
import { getRelativeLocaleUrl } from 'astro:i18n';

const locales = {
  'ja': '日本語',
  'en': 'English',
  'zh-cn': '简体中文',
};

const currentLang = Astro.currentLocale || 'ja';

// URLから言語プレフィックスを除去して、纯粋なパスを取得
const currentPath = Astro.url.pathname
  .replace(new RegExp(`^/${currentLang}/`), '/') // 先頭の言語パスを削除
  .replace(/^\//, ''); // 先頭のスラッシュを削除(getRelativeLocaleUrlがよしなに処理するため)

---

<div class="language-switcher">
  <button class="lang-button">
    {locales[currentLang]} ▼
  </button>
  <div class="lang-dropdown">
    {Object.entries(locales).map(([lang, label]) => {
      // 魔法のメソッド:現在のパスを維持したまま、指定言語のURLを生成
      const url = getRelativeLocaleUrl(lang, currentPath);
      return (
        <a
          href={url}
          class={lang === currentLang ? 'active' : ''}
        >
          {label}
        </a>
      );
    })}
  </div>
</div>

<style>
  /* スタイルは適宜調整してください */
  .language-switcher { position: relative; display: inline-block; }
  .lang-dropdown { display: none; position: absolute; background: white; border: 1px solid #ccc; }
  .language-switcher:hover .lang-dropdown { display: block; }
  .active { font-weight: bold; color: blue; }
</style>

これをヘッダーなどに配置すれば、マルチリンガルサイトの完成です!

ユーザーの使用言語自動検出(オプション)

初めて訪れたユーザーを、ブラウザの言語設定に合わせてリダイレクトさせたい場合、Middlewareを使います。

// src/middleware.ts
import { defineMiddleware } from 'astro:middleware';

export const onRequest = defineMiddleware((context, next) => {
  const url = context.url;
  const currentLocale = context.currentLocale;
  const preferredLocale = context.preferredLocale; // ブラウザの言語設定

  // トップページへのアクセスで、かつブラウザ言語が現在の表示言語と異なる場合
  if (url.pathname === '/' && preferredLocale && preferredLocale !== currentLocale) {
    return context.redirect(`/${preferredLocale}/`);
  }

  return next();
});

※Cookieを使ってユーザーの選択を保存するロジックを追加すると、より親切です(強制リダイレクトループを防ぐため)。

SEO対策(hreflangタグ)

Googleなどの検索エンジンに「このページには英語版があるよ」と伝えるために、hreflang タグの設定は必須です。src/layouts/Layout.astro<head> に以下を追加します。

---
import { getAbsoluteLocaleUrl } from 'astro:i18n';

const locales = ['ja', 'en', 'zh-cn'];
// パスから言語部分を取り除く処理(簡易版)
const currentPath = Astro.url.pathname
  .replace(/^\/(en|zh-cn|ja)\//, '')
  .replace(/^\//, '');
---

<head>
  <!-- 各言語版のURLを出力 -->
  {locales.map((lang) => (
    <link
      rel="alternate"
      hreflang={lang}
      href={getAbsoluteLocaleUrl(lang, currentPath)}
    />
  ))}
  
  <!-- デフォルト言語(x-default)の指定 -->
  <link
    rel="alternate"
    hreflang="x-default"
    href={getAbsoluteLocaleUrl('ja', currentPath)}
  />
  
  <link rel="canonical" href={getAbsoluteLocaleUrl(Astro.currentLocale, currentPath)} />
</head>

結論

Astroのi18n機能は、設定ファイル、フォルダ構成、ヘルパー関数の3点セットで成り立っています。

  1. 設定: astro.config.mjs で言語を定義。
  2. 構成: 言語別フォルダか Content Collections で管理。
  3. 実装: getRelativeLocaleUrl でリンク生成。

これだけで、SEOにも強く、管理しやすい多言語サイトが作れます。もう「多言語対応」のタスクに怯える必要はありません。
ぜひあなたのブログも世界に発信してみてください!

FAQ

設定完了までどのくらいかかりますか?
基本設定(astro.config.mjs)だけなら5分〜10分。言語切り替え機能やSEO設定を含めても30分程度で実装可能です。
URLのプレフィックス設定はどうすべき?
個人のブログや一般的なサイトなら `prefixDefaultLocale: false`(デフォルト言語はプレフィックスなし)が推奨です。URLが短くなりスッキリします。SEO要件が厳格な場合は `true` を検討してください。
翻訳テキストの管理はどうするのがベスト?
記事本文は `src/content/blog/{lang}/` のようにContent Collectionsで管理し、UIの文言は `src/i18n/ui.ts` のような辞書ファイルで管理するのが、Astroにおけるベストプラクティスです。

3 min read · 公開日: 2025年12月2日 · 更新日: 2026年1月22日

コメント

GitHubアカウントでログインしてコメントできます

関連記事