Локализованные маршруты в коа

Я разрабатываю сайт с несколькими языками. Поэтому некоторые маршруты также должны быть локализованы, и я не уверен, как это сделать правильно.

Я использую @koa/router для маршрутизации.

В данном примере это только английский и шведский, но сайт поддерживает и другие языки.

Я могу настроить маршруты для соответствия словам на разных языках, например

router.get('/(create-account|skapa-konto)/', (ctx, next) => {
  ctx.body = translate('signup_welcome');
  await next();
});

Но я хочу, чтобы английский сайт отвечал только на «/ sign-up» и отправлял 404 на «/ skapa-konto» (и наоборот).

В реальном мире маршрут указывал бы на какую-то функцию контроллера. Поэтому, если я настрою отдельные маршруты для каждого языка, мне придется вручную изменить все локализованные маршруты, если функция контроллера изменится в будущем. Это то, чего я хотел бы избежать;)

Какие-либо предложения?


person Magnus Jonsson    schedule 29.05.2020    source источник


Ответы (2)


В итоге я решил это, расширив маршрутизатор следующим образом:

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, если два языка имели одинаковые пути.

person Magnus Jonsson    schedule 02.06.2020

Эта проблема меня заинтересовала, поэтому я создал небольшой репозиторий на 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 находится здесь, и я бы очень хотел, чтобы вы внесли в него свой вклад, если у вас есть идея о том, как улучшить это.

Ваше здоровье!

person Pedro Filipe    schedule 29.05.2020
comment
Интересно! Скоро посмотрю внимательнее :) - person Magnus Jonsson; 29.05.2020
comment
Теперь я потратил некоторое время, чтобы просмотреть его, и на самом деле это не решает проблему, когда мне пришлось бы обновлять все локализованные версии. Допустим, у меня есть RegisterController с функцией регистрации. Затем я хочу изменить маршрут, чтобы он указывал на функцию signupAndVerify. Затем мне пришлось бы редактировать каждую локализованную версию и обновлять их вручную. - person Magnus Jonsson; 02.06.2020