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

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

できあがった Astro ブログを海外向けに公開しようとしたら、多言語対応のやり方が分からない——そんな場面はよくあります。locales、defaultLocale、prefixDefaultLocale といった設定項目、コンテンツの置き場所、Content Collections の意味、言語切り替えの実装……どこから手をつければいいか迷いがちです。

Astro の i18n は、公式ドキュメントが技術寄りに見えるだけで、実際はそれほど難しくありません。本記事では基本設定から言語切り替えまで、手順ごとに完全なコードを示し、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: 'en',

    // デフォルト言語の URL にプレフィックスを付けるか
    prefixDefaultLocale: false,
  }
});

各項目の意味は次のとおりです。

locales — サポートする言語の一覧です。'en'(英語)、'zh-cn'(簡体字中国語)、'ja'(日本語)のように標準の言語コードを使います。より細かく分ける場合は 'en-US''en-GB' のように指定できます。

defaultLocale — 初回訪問時に表示するデフォルト言語です。locales に含まれる値でなければ、Astro はエラーを出します。

prefixDefaultLocale — ここが悩みどころです。false ならデフォルト言語は /about のようにプレフィックスなし、他言語は /zh-cn/about/ja/about のようになります。true なら全言語にプレフィックスが付きます(/en/about/zh-cn/about など)。

多くのサイトでは false で十分です。デフォルト言語の URL が短く保てます。URL の形式を揃えたい、SEO 上の理由がある場合は true を検討してください。

3 つのルーティング戦略の比較

Astro の i18n ルーティングには、おおまかに 3 つの戦略があります。表で比較し、自分のプロジェクトに合うものを選びましょう。

戦略設定URL 例向いている場面メリット・デメリット
戦略 1:デフォルト言語にプレフィックスなしprefixDefaultLocale: false/about
/zh-cn/about
/ja/about
一般的なサイト(推奨)✅ デフォルト言語の URL が短い
❌ URL 形式が言語ごとに異なる
戦略 2:全言語にプレフィックスありprefixDefaultLocale: true/en/about
/zh-cn/about
/ja/about
URL 形式の統一や特殊な SEO 要件✅ URL 形式が統一
✅ 言語切り替えロジックが単純
❌ デフォルト言語の URL がやや長い
戦略 3:手動モードrouting: 'manual'完全カスタム複雑な多言語要件・ルートの完全制御✅ 柔軟性が高い
❌ 設定・実装が複雑

ブログやドキュメントサイトなら、戦略 1 で十分なことが多いです。URL をきれいに揃えたいなら戦略 2。ユーザー行動に応じて言語を切り替えたり、言語ごとにサブドメインを使ったりする場合は戦略 3 を検討します。

中英バイリンガルのブログなら、設定を少し変えるだけです。locales['zh-cn', 'en'] に、defaultLocale'zh-cn'(主要読者が中国語の場合)にすれば、基本設定は完了です。

Astro i18n 多言語対応の設定

30 分で Astro サイトの多言語設定を完了する手順

  1. 1

    Step 1: astro.config.mjs の設定

    astro.config.mjs に i18n を追加:
  2. 2

    Step 2: 多言語コンテンツの整理

    コンテンツの置き方を選ぶ:
  3. 3

    Step 3: UI 翻訳辞書の作成

    src/i18n/ui.ts に UI 文言の辞書を用意:
  4. 4

    Step 4: 言語切り替えの実装

    getRelativeLocaleUrl と Astro.currentLocale でコンポーネントを組み立てる:
  5. 5

    Step 5: SEO 最適化

    Layout に hreflang を追加し、sitemap を設定、meta を各言語にローカライズ

多言語コンテンツの整理(2 つの方法から選択)

設定が終わったら、コンテンツの置き方です。ファイルをどう分けるかでつまずく人は多いですが、Astro には主に 2 つのやり方があります。

方法 1:言語別フォルダ(初心者向け)

いちばん直感的な方法です。言語ごとにフォルダを分けます。

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

prefixDefaultLocale: false(デフォルト言語にプレフィックスなし)の場合、デフォルト言語のファイルは pages 直下に置き、他言語だけサブフォルダを作ります。

構造が分かりやすく、言語ごとにページを独立して編集できます。一方、ページ数と言語数が増えるとファイルが爆発します。20 ページ × 5 言語なら 100 ファイル——想像するだけでも大変です。

方法 2:動的ルート(上級者向け)

方法 1 が煩雑に感じたら、動的ルートが有効です。1 ファイルで全言語を扱えます。

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

[...slug].astrolang パラメータに応じてコンテンツを出し分けます。

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

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

const { lang, slug } = Astro.params;
// lang と slug に応じてコンテンツを読み込む
---

