Juicer
Français

Internationalisation (i18n)

Publier un site en plusieurs langues — URL préfixées par langue, navigation par langue, chaînes d’interface traduites et hreflang automatique.

Un site juicer peut livrer le même contenu en plusieurs langues. Chaque langue obtient sa propre arborescence de contenu, son propre espace d’URL et sa propre navigation ; les pages qui existent en plus d’une langue s’interrelient automatiquement, et l’habillage d’interface (libellés de boutons, « Précédent »/« Suivant », champ de recherche, …) est traduit via des dictionnaires. Les sites monolingues ne paient rien pour tout cela — rien ne s’active tant que vous ne déclarez pas une liste languages.

Cette page documente chaque rouage. La documentation de juicer.build que vous lisez est elle-même bilingue (anglais + français), donc tout est éprouvé sur soi.

L’activer

Déclarez les langues et la langue par défaut dans site.toml :

languages             = ["en", "fr"]
defaultLanguage       = "en"
defaultLanguageInRoot = true
  • languages — la liste des codes de langue que vous publiez. Une liste vide ou absente signifie un site monolingue (aucun changement de comportement).
  • defaultLanguage — la langue de repli. Une traduction manquante (d’une page ou d’une chaîne d’interface) retombe sur cette langue. Par défaut, la première entrée de languages ; sur un site sans languages, c’est "en".
  • defaultLanguageInRoot — quand true, la langue par défaut est servie à la racine du site sans préfixe /<code>/, et seules les autres langues obtiennent un préfixe. Quand false (le défaut), chaque langue est préfixée, y compris la langue par défaut.

Disposition du contenu

Une fois languages défini, le contenu réside un répertoire plus bas — sous content/<lang>/ :

content/
├── en/
│   ├── _index.md
│   └── getting-started/
│       ├── _index.md
│       └── installation.md
└── fr/
    ├── _index.md
    └── getting-started/
        ├── _index.md
        └── installation.md

La langue d’une page est le premier segment de chemin sous content/. Deux pages qui partagent le même chemin après le segment de langue — en/getting-started/installation.md et fr/getting-started/installation.md — sont des traductions l’une de l’autre. Vous n’avez pas à tout traduire : une page qui n’existe que dans une langue n’a simplement aucune traduction, et rien ne pointe vers une page manquante.

URL

defaultLanguageInRoot décide de la forme de l’espace d’URL. Avec la configuration ci-dessus (en par défaut, à la racine) :

PageURL
content/en/getting-started/installation.md/getting-started/installation/
content/fr/getting-started/installation.md/fr/getting-started/installation/

La langue par défaut conserve les URL propres, sans préfixe — important quand vous ajoutez une seconde langue à un site existant et ne voulez pas casser les liens entrants. Mettez defaultLanguageInRoot = false pour préfixer chaque langue symétriquement (/en/… et /fr/…).

Tip

Construire des liens préfixés par langue à la main est source d’erreurs. Utilisez les assistants relLangURL / absLangURL — ils appliquent le bon préfixe pour vous.

Données de langue par page

Chaque page expose sa langue et ses traductions dans le contexte de rendu :

  • .page.lang — le code de langue de la page ("" sur un site monolingue).
  • .page.translations — la liste des versions dans les autres langues de cette page. Chaque entrée porte lang, title et url. Vide quand la page n’a pas de traduction.
  • .page.languagestoutes les langues configurées (dans l’ordre déclaré), chacune avec lang, une url vers laquelle basculer et un indicateur current. L’url est l’URL propre de la page pour la langue courante, sa traduction là où il en existe une, et l’accueil de cette langue sinon — l’entrée n’est donc jamais manquante, même sur une page non traduite. Vide pour les sites monolingues.

.page.languages est ce qu’un sélecteur de langue complet itère — il offre toujours chaque langue, à la manière de Starlight ou Docusaurus :

{{ if .page.languages }}
<details aria-label="{{ i18n .page.lang 'aria_language' }}">
  <summary>{{ i18n .page.lang 'langname' }}</summary>
  {{ for l <- .page.languages }}
  <a href="{{ l.url }}" hreflang="{{ l.lang }}"{{ if l.current }} aria-current="true"{{ end }}>
    {{ i18n l.lang 'langname' }}
  </a>
  {{ end }}
</details>
{{ end }}

