Use Express Middleware to Implement Locale-Specific URLs

Use Express Middleware to Implement Locale-Specific URLs

At TextPixie, we recognize that language translation is a global challenge that affects millions of people. To effectively address this issue, it is essential to provide our AI Translator to a diverse audience in multiple languages. As we expanded our service to support various languages, we encountered the challenge of implementing locale-specific URLs in a straightforward manner.

Unlike some frameworks that come with built-in internationalization (i18n) support, such as Next.js, our entire backend is built with Express. This meant we needed to devise a custom solution to manage locale-specific URLs efficiently. We came up with a simple solution using Express middleware to tackle this challenge, which we believe can benefit other developers facing similar issues in their multilingual applications.

The Challenge

When building our multilingual application, we needed a URL structure that could accommodate different locales while maintaining clean, manageable code. We wanted our URLs to look like this:

  • https://textpixie.com/ (English Homepage)
  • https://textpixie.com/zh-tw/ (Traditional Chinese Homepage)
  • https://textpixie.com/image-translator (English Image Translator)
  • https://textpixie.com/zh-tw/image-translator (Traditional Chinese Image Translator)

Initially, we considered a straightforward approach with route definitions like this:

app.get('/:lang/', homepageHandler);
app.get('/', defaultHomepageHandler);
app.get('/:lang/image-translator', imageTranslatorHandler);
app.get('/image-translator', defaultImageTranslatorHandler);

However, we quickly realized this approach had two significant drawbacks:

  1. Route Conflicts: The /:lang/ route could incorrectly match URLs intended for specific pages, e.g., /image-translator. This could lead to unexpected behavior where the application might not serve the correct page.
  2. Code Duplication: We'd need multiple route definitions for each page to handle different locales, leading to repetitive code.

Our Middleware Solution

To address these issues, we developed a custom middleware function:

function extractLocale(req, res, next) {
    const pathParts = req.path.split("/").filter(Boolean);
    const firstDir = pathParts[0];
    if (checkLocalsExisted(firstDir)) {
        req.lang = firstDir;
        req.url = req.url.replace(`/${firstDir}`, "");
    } else {
        req.lang = "en";
    }
    next();
}

This middleware performs two key operations:

  1. Locale Extraction: It checks if the first URL segment is a valid locale.
  2. URL Rewriting: If a locale is found, it's removed from the URL and stored in req.lang.

The URL rewriting step is crucial as it allows subsequent route handlers to match the path without considering the locale prefix.

Implementation

We implemented this middleware in our Express application like so:

const express = require('express');
const app = express();

app.use(extractLocale);

app.get('/', homeHandler);
app.get('/image-translator', imageTranslatorHandler);

Let's examine how different URLs are processed:

  1. https://textpixie.com/
    • req.lang: "en" (default)
    • req.url: "/"
    • Matched route: app.get('/', homeHandler)
  2. https://textpixie.com/zh-tw/
    • req.lang: "zh-tw"
    • req.url: "/"
    • Matched route: app.get('/', homeHandler)
  3. https://textpixie.com/image-translator
    1. req.lang: "en" (default)
    2. req.url: "/image-translator"
    3. Matched route: app.get('/image-translator', imageTranslatorHandler)
  4. https://textpixie.com/zh-tw/image-translator
    1. req.lang: "zh-tw"
    2. req.url: "/image-translator"
    3. Matched route: app.get('/image-translator', imageTranslatorHandler)

This approach allows a single route definition to handle multiple locale versions of a page. The middleware extracts the locale information, making it available via req.lang, while the rewritten URL ensures correct route matching.

Conclusion

By leveraging Express middleware for locale extraction and URL rewriting, we have created a scalable solution for handling locale-specific URLs for our web applications. This method has simplified our route definitions, eliminated conflicts, and separated locale handling from core routing logic.

If you're interested in seeing this solution in action, check out our AI Translator to translate texts, images and audios.