コードの再利用性が高く、保守コストは下がります。ただし Astro の動的ルートの理解が必要です。初心者はまず方法 1 で慣れ、慣れたら方法 2 に移行するのも手です。

Content Collections による多言語(ブログなら必須)

ページファイルの話のあと、ブログでは Content Collections が本題です。Astro が推奨するコンテンツ管理で、ブログやドキュメント向きです。

ディレクトリ例:

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

src/content/config.ts で schema を定義します。

// 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(),
    lang: z.enum(['en', 'zh-cn', 'ja']),  // 言語フィールド
  }),
});

export const collections = {
  blog: blogCollection,
};

ページ側では次のように、現在の言語の記事だけ取得します。

---
// src/pages/blog/index.astro
import { getCollection } from 'astro:content';

const currentLang = Astro.currentLocale;  // 現在の言語
const posts = await getCollection('blog', ({ data }) => {
  return data.lang === currentLang;
});
---

ユーザーが選んだ言語に応じて記事が切り替わります。Astro 公式ドキュメントもこの方式で多言語を管理しており、実績は十分です。

UI 翻訳ファイルの管理

記事以外に、ナビ・ボタン・フォームラベルなど固定 UI も翻訳が必要です。翻訳辞書を 1 か所にまとめるのがおすすめです。

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

ヘルパー関数を追加します。

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

// URL から現在の言語を取得
export function getLangFromUrl(url: URL) {
  const [, lang] = url.pathname.split('/');
  if (lang in ui) return lang as keyof typeof ui;
  return 'zh-cn';  // デフォルト言語
}

// 翻訳関数を返す
export function useTranslations(lang: keyof typeof ui) {
  return function t(key: keyof typeof ui[typeof lang]) {
    return ui[lang][key] || ui['zh-cn'][key];
  }
}

コンポーネントでの利用例:

---
// 任意のコンポーネント
import { getLangFromUrl, useTranslations } from '@/i18n/utils';

const lang = getLangFromUrl(Astro.url);
const t = useTranslations(lang);
---

<nav>
  <a href="/">{t('nav.home')}</a>
  <a href="/about">{t('nav.about')}</a>
  <a href="/blog">{t('nav.blog')}</a>
</nav>

翻訳が 1 ファイルに集約され、変更も楽です。文言が非常に多い場合は、モジュール単位で JSON に分割しても構いません。

言語切り替えの実装(完全なコード付き)

設定とコンテンツ整理が終わったら、いよいよ言語切り替えです。最初は難しく感じるかもしれませんが、Astro のヘルパーを理解すればシンプルです。

Astro i18n ヘルパー関数の理解

多言語 URL 用に、次の関数が用意されています。

getRelativeLocaleUrl(locale, path) — 指定言語の相対パスを返します。

import { getRelativeLocaleUrl } from 'astro:i18n';

// 英語版「about」ページの URL
const url = getRelativeLocaleUrl('en', 'about');
// 返り値:'/en/about' または '/about'(設定による)

getAbsoluteLocaleUrl(locale, path) — ドメイン付きの絶対 URL を返します。

import { getAbsoluteLocaleUrl } from 'astro:i18n';

const url = getAbsoluteLocaleUrl('en', 'about');
// 返り値:'https://example.com/en/about'

Astro.currentLocale — 現在ページの言語コードです。

---
const currentLang = Astro.currentLocale;
// 返り値:'zh-cn'、'en' など
---

Astro.preferredLocale — ブラウザの優先言語(サイトが対応している場合)です。

---
const browserLang = Astro.preferredLocale;
// 返り値:ブラウザ設定の言語(locales に含まれる場合)
---

これらが揃えば、言語切り替えコンポーネントを組み立てられます。

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

そのまま使えるシンプルな例です。

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

// 対応言語(本番では設定から読むのが望ましい)
const locales = {
  'zh-cn': '简体中文',
  'en': 'English',
  'ja': '日本語',
};

