# 多语言

在 Astro 配置里设置 `i18n`，minidocs 就会按语言组织内容，为每种语言生成页面。这一页讲多语言站点和单语言站点的不同之处。

## 配置

```js
// astro.config.mjs
export default defineConfig({
  i18n: { locales: ['zh', 'en'], defaultLocale: 'zh' },
  integrations: [mdx(), minidocs()],
});
```

默认语言的地址不带语言段，比如 `/docs/`；其他语言带，比如 `/en/docs/`。想让默认语言也带上，设置 Astro 的 `routing: { prefixDefaultLocale: true }`。

`locales` 只支持字符串，`routing: 'manual'` 也还不支持。没有翻译的页面怎么显示，用下文 minidocs 的 `fallback` 声明，不要用 Astro 的 `i18n.fallback`。

## 内容

集合的第一级目录是语言：

```
src/content/docs/
├── zh/
│   ├── index.mdx
│   └── guides/sidebar.mdx
└── en/
    ├── index.mdx
    └── guides/sidebar.mdx
```

页面 id 不含语言，两种语言里的 `guides/sidebar.mdx` 是同一个页面 `guides/sidebar` 的两个版本。

## 路由和数据

和单语言站点相比，有三处不同：

- 路由文件前面多一层 `[...locale]`，比如 `src/pages/[...locale]/docs/[...slug].astro`，`page.params` 里多了 `locale`
- `defineTree` 返回每种语言一棵树，渲染时用 `tree[locale]`
- `toHref` 要传语言：`toHref('docs', 'guides/sidebar', locale)`

`listPages` 返回的每个页面都带着 `locale`，就是它所在的语言，布局里直接用：

```astro
---
const { id, locale } = Astro.props;
const nav = navState(tree[locale], id);
---

<html lang={locale}>
```

## 没有翻译的页面

页面还没有翻译时，可以先显示另一种语言的内容：

```js
minidocs({ fallback: { en: 'zh' } })
```

这样英文站上缺的页面会显示中文内容，地址仍然在英文站下，页头、侧边栏这些界面也还是英文。可以依次列出几种语言，按顺序找：`{ 'zh-tw': ['zh', 'en'] }`。

显示的是另一种语言的内容时，页面的 `isFallback` 是 `true`，`contentLocale` 是内容实际的语言。可以借此提示读者，并让搜索引擎指向原文：

```astro
---
const { id, isFallback, contentLocale } = Astro.props;
---

{isFallback && <link rel="canonical" href={toHref('docs', id, contentLocale)} />}
{isFallback && (
  <Callout.Root>
    This page is not translated yet. <a href={toHref('docs', id, contentLocale)}>View the original</a>
  </Callout.Root>
)}
```

没有声明 `fallback` 时，缺翻译的页面在那种语言里就不存在。`dir()` 会跳过它；`page()` 引用它时，构建会报错。

## 语言切换器

每个页面的 `alternates` 列出它存在的所有语言和对应的地址，包括当前语言：

```astro
---
const { locale, alternates } = Astro.props;
---

<nav aria-label="Language">
  {alternates.map((alt) => (
    <a href={alt.href} hreflang={alt.locale} aria-current={alt.locale === locale ? 'true' : undefined}>
      {alt.locale}
    </a>
  ))}
</nav>
```

同样的数据也可以写进 `<head>`：`<link rel="alternate" hreflang={alt.locale} href={alt.href} />`。

## 站点自己的页面

首页这类不来自集合的页面，也放在 `[...locale]` 下面，用 `toLocaleParam` 为每种语言生成一份：

```astro
---
// src/pages/[...locale]/index.astro
import { type Locale, toLocaleParam } from '@minidocs/astro/runtime';

const locales: Locale[] = ['zh', 'en'];

export function getStaticPaths() {
  return locales.map((locale) => ({ params: { locale: toLocaleParam(locale) }, props: { locale } }));
}
---
```

默认语言不带语言段时，`toLocaleParam` 返回 `undefined`，生成的就是 `/`。

404 页面放在 `src/pages/404.astro`，不放在 `[...locale]` 下面，因为托管平台通常只认站点根目录的 `404.html`。

## 正文里的链接

正文里链接到同一个语言目录里的文件，链接就留在这种语言里，没有翻译的页面也是如此，见[正文里的链接](/docs/guides/links.md#%E5%A4%9A%E8%AF%AD%E8%A8%80%E7%AB%99%E7%82%B9)。
