feat: add URL-based language switching (#de, #pl, #es, #ar, #uk)

- Visit #de to switch to German and go to home
- Visit #pl for Polish, #es for Spanish, #ar for Arabic, #uk for Ukrainian
- Language is persisted to localStorage
- URL is cleaned up after switching (hash removed)
This commit is contained in:
2026-01-15 17:47:20 +01:00
parent 8b8e5a19ad
commit a03aa09570
2 changed files with 20 additions and 1 deletions

View File

@@ -1937,6 +1937,13 @@ function handleRoute(shouldUpdateUrl = true) {
case RouteType.LESSON: case RouteType.LESSON:
navigateToLesson(route.moduleId, route.lessonIndex, shouldUpdateUrl); navigateToLesson(route.moduleId, route.lessonIndex, shouldUpdateUrl);
break; break;
case RouteType.LANGUAGE:
// Switch language and redirect to home
setLanguage(route.lang);
applyTranslations();
history.replaceState(null, "", window.location.pathname);
showLandingPage();
return; // Skip updateNavHighlight/updatePageMeta since we're redirecting
default: default:
showLandingPage(); showLandingPage();
} }

View File

@@ -4,6 +4,7 @@
* *
* Route formats: * Route formats:
* - # -> Home landing page * - # -> Home landing page
* - #de, #pl, #ar -> Switch language and go to home
* - #css -> CSS section landing * - #css -> CSS section landing
* - #html -> HTML section landing * - #html -> HTML section landing
* - #tailwind -> Tailwind section landing * - #tailwind -> Tailwind section landing
@@ -18,7 +19,8 @@ export const RouteType = {
HOME: "home", HOME: "home",
SECTION: "section", SECTION: "section",
REFERENCE: "reference", REFERENCE: "reference",
LESSON: "lesson" LESSON: "lesson",
LANGUAGE: "language"
}; };
/** /**
@@ -26,6 +28,11 @@ export const RouteType = {
*/ */
const SECTIONS = ["css", "html", "tailwind"]; const SECTIONS = ["css", "html", "tailwind"];
/**
* Valid language codes for URL-based switching
*/
const LANGUAGES = ["en", "de", "pl", "es", "ar", "uk"];
/** /**
* Parse current URL hash into route info * Parse current URL hash into route info
* @returns {{ type: string, moduleId?: string, lessonIndex?: number, sectionId?: string, refId?: string } | null} * @returns {{ type: string, moduleId?: string, lessonIndex?: number, sectionId?: string, refId?: string } | null}
@@ -44,6 +51,11 @@ export function parseHash() {
if (parts.length === 1) { if (parts.length === 1) {
const segment = parts[0]; const segment = parts[0];
// Language switching (e.g., #de, #pl, #ar)
if (LANGUAGES.includes(segment)) {
return { type: RouteType.LANGUAGE, lang: segment };
}
// Section landing pages // Section landing pages
if (SECTIONS.includes(segment)) { if (SECTIONS.includes(segment)) {
return { type: RouteType.SECTION, sectionId: segment }; return { type: RouteType.SECTION, sectionId: segment };