# 多语言
> 按语言组织内容与翻译表，生成多语言站点与语言切换入口。
- 官方英文原文：https://gohugo.io/content-management/multilingual/
- 本页规范地址：https://hugozh.cn/content-management/multilingual/
- 最近更新：2026-10-02
- 最后提交：912b1d3 chore(site): 添加 static/CNAME（hugozh.cn），供 GitHub Pages 等平台绑定自定义域名
- 站点：Hugo 中文文档（https://hugozh.cn/）· 社区维护的非官方中文翻译，如有出入以官方英文原文为准

---
## 配置

多语言站点的语言清单与默认语言都在项目配置中声明。以下是基础设置：

```toml
defaultContentLanguage = "en"
defaultContentLanguageInSubdir = false
disableDefaultLanguageRedirect = false
disableLanguages = []
```

`defaultContentLanguage` 是项目的默认语言，写法遵循 RFC 5646；定义了一种或多种语言后，该值必须与某个已定义的语言键匹配。

`defaultContentLanguageInSubdir` 决定是否把默认语言的内容也输出到同名子目录中，默认值为 `false`。

`disableDefaultLanguageRedirect` 用于关闭默认语言的重定向别名，默认值为 `false`。当 `defaultContentLanguageInSubdir` 为 `true` 时，它会阻止根目录跳转到语言子目录；为 `false` 时，它会阻止语言子目录跳转回根目录。

`disableLanguages` 是一个语言键切片，用于在构建时停用这些语言。虽然可用，但更推荐在每个语言下使用 `disabled` 键。完整的语言设置说明见 [配置](/configuration/)。

## 翻译内容

管理内容翻译有两种方式，两者都会为每个页面指定语言，并把它与对应的翻译页面互相链接。

### 按文件名翻译

以两个文件为例：

```text
content/about.en.md
content/about.fr.md
```

第一个文件被指派为英语并与第二个链接，第二个被指派为法语并与第一个链接。语言取自文件名后缀中的语言代码；只要路径与文件基名相同，这些内容就会作为彼此的翻译页面链接起来。

> [!NOTE]
> 文件名中的语言代码必须小写，例如使用 `about.en-us.md`，而不是 `about.en-US.md`。

> [!NOTE]
> 如果文件没有语言代码，它会被指派为默认语言。

### 按目录翻译

这种方式为每种语言使用不同的内容目录，通过 `contentDir` 参数设置：

```toml
[languages.en]
  contentDir = "content/english"
  label = "English"
  weight = 10
[languages.fr]
  contentDir = "content/french"
  label = "Français"
  weight = 20
```

`contentDir` 的取值可以是任何合法路径，甚至可以是绝对路径，唯一的限制是各内容目录之间不能重叠。

配合上面的配置：

```text
content/english/about.md
content/french/about.md
```

第一个文件被指派为英语并与第二个链接，第二个被指派为法语并与第一个链接。语言由文件所在的 `content` 目录决定；只要相对于各自语言的内容目录而言路径与基名相同，这些内容就会作为翻译页面链接起来。

### 绕开默认链接规则

只要在 front matter 中设置相同的 `translationKey`，无论基名或位置如何，页面都会被链接为翻译页面。例如下面三个文件：

```text
content/about-us.en.md
content/om.nn.md
content/presentation/a-propos.fr.md
```

在三个页面中都写入以下 front matter，它们就会被视为彼此的翻译：

```toml
translationKey = "about"
```

### 本地化永久链接

由于链接依赖路径与文件名，所有翻译页面通常共享同一个 URL（只有语言子目录不同）。要本地化 URL，可在 front matter 中设置 `slug` 或 `url`。

例如法语翻译可以使用自己的本地化 slug：

```yaml
---
title: A Propos
slug: "a-propos"
---
```

最终 URL 为：

```text
content/about.md    -> https://example.org/about/
content/about.fr.md -> https://example.org/fr/a-propos/
```

