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 =================
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

View File

@@ -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<Array>} 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;