Routing issues with multi language

I have started with a little routing and than it have been growing for a while… All routes worked when the website was single language… :wink: And the now with multilingual setup, routes brakes down everywhere. :confused:

So home is fine with / but with /br it gives me the error 404 page. So how do I make sure all my routes works fine with the new languages?

PS: None of the routes work with the language code. Inclusive the default Kirby routes.

I need a little help to get out of this mess of mine. Please, @texnixe :pray:

c::set('routes', array(
  // Articles route
  array(
    'pattern' => 'articles/(:any)/amp',
    'action'  => function($article) {
      $page = page('articles/' . $article);
      site()->visit($page);

      tpl::$data = array_merge(tpl::$data, array(
        'kirby' => kirby(),
        'site'  => site(),
        'pages' => pages(),
        'page'  => page()
      ), $page->templateData());

      echo tpl::load(kirby()->roots()->templates() . DS . 'amp.php');
      return false;
    }
  ),
  // Logout route
  array(
    'pattern' => 'logout',
    'action' => function () {
      if ($user = site()->user()) $user->logout();
      go('login');
    }
  ),
  // Omit lander and link pages
  array(
    'pattern' => '(:any)',
    'action'  => function($uid) {

      $page = page($uid);

      // if(!$page) $page = page('articles/' . $uid);
      if(!$page) $page = page('lander/' . $uid);
      if(!$page) $page = page('legacy/' . $uid);
      if(!$page) $page = page('link/' . $uid);
      if(!$page) $page = site()->errorPage();

      return site()->visit($page);
      return false;
    }
  ),
  array(
    'pattern' => 'lander/(:any)',
    'action'  => function($uid) {
      go($uid);
    }
  ),
  array(
    'pattern' => 'legacy/(:any)',
    'action'  => function($uid) {
      go($uid);
    }
  ),
  array(
    'pattern' => 'link/(:any)',
    'action'  => function($uid) {
      go($uid);
    }
  )
));


if(!function_exists('panel')) return;
panel()->routes(array(
  array(
    'pattern' => '(logout)', // the trick here is the parens around the route pattern
    'method'  => 'GET|POST',
    'filter'  => array('auth'),
    'action'  => function() {
      if($user = panel()->user()) {
        $user->logout();
      }
      go('/');
    },
  ),
));

Do the routes work in the sites default language but not in others?

So before I added multi language it all worked fine. The default route like /home only works as long I keep the language setup this way 'url' => '/', but when I add /br than it goes to the 404 error page.

So in short everything that gets the language code in the url seems to fail. Did that answer the question? Ideas @jimbobrjames ?

I dont have a huge experience with routes, but I have done a couple of funky things with them. Since you are getting a 404 page, i guess the codes ok, otherwise you would get a Whoops page, I guess, becuase it cant find the page rather bombing out from a coding error.

Feels like the pattern is the problem. I think maybe you need something like:

'pattern' => '(:any)/articles/(:any)/amp',

Thats a total stab in the dark, but i think its not working becuase the page you have in the pattern does work from the site root with no language, but when you switch languages you technically add an other level of depth, virtually, as if it was a folder. I hope @texnixe can clear that assumption up if I have it totally wrong.

1 Like

If you use the language code only on the non-default language, you have two options

  • either duplicate your routes to include one with the language code and one without
  • make the language code an optional placeholder (there are some examples here on the forum), just (:any)as suggested. by @jimbobrjames won’t work

Also, you will have to add the language code with site()->visit('some-page', $langcode).

It’s a bit late now and I’m tired, will get back to you tomorrow if you can’t sort it out in the. meantime.

1 Like

Get some sleep. I think I will need a little help one way or another tomorrow. :slight_smile: Good night!

Have a look at this post: Routes pattern for multilanguage site

