在做国际化网站的时候,很常见的一个业务需求是网站页面需要有多语言版本,且每个页面的多语言版本需要有对应的URL,便于搜索引擎爬虫收录。
常见的多语言URL结构是:
| URL | 语言 |
|---|---|
| example.com/ | 英语 |
| example.com/es/ | 西班牙语 |
| example.com/fr/ | 法语 |
有一个开源库叫 i18next,挺多人用的。
但在最近的一个项目中,感觉有点复杂,会让后端变得不必要的复杂。我们的后端是 Express 的,决定自己写一下。
分析下来,多语言的页面无非要实现两个功能点:
- 多语言URL
- 管理多语言内容,并根据 URL 中的语言加载到页面上
今天来分享一下多语言URL这个功能点通过Express中间件如何实现。
问题
我们最初的做法是在 Express 中定义如下路由:
app.get('/:lang/', homepageHandler);
app.get('/', defaultHomepageHandler);
app.get('/:lang/feature-one', featureOneHandler);
app.get('/feature-one', defaultFeatureOneHandler);
然而,这种方法很快遇到了两个主要问题:
- 路由冲突:/:lang/ 路由可能意外匹配其他页面的URL,导致页面渲染错误;
- 代码重复:为了处理不同语言,我们必须为每个页面定义多条路由,导致代码冗余且难以维护。
Local提取中间件
为了解决这些问题,我们开发了一个简单的 Express 中间件:
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();
}
这个中间件主要做了两件事:
- 语言提取:它会检查 URL 的第一部分是否是有效的语言标识(例如“en”或“zh-cn”);
- URL 重写:如果找到有效的语言标识,它会从 URL 中移除语言部分,并将语言信息存储在 req.lang 中,确保应用程序的其他部分可以无需考虑语言前缀正常处理请求。
实现
将这个中间件集成到我们的 Express 应用中,代码如下:
const express = require('express');
const app = express();
app.use(extractLocale);
app.get('/', homeHandler);
app.get('/feature-one', featureOneHandler);
以下是中间件如何处理不同 URL 的示例:
-
- req.lang: “en”(默认)
- req.url: “/”
- 匹配路由:app.get('/', homeHandler)
-
- req.lang: “zh-cn”
- req.url: “/”
- 匹配路由:app.get('/', homeHandler)
-
- req.lang: “en”(默认)
- req.url: “/feature-one”
- 匹配路由:app.get('/feature-one', featureOneHandler)
-
- req.lang: “zh-cn”
- req.url: “/feature-one”
- 匹配路由:app.get('/feature-one', featureOneHandler)
这种实现允许我们使用单一路由来处理多语言URL,极大简化了代码维护。中间件确保提取并处理正确的语言信息,而无需重复定义多条路由。
结语
通过引入这个简单的 Express 中间件,我们即可轻松处理特定语言的URL。帮助我们避免了路由冲突,还减少了代码的重复性。
想体验下通过这个方案实现的多语言网站的效果,可以看下我们的Free AI Translator。