Cloudflare Workers and Website Localization (1/5)

Jakub Pomykała
Jakub Pomykała
Last updated: February 27, 20246 min read
Cloudflare Workers and Website Localization (1/5)

In this tutorial, we will show you how to use Cloudflare Workers to create a simple website localization feature, without the need to install any additional dependencies. We will localize a simple static HTML website hosted on GitHub Pages, but you can use the same approach to localize any website, including those built with popular frameworks like React, Angular, or Vue.js.

Let's get started!

Prerequisites

Before you begin, you will need the following:

How it works

Cloudflare Worker will act as a proxy between the user and the website. It will intercept incoming requests and serve the appropriate localized content based on the lang query parameter or use a default language if the lang query parameter is not present. In the further examples, will use the accept-language header to detect the user's preferred language if the lang query parameter is not present.

proxy

We will use the SimpleLocalize Translation Hosting to fetch the localized content for the website. SimpleLocalize provides a CDN for translations, so the localized content will be served quickly and efficiently.

Sample website

For the purpose of this tutorial, we will use a simple HTML website with a few strings. The website has a few strings that need to be localized, and three buttons to switch between languages.

Static HTML website hosted on GitHub Pages

I've added data-i18n-key attribute to the HTML elements that need to be localized, for example:

<!-- Before -->
<h3 class="mt-3">Hello World</h3>

<!-- After -->
<h3 class="mt-3" data-i18n-key="hello_world">Hello World</h3>

and buttons to switch between languages:

<a href="?lang=en" data-i18n-key="languages.english">English</a>
<a href="?lang=pl" data-i18n-key="languages.polish">Polish</a>
<a href="?lang=es" data-i18n-key="languages.spanish">Spanish</a>

The way how the keys are created is up to you. You can use any naming convention you like, but it's a good practice to use a hierarchical structure to keep the keys organized. You can learn more about the best practices for naming keys on our blog post What is a translation key?.

Setting up SimpleLocalize

I already have a SimpleLocalize project with translation keys from the sample website, and auto-translated content into Spanish and Polish.

SimpleLocalize project with translation keys

I've also published the translations to the CDN, so they are available at the following URLs:

  • https://cdn.simplelocalize.io/{your-project-token}/_latest/en
  • https://cdn.simplelocalize.io/{your-project-token}/_latest/es
  • https://cdn.simplelocalize.io/{your-project-token}/_latest/pl

Setting up Cloudflare Workers

First, we need to set up a Cloudflare Worker to handle the localization logic. You can create a new Worker by navigating to the Workers section in the Cloudflare dashboard and clicking on the "Create application" button in the "Workers & Pages" section.

Create Cloudflare Worker

Next, you can paste the following code into the Worker editor and replace the placeholders with your project token from SimpleLocalize, default language, and website URL. The addEventListener function is used to intercept incoming requests.

const DEBUG = true; // Set to true to bypass cache
const DEFAULT_LANGUAGE = "en"; // Replace with your default language
const PROJECT_TOKEN = "YOUR-PROJECT-TOKEN"; // Replace with your project token
const ENVIRONMENT = "_latest"; // Replace with your environment name
const BASE_URL = "https://example.com"; // Replace with your website URL

addEventListener('fetch', event => {
  event.respondWith(handleEvent(event));
});

Let's add two classes to the Worker code: ElementHandler and LangHandler.


class ElementHandler {
  constructor(countryStrings) {
    this.languageTranslations = countryStrings;
  }

  element(element) {
    const i18nKey = element.getAttribute('data-i18n-key');
    if (i18nKey) {
      const translation = this.languageTranslations[i18nKey];
      if (translation) {
        element.setInnerContent(translation);
      }
    }
  }
}

class LangHandler {
  constructor(language) {
    this.language = language;
  }

  element(element) {
    element.setAttribute('lang', this.language);
  }
}

The ElementHandler class will be used to replace the content of HTML elements with the localized version, e.g.:

<!-- Before -->
<h3 class="mt-3" data-i18n-key="hello_world">Hello World</h3>

<!-- After -->
<h3 class="mt-3" data-i18n-key="hello_world">Hola Mundo</h3>

The LangHandler class will be used to set the lang attribute of the HTML element to the specified language.

<!-- Before -->
<html lang="en">

<!-- After -->
<html lang="es">

Next, we need to add the getTranslations function to fetch the localized content from SimpleLocalize based on the specified language key.


async function getTranslations(languageKey) {
  return await fetch(`https://cdn.simplelocalize.io/${PROJECT_TOKEN}/${ENVIRONMENT}/${languageKey}`)
    .then(response => response.json())
    .catch(() => ({}));
}

Finally, we need to add the handleEvent function to handle incoming requests and serve the appropriate localized content.

async function handleEvent(event) {
  const url = new URL(event.request.url);
  const pathname = url.pathname;
  const fetchOptions = {
    cacheControl: {
      bypassCache: DEBUG
    }
  };

  try {
    const languageKey = url.searchParams.get('lang') ?? DEFAULT_LANGUAGE;
    const translations = await getTranslations(languageKey);
    const originalContentAtPath = await fetch(BASE_URL + pathname, fetchOptions);
    return new HTMLRewriter()
      .on('[data-i18n-key]', new ElementHandler(translations))
      .on('[lang]', new LangHandler(languageKey))
      .transform(originalContentAtPath);
  } catch (e) {
    console.error(e);
    return new Response(`Not found`, {
      status: 404
    });
  }
}

You can find the complete code in the Cloudflare Workers and Website Localization repository.

Once you have added the code to the Worker editor, you can click on the "Save and Deploy" button to deploy the Worker to the Cloudflare network.

i18n Cloudflare Worker

Remember that you have to open the website from the Cloudflare domain to see the changes. If you open the website from the original domain, the Worker will not be triggered. You can also configure Worker routes to match your website domain and path in the "Routes" section of the Cloudflare dashboard. We will cover this in the following tutorials.

Testing the Cloudflare Worker

To test the Cloudflare Worker, you can add the lang query parameter to the URL of your website to specify the desired language. For example, you can add ?lang=es to the end of the URL to specify Spanish as the desired language.

Triggering CloudFlare Worker to serve localized website

Conclusion

In this tutorial, we showed you how to use Cloudflare Workers to create a simple website localization feature. We used the lang query parameter to specify the desired language to serve the appropriate version of the website, and SimpleLocalize as a single source of truth for translations.

In the following tutorials, we will show you how to:

  • extract all i18n keys from the website and upload them to SimpleLocalize,
  • use the accept-language header to detect the user's preferred language,
  • use the Cloudflare Workers KV to store and serve the localized content, and
  • configure Worker routes to match your website domain and path.

Stay tuned for the next tutorials!

Jakub Pomykała
Jakub Pomykała
Founder of SimpleLocalize