Skip

Fallback lang attributes

I recently stumbled over a post in the Kirby forums about the HTML lang attribute for multilingual sites.

It made me realise that I never even thought about how to handle the lang attribute when content of a page isn’t fully translated. A good opportunity to diving a bit deeper and writing an article about my learnings and ideas.

What the lang attribute does

The lang attribute helps screen readers pronounce words correctly and enables translation tools to work more reliably. There is automatic language detection for both screen readers and translation tools, but they’re often unreliable. That’s why we have to do this:

<html lang="en">

If you’re dealing with mixed-language content it’s nice to put an additional lang attribute on specific elements. For example, let’s say you’re writing a web dev article and suddenly there’s an Eichhƶrnchen šŸæļø

there's an <span lang="de">Eichhƶrnchen</span> šŸæļø

How Kirby handles translations

Kirby’s language handling is quite straightforward. Once you create a translation of a page, meaning you e.g. add a file like default.de.txt next to your default.en.txt, the page’s content can be returned in that language. If there’s no translation, the default language will be used. You can read more about this in Kirby’s docs.

Setting the lang attribute with Kirby looks something like this:

<html lang="<?= $kirby->language()->code() ?>">

This will set the lang attribute to the current language’s code. If the current language is German, the attribute will be lang="de".

Mixed-language pages

So what happens when a page isn’t fully translated? If a visitor views a German page that falls back to English content, some content is actually in English. Actually, we have a page that’s partially English and partially German. While the menu with other pages’ titles might be fully translated, the content of the article is using the fallback language.

What we need is an easy way to set a lang attribute on our content wrapper, in addition to the lang attribute on our document element, our <html>. To prevent this complex logic from polluting our template code, we can create some custom page methods.

One method to check if the current page is translated:

'isTranslated' => function () {
    return $this->translation(kirby()->language()->code())->exists();
}

And one method to return a lang attribute for the fallback language:

'fallbackLang' => function () {
    if ($this->isTranslated()) return null;
    return 'lang="' . kirby()->defaultLanguage()->code() . '"';
}

The fallbackLang() method returns null when the page is fully translated in the current language, which means no lang attribute is needed since the content matches the language specified in the <html> element. If the page is not translated, it returns a lang attribute with the default language’s code.

Now we can use them in our templates like this:

<main <?= $page->fallbackLang() ?>>

    <?php if($page->isTranslated() === false): ?>
        <p class="notice">
            Oops, this page has not been translated yet.
        </p>
    <?php endif ?>

    <?= $page->text()->kt() ?>

</main>

To be honest, I haven’t worked on a multi language site for some time, but I wanted to publish this article anyways as it’s getting a bit dusty in my drafts. There’s a good chance I’ll update it with some practical learnings in the future.

Have you implemented something similar on your multi language sites? I’d love to hear about your approaches over on Mastodon.