【Strapi 系列之三】中英双语怎么做?Strapi i18n 从配置到 API 取数

这篇是 Strapi 系列第 3 篇,接着上一篇建模,直接解决实际开发中的双语问题:如何让同一篇文章拥有中英文版本,如何查询指定语言,以及英文内容缺失时怎么处理。

重点会放在 Strapi 5 的 i18n 配置、内容本地化、REST API 示例和前端取数,尤其会讲清楚一个容易踩坑的地方:语言回退并不等于 API 会自动把缺失的英文内容替换成中文。

中英双语怎么做?Strapi i18n 从配置到 API 取数

做官网时,很多开发者都会遇到一个问题:中文版已经上线了,现在要增加英文版,难道还要再建一套 Article、Project、Service 模型吗?

当然不必。

如果使用 Strapi 5,自带的 i18n(国际化)功能就能管理多语言内容。同一篇文章可以拥有中文和英文版本,两个版本可以分别编辑、发布,还能通过 API 按语言获取。

但这里有几个容易踩坑的地方:开启 i18n 不等于所有字段自动支持翻译,创建英文版本不等于自动翻译,而且指定英文查询也不意味着缺失内容会自动回退到中文。

这篇文章以设计公司官网为例,从配置到 API,一步步搭建中英双语内容系统。

一、先理解 Strapi i18n 的工作方式

假设我们有一篇中文文章:

  • 标题:如何设计一个好网站

  • Slug:how-to-design-a-website

  • 语言:中文

  • 状态:已发布

现在需要增加英文版本:

  • 标题:How to Design a Great Website

  • Slug:how-to-design-a-website

  • 语言:English

  • 状态:已发布

在 Strapi 中,这两个版本属于同一个内容的不同语言版本,而不是必须手动维护两套独立模型。

每个版本可以有自己的标题、正文、Slug 和发布状态。它们也可以根据实际业务拥有不同的图片、模块和关联内容。

注意:i18n 负责管理多语言内容,不负责自动翻译所有文本。翻译工作仍然需要人工完成,或者接入合适的翻译服务。

二、第一步:开启 i18n,添加中文和英文

Strapi 5 已经将 i18n 集成到核心功能中,通常不需要像旧版 Strapi 4 那样单独安装 @strapi/plugin-i18n。

1. 添加语言

进入 Strapi 管理后台,打开:

Settings → Global Settings → Internationalization

在这里添加需要支持的语言。

例如:

语言 Locale Code 用途
English en 英文站
简体中文 zh-Hans 中文站

Locale Code 是语言版本的标识,API 查询时会用到它。

这里有个小细节:不要想当然地把中文写成 zh-CN。Strapi 使用的是你实际添加的 Locale Code,因此后续所有 API 参数都应该以后台显示的代码为准。

如果你希望中文成为默认语言,也可以在国际化设置中调整默认 Locale。

默认语言会影响未指定 locale 时的 API 查询结果,因此最好在项目初期就确定好。

2. 为内容类型开启国际化

添加语言只是第一步,还需要为需要翻译的内容类型开启 i18n。

进入:

Content-Type Builder → Article → Advanced Settings

找到 Internationalization 相关设置,启用该内容类型的国际化能力。

保存后,Article 就可以管理不同语言版本。

同样的操作可以应用到 Project、Service、Page 等内容类型。

不过,并不是每个内容类型都必须开启国际化。

例如:

  • Article:通常需要多语言。

  • Project:如果案例要面向海外用户,通常需要多语言。

  • Site Setting:是否多语言,要看配置内容和业务需求。

  • Form Submission:通常不需要多语言内容版本,可以通过前端界面语言处理提示文本。

建议只给真正需要翻译的内容类型开启 i18n,不要为了"以后可能用到"就全部打开。

三、第二步:字段是否都需要翻译?

这是建模时非常重要的一步。

Strapi 允许在内容类型层面启用 i18n,也允许针对字段配置是否支持本地化。

例如,Article 可以设计成这样:

字段 示例 是否需要翻译
title 文章标题 是
slug URL 标识 通常是
description 摘要 是
content 正文 是
cover 封面图片 视业务而定
seoTitle SEO 标题 是
seoDescription SEO 描述 是

