在本教程中,你将构建示例国际化和本地化引擎(通常称为 i18n 和 l10n),提供站点内容,并根据访问者在全球的位置自动翻译内容。
本教程使用 Cloudflare Workers 运行时内置的 HTMLRewriter 类,允许在 Cloudflare 全球网络上解析和重写 HTML。这使开发者能够高效且透明地自定义 Workers 应用。
所有框架指南都假定你已具备 Git ↗ 的基础知识。如果你是 Git 新手,请参阅这份精简 Git 手册 ↗,了解如何在本地设置 Git。
如果你使用 SSH 克隆,则必须在每台用于向 GitHub 推送或拉取的计算机上生成 SSH 密钥 ↗。
更多信息请参阅 GitHub 文档 ↗和 Git 文档 ↗。
本教程设计为使用现有网站。为简化此过程,你将使用 HTML5 UP ↗ 的免费 HTML5 模板。以此网站为基础,你将使用 Workers 平台的 HTMLRewriter 功能叠加 i18n 层,根据用户语言自动翻译站点。
若要部署自己的站点版本,可在 GitHub ↗ 找到源代码。部署说明位于项目的 README 中。
使用 create-cloudflare CLI 创建新应用,这是用于创建和部署新应用到 Cloudflare 的 CLI。
npm create cloudflare@latest -- i18n-exampleyarn create cloudflare i18n-examplepnpm create cloudflare@latest i18n-example设置时,选择以下选项:
- 对于 What would you like to start with?,选择
Framework Starter。 - 对于 Which development framework do you want to use?,选择
React。 - 对于 Do you want to deploy your application?,选择
No。
新生成的 i18n-example 项目将包含两个文件夹:public 和 src,其中包含 React 应用的文件:
cd i18n-example
lspublic src package.json我们需要对生成的项目做一些调整,首先用 HTML5 UP 模板默认生成的 HTML 代码替换 public 目录内的内容,如演示截图所示:下载此项目的发行版 ↗(ZIP 文件),并将 public 文件夹复制到你的项目以开始。
接下来,让我们创建带有 index.js 文件的 functions 目录,应用逻辑将在此编写。
mkdir functions
cd functions
touch index.js此外,我们将移除 src/ 目录,因为其内容对本项目不必要。静态 HTML 更新后,你可以专注于 functions 文件夹中 index.js 的脚本。
Workers 运行时提供的 HTMLRewriter 类允许开发者解析 HTML 并编写 JavaScript 来查询和转换页面的每个元素。
本教程中的示例网站是位于 public 目录中的基本单页 HTML 项目。它包含文本为 Example Site 的 h1 元素和若干具有不同文本的 p 元素:
此页面的独特之处在于 HTML 中添加了 data 属性 ↗——此页面上多个元素定义的自定义属性。此页面上 h1 标签以及许多 p 标签上的 data-i18n-key 表示存在对应的国际化键,应用于查找此文本的翻译:
<!-- source clipped from i18n-example site -->
<div class="inner">
<h1 data-i18n-key="headline">Example Site</h1>
<p data-i18n-key="subtitle">This is my example site. Depending o...</p>
<p data-i18n-key="disclaimer">Disclaimer: the initial translations...</p>
</div>使用 HTMLRewriter,你将解析 ./public/index.html 页面内的 HTML。找到 data-i18n-key 属性时,应使用属性值从 strings 对象检索匹配的翻译。使用 HTMLRewriter,可以查询元素来完成查找 data 属性等任务。但是,顾名思义,你也可以通过获取翻译字符串并直接插入 HTML 来重写元素。
此项目的另一个功能基于传入请求上的 Accept-Language 标头。你可以为每个请求设置翻译语言,让世界各地的用户看到本地相关且翻译过的页面。
从 functions/index.js 文件开始。本教程中的应用完全在此文件中。
在此文件内,首先添加运行 Pages Function 的默认代码。
export function onRequest(context) {
return new Response("Hello, world!");
}代码的重要部分在 onRequest 函数中。要在站点上实现翻译,从 env.ASSETS.fetch(request) 获取 HTML 响应——这允许你从 Pages 项目获取静态资源并将其传递给新的 HTMLRewriter 实例。实例化 HTMLRewriter 时,可以使用 on 函数附加处理程序。对于本教程,你将使用 [data-i18n-key] 选择器(请参阅 HTMLRewriter 文档 了解更高级用法)定位所有具有 data-i18n-key 属性的元素,这意味着它们必须被翻译。任何匹配的元素都将传递给 ElementHandler 类的实例,其中包含翻译逻辑。创建 HTMLRewriter 实例后,transform 函数接受 response 并可以返回给客户端:
export async function onRequest(context) {
const { request, env } = context;
const response = await env.ASSETS.fetch(request);
return new HTMLRewriter()
.on("[data-i18n-key]", new ElementHandler(countryStrings))
.transform(response);
}ElementHandler 将接收 HTMLRewriter 实例解析的每个元素,由于表达性 API,你可以查询每个传入元素的信息。
在工作原理中,文档描述了 data-i18n-key,可用于查找网站用户界面对应翻译字符串的自定义 data 属性。在 ElementHandler 中,可以定义 element 函数,在解析每个元素时调用。在 element 函数内,可以使用 getAttribute 查询自定义 data 属性:
class ElementHandler {
element(element) {
const i18nKey = element.getAttribute("data-i18n-key");
}
}定义 i18nKey 后,可以使用它搜索对应的翻译字符串。现在将设置带有键值对的 strings 对象,对应 data-i18n-key 值。目前,你将定义单个示例字符串 headline,德语 string 为 "Beispielseite"("Example Site"),并在 element 函数中检索它:
const strings = {
headline: "Beispielseite",
};
class ElementHandler {
element(element) {
const i18nKey = element.getAttribute("data-i18n-key");
const string = strings[i18nKey];
}
}获取翻译 string 并使用 setInnerContent 函数插入原始元素:
const strings = {
headline: "Beispielseite",
};
class ElementHandler {
element(element) {
const i18nKey = element.getAttribute("data-i18n-key");
const string = strings[i18nKey];
if (string) {
element.setInnerContent(string);
}
}
}要确认一切看起来正常,使用 Wrangler 内置的预览功能。调用 wrangler pages dev ./public 打开项目的实时预览。每次代码更改后命令都会刷新。
你可以扩展此翻译功能,根据传入请求的 Accept-Language 标头提供特定国家/地区的翻译。通过获取此标头、解析它并将解析的语言传递给 ElementHandler,你可以检索用户母语中的翻译字符串,前提是它在 strings 中已定义。
要实现此功能:
- 更新
strings对象,添加第二层键值对,允许以strings[country][key]格式查找字符串。 - 将
countryStrings对象传递给ElementHandler,以便在解析过程中使用。 - 从传入请求获取
Accept-Language标头,解析它,并将解析的语言传递给ElementHandler。
要解析 Accept-Language 标头,安装 accept-language-parser ↗ npm 包:
npm i accept-language-parser导入代码后,使用包根据 Accept-Language 标头解析客户端最相关的语言,并将其传递给 ElementHandler。项目的最终代码,包含德国和日本的示例翻译(使用 Google Translate),如下:
import parser from "accept-language-parser";
// do not set to true in production!
const DEBUG = false;
const strings = {
de: {
title: "Beispielseite",
headline: "Beispielseite",
subtitle:
"Dies ist meine Beispielseite. Abhängig davon, wo auf der Welt Sie diese Site besuchen, wird dieser Text in die entsprechende Sprache übersetzt.",
disclaimer:
"Haftungsausschluss: Die anfänglichen Übersetzungen stammen von Google Translate, daher sind sie möglicherweise nicht perfekt!",
tutorial:
"Das Tutorial für dieses Projekt finden Sie in der Cloudflare Workers-Dokumentation.",
copyright: "Design von HTML5 UP.",
},
ja: {
title: "サンプルサイト",
headline: "サンプルサイト",
subtitle:
"これは私の例のサイトです。 このサイトにアクセスする世界の場所に応じて、このテキストは対応する言語に翻訳されます。",
disclaimer:
"免責事項:最初の翻訳はGoogle翻訳からのものですので、完璧ではないかもしれません!",
tutorial:
"Cloudflare Workersのドキュメントでこのプロジェクトのチュートリアルを見つけてください。",
copyright: "HTML5 UPによる設計。",
},
};
class ElementHandler {
constructor(countryStrings) {
this.countryStrings = countryStrings;
}
element(element) {
const i18nKey = element.getAttribute("data-i18n-key");
if (i18nKey) {
const translation = this.countryStrings[i18nKey];
if (translation) {
element.setInnerContent(translation);
}
}
}
}
export async function onRequest(context) {
const { request, env } = context;
try {
let options = {};
if (DEBUG) {
options = {
cacheControl: {
bypassCache: true,
},
};
}
const languageHeader = request.headers.get("Accept-Language");
const language = parser.pick(["de", "ja"], languageHeader);
const countryStrings = strings[language] || {};
const response = await env.ASSETS.fetch(request);
return new HTMLRewriter()
.on("[data-i18n-key]", new ElementHandler(countryStrings))
.transform(response);
} catch (e) {
if (DEBUG) {
return new Response(e.message || e.toString(), {
status: 404,
});
} else {
return env.ASSETS.fetch(request);
}
}
}基于 Cloudflare Pages 构建的 i18n 工具已完成,是时候将其部署到你的域名。
要将应用部署到 *.pages.dev 子域名,需要指定要提供的静态资源目录,在项目的 Wrangler 文件中配置 pages_build_output_dir 并将值设为 ./public:
{
"$schema": "./node_modules/wrangler/config-schema.json",
"name": "i18n-example",
"pages_build_output_dir": "./public",
// Set this to today's date
"compatibility_date": "2026-08-17"
}"$schema" = "./node_modules/wrangler/config-schema.json"
name = "i18n-example"
pages_build_output_dir = "./public"
# Set this to today's date
compatibility_date = "2026-08-17"接下来,需要在项目的 package.json 文件中配置部署脚本。添加值为 wrangler pages deploy 的 deploy 脚本:
"scripts": {
"dev": "wrangler pages dev",
"deploy": "wrangler pages deploy"
}使用 wrangler,通过 deploy 命令部署到 Cloudflare 网络:
npm run deploy
在本教程中,你使用 HTMLRewriter 构建并部署了 i18n 工具。要查看此应用的完整源代码,请参阅 GitHub 上的仓库 ↗。
若要开始构建自己的项目,请查看现有的快速入门模板列表。