From d353221f1c213c72c66ab556a4d2311c83deb748 Mon Sep 17 00:00:00 2001 From: Michael Czechowski Date: Tue, 30 Dec 2025 15:43:00 +0100 Subject: [PATCH] feat(i18n): add dynamic lesson loading by language - Import both EN and DE lesson files in lessons.js - loadModules() now accepts language parameter - toggleLanguage() reloads lessons in new language - initializeModules() uses current language from i18n - Preserves user position when switching languages --- src/app.js | 24 ++++-- src/config/lessons.js | 166 ++++++++++++++++++++++++++++-------------- 2 files changed, 128 insertions(+), 62 deletions(-) diff --git a/src/app.js b/src/app.js index 6165b9b..27fa5f9 100644 --- a/src/app.js +++ b/src/app.js @@ -113,17 +113,29 @@ function toggleExpectedResult() { // ================= LANGUAGE TOGGLE ================= -function toggleLanguage() { +async function toggleLanguage() { const currentLang = getLanguage(); const newLang = currentLang === "en" ? "de" : "en"; setLanguage(newLang); applyTranslations(); - updateProgressDisplay(); - // Reload current lesson to update any dynamic text + + // Reload lessons in new language const engineState = lessonEngine.getCurrentState(); - if (engineState.lesson) { + const currentModuleId = engineState.module?.id; + const currentLessonIndex = engineState.lessonIndex; + + const modules = await loadModules(newLang); + lessonEngine.setModules(modules); + renderModuleList(elements.moduleList, modules, selectModule, selectLesson); + + // Restore position in current module/lesson + if (currentModuleId) { + lessonEngine.setModuleById(currentModuleId); + lessonEngine.setLessonByIndex(currentLessonIndex); loadCurrentLesson(); } + + updateProgressDisplay(); } // ================= HINT SYSTEM ================= @@ -186,7 +198,7 @@ function saveUserSettings() { async function initializeModules() { try { - const modules = await loadModules(); + const modules = await loadModules(getLanguage()); lessonEngine.setModules(modules); // Use the new renderModuleList function with both callbacks @@ -317,7 +329,7 @@ function loadCurrentLesson() { // Hide expected overlay state.showExpected = false; elements.expectedOverlay.classList.remove("visible"); - elements.showExpectedBtn.textContent = "Show Expected"; + elements.showExpectedBtn.textContent = t("showExpected"); elements.showExpectedBtn.classList.remove("btn-primary"); // Update UI diff --git a/src/config/lessons.js b/src/config/lessons.js index 397bd96..1fc8b8c 100644 --- a/src/config/lessons.js +++ b/src/config/lessons.js @@ -1,63 +1,112 @@ /** * Lesson Config - Functions for loading lesson configurations + * Supports English and German lesson content */ -// Import lesson configs -import basicSelectorsConfig from "../../lessons/00-basic-selectors.json"; -import advancedSelectorsConfig from "../../lessons/01-advanced-selectors.json"; -import tailwindConfig from "../../lessons/10-tailwind-basics.json"; -// CSS lessons -import boxModelConfig from "../../lessons/01-box-model.json"; -import flexboxConfig from "../../lessons/flexbox.json"; -import responsiveConfig from "../../lessons/08-responsive.json"; -import unitsVariablesConfig from "../../lessons/05-units-variables.json"; -import transitionsAnimationsConfig from "../../lessons/06-transitions-animations.json"; -// HTML lessons -import htmlElementsConfig from "../../lessons/20-html-elements.json"; -import htmlFormsBasicConfig from "../../lessons/21-html-forms-basic.json"; -import htmlFormsValidationConfig from "../../lessons/22-html-forms-validation.json"; -import htmlDetailsSummaryConfig from "../../lessons/23-html-details-summary.json"; -import htmlProgressMeterConfig from "../../lessons/24-html-progress-meter.json"; -import htmlDatalistConfig from "../../lessons/25-html-datalist.json"; -import htmlDataAttributesConfig from "../../lessons/26-html-data-attributes.json"; -import htmlDialogConfig from "../../lessons/27-html-dialog.json"; -import htmlFormsFieldsetConfig from "../../lessons/28-html-forms-fieldset.json"; -import htmlFigureConfig from "../../lessons/29-html-figure.json"; -import htmlTablesConfig from "../../lessons/30-html-tables.json"; -import htmlMarqueeConfig from "../../lessons/31-html-marquee.json"; -import htmlSvgConfig from "../../lessons/32-html-svg.json"; +// English lesson imports +import basicSelectorsEN from "../../lessons/00-basic-selectors.json"; +import advancedSelectorsEN from "../../lessons/01-advanced-selectors.json"; +import boxModelEN from "../../lessons/01-box-model.json"; +import unitsVariablesEN from "../../lessons/05-units-variables.json"; +import transitionsAnimationsEN from "../../lessons/06-transitions-animations.json"; +import responsiveEN from "../../lessons/08-responsive.json"; +import tailwindEN from "../../lessons/10-tailwind-basics.json"; +import htmlElementsEN from "../../lessons/20-html-elements.json"; +import htmlFormsBasicEN from "../../lessons/21-html-forms-basic.json"; +import htmlFormsValidationEN from "../../lessons/22-html-forms-validation.json"; +import htmlDetailsSummaryEN from "../../lessons/23-html-details-summary.json"; +import htmlProgressMeterEN from "../../lessons/24-html-progress-meter.json"; +import htmlDatalistEN from "../../lessons/25-html-datalist.json"; +import htmlDataAttributesEN from "../../lessons/26-html-data-attributes.json"; +import htmlDialogEN from "../../lessons/27-html-dialog.json"; +import htmlFormsFieldsetEN from "../../lessons/28-html-forms-fieldset.json"; +import htmlFigureEN from "../../lessons/29-html-figure.json"; +import htmlTablesEN from "../../lessons/30-html-tables.json"; +import htmlMarqueeEN from "../../lessons/31-html-marquee.json"; +import htmlSvgEN from "../../lessons/32-html-svg.json"; +import flexboxEN from "../../lessons/flexbox.json"; -// Module store -const moduleStore = [ - htmlElementsConfig, - htmlFormsBasicConfig, - htmlFormsValidationConfig, - htmlDetailsSummaryConfig, - htmlProgressMeterConfig, - htmlDatalistConfig, - htmlDataAttributesConfig, - htmlDialogConfig, - htmlFormsFieldsetConfig, - htmlFigureConfig, - htmlTablesConfig, - htmlMarqueeConfig, - htmlSvgConfig, - boxModelConfig, - flexboxConfig, - responsiveConfig, - unitsVariablesConfig, - transitionsAnimationsConfig, - basicSelectorsConfig, - advancedSelectorsConfig, - tailwindConfig +// German lesson imports +import basicSelectorsDE from "../../lessons/de/00-basic-selectors.json"; +import advancedSelectorsDE from "../../lessons/de/01-advanced-selectors.json"; +import boxModelDE from "../../lessons/de/01-box-model.json"; +import unitsVariablesDE from "../../lessons/de/05-units-variables.json"; +import transitionsAnimationsDE from "../../lessons/de/06-transitions-animations.json"; +import responsiveDE from "../../lessons/de/08-responsive.json"; +import tailwindDE from "../../lessons/de/10-tailwind-basics.json"; +import htmlElementsDE from "../../lessons/de/20-html-elements.json"; +import htmlFormsBasicDE from "../../lessons/de/21-html-forms-basic.json"; +import htmlFormsValidationDE from "../../lessons/de/22-html-forms-validation.json"; +import htmlDetailsSummaryDE from "../../lessons/de/23-html-details-summary.json"; +import htmlProgressMeterDE from "../../lessons/de/24-html-progress-meter.json"; +import htmlDatalistDE from "../../lessons/de/25-html-datalist.json"; +import htmlDataAttributesDE from "../../lessons/de/26-html-data-attributes.json"; +import htmlDialogDE from "../../lessons/de/27-html-dialog.json"; +import htmlFormsFieldsetDE from "../../lessons/de/28-html-forms-fieldset.json"; +import htmlFigureDE from "../../lessons/de/29-html-figure.json"; +import htmlTablesDE from "../../lessons/de/30-html-tables.json"; +import htmlMarqueeDE from "../../lessons/de/31-html-marquee.json"; +import htmlSvgDE from "../../lessons/de/32-html-svg.json"; +import flexboxDE from "../../lessons/de/flexbox.json"; + +// English module store +const moduleStoreEN = [ + htmlElementsEN, + htmlFormsBasicEN, + htmlFormsValidationEN, + htmlDetailsSummaryEN, + htmlProgressMeterEN, + htmlDatalistEN, + htmlDataAttributesEN, + htmlDialogEN, + htmlFormsFieldsetEN, + htmlFigureEN, + htmlTablesEN, + htmlMarqueeEN, + htmlSvgEN, + boxModelEN, + flexboxEN, + responsiveEN, + unitsVariablesEN, + transitionsAnimationsEN, + basicSelectorsEN, + advancedSelectorsEN, + tailwindEN +]; + +// German module store +const moduleStoreDE = [ + htmlElementsDE, + htmlFormsBasicDE, + htmlFormsValidationDE, + htmlDetailsSummaryDE, + htmlProgressMeterDE, + htmlDatalistDE, + htmlDataAttributesDE, + htmlDialogDE, + htmlFormsFieldsetDE, + htmlFigureDE, + htmlTablesDE, + htmlMarqueeDE, + htmlSvgDE, + boxModelDE, + flexboxDE, + responsiveDE, + unitsVariablesDE, + transitionsAnimationsDE, + basicSelectorsDE, + advancedSelectorsDE, + tailwindDE ]; /** - * Load all available modules + * Load all available modules for a given language + * @param {string} language - Language code ('en' or 'de') * @returns {Promise} Promise resolving to array of modules */ -export async function loadModules() { - return moduleStore.map((module) => ({ +export async function loadModules(language = "en") { + const store = language === "de" ? moduleStoreDE : moduleStoreEN; + return store.map((module) => ({ ...module, lessons: module.lessons.map((lesson) => ({ ...lesson, @@ -69,10 +118,12 @@ export async function loadModules() { /** * Get a module by its ID * @param {string} moduleId - The module ID to find + * @param {string} language - Language code ('en' or 'de') * @returns {Object|null} The module object or null if not found */ -export function getModuleById(moduleId) { - return moduleStore.find((module) => module.id === moduleId) || null; +export function getModuleById(moduleId, language = "en") { + const store = language === "de" ? moduleStoreDE : moduleStoreEN; + return store.find((module) => module.id === moduleId) || null; } /** @@ -118,20 +169,23 @@ function validateModuleConfig(config) { /** * Add a custom module to the store * @param {Object} moduleConfig - The module configuration to add + * @param {string} language - Language code ('en' or 'de') * @returns {boolean} Success status */ -export function addCustomModule(moduleConfig) { +export function addCustomModule(moduleConfig, language = "en") { try { validateModuleConfig(moduleConfig); + const store = language === "de" ? moduleStoreDE : moduleStoreEN; + // Check if module with same ID already exists - const existingIndex = moduleStore.findIndex((m) => m.id === moduleConfig.id); + const existingIndex = store.findIndex((m) => m.id === moduleConfig.id); if (existingIndex >= 0) { // Replace existing module - moduleStore[existingIndex] = moduleConfig; + store[existingIndex] = moduleConfig; } else { // Add new module - moduleStore.push(moduleConfig); + store.push(moduleConfig); } return true;