コンテンツにスキップ

HTMLRewriterを使ったウェブサイトのローカライズ

Last reviewed: 5 months ago

このチュートリアルでは、アプリケーションのための国際化およびローカライズエンジン(一般にi18nおよびl10nと呼ばれる)を構築し、サイトのコンテンツを提供し、訪問者の世界の位置に基づいてコンテンツを自動的に翻訳します。

このチュートリアルでは、Cloudflare Workersランタイムに組み込まれたHTMLRewriterクラスを使用します。これにより、Cloudflareのグローバルネットワーク上でHTMLを解析および書き換えることができます。これにより、開発者はWorkersアプリケーションを効率的かつ透明にカスタマイズすることができます。

日本語、ドイツ語、英語に成功裏にローカライズされたサイトの例


Before you continue

All of the framework guides assume you already have a fundamental understanding of Git ↗. If you are new to Git, refer to this summarized Git handbook ↗ on how to set up Git on your local machine.

If you clone with SSH, you must generate SSH keys ↗ on each computer you use to push or pull from GitHub.

Refer to the GitHub documentation ↗ and Git documentation ↗ for more information.

前提条件

このチュートリアルは、既存のウェブサイトを使用するように設計されています。このプロセスを簡素化するために、HTML5 UP ↗からの無料のHTML5テンプレートを使用します。このウェブサイトをベースにして、WorkersプラットフォームのHTMLRewriter機能を使用してi18nレイヤーを重ね、ユーザーの言語に基づいてサイトを自動的に翻訳します。

自分のバージョンのサイトをデプロイしたい場合は、ソースをGitHub ↗で見つけることができます。このアプリケーションをデプロイする方法については、プロジェクトのREADMEに記載されています。

新しいアプリケーションを作成する

create-cloudflareを使用して新しいアプリケーションを作成します。これは、Cloudflareに新しいアプリケーションを作成およびデプロイするためのCLIです。

Terminal window
npm create cloudflare@latest -- i18n-example

セットアップのために、次のオプションを選択します:

  • 何から始めたいですか? では、Framework Starterを選択します。
  • どの開発フレームワークを使用しますか? では、Reactを選択します。
  • アプリケーションをデプロイしますか? では、Noを選択します。

新しく生成されたi18n-exampleプロジェクトには、publicとsrcの2つのフォルダーが含まれています。これらにはReactアプリケーションのファイルが含まれています:

Terminal window
cd i18n-example
ls
public src package.json

生成されたプロジェクトにいくつかの調整を行う必要があります。まず、publicディレクトリ内のコンテンツを、デモスクリーンショットで見られるHTML5 UPテンプレートのデフォルト生成HTMLコードに置き換えます。このプロジェクトのコードのリリース ↗(ZIPファイル)をダウンロードし、publicフォルダーを自分のプロジェクトにコピーして始めます。

次に、index.jsファイルを持つfunctionsディレクトリを作成します。ここにアプリケーションのロジックが書かれます。

Terminal window
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内にデータ属性 ↗が追加されていることです。これは、このページのいくつかの要素に定義されたカスタム属性です。このページのh1タグや多くのpタグにあるdata-i18n-keyは、対応する国際化キーが存在し、このテキストの翻訳を検索するために使用されるべきことを示しています:

<!-- i18n-exampleサイトからのソースクリップ -->
<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を使用すると、データ属性を見つけるなどのタスクを実行するために要素をクエリできます。ただし、名前が示すように、翻訳された文字列を取得して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のおかげで、各受信要素の情報をクエリできます。

How it worksでは、ドキュメントがdata-i18n-keyを説明しています。これは、ウェブサイトのユーザーインターフェースに対応する翻訳された文字列を見つけるために使用できるカスタムデータ属性です。ElementHandler内で、各要素が解析されると呼び出されるelement関数を定義できます。element関数内で、getAttributeを使用してカスタムデータ属性をクエリできます:

class ElementHandler {
element(element) {
const i18nKey = element.getAttribute("data-i18n-key");
}
}

i18nKeyが定義されたので、これを使用して対応する翻訳された文字列を検索できます。これから、data-i18n-keyの値に対応するキーと値のペアを持つstringsオブジェクトを設定します。今のところ、ドイツ語のstring、"Beispielseite"("Example Site")を持つ単一の例の文字列headlineを定義し、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オブジェクトを更新し、キーと値のペアの2層目を追加して、strings[country][key]形式で文字列を検索できるようにします。
  2. ElementHandlerにcountryStringsオブジェクトを渡し、解析プロセス中に使用できるようにします。
  3. 受信リクエストからAccept-Languageヘッダーを取得し、解析して、解析された言語をElementHandlerに渡します。

Accept-Languageヘッダーを解析するには、accept-language-parser ↗ npmパッケージをインストールします:

Terminal window
npm i accept-language-parser

コードにインポートしたら、Accept-Languageヘッダーに基づいてクライアントに最も関連性の高い言語を解析し、それをElementHandlerに渡します。プロジェクトの最終コードは、ドイツ語と日本語のサンプル翻訳を含む次のようになります(Google翻訳を使用):

import parser from "accept-language-parser";
// 本番環境ではtrueに設定しないでください!
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.tomlファイルでpages_build_output_dirを設定し、その値を./publicに設定する必要があります:

name = "i18n-example"
pages_build_output_dir = "./public"
compatibility_date = "2024-01-29"

次に、プロジェクトのpackage.jsonファイルにデプロイスクリプトを設定する必要があります。デプロイスクリプトをwrangler pages deployの値で追加します:

"scripts": {
"dev": "wrangler pages dev",
"deploy": "wrangler pages deploy"
}

wranglerを使用して、deployコマンドを使用してCloudflareのネットワークにデプロイします:

Terminal window
npm run deploy

日本語、ドイツ語、英語に成功裏にローカライズされたサイトの例

関連リソース

このチュートリアルでは、HTMLRewriterを使用してi18nツールを構築し、デプロイしました。このアプリケーションの完全なソースコードを確認するには、GitHubのリポジトリ ↗を参照してください。

自分のプロジェクトを構築し始めたい場合は、既存のクイックスタートテンプレートのリストを確認してください。