Set current language of virtual page

Is there a way to set the current language when I return a virtual page (using new Page()) in a route? Right now all content in the template is returned in the default language.

By the way: I struggle a lot with getting a multi language site to work. I wish there was a ‘language starter kit’—or does something like that exist?

As far as I understand it, as soon as you start using custom routes, a lot needs to be hand-coded in order for the languages to work properly.

For example do I not understand the concept of the “language scope”, especially when the 'language' of the route is set to wildcard / asterisk. In my eyes it does nothing but handing the current language as a parameter to the 'action' method. I would have hoped that it would set the current language for all kirby methods like page(), just as it happens in a template.

As always: thank you!

How do you create the virtual page, could you please post all relevant code?

I got it working, but I remember having wrestled through a lot as well.

The gist is you need a translations key in the $options-array you feed in the new Page($options). This one contains another array with the slugs of the translated versions of your current virtual page like this:

$translations = [
    "nl" => "mijn-eerste-blog-post",
    "en" => "my-first-blog-post"

To set the language, I have this at the top of the function that handles this route:

site()->visit(site()->homepage(), $lang);

Where $lang is coming in as parameter from the route.

1 Like

I don’t know if this is the "best way" to cope with this (as it dates from the beta iirc), but it works for me.

This is what I use:

  'pattern' => ['sammlungen/(:any)',
  'language' => '*',
  'action' => function($language, $title) {
    $slug = 'projekte/sammlungen/' . $title;
    $collection = page($slug);

    if ($collection) {
      $title = $collection->content($language)->title();
      $selection = $collection->content($language)->selection();

      // return virtual page based on template
      return new Page([
        'slug' => $slug . '/' . $index,
        'template' => 'project',
        'content' => [
          'title' => $title,
          'selection' => $selection

    return false;

I was aware of the translations key, but I couldn’t find out what I need to put in there (except a “translations array”). I thought it’s where to put translated content, when you create a page in order to save it to a file afterwards. Thank you for pointing that out! (Something that I wish was stated somewhere in the docs.)

To set the language, I have this at the top of the function that handles this route:

site()->visit(site()->homepage(), $lang);

Good to know—I thought visit() was only useful when being returned by the route action.

Thank you @bvdputte! I will try and see if this works, but it looks promising.

Oh I think I found the “right” solution. I don’t return the virtual page (created by new Page()) directly but put it into a variable ($virtual). Using site()->visit($virtual, $language)works!

Of course you could also create the new Page() directly as an argument for the visit() method (kind of wrap the new Page() code with it).

Glad you worked it out :+1:

1 Like

I think we need some more documentation for this…

@texnixe, yes that might be handy. Especially for the multilingual stuff :wink:

That’s what I meant.


The docs for simple virtual page have an example for this.

But i’m wondering if they can have different slugs? I could make 2 virtual pages but they would not be related to each other

Yes they can. You put them in the translations key of the (virtual) page. As an example:

'translations' => [
  'en' => [
    'code' => 'en',
    'slug' => 'slug1'
  'de' => [
    'code' => 'de',
    'slug' => 'slug2'
1 Like

that works! I think we need more docs on multi language sites