Привет, Хабр! Хочу рассказать вам о том, как в одном проекте возникла проблема с роутингом и как мы её решали. Сначала наш проект был самым обычным сайтом. Сайт развивался, аудитория расширялась и возникла необходимость поддержки мультиязычности. Проект был на базе фреймворка Laravel и проблем с мультиязычностью не возникло (нужный язык подтягивался из сессии, либо брался дефолтный). Мы написали переводы, прописали ключи переводов вместо захардкоженных фраз и взяли в работу следующие фичи. В какой-то момент команда SEO поняла, что такой подход мешает ранжированию сайта. Тогда команде разработки поступила команда добавить языковые подпапки в УРЛ, кроме языка по умолчанию. Наши роуты приняли примерно такой вид:  Всё встало на свои места и мы снова принялись за новые фичи.
Чуть позже возникла необходимость развернуть приложение на нескольких доменах. В целом эти сайты имеют одну БД, но в зависимости от домена могут меняться некоторые настройки.
Некоторые сайты могут быть мультиязычные (причем с ограниченным набором языком, а не со всеми поддерживаемыми), некоторые — только один язык. Было принято решение обрабатывать все домены одним приложением (nginx проксирует все домены на один апстрим). Набор поддерживаемых конкретным сайтом языков и язык по умолчанию должен настраиваться в админке, что на корню зарубило вариант конфига/env-переменных. Стало ясно, что текущее решение не удовлетворяет наши хотелки. Для начала нам нужно указать в конфигурации приложения все поддерживаемые языки. Нам понадобится сущность сайта Site и сервис определения настроек сайта. Добавим наш сервис в контейнер Теперь определим роуты. Части роутов, подлежащие локализации, обрамлены двойными минусами (--). Это маски для замены. Теперь законфигурируем эти маски. Для отображения компонента выбора языка нам нужно передать в шаблон только те языки, которые поддерживаются сайтом. Напишем для этого мидлварь... Теперь нужно кастомизировать роутер. Вернее не сам роутер, а коллекцию роутов... …, основной класс приложения ... … и подменить наши кастомные классы. Следующий шаг — определение языка по первой части УРЛ запроса. Для этого перед диспатчингом мы получим первый его сегмент, проверим поддержку такого языка сайтом, и запустим диспатчинг с новым запросом уже без этого сегмента. Немного поправим класс App\Http\Kernel, а заодно добавим наш миддлварь App\Http\Middleware\ViewData в группу web Если не кэшировать роуты, то можно уже работать. Но на бою без кэша — идея не из лучших. Мы уже научили наше приложение получать роуты из кэша, теперь нужно научить правильно его сохранять. Кастомизируем консольную команду route:cache Команда route:clear просто удаляет файл кэша, Её мы трогать не будем. А вот команде route:list теперь не помешает опция locale. Теперь нам нужно эти команды заставить работать. Сейчас будут работать вендорные команды. Чтобы заменить реализацию консольных команд, нужно включить в приложение сервис провайдер, реализующий интерфейс Illuminate\Contracts\Support\DeferrableProvider. Метод provides() должен вернуть массив ключей контейра, соответствующих классам команд. Ну и конечно же, добавляем провайдер в конфигурацию. Теперь всё работает! На этом всё. Спасибо за внимание! Дилетант