Translate nl en #11
@@ -3,26 +3,7 @@ baseURL = "/"
|
||||
title = ""
|
||||
theme = "chill-theme"
|
||||
defaultContentLanguage = "fr"
|
||||
disableLanguages = ["en", "nl"]
|
||||
|
||||
# Language configuration
|
||||
[languages]
|
||||
[languages.fr]
|
||||
languageCode = "fr-fr"
|
||||
languageName = "Français"
|
||||
weight = 1
|
||||
contentDir = "content"
|
||||
|
||||
[languages.en]
|
||||
languageCode = "en-us"
|
||||
languageName = "English"
|
||||
weight = 2
|
||||
contentDir = "content/en"
|
||||
[languages.nl]
|
||||
languageCode = "nl-nl"
|
||||
languageName = "Nederlands"
|
||||
weight = 3
|
||||
contentDir = "content/nl"
|
||||
defaultContentLanguageInSubdir = false
|
||||
|
||||
# Required Features
|
||||
enableEmoji = true # Enable emoji support
|
||||
@@ -69,14 +50,13 @@ pagination = { pagerSize = 6, path = "page" }
|
||||
category = 'categories'
|
||||
tag = 'tags'
|
||||
|
||||
# Theme Parameters
|
||||
# Theme Parameters shared by every language.
|
||||
# Anything language specific lives under [languages.<lang>.params] below.
|
||||
[params]
|
||||
description = "Logiciel libre d'accompagnement social"
|
||||
author = "Champs-Libres"
|
||||
logo = "/images/logo-darkmode.png" # Path to your logo
|
||||
customCSS = ["css/custom.css"]
|
||||
|
||||
|
||||
# Header Configuration
|
||||
[params.header]
|
||||
background = "header backdrop-blur-sm"
|
||||
@@ -84,7 +64,6 @@ pagination = { pagerSize = 6, path = "page" }
|
||||
menu.linkClass = "text-white hover:text-primary font-bold transition duration-200"
|
||||
|
||||
[params.header.buttons.demo]
|
||||
text = "Démo"
|
||||
url = "http://demo.chill.social/"
|
||||
mobileClass = "block text-center px-6 py-3 rounded-lg font-bold transition duration-200 ease-in-out bg-primary-400 text-white hover:bg-primary-400 hover:scale-105"
|
||||
open_tab = true
|
||||
@@ -93,154 +72,239 @@ pagination = { pagerSize = 6, path = "page" }
|
||||
[params.header.logo]
|
||||
src = "/images/logo-darkmode.png"
|
||||
|
||||
[params.pdfFolder]
|
||||
name = "Voir la brochure CHILL"
|
||||
filename = "Brochure_Chill.pdf"
|
||||
url = "/pdf/Brochure_Chill.pdf"
|
||||
|
||||
# Global CTA Configuration (optional)
|
||||
[params.cta]
|
||||
enable = true
|
||||
title = "Prêt·e à commencer ?"
|
||||
description = "Testez Chill dès aujourd'hui et découvrez comment il peut transformer votre gestion sociale."
|
||||
enable = true
|
||||
|
||||
# Gradient background
|
||||
gradient_from = "#0d242e" # Blue-600
|
||||
gradient_to = "#23627e" # Purple-600
|
||||
gradient_angle = 30
|
||||
# Gradient background
|
||||
gradient_from = "#0d242e" # Blue-600
|
||||
gradient_to = "#23627e" # Purple-600
|
||||
gradient_angle = 30
|
||||
|
||||
# Primary button
|
||||
[params.cta.primary_button]
|
||||
text = "Réserver une démo"
|
||||
url = "/contact?subject=demo"
|
||||
open_tab = false
|
||||
|
||||
# Secondary button
|
||||
[params.cta.secondary_button]
|
||||
text = "Essayer la démo"
|
||||
url = "http://demo.chill.social/"
|
||||
open_tab = true
|
||||
# Social Media Links (optional)
|
||||
[params.social]
|
||||
gitlab = "https://gitlab.com/Chill-Projet/chill-bundles"
|
||||
# Navigation Menu
|
||||
[menu]
|
||||
[[menu.main]]
|
||||
name = "Fonctionnalités"
|
||||
url = "/features"
|
||||
weight = 1
|
||||
[[menu.main]]
|
||||
name = "Secteur d'activités"
|
||||
weight = 2
|
||||
url = "/sector"
|
||||
[menu.main.params]
|
||||
has_submenu = true
|
||||
submenu = [
|
||||
{ name = "Grandes Collectivités", url = "/sector/collectivities" },
|
||||
{ name = "Services et associations", url = "/sector/associations" },
|
||||
]
|
||||
[[menu.main]]
|
||||
name = "Tarification"
|
||||
url = "/pricing"
|
||||
weight = 3
|
||||
[[menu.main]]
|
||||
name = "Ressources"
|
||||
weight = 4
|
||||
[menu.main.params]
|
||||
has_submenu = true
|
||||
submenu = [
|
||||
# { name = "Blog", url = "/resources/blog" },
|
||||
{ name = "Manuels", url = "/resources/manuals" },
|
||||
{ name = "Tutoriels", url = "/resources/tutorials" },
|
||||
{ name = "Installation", url = "/resources/installation" },
|
||||
{ name = "Documentation technique", url = "/resources/technical-documentation" },
|
||||
{ name = "FAQ", url = "/resources/faq" }
|
||||
]
|
||||
[[menu.main]]
|
||||
name = "Nous contacter"
|
||||
url = "/contact"
|
||||
weight = 5
|
||||
[[menu.footer_column]]
|
||||
name = "Champs-Libres"
|
||||
url = "https://www.champs-libres.coop/"
|
||||
weight = 1
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Languages
|
||||
# French is the default language and is served from the site root ("/"),
|
||||
# English lives under "/en/" and Dutch under "/nl/".
|
||||
# ---------------------------------------------------------------------------
|
||||
[languages]
|
||||
|
||||
# ------------------------------- Français ----------------------------------
|
||||
[languages.fr]
|
||||
languageCode = "fr-fr"
|
||||
languageName = "Français"
|
||||
weight = 1
|
||||
contentDir = "content/fr"
|
||||
|
||||
[languages.fr.params]
|
||||
description = "Logiciel libre d'accompagnement social"
|
||||
|
||||
[languages.fr.params.header.buttons.demo]
|
||||
text = "Démo"
|
||||
|
||||
[languages.fr.params.pdfFolder]
|
||||
name = "Voir la brochure CHILL"
|
||||
filename = "Brochure_Chill.pdf"
|
||||
url = "/pdf/Brochure_Chill.pdf"
|
||||
|
||||
[languages.fr.params.cta]
|
||||
title = "Prêt·e à commencer ?"
|
||||
description = "Testez Chill dès aujourd'hui et découvrez comment il peut transformer votre gestion sociale."
|
||||
[languages.fr.params.cta.primary_button]
|
||||
text = "Réserver une démo"
|
||||
url = "/contact?subject=demo"
|
||||
open_tab = false
|
||||
[languages.fr.params.cta.secondary_button]
|
||||
text = "Essayer la démo"
|
||||
url = "http://demo.chill.social/"
|
||||
open_tab = true
|
||||
|
||||
[languages.fr.menu]
|
||||
[[languages.fr.menu.main]]
|
||||
name = "Fonctionnalités"
|
||||
url = "/features"
|
||||
weight = 1
|
||||
[[languages.fr.menu.main]]
|
||||
name = "Secteur d'activités"
|
||||
url = "/sector"
|
||||
weight = 2
|
||||
[languages.fr.menu.main.params]
|
||||
has_submenu = true
|
||||
submenu = [
|
||||
{ name = "Grandes Collectivités", url = "/sector/collectivities" },
|
||||
{ name = "Services et associations", url = "/sector/associations" },
|
||||
]
|
||||
[[languages.fr.menu.main]]
|
||||
name = "Tarification"
|
||||
url = "/pricing"
|
||||
weight = 3
|
||||
[[languages.fr.menu.main]]
|
||||
name = "Ressources"
|
||||
weight = 4
|
||||
[languages.fr.menu.main.params]
|
||||
has_submenu = true
|
||||
submenu = [
|
||||
# { name = "Blog", url = "/resources/blog" },
|
||||
{ name = "Manuels", url = "/resources/manuals" },
|
||||
{ name = "Tutoriels", url = "/resources/tutorials" },
|
||||
{ name = "Installation", url = "/resources/installation" },
|
||||
{ name = "Documentation technique", url = "/resources/technical-documentation" },
|
||||
{ name = "FAQ", url = "/resources/faq" }
|
||||
]
|
||||
[[languages.fr.menu.main]]
|
||||
name = "Nous contacter"
|
||||
url = "/contact"
|
||||
weight = 5
|
||||
|
||||
[[languages.fr.menu.footer_column]]
|
||||
name = "Champs-Libres"
|
||||
url = "https://www.champs-libres.coop/"
|
||||
weight = 1
|
||||
|
||||
# ------------------------------- English -----------------------------------
|
||||
[languages.en]
|
||||
languageCode = "en-us"
|
||||
languageName = "English"
|
||||
weight = 2
|
||||
contentDir = "content/en"
|
||||
|
||||
[languages.en.params]
|
||||
description = "Free and open source software for social work"
|
||||
|
||||
[languages.en.params.header.buttons.demo]
|
||||
text = "Demo"
|
||||
|
||||
[languages.en.params.pdfFolder]
|
||||
name = "See the CHILL brochure"
|
||||
filename = "Brochure_Chill.pdf"
|
||||
url = "/pdf/Brochure_Chill.pdf"
|
||||
|
||||
[languages.en.params.cta]
|
||||
title = "Ready to get started?"
|
||||
description = "Try Chill today and discover how it can transform the way you manage social support."
|
||||
[languages.en.params.cta.primary_button]
|
||||
text = "Book a demo"
|
||||
url = "/en/contact?subject=demo"
|
||||
open_tab = false
|
||||
[languages.en.params.cta.secondary_button]
|
||||
text = "Try the demo"
|
||||
url = "http://demo.chill.social/"
|
||||
open_tab = true
|
||||
|
||||
[languages.en.menu]
|
||||
[[languages.en.menu.main]]
|
||||
name = "Features"
|
||||
url = "/features"
|
||||
weight = 1
|
||||
[[languages.en.menu.main]]
|
||||
name = "Installation"
|
||||
weight = 2
|
||||
[languages.en.menu.main.params]
|
||||
has_submenu = true
|
||||
submenu = [
|
||||
{ name = "By Champs Libres", url = "/install/hosted" },
|
||||
{ name = "Self-hosted", url = "/install/on-premise" }
|
||||
]
|
||||
[[languages.en.menu.main]]
|
||||
name = "Pricing"
|
||||
url = "/pricing"
|
||||
weight = 3
|
||||
[[languages.en.menu.main]]
|
||||
name = "Resources"
|
||||
weight = 4
|
||||
[languages.en.menu.main.params]
|
||||
has_submenu = true
|
||||
submenu = [
|
||||
# { name = "Blog", url = "/resources/blog" },
|
||||
{ name = "Technical Documentation", url = "/resources/technical-documentation" },
|
||||
{ name = "Manuals", url = "/resources/manuals" },
|
||||
{ name = "Tutorials", url = "/resources/tutorials" },
|
||||
{ name = "FAQ", url = "/resources/faq" }
|
||||
]
|
||||
[[languages.en.menu.main]]
|
||||
name = "Contact Us"
|
||||
url = "/contact"
|
||||
weight = 5
|
||||
[[languages.en.menu.main]]
|
||||
name = "Features"
|
||||
url = "/en/features"
|
||||
weight = 1
|
||||
[[languages.en.menu.main]]
|
||||
name = "Sectors"
|
||||
url = "/en/sector"
|
||||
weight = 2
|
||||
[languages.en.menu.main.params]
|
||||
has_submenu = true
|
||||
submenu = [
|
||||
{ name = "Large local authorities", url = "/en/sector/collectivities" },
|
||||
{ name = "Services and non-profits", url = "/en/sector/associations" },
|
||||
]
|
||||
[[languages.en.menu.main]]
|
||||
name = "Pricing"
|
||||
url = "/en/pricing"
|
||||
weight = 3
|
||||
[[languages.en.menu.main]]
|
||||
name = "Resources"
|
||||
weight = 4
|
||||
[languages.en.menu.main.params]
|
||||
has_submenu = true
|
||||
submenu = [
|
||||
# { name = "Blog", url = "/en/resources/blog" },
|
||||
{ name = "Manuals", url = "/en/resources/manuals" },
|
||||
{ name = "Tutorials", url = "/en/resources/tutorials" },
|
||||
{ name = "Installation", url = "/en/resources/installation" },
|
||||
{ name = "Technical documentation", url = "/en/resources/technical-documentation" },
|
||||
{ name = "FAQ", url = "/en/resources/faq" }
|
||||
]
|
||||
[[languages.en.menu.main]]
|
||||
name = "Contact us"
|
||||
url = "/en/contact"
|
||||
weight = 5
|
||||
|
||||
[[languages.en.menu.footer_column_1]]
|
||||
name = "Download the CHILL brochure"
|
||||
url = "/pdf/Brochure_Chill.pdf"
|
||||
weight = 1
|
||||
[[languages.en.menu.footer_column]]
|
||||
name = "Champs-Libres"
|
||||
url = "https://www.champs-libres.coop/"
|
||||
weight = 1
|
||||
|
||||
# ------------------------------ Nederlands ---------------------------------
|
||||
[languages.nl]
|
||||
languageCode = "nl-nl"
|
||||
languageName = "Nederlands"
|
||||
weight = 3
|
||||
contentDir = "content/nl"
|
||||
|
||||
[languages.nl.params]
|
||||
description = "Vrije en opensource software voor maatschappelijk werk"
|
||||
|
||||
[languages.nl.params.header.buttons.demo]
|
||||
text = "Demo"
|
||||
|
||||
[languages.nl.params.pdfFolder]
|
||||
name = "Bekijk de CHILL-brochure"
|
||||
filename = "Brochure_Chill.pdf"
|
||||
url = "/pdf/Brochure_Chill.pdf"
|
||||
|
||||
[languages.nl.params.cta]
|
||||
title = "Klaar om te starten?"
|
||||
description = "Probeer Chill vandaag nog en ontdek hoe het uw sociale begeleiding kan transformeren."
|
||||
[languages.nl.params.cta.primary_button]
|
||||
text = "Een demo boeken"
|
||||
url = "/nl/contact?subject=demo"
|
||||
open_tab = false
|
||||
[languages.nl.params.cta.secondary_button]
|
||||
text = "De demo uitproberen"
|
||||
url = "http://demo.chill.social/"
|
||||
open_tab = true
|
||||
|
||||
[languages.nl.menu]
|
||||
[[languages.nl.menu.main]]
|
||||
name = "Functies"
|
||||
url = "/features"
|
||||
weight = 1
|
||||
[[languages.nl.menu.main]]
|
||||
name = "Installatie"
|
||||
weight = 2
|
||||
[languages.nl.menu.main.params]
|
||||
has_submenu = true
|
||||
submenu = [
|
||||
{ name = "Door Champs Libres", url = "/install/hosted" },
|
||||
{ name = "Zelf gehost", url = "/install/on-premise" }
|
||||
]
|
||||
[[languages.nl.menu.main]]
|
||||
name = "Prijsstelling"
|
||||
url = "/pricing"
|
||||
weight = 3
|
||||
[[languages.nl.menu.main]]
|
||||
name = "Bronnen"
|
||||
weight = 4
|
||||
[languages.nl.menu.main.params]
|
||||
has_submenu = true
|
||||
submenu = [
|
||||
{ name = "Technische Documentatie", url = "/resources/technical-documentation" },
|
||||
{ name = "Handleidingen", url = "/resources/manuals" },
|
||||
{ name = "Tutorials", url = "/resources/tutorials" },
|
||||
{ name = "FAQ", url = "/resources/faq" },
|
||||
# { name = "Blog", url = "/resources/blog" }
|
||||
]
|
||||
[[languages.nl.menu.main]]
|
||||
name = "Contacteer Ons"
|
||||
url = "/contact"
|
||||
weight = 5
|
||||
[[languages.nl.menu.main]]
|
||||
name = "Functionaliteiten"
|
||||
url = "/nl/features"
|
||||
weight = 1
|
||||
[[languages.nl.menu.main]]
|
||||
name = "Sectoren"
|
||||
url = "/nl/sector"
|
||||
weight = 2
|
||||
[languages.nl.menu.main.params]
|
||||
has_submenu = true
|
||||
submenu = [
|
||||
{ name = "Grote lokale besturen", url = "/nl/sector/collectivities" },
|
||||
{ name = "Diensten en verenigingen", url = "/nl/sector/associations" },
|
||||
]
|
||||
[[languages.nl.menu.main]]
|
||||
name = "Tarieven"
|
||||
url = "/nl/pricing"
|
||||
weight = 3
|
||||
[[languages.nl.menu.main]]
|
||||
name = "Hulpbronnen"
|
||||
weight = 4
|
||||
[languages.nl.menu.main.params]
|
||||
has_submenu = true
|
||||
submenu = [
|
||||
# { name = "Blog", url = "/nl/resources/blog" },
|
||||
{ name = "Handleidingen", url = "/nl/resources/manuals" },
|
||||
{ name = "Tutorials", url = "/nl/resources/tutorials" },
|
||||
{ name = "Installatie", url = "/nl/resources/installation" },
|
||||
{ name = "Technische documentatie", url = "/nl/resources/technical-documentation" },
|
||||
{ name = "FAQ", url = "/nl/resources/faq" }
|
||||
]
|
||||
[[languages.nl.menu.main]]
|
||||
name = "Contacteer ons"
|
||||
url = "/nl/contact"
|
||||
weight = 5
|
||||
|
||||
[[languages.nl.menu.footer_column_1]]
|
||||
name = "Download de CHILL-brochure"
|
||||
url = "/pdf/Brochure_Chill.pdf"
|
||||
weight = 1
|
||||
[[languages.nl.menu.footer_column]]
|
||||
name = "Champs-Libres"
|
||||
url = "https://www.champs-libres.coop/"
|
||||
weight = 1
|
||||
@@ -52,6 +52,19 @@
|
||||
|
||||
<!-- Canonical URL -->
|
||||
<link rel="canonical" href="{{ .Permalink }}">
|
||||
|
||||
<!-- Alternate language versions of this page -->
|
||||
{{ if hugo.IsMultilingual }}
|
||||
{{ range .AllTranslations }}
|
||||
<link rel="alternate" hreflang="{{ .Language.Lang }}" href="{{ .Permalink }}">
|
||||
{{ end }}
|
||||
{{ with hugo.Sites.Default.Home }}
|
||||
<link rel="alternate" hreflang="x-default" href="{{ .Permalink }}">
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
<!-- Language preference / browser language detection (must run before paint) -->
|
||||
{{ partial "language-detection" . }}
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
@@ -91,17 +104,6 @@
|
||||
<!-- Footer -->
|
||||
{{ partial "footer" . }}
|
||||
|
||||
<!-- Language Preference Script -->
|
||||
{{ if hugo.IsMultilingual }}
|
||||
{{ if .Site.Params.visitorapi_pid }}
|
||||
<script>
|
||||
// Inject VisitorAPI project ID from Hugo config
|
||||
window.HUGO_VISITOR_API_PID = "{{ .Site.Params.visitorapi_pid }}";
|
||||
</script>
|
||||
{{ end }}
|
||||
<script src="{{ "js/language-preference.js" | relURL }}"></script>
|
||||
{{ end }}
|
||||
|
||||
<!-- Mobile Menu Script -->
|
||||
<script>
|
||||
const mobileMenuButton = document.getElementById('mobile-menu-button');
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ .Site.LanguageCode | default "en" }}" class="h-full">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<!-- Google Tag Manager -->
|
||||
{{ partial "google-tag-manager" . }}
|
||||
|
||||
<!-- Google Analytics -->
|
||||
{{ partial "google-analytics" . }}
|
||||
|
||||
<!-- Title -->
|
||||
<title>{{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ .Title }} | {{ .Site.Title }}{{ end }}</title>
|
||||
|
||||
<!-- Meta Tags -->
|
||||
<meta name="description" content="{{ with .Description }}{{ . }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}">
|
||||
<meta name="author" content="{{ .Site.Params.author | default .Site.Title }}">
|
||||
<meta name="robots" content="{{ with .Params.robots }}{{ . }}{{ else }}index, follow{{ end }}">
|
||||
|
||||
<!-- Open Graph -->
|
||||
<meta property="og:title" content="{{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ .Title }} | {{ .Site.Title }}{{ end }}">
|
||||
<meta property="og:description" content="{{ with .Description }}{{ . }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}">
|
||||
<meta property="og:type" content="{{ if .IsHome }}website{{ else }}article{{ end }}">
|
||||
<meta property="og:url" content="{{ .Permalink }}">
|
||||
<meta property="og:site_name" content="{{ .Site.Title }}">
|
||||
{{ with .Params.image }}
|
||||
<meta property="og:image" content="{{ . | absURL }}">
|
||||
{{ else }}
|
||||
{{ with .Site.Params.image }}
|
||||
<meta property="og:image" content="{{ . | absURL }}">
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
<!-- Twitter Card -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="{{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ .Title }} | {{ .Site.Title }}{{ end }}">
|
||||
<meta name="twitter:description" content="{{ with .Description }}{{ . }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}">
|
||||
{{ with .Site.Params.twitter }}
|
||||
<meta name="twitter:site" content="@{{ . }}">
|
||||
{{ end }}
|
||||
{{ with .Params.image }}
|
||||
<meta name="twitter:image" content="{{ . | absURL }}">
|
||||
{{ else }}
|
||||
{{ with .Site.Params.image }}
|
||||
<meta property="twitter:image" content="{{ . | absURL }}">
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/x-icon" href="{{ "images/favicon.ico" | relURL }}">
|
||||
|
||||
<!-- Canonical URL -->
|
||||
<link rel="canonical" href="{{ .Permalink }}">
|
||||
|
||||
<!-- Alternate languages -->
|
||||
{{ if hugo.IsMultilingual }}
|
||||
{{ range .AllTranslations }}
|
||||
<link rel="alternate" hreflang="{{ .Lang }}" href="{{ .Permalink }}">
|
||||
{{ end }}
|
||||
{{ with .Sites.Default.Home }}
|
||||
<link rel="alternate" hreflang="x-default" href="{{ .Permalink }}">
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
<!-- Language preference / browser language detection (must run before paint) -->
|
||||
{{ partial "language-detection" . }}
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Plus+Jakarta+Sans:wght@600;700;800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- Main CSS -->
|
||||
<link rel="stylesheet" href="{{ "css/style.css" | relURL }}">
|
||||
|
||||
<!-- Additional Meta Tags from Front Matter -->
|
||||
{{ with .Params.customMeta }}
|
||||
{{ range . }}
|
||||
<meta {{ range $key, $value := . }} {{ $key }}="{{ $value }}"{{ end }}>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
<!-- Custom Head Content -->
|
||||
{{ partial "custom-head" . }}
|
||||
</head>
|
||||
<body class="min-h-screen flex flex-col">
|
||||
<!-- Google Tag Manager (noscript) -->
|
||||
{{ if and hugo.IsProduction .Site.Params.googleTagManager }}
|
||||
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id={{ .Site.Params.googleTagManager }}"
|
||||
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
|
||||
{{ end }}
|
||||
<!-- End Google Tag Manager (noscript) -->
|
||||
<!-- Header -->
|
||||
<div class="fixed top-0 left-0 right-0 z-50">
|
||||
{{ partial "header" . }}
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="pt-20">
|
||||
{{ block "main" . }}{{ end }}
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
{{ partial "footer" . }}
|
||||
|
||||
<!-- Language Preference Script -->
|
||||
{{ if hugo.IsMultilingual }}
|
||||
{{ if .Site.Params.visitorapi_pid }}
|
||||
<script>
|
||||
// Inject VisitorAPI project ID from Hugo config
|
||||
window.HUGO_VISITOR_API_PID = "{{ .Site.Params.visitorapi_pid }}";
|
||||
</script>
|
||||
{{ end }}
|
||||
<script src="{{ "js/language-preference.js" | relURL }}"></script>
|
||||
{{ end }}
|
||||
|
||||
<!-- Mobile Menu Script -->
|
||||
<script>
|
||||
const mobileMenuButton = document.getElementById('mobile-menu-button');
|
||||
if (mobileMenuButton) {
|
||||
mobileMenuButton.addEventListener('click', function() {
|
||||
const mobileMenu = document.getElementById('mobile-menu');
|
||||
if (mobileMenu) {
|
||||
mobileMenu.classList.toggle('hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -130,10 +130,10 @@
|
||||
<div class="mt-6 pt-8 border-t border-gray-600">
|
||||
<div class="flex flex-col md:flex-row justify-between items-center space-y-4 md:space-y-0">
|
||||
<p class="text-gray-600 text-sm">
|
||||
© {{ now.Format "2006" }} {{ .Site.Title }}. All rights reserved.
|
||||
© {{ now.Format "2006" }} {{ with .Site.Title }}{{ . }}. {{ end }}{{ i18n "allRightsReserved" }}
|
||||
</p>
|
||||
<a href="https://www.champs-libres.coop/" class="text-sm text-gray-600 hover:text-primary-600" target="_blank" rel="noopener noreferrer">
|
||||
Designed by Champs-Libres
|
||||
{{ i18n "designedBy" }} Champs-Libres
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -89,21 +89,7 @@
|
||||
{{ end }}
|
||||
|
||||
<!-- Mobile Language Switcher -->
|
||||
{{ if hugo.IsMultilingual }}
|
||||
<div class="py-4 border-t border-gray-200 mt-4">
|
||||
<div class="text-lg text-gray-600 font-semibold mb-2">{{ i18n "language" }}</div>
|
||||
{{ range .Site.Languages }}
|
||||
{{ if eq $.Site.Language.Lang .Lang }}
|
||||
<span class="block py-2 text-primary-600 font-bold">{{ .LanguageName }}</span>
|
||||
{{ else }}
|
||||
{{ $langURL := printf "/%s/" .Lang }}
|
||||
<a href="{{ $langURL }}" class="language-switch-link block py-2 text-gray-700 hover:text-primary-600" data-lang="{{ .Lang }}">
|
||||
{{ .LanguageName }}
|
||||
</a>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ partial "language-switcher-mobile" . }}
|
||||
|
||||
{{ if not $headerConfig.hideButtons }}
|
||||
<div class="pt-4 space-y-4">
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
{{/*
|
||||
Language preference + browser language detection.
|
||||
|
||||
Rendered inline in <head> on purpose: it must run before the page paints so
|
||||
a visitor is never shown a flash of the wrong language, and inlining avoids
|
||||
an extra HTTP request for ~1 KB of script.
|
||||
|
||||
Hugo hands the script everything it needs, so the script never has to guess
|
||||
URLs: the home page of every language and the translations of *this* page.
|
||||
That keeps it correct even though French is served from the site root ("/")
|
||||
while the other languages sit under a prefix ("/en/", "/nl/").
|
||||
*/}}
|
||||
{{ if hugo.IsMultilingual }}
|
||||
{{ $homes := dict }}
|
||||
{{ range hugo.Sites }}
|
||||
{{ $homes = merge $homes (dict .Language.Lang .Home.RelPermalink) }}
|
||||
{{ end }}
|
||||
{{ $translations := dict }}
|
||||
{{ range .AllTranslations }}
|
||||
{{ $translations = merge $translations (dict .Lang .RelPermalink) }}
|
||||
{{ end }}
|
||||
<script>
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var STORAGE_KEY = 'hugo_preferred_language';
|
||||
var SESSION_KEY = 'language_redirected';
|
||||
|
||||
var CURRENT = {{ .Site.Language.Lang }};
|
||||
var HOMES = {{ $homes | jsonify | safeJS }};
|
||||
var TRANSLATIONS = {{ $translations | jsonify | safeJS }};
|
||||
var AVAILABLE = Object.keys(HOMES);
|
||||
|
||||
function read(store, key) {
|
||||
try { return window[store].getItem(key); } catch (e) { return null; }
|
||||
}
|
||||
|
||||
function write(store, key, value) {
|
||||
try { window[store].setItem(key, value); } catch (e) { /* private mode */ }
|
||||
}
|
||||
|
||||
/* Match a browser language tag ("nl-BE", "en") against the languages this
|
||||
site actually publishes, comparing on the primary subtag. */
|
||||
function matchLanguage(tag) {
|
||||
var primary = String(tag).toLowerCase().split('-')[0];
|
||||
for (var i = 0; i < AVAILABLE.length; i++) {
|
||||
if (AVAILABLE[i].toLowerCase().split('-')[0] === primary) {
|
||||
return AVAILABLE[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function detectFromBrowser() {
|
||||
var tags = navigator.languages || (navigator.language ? [navigator.language] : []);
|
||||
for (var i = 0; i < tags.length; i++) {
|
||||
var match = matchLanguage(tags[i]);
|
||||
if (match) { return match; }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function targetFor(lang) {
|
||||
/* Prefer the translation of the page being viewed, so a deep link keeps
|
||||
the visitor on the same page in their language. */
|
||||
return TRANSLATIONS[lang] || HOMES[lang] || null;
|
||||
}
|
||||
|
||||
function resolveAndRedirect() {
|
||||
/* Auto-redirect at most once per browsing session: after that the
|
||||
visitor is free to navigate to any language by hand. */
|
||||
if (read('sessionStorage', SESSION_KEY)) { return; }
|
||||
write('sessionStorage', SESSION_KEY, '1');
|
||||
|
||||
var stored = read('localStorage', STORAGE_KEY);
|
||||
var wanted = (stored && HOMES[stored]) ? stored : detectFromBrowser();
|
||||
|
||||
if (!wanted || wanted === CURRENT) { return; }
|
||||
|
||||
var target = targetFor(wanted);
|
||||
if (target) {
|
||||
window.location.replace(target + window.location.search + window.location.hash);
|
||||
}
|
||||
}
|
||||
|
||||
/* Remember an explicit choice made through the language switcher. */
|
||||
function rememberChoices() {
|
||||
var links = document.querySelectorAll('.language-switch-link');
|
||||
for (var i = 0; i < links.length; i++) {
|
||||
links[i].addEventListener('click', function () {
|
||||
var lang = this.getAttribute('data-lang');
|
||||
if (lang) { write('localStorage', STORAGE_KEY, lang); }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
resolveAndRedirect();
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', rememberChoices);
|
||||
} else {
|
||||
rememberChoices();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,24 @@
|
||||
{{/*
|
||||
Mobile language switcher, used inside the collapsed navigation menu.
|
||||
Same translation-aware linking as the desktop switcher.
|
||||
*/}}
|
||||
{{ if hugo.IsMultilingual }}
|
||||
{{ $page := . }}
|
||||
<div class="py-4 border-t border-gray-200 mt-4">
|
||||
<div class="text-lg text-gray-600 font-semibold mb-2">{{ i18n "language" }}</div>
|
||||
{{ range hugo.Sites }}
|
||||
{{ $lang := .Language.Lang }}
|
||||
{{ if eq $.Site.Language.Lang $lang }}
|
||||
<span class="block py-2 text-primary-600 font-bold">{{ .Language.LanguageName }}</span>
|
||||
{{ else }}
|
||||
{{ $target := .Home }}
|
||||
{{ range $page.AllTranslations }}
|
||||
{{ if eq .Lang $lang }}{{ $target = . }}{{ end }}
|
||||
{{ end }}
|
||||
<a href="{{ $target.RelPermalink }}" hreflang="{{ $lang }}" lang="{{ $lang }}" class="language-switch-link block py-2 text-gray-700 hover:text-primary-600" data-lang="{{ $lang }}">
|
||||
{{ .Language.LanguageName }}
|
||||
</a>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
@@ -1,8 +1,16 @@
|
||||
{{/*
|
||||
Desktop language switcher.
|
||||
|
||||
Links point at the translation of the *current* page when one exists, so
|
||||
switching language keeps the visitor where they are. When the current page
|
||||
has no translation we fall back to the home page of the target language.
|
||||
*/}}
|
||||
{{ if hugo.IsMultilingual }}
|
||||
{{ $page := . }}
|
||||
{{ $headerConfig := .Site.Params.header }}
|
||||
{{ $dropdownConfig := $headerConfig.menu.dropdown }}
|
||||
<div class="relative group">
|
||||
<button class="flex items-center {{ with $headerConfig.menu.linkClass }}{{ . }}{{ else }}text-base text-gray-900 hover:text-primary-600 font-bold transition duration-200{{ end }}">
|
||||
<button type="button" aria-label="{{ i18n "switchLanguage" }}" class="flex items-center {{ with $headerConfig.menu.linkClass }}{{ . }}{{ else }}text-base text-gray-900 hover:text-primary-600 font-bold transition duration-200{{ end }}">
|
||||
{{ .Site.Language.LanguageName }}
|
||||
<svg class="ml-2 w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
||||
@@ -10,15 +18,19 @@
|
||||
</button>
|
||||
<div class="absolute right-0 mt-2 {{ $dropdownConfig.width | default "w-72" }} opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 ease-in-out">
|
||||
<div class="{{ $dropdownConfig.container_padding | default "py-6" }} {{ $dropdownConfig.background | default "bg-white" }} {{ $dropdownConfig.border | default "border border-gray-100" }} {{ $dropdownConfig.shadow | default "shadow-xl" }} {{ $dropdownConfig.radius | default "rounded-lg" }}">
|
||||
{{ range .Site.Languages }}
|
||||
{{ if ne $.Site.Language.Lang .Lang }}
|
||||
{{ $langURL := printf "/%s/" .Lang }}
|
||||
<a href="{{ $langURL }}" class="language-switch-link block {{ $dropdownConfig.item_padding | default "px-8 py-3" }} {{ $dropdownConfig.text_size | default "text-sm" }} {{ $dropdownConfig.text_color | default "text-gray-700" }} {{ $dropdownConfig.hover_background | default "hover:bg-gray-50" }}" data-lang="{{ .Lang }}">
|
||||
{{ .LanguageName }}
|
||||
{{ range hugo.Sites }}
|
||||
{{ $lang := .Language.Lang }}
|
||||
{{ if ne $.Site.Language.Lang $lang }}
|
||||
{{ $target := .Home }}
|
||||
{{ range $page.AllTranslations }}
|
||||
{{ if eq .Lang $lang }}{{ $target = . }}{{ end }}
|
||||
{{ end }}
|
||||
<a href="{{ $target.RelPermalink }}" hreflang="{{ $lang }}" lang="{{ $lang }}" class="language-switch-link block {{ $dropdownConfig.item_padding | default "px-8 py-3" }} {{ $dropdownConfig.text_size | default "text-sm" }} {{ $dropdownConfig.text_color | default "text-gray-700" }} {{ $dropdownConfig.hover_background | default "hover:bg-gray-50" }}" data-lang="{{ $lang }}">
|
||||
{{ .Language.LanguageName }}
|
||||
</a>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -14,8 +14,8 @@ Author: Jeroen van der Schee
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ if gt (len .images) 1 }}
|
||||
<button class="carousel-prev" aria-label="Précédent">❮</button>
|
||||
<button class="carousel-next" aria-label="Suivant">❯</button>
|
||||
<button class="carousel-prev" aria-label="{{ i18n "previous" }}">❮</button>
|
||||
<button class="carousel-next" aria-label="{{ i18n "next" }}">❯</button>
|
||||
|
||||
<div class="carousel-dots">
|
||||
{{ range $i, $_ := .images }}
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
showResult('Erreur réseau, veuillez ré-essayer.', false);
|
||||
showResult({{ i18n "networkError" }}, false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,183 +0,0 @@
|
||||
/**
|
||||
* Language Preference Manager
|
||||
* Stores user's language selection in localStorage and auto-redirects on subsequent visits
|
||||
* Supports automatic language detection via VisitorAPI
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// Configuration
|
||||
const STORAGE_KEY = 'hugo_preferred_language';
|
||||
const SESSION_KEY = 'language_redirected';
|
||||
|
||||
// Get VisitorAPI project ID from Hugo config (injected via template)
|
||||
const VISITOR_API_PID = window.HUGO_VISITOR_API_PID || null;
|
||||
|
||||
/**
|
||||
* Initialize the language preference system
|
||||
*/
|
||||
function init() {
|
||||
// Check and redirect on page load (before DOM ready for faster redirect)
|
||||
checkAndRedirect();
|
||||
|
||||
// Attach click handlers after DOM is ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', attachLanguageSwitchers);
|
||||
} else {
|
||||
attachLanguageSwitchers();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we should redirect to user's preferred language
|
||||
*/
|
||||
function checkAndRedirect() {
|
||||
try {
|
||||
const preferredLang = getStoredLanguage();
|
||||
const currentLang = getCurrentLanguage();
|
||||
|
||||
// If user has a stored preference and it matches current language, we're good
|
||||
if (preferredLang && preferredLang === currentLang) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If user has a stored preference, use it
|
||||
if (preferredLang && preferredLang !== currentLang && isValidLanguage(preferredLang)) {
|
||||
console.log('Language preference: Redirecting to stored preference:', preferredLang);
|
||||
redirectToLanguage(preferredLang);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
// Silently fail if localStorage/sessionStorage is unavailable
|
||||
console.warn('Language preference: Storage unavailable', e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach click event listeners to all language switch links
|
||||
*/
|
||||
function attachLanguageSwitchers() {
|
||||
const links = document.querySelectorAll('.language-switch-link');
|
||||
|
||||
links.forEach(function(link) {
|
||||
link.addEventListener('click', handleLanguageClick);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle click on language switch link
|
||||
* @param {Event} e - Click event
|
||||
*/
|
||||
function handleLanguageClick(e) {
|
||||
const lang = e.currentTarget.getAttribute('data-lang');
|
||||
|
||||
if (lang) {
|
||||
try {
|
||||
setStoredLanguage(lang);
|
||||
console.log('Language preference: Stored user preference:', lang);
|
||||
} catch (e) {
|
||||
console.warn('Language preference: Could not save preference', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Let the link navigate normally (don't prevent default)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stored language preference from localStorage
|
||||
* @returns {string|null} Language code or null
|
||||
*/
|
||||
function getStoredLanguage() {
|
||||
try {
|
||||
return localStorage.getItem(STORAGE_KEY);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the language preference in localStorage
|
||||
* @param {string} lang - Language code to store
|
||||
*/
|
||||
function setStoredLanguage(lang) {
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, lang);
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current language from the URL path
|
||||
* @returns {string} Current language code (e.g., 'en', 'zh-cn')
|
||||
*/
|
||||
function getCurrentLanguage() {
|
||||
const path = window.location.pathname;
|
||||
|
||||
// Match language code pattern: /en/ or /zh-cn/ at start of path
|
||||
const match = path.match(/^\/([a-z]{2}(-[a-z]{2})?)\//i);
|
||||
|
||||
if (match) {
|
||||
return match[1].toLowerCase();
|
||||
}
|
||||
|
||||
// If no language in path, assume default language (root path)
|
||||
// This handles cases like "/" which might be the default language
|
||||
return getDefaultLanguage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default language from HTML lang attribute or fallback
|
||||
* @returns {string} Default language code
|
||||
*/
|
||||
function getDefaultLanguage() {
|
||||
const htmlLang = document.documentElement.getAttribute('lang');
|
||||
if (htmlLang) {
|
||||
// Extract primary language code (e.g., 'en-US' -> 'en')
|
||||
return htmlLang.split('-')[0].toLowerCase();
|
||||
}
|
||||
return 'en'; // Fallback
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a language code is valid by looking for available language links
|
||||
* @param {string} lang - Language code to validate
|
||||
* @returns {boolean} True if valid
|
||||
*/
|
||||
function isValidLanguage(lang) {
|
||||
// Check if any language switch link exists for this language
|
||||
const link = document.querySelector('.language-switch-link[data-lang="' + lang + '"]');
|
||||
return link !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to a specific language, preserving the current path
|
||||
* @param {string} targetLang - Target language code
|
||||
*/
|
||||
function redirectToLanguage(targetLang) {
|
||||
const currentPath = window.location.pathname;
|
||||
const search = window.location.search;
|
||||
const hash = window.location.hash;
|
||||
|
||||
let newPath;
|
||||
|
||||
// Check if current path has a language prefix
|
||||
if (currentPath.match(/^\/[a-z]{2}(-[a-z]{2})?\//i)) {
|
||||
// Replace existing language: /en/about -> /zh-cn/about
|
||||
newPath = currentPath.replace(
|
||||
/^\/[a-z]{2}(-[a-z]{2})?\//i,
|
||||
'/' + targetLang + '/'
|
||||
);
|
||||
} else {
|
||||
// Add language prefix: /about -> /zh-cn/about
|
||||
newPath = '/' + targetLang + currentPath;
|
||||
}
|
||||
|
||||
// Perform redirect
|
||||
window.location.href = newPath + search + hash;
|
||||
}
|
||||
|
||||
// Initialize when script loads
|
||||
init();
|
||||
|
||||
})();
|
||||
Reference in New Issue
Block a user