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
Step 1: astro.config.mjs の設定
astro.config.mjs に i18n を追加: -
2
Step 2: 多言語コンテンツの整理
コンテンツの置き方を選ぶ: -
3
Step 3: UI 翻訳辞書の作成
src/i18n/ui.ts に UI 文言の辞書を用意: -
4
Step 4: 言語切り替えの実装
getRelativeLocaleUrl と Astro.currentLocale でコンポーネントを組み立てる: -
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].astro で lang パラメータに応じてコンテンツを出し分けます。
---
// 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>
コンポーネントの流れは次のとおりです。
- 現在の言語とパスを取得
- 各言語の URL を生成
- 現在の言語をハイライト
- クリックで同じページの別言語版へ遷移
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 情報のローカライズ
title、description、keywords も言語ごとに用意しましょう。
---
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 設定にはどのくらい時間がかかりますか?
• astro.config.mjs で locales・defaultLocale・prefixDefaultLocale を設定
言語切り替えや SEO まで含めた多言語サイト全体でも、おおよそ 30 分です。
prefixDefaultLocale は true と false、どちらにすべきですか?
• デフォルト言語の URL が短くなる(例:/about)
URL 形式を統一したい、特殊な SEO 要件がある場合は true(例:/en/about)を検討してください。
多言語コンテンツはどう整理すればよいですか?
方法 1:言語別フォルダ(初心者向け)
• 構造が分かりやすいが、ファイル数は増える
方法 2:動的ルート(上級者向け)
• コードの再利用性が高く、保守コストが低い
ブログ記事は Content Collections で多言語管理するのがおすすめです。
言語切り替えはどう実装しますか?
ブラウザ言語の検出や Cookie での記憶を組み合わせると、UX がさらに向上します。
多言語サイトの SEO はどう最適化しますか?
• Layout に hreflang を追加し、各言語版を検索エンジンに通知
• sitemap プラグインで多言語 sitemap を自動生成
• title・description などの meta を各言語にローカライズ
Astro は i18n SEO にも配慮しており、多くの処理が自動化されます。
5分で読めます · 公開日: 2025年12月2日 · 更新日: 2026年6月8日
Astro 完全ガイド
検索からこのページに来た場合は、前後の記事もあわせて読むと同じテーマの理解がかなり早く深まります。
前の記事
おすすめ Astro ブログテーマ 5 選|インストール・設定チュートリアル付き
ブログを素早く立ち上げたいけど、どのテーマを選べばいいか迷っている方へ。AstroPaper、Astro Air Blog など実際に試したおすすめテーマ 5 選と、詳しいインストール・設定手順、よくあるトラブルの解決法を紹介。30 分で個人ブログを完成できます。
第 5 / 18 記事
次の記事
Astro サイト SEO 完全ガイド:メタタグから検索順位向上まで
メタタグ、サイトマップ、robots.txt、JSON-LD 構造化データまで、Astro サイトの SEO 設定を手順どおりに解説。30 分でコア設定を完了し、Google 検索順位を上げるためのコピペ可能なコード例付き。
第 7 / 18 記事
関連記事
Astro とは?3 分でわかるゼロ JS・アイランドアーキテクチャ・コンテンツ優先の本当の意味
Astro とは?3 分でわかるゼロ JS・アイランドアーキテクチャ・コンテンツ優先の本当の意味
ゼロから Astro ブログを構築:1 時間でトップページからデプロイまで
ゼロから Astro ブログを構築:1 時間でトップページからデプロイまで
Astro Content Collections 完全ガイド:概念から Schema 検証の実践まで
コメント
GitHubアカウントでログインしてコメントできます