字段是否支持本地化,应根据业务含义决定。

例如,一篇文章的标题和正文显然需要翻译;但某些内部统计标识、与语言无关的业务编码,可能不需要单独维护语言版本。

对于 Component 和 Dynamic Zone,也要结合页面内容考虑。不同语言版本可以拥有不同的组件内容,Dynamic Zone 的模块组合甚至可以不同。

例如,中文首页包含四个模块,而英文首页只展示三个模块。这类需求可以由 i18n 配合 Dynamic Zone 实现,不必强制两种语言的页面结构完全一致。

需要特别注意:字段本地化与内容类型本地化不是一回事。开启内容类型国际化之后,还要检查需要独立翻译的字段是否允许本地化。对于不需要翻译的字段,则应根据实际场景决定是否共享。

四、第三步:在后台创建英文版本

配置完成后,进入 Content Manager,打开一篇已经创建的中文文章。

启用 i18n 后,编辑界面会出现语言选择入口。

选择 English,接下来就可以创建或编辑该文章的英文版本。

通常的工作流程是:

  1. 先创建中文文章。

  2. 在语言选择入口中切换到英文。

  3. 填写英文标题、摘要和正文。

  4. 检查英文图片、关联内容和 SEO 字段。

  5. 保存并发布英文版本。

Strapi 还提供从其他语言填充内容的功能,可以减少重复填写字段的工作量。不过,填充内容不代表完成翻译,仍然需要检查文本、链接和图片。

中文发布了,英文就自动发布吗?

不要这样假设。

不同语言版本可以分别管理草稿和发布状态。中文已经发布,并不代表英文版本也已发布。

例如:

语言版本 状态
中文 Published
英文 Draft

这种情况下,前端查询英文已发布内容时,不能因为中文版本已发布,就认为英文内容也已经可用。

正式上线前,最好把中文和英文分别纳入内容发布检查流程。

五、第四步:REST API 如何获取指定语言?

这是开发中最常用的部分。

假设 Strapi 地址为:

http://localhost:1337

Article 的 REST API 路径为:

/api/articles

1. 获取中文文章

如果中文 Locale Code 是 zh-Hans:

GET http://localhost:1337/api/articles?locale=zh-Hans

2. 获取英文文章

GET http://localhost:1337/api/articles?locale=en

关键就在于 locale 参数。

在 Strapi 5 中,REST API 使用 locale 指定内容语言。如果不传这个参数,API 会使用当前配置的默认语言。

因此,开发时最好显式指定语言,不要依赖默认值来判断当前页面应该显示什么内容。

3. 获取指定语言的某篇文章

假设文章的 Slug 是 how-to-design-a-website:

复制代码
GET /api/articles?locale=en&filters[slug][$eq]=how-to-design-a-website

如果还需要封面图片,可以添加 populate 参数:

复制代码
GET /api/articles?locale=en&filters[slug][$eq]=how-to-design-a-website&populate[cover]=true

上面示例中的 cover 必须与实际模型的媒体字段名一致。

另外,如果你使用 URL 编码工具构建查询参数,可以避免方括号、空格等字符造成的 URL 问题。

4. 只获取需要的字段

如果前端列表只需要标题、Slug 和摘要,可以使用 fields 精简返回内容:

复制代码
GET /api/articles?locale=en&fields[0]=title&fields[1]=slug&fields[2]=description

这样可以减少不必要的数据传输。

需要记住:fields 适合选择普通字段;媒体、Relation、Component 和 Dynamic Zone 等关联数据通常需要通过 populate 获取。

六、第五步:前端怎么封装多语言请求?

如果每个页面都手动拼接 locale,项目变大后很容易出现遗漏。

更好的做法是封装统一的 API 请求函数。

下面以 Next.js 为例:

复制代码
const STRAPI_URL =
  process.env.NEXT_PUBLIC_STRAPI_URL ??
  "http://localhost:1337";

type Locale = "en" | "zh-Hans";

