From 6150ce762d50f0f65f177c3b17c44783b770a202 Mon Sep 17 00:00:00 2001 From: pipeline Date: Tue, 21 Jul 2026 20:54:20 +0300 Subject: [PATCH] fix: harden editorial brand rendering and SEO --- data/locales.yaml | 1 + i18n/de.yaml | 1 + i18n/en.yaml | 1 + layouts/_default/baseof.html | 5 +- layouts/partials/cta-button.html | 7 ++- layouts/partials/footer.html | 14 +++-- layouts/partials/head-seo.html | 54 ++++++++++++----- layouts/partials/locale-switcher.html | 13 +++- .../partials/responsible-gambling-block.html | 10 +++- layouts/partials/schema-jsonld.html | 59 +++++++++++++------ layouts/partials/trust-badges.html | 13 ++-- layouts/robots.txt | 5 ++ layouts/sitemap.xml | 27 +++++++++ 13 files changed, 158 insertions(+), 52 deletions(-) create mode 100644 layouts/sitemap.xml diff --git a/data/locales.yaml b/data/locales.yaml index 1dc5033..4b7795d 100644 --- a/data/locales.yaml +++ b/data/locales.yaml @@ -13,6 +13,7 @@ ageRestriction: 18 responsibleGamblingOrgName: "BeGambleAware / GamCare" responsibleGamblingUrl: "https://www.begambleaware.org" + responsibleGamblingPhone: "0808 8020 133" currencySymbol: "£" requiredKycDocs: - "Passport, driving licence or national ID card" diff --git a/i18n/de.yaml b/i18n/de.yaml index f3685b8..ef2cd2c 100644 --- a/i18n/de.yaml +++ b/i18n/de.yaml @@ -28,6 +28,7 @@ affiliateDisclosure: "Partnerhinweis" allRightsReserved: "Alle Rechte vorbehalten." gambleResponsibly: "Verantwortungsvoll spielen" licenceStatement: "%s ist durch %s lizenziert und reguliert (Lizenznr. %s)." +editorialSiteStatement: "%s ist eine unabhängige Informations- und Affiliate-Website, kein Glücksspielanbieter." overAgeOnly: "Nur für Erwachsene" sslSecured: "SSL-gesichert" licensedOperator: "Lizenzierter iGaming-Anbieter" diff --git a/i18n/en.yaml b/i18n/en.yaml index 2d5d15b..443efb7 100644 --- a/i18n/en.yaml +++ b/i18n/en.yaml @@ -37,6 +37,7 @@ affiliateDisclosure: "Affiliate disclosure" allRightsReserved: "All rights reserved." gambleResponsibly: "Gamble responsibly" licenceStatement: "%s is licensed and regulated by %s (Licence No. %s)." +editorialSiteStatement: "%s is an independent information and affiliate website, not a gambling operator." overAgeOnly: "Adults only" sslSecured: "SSL secured" licensedOperator: "Licensed iGaming operator" diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html index 2f1d580..711e33a 100644 --- a/layouts/_default/baseof.html +++ b/layouts/_default/baseof.html @@ -48,11 +48,10 @@ {{ partial "footer.html" . }} - {{ partial "cookie-consent.html" . }} - {{/* App bundle (age gate, cookie consent, games filter). Loads BEFORE Alpine so the + {{/* App bundle (age gate and games filter). Loads BEFORE Alpine so the gamesFilter component is registered on the alpine:init event. */}} - {{ $appJs := slice (resources.Get "js/age-gate.js") (resources.Get "js/cookie-consent.js") (resources.Get "js/games-filter.js") | resources.Concat "js/app.js" | minify | fingerprint "sha256" }} + {{ $appJs := slice (resources.Get "js/age-gate.js") (resources.Get "js/games-filter.js") | resources.Concat "js/app.js" | minify | fingerprint "sha256" }} {{/* Alpine.js — vendored first-party (assets/js/vendor/alpine.js, pinned 3.14.8, diff --git a/layouts/partials/cta-button.html b/layouts/partials/cta-button.html index b7b8352..6ad942a 100644 --- a/layouts/partials/cta-button.html +++ b/layouts/partials/cta-button.html @@ -15,6 +15,10 @@ {{- $text := .text | default "Visit Casino" -}} {{- $style := .style | default "primary" -}} {{- $size := .size | default "" -}} +{{- $affiliateURL := $brand.affiliateBaseUrl | default "" -}} +{{- $siteURL := strings.TrimSuffix "/" $page.Site.BaseURL -}} +{{- $targetURL := strings.TrimSuffix "/" $affiliateURL -}} +{{- if and $affiliateURL (ne $targetURL $siteURL) -}} {{- $subid := .subid | default "" -}} {{- if not $subid -}} @@ -27,7 +31,7 @@ {{- if not $subid -}}{{- $subid = $page.Type -}}{{- end -}} {{- if or (eq $subid "_index") (eq $subid "") -}}{{- $subid = "home" -}}{{- end -}} -{{- $href := printf "%s?%s=%s" $brand.affiliateBaseUrl $brand.affiliateSubIdParam $subid -}} +{{- $href := printf "%s?%s=%s" $affiliateURL $brand.affiliateSubIdParam $subid -}} {{- $classes := "btn-primary" -}} {{- if eq $style "secondary" -}}{{- $classes = "btn-secondary" -}}{{- end -}} @@ -37,3 +41,4 @@ {{- with .class -}}{{- $classes = printf "%s %s" $classes . -}}{{- end -}} {{ $text }} +{{- end -}} diff --git a/layouts/partials/footer.html b/layouts/partials/footer.html index 40089ab..6c108d7 100644 --- a/layouts/partials/footer.html +++ b/layouts/partials/footer.html @@ -18,6 +18,7 @@

{{ $brand.tagline }}

{{- range $brand.socialLinks -}} + {{- if and .url (not (in .url ".example")) -}} {{- if eq .platform "twitter" -}} @@ -31,8 +32,12 @@ {{ .platform | title }} {{- end -}} + {{- end -}} {{- end -}}
+ {{- with $brand.contactEmail -}} + {{- if not (in . ".example") -}}

{{ . }}

{{- end -}} + {{- end -}} {{/* Explore */}} @@ -52,9 +57,6 @@ {{- range $legal -}}
  • {{ .LinkTitle }}
  • {{- end -}} -
  • - -
  • @@ -74,7 +76,11 @@

    {{ $brand.ageRestrictionDefault }}+ {{ i18n "gambleResponsibly" }} - {{ printf (i18n "licenceStatement") $brand.legalOperatorName $brand.licenseAuthority $brand.licenseNumber }} + {{- if and $brand.legalOperatorName $brand.licenseAuthority $brand.licenseNumber (not (in $brand.licenseNumber "XXX")) -}} + {{ printf (i18n "licenceStatement") $brand.legalOperatorName $brand.licenseAuthority $brand.licenseNumber }} + {{- else -}} + {{ printf (i18n "editorialSiteStatement") $brand.name }} + {{- end -}}

    © {{ now.Year }} {{ $brand.name }}. {{ i18n "allRightsReserved" }} diff --git a/layouts/partials/head-seo.html b/layouts/partials/head-seo.html index 583f252..ef8cde6 100644 --- a/layouts/partials/head-seo.html +++ b/layouts/partials/head-seo.html @@ -3,11 +3,10 @@ fall back sensibly to brand data. */ -}} {{- $brand := .Site.Data.brand -}} -{{- $title := "" -}} -{{- if .IsHome -}} - {{- $title = .Params.title | default (printf "%s | %s" $brand.name $brand.tagline) -}} -{{- else -}} - {{- $title = printf "%s | %s" (.Params.title | default .Title) $brand.name -}} +{{- $pageTitle := .Params.seoTitle | default .Params.title | default .Title -}} +{{- $title := $pageTitle -}} +{{- if and (not .IsHome) (not (in (lower $pageTitle) (lower $brand.name))) -}} + {{- $title = printf "%s | %s" $pageTitle $brand.name -}} {{- end -}} {{- $desc := "" -}} @@ -19,30 +18,55 @@ {{- $desc = $desc | plainify | chomp | truncate 160 -}} {{- /* Per-page OG image override (.Params.image) with fallback to the site default. */ -}} -{{- $ogImage := (.Params.image | default .Site.Params.ogImage) | absURL -}} +{{- $imagePath := .Params.image | default .Site.Params.ogImage -}} +{{- $ogImage := cond (ne $imagePath "") ($imagePath | absURL) "" -}} +{{- $ogImageAlt := .Params.imageAlt | default (printf "%s - %s" $brand.name $pageTitle) -}} +{{- $ogLocale := $brand.defaultLocale | default .Language.LanguageCode | default .Language.Lang -}} +{{- range $brand.supportedLocales -}} + {{- if or (eq . $.Language.LanguageCode) (hasPrefix . (printf "%s-" $.Language.Lang)) -}}{{- $ogLocale = . -}}{{- end -}} +{{- end -}} +{{- $isPlaceholderHost := or (in (lower .Site.BaseURL) "example.com") (in (lower .Site.BaseURL) ".example") -}} +{{- $noindex := or .Params.noindex $isPlaceholderHost (eq .Kind "404") -}} +{{- $schemaType := .Params.schemaType | default "none" -}} +{{- $isArticle := in (slice "Article" "Review") $schemaType -}} +{{- $canonical := .Params.canonicalURL | default .Permalink -}} {{ $title }} - - - +{{- if ne .Kind "404" }} + +{{- end }} + + {{- /* Open Graph */ -}} - + - - - - + +{{- with $ogImage }} + + + + +{{- end }} +{{- if and $isArticle (not .Date.IsZero) }} + +{{- end }} +{{- if and $isArticle (not .Lastmod.IsZero) }} + +{{- end }} {{- /* Twitter */ -}} - +{{- with $ogImage }} + + +{{- end }} {{- /* hreflang alternates for the multilingual build. No-op on single-language sites (.IsTranslated is false), so it is safe to keep permanently. URL diff --git a/layouts/partials/locale-switcher.html b/layouts/partials/locale-switcher.html index 45e7b0a..bd23250 100644 --- a/layouts/partials/locale-switcher.html +++ b/layouts/partials/locale-switcher.html @@ -62,9 +62,20 @@ function init() { var brand = window.__BRAND__ || {}; + var pageLocale = document.documentElement.lang || ""; var stored = null; try { stored = localStorage.getItem("siteLocale"); } catch (e) {} - apply(stored || brand.defaultLocale); + var preferred = stored; + if (pageLocale) { + var locales = window.__LOCALES__ || []; + for (var i = 0; i < locales.length; i++) { + if (locales[i].locale === pageLocale || locales[i].locale.indexOf(pageLocale + "-") === 0) { + preferred = locales[i].locale; + break; + } + } + } + apply(preferred || brand.defaultLocale); // If a region selector is present (reintroduced later), wire it up. document.querySelectorAll("[data-locale-switcher]").forEach(function (sel) { sel.addEventListener("change", function () { apply(this.value); }); diff --git a/layouts/partials/responsible-gambling-block.html b/layouts/partials/responsible-gambling-block.html index af9d110..f04d23c 100644 --- a/layouts/partials/responsible-gambling-block.html +++ b/layouts/partials/responsible-gambling-block.html @@ -6,21 +6,27 @@ {{- $variant := .variant | default "full" -}} {{- $brand := $page.Site.Data.brand -}} {{- $locales := $page.Site.Data.locales -}} -{{- $currentLocale := $page.Language.LanguageCode | default $brand.defaultLocale -}} +{{- $currentLocale := $brand.defaultLocale -}} +{{- range $brand.supportedLocales -}} + {{- if or (eq . $page.Language.LanguageCode) (hasPrefix . (printf "%s-" $page.Language.Lang)) -}}{{- $currentLocale = . -}}{{- end -}} +{{- end -}} {{- $default := "" -}} {{- with where $locales "locale" $currentLocale -}}{{- $default = index . 0 -}}{{- end -}} {{- $age := $brand.ageRestrictionDefault -}} {{- $orgName := "your national support service" -}} {{- $orgUrl := "#" -}} +{{- $orgPhone := "" -}} {{- with $default -}} {{- $age = .ageRestriction -}} {{- $orgName = .responsibleGamblingOrgName -}} {{- $orgUrl = .responsibleGamblingUrl -}} + {{- $orgPhone = .responsibleGamblingPhone | default "" -}} {{- end -}} {{- if eq $variant "compact" -}}

    {{ printf (i18n "rgCompactIntro") $age }}

    {{ i18n "rgNeedHelp" }} {{ $orgName }}.

    + {{- with $orgPhone }}

    Free helpline: {{ . }}.

    {{ end -}} {{- else -}}

    @@ -52,7 +58,7 @@ {{ .label | default .locale }} {{ .ageRestriction }}+ - {{ .responsibleGamblingOrgName }} + {{ .responsibleGamblingOrgName }}{{ with .responsibleGamblingPhone }}
    {{ . }}{{ end }} {{- end -}} diff --git a/layouts/partials/schema-jsonld.html b/layouts/partials/schema-jsonld.html index dab8cac..8823f61 100644 --- a/layouts/partials/schema-jsonld.html +++ b/layouts/partials/schema-jsonld.html @@ -16,18 +16,16 @@ "url" .Site.BaseURL "logo" ($brand.logoPath | absURL) "description" $brand.tagline - "email" $brand.contactEmail - "foundingDate" (printf "%d" (int $brand.foundedYear)) "sameAs" $sameAs -}} + {{- with $brand.contactEmail -}}{{- if not (in . ".example") -}}{{- $data = merge $data (dict "email" .) -}}{{- end -}}{{- end -}} + {{- with $brand.foundedYear -}}{{- if gt (int .) 0 -}}{{- $data = merge $data (dict "foundingDate" (printf "%d" (int .))) -}}{{- end -}}{{- end -}} {{- end -}} -{{- if or .IsHome (eq $type "FAQPage") .Params.showFaqAccordion -}} - {{- $faqKey := printf "faq.%s" .Language.Lang -}} - {{- $faqs := index .Site.Data $faqKey -}} - {{- if not $faqs -}}{{- $faqs = .Site.Data.faq -}}{{- end -}} +{{- if eq $type "FAQPage" -}} + {{- $faqs := .Params.faqItems | default .Site.Data.faq -}} {{- $items := slice -}} {{- range $faqs -}} {{- $items = $items | append (dict @@ -36,41 +34,64 @@ "acceptedAnswer" (dict "@type" "Answer" "text" .answer) ) -}} {{- end -}} - {{- $data := dict - "@context" "https://schema.org" - "@type" "FAQPage" - "mainEntity" $items - -}} - + {{- if gt (len $items) 0 -}} + {{- $data := dict + "@context" "https://schema.org" + "@type" "FAQPage" + "mainEntity" $items + -}} + + {{- end -}} {{- end -}} -{{- if and (eq $type "Article") .Params.showRatingWidget -}} +{{- if eq $type "Review" -}} {{- $authorName := .Params.authorName | default $brand.name -}} {{- $authorType := cond (isset .Params "authorName") "Person" "Organization" -}} {{- $data := dict "@context" "https://schema.org" "@type" "Review" "headline" (.Params.h1 | default .Title) + "description" (.Params.description | default .Summary | plainify) "url" .Permalink - "datePublished" (.Date.Format "2006-01-02") - "dateModified" (.Lastmod.Format "2006-01-02") "author" (dict "@type" $authorType "name" $authorName) "publisher" (dict "@type" "Organization" "name" $brand.name "logo" (dict "@type" "ImageObject" "url" ($brand.logoPath | absURL))) - "itemReviewed" (dict "@type" "Organization" "name" $brand.name) + "itemReviewed" (dict "@type" "Organization" "name" (.Params.reviewedOrganization | default .Title)) "reviewRating" (dict "@type" "Rating" "ratingValue" (.Params.rating | default $brand.ratingValue) "bestRating" 5 "worstRating" 1) -}} + {{- if not .Date.IsZero -}}{{- $data = merge $data (dict "datePublished" (.Date.Format "2006-01-02")) -}}{{- end -}} + {{- if not .Lastmod.IsZero -}}{{- $data = merge $data (dict "dateModified" (.Lastmod.Format "2006-01-02")) -}}{{- end -}} {{- else if eq $type "Article" -}} + {{- $authorName := .Params.authorName | default $brand.name -}} + {{- $authorType := cond (isset .Params "authorName") "Person" "Organization" -}} {{- $data := dict "@context" "https://schema.org" "@type" "Article" "headline" (.Params.h1 | default .Title) + "description" (.Params.description | default .Summary | plainify) "url" .Permalink - "datePublished" (.Date.Format "2006-01-02") - "dateModified" (.Lastmod.Format "2006-01-02") - "author" (dict "@type" "Organization" "name" $brand.name) + "mainEntityOfPage" (dict "@type" "WebPage" "@id" .Permalink) + "author" (dict "@type" $authorType "name" $authorName) "publisher" (dict "@type" "Organization" "name" $brand.name "logo" (dict "@type" "ImageObject" "url" ($brand.logoPath | absURL))) -}} + {{- with (.Params.image | default .Site.Params.ogImage) -}}{{- $data = merge $data (dict "image" (. | absURL)) -}}{{- end -}} + {{- if not .Date.IsZero -}}{{- $data = merge $data (dict "datePublished" (.Date.Format "2006-01-02")) -}}{{- end -}} + {{- if not .Lastmod.IsZero -}}{{- $data = merge $data (dict "dateModified" (.Lastmod.Format "2006-01-02")) -}}{{- end -}} +{{- else if and (eq $type "HowTo") .Params.howToSteps -}} + {{- $steps := slice -}} + {{- range .Params.howToSteps -}} + {{- $steps = $steps | append (dict "@type" "HowToStep" "name" .name "text" .text) -}} + {{- end -}} + {{- if gt (len $steps) 0 -}} + {{- $data := dict + "@context" "https://schema.org" + "@type" "HowTo" + "name" (.Params.h1 | default .Title) + "description" (.Params.description | default .Summary | plainify) + "step" $steps + -}} + + {{- end -}} {{- end -}} diff --git a/layouts/partials/trust-badges.html b/layouts/partials/trust-badges.html index c335f3b..bf73631 100644 --- a/layouts/partials/trust-badges.html +++ b/layouts/partials/trust-badges.html @@ -7,16 +7,15 @@ {{ $brand.ageRestrictionDefault }} {{ i18n "overAgeOnly" }} - - - {{ $brand.licenseAuthority }} - + {{- if and $brand.licenseAuthority $brand.licenseNumber (not (in $brand.licenseNumber "XXX")) }} + + + {{ $brand.licenseAuthority }} + + {{- end }} {{ i18n "sslSecured" }} - - {{ i18n "licensedOperator" }} -

    diff --git a/layouts/robots.txt b/layouts/robots.txt index 6b4c001..d359c5f 100644 --- a/layouts/robots.txt +++ b/layouts/robots.txt @@ -1,4 +1,9 @@ User-agent: * +{{- if or (in (lower .Site.BaseURL) "example.com") (in (lower .Site.BaseURL) ".example") }} +Disallow: / +{{- else }} Allow: / +Disallow: /admin/ Sitemap: {{ "sitemap.xml" | absURL }} +{{- end }} diff --git a/layouts/sitemap.xml b/layouts/sitemap.xml new file mode 100644 index 0000000..3cfbe27 --- /dev/null +++ b/layouts/sitemap.xml @@ -0,0 +1,27 @@ +{{- printf "" | safeHTML }} + + {{- if not (or (in (lower .Site.BaseURL) "example.com") (in (lower .Site.BaseURL) ".example")) }} + {{- $language := .Language.Lang }} + {{- range where .Site.AllPages "Language.Lang" $language }} + {{- if and (not .Params.noindex) (ne .Kind "404") (ne .Kind "taxonomy") (ne .Kind "term") }} + + {{ .Permalink }} + {{- if not .Lastmod.IsZero }} + {{ .Lastmod.Format "2006-01-02" }} + {{- end }} + {{- if .IsTranslated }} + {{- range .AllTranslations }} + + {{- end }} + {{- $defaultLang := (index site.Languages 0).Lang }} + {{- range .AllTranslations }} + {{- if eq .Language.Lang $defaultLang }} + + {{- end }} + {{- end }} + {{- end }} + + {{- end }} + {{- end }} + {{- end }} +