跳转到内容
搜索文档

使用 HTMLRewriter 本地化网站

最后更新 查看 MarkdownAgent 设置

在本教程中,你将构建示例国际化和本地化引擎(通常称为 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-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
ls
public src package.json

我们需要对生成的项目做一些调整,首先用 HTML5 UP 模板默认生成的 HTML 代码替换 public 目录内的内容,如演示截图所示:下载此项目的发行版 ↗(ZIP 文件),并将 public 文件夹复制到你的项目以开始。

接下来,让我们创建带有 index.js 文件的 functions 目录,应用逻辑将在此编写。

mkdir functions
cd functions
touch index.js

此外,我们将移除 src/ 目录,因为其内容对本项目不必要。静态 HTML 更新后,你可以专注于 functions 文件夹中 index.js 的脚本。

理解 data-i18n-key

Workers 运行时提供的 HTMLRewriter 类允许开发者解析 HTML 并编写 JavaScript 来查询和转换页面的每个元素。

本教程中的示例网站是位于 public 目录中的基本单页 HTML 项目。它包含文本为 Example Site 的 h1 元素和若干具有不同文本的 p 元素:

Chrome DevTools 中显示的上述元素演示代码

此页面的独特之处在于 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 标头。你可以为每个请求设置翻译语言,让世界各地的用户看到本地相关且翻译过的页面。

使用 HTML Rewriter API

从 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);
}

转换 HTML

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 中已定义。

要实现此功能:

  1. 更新 strings 对象,添加第二层键值对,允许以 strings[country][key] 格式查找字符串。
  2. 将 countryStrings 对象传递给 ElementHandler,以便在解析过程中使用。
  3. 从传入请求获取 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 上的仓库 ↗。

若要开始构建自己的项目,请查看现有的快速入门模板列表。

这篇文档对您有帮助吗?