export async function getArticles(locale: Locale) {
  const params = new URLSearchParams();

  params.set("locale", locale);
  params.set("populate[cover]", "true");

  const response = await fetch(
    `${STRAPI_URL}/api/articles?${params.toString()}`
  );

  if (!response.ok) {
    throw new Error("Failed to fetch articles");
  }

  return response.json();
}

页面调用时:

复制代码
const result = await getArticles("zh-Hans");

英文页面则调用:

复制代码
const result = await getArticles("en");

这样做有三个好处:

  • 所有页面统一使用 Locale Code。

  • 后续增加日语、韩语时,只需扩展语言类型和配置。

  • API 查询参数集中管理,减少拼接错误。

生产环境中,还需要根据你的 Strapi 访问策略配置合适的鉴权方式。不要把具有写权限的 API Token 暴露给浏览器;如果接口需要私密凭证,可以由服务端调用 Strapi。

七、第六步:语言回退怎么设计?

假设用户打开英文页面,但某篇文章还没有英文翻译。

这时应该显示什么?

常见方案有三种。

方案 A:缺失就不展示

英文页面只展示已经发布英文版本的内容。

优点是语言一致,不会突然出现中文内容。适合对国际化质量要求较高的官网。

方案 B:前端主动回退到默认语言

先请求英文内容,如果没有找到,再请求中文内容。

伪代码如下:

复制代码
async function getArticle(locale: string, slug: string) {
  const article = await findArticle(locale, slug);

  if (article) {
    return article;
  }

  if (locale !== "zh-Hans") {
    return findArticle("zh-Hans", slug);
  }

  return null;
}

这里的 findArticle() 代表你自己封装的查询函数,需要按项目实际 API 实现。

这种方式可以避免页面完全空白,但需要注意:回退后返回的内容可能是中文,页面语言、标题和 SEO 信息也应同步考虑。

方案 C:使用备用内容或提示信息

如果英文版本不存在,可以展示备用文章、相关内容,或者提示"英文版本正在准备中"。

这种方式适合新闻、产品说明和内容型网站,可以让用户明确知道当前语言版本的状态。

重点:Strapi 的 locale****参数用于筛选指定语言的内容,不是一个自动翻译或自动回退开关。 如果需要语言回退,应在前端或后端明确实现,并处理好发布状态。

八、上线前,检查这几个问题

  • 是否已经添加所有需要的 Locale Code?

  • 需要翻译的内容类型是否启用了 i18n?

  • 标题、正文、SEO 字段是否允许本地化?

  • 中文和英文是否分别完成发布?

  • 所有 API 请求是否明确指定 locale?

  • 封面图片、Relation 和 Dynamic Zone 是否按实际需求正确返回?

  • 缺失翻译时,是否定义了明确的回退策略?

  • 新增语言后,URL、缓存和 SEO 是否也同步支持?

尤其要注意 URL 设计。例如,可以采用 /zh-Hans/articles/... 和 /en/articles/... 这样的路径,让用户和搜索引擎都能清楚识别页面语言。前端还应根据真实存在的语言版本生成对应的语言切换链接,而不是简单地替换 URL 前缀。

写在最后

Strapi i18n 的核心思路并不复杂:先配置语言,再为需要翻译的内容类型和字段开启本地化,最后通过 locale 参数获取对应语言的数据。

真正容易出问题的地方,是默认语言、字段本地化、独立发布,以及缺失翻译时的回退逻辑。

下一篇,我们继续进入 API 和权限部分:RBAC + REST API 速查表:权限配错全站裸奔。

相关推荐
Jack N7 天前
【Strapi 系列之二】Collection / Single / Component 怎么选?Strapi 字段大全一篇讲透
前端·javascript·strapi
a3158238061 年前
Linux部署Python Django工程和Node工程,使用宝塔面板
linux·服务器·python·django·node·strapi·宝塔面板
cuiyuchen1112 年前
通过strApi5的导入导出功能 把sqlite数据转移到mysql
strapi
窥见漫天星光-莹2 年前
Certificate has expired(npm 安装strapi)
npm·strapi
WEB前端圈3 年前
【strapi系列】strapi在postman中如何调试public和认证用户Authorization的接口
测试工具·postman·strapi·无头cms