Need help with removing "blog" from URL/slug in multilanguage site

Servus

I have a very simple content structure for a blog I am creating. Under /content/2_blog I create articles in a multi-language site environment.

So when I create a new article with the title “I love kirby” the URL gets generated as

https://blog.mysite/blog/2024-06-26-i-love-kirby

I guess it is obvious why, but I want to get rid of the “blog” in the URL. That is why, after researching, I did two things.

First, I added the following to my config.php:

'routes' => [
        // Let's get rid of the "blog" in the URL
        [
            'pattern' => '(:any)',
            'language' => '*',
            'action'  => function($uid) {
                $page = page($uid);
                if(!$page) $page = page('blog/' . $uid);
                if(!$page) $page = site()->errorPage();
                return site()->visit($page);
            }
        ],
        [
            'pattern' => 'blog/(:any)',
            'language' => '*',
            'action'  => function($uid) {
                go($uid);
            }
        ],
[...]

Also, I created an article.php in site/models:

<?php

use Kirby\Cms\Url;

class ArticlePage extends Page
{
    public function urlForLanguage($language = null, array $options = null): string
    {
        if ($language === null || $this->kirby()->defaultLanguage()->code() === $language) {
            return $this->url = $this->site()->urlForLanguage($language) . '/' . $this->slug($language);
        }
        
        return parent::urlForLanguage($language, $options);
    }
}

Now in my panel, when opening the article it still shows the URL including the “blog” which I do not mind:
image

What I do mind is that after my two changes described above, the “blog” gets successfully removed from the URL when I click to Preview the article from the panel but it I end up getting a 404 File Not Found Error.

Where is my mistake? :slight_smile:

Any input is highly welcome.

Thanks
Andreas

See Page blueprint | Kirby CMS

Thank you, that solves the “cosmetic” problem of the preview URL, but not the actual one that my URL rewriting seems to be errorneous since it produces 404s.

There’s an error in your routes, with the language prop, the first parameter is always the language

 [
            'pattern' => '(:any)',
            'language' => '*',
            'action'  => function($lang, $uid) {
                $page = page($uid);
                if(!$page) $page = page('blog/' . $uid);
                if(!$page) $page = site()->errorPage();
                return site()->visit($page, $lang); // pass $lang here as well
            }
        ],
        [
            'pattern' => 'blog/(:any)',
            'language' => '*',
            'action'  => function($lang, $uid) {
                go($uid);
            }
        ],
1 Like

Thank you a lot - as always @texnixe :+1:

May I ask for your help in this regard again @texnixe since the problem resurfaced: I have a language switcher built into my site which works like this:

<?php foreach($kirby->languages() as $language): ?>
                    
                    <a href="<?php e($page->translation($language->code())->exists(), $page->url($language->code()), page('error')->url($language->code())) ?>"
                        class="<?php e($kirby->language() === $language, ' class="languageactive"') ?>">
                        <div>
                          <?php echo Str::upper($language->code()); ?>
                        </div>
                    </a>
                    
<?php endforeach ?>

Unfortunately, it produces URLs with the /blog included - which I do not understand, because when I loop through my articles on the main page like this:

    <?php foreach($articles as $article): // $articles is provided by site/controller/default.php ?>

I can use the normal

$article->url()

to get the proper URL without the “blog” in the URL.

What am I doing wrog here?

Thanks
Andreas

Without having tested it I think the problem is your model, which only works for the default language, it seems.

You are absolutely right of course - the explanation was right in front of me:

<?php

use Kirby\Cms\Url;

class ArticlePage extends Page
{
    public function urlForLanguage($language = null, array $options = null): string
    {
        if ($language === null || $this->kirby()->defaultLanguage()->code() === $language) {
            return $this->url = $this->site()->urlForLanguage($language) . '/' . $this->slug($language);
        }
        
        return parent::urlForLanguage($language, $options);
    }
}

I did not realize that it only returns the changed URL for the default language NOT for any other provided language which of course makes this a bad piece of code.

class ArticlePage extends Page
{
    public function urlForLanguage($language = null, array $options = null): string
    {
        return $this->url = $this->site()->urlForLanguage($language) . '/' . $this->slug($language);
    }
}

Is cleaner and works with ANY language.

Thanks @texnixe !

PS kudos to ChatGPT to ELI5 the whole thing to me:


Let’s break down the PHP code snippet step by step to understand what it does:

Overview

This code defines a custom class ArticlePage that extends the Page class from Kirby, a content management system (CMS). The primary focus is on the urlForLanguage method, which constructs URLs for different languages.

Code Explanation

  1. Namespace and Import

    use Kirby\Cms\Url;
    
    • This line imports the Url class from the Kirby CMS namespace.
  2. Class Declaration

    class ArticlePage extends Page
    
    • This declares a new class ArticlePage that extends Kirby’s Page class.
  3. Method Definition

    public function urlForLanguage($language = null, array $options = null): string
    
    • This method, urlForLanguage, constructs and returns the URL for the current page in the specified language.
    • Parameters:
      • $language: The language code (optional).
      • $options: An array of additional options (optional).
  4. Language Check

    if ($language === null || $this->kirby()->defaultLanguage()->code() === $language) {
    
    • Checks if the $language parameter is null or if it matches the default language code of the site.
    • null value means no specific language is requested, or it’s the default language.
  5. URL Construction for Default Language

    return $this->url = $this->site()->urlForLanguage($language) . '/' . $this->slug($language);
    
    • Constructs the URL for the default language.
    • Uses $this->site()->urlForLanguage($language) to get the base URL for the specified language.
    • Appends the slug of the page for the specified language using $this->slug($language).
    • Assigns the constructed URL to $this->url and returns it.
  6. URL Construction for Other Languages

    return parent::urlForLanguage($language, $options);
    
    • If the language is not null and not the default language, it calls the parent class’s urlForLanguage method to construct the URL.
    • This allows for fallback to the default behavior provided by the Page class in Kirby.

Summary

The urlForLanguage method in the ArticlePage class constructs a language-specific URL for the page. It first checks if the requested language is null or the default language. If so, it constructs a URL using the site’s URL for that language and appends the page’s slug. For other languages, it falls back to the default urlForLanguage method from the parent Page class.

Example Usage

Suppose you have a page with the slug my-article and you want to get its URL in the default language and another language:

  • Default Language

    $articlePage = new ArticlePage();
    echo $articlePage->urlForLanguage(); // Output: default_language_site_url/my-article
    
  • Specific Language (e.g., ‘fr’ for French)

    echo $articlePage->urlForLanguage('fr'); // Output: site_url_for_french/my-article (assuming fallback)
    

Note

The TODO comment and forum link suggest that there may be some uncertainty about the necessity or functionality of this method, possibly implying that it might not be fully utilized or there are doubts about its effectiveness. This might be an area for further review or testing.