hotel-am-see/js/i18n.js
Jontil88 2b462bc0e6 Initial commit: Hotel am See website with bilingual i18n (DE/EN)
- Static HTML hotel website with Bootstrap 5
- i18next-based client-side internationalization
- Language switcher (DE/EN) in navbar
- German and English translation JSON files
- Translated: navigation, content, forms, FAQ, legal pages (AGB)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 22:49:57 +01:00

135 lines
3.8 KiB
JavaScript

(function () {
'use strict';
function detectLanguage() {
var stored = localStorage.getItem('i18n-lang');
if (stored) return stored;
var browserLang = navigator.language || navigator.userLanguage || '';
var short = browserLang.split('-')[0].toLowerCase();
if (short === 'en') return 'en';
return 'de';
}
function interpolate(str, options) {
if (!str || !options) return str;
return str.replace(/\{\{(\w+)\}\}/g, function (match, key) {
return options[key] !== undefined ? options[key] : match;
});
}
function translatePage() {
var elements = document.querySelectorAll('[data-i18n]');
elements.forEach(function (el) {
var rawKey = el.getAttribute('data-i18n');
if (!rawKey) return;
var optionsAttr = el.getAttribute('data-i18n-options');
var options = optionsAttr ? JSON.parse(optionsAttr) : null;
// Handle attribute prefixes like [value], [placeholder]
var attrMatch = rawKey.match(/^\[(\w+)\](.+)$/);
if (attrMatch && attrMatch[1] !== 'html') {
var attr = attrMatch[1];
var tKey = attrMatch[2];
var val = i18next.t(tKey, options || {});
val = interpolate(val, options);
el.setAttribute(attr, val);
return;
}
// Handle [html] prefix for innerHTML
var isHtml = false;
var lookupKey = rawKey;
if (rawKey.indexOf('[html]') === 0) {
isHtml = true;
lookupKey = rawKey.substring(6);
}
var translation = i18next.t(lookupKey, options || {});
translation = interpolate(translation, options);
if (isHtml) {
el.innerHTML = translation;
} else {
el.textContent = translation;
}
});
// Update document lang attribute
document.documentElement.lang = i18next.language;
// Update document title
var pageKey = detectPageKey();
if (pageKey) {
var titleTranslation = i18next.t(pageKey + '.page_title');
if (titleTranslation && titleTranslation !== pageKey + '.page_title') {
document.title = titleTranslation;
}
}
// Update active state on language switcher
updateSwitcherActive();
}
function detectPageKey() {
var path = window.location.pathname;
var filename = path.substring(path.lastIndexOf('/') + 1).replace('.html', '');
if (!filename || filename === '' || filename === 'index') return 'index';
return filename;
}
function updateSwitcherActive() {
var lang = i18next.language;
var options = document.querySelectorAll('.lang-option');
options.forEach(function (el) {
if (el.getAttribute('data-lang') === lang) {
el.classList.add('active');
el.setAttribute('aria-current', 'true');
} else {
el.classList.remove('active');
el.removeAttribute('aria-current');
}
});
}
function initSwitcher() {
document.addEventListener('click', function (e) {
var target = e.target.closest('.lang-option');
if (!target) return;
e.preventDefault();
var lang = target.getAttribute('data-lang');
if (lang && lang !== i18next.language) {
i18next.changeLanguage(lang, function () {
localStorage.setItem('i18n-lang', lang);
translatePage();
});
}
});
}
var lng = detectLanguage();
i18next
.use(i18nextHttpBackend)
.init({
lng: lng,
fallbackLng: 'de',
backend: {
loadPath: './locales/{{lng}}.json'
},
interpolation: {
escapeValue: false
}
}, function (err) {
if (err) console.error('i18next init error:', err);
translatePage();
initSwitcher();
// Signal that i18n is ready (used by preloader)
window.i18nReady = true;
document.dispatchEvent(new CustomEvent('i18nReady'));
});
})();