两个页面仍然是彼此的翻译。

自 v0.167.0 起，在 `section`、分类法或术语页面上设置 `slug` 时，Hugo 会把该 slug 应用到其下所有页面的 URL。例如本地化 `products` section 及其子页面的地址：

```yaml
---
title: Produits
slug: "produits"
---
```

最终 URL 为：

```text
content/products/_index.fr.md             -> https://example.org/fr/produits/
content/products/electronics/_index.fr.md -> https://example.org/fr/produits/electronics/
content/products/electronics/tv.fr.md     -> https://example.org/fr/produits/electronics/tv/
```

`slug` 与 `url` 的详细行为见 [URL 管理](/content-management/urls/)。

### 页面包

为避免重复维护文件，每个页面包（page bundle）都会继承其翻译页面所在包的资源，内容文件（Markdown、HTML 等）除外。因此模板中可以直接访问所有关联包中的文件。

如果关联的多个包中存在基名相同的文件，只会保留其中的一个，选择顺序是：

- 优先使用当前语言包中的文件；
- 否则按语言 `weight` 的顺序，取最先找到的文件。

> [!NOTE]
> 页面包资源与内容文件遵循相同的语言指派逻辑，既可以按文件名区分（`image.jpg`、`image.fr.jpg`），也可以按目录区分（`english/about/header.jpg`、`french/about/header.jpg`）。

## 界面文案的翻译表

模板中的固定文案通过翻译表维护，放在 `i18n/` 目录下，每种语言一个文件：

```toml
# i18n/de.toml
products = "Produkte"
services = "Leistungen"
```

在模板中用翻译函数取值，键名即翻译表中的键：

```go-html-template
<a href="{{ .RelPermalink }}">{{ T "products" }}</a>
```

`i18n` 与 `T` 是同一个函数的两种写法，可以交替使用。翻译表还支持按数量区分单复数等形式，具体写法以当前版本的官方文档为准。

## 本地化

以下示例假定项目的主语言是英语，另有法语与德语翻译：

```toml
defaultContentLanguage = "en"

[languages.en]
  contentDir = "content/en"
  label = "English"
  weight = 1
[languages.fr]
  contentDir = "content/fr"
  label = "Français"
  weight = 2
[languages.de]
  contentDir = "content/de"
  label = "Deutsch"
  weight = 3
```

### 日期、货币、数字与百分比

日期用 `time.Format` 格式化，例如模板中写入 `{{ .Date | time.Format ":date_full" }}`，`2021-11-03T12:34:56+01:00` 会分别渲染为 Wednesday, November 3, 2021（英语）、mercredi 3 novembre 2021（法语）、Mittwoch, 3. November 2021（德语）。

货币、数字与百分比分别使用 `lang.FormatCurrency`、`lang.FormatNumber`、`lang.FormatPercent`。同一个数值 `512.5032` 在三种语言下的输出如下：

| 格式化方式 | English | Français | Deutsch |
| --- | --- | --- | --- |
| `FormatCurrency 2 "USD"` | $512.50 | 512,50 $US | 512,50 $ |
| `FormatNumber 2` | 512.50 | 512,50 | 512,50 |
| `FormatPercent 2` | 512.50% | 512,50 % | 512,50 % |

## 菜单

菜单项的本地化方式取决于菜单的定义位置：

- 使用 section 页面菜单**自动**定义菜单项时，必须借助翻译表来本地化每一项；
- 在 **front matter** 中定义菜单项时，菜单本身已经按语言区分；若其中的名称不够用，再用翻译表补充；
- 在**项目配置**中定义菜单项时，必须在每个语言键下分别创建菜单项；名称不够用时同样借助翻译表。

### 按语言定义菜单

既可以写在单个配置文件中，也可以使用配置目录结构。

#### 单个配置文件

条目较少时使用单个配置文件即可：

