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:
24
src/app.js
24
src/app.js
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user