const currentLang = Astro.currentLocale || 'zh-cn';
const currentPath = Astro.url.pathname
  .replace(`/${currentLang}/`, '/')
  .replace(/^\//, '');
---

<div class="language-switcher">
  <button class="lang-button">
    {locales[currentLang]} ▼
  </button>
  <div class="lang-dropdown">
    {Object.entries(locales).map(([lang, label]) => {
      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-button {
    padding: 8px 16px;
    background: #f3f4f6;
    border: 1px solid #d1d5db;
    border-radius: 6px;
    cursor: pointer;
  }

  .lang-button:hover {
    background: #e5e7eb;
  }

  .lang-dropdown {
    display: none;
    position: absolute;
    top: 100%;
    right: 0;
    margin-top: 4px;
    background: white;
    border: 1px solid #d1d5db;
    border-radius: 6px;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
    min-width: 150px;
  }

  .language-switcher:hover .lang-dropdown {
    display: block;
  }

  .lang-dropdown a {
    display: block;
    padding: 10px 16px;
    color: #374151;
    text-decoration: none;
    transition: background 0.2s;
  }

  .lang-dropdown a:hover {
    background: #f3f4f6;
  }

  .lang-dropdown a.active {
    background: #dbeafe;
    color: #1e40af;
    font-weight: 500;
  }
</style>

<script>
  // モバイル:タップで開閉
  document.querySelector('.lang-button')?.addEventListener('click', (e) => {
    e.stopPropagation();
    const dropdown = document.querySelector('.lang-dropdown');
    dropdown?.classList.toggle('show');
  });

  document.addEventListener('click', () => {
    document.querySelector('.lang-dropdown')?.classList.remove('show');
  });
</script>

Layout やナビに読み込みます。

---
// src/layouts/Layout.astro
import LanguageSwitcher from '@/components/LanguageSwitcher.astro';
---

<header>
  <nav>
    <!-- 他のナビ項目 -->
    <LanguageSwitcher />
  </nav>
</header>

コンポーネントの流れは次のとおりです。

  1. 現在の言語とパスを取得
  2. 各言語の URL を生成
  3. 現在の言語をハイライト
  4. クリックで同じページの別言語版へ遷移

getRelativeLocaleUrl(lang, currentPath) を使うと、言語を切り替えても同じページの別言語版に留まり、トップに戻ることはありません。

ブラウザ言語の検出(任意・推奨)

初回訪問時にブラウザの言語へ自動遷移させるには、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();
});

毎回強制リダイレクトすると、手動で言語を変えたあとまた戻されてしまい、UX が悪化します。

Cookie でユーザー選択を記憶する方が安全です。

// 改善版 middleware
export const onRequest = defineMiddleware((context, next) => {
  const url = context.url;
  const currentLocale = context.currentLocale;
  const preferredLocale = context.preferredLocale;
  const savedLang = context.cookies.get('user-lang')?.value;

  if (savedLang && savedLang !== currentLocale && url.pathname === '/') {
    return context.redirect(`/${savedLang}/`);
  }

  if (!savedLang && url.pathname === '/' && preferredLocale && preferredLocale !== currentLocale) {
    context.cookies.set('user-lang', preferredLocale, {
      path: '/',
      maxAge: 31536000,  // 1 年
    });
    return context.redirect(`/${preferredLocale}/`);
  }

  return next();
});

言語切り替え時に Cookie も更新します。

<script>
  document.querySelectorAll('.lang-dropdown a').forEach((link) => {
    link.addEventListener('click', (e) => {
      const lang = e.target.getAttribute('data-lang');
      document.cookie = `user-lang=${lang}; path=/; max-age=31536000`;
    });
  });
</script>

次回訪問時は、保存した言語が優先されます。

応用テクニック(fallback・ドメインマッピング・SEO)

基本が終わったら、次の応用に進めます。シンプルなプロジェクトなら、必要になったときに戻って読めば十分です。

Fallback 戦略

翻訳が途中のサイトでは、未翻訳ページを別言語で表示したいことがあります。それが fallback です。

// astro.config.mjs
export default defineConfig({
  i18n: {
    locales: ['en', 'zh-cn', 'ja'],
    defaultLocale: 'en',
    fallback: {
      ja: 'en',      // 日本語がなければ英語
      'zh-cn': 'en', // 中国語がなければ英語
    },
  }
});

/ja/some-page が存在しない場合、Astro は /en/some-page の内容を表示し、404 を避けられます。段階的に翻訳を進めるサイトで特に有用です。

カスタムドメインマッピング

言語ごとに別ドメインを使う製品もあります。例:

  • 英語:example.com
  • 中国語:example.cn
  • 日本語:example.jp

Astro でも設定できますが、ドメインマッピングは SSR モードでのみ利用可能です。

// astro.config.mjs
export default defineConfig({
  output: 'server',  // SSR 必須
  adapter: node(),   // アダプターが必要
  i18n: {
    locales: ['en', 'zh-cn', 'ja'],
    defaultLocale: 'en',
    domains: {
      'zh-cn': 'https://example.cn',
      ja: 'https://example.jp',
    },
  }
});

中国語は example.cn、日本語は example.jp にデプロイされます。デフォルトの静的サイト(SSG)では使えません。

SEO 最適化の要点

多言語 SEO の要は、検索エンジンに「このページの他言語版はここ」と伝えることです。中心は hreflang タグです。

Astro は i18n SEO にも配慮しており、設定が正しければ多くが自動化されます。次は手動で押さえたい点です。

1. Layout に hreflang を追加

---
// src/layouts/Layout.astro
import { getAbsoluteLocaleUrl } from 'astro:i18n';

const locales = ['en', 'zh-cn', 'ja'];
const currentPath = Astro.url.pathname
  .replace(/^\/(en|zh-cn|ja)\//, '')
  .replace(/^\//, '');
---

<html>
<head>
  {locales.map((lang) => (
    <link
      rel="alternate"
      hreflang={lang}
      href={getAbsoluteLocaleUrl(lang, currentPath)}
    />
  ))}

  <link
    rel="alternate"
    hreflang="x-default"
    href={getAbsoluteLocaleUrl('en', currentPath)}
  />

  <meta name="description" content={description[currentLang]} />

  <link rel="canonical" href={getAbsoluteLocaleUrl(currentLang, currentPath)} />
</head>
</html>

2. sitemap.xml の多言語設定

@astrojs/sitemap を使う場合、言語ごとの sitemap が自動生成されます。site の指定が必要です。

// astro.config.mjs
import sitemap from '@astrojs/sitemap';

export default defineConfig({
  site: 'https://example.com',  // 必須
  integrations: [sitemap()],
});

3. meta 情報のローカライズ

titledescriptionkeywords も言語ごとに用意しましょう。

---
const meta = {
  'en': {
    title: 'Welcome to My Blog',
    description: 'A blog about web development',
  },
  'zh-cn': {
    title: '欢迎来到我的博客',
    description: '一个关于 Web 开发的博客',
  },
};

const currentLang = Astro.currentLocale || 'en';
---

<head>
  <title>{meta[currentLang].title}</title>
  <meta name="description" content={meta[currentLang].description} />
</head>

ここまで押さえれば、検索エンジンが各言語版を正しくインデックスしやすくなります。

まとめ

Astro i18n の流れを振り返ります。

ステップ 1:astro.config.mjs の設定(約 5 分)

  • locales に対応言語を列挙
  • defaultLocale を指定
  • prefixDefaultLocale の方針を決める

ステップ 2:多言語コンテンツの整理

  • 初心者:言語別フォルダ
  • 上級者:動的ルート + Content Collections
  • UI 翻訳辞書も忘れずに

ステップ 3:言語切り替えの実装

  • getRelativeLocaleUrl で URL を生成
  • Astro.currentLocale で現在言語を取得
  • 任意:ブラウザ言語検出と Cookie 記憶

実際に使ってみると、設定は軽く、ヘルパーも扱いやすく、全言語ルートはビルド時に静的生成されるためパフォーマンスも良好です。多言語が必要なら、Astro 組み込みの i18n は十分検討に値します。

さっそく Astro サイトに多言語を足してみてください。詰まったら Astro 公式 i18n ドキュメント で API の詳細を確認できます。

実践で得たコツやハマりどころがあれば、コメントで共有してください。一緒に学びましょう。

FAQ

Astro の i18n 設定にはどのくらい時間がかかりますか?
基本設定は 5〜10 分で済みます。
• astro.config.mjs で locales・defaultLocale・prefixDefaultLocale を設定

言語切り替えや SEO まで含めた多言語サイト全体でも、おおよそ 30 分です。
prefixDefaultLocale は true と false、どちらにすべきですか?
多くの場合は false で十分です。
• デフォルト言語の URL が短くなる(例:/about)

URL 形式を統一したい、特殊な SEO 要件がある場合は true(例:/en/about)を検討してください。
多言語コンテンツはどう整理すればよいですか?
主に 2 つの方法があります。

方法 1:言語別フォルダ(初心者向け)
• 構造が分かりやすいが、ファイル数は増える

方法 2:動的ルート(上級者向け)
• コードの再利用性が高く、保守コストが低い

ブログ記事は Content Collections で多言語管理するのがおすすめです。
言語切り替えはどう実装しますか?
Astro の getRelativeLocaleUrl で各言語の URL を生成し、Astro.currentLocale で現在の言語を取得します。

ブラウザ言語の検出や Cookie での記憶を組み合わせると、UX がさらに向上します。
多言語サイトの SEO はどう最適化しますか?
主な手順は次のとおりです。
• Layout に hreflang を追加し、各言語版を検索エンジンに通知
• sitemap プラグインで多言語 sitemap を自動生成
• title・description などの meta を各言語にローカライズ

Astro は i18n SEO にも配慮しており、多くの処理が自動化されます。

5分で読めます · 公開日: 2025年12月2日 · 更新日: 2026年6月8日

関連記事

コメント

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