```toml
# 单个配置文件，按语言分别定义菜单项
[languages.de]
label = "Deutsch"
locale = "de-DE"
weight = 1
[[languages.de.menus.main]]
name = "Produkte"
pageRef = "/products"
weight = 10
[[languages.de.menus.main]]
name = "Leistungen"
pageRef = "/services"
weight = 20

[languages.en]
label = "English"
locale = "en-US"
weight = 2
[[languages.en.menus.main]]
name = "Products"
pageRef = "/products"
weight = 10
[[languages.en.menus.main]]
name = "Services"
pageRef = "/services"
weight = 20
```

#### 配置目录

菜单结构较复杂时，可以创建配置目录，把菜单项按语言拆成多个文件：

```text
config/
└── _default/
    ├── menus.de.toml
    ├── menus.en.toml
    └── hugo.toml
```

```toml
# config/_default/menus.de.toml
[[main]]
  name = "Produkte"
  pageRef = "/products"
  weight = 10
[[main]]
  name = "Leistungen"
  pageRef = "/services"
  weight = 20
```

```toml
# config/_default/menus.en.toml
[[main]]
  name = "Products"
  pageRef = "/products"
  weight = 10
[[main]]
  name = "Services"
  pageRef = "/services"
  weight = 20
```

### 使用翻译表

渲染菜单项文本时，示例菜单模板会查询当前语言的翻译表，为此需要使用菜单项的 `identifier`：

```go-html-template
{{ or (T .Identifier) .Name | safeHTML }}
```

如果翻译表不存在，或者翻译表中没有对应的 `identifier` 键，模板会回退到 `name`。`identifier` 的取值取决于菜单的定义方式：

- 使用 section 页面菜单**自动**定义时，`identifier` 是页面的 `.Section`；
- 在**项目配置**或 **front matter** 中定义时，需要为菜单项显式设置 `identifier`。

例如在项目配置中定义菜单项，并为每项指定 `identifier`：

```toml
# 项目配置
[[menus.main]]
identifier = "products"
name = "Products"
pageRef = "/products"
weight = 10

[[menus.main]]
identifier = "services"
name = "Services"
pageRef = "/services"
weight = 20
```

再在翻译表中建立同名的条目：

```toml
# i18n/de.toml
products = "Produkte"
services = "Leistungen"
```

## 缺失的翻译

如果某个字符串在当前语言下没有翻译，Hugo 会使用默认语言的值；若默认值也不存在，则显示空字符串。

翻译过程中，可视化地标出缺失项会很有帮助。配置项 `enableMissingTranslationPlaceholders` 会用占位符 `[i18n] identifier` 标记所有未翻译的字符串，其中 `identifier` 是缺失翻译的 id。

> [!NOTE]
> 开启该设置后生成的站点会带有这些占位符，通常不适合直接用于生产环境。

至于**内容**缺失翻译时的合并，请使用 `lang.Merge`。

要定位缺失的翻译字符串，可以在构建时加上 `--printI18nWarnings` 参数：

```sh
hugo build --printI18nWarnings | grep i18n
i18n|MISSING_TRANSLATION|en|wordCount
```

## 多语言主题支持

要让主题支持多语言模式，模板中的 URL 需要满足以下条件：

- 来自内建的 `.Permalink` 或 `.RelPermalink`；
- 或由 `urls.RelLangURL`、`urls.AbsLangURL` 函数构造，或者带上页面的 `LanguagePrefix`。

定义了多种语言时，`LanguagePrefix` 会返回 `/en`（或当前语言的对应前缀）；单语言站点中它是空字符串，因此不会产生副作用。

## 生成多语言内容

如果翻译内容放在同一目录中：

```sh
hugo new content post/test.en.md
hugo new content post/test.de.md
```

如果翻译内容分目录存放：

```sh
hugo new content content/en/post/test.md
hugo new content content/de/post/test.md
```

生成命令的完整参数见 [基础用法](/getting-started/basic-usage/)。

