Configurer Zola pour un site multilingues

Optimisation SEO et ajout d'un bouton de choix de langue sur un site partiellement internationalisé.

2025-08-20

Zola est le générateur statique de site (SSG) utilisé pour faire tourner ce site. La structure du site vient directement de la structure de fichiers Markdown qui sont transformés par zola. Il supporte les sites multilingues nativement. Dans cet article, je partage quelques astuces pour configurer correctement un tel site.

Internationalisation avec Zola

Zola est un programme écrit en rust, qui transforme des pages de contenus (des fichiers .md dans le dossier content/) et des templates (des fichiers html définissant comment afficher le contenu) en un site web statiquement généré. Par exemple, le contenu de cette page est écrit en français dans le fichier /content/blog/2025-08-20-zola-intl-routing.md et en anglais dans le fichier /content/blog/2025-08-20-zola-intl-routing.en.md, et rundu en utilisant un template page.html.

Dans les templates, quand un texte doit être traduit, il faut définir une traduction dans le fichier config.toml à la racine. C'est le cas pour les liens en haut de la page. Ils ne sont pas définis dans des pages de contenus, mais ajoutés dans les templates qui s'affichent sur toutes les pages.

Ces liens sont ajoutés ainsi:

<a href="{{ get_url(path="@/projects/_index.md", lang=lang) }}">{{ trans(key="projects", lang=lang) }}</a>
<a href="{{ get_url(path="@/blog/_index.md", lang=lang) }}">{{ trans(key="blog", lang=lang) }}</a>
<a href="{{ get_url(path="@/about.md", lang=lang) }}">{{ trans(key="about", lang=lang) }}</a>

Le paramètre lang est une variable rendue disponible par zola, qui représente la langue courante, get_url permet d'obtenir l'URL de la page indiquée dans la langue spécifiée, et trans() permet d'obtenir une traduction définie dans le fichier config.toml.

URL canonique et alternatives

Les bonnes pratiques SEO suggèrent d'indiquer sur chaque page d'un site multilingue:

Avec zola, cela peut être fait en utilisant la variable section.translations (pour les répertoires contenant plusieurs pages), ou page.translations pour les pages individuelles. Il faut tout d'abord identifier les URL de la page dans chaque langue. Cela peut être fait dans le template de base, par exemple base.html (celui dont les autres templates dérivent).

{% set translations = section.translations | default(value=page.translations) %}
{% if translations %}
    {% set en = translations | filter(attribute="lang", value="en") | first %}
    {% if en %}
        {% set en_url = en.permalink %}
    {% endif %}

    {% set fr = translations | filter(attribute="lang", value="fr") | first %}
    {% if fr %}
        {% set fr_url = fr.permalink %}
    {% endif %}
{% endif %}

On obtient alors l'URL canonique selon la langue courante :

{% if lang == "en" %}
    {% set canonical_url = en_url %}
{% else %}
    {% set canonical_url = fr_url %}
{% endif %}

Puis dans entre les balises head:

<link rel="canonical" href="{{ canonical_url }}" />

{% if fr_url %}
    <link rel="alternate" hreflang="fr" href="{{ fr_url }}" />
{% endif %}
{% if en_url %}
    <link rel="alternate" hreflang="en" href="{{ en_url }}" />
{% endif %}

Ne pas oublier d'ajouter l'attribut langue à la racine:

<html lang="{{ lang }}">

Pagination

Les sections sont définies par un fichier _index.md dans un dossier, avec quelques méta-données, et permettent de grouper des pages. Par exemple, une section peut afficher une liste paginée de projets, ou de posts de blog. C'est le cas par exemple de la section blog de ce site. Dans ce cas, la page peut être paginée. Quand c'est le cas, zola fournit une variable paginator. Mais juste en.permalink ne contient pas les informations de pagination. Il faut rajouter manuellement /page/<pageNumber> comme ceci:

{% set translations = section.translations | default(value=page.translations) %}
{% if translations %}
    {% set en = translations | filter(attribute="lang", value="en") | first %}
    {% if en and paginator and paginator.current_index > 1 %}
        {% set en_url = en.permalink ~ "page/" ~ paginator.current_index %}
    {% elif en %}
        {% set en_url = en.permalink %}
    {% endif %}

    {% set fr = translations | filter(attribute="lang", value="fr") | first %}
    {% if fr and paginator and paginator.current_index > 1 %}
        {% set fr_url = fr.permalink ~ "page/" ~ paginator.current_index %}
    {% elif fr %}
        {% set fr_url = fr.permalink %}
    {% endif %}
{% endif %}

De cette manière, la page 2 de la section blog en anglais peut référencer la page 2 de la section blog en français.

De plus, nous pouvons également spécifier que cette page fait partie d'une série paginée en ajoutant des méta-données indiquant la page précédente et suivante :

{% if paginator.previous %}
  <link rel="prev" href="{{ paginator.previous }}">
{% endif %}

{% if paginator.next %}
  <link rel="next" href="{{ paginator.next }}">
{% endif %}

Changement de langue

Avec les éléments définis précédement, nous pouvons facilement implémenter un commuteur de langue. il suffit d'ajouter un lien vers l'autre langue conditionnellement.

{% if lang == "en" and fr_url %}
    <div class="lang-switch">
        <a href="{{ fr_url }}" aria-label="Switch to French">
            FR
        </a>
    </div>
{% elif lang == "fr" and en_url %}
    <div class="lang-switch">
        <a href="{{ en_url }}" aria-label="Switch to English">
            EN
        </a>
    </div>
{% endif %}

Si la langue courante est la seule disponible, l'autre URL sera nulle et le lien ne serra pas affiché. Il pourrait également être simplement désactivé ou renvoyer vers l'accueil.

Conclusion

Configurer un site multilingues avec zola est tout à fait possible, mais représentait quelques petits ajustements qui ne m'étaient pas directement évidents en lisant la documentation de zola. Cet article est ma tentative de partager les quelques astuces apprises lors de la mise en place de ce site. J'espère que ça pourra aider certains, et je serai heureux de lire vos suggestions pour améliorer cette approche.