Public Access
Homepage pilot of the website i18n project. Adds the translated homepage for DE and ES, the shared selector at /static/lang.js, and the hreflang cluster on all three language versions (EN included, or the cluster is asymmetric and ignored). - de/index.html, es/index.html: translator output plus the i18n build (html lang, self-referencing canonical, hreflang cluster, lang.js tag, switcher slot in the footer). - index.html: same cluster + lang.js tag + switcher slot; canonical and og:url normalised to the lowercase host. - static/lang.js: the tested selector (client-side language choice, auto-detect on the EN homepage only, once per session; a stored choice or ?lang= always wins). - sitemap.xml: /de/ and /es/ (priority 1.0, lastmod 2026-10-24). - llms.txt: new '## Languages' section. Two gaps in the build recipe were closed while building: a mirrored page cannot use root-relative source paths (style.css, images/* would resolve to /de/style.css -> 404), so the build now re-points assets at the site root and keeps page links on the EN route until the language page exists; and og:url is now this page's own URL instead of the EN one inherited from the source. Not built yet: support.html, odoo-hosting-2027.html, the blog, the bootcamp page, the legal pages.
43 lines
3.6 KiB
JavaScript
43 lines
3.6 KiB
JavaScript
/* ODOO4projects lang selector + EN-homepage auto-detect, <script defer src="/static/lang.js">.
|
|
Inert on /de/ & /es/; stored choice (?lang= or click) wins forever; detect once per session; no loop.
|
|
ES5, no deps. Contract: work/i18n/I18N-BUILD-PLAN.md */
|
|
(function () {
|
|
"use strict";
|
|
var HOME={en:"/",de:"/de/",es:"/es/"},ORDER=["en","de","es"],KEY="o4p_lang",SESS="o4p_lang_checked",YEAR=31536000;
|
|
function ls(){try{return window.localStorage||null}catch(e){return null}}
|
|
function ss(){try{return window.sessionStorage||null}catch(e){return null}}
|
|
function rd(s,k){try{return s?s.getItem(k):null}catch(e){return null}}
|
|
function wr(s,k,v){try{if(s)s.setItem(k,v)}catch(e){}}
|
|
function gck(k){try{var m=(document.cookie||"").match(new RegExp("(?:^|;\\s*)"+k+"=([^;]*)"));return m?decodeURIComponent(m[1]):null}catch(e){return null}}
|
|
function sck(k,v){try{document.cookie=k+"="+encodeURIComponent(v)+";path=/;max-age="+YEAR+";SameSite=Lax"}catch(e){}}
|
|
function stored(){return rd(ls(),KEY)||gck(KEY)}
|
|
function remember(l){wr(ls(),KEY,l);sck(KEY,l)}
|
|
function cur(){var p=location.pathname||"/";return /^\/de(\/|$)/.test(p)?"de":/^\/es(\/|$)/.test(p)?"es":"en"}
|
|
function enHome(){var p=location.pathname||"/";return p==="/"||p===""||/^\/index\.html$/i.test(p)}
|
|
/* equivalent page in another lang: this page's hreflang alternate, else that lang's homepage */
|
|
function urlFor(l){var a,i,h;try{a=document.querySelectorAll('link[rel="alternate"][hreflang]')}catch(e){a=[]}
|
|
for(i=0;i<(a?a.length:0);i++){h=(a[i].getAttribute("hreflang")||"").toLowerCase();if(!h)continue;
|
|
if(l==="en"&&(h==="en"||h.indexOf("en-")===0))return a[i].href;if(h===l)return a[i].href}return HOME[l]}
|
|
var c=cur();
|
|
/* (1) explicit ?lang= wins and is remembered */
|
|
var q=(location.search||"").match(/[?&]lang=(en|de|es)(?:&|$)/i);
|
|
if(q){var pick=q[1].toLowerCase();remember(pick);if(pick!==c){location.replace(urlFor(pick));return}}
|
|
/* (2) auto-detect: EN homepage only, once per session, never over a stored choice */
|
|
if(c==="en"&&enHome()&&!stored()&&rd(ss(),SESS)!=="1"){wr(ss(),SESS,"1");var pref="";
|
|
try{pref=(navigator.languages&&navigator.languages[0])||navigator.language||""}catch(e){pref=""}
|
|
pref=String(pref).toLowerCase();
|
|
if(pref.indexOf("de")===0){location.replace(HOME.de);return}
|
|
if(pref.indexOf("es")===0){location.replace(HOME.es);return}}
|
|
/* (3) switcher EN | DE | ES */
|
|
render(c);
|
|
function render(current){var nav=document.createElement("nav"),i,a;nav.className="lang-switch";nav.setAttribute("aria-label","Language");
|
|
for(i=0;i<ORDER.length;i++){a=document.createElement("a");a.href=urlFor(ORDER[i]);a.textContent=ORDER[i].toUpperCase();
|
|
a.setAttribute("hreflang",ORDER[i]==="en"?"en-US":ORDER[i]);if(ORDER[i]===current)a.setAttribute("aria-current","true");
|
|
nav.appendChild(a);if(i<ORDER.length-1)nav.appendChild(document.createTextNode(" | "))}
|
|
var slot=null;try{slot=document.querySelector("[data-lang-switcher]")}catch(e){slot=null}
|
|
try{if(!document.getElementById("o4p-lang-style")){var s=document.createElement("style");s.id="o4p-lang-style";
|
|
s.appendChild(document.createTextNode(".lang-switch{font:600 12px/1 Inter,system-ui,sans-serif;letter-spacing:.06em;white-space:nowrap}.lang-switch a{color:inherit;text-decoration:none}.lang-switch a[aria-current=true]{color:#a3801a;text-decoration:underline}.lang-switch--float{position:fixed;top:10px;right:12px;z-index:99;background:rgba(244,240,232,.92);border:1px solid rgba(201,162,39,.4);border-radius:999px;padding:6px 12px;color:#0d1b2e}"));
|
|
document.head.appendChild(s)}
|
|
if(slot){slot.appendChild(nav)}else{nav.className="lang-switch lang-switch--float";document.body.appendChild(nav)}}catch(e){}}
|
|
})();
|