A Simple Way to Handle Locale-Specific URLs in Express
Sergey Li
Posted on August 28, 2024
Introduction
At TextPixie, as we expanded our AI Translator to support multiple languages, we needed a straightforward internationalization solution that would work with our existing Express-based backend, which doesn’t have built-in i18n support.
While we considered using i18next, we found that it offered much more than we needed and would complicate our backend. Instead, we decided to build a custom solution that focused on the two key requirements for our project:
- Setting up locale-specific URLs.
- Managing strings in Google Sheets and loading them onto pages based on the locale in the URL.
In this article, I’ll walk you through how we implemented locale-specific URLs using a simple Express middleware. In a follow-up article, I’ll dive into how we manage strings in Google Sheets and load them onto pages based on the locale in the URL.
The Problem
When we started expanding our AI Translator, we knew that to make the app truly multilingual, we needed clean, accessible URLs for each supported language. Ideally, our URLs would look something 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)
Our initial approach involved defining routes in Express like this:
app.get('/:lang/', homepageHandler);
app.get('/', defaultHomepageHandler);
app.get('/:lang/image-translator', imageTranslatorHandler);
app.get('/image-translator', defaultImageTranslatorHandler);
However, this method quickly led to two significant issues:
- Route Conflicts: The /:lang/ route could unintentionally match URLs meant for other pages, such as /image-translator, leading to incorrect page rendering.
- Code Duplication: We had to define multiple routes for each page to handle different languages, which resulted in repetitive and hard-to-maintain code.
Our Middleware Solution
To overcome these challenges, we developed a simple Express 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 does two key things:
- Locale Extraction: It checks if the first part of the URL is a valid locale (e.g., “en” or “zh-tw”).
- URL Rewriting: If a valid locale is found, it removes the locale from the URL and stores it in req.lang, allowing the rest of the app to process the request without worrying about the locale prefix.
Implementation
We integrated this middleware into our Express application as follows:
const express = require('express');
const app = express();
app.use(extractLocale);
app.get('/', homeHandler);
app.get('/image-translator', imageTranslatorHandler);
Here’s how the middleware processes different URLs:
-
- req.lang: "en" (default)
- req.url: "/"
- Matched route: app.get('/', homeHandler)
-
- req.lang: "zh-tw"
- req.url: "/"
- Matched route: app.get('/', homeHandler)
-
https://textpixie.com/image-translator
- req.lang: "en" (default)
- req.url: "/image-translator"
- Matched route: app.get('/image-translator', imageTranslatorHandler)
-
https://textpixie.com/zh-tw/image-translator
- req.lang: "zh-tw"
- req.url: "/image-translator"
- Matched route: app.get('/image-translator', imageTranslatorHandler)
This setup allows us to use a single route definition to handle multiple locales, simplifying our codebase and making it easier to maintain. The middleware ensures that the correct locale is extracted and handled without the need for duplicating routes.
Conclusion
By implementing this simple Express middleware, we were able to create a clean and efficient solution for handling locale-specific URLs in our web app. This approach helped us avoid route conflicts, reduce code duplication, and streamline our internationalization process.
In a follow-up article, I’ll dive into how we manage strings in Google Sheets and load them onto pages based on the locale in the URL. This second step is crucial for ensuring that the right content is displayed to users based on their language preference.
If you’re interested in seeing this solution in action, check out TextPixie AI Translator to experience how we handle multilingual content.
This article was originally published at TextPixie Blog.
Posted on August 28, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.