Le thème juicerdocs livre ceci sous forme de menu déroulant dans sa barre supérieure. (Utilisez plutôt .page.translations quand vous voulez un sélecteur qui ne liste que les traductions existantes — pratique pour une bascule en ligne à deux langues.)

Chaînes d’interface : l’assistant i18n

Le texte d’habillage — libellés de boutons, flèches de pagination, champ de recherche — est recherché par clé plutôt que codé en dur, pour pouvoir être traduit :

{{ i18n .page.lang 'nav_prev' }}

i18n prend un code de langue et une clé. La recherche retombe dans cet ordre :

  1. le dictionnaire de la langue demandée,
  2. le dictionnaire de la defaultLanguage,
  3. la clé littérale (pour qu’une chaîne manquante soit visible, pas vide).

Comme la langue est le premier argument, {{ i18n t.lang 'langname' }} recherche une clé dans le tableau d’une autre langue — c’est ainsi que le sélecteur ci-dessus affiche le nom propre de chaque langue.

Dictionnaires

Les chaînes proviennent de fichiers i18n/<lang>.toml — des tableaux plats clé = "valeur" :

# i18n/fr.toml# i18n/fr.toml
nav_prev = "Précédent"
nav_next = "Suivant"

Les dictionnaires sont recueillis à deux endroits et fusionnés :

  • le(s) thème(s) actif(s)<theme>/i18n/<lang>.toml
  • votre site<src>/i18n/<lang>.toml

avec la même priorité que toute autre ressource superposée par thème : votre site l’emporte sur un thème, et un thème plus tôt dans la chaîne de recherche l’emporte sur un thème plus tardif (hérité). La fusion est par langue et par clé, de sorte qu’un thème peut livrer un dictionnaire d’interface anglais complet pendant que votre site n’ajoute que la poignée de clés qu’il supplante — ou les langues supplémentaires qu’il publie.

Note

C’est pourquoi un thème comme juicerdocs rend un habillage anglais sans aucune configuration : il livre son propre i18n/en.toml. Pour traduire l’habillage, déposez un i18n/fr.toml dans votre site (ou supplantez des clés individuelles) ; vous n’avez jamais à forker le thème.

La barre latérale parcourt .site.root.subsections. Sur un site multilingue, .site.root est délimité à la langue de la page courante — il se résout vers le <lang>/_index.md de cette langue. Ainsi une page française obtient l’arbre de sections français, une page anglaise l’anglais, sans aucun travail par thème. Une section qui n’a pas encore été traduite n’apparaît simplement pas dans la navigation de cette langue.

.page.prev / .page.next — la pagination séquentielle — sont délimités de la même façon : la navigation ne parcourt que l’ordre de lecture de la langue courante, de sorte que la dernière page anglaise ne pointe jamais « suivant » vers le français.

URLs conscientes de la langue

relLangURL et absLangURL sont les compagnons conscients de la langue de relURL / absURL. Ils prennent la langue comme premier argument et préfixent le chemin du segment d’URL de cette langue :

<a href="{{ relLangURL .page.lang '/getting-started/' }}"></a>

Sur une page anglaise (par défaut, à la racine) cela donne /getting-started/ ; sur une page française, /fr/getting-started/. Utilisez-les pour les liens fixes dans les gabarits partagés — comme un appel à l’action de héros — afin qu’ils restent dans la langue du lecteur. (.page.url et .page.translations[].url sont déjà corrects pour la langue ; vous n’avez besoin de relLangURL que pour les chemins que vous écrivez à la main.)

Sitemap et hreflang

Quand un site est multilingue, sitemap.xml gagne automatiquement des entrées <xhtml:link rel="alternate" hreflang="…"> reliant chaque page à ses traductions, pour que les moteurs de recherche servent la bonne langue. Les sites monolingues obtiennent un sitemap ordinaire, sans bruit hreflang. Aucune configuration requise dans un cas comme dans l’autre.

Ce qui reste sans configuration

Warning

Tout ce qui figure sur cette page est dormant tant que languages n’est pas défini. Un site sans liste languages se construit à l’octet près comme avant l’existence de l’i18n : .page.lang vaut "", .page.translations est vide, le sitemap n’a pas de hreflang, le contenu reste dans content/ (pas content/<lang>/), et l’assistant i18n résout toujours les dictionnaires de thème via le repli sur la langue par défaut, de sorte que l’habillage thémé se rend en anglais.

Recherche

Esc
pour naviguer pour ouvrir Esc pour fermer