feat: i18n pilot — shared JS snippet + ichbinotto.de translation
Add shared/i18n.js: client-side language detection snippet. - Detects browser language via navigator.language - Falls back to German, stores preference in localStorage - Swaps text via data-de/data-en attributes on any element - Handles <title>, <meta>, and regular elements - Optional toggle button via data-i18n-toggle attribute - Exposes window.onepagerI18n API for programmatic use Pilot implementation on ichbinotto.de: - All visible text annotated with data-de/data-en - Language toggle button in footer - Title and meta description translated Implements Gitea issue #1 (pilot phase).
This commit is contained in:
77
shared/i18n.js
Normal file
77
shared/i18n.js
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* i18n for onepager sites.
|
||||
*
|
||||
* Add data-de="..." data-en="..." to any translatable element.
|
||||
* The element's initial innerHTML is the German default.
|
||||
*
|
||||
* Include at bottom of <body>:
|
||||
* <script src="/shared/i18n.js"></script>
|
||||
*
|
||||
* Detection: localStorage override > navigator.language > German default.
|
||||
*
|
||||
* Optional language toggle: add data-i18n-toggle to a <button>.
|
||||
* The button text updates to show the other language (DE/EN).
|
||||
*/
|
||||
(function () {
|
||||
var SUPPORTED = ['de', 'en'];
|
||||
var DEFAULT = 'de';
|
||||
var KEY = 'onepager-lang';
|
||||
|
||||
function detect() {
|
||||
var stored = null;
|
||||
try { stored = localStorage.getItem(KEY); } catch (e) { /* private browsing */ }
|
||||
if (stored && SUPPORTED.indexOf(stored) !== -1) return stored;
|
||||
var nav = (navigator.language || navigator.userLanguage || '').slice(0, 2).toLowerCase();
|
||||
return SUPPORTED.indexOf(nav) !== -1 ? nav : DEFAULT;
|
||||
}
|
||||
|
||||
function apply(lang) {
|
||||
document.documentElement.lang = lang;
|
||||
|
||||
var els = document.querySelectorAll('[data-de][data-en]');
|
||||
for (var i = 0; i < els.length; i++) {
|
||||
var el = els[i];
|
||||
var val = el.getAttribute('data-' + lang);
|
||||
if (val === null) continue;
|
||||
var tag = el.tagName;
|
||||
if (tag === 'TITLE') {
|
||||
document.title = val;
|
||||
} else if (tag === 'META') {
|
||||
el.setAttribute('content', val);
|
||||
} else {
|
||||
el.innerHTML = val;
|
||||
}
|
||||
}
|
||||
|
||||
// Update toggle buttons
|
||||
var toggles = document.querySelectorAll('[data-i18n-toggle]');
|
||||
for (var j = 0; j < toggles.length; j++) {
|
||||
toggles[j].textContent = lang === 'de' ? 'EN' : 'DE';
|
||||
toggles[j].setAttribute('aria-label',
|
||||
lang === 'de' ? 'Switch to English' : 'Auf Deutsch wechseln');
|
||||
}
|
||||
|
||||
try { localStorage.setItem(KEY, lang); } catch (e) { /* private browsing */ }
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
var current = document.documentElement.lang || DEFAULT;
|
||||
apply(current === 'de' ? 'en' : 'de');
|
||||
}
|
||||
|
||||
function init() {
|
||||
apply(detect());
|
||||
var toggles = document.querySelectorAll('[data-i18n-toggle]');
|
||||
for (var k = 0; k < toggles.length; k++) {
|
||||
toggles[k].addEventListener('click', toggle);
|
||||
}
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
|
||||
window.onepagerI18n = { apply: apply, toggle: toggle, detect: detect };
|
||||
})();
|
||||
Reference in New Issue
Block a user