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 =================
|
||||
|
||||
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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user