I'm trying to utilize Symfony 4's multilang (or multi-locale) routing pattern.
My application is truly international and supports over 25 different languages, though the translations come incrementally and many routes are not translated to some languages yet.
In such case I want them to fall back into english-default.
My config/packages/translation.yaml
looks like this:
framework:
default_locale: en
translator:
default_path: '%kernel.project_dir%/translations'
fallbacks:
- en
My routes are defined within routes.yaml
file. For example:
about_index:
path:
en: /about-us
pl: /o-nas
controller: Symfony\Bundle\FrameworkBundle\Controller\TemplateController
defaults:
template: About/index.html.twig
Now, whenever I open the site with either pl
or en
locale - everything works as expected, but when for example I set it to de
, I get "Unable to generate a URL for the named route "about_index" as such route does not exist."
error.
How do I force Symfony to fallback to en
paths whenever the route in desired locale does not yet exist?
So, after quite a bit of investigation it appears there's no way to make it work like that with Symfony's default methods.
I went for the "workaround" approach and extended Symfony's Twig Bridge's Routing extension with my own Twig function, autopath()
:
namespace App\Twig;
use Symfony\Bridge\Twig\Extension\RoutingExtension;
use Twig\TwigFunction;
// auto-wired services
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* Extends Symfony's Twig Bridge's routing extension providing more flexible localization.
*
* @author Kyeno
*/
class KyAutoPathExtension extends RoutingExtension
{
private $router;
private $request;
public function __construct(UrlGeneratorInterface $router, RequestStack $requestStack)
{
$this->router = $router;
$this->request = $requestStack->getCurrentRequest();
parent::__construct($router);
}
/**
* {@inheritdoc}
*
* @return TwigFunction[]
*/
public function getFunctions()
{
return [
new TwigFunction('autopath', [$this, 'getPathAuto'], ['is_safe_callback' => [$this, 'isUrlGenerationSafe']])
];
}
/**
* @param string $name
* @param array $parameters
* @param bool $relative
*
* @return string
*/
public function getPathAuto($name, $parameters = [], $relative = false)
{
// obtain current and default locales from request object
$localeRequested = $this->request->getLocale();
$localeDefault = $this->request->getDefaultLocale();
// build localized route name
// NOTE: Symfony does NOT RECOMMEND this way in their docs, but it's the fastest that popped in my mind
foreach([sprintf('%s.%s', $name, $localeRequested), sprintf('%s.%s', $name, $localeDefault)] as $nameLocalized) {
// if such route exists, link to it and break the loop
if($this->router->getRouteCollection()->get($nameLocalized)) {
return $this->router->generate($nameLocalized, $parameters, $relative ? UrlGeneratorInterface::RELATIVE_PATH : UrlGeneratorInterface::ABSOLUTE_PATH);
}
}
// when no matches found, attempt relying on Symfony Twig Bridge's original path() function
// (and likely fail with exception, unless they fix/allow it)
return parent::getPath($name, $parameters, $relative);
}
}