So your first route (e.g. the first one, should start like this:

'pattern' => '(?:(^[A-Za-z]{2})//?)?articles/(:any)/amp',
'action'  => function($lang, $article) {
    $lang = $lang ?: 'en'; // the default language
    // do stuff
  }

On the first rule I have added the code. But not sure if I get this right yet.

I get an error "session_start(): Cannot send session cache limiter - headers already sent "

On this line: echo tpl::load(kirby()->roots()->templates() . DS . 'amp.php');

I have tried to search here on the forum but there is very little with multi lang setup and routes.

On the second rule set is also failing, the omiting rules. The logout rules works without any issues.

How will you make it generic?

Maybe the error occurs because of the echo statement, see this: Exception with custom route

Yes you are right. That worked well! :smile: That leaves me with the last rule set, the omiting. I have tried, but without success, and everything I see on the forum is using different approaches.

Does it exist a kirby way? And a generic way?

array(
    'pattern' => '(:any)',
    'action'  => function($uid) {
  
      $page = page($uid);
  
      // if(!$page) $page = page('articles/' . $uid);
      if(!$page) $page = page('lander/' . $uid);
      if(!$page) $page = page('legacy/' . $uid);
      if(!$page) $page = page('link/' . $uid);
      if(!$page) $page = site()->errorPage();
  
      return site()->visit($page);
      return false;
    }
  ),
  array(
    'pattern' => 'lander/(:any)',
    'action'  => function($uid) {
      go($uid);
    }
  ),
  array(
    'pattern' => 'legacy/(:any)',
    'action'  => function($uid) {
      go($uid);
    }
  ),
  array(
    'pattern' => 'link/(:any)',
    'action'  => function($uid) {
      go($uid);
    }
  ),

I don´t seem to understand the logic of the language code etc. :confused:

This should work. Note I changed the pages to test this in a starter kit, so you have to adapt again.

c::set('routes', array(
  // Articles route

  // Omit lander and link pages
  array(
    'pattern' => '(?:(^[A-Za-z]{2})//?)?(:any)',
    'action'  => function($lang,$uid) {
      $lang = $lang ?: 'en';
      $page = page($uid);

      // if(!$page) $page = page('articles/' . $uid);
      if(!$page) $page = page('projects/' . $uid);
      if(!$page) $page = page('blog/' . $uid);
      //if(!$page) $page = page('link/' . $uid);
      if(!$page) $page = site()->errorPage();

      return site()->visit($page, $lang);

    }
  ),
  array(
    'pattern' => '(?:(^[A-Za-z]{2})//?)?projects/(:any)',
    'action'  => function($lang,$uid) {
      $lang = $lang ?: '';
    go($lang.'/'.$uid);
    }
  ),
  array(
    'pattern' => '(?:(^[A-Za-z]{2})//?)?blog/(:any)',
    'action'  => function($lang,$uid) {
        $lang = $lang ?: '';
      go($lang.'/'.$uid);
    }
  )
));
1 Like

All the custom routes seems to work with the code you have made @texnixe . (I have not test all pages but if one work the rest should too, I hope.) :slight_smile:

Case #1
So the domain.com/en fails with my current custom routes. Even though the other pages for ex. domain.com/articles works fine, and swaps languages without any issues. All other pages seems to work.
And the pages that uses the URL-key parameter is also failing to load. They gives a error 404, and goes to the default error page.

My current routes:

c::set('routes', array(
  // Articles route
  array(
    'pattern' => '(?:(^[A-Za-z]{2})//?)?articles/(:any)/amp',
    'action'  => function($lang, $article) {
      $lang = $lang ?: 'br'; // the default language
      $page = page('articles/' . $article);
      site()->visit($page, $lang);

      tpl::$data = array_merge(tpl::$data, array(
        'kirby' => kirby(),
        'site'  => site(),
        'pages' => pages(),
        'page'  => page()
      ), $page->templateData());

      return new Response(tpl::load(kirby()->roots()->templates() . DS . 'amp.php'), 'html', 200);
    }
  ),
  // Omit lander and link pages
  array(
    'pattern' => '(?:(^[A-Za-z]{2})//?)?(:any)',
    'action'  => function($lang,$uid) {
      $lang = $lang ?: 'br';
      $page = page($uid);

      // if(!$page) $page = page('articles/' . $uid);
      if(!$page) $page = page('lander/' . $uid);
      if(!$page) $page = page('blog/' . $uid);
      //if(!$page) $page = page('link/' . $uid);
      if(!$page) $page = site()->errorPage();

      return site()->visit($page, $lang);

    }
  ),
  array(
    'pattern' => '(?:(^[A-Za-z]{2})//?)?lander/(:any)',
    'action'  => function($lang,$uid) {
      $lang = $lang ?: '';
    go($lang.'/'.$uid);
    }
  ),
  array(
    'pattern' => '(?:(^[A-Za-z]{2})//?)?blog/(:any)',
    'action'  => function($lang,$uid) {
        $lang = $lang ?: '';
      go($lang.'/'.$uid);
    }
  ),
  // Logout route
  array(
    'pattern' => 'logout',
    'action' => function () {
      if ($user = site()->user()) $user->logout();
      go('login');
    }
  ),


));

Case #2
If I add /br to the url for the default language. domain.com do not work anymore. The initial redirect do not work. Any idea for why? This seems to be the case even if I comment out all custom routes and restart the server. Did work before… So I don´t know what that can have caused this.

Case #3
When I remove (comment out) all custom routes and restart my server I get faced with a odd issue. On the home page I click the English language and than I go back to the default Portuguese the page reloads with the /en language extension. Is this a nav issue? Cause the other pages seems to work.

Case #4
This code seems to also not work as intended in multi-language setup. This is not a deal-breaker, but upon logout from the panel you get redirected to the home page.

if(!function_exists('panel')) return;
panel()->routes(array(
  array(
    'pattern' => '(logout)', // the trick here is the parens around the route pattern
    'method'  => 'GET|POST',
    'filter'  => array('auth'),
    'action'  => function() {
      if($user = panel()->user()) {
        $user->logout();
      }
      go('/');
    },
  ),
));

Question
What will you recommend run the default language with or without the language code in the url? Are there any benefits to one or the other?

Info
You can put any two letters in the language code and it will still load the page. This is not important but if this goes to the error page that will be more consistent. It there is a way… :blush:

Oh my, routing with multi-lang is a chore, really.

If you use URL-keys, that complicates it and you need separate routes for those pages.

Routes are easier to deal with, I think, if you use the lang code for all languages.

To avoid the issue with none existing language code, check if the lang code exists in an array with possible lang codes, otherwise hit the error page or redirect.

If you use URL-keys, that complicates it and you need separate routes for those pages.

Yes, but that URL-keys for ex. is some of what makes it really good. So how will the separation of they look like?

Routes are easier to deal with, I think, if you use the lang code for all languages.

So how do you attack the routes this way?

To avoid the issue with none existing language code, check if the lang code exists in an array with possible lang codes, otherwise hit the error page or redirect.

Is this need if we drop the regular expression?

What is your take @texnixe on this:

What will you recommend run the default language with or without the language code in the url? Are there any benefits to one or the other? And to use both languages with the codes seems to work better in Kirby or is that the case?

And in my Case #1 above, the home page do not work in the none default language, how to get around that?

Case #1 is probably caused by the omit rule, maybe only redirect if the page is not the home page.

Don’t know, but this answer on SO has some good points, I think:

For pages with URL-key, you would have separate routes, but the final solution depends on whether or not you use the lang code for the default language or not

With lang code, en

'pattern' => 'en/lander/(:any)',

With lang code, br

'pattern' => 'br/whatever-that-page-is-called-in brasilian/(:any)',
1 Like

So I take the master-decision and go for language codes on both languages. Meaning /br is the default language and the /en is the secondary language. With the AMP route active it all works. After clearing some cache issues etc.

Is there any way to just put in the language code if there is none, aka default for ex.?

Based on my choice with leaving the default code in how will this look?

PS: I really miss good docs on this. AMP pages and Omit with multi-language and retain all functions like URL-keys… :wink: Also AMP pages with omiting the blog url for ex. and with multi-lang. (Advanced stuff :smile: )