getkirby / ideas

This is the backlog of ideas and feature requests from the last two years. Use our new feedback platform to post your new ideas or vote on existing ideas.
https://feedback.getkirby.com
20 stars 0 forks source link

Router: improve path resolution in multilang #499

Open qwerdee opened 4 years ago

qwerdee commented 4 years ago

When content and slugs are translated to english and default language is set to german, kirby's current behavior is as follows:

mysite.com/de/preise -> german content mysite.com/de/pricing -> 404 mysite.com/en/pricing -> english content mysite.com/en/preise -> english content

Despite being a bit inconsistent it does lead to duplicate content since every page has at least two valid url for each non default translation. This number even increases exponentially for each level a page is nested, so a page like /news/february/a-title can have up to 8 valid urls for the same translation, if each parent page has translated slugs as well.

I would argue that it would make sense to implement either or both (via config) of these behaviors:

strict:

each page has only one url per language, everything else results in 404

mysite.com/de/preise -> german content mysite.com/de/pricing -> 404 mysite.com/en/pricing -> english content mysite.com/en/preise-> 404

There is no problem with duplicate content and it is always clear which url is the correct one.

redirect:

each page site has only one url, alternative slugs result in redirect

mysite.com/de/preise -> german content mysite.com/de/pricing -> 301 to /de/preise mysite.com/en/pricing -> english content mysite.com/en/preise -> 301 to /en/pricing

This be more like how kirby handles non-default-lang prefix with default-lang slug, except that it avoids duplicate content via correct redircts and works from the other way round as well. This would mean more work for the router, but of course only if it does not find a page with the given path right away

qwerdee commented 4 years ago

It also would be awesome to be able to deactivate the fallback to default language, if the page is not translated. It is not always what you want.

qwerdee commented 4 years ago

This all is doable as a plugin, however I think kirby's behavior is not really what you would expect, if you don't know about it. Here the plugin I use, with page and pages methods for convenience (eg. filter to only show blog posts that are translated)

<?php

use Kirby\Cms\Page;
use Kirby\Toolkit\Str;
use Kirby\Cms\ModelWithContent;
use Kirby\Exception\NotFoundException;

Kirby::plugin('querdee/better-multilang', [
    'hooks' => [
        'route:after' => function ($route, $path, $method, $result) {
            $language = kirby()->language();
            $defaultLanguage = kirby()->defaultLanguage();

            if ($language === $defaultLanguage) {
                return $result;
            }

            // block untranslated pages to prevent fallback to default language
            if ($result instanceof ModelWithContent && $result->translation($language)->exists() === false) {
                return new NotFoundException();
            }

            // block alternative urls (containing default language slugs) 
            if (
                $result instanceof Page
                && Str::rtrim(implode('/', [$language->baseUrl(), $path]), '/') !== $result->url()
            ) {
                return new NotFoundException();
            }

            return $result;
        },
    ],
    'pageMethods' => [
        'isTranslated' => function (?string $languageCode = null) {
            return $this->translation($languageCode)->exists();
        },
    ],
    'pagesMethods' => [
        'translated' => function (?string $languageCode = null) {
            return $this->filter(fn (ModelWithContent $model) => $model->translation($languageCode)->exists());
        },
    ],
]);