mirror of
https://github.com/Shik3i/KoalaSync.git
synced 2026-08-28 19:47:25 +00:00
feat(website): implement custom i18n static compiler & full 6-language expansion
- Added pure Node.js dynamic i18n static site generator (build.js). - Structured locales for English, German, French, Spanish, Brazilian Portuguese, and Russian. - Replaced two-state toggle with premium glassmorphic language select dropdown. - Integrated robust segment-based locale routing with safe dynamic fallbacks for legal and invite pages. - Audited Core Web Vitals (LCP preloads, CLS dimensions) and SEO structures (robots, sitemap). - Added dedicated Localization section to README and created contributor TRANSLATION guide.
This commit is contained in:
+94
-30
@@ -310,7 +310,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
const updateDynamicVersion = async () => {
|
||||
try {
|
||||
const response = await fetch('version.json');
|
||||
const versionPath = document.documentElement.lang === 'de' ? '../version.json' : 'version.json';
|
||||
const response = await fetch(versionPath);
|
||||
if (!response.ok) return;
|
||||
const data = await response.json();
|
||||
const { version, date } = data;
|
||||
@@ -443,41 +444,102 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
}
|
||||
|
||||
// Language Selection Umschalter
|
||||
const toggleLanguage = (e) => {
|
||||
if (e) e.preventDefault();
|
||||
// Dynamically localize home links on root dynamic pages (impressum, datenschutz, join)
|
||||
const localizeHomeLinks = () => {
|
||||
const html = document.documentElement;
|
||||
const currentIsEnglish = html.classList.contains('lang-en');
|
||||
const newLang = currentIsEnglish ? 'de' : 'en';
|
||||
html.classList.remove('lang-en', 'lang-de');
|
||||
html.classList.add('lang-' + newLang);
|
||||
html.lang = newLang;
|
||||
localStorage.setItem('koala_lang', newLang);
|
||||
|
||||
// Update titles dynamically based on page
|
||||
var path = window.location.pathname;
|
||||
var isIndex = path === '/' || path.endsWith('index.html') || path.split('/').pop() === '';
|
||||
var isJoin = path.includes('join');
|
||||
|
||||
if (isIndex) {
|
||||
const titles = {
|
||||
en: 'KoalaSync | Real-time Video Synchronization for Friends',
|
||||
de: 'KoalaSync | Echtzeit-Video-Synchronisation für Freunde'
|
||||
};
|
||||
document.title = titles[newLang] || titles.en;
|
||||
} else if (isJoin) {
|
||||
const titles = {
|
||||
en: 'Join Room | KoalaSync',
|
||||
de: 'Raum beitreten | KoalaSync'
|
||||
};
|
||||
document.title = titles[newLang] || titles.en;
|
||||
const activeLang = localStorage.getItem('koala_lang') || (navigator.language.startsWith('de') ? 'de' : 'en');
|
||||
const path = window.location.pathname;
|
||||
const pathSegments = path.split('/');
|
||||
const isSubdir = pathSegments.some(seg => ['de', 'fr', 'es', 'pt-BR', 'ru'].includes(seg));
|
||||
|
||||
// Only need to do this dynamic rewrite if we are NOT already inside a localized subdirectory
|
||||
if (!isSubdir) {
|
||||
const homeLinks = document.querySelectorAll('a[href="index.html"], a[href="de/index.html"], a[href="fr/index.html"], a[href="es/index.html"], a[href="pt-BR/index.html"], a[href="ru/index.html"]');
|
||||
homeLinks.forEach(link => {
|
||||
link.href = (activeLang === 'en') ? 'index.html' : `${activeLang}/index.html`;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
document.querySelectorAll('.lang-toggle').forEach(btn => {
|
||||
btn.addEventListener('click', toggleLanguage);
|
||||
// Modern Language Selector Navigation and State Toggling
|
||||
const handleLanguageChange = (e) => {
|
||||
const select = e.currentTarget;
|
||||
const newLang = select.value;
|
||||
const path = window.location.pathname;
|
||||
|
||||
// Save the user's preference
|
||||
localStorage.setItem('koala_lang', newLang);
|
||||
|
||||
// Determine if we are on a static landing page versus a dynamic utility page
|
||||
const isLegalOrJoin = path.includes('impressum') || path.includes('datenschutz') || path.includes('join');
|
||||
const isIndex = !isLegalOrJoin;
|
||||
|
||||
if (isIndex) {
|
||||
// Static navigation: Route to correct subdirectory
|
||||
const pathSegments = path.split('/');
|
||||
const isSubdir = pathSegments.some(seg => ['de', 'fr', 'es', 'pt-BR', 'ru'].includes(seg));
|
||||
|
||||
let targetPath;
|
||||
if (newLang === 'en') {
|
||||
if (isSubdir) {
|
||||
targetPath = '../index.html';
|
||||
} else {
|
||||
targetPath = 'index.html';
|
||||
}
|
||||
} else {
|
||||
if (isSubdir) {
|
||||
// Switching from one language subdirectory to another (e.g., /de/ to /fr/)
|
||||
targetPath = '../' + newLang + '/index.html';
|
||||
} else {
|
||||
// Switching from root (English) to a language subdirectory (e.g., / to /fr/)
|
||||
targetPath = newLang + '/index.html';
|
||||
}
|
||||
}
|
||||
|
||||
window.location.href = targetPath;
|
||||
} else {
|
||||
// Dynamic page: Toggle classes and update elements dynamically without navigating away
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('lang-en', 'lang-de', 'lang-fr', 'lang-es', 'lang-pt-br', 'lang-ru');
|
||||
|
||||
// Fallback dynamic pages to 'en' if 'de' is not chosen (since fr/es markup is not present)
|
||||
const activeDisplayLang = (newLang === 'de') ? 'de' : 'en';
|
||||
html.classList.add('lang-' + activeDisplayLang);
|
||||
html.lang = activeDisplayLang;
|
||||
|
||||
// Sync all selects on the page to the new value
|
||||
document.querySelectorAll('.lang-dropdown').forEach(sel => {
|
||||
sel.value = newLang;
|
||||
});
|
||||
|
||||
// Update titles dynamically
|
||||
const isJoin = path.includes('join');
|
||||
if (isJoin) {
|
||||
const titles = { en: 'Join Room | KoalaSync', de: 'Raum beitreten | KoalaSync' };
|
||||
document.title = titles[activeDisplayLang] || titles.en;
|
||||
}
|
||||
|
||||
// Localize home links dynamically
|
||||
localizeHomeLinks();
|
||||
}
|
||||
};
|
||||
|
||||
// Register change event listener for the dropdowns
|
||||
document.querySelectorAll('.lang-dropdown').forEach(select => {
|
||||
select.addEventListener('change', handleLanguageChange);
|
||||
});
|
||||
|
||||
// Initialize language select elements to show the current preferred language
|
||||
const initLanguageSelectorValue = () => {
|
||||
const savedLang = localStorage.getItem('koala_lang');
|
||||
const browserLang = navigator.language.startsWith('de') ? 'de' : 'en';
|
||||
const activePref = savedLang || browserLang;
|
||||
|
||||
document.querySelectorAll('.lang-dropdown').forEach(select => {
|
||||
select.value = activePref;
|
||||
});
|
||||
};
|
||||
|
||||
// Impressum Email Obfuscation Click Reveal
|
||||
document.querySelectorAll('.email-reveal').forEach(el => {
|
||||
el.addEventListener('click', function() {
|
||||
@@ -595,4 +657,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
detectBrowserAndElevateBadge();
|
||||
checkInvite();
|
||||
updateDynamicVersion();
|
||||
localizeHomeLinks();
|
||||
initLanguageSelectorValue();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user