BetterLink Logo 比邻
切换语言
切换主题

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

Astro i18n 多语言配置指南

引言

你是不是也遇到过这种情况:花了两周时间做了个精美的 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>

这个组件的核心逻辑是:

  1. 获取当前语言和当前路径
  2. 为每种支持的语言生成对应的 URL
  3. 高亮显示当前语言
  4. 点击切换到对应语言的页面

不知道你有没有注意到,我用了 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日

相关文章