跳转到内容
搜索文档

使用 HTMLRewriter 本地化网站

最后更新 查看 MarkdownAgent 设置

在本教程中,你将构建示例国际化和本地化引擎(通常称为 i18nl10n),提供站点内容,并根据访问者在全球的位置自动翻译内容。

本教程使用 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 项目将包含两个文件夹:publicsrc,其中包含 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 Siteh1 元素和若干具有不同文本的 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 上的仓库

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

这篇文档对您有帮助吗?