StenopePHP / Stenope

The static website generator for Symfony developers
https://stenopephp.github.io/Stenope/
MIT License
117 stars 9 forks source link

Stenope doesn't generate pages for localized routes #187

Open Alexandre-Fernandez opened 2 months ago

Alexandre-Fernandez commented 2 months ago

I have the following config :

config/services.yaml

parameters:
  app.supported_locales: "en|fr"

src/Controller/AppController.php

    #[Route("/{_locale<%app.supported_locales%>}", name: "index")]
    public function index(): Response
    { /* ... */ }

When I run php bin/console -e prod stenope:build ./static stenope doesn't generate any page for my route.

EDIT: I also tried replacing my custom parameter with enabled_locales, and it still doesn't work, is there a workaround ?

ogizanagi commented 2 months ago

Well, Stenope cannot guess any route containing parameters in its path. It'll start by gathering every routes without parameters, and crawl dynamically the site by collecting URLs rendered by the Symfony URL generator.

In order to crawl these, you need at least one route, without any path parameters, rendering a page referencing the routes with each of the expected values.

If you don't have such pages in your app, nor want to, an alternative is to create a listener on the Stenope build command, in order to call the URL generator for such routes, allowing to collect them.

We have such a listener for routes that are not referenced anywhere in our templates here.

Do the same to generate the URLs of the main entrypoints of your app, in each locale, then Stenope will do the rest.

Alexandre-Fernandez commented 2 months ago

Well, Stenope cannot guess any route containing parameters in its path. It'll start by gathering every routes without parameters, and crawl dynamically the site by collecting URLs rendered by the Symfony URL generator.

In order to crawl these, you need at least one route, without any path parameters, rendering a page referencing the routes with each of the expected values.

If you don't have such pages in your app, nor want to, an alternative is to create a listener on the Stenope build command, in order to call the URL generator for such routes, allowing to collect them.

We have such a listener for routes that are not referenced anywhere in our templates here.

Do the same to generate the URLs of the main entrypoints of your app, in each locale, then Stenope will do the rest.

If stenope can access kernel.enabled_locales then I'm guessing it can guess the urls by replacing _locale, if it doesn't have access to that parameter then it would be nice if you could provide it in the stenope yaml config.

Thanks for the tips, here's my current workaround for parameterless routes :

<?php

namespace App\Bundle\Stenope;

use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouterInterface;

class StenopeBuildListener implements EventSubscriberInterface
{
    public function __construct(
        private readonly UrlGeneratorInterface $urlGenerator,
        private readonly RouterInterface $router,
        #[Autowire("%kernel.enabled_locales%")]
        private array $enabledLocales,
    ) {
    }

    public static function getSubscribedEvents(): array
    {
        return [ConsoleEvents::COMMAND => "onCommand"];
    }

    public function onCommand(ConsoleCommandEvent $event): void
    {
        if (null !== $event->getCommand() && "stenope:build" !== $event->getCommand()->getName()) {
            return;
        }

        $this->generateLocalizedUrls();
    }

    private function generateLocalizedUrls()
    {
        foreach ($this->router->getRouteCollection() as $name => $route) {
            if (!str_contains($route->getPath(), "_locale")) { // no unlocalized routes
                continue;
            }
            if (!$route->getRequirement("_locale") || count($route->getRequirements()) !== 1) { // no routes with other params than _locale
                continue;
            }
            foreach ($this->enabledLocales as $locale) {
                $this->urlGenerator->generate($name, [
                    "_locale" => $locale,
                ]);
            }
        }
    }
}
indyjonesnl commented 2 months ago

@Alexandre-Fernandez I ran into the same problem and fixed it (temporarily) with https://github.com/StenopePHP/Stenope/compare/master...indyjonesnl:Stenope:patch-1

There are prettier / better solutions, I agree. But this does allow me to use localized routes with Stenope.