fix(lang): redirect par langue reste sur l'hôte courant (.onion compris) + détection langue fiable

- base mailto hote courant via Host header (http pour .onion, https sinon) :
  canonical/hreflang restent sur le domaine actuel, plus de fuite vers le Funnel
- meta refresh rendu relatif et différé à 1.5s pour laisser le JS choisir la langue
- localStorage isolé dans son propre try/catch : la détection navigator s'exécute
  même quand le stockage est bloqué (Tor), plus de Français systématique
This commit is contained in:
riricdev 2026-09-06 16:28:37 +02:00
commit 03c0c134fe

View file

@ -139,9 +139,18 @@ function html(body: string, cache = "no-cache") {
})
}
function langRedirectPage(rest: string): string {
const fr = `${SITE_URL}/fr${rest}`
const en = `${SITE_URL}/en${rest}`
function baseUrl(req: Request, fallback = SITE_URL): string {
const host = (req.headers.get("host") || "").trim()
if (!host) return fallback
// Tor = HTTP, tout le reste est servi en HTTPS (le schéma suit l'hôte courant,
// pour que la redirection de langue reste sur le domaine actuel, .onion compris).
const scheme = host.toLowerCase().endsWith(".onion") ? "http" : "https"
return `${scheme}://${host}`
}
function langRedirectPage(rest: string, base: string): string {
const fr = `${base}/fr${rest}`
const en = `${base}/en${rest}`
const restJs = JSON.stringify(rest)
return `<!doctype html>
<html lang="fr">
@ -154,18 +163,18 @@ function langRedirectPage(rest: string): string {
<link rel="alternate" hreflang="fr" href="${fr}" />
<link rel="alternate" hreflang="en" href="${en}" />
<link rel="alternate" hreflang="x-default" href="${fr}" />
<meta http-equiv="refresh" content="0; url=${fr}" />
<meta http-equiv="refresh" content="1.5; url=/fr${rest}" />
<script>(function(){
var rest=${restJs};
var saved=null;
try{ saved=localStorage.getItem("zektyc-lang"); }catch(e){}
var target="/fr"+rest;
try{
var saved=localStorage.getItem("zektyc-lang");
if(saved==="en"||saved==="fr"){ target="/"+saved+rest; }
else {
var ln=(navigator.language||navigator.userLanguage||"").toLowerCase();
if(saved==="en"||saved==="fr"){
target="/"+saved+rest;
} else {
var ln=(navigator.languages&&navigator.languages.length?navigator.languages[0]:(navigator.language||navigator.userLanguage||"")).toLowerCase();
if(ln.indexOf("en")===0) target="/en"+rest;
}
}catch(e){}
if(target.indexOf("/en")===0){
fetch(target,{method:"HEAD",cache:"no-store"}).then(function(r){
location.replace(r.ok?target:"/fr"+rest);
@ -1288,8 +1297,8 @@ server = serve({
return new Response(null, { status: 301, headers: { Location: `/en/zmap${sub}/` } })
}
const artMatch = matchArticle(pathname, articles)
if (artMatch) return html(langRedirectPage(artPath(artMatch)), "no-cache")
if (isHtmlFile(pathname)) return html(langRedirectPage(pathname === "/" ? "/" : pathname), "no-cache")
if (artMatch) return html(langRedirectPage(artPath(artMatch), baseUrl(req)), "no-cache")
if (isHtmlFile(pathname)) return html(langRedirectPage(pathname === "/" ? "/" : pathname, baseUrl(req)), "no-cache")
}
const p = resolveLangPath(pathname) ?? pathname