Multi-language for only some pages

Hi there.

We have a German language website and we’d like to create a few new foreign language pages. The product owner doesn’t want to use the built-in multi-language feature because he’s afraid of getting dinged by google if we have every page “duplicated” into the five different languages we might use.

ie, https://kautionsfrei.de, https://kautionsfrei.de/en, https://kautionsfrei.de/fr, etc.

Instead we’re going to have a german website, with just four or five separate pages in the foreign languages. For example a /english page.

Right now I have the ‘languages’ option defined for just German.

But… I’m thinking that if english isn’t defined in the languages option, the new /english page isn’t going to work with l::get().

So, I’m kinda stuck, either implementing my own version of the L class, or having multiple versions of each page.

Anybody got any tips/suggestions in either problem?

Ideally, I’d just like to turn multi-language on and have kirby return 404 for any page that doesn’t exist in that language and not display the page in the sitemap. (I can work on that separately).

Have you checked https://getkirby.com/docs/cookbook/multilanguage-secrets ?

Had not seen that… It helps.

So it looks like I’ll need to adjust all my templates to redirect to my 404 page if there is no translation.
And I’ll need to see how the sitemap plugin handles multi-lang to filter out all the pages that I don’t want included.

Maybe it’s easier for you to implement some custom page method who does that, and use that in your templates?

Yeah, I did this: https://github.com/plusForta/kirby-xml-sitemap/commit/82be075071470ff783018dd72aadc2ba8e9fd7e9 for the sitemap, and created a isTranslated() Page method for the pages:

// returns true if this page is translated for the requested URL
// and specified language.
page::$methods['isTranslated'] = function($page, $lang = null) {
    if ($lang === null) {
        $lang = site()->language();
    }
    return $page->content($lang->code())->exists();
};

And made a small snippet that included (among other things):


if (!$page->isTranslated(site()->language()) && $page->intendedTemplate() !== 'error') {
    go('/error');
}

You might as well do that in the header snippet instead of adding it to every template.

Is this still the way to go?
Checking if a page is translated in template works, but what about links? ex. in the site navigation we loop through some links, we should also test if each link exists… + this interferes with pagination, ex search of a site, returns all pages with the current language code even if they don’t exist translated…

As long as you filter your search, you shouldn’t have a problem, same for pagination, menu etc…

1 Like

We added an extra toggle Published to each page, and for each bleuprint we create the model where we add a new function see below. This works, it’s just a bit of extra code…

    public function isTranslatedAndPublished($langCode): bool
	{
		if ($this->isDraft()) {
			return false;
		}

		if ($this->translation($langCode)->exists()) {

			return $this->content()->published()->toBool();
		}
		return false;
	}

So in the frontend when we request children or a page… we add $page->isTranslatedAndPublished(kirby()->language()->code() or

$pages = ...
 $pages = $pages->filter(function ($child) {
        return $child->isTranslatedAndPublished(
            kirby()
                ->language()
                ->code(),
        );
    });

Is it not better to override another method like the children() or is this the best approach?

No, I don’t think so. And that would have to be done in the parent model, anyway.

What you could do to keep your template/controller clean is to hide the filter logic away in a custom pages method, e.g. filterTranslated() or so.

1 Like