Multilang virtual pages; particular page not translated -> howto

I have a set of virtual pages; and not all content is translated (in the default language, everything is!). When I leave out a translation in the configuration array that is fed to Pages::factory() I get errors when I loop over the children within the language version of Kirby where items are missing.

Somehow it must be possible, since Kirby knows how to handle this if you have a contentfolder where there’s no translate txt files inside. E.g. also $page->translation('nl')->exists() works perfectly in this use case.

This is my simplified setup, where there’s no NL translation for a given child (virtual page):

<?php

class TestsPage extends Page
{
    public function children()
    {
        $children = Pages::factory([
            [
                'slug' => 'aaa-en',
                'template' => 'test',
                'model' => 'test',
                'translations' => [
                    'en' => [
                        'code' => 'en',
                        'slug' => 'aaa-en',
                        'content' => [
                            'title' => 'aaa aaa - en'
                        ]
                    ],
                    'nl' => [
                        'code' => 'nl',
                        'slug' => 'aaa-nl',
                        'content' => [
                            'title' => 'aaa aaa - nl'
                        ]
                    ],
                    'fr' => [
                        'code' => 'fr',
                        'slug' => 'aaa-fr',
                        'content' => [
                            'title' => 'aaa aaa - fr'
                        ]
                    ],
                    'de' => [
                        'code' => 'de',
                        'slug' => 'aaa-de',
                        'content' => [
                            'title' => 'aaa aaa - de'
                        ]
                    ]
                ]
            ],
            [
                'slug' => 'bbb-en',
                'template' => 'test',
                'model' => 'test',
                'translations' => [
                    'en' => [
                        'code' => 'en',
                        'slug' => 'bbb-en',
                        'content' => [
                            'title' => 'bbb bbb - en'
                        ]
                    ],
                    // NL doesn't exist. How should I set this up to make it work?
                    // `code` is a required prop
                    // 'nl' => [
                    //     'code' => null
                    // ],
                    'fr' => [
                        'code' => 'fr',
                        'slug' => 'bbb-fr',
                        'content' => [
                            'title' => 'bbb bbb - fr'
                        ]
                    ],
                    'de' => [
                        'code' => 'de',
                        'slug' => 'bbb-de',
                        'content' => [
                            'title' => 'bbb bbb - de'
                        ]
                    ]
                ]
            ]
        ], $this);

        return $children;
    }
}

When I loop over these items in a foreach($page->children() as $child) in NL I get the following error:

The default behaviour (with Kirby fetching content from the content folder) is it uses the default language, so I’ld assume to be shown an “EN” version here of the “bbb” content, and not an error?

What do I not see?

I think I could “quickfix” the default behaviour by also feeding the content for the default language to the missing languages in the array that feeds Pages::factory. But, afterwards, how do I figure out if content is translated, and which is a copy from the default language?

$contentTranslation->exists()only looks a the filesystem, hence is useless in a virtual pages scenario?

What if you just pass an empty array?

Hi @texnixe , this results in another error; The property "code" is required.

I meant an empty array for the content… or you could also try null for the content, but don’t know if that works.

Hi @texnixe , I’ve changed this in my example above (I don’t have a translation in NL):

'nl' => [
    'code' => 'nl',
    'slug' => 'non-existant-translation-shouldnt-have-a-slug',
    'content' => [] // null has the same result
],

This no longer produces an error at the foreach loop, which is nice.
But, I still have no tools to identify within Kirby if this “translation” exists? For virtual pages, $virtualPage->translation()->exists() always returns false, which is kinda expected when I look at this code: kirby/ContentTranslation.php at 3.7.5 · getkirby/kirby · GitHub

Is this a bug then?

I wouldn’t say it’s a bug, rather a method that like the $contentTranslation->contentFile() is useless in a virtual page context and would have to be overwritten (don’t know if that’s possible in an easy way without side effects).

It might make sense to use another way to check if a translation exists, i.e. check if $content is empty or something like that.

The $contentTranslation->exists() method is probably not very reliable to determine if a translation really exists anyway, because the file might exists, but still important parts not translated.

I don’t really know how to describe it neither :sweat_smile:, but I’ld need to be able to identify if a translation exists in a virtual pages context somehow. Preferably in a consistent/invisible way with how Kirby’s API works. Hence the question :wink:

Any hint on how to achieve this?

Not really.

What does dump($page>content('nl')) return in your example? Does this fall back to the default language?

Or dump($page->translation('nl'))?

When using normal pages, these both return the default content, because that’s Kirby’s default behavior to fall back to the default language if no translation exists. That’s why the $contentTranslation->exists() method checks if the translation exists in the file system.

IMO, the best and finally most reliable way to check if a page has been translated in a non-virtual page context, is to let the user indicate if a page is translated or not, e.g. via a toggle field.

This is also what you could do in your virtual page context?

This returns array with the content of the virtual page in the default language.

Same, but in the array some more keys are added (e.g. exists (which is always false).

I think Kirby’s default way makes absolute sense, and I’ld like to keep using that paradigm. It’s also what is proposed in the docs to make a language navigation, which I usually use in websites I build.
By virtual pages’ nature, they are not available on the filesystem though. So the “default way” doesn’t work as expected which is inconsistent.
Essentially, an empty language key in the virtual pages array is the same thing as a missing translation-txt-file on the file system. Since virtual pages are being communicated as a feature in Kirby (which they really, really are), I’ld assume this behaves the same way. Hence, in my eyes this can be considered a bug?

Right now, I’m even unable to check whether a translation exists for any given virtual page.
I’ld like to have a singular way in the code to identify if a particular translation of a page exists (“virtual page” or “physical page”).

You mean the error that is thrown when the array is missing completely? That might be. Maybe the best is to open an issue on GitHub.

Not really. More the fact that there isn’t a singular way fo filter out virtual pages without translations.
Of course you could build your own schema / API / conventions on top of Kirby that results in accounting for that use case in every place you want to check this – which will quickly become unmaintainable & unreliable spaghetti.

You’re right, I’ll add to github. Maybe someone from the core dev team has an idea on how to fix this.

Edit - added FFR: Check for content translations doesn't work with virtual pages · Issue #4674 · getkirby/kirby · GitHub