这篇是 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,接下来就可以创建或编辑该文章的英文版本。
通常的工作流程是:
-
先创建中文文章。
-
在语言选择入口中切换到英文。
-
填写英文标题、摘要和正文。
-
检查英文图片、关联内容和 SEO 字段。
-
保存并发布英文版本。
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 速查表:权限配错全站裸奔。