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
This commit is contained in:
2025-12-30 15:43:00 +01:00
parent f0f7cd6779
commit 2e8cd6a050
2 changed files with 128 additions and 62 deletions

View File

@@ -113,17 +113,29 @@ function toggleExpectedResult() {
// ================= LANGUAGE TOGGLE ================= // ================= LANGUAGE TOGGLE =================
function toggleLanguage() { async function toggleLanguage() {
const currentLang = getLanguage(); const currentLang = getLanguage();
const newLang = currentLang === "en" ? "de" : "en"; const newLang = currentLang === "en" ? "de" : "en";
setLanguage(newLang); setLanguage(newLang);
applyTranslations(); applyTranslations();
updateProgressDisplay();
// Reload current lesson to update any dynamic text // Reload lessons in new language
const engineState = lessonEngine.getCurrentState(); 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(); loadCurrentLesson();
} }
updateProgressDisplay();
} }
// ================= HINT SYSTEM ================= // ================= HINT SYSTEM =================
@@ -186,7 +198,7 @@ function saveUserSettings() {
async function initializeModules() { async function initializeModules() {
try { try {
const modules = await loadModules(); const modules = await loadModules(getLanguage());
lessonEngine.setModules(modules); lessonEngine.setModules(modules);
// Use the new renderModuleList function with both callbacks // Use the new renderModuleList function with both callbacks
@@ -317,7 +329,7 @@ function loadCurrentLesson() {
// Hide expected overlay // Hide expected overlay
state.showExpected = false; state.showExpected = false;
elements.expectedOverlay.classList.remove("visible"); elements.expectedOverlay.classList.remove("visible");
elements.showExpectedBtn.textContent = "Show Expected"; elements.showExpectedBtn.textContent = t("showExpected");
elements.showExpectedBtn.classList.remove("btn-primary"); elements.showExpectedBtn.classList.remove("btn-primary");
// Update UI // Update UI

View File

@@ -1,63 +1,112 @@
/** /**
* Lesson Config - Functions for loading lesson configurations * Lesson Config - Functions for loading lesson configurations
* Supports English and German lesson content
*/ */
// Import lesson configs // English lesson imports
import basicSelectorsConfig from "../../lessons/00-basic-selectors.json"; import basicSelectorsEN from "../../lessons/00-basic-selectors.json";
import advancedSelectorsConfig from "../../lessons/01-advanced-selectors.json"; import advancedSelectorsEN from "../../lessons/01-advanced-selectors.json";
import tailwindConfig from "../../lessons/10-tailwind-basics.json"; import boxModelEN from "../../lessons/01-box-model.json";
// CSS lessons import unitsVariablesEN from "../../lessons/05-units-variables.json";
import boxModelConfig from "../../lessons/01-box-model.json"; import transitionsAnimationsEN from "../../lessons/06-transitions-animations.json";
import flexboxConfig from "../../lessons/flexbox.json"; import responsiveEN from "../../lessons/08-responsive.json";
import responsiveConfig from "../../lessons/08-responsive.json"; import tailwindEN from "../../lessons/10-tailwind-basics.json";
import unitsVariablesConfig from "../../lessons/05-units-variables.json"; import htmlElementsEN from "../../lessons/20-html-elements.json";
import transitionsAnimationsConfig from "../../lessons/06-transitions-animations.json"; import htmlFormsBasicEN from "../../lessons/21-html-forms-basic.json";
// HTML lessons import htmlFormsValidationEN from "../../lessons/22-html-forms-validation.json";
import htmlElementsConfig from "../../lessons/20-html-elements.json"; import htmlDetailsSummaryEN from "../../lessons/23-html-details-summary.json";
import htmlFormsBasicConfig from "../../lessons/21-html-forms-basic.json"; import htmlProgressMeterEN from "../../lessons/24-html-progress-meter.json";
import htmlFormsValidationConfig from "../../lessons/22-html-forms-validation.json"; import htmlDatalistEN from "../../lessons/25-html-datalist.json";
import htmlDetailsSummaryConfig from "../../lessons/23-html-details-summary.json"; import htmlDataAttributesEN from "../../lessons/26-html-data-attributes.json";
import htmlProgressMeterConfig from "../../lessons/24-html-progress-meter.json"; import htmlDialogEN from "../../lessons/27-html-dialog.json";
import htmlDatalistConfig from "../../lessons/25-html-datalist.json"; import htmlFormsFieldsetEN from "../../lessons/28-html-forms-fieldset.json";
import htmlDataAttributesConfig from "../../lessons/26-html-data-attributes.json"; import htmlFigureEN from "../../lessons/29-html-figure.json";
import htmlDialogConfig from "../../lessons/27-html-dialog.json"; import htmlTablesEN from "../../lessons/30-html-tables.json";
import htmlFormsFieldsetConfig from "../../lessons/28-html-forms-fieldset.json"; import htmlMarqueeEN from "../../lessons/31-html-marquee.json";
import htmlFigureConfig from "../../lessons/29-html-figure.json"; import htmlSvgEN from "../../lessons/32-html-svg.json";
import htmlTablesConfig from "../../lessons/30-html-tables.json"; import flexboxEN from "../../lessons/flexbox.json";
import htmlMarqueeConfig from "../../lessons/31-html-marquee.json";
import htmlSvgConfig from "../../lessons/32-html-svg.json";
// Module store // German lesson imports
const moduleStore = [ import basicSelectorsDE from "../../lessons/de/00-basic-selectors.json";
htmlElementsConfig, import advancedSelectorsDE from "../../lessons/de/01-advanced-selectors.json";
htmlFormsBasicConfig, import boxModelDE from "../../lessons/de/01-box-model.json";
htmlFormsValidationConfig, import unitsVariablesDE from "../../lessons/de/05-units-variables.json";
htmlDetailsSummaryConfig, import transitionsAnimationsDE from "../../lessons/de/06-transitions-animations.json";
htmlProgressMeterConfig, import responsiveDE from "../../lessons/de/08-responsive.json";
htmlDatalistConfig, import tailwindDE from "../../lessons/de/10-tailwind-basics.json";
htmlDataAttributesConfig, import htmlElementsDE from "../../lessons/de/20-html-elements.json";
htmlDialogConfig, import htmlFormsBasicDE from "../../lessons/de/21-html-forms-basic.json";
htmlFormsFieldsetConfig, import htmlFormsValidationDE from "../../lessons/de/22-html-forms-validation.json";
htmlFigureConfig, import htmlDetailsSummaryDE from "../../lessons/de/23-html-details-summary.json";
htmlTablesConfig, import htmlProgressMeterDE from "../../lessons/de/24-html-progress-meter.json";
htmlMarqueeConfig, import htmlDatalistDE from "../../lessons/de/25-html-datalist.json";
htmlSvgConfig, import htmlDataAttributesDE from "../../lessons/de/26-html-data-attributes.json";
boxModelConfig, import htmlDialogDE from "../../lessons/de/27-html-dialog.json";
flexboxConfig, import htmlFormsFieldsetDE from "../../lessons/de/28-html-forms-fieldset.json";
responsiveConfig, import htmlFigureDE from "../../lessons/de/29-html-figure.json";
unitsVariablesConfig, import htmlTablesDE from "../../lessons/de/30-html-tables.json";
transitionsAnimationsConfig, import htmlMarqueeDE from "../../lessons/de/31-html-marquee.json";
basicSelectorsConfig, import htmlSvgDE from "../../lessons/de/32-html-svg.json";
advancedSelectorsConfig, import flexboxDE from "../../lessons/de/flexbox.json";
tailwindConfig
// 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<Array>} Promise resolving to array of modules * @returns {Promise<Array>} Promise resolving to array of modules
*/ */
export async function loadModules() { export async function loadModules(language = "en") {
return moduleStore.map((module) => ({ const store = language === "de" ? moduleStoreDE : moduleStoreEN;
return store.map((module) => ({
...module, ...module,
lessons: module.lessons.map((lesson) => ({ lessons: module.lessons.map((lesson) => ({
...lesson, ...lesson,
@@ -69,10 +118,12 @@ export async function loadModules() {
/** /**
* Get a module by its ID * Get a module by its ID
* @param {string} moduleId - The module ID to find * @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 * @returns {Object|null} The module object or null if not found
*/ */
export function getModuleById(moduleId) { export function getModuleById(moduleId, language = "en") {
return moduleStore.find((module) => module.id === moduleId) || null; 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 * Add a custom module to the store
* @param {Object} moduleConfig - The module configuration to add * @param {Object} moduleConfig - The module configuration to add
* @param {string} language - Language code ('en' or 'de')
* @returns {boolean} Success status * @returns {boolean} Success status
*/ */
export function addCustomModule(moduleConfig) { export function addCustomModule(moduleConfig, language = "en") {
try { try {
validateModuleConfig(moduleConfig); validateModuleConfig(moduleConfig);
const store = language === "de" ? moduleStoreDE : moduleStoreEN;
// Check if module with same ID already exists // 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) { if (existingIndex >= 0) {
// Replace existing module // Replace existing module
moduleStore[existingIndex] = moduleConfig; store[existingIndex] = moduleConfig;
} else { } else {
// Add new module // Add new module
moduleStore.push(moduleConfig); store.push(moduleConfig);
} }
return true; return true;