Astro i18n 配置指南:30分钟实现多语言网站(含语言切换器)

引言
你是不是也遇到过这种情况:花了两周时间做了个精美的 Astro 博客,准备推向国际市场,结果发现多语言支持不会做?打开 astro.config.mjs 一看,locales、defaultLocale、prefixDefaultLocale… 这一堆配置项根本看不懂。想组织多语言内容吧,不知道该按页面分还是按语言分,Content Collections 又是个啥?最头疼的是语言切换器,完全不知道从何下手。
说实话,我第一次配 Astro i18n 的时候,花了整整一下午研究 prefixDefaultLocale 到底该选 true 还是 false。后来才发现,其实 Astro 的 i18n 配置没那么复杂,只是官方文档写得太技术化了。
这篇文章我会用最直白的语言,带你走完整个 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: {
// 告诉 Astro 你的网站支持哪些语言
locales: ['en', 'zh-cn', 'ja'],
// 默认语言(必须是 locales 里的某一个)
defaultLocale: 'en',
// 是否给默认语言加路径前缀
prefixDefaultLocale: false,
}
});咱们一个个看这些配置项:
locales - 就是你网站支持的所有语言。用标准的语言代码就行,比如 'en'(英文)、'zh-cn'(简体中文)、'ja'(日文)。如果你要做更细的语言区分,也可以写成 'en-US'、'en-GB' 这样。
defaultLocale - 默认语言,访客第一次来你网站时看到的语言。这个值必须是 locales 数组里的某一个,不然 Astro 会报错。
prefixDefaultLocale - 这是个我纠结了很久的配置。设置成 false 的话,默认语言的 URL 不带语言前缀(比如 /about),其他语言才带(比如 /zh-cn/about、/ja/about)。设置成 true 的话,所有语言都带前缀(/en/about、/zh-cn/about)。
大多数情况下用 false 就够了,这样默认语言的 URL 更简洁。如果你特别在意 URL 统一性,或者有 SEO 方面的特殊需求,可以考虑 true。
三种路由策略对比
Astro 的 i18n 路由其实有三种策略,咱们用表格对比一下,看看哪种适合你:
| 策略 | 配置方式 | 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 其实给了你两条路,看你习惯哪种。
方案 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 太繁琐,可以试试动态路由。只需要一个文件,就能处理所有语言:
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 的动态路由逻辑。如果你是 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 文本,比如导航栏、按钮、表单标签这些。我推荐创建一个翻译字典:
// 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>这套方案简单实用,翻译都集中在一个文件里,改起来很方便。当然,如果你的翻译文本特别多,也可以拆成多个 JSON 文件按模块管理。
实现语言切换器(含完整代码)
前面配置和内容组织都搞定了,现在到最关键的环节 —— 语言切换器。说实话这是我当初最头疼的部分,研究了好几个网站的实现才弄明白。不过理解了 Astro 的 helper 函数之后,其实挺简单的。
理解 Astro i18n helper 函数
Astro 提供了几个超好用的函数,专门用来处理多语言 URL。咱们先认识一下它们:
getRelativeLocaleUrl(locale, path) - 获取某个语言的相对路径。
import { getRelativeLocaleUrl } from 'astro:i18n';
// 获取英文版关于页面的 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) 来生成 URL。这样就能确保切换语言后,用户还停留在同一个页面的不同语言版本,而不是跳回首页。
浏览器语言检测(可选但推荐)
有时候你希望用户第一次访问时,自动跳转到他浏览器设置的语言。这个可以用 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 记住用户的选择:
// 改进版 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。如果你的项目是静态网站(默认模式),就用不了这个功能了。
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>
<!-- 为每种语言添加 hreflang 标签 -->
{locales.map((lang) => (
<link
rel="alternate"
hreflang={lang}
href={getAbsoluteLocaleUrl(lang, currentPath)}
/>
))}
<!-- 添加默认语言标签 -->
<link
rel="alternate"
hreflang="x-default"
href={getAbsoluteLocaleUrl('en', currentPath)}
/>
<!-- 本地化 meta 描述 -->
<meta name="description" content={description[currentLang]} />
<!-- 规范 URL -->
<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 这些 meta 信息也翻译成对应的语言:
---
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 配置流程:
第一步:配置 astro.config.mjs(5分钟)
- 设置
locales数组(支持的语言) - 设置
defaultLocale(默认语言) - 根据需求选择
prefixDefaultLocale策略
第二步:组织多语言内容(选择适合自己的方案)
- 新手推荐:按语言分文件夹
- 进阶推荐:动态路由 + Content Collections
- 别忘了配置 UI 翻译字典
第三步:实现语言切换器(复制粘贴代码就能用)
- 使用
getRelativeLocaleUrl生成 URL - 用
Astro.currentLocale获取当前语言 - 可选:添加浏览器语言检测和 Cookie 记忆功能
用了一段时间 Astro i18n,我发现它真的挺方便。配置简单,helper 函数好用,性能也不错(所有语言的路由都会在构建时预生成)。如果你的网站需要支持多语言,Astro 的内置方案绝对值得一试。
现在就动手给你的 Astro 网站加上多语言支持吧!遇到问题的话,可以翻翻 Astro 官方 i18n 文档,那里有更详细的 API 说明。
有什么实践经验或踩过的坑,欢迎在评论区分享,咱们一起交流学习。
发布于: 2025年12月2日 · 修改于: 2025年12月4日


