Comment supporter le multilinguisme sur un blog Jekyll avec Polyglot (1) - Application du plugin Polyglot & implémentation des balises hreflang alt, du sitemap et du bouton de sélection de langue
Cet article présente le processus d'implémentation du support multilingue sur un blog Jekyll basé sur le thème 'jekyll-theme-chirpy' en utilisant le plugin Polyglot. C'est le premier article de cette série, couvrant l'application du plugin Polyglot et la modification de l'en-tête HTML et du sitemap.
Aperçu
Il y a environ 4 mois, début juillet 2024, j’ai ajouté le support multilingue à ce blog hébergé sur GitHub Pages et basé sur Jekyll en appliquant le plugin Polyglot. Cette série partage les bugs rencontrés lors de l’application du plugin Polyglot au thème Chirpy, leur processus de résolution, ainsi que la méthode pour écrire l’en-tête HTML et le sitemap.xml en tenant compte du SEO. La série se compose de deux articles, et celui-ci est le premier de la série.
- Partie 1 : Application du plugin Polyglot & implémentation des balises hreflang alt, du sitemap et du bouton de sélection de langue (cet article)
- Partie 2 : Dépannage de l’échec de construction du thème Chirpy et des erreurs de fonction de recherche
Exigences
- Le résultat de la construction (pages web) doit pouvoir être fourni en distinguant les chemins par langue (ex.
/posts/ko/
,/posts/ja/
). - Pour minimiser le temps et l’effort supplémentaires nécessaires au support multilingue, il devrait être possible de reconnaître automatiquement la langue en fonction du chemin local où se trouve le fichier (ex.
/_posts/ko/
,/_posts/ja/
) lors de la construction, sans avoir à spécifier manuellement les balises ‘lang’ et ‘permalink’ dans le YAML front matter du fichier Markdown original. - La partie en-tête de chaque page du site doit inclure les balises méta Content-Language appropriées et les balises alternatives hreflang pour répondre aux directives SEO de Google pour la recherche multilingue.
- Il devrait être possible de fournir tous les liens des pages supportant chaque langue sur le site sans omission dans
sitemap.xml
, etsitemap.xml
lui-même ne devrait exister qu’une seule fois dans le chemin racine sans duplication. - Toutes les fonctionnalités fournies par le thème Chirpy doivent fonctionner normalement sur chaque page de langue, et si ce n’est pas le cas, elles doivent être modifiées pour fonctionner correctement.
- Fonctionnement normal des fonctions ‘Recently Updated’, ‘Trending Tags’
- Pas d’erreur lors du processus de construction utilisant GitHub Actions
- Fonctionnement normal de la fonction de recherche de posts en haut à droite du blog
Application du plugin Polyglot
Comme Jekyll ne prend pas en charge nativement les blogs multilingues, un plugin externe doit être utilisé pour implémenter un blog multilingue répondant aux exigences ci-dessus. Après recherche, j’ai constaté que Polyglot est largement utilisé pour l’implémentation de sites web multilingues et peut satisfaire la plupart des exigences ci-dessus, j’ai donc adopté ce plugin.
Installation du plugin
Comme j’utilise Bundler, j’ai ajouté le contenu suivant à Gemfile
.
1
2
3
group :jekyll_plugins do
gem "jekyll-polyglot"
end
Ensuite, exécuter bundle update
dans le terminal terminera automatiquement l’installation.
Si vous n’utilisez pas Bundler, vous pouvez installer directement la gem avec la commande gem install jekyll-polyglot
dans le terminal, puis ajouter le plugin à _config.yml
comme suit :
1
2
plugins:
- jekyll-polyglot
Configuration
Ensuite, ouvrez le fichier _config.yml
et ajoutez le contenu suivant :
1
2
3
4
5
6
# Polyglot Settings
languages: ["en", "ko", "ja", "zh-TW", "es", "pt-BR", "fr", "de"]
default_lang: "en"
exclude_from_localization: ["javascript", "images", "css", "public", "assets", "sitemap"]
parallel_localization: false
lang_from_path: true
- languages : Liste des langues à prendre en charge
- default_lang : Langue de repli par défaut
- exclude_from_localization : Spécifie les expressions régulières des chemins de fichiers/dossiers racine à exclure de la localisation
- parallel_localization : Valeur booléenne indiquant s’il faut paralléliser le traitement multilingue lors de la construction
- lang_from_path : Valeur booléenne, si définie sur ‘true’, elle reconnaîtra et utilisera automatiquement le code de langue inclus dans la chaîne de chemin du fichier Markdown sans avoir à spécifier explicitement l’attribut ‘lang’ dans le YAML front matter à l’intérieur du fichier Markdown
La documentation officielle du protocole Sitemap stipule ce qui suit :
“L’emplacement d’un fichier Sitemap détermine l’ensemble des URL qui peuvent être incluses dans ce Sitemap. Un fichier Sitemap situé à http://example.com/catalog/sitemap.xml peut inclure toutes les URL commençant par http://example.com/catalog/ mais ne peut pas inclure les URL commençant par http://example.com/images/.”
“Il est fortement recommandé de placer votre Sitemap dans le répertoire racine de votre serveur web.”
Pour se conformer à cela, il faut ajouter ‘sitemap.xml’ à la liste ‘exclude_from_localization’ pour s’assurer qu’un seul fichier
sitemap.xml
existe dans le répertoire racine, et non des fichiers de contenu identique créés pour chaque langue, comme dans le mauvais exemple ci-dessous.Mauvais exemple (le contenu de chaque fichier n’est pas différent par langue, tous sont identiques) :
/sitemap.xml
/ko/sitemap.xml
/es/sitemap.xml
/pt-BR/sitemap.xml
/ja/sitemap.xml
/fr/sitemap.xml
/de/sitemap.xml
(Mise à jour du 14.01.2025) Suite à l’acceptation de la Pull Request que j’ai soumise pour renforcer le contenu mentionné ci-dessus dans le README, on peut maintenant trouver les mêmes instructions dans la documentation officielle de Polyglot.
Définir ‘parallel_localization’ sur ‘true’ présente l’avantage de réduire considérablement le temps de construction, mais à partir de juillet 2024, lorsque cette fonctionnalité était activée pour ce blog, il y avait un bug où les titres des liens dans les sections ‘Recently Updated’ et ‘Trending Tags’ de la barre latérale droite de la page n’étaient pas traités correctement et se mélangeaient avec d’autres langues. Cela semble encore instable, donc si vous voulez l’appliquer à votre site, il est nécessaire de tester à l’avance pour vérifier son bon fonctionnement. De plus, cette fonctionnalité n’est pas prise en charge sous Windows, elle doit donc être désactivée.
De plus, dans Jekyll 4.0, vous devez désactiver la génération de sourcemaps CSS comme suit.
1
2
sass:
sourcemap: never # Dans Jekyll 4.0, les sourcemaps SCSS seront générées incorrectement en raison du fonctionnement de Polyglot
Points à noter lors de la rédaction de posts
Voici les points à noter lors de la rédaction de posts multilingues :
- Spécification du code de langue approprié : Il faut spécifier le code de langue ISO approprié en utilisant soit le chemin du fichier (ex.
/_posts/ko/example-post.md
) soit l’attribut ‘lang’ dans le YAML front matter (ex.lang: ko
). Référez-vous aux exemples de la documentation pour développeurs Chrome.
Cependant, bien que la documentation pour développeurs Chrome indique les codes régionaux sous la forme ‘pt_BR’, il faut en réalité utiliser ‘pt-BR’ avec un tiret (-) au lieu d’un underscore (_) pour que cela fonctionne correctement lors de l’ajout ultérieur de balises alternatives hreflang dans l’en-tête HTML.
- Les chemins et noms de fichiers doivent être cohérents.
Pour plus de détails, veuillez consulter le README du dépôt GitHub untra/polyglot.
Modification de l’en-tête HTML et du sitemap
Maintenant, pour le SEO, nous devons insérer la balise méta Content-Language et les balises alternatives hreflang dans l’en-tête HTML de chaque page du blog.
En-tête HTML
À partir de la version 1.8.1, la dernière version en novembre 2024, Polyglot dispose d’une fonctionnalité qui effectue automatiquement les tâches ci-dessus lorsque la balise Liquid {% I18n_Headers %}
est appelée dans la partie en-tête de la page. Cependant, cela suppose que l’attribut ‘permalink’ a été explicitement spécifié pour cette page, et ne fonctionne pas correctement si ce n’est pas le cas.
J’ai donc récupéré le head.html du thème Chirpy puis ajouté directement le contenu suivant. J’ai travaillé en me référant à la page SEO Recipes du blog officiel de Polyglot, mais j’ai modifié pour utiliser l’attribut page.url
à la place si page.permalink
n’existe pas.
1
2
3
4
5
6
<meta http-equiv="Content-Language" content="{{site.active_lang}}">
{% if site.default_lang %}<link rel="alternate" hreflang="{{site.default_lang}}" href="{{site.url}}{{page.url}}" />{% endif %}
{% for lang in site.languages %}{% if lang == site.default_lang %}{% continue %}{% endif %}
<link rel="alternate" hreflang="{{lang}}" href="{{site.url}}/{{lang}}{{page.url}}" />
{% endfor %}
Sitemap
Comme le sitemap généré automatiquement par Jekyll lors de la construction ne prend pas correctement en charge les pages multilingues, créez un fichier sitemap.xml
dans le répertoire racine et entrez le contenu suivant :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
---
layout: content
---
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">
{% for lang in site.languages %}
{% for node in site.pages %}
{% comment %}<!-- vérification très paresseuse pour voir si la page est dans la liste d'exclusion - cela signifie que les pages exclues ne seront pas du tout dans le sitemap, écrivez des exceptions si nécessaire -->{% endcomment %}
{% unless site.exclude_from_localization contains node.path %}
{% comment %}<!-- en supposant que s'il n'y a pas de mise en page attribuée, alors n'incluez pas la page dans le sitemap, vous voudrez peut-être changer cela -->{% endcomment %}
{% if node.layout %}
<url>
<loc>{% if lang == site.default_lang %}{{ node.url | absolute_url }}{% else %}{{ node.url | prepend: lang | prepend: '/' | absolute_url }}{% endif %}</loc>
{% if node.last_modified_at and node.last_modified_at != node.date %}<lastmod>{{ node.last_modified_at | date: '%Y-%m-%dT%H:%M:%S%:z' }}</lastmod>{% elsif node.date %}<lastmod>{{ node.date | date: '%Y-%m-%dT%H:%M:%S%:z' }}</lastmod>{% endif %}
</url>
{% endif %}
{% endunless %}
{% endfor %}
{% comment %}<!-- Ceci parcourt toutes les collections du site, y compris les posts -->{% endcomment %}
{% for collection in site.collections %}
{% for node in site[collection.label] %}
<url>
<loc>{% if lang == site.default_lang %}{{ node.url | absolute_url }}{% else %}{{ node.url | prepend: lang | prepend: '/' | absolute_url }}{% endif %}</loc>
{% if node.last_modified_at and node.last_modified_at != node.date %}<lastmod>{{ node.last_modified_at | date: '%Y-%m-%dT%H:%M:%S%:z' }}</lastmod>{% elsif node.date %}<lastmod>{{ node.date | date: '%Y-%m-%dT%H:%M:%S%:z' }}</lastmod>{% endif %}
</url>
{% endfor %}
{% endfor %}
{% endfor %}
</urlset>
Ajout d’un bouton de sélection de langue dans la barre latérale
(Mise à jour du 05.02.2025) J’ai amélioré le bouton de sélection de langue en le transformant en une liste déroulante.
Créez un fichier _includes/lang-selector.html
et entrez le contenu suivant :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<link rel="stylesheet" href="{{ '/assets/css/lang-selector.css' | relative_url }}">
<div class="lang-dropdown">
<select class="lang-select" onchange="changeLang(this.value)" aria-label="Sélectionner la langue">
{%- for lang in site.languages -%}
<option value="{% if lang == site.default_lang %}{{ page.url }}{% else %}/{{ lang }}{{ page.url }}{% endif %}"
{% if lang == site.active_lang %}selected{% endif %}>
{% case lang %}
{% when 'ko' %}🇰🇷 한국어
{% when 'en' %}🇺🇸 English
{% when 'ja' %}🇯🇵 日本語
{% when 'zh-TW' %}🇹🇼 正體中文
{% when 'es' %}🇪🇸 Español
{% when 'pt-BR' %}🇧🇷 Português
{% when 'fr' %}🇫🇷 Français
{% when 'de' %}🇩🇪 Deutsch
{% else %}{{ lang }}
{% endcase %}
</option>
{%- endfor -%}
</select>
</div>
<script>
function changeLang(url) {
window.location.href = url;
}
</script>
De plus, créez un fichier assets/css/lang-selector.css
et entrez le contenu suivant :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/**
* Style du sélecteur de langue
*
* Définit le style de la liste déroulante de sélection de langue située dans la barre latérale.
* Prend en charge le mode sombre du thème et est optimisé pour l'environnement mobile.
*/
/* Conteneur du sélecteur de langue */
.lang-selector-wrapper {
padding: 0.35rem;
margin: 0.15rem 0;
text-align: center;
}
/* Conteneur de la liste déroulante */
.lang-dropdown {
position: relative;
display: inline-block;
width: auto;
min-width: 120px;
max-width: 80%;
}
/* Élément de sélection */
.lang-select {
/* Style de base */
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
width: 100%;
padding: 0.5rem 2rem 0.5rem 1rem;
/* Police et couleur */
font-family: Lato, "Pretendard JP Variable", "Pretendard Variable", sans-serif;
font-size: 0.95rem;
color: var(--sidebar-muted);
background-color: var(--sidebar-bg);
/* Forme et interaction */
border-radius: var(--bs-border-radius, 0.375rem);
cursor: pointer;
transition: all 0.2s ease;
/* Ajout de l'icône de flèche */
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: right 0.75rem center;
background-size: 1rem;
}
/* Style des emojis de drapeaux */
.lang-select option {
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji", sans-serif;
padding: 0.35rem;
font-size: 1rem;
}
.lang-flag {
display: inline-block;
margin-right: 0.5rem;
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji", sans-serif;
}
/* État au survol */
.lang-select:hover {
color: var(--sidebar-active);
background-color: var(--sidebar-hover);
}
/* État de focus */
.lang-select:focus {
outline: 2px solid var(--sidebar-active);
outline-offset: 2px;
color: var(--sidebar-active);
}
/* Prise en charge de Firefox */
.lang-select:-moz-focusring {
color: transparent;
text-shadow: 0 0 0 var(--sidebar-muted);
}
/* Prise en charge d'IE */
.lang-select::-ms-expand {
display: none;
}
/* Prise en charge du mode sombre */
[data-mode="dark"] .lang-select {
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
}
/* Optimisation pour l'environnement mobile */
@media (max-width: 768px) {
.lang-select {
padding: 0.75rem 2rem 0.75rem 1rem; /* Zone tactile plus grande */
}
.lang-dropdown {
min-width: 140px; /* Zone de sélection plus large sur mobile */
}
}
Ensuite, j’ai ajouté les trois lignes suivantes juste avant la classe “sidebar-bottom” dans le _includes/sidebar.html
du thème Chirpy pour que Jekyll charge le contenu de _includes/lang-selector.html
précédemment écrit lors de la construction de la page :
1
2
3
4
5
6
7
(début)...
<div class="lang-selector-wrapper w-100">
{%- include lang-selector.html -%}
</div>
<div class="sidebar-bottom d-flex flex-wrap align-items-center w-100">
...(fin)
Lecture complémentaire
Suite dans la Partie 2