Routes in multi-lang setup

This is support in need of support :slightly_smiling:

I’ve got the following language setup:

c::set('languages', array(
    'en' => array(
      'name'    => 'English',
      'code'    => 'en',
      'locale'  => 'en_US',
      'default' => true,
      'url'     => '/',
    ),
    'de' => array(
      'name'    => 'Deutsch',
      'code' => 'de',
      'locale'  => 'de_DE',
      'url'     => '/de',
      'default' => false
    )
));

The default language does not have a language code.

This is my pretty simple route, which is supposed to redirect to the first child if a page has subpages:

c::set('routes', array(
    array(
      'pattern' => '(:any)',
      'action'  => function($uid) {
        $page = page($uid);
        if($page->hasVisibleChildren()) {
          $page = $page->children()->visible()->first();
        }
        else {
          $page = $page;
        }
        if(!$page) $page = site()->errorPage();

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

      }
    ),
  ));

This works perfectly fine for the default language, but the non-default language throws an error, because $uid == de is obviously not a page.

Now I could change my language setup and give the default language a language code as well like this:

c::set('languages', array(
    'en' => array(
      'name'    => 'English',
      'code'    => 'en',
      'locale'  => 'en_US',
      'default' => true,
      'url'     => '/en',
    ),
    'de' => array(
      'name'    => 'Deutsch',
      'code' => 'de',
      'locale'  => 'de_DE',
      'url'     => '/de',
      'default' => false
    )
));

Then the following route works for both languages:

c::set('routes', array(
    array(
      'pattern' => '(:any)/(:any)',
      'action'  => function($lang, $uid) {
        $page = page($uid);
        if($page->hasVisibleChildren() && !($page->is(page('home')) || ($page->is(page('news'))))) {
          $page = $page->children()->visible()->first();
        }
        else {
          $page = $page;
        }
        if(!$page) $page = site()->errorPage();

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

      }
    ),
  ));

Where is the problem, then? Well, I don’t want to use the language code for the default language.

I have also tried to get this working with some regex, but to no avail, because if the non-default language has a language code and the other one does not, I can’t fetch the page UID for the non-default language because the language code is sort of “in the way”. What am I overlooking? Any suggestions?

Thanks.

1 Like

@texnixe
Look at the two very last routes in
https://github.com/ausminternet/blogprint/blob/master/site/config/config.php.

May be it can help you, if you add both routes at the same time. Not tested yet!

Good luck!

Adding multiple routes is what I’ve tried … but with no success so far …

Did you add the second route ('pattern' => '(:any)/(:any)') first?

You could probably use something like this:

c::set('routes', array(
    array(
      'pattern' => '(?:(de)/)?(:any)',
      'action'  => function($lang, $uid) {
        if(!$lang) $lang = 'en';

The first group will only match if it is de, otherwise it will be ignored (so it will be empty) and the second group will get the whole UID in both cases.

I have not tested that, but if it works, I will add it to the docs.


In case someone needs an example with three languages (en, de, fr):

c::set('routes', array(
    array(
      'pattern' => '(?:(de|fr)/)?(:any)',
      'action'  => function($lang, $uid) {
        if(!$lang) $lang = 'en';
2 Likes

@lukasbestle, it’s similar to what I had come up with myself, but unfortunately, it does not work for the start page of the non-default language :cry:

Yeah, I had thought about adding it to the docs as well, as soon as we have a working solution.

Well, then let’s make the slash optional as well:

c::set('routes', array(
    array(
      'pattern' => '(?:(de)/?)?(:any)',
      'action'  => function($lang, $uri) {
        if(!$lang) $lang = 'en';
        if(!$uri)  $uri  = site()->homePage()->uri();

Breaks for an URI like deblabla however, but I hope that’s OK.

Unfortunately, that does not make any difference for me :confused: (or I’m doing something wrong). Have you tested this?

I finally got it working, but it looks a bit hacky to me:

c::set('routes', array(
    array(
      'pattern' => '(?:(de)/?)?(:any)',
      'action'  => function($lang, $uri) {
        if($lang == '' && $uri == 'de') {
          $lang = 'de';
          $uri = 'home';
        }

        $page = page($uri);
        if($page->hasVisibleChildren() && !($page->is(page('home')) || ($page->is(page('news'))))) {
          $page = $page->children()->visible()->first();
        }
        else {
          $page = $page;
        }
        if(!$page) $page = site()->errorPage();

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

The reason is that on the start page, the $lang variable for the non-default language is empty, and $uri contains the language code. Still wondering if there is a less weird way of achieving this.

1 Like

Yeah, looks a bit hacky. I think there should be a Kirby feature to make it easier to match multilang URLs. I have opened an issue on GitHub.

4 Likes