我正在开发一种具有多种语言的网站。因此,某些路线也必须本地化,我不确定如何正确执行此操作。
我正在使用@koa/router
进行路由。
在此示例中,仅英语和瑞典语,但该网站将处理更多语言。
我可以设置路线以匹配不同语言的单词,例如
router.get('/(create-account|skapa-konto)/', (ctx, next) => {
ctx.body = translate('signup_welcome');
await next();
});
但是,我希望英语站点仅响应“ / sign-up”,并发送404的“ / skapa-konto”(反之亦然)。
在现实世界中,路线将指向某些控制器功能。因此,如果我为每种语言设置单独的路由,那么将来如果控制器功能发生变化,则必须手动更改所有本地化的路由。那是我要避免的事情;)
有什么建议吗?
我最终通过扩展路由器来解决此问题:
const LocalizedRouter = class extends Router {
/**
* Set up route mapping
* @param {object} options
*/
constructor(options) {
if (!Array.isArray(options.languages)) {
throw new TypeError('Languages must be of type Array');
}
super(options);
this.languages = options.languages;
}
/**
* Router function for GET method
* @param {string | Object<string, string>} RouteCollection
*/
get(routes, func) {
if (typeof(routes) === 'string') {
super.get(routes, func);
return;
}
if (typeof(routes) === 'object') {
for(const key in routes) {
if(!this.languages.includes(key)) {
continue;
}
if(typeof(func) !== 'function') {
throw new TypeError('Middleware must be a function');
}
const checkLanguageAndMount = async (ctx, next) => {
if(ctx.state.lang !== key) {
return next();
}
return func(ctx, next);
};
super.get(routes[key], checkLanguageAndMount);
}
return;
}
throw new TypeError('"Routes" must be a string or an object');
}
};
然后我可以像这样设置路线:
const myRouter = new LocalizedRouter({
languages: ['en', 'sv']
});
myRouter.get({
'en': '/create-account',
'sv': '/skapa-konto'
}, (ctx, next) => {
ctx.body = translate('signup_welcome');
await next();
};
这可能可以清理,但确实可以解决我想做的事情。
编辑:修复了两种语言具有相同路径时会导致404的错误
这个问题使我感兴趣,所以我用一些代码创建了一个小型github存储库。我会在这里解释:
我创建了一个带有一些选项的数组:
const localeConfig = [
{
locale: "en",
routes: [
{
path: "/sign-up",
controllers: [enController],
method: "GET",
},
],
prefix: false,
},
{
locale: "se",
routes: [
{
path: "/skapa-konto",
controllers: [seController],
method: "GET",
},
],
prefix: false,
},
];
然后我将此对象传递给setupRoutes函数,该函数基本上对数组进行迭代,并根据这些选项生成所有路由。
const setupRoutes = (localeConfig) => {
// Have some check to prevent duplicate routes
localeConfig.forEach((opt) => {
// Adding prefix according to option
const localePrefix = opt.prefix ? `/${opt.locale}` : "";
opt.routes.forEach((route) => {
const path = `${localePrefix}${route.path}`;
router[route.method.toLowerCase()].apply(router, [
path,
...route.controllers,
]);
});
});
};
因此,例如,如果您要更改任何一种语言的控制器,则只需更新特定的语言环境object.route.controllers
。我想您甚至可以在一个不同的文件中包含每个不同的区域设置以具有某种模块化。
github仓库是here,如果您对如何改进它有任何想法,我非常希望您对此做出贡献。
干杯!