/** * Internationalization module for Code Crispies */ const translations = { en: { // Page pageTitle: "Code Crispies - Learn HTML & CSS Interactively", skipLink: "Skip to main content", // Header menuOpen: "Open menu", langSwitch: "EN", langSwitchLabel: "Switch language", help: "Help", // Instructions loading: "Loading...", selectLesson: "Please select a lesson to begin.", editorLabel: "CSS Editor", undoTitle: "Undo (Ctrl+Z)", redoTitle: "Redo (Ctrl+Shift+Z)", resetCodeTitle: "Reset to initial code", run: "Run", rerun: "Re-run", // Preview yourOutput: "Your Output", showExpected: "Show Expected", hideExpected: "Hide Expected", previous: "Previous", next: "Next", levelIndicator: "Lesson {current} of {total}", lessonLabel: "Lesson", // Sidebar menu: "Menu", closeMenu: "Close menu", language: "Language", progress: "Progress", progressText: "{percent}% Complete ({completed}/{total})", lessons: "Lessons", settings: "Settings", showHints: "Show Hints", resetAllProgress: "Reset All Progress", openSource: "Open Source:", by: "by", // Help dialog helpTitle: "Help", aboutTitle: "About Code Crispies", aboutText: "Code Crispies is a free, open-source platform for learning web development through hands-on exercises. No account required - just start coding!", learningModesTitle: "Learning Modes", modeCss: "CSS - Write CSS rules to style elements", modeTailwind: "Tailwind - Apply utility classes directly in HTML", modeHtml: "HTML - Practice semantic markup and native elements", gettingStartedTitle: "Getting Started", gettingStartedText: "Open the menu (☰) to browse lesson modules. Each module covers a specific topic with progressive exercises.", completingLessonsTitle: "Completing Lessons", completingStep1: "Read the task instructions on the left", completingStep2: "Write your code in the editor", completingStep3: "Watch the live preview update as you type", completingStep4: "Follow hints to fix any issues", completingStep5: "Click Next when complete", editorToolsTitle: "Editor Tools", editorToolUndo: "↶ Undo / ↷ Redo - Navigate edit history", editorToolReset: "⟲ Reset - Restore initial code for current lesson", editorToolExpected: "Show Expected - Toggle the target result overlay", keyboardShortcutsTitle: "Keyboard Shortcuts", shortcutRun: "Ctrl+Enter - Validate immediately", shortcutUndo: "Ctrl+Z - Undo", shortcutRedo: "Ctrl+Shift+Z - Redo", emmetTitle: "Emmet Shortcuts (HTML mode)", emmetText: "Type abbreviations and press Tab to expand:", emmetClass: "div.box → div with class", emmetChildren: "ul>li*3 → ul with 3 li children", emmetNested: "form>input+button → nested structure", emmetContent: "p{Hello} → p with text content", // More Projects moreProjectsTitle: "More Projects", htmlOverJsDesc: " - Learn to leverage native HTML elements instead of custom JavaScript solutions", mandalaDesc: " - Interactive visualization of JavaScript technologies organized by complexity", // Contact contactTitle: "Contact & Links", contactText: 'Code Crispies is developed by LibreTECH', // Reset dialog resetDialogTitle: "Reset Progress", resetDialogText: "Are you sure you want to reset all your progress? This cannot be undone.", cancel: "Cancel", resetAll: "Reset All", // Reset code dialog resetCodeDialogTitle: "Reset Code", resetCodeDialogText: "Reset your code to the initial state for this lesson?", dontShowAgain: "Don't show this again", reset: "Reset", // Dynamic content loadingFallbackText: "Could not load lesson. Please select one from the menu or check the help.", completed: "Completed", successMessage: "CRISPY! ٩(◕‿◕)۶ Your code works correctly.", keepTrying: "Keep trying!", failedToLoad: "Failed to load modules. Please refresh the page.", tailwindPlaceholder: "Enter Tailwind classes (e.g., bg-blue-500 text-white p-4)", lessonFallback: "Lesson {index}", untitledLesson: "Untitled Lesson" }, de: { // Page pageTitle: "Code Crispies - HTML & CSS interaktiv lernen", skipLink: "Zum Hauptinhalt springen", // Header menuOpen: "Menü öffnen", langSwitch: "DE", langSwitchLabel: "Sprache wechseln", help: "Hilfe", // Instructions loading: "Laden...", selectLesson: "Bitte wähle eine Lektion aus, um zu beginnen.", editorLabel: "CSS-Editor", undoTitle: "Rückgängig (Strg+Z)", redoTitle: "Wiederholen (Strg+Umschalt+Z)", resetCodeTitle: "Auf Anfangscode zurücksetzen", run: "Ausführen", rerun: "Erneut ausführen", // Preview yourOutput: "Dein Ergebnis", showExpected: "Lösung einblenden", hideExpected: "Lösung ausblenden", previous: "Zurück", next: "Weiter", levelIndicator: "Lektion {current} von {total}", lessonLabel: "Lektion", // Sidebar menu: "Menü", closeMenu: "Menü schließen", language: "Sprache", progress: "Fortschritt", progressText: "{percent}% abgeschlossen ({completed}/{total})", lessons: "Lektionen", settings: "Einstellungen", showHints: "Hinweise anzeigen", resetAllProgress: "Fortschritt zurücksetzen", openSource: "Open Source:", by: "von", // Help dialog helpTitle: "Hilfe", aboutTitle: "Über Code Crispies", aboutText: "Code Crispies ist eine kostenlose Open-Source-Plattform zum Erlernen von Webentwicklung durch praktische Übungen. Kein Konto erforderlich - einfach loslegen!", learningModesTitle: "Lernmodi", modeCss: "CSS - Schreibe CSS-Regeln zum Stylen von Elementen", modeTailwind: "Tailwind - Wende Utility-Klassen direkt im HTML an", modeHtml: "HTML - Übe semantisches Markup und native Elemente", gettingStartedTitle: "Erste Schritte", gettingStartedText: "Öffne das Menü (☰), um Lektionsmodule zu durchsuchen. Jedes Modul behandelt ein Thema mit aufeinander aufbauenden Übungen.", completingLessonsTitle: "Lektionen abschließen", completingStep1: "Lies die Aufgabenstellung auf der linken Seite", completingStep2: "Schreibe deinen Code im Editor", completingStep3: "Beobachte die Live-Vorschau während du tippst", completingStep4: "Folge den Hinweisen, um Fehler zu beheben", completingStep5: "Klicke auf Weiter, wenn du fertig bist", editorToolsTitle: "Editor-Werkzeuge", editorToolUndo: "↶ Rückgängig / ↷ Wiederholen - Bearbeitungsverlauf navigieren", editorToolReset: "⟲ Zurücksetzen - Ursprünglichen Code wiederherstellen", editorToolExpected: "Lösung einblenden - Zielergebnis ein-/ausblenden", keyboardShortcutsTitle: "Tastenkürzel", shortcutRun: "Strg+Enter - Sofort validieren", shortcutUndo: "Strg+Z - Rückgängig", shortcutRedo: "Strg+Umschalt+Z - Wiederholen", emmetTitle: "Emmet-Kürzel (HTML-Modus)", emmetText: "Tippe Abkürzungen und drücke Tab zum Erweitern:", emmetClass: "div.box → div mit Klasse", emmetChildren: "ul>li*3 → ul mit 3 li-Kindern", emmetNested: "form>input+button → verschachtelte Struktur", emmetContent: "p{Hallo} → p mit Textinhalt", // More Projects moreProjectsTitle: "Weitere Projekte", htmlOverJsDesc: " - Lerne, native HTML-Elemente statt eigener JavaScript-Lösungen zu nutzen", mandalaDesc: " - Interaktive Visualisierung von JavaScript-Technologien nach Komplexität geordnet", // Contact contactTitle: "Kontakt & Links", contactText: 'Code Crispies wird von LibreTECH entwickelt', // Reset dialog resetDialogTitle: "Fortschritt zurücksetzen", resetDialogText: "Bist du sicher, dass du deinen gesamten Fortschritt zurücksetzen möchtest? Dies kann nicht rückgängig gemacht werden.", cancel: "Abbrechen", resetAll: "Alles zurücksetzen", // Reset code dialog resetCodeDialogTitle: "Code zurücksetzen", resetCodeDialogText: "Code auf den Anfangszustand dieser Lektion zurücksetzen?", dontShowAgain: "Nicht mehr anzeigen", reset: "Zurücksetzen", // Dynamic content loadingFallbackText: "Lektion konnte nicht geladen werden. Bitte wähle eine aus dem Menü oder prüfe die Hilfe.", completed: "Erledigt", successMessage: "CRISPY! ٩(◕‿◕)۶ Dein Code funktioniert.", keepTrying: "Weiter versuchen!", failedToLoad: "Module konnten nicht geladen werden. Bitte Seite neu laden.", tailwindPlaceholder: "Tailwind-Klassen eingeben (z.B. bg-blue-500 text-white p-4)", lessonFallback: "Lektion {index}", untitledLesson: "Unbenannte Lektion" }, // Polish pl: { // Page pageTitle: "Code Crispies - Nauka HTML i CSS interaktywnie", skipLink: "Przejdź do głównej treści", // Header menuOpen: "Otwórz menu", langSwitch: "PL", langSwitchLabel: "Zmień język", help: "Pomoc", // Instructions loading: "Ładowanie...", selectLesson: "Wybierz lekcję, aby rozpocząć.", editorLabel: "Edytor CSS", undoTitle: "Cofnij (Ctrl+Z)", redoTitle: "Ponów (Ctrl+Shift+Z)", resetCodeTitle: "Przywróć kod początkowy", run: "Uruchom", rerun: "Uruchom ponownie", // Preview yourOutput: "Twój wynik", showExpected: "Pokaż oczekiwane", hideExpected: "Ukryj oczekiwane", previous: "Poprzednia", next: "Następna", levelIndicator: "Lekcja {current} z {total}", lessonLabel: "Lekcja", // Sidebar menu: "Menu", closeMenu: "Zamknij menu", language: "Język", progress: "Postęp", progressText: "{percent}% ukończone ({completed}/{total})", lessons: "Lekcje", settings: "Ustawienia", showHints: "Pokaż podpowiedzi", resetAllProgress: "Resetuj cały postęp", openSource: "Open Source:", by: "przez", // Help dialog helpTitle: "Pomoc", aboutTitle: "O Code Crispies", aboutText: "Code Crispies to darmowa platforma open-source do nauki tworzenia stron internetowych poprzez praktyczne ćwiczenia. Nie wymaga konta - po prostu zacznij kodować!", learningModesTitle: "Tryby nauki", modeCss: "CSS - Pisz reguły CSS do stylizowania elementów", modeTailwind: "Tailwind - Stosuj klasy utility bezpośrednio w HTML", modeHtml: "HTML - Ćwicz semantyczne znaczniki i natywne elementy", gettingStartedTitle: "Pierwsze kroki", gettingStartedText: "Otwórz menu (☰), aby przeglądać moduły lekcji. Każdy moduł obejmuje konkretny temat z progresywnymi ćwiczeniami.", completingLessonsTitle: "Ukończanie lekcji", completingStep1: "Przeczytaj instrukcje zadania po lewej stronie", completingStep2: "Napisz swój kod w edytorze", completingStep3: "Obserwuj podgląd na żywo podczas pisania", completingStep4: "Postępuj zgodnie z podpowiedziami, aby naprawić problemy", completingStep5: "Kliknij Następna po ukończeniu", editorToolsTitle: "Narzędzia edytora", editorToolUndo: "↶ Cofnij / ↷ Ponów - Nawiguj historię edycji", editorToolReset: "⟲ Resetuj - Przywróć początkowy kod", editorToolExpected: "Pokaż oczekiwane - Przełącz nakładkę wyniku docelowego", keyboardShortcutsTitle: "Skróty klawiszowe", shortcutRun: "Ctrl+Enter - Natychmiastowa walidacja", shortcutUndo: "Ctrl+Z - Cofnij", shortcutRedo: "Ctrl+Shift+Z - Ponów", emmetTitle: "Skróty Emmet (tryb HTML)", emmetText: "Wpisz skróty i naciśnij Tab, aby rozwinąć:", emmetClass: "div.box → div z klasą", emmetChildren: "ul>li*3 → ul z 3 elementami li", emmetNested: "form>input+button → zagnieżdżona struktura", emmetContent: "p{Cześć} → p z tekstem", // More Projects moreProjectsTitle: "Więcej projektów", htmlOverJsDesc: " - Naucz się wykorzystywać natywne elementy HTML zamiast niestandardowych rozwiązań JavaScript", mandalaDesc: " - Interaktywna wizualizacja technologii JavaScript uporządkowanych według złożoności", // Contact contactTitle: "Kontakt i linki", contactText: 'Code Crispies jest rozwijany przez LibreTECH', // Reset dialog resetDialogTitle: "Resetuj postęp", resetDialogText: "Czy na pewno chcesz zresetować cały postęp? Tej operacji nie można cofnąć.", cancel: "Anuluj", resetAll: "Resetuj wszystko", // Reset code dialog resetCodeDialogTitle: "Resetuj kod", resetCodeDialogText: "Przywrócić kod do stanu początkowego tej lekcji?", dontShowAgain: "Nie pokazuj ponownie", reset: "Resetuj", // Dynamic content loadingFallbackText: "Nie można załadować lekcji. Wybierz jedną z menu lub sprawdź pomoc.", completed: "Ukończono", successMessage: "CRISPY! ٩(◕‿◕)۶ Twój kod działa poprawnie.", keepTrying: "Próbuj dalej!", failedToLoad: "Nie udało się załadować modułów. Odśwież stronę.", tailwindPlaceholder: "Wprowadź klasy Tailwind (np. bg-blue-500 text-white p-4)", lessonFallback: "Lekcja {index}", untitledLesson: "Lekcja bez tytułu" }, // Spanish es: { // Page pageTitle: "Code Crispies - Aprende HTML y CSS de forma interactiva", skipLink: "Saltar al contenido principal", // Header menuOpen: "Abrir menú", langSwitch: "ES", langSwitchLabel: "Cambiar idioma", help: "Ayuda", // Instructions loading: "Cargando...", selectLesson: "Selecciona una lección para comenzar.", editorLabel: "Editor CSS", undoTitle: "Deshacer (Ctrl+Z)", redoTitle: "Rehacer (Ctrl+Shift+Z)", resetCodeTitle: "Restaurar código inicial", run: "Ejecutar", rerun: "Volver a ejecutar", // Preview yourOutput: "Tu resultado", showExpected: "Mostrar esperado", hideExpected: "Ocultar esperado", previous: "Anterior", next: "Siguiente", levelIndicator: "Lección {current} de {total}", lessonLabel: "Lección", // Sidebar menu: "Menú", closeMenu: "Cerrar menú", language: "Idioma", progress: "Progreso", progressText: "{percent}% completado ({completed}/{total})", lessons: "Lecciones", settings: "Configuración", showHints: "Mostrar pistas", resetAllProgress: "Reiniciar todo el progreso", openSource: "Código abierto:", by: "por", // Help dialog helpTitle: "Ayuda", aboutTitle: "Acerca de Code Crispies", aboutText: "Code Crispies es una plataforma gratuita de código abierto para aprender desarrollo web a través de ejercicios prácticos. No se requiere cuenta, ¡solo empieza a programar!", learningModesTitle: "Modos de aprendizaje", modeCss: "CSS - Escribe reglas CSS para estilizar elementos", modeTailwind: "Tailwind - Aplica clases de utilidad directamente en HTML", modeHtml: "HTML - Practica marcado semántico y elementos nativos", gettingStartedTitle: "Primeros pasos", gettingStartedText: "Abre el menú (☰) para explorar los módulos de lecciones. Cada módulo cubre un tema específico con ejercicios progresivos.", completingLessonsTitle: "Completar lecciones", completingStep1: "Lee las instrucciones de la tarea a la izquierda", completingStep2: "Escribe tu código en el editor", completingStep3: "Observa la vista previa en vivo mientras escribes", completingStep4: "Sigue las pistas para corregir problemas", completingStep5: "Haz clic en Siguiente cuando termines", editorToolsTitle: "Herramientas del editor", editorToolUndo: "↶ Deshacer / ↷ Rehacer - Navegar historial de edición", editorToolReset: "⟲ Reiniciar - Restaurar código inicial", editorToolExpected: "Mostrar esperado - Alternar superposición del resultado objetivo", keyboardShortcutsTitle: "Atajos de teclado", shortcutRun: "Ctrl+Enter - Validar inmediatamente", shortcutUndo: "Ctrl+Z - Deshacer", shortcutRedo: "Ctrl+Shift+Z - Rehacer", emmetTitle: "Atajos Emmet (modo HTML)", emmetText: "Escribe abreviaturas y presiona Tab para expandir:", emmetClass: "div.box → div con clase", emmetChildren: "ul>li*3 → ul con 3 hijos li", emmetNested: "form>input+button → estructura anidada", emmetContent: "p{Hola} → p con contenido de texto", // More Projects moreProjectsTitle: "Más proyectos", htmlOverJsDesc: " - Aprende a aprovechar elementos HTML nativos en lugar de soluciones JavaScript personalizadas", mandalaDesc: " - Visualización interactiva de tecnologías JavaScript organizadas por complejidad", // Contact contactTitle: "Contacto y enlaces", contactText: 'Code Crispies es desarrollado por LibreTECH', // Reset dialog resetDialogTitle: "Reiniciar progreso", resetDialogText: "¿Estás seguro de que quieres reiniciar todo tu progreso? Esta acción no se puede deshacer.", cancel: "Cancelar", resetAll: "Reiniciar todo", // Reset code dialog resetCodeDialogTitle: "Reiniciar código", resetCodeDialogText: "¿Restaurar el código al estado inicial de esta lección?", dontShowAgain: "No mostrar de nuevo", reset: "Reiniciar", // Dynamic content loadingFallbackText: "No se pudo cargar la lección. Selecciona una del menú o consulta la ayuda.", completed: "Completado", successMessage: "¡CRISPY! ٩(◕‿◕)۶ Tu código funciona correctamente.", keepTrying: "¡Sigue intentando!", failedToLoad: "No se pudieron cargar los módulos. Actualiza la página.", tailwindPlaceholder: "Ingresa clases de Tailwind (ej. bg-blue-500 text-white p-4)", lessonFallback: "Lección {index}", untitledLesson: "Lección sin título" }, // Arabic ar: { // Page pageTitle: "Code Crispies - تعلم HTML و CSS بشكل تفاعلي", skipLink: "انتقل إلى المحتوى الرئيسي", // Header menuOpen: "افتح القائمة", langSwitch: "AR", langSwitchLabel: "تغيير اللغة", help: "مساعدة", // Instructions loading: "جاري التحميل...", selectLesson: "اختر درسًا للبدء.", editorLabel: "محرر CSS", undoTitle: "تراجع (Ctrl+Z)", redoTitle: "إعادة (Ctrl+Shift+Z)", resetCodeTitle: "استعادة الكود الأولي", run: "تشغيل", rerun: "إعادة التشغيل", // Preview yourOutput: "نتيجتك", showExpected: "إظهار المتوقع", hideExpected: "إخفاء المتوقع", previous: "السابق", next: "التالي", levelIndicator: "الدرس {current} من {total}", lessonLabel: "درس", // Sidebar menu: "القائمة", closeMenu: "إغلاق القائمة", language: "اللغة", progress: "التقدم", progressText: "{percent}% مكتمل ({completed}/{total})", lessons: "الدروس", settings: "الإعدادات", showHints: "إظهار التلميحات", resetAllProgress: "إعادة تعيين كل التقدم", openSource: "مفتوح المصدر:", by: "بواسطة", // Help dialog helpTitle: "مساعدة", aboutTitle: "عن Code Crispies", aboutText: "Code Crispies هي منصة مجانية مفتوحة المصدر لتعلم تطوير الويب من خلال تمارين عملية. لا يلزم حساب - فقط ابدأ البرمجة!", learningModesTitle: "أوضاع التعلم", modeCss: "CSS - اكتب قواعد CSS لتنسيق العناصر", modeTailwind: "Tailwind - طبق فئات الأدوات مباشرة في HTML", modeHtml: "HTML - تدرب على الترميز الدلالي والعناصر الأصلية", gettingStartedTitle: "البداية", gettingStartedText: "افتح القائمة (☰) لتصفح وحدات الدروس. كل وحدة تغطي موضوعًا محددًا مع تمارين تدريجية.", completingLessonsTitle: "إكمال الدروس", completingStep1: "اقرأ تعليمات المهمة على اليسار", completingStep2: "اكتب الكود في المحرر", completingStep3: "شاهد المعاينة المباشرة أثناء الكتابة", completingStep4: "اتبع التلميحات لإصلاح أي مشاكل", completingStep5: "انقر على التالي عند الانتهاء", editorToolsTitle: "أدوات المحرر", editorToolUndo: "↶ تراجع / ↷ إعادة - التنقل في سجل التحرير", editorToolReset: "⟲ إعادة تعيين - استعادة الكود الأولي", editorToolExpected: "إظهار المتوقع - تبديل طبقة النتيجة المستهدفة", keyboardShortcutsTitle: "اختصارات لوحة المفاتيح", shortcutRun: "Ctrl+Enter - التحقق فورًا", shortcutUndo: "Ctrl+Z - تراجع", shortcutRedo: "Ctrl+Shift+Z - إعادة", emmetTitle: "اختصارات Emmet (وضع HTML)", emmetText: "اكتب الاختصارات واضغط Tab للتوسيع:", emmetClass: "div.box ← div مع فئة", emmetChildren: "ul>li*3 ← ul مع 3 عناصر li", emmetNested: "form>input+button ← هيكل متداخل", emmetContent: "p{مرحبا} ← p مع محتوى نصي", // More Projects moreProjectsTitle: "مشاريع أخرى", htmlOverJsDesc: " - تعلم استخدام عناصر HTML الأصلية بدلاً من حلول JavaScript المخصصة", mandalaDesc: " - تصور تفاعلي لتقنيات JavaScript مرتبة حسب التعقيد", // Contact contactTitle: "التواصل والروابط", contactText: 'Code Crispies تم تطويره بواسطة LibreTECH', // Reset dialog resetDialogTitle: "إعادة تعيين التقدم", resetDialogText: "هل أنت متأكد أنك تريد إعادة تعيين كل تقدمك؟ لا يمكن التراجع عن هذا الإجراء.", cancel: "إلغاء", resetAll: "إعادة تعيين الكل", // Reset code dialog resetCodeDialogTitle: "إعادة تعيين الكود", resetCodeDialogText: "استعادة الكود إلى الحالة الأولية لهذا الدرس؟", dontShowAgain: "لا تظهر هذا مرة أخرى", reset: "إعادة تعيين", // Dynamic content loadingFallbackText: "تعذر تحميل الدرس. اختر واحدًا من القائمة أو تحقق من المساعدة.", completed: "مكتمل", successMessage: "CRISPY! ٩(◕‿◕)۶ الكود يعمل بشكل صحيح.", keepTrying: "استمر في المحاولة!", failedToLoad: "فشل تحميل الوحدات. قم بتحديث الصفحة.", tailwindPlaceholder: "أدخل فئات Tailwind (مثل bg-blue-500 text-white p-4)", lessonFallback: "درس {index}", untitledLesson: "درس بدون عنوان" }, // Ukrainian uk: { // Page pageTitle: "Code Crispies - Вивчай HTML та CSS інтерактивно", skipLink: "Перейти до основного вмісту", // Header menuOpen: "Відкрити меню", langSwitch: "UK", langSwitchLabel: "Змінити мову", help: "Допомога", // Instructions loading: "Завантаження...", selectLesson: "Оберіть урок, щоб почати.", editorLabel: "Редактор CSS", undoTitle: "Скасувати (Ctrl+Z)", redoTitle: "Повторити (Ctrl+Shift+Z)", resetCodeTitle: "Відновити початковий код", run: "Запустити", rerun: "Запустити знову", // Preview yourOutput: "Ваш результат", showExpected: "Показати очікуване", hideExpected: "Сховати очікуване", previous: "Попередній", next: "Наступний", levelIndicator: "Урок {current} з {total}", lessonLabel: "Урок", // Sidebar menu: "Меню", closeMenu: "Закрити меню", language: "Мова", progress: "Прогрес", progressText: "{percent}% завершено ({completed}/{total})", lessons: "Уроки", settings: "Налаштування", showHints: "Показувати підказки", resetAllProgress: "Скинути весь прогрес", openSource: "Відкритий код:", by: "від", // Help dialog helpTitle: "Допомога", aboutTitle: "Про Code Crispies", aboutText: "Code Crispies — це безкоштовна платформа з відкритим кодом для вивчення веб-розробки через практичні вправи. Обліковий запис не потрібен — просто починайте кодувати!", learningModesTitle: "Режими навчання", modeCss: "CSS - Пишіть правила CSS для стилізації елементів", modeTailwind: "Tailwind - Застосовуйте утилітарні класи безпосередньо в HTML", modeHtml: "HTML - Практикуйте семантичну розмітку та нативні елементи", gettingStartedTitle: "Початок роботи", gettingStartedText: "Відкрийте меню (☰), щоб переглянути модулі уроків. Кожен модуль охоплює конкретну тему з прогресивними вправами.", completingLessonsTitle: "Завершення уроків", completingStep1: "Прочитайте інструкції завдання зліва", completingStep2: "Напишіть свій код у редакторі", completingStep3: "Спостерігайте за попереднім переглядом під час введення", completingStep4: "Слідуйте підказкам, щоб виправити проблеми", completingStep5: "Натисніть Наступний після завершення", editorToolsTitle: "Інструменти редактора", editorToolUndo: "↶ Скасувати / ↷ Повторити - Навігація історією редагування", editorToolReset: "⟲ Скинути - Відновити початковий код", editorToolExpected: "Показати очікуване - Перемкнути накладення цільового результату", keyboardShortcutsTitle: "Гарячі клавіші", shortcutRun: "Ctrl+Enter - Негайна перевірка", shortcutUndo: "Ctrl+Z - Скасувати", shortcutRedo: "Ctrl+Shift+Z - Повторити", emmetTitle: "Скорочення Emmet (режим HTML)", emmetText: "Введіть скорочення та натисніть Tab для розгортання:", emmetClass: "div.box → div з класом", emmetChildren: "ul>li*3 → ul з 3 дочірніми li", emmetNested: "form>input+button → вкладена структура", emmetContent: "p{Привіт} → p з текстовим вмістом", // More Projects moreProjectsTitle: "Більше проектів", htmlOverJsDesc: " - Навчіться використовувати нативні HTML-елементи замість власних JavaScript-рішень", mandalaDesc: " - Інтерактивна візуалізація JavaScript-технологій, впорядкованих за складністю", // Contact contactTitle: "Контакти та посилання", contactText: 'Code Crispies розроблено LibreTECH', // Reset dialog resetDialogTitle: "Скинути прогрес", resetDialogText: "Ви впевнені, що хочете скинути весь свій прогрес? Цю дію неможливо скасувати.", cancel: "Скасувати", resetAll: "Скинути все", // Reset code dialog resetCodeDialogTitle: "Скинути код", resetCodeDialogText: "Відновити код до початкового стану цього уроку?", dontShowAgain: "Більше не показувати", reset: "Скинути", // Dynamic content loadingFallbackText: "Не вдалося завантажити урок. Виберіть один з меню або перевірте допомогу.", completed: "Завершено", successMessage: "CRISPY! ٩(◕‿◕)۶ Ваш код працює правильно.", keepTrying: "Продовжуйте спроби!", failedToLoad: "Не вдалося завантажити модулі. Оновіть сторінку.", tailwindPlaceholder: "Введіть класи Tailwind (напр. bg-blue-500 text-white p-4)", lessonFallback: "Урок {index}", untitledLesson: "Урок без назви" } }; let currentLang = "en"; // Available languages in cycle order const availableLanguages = ["en", "de", "pl", "es", "ar", "uk"]; /** * Get array of available language codes */ export function getAvailableLanguages() { return availableLanguages; } /** * Get the next language in the cycle * @param {string} currentLang - Current language code * @returns {string} Next language code */ export function getNextLanguage(current = currentLang) { const currentIndex = availableLanguages.indexOf(current); const nextIndex = (currentIndex + 1) % availableLanguages.length; return availableLanguages[nextIndex]; } /** * Detect initial language from localStorage or browser */ export function detectLanguage() { // Check localStorage first const stored = localStorage.getItem("codeCrispies.language"); if (stored && translations[stored]) { return stored; } // Check browser language const browserLang = navigator.language.split("-")[0]; if (translations[browserLang]) { return browserLang; } return "en"; } /** * Get current language */ export function getLanguage() { return currentLang; } /** * Set language and persist to localStorage */ // RTL languages const rtlLanguages = ["ar"]; export function setLanguage(lang) { if (!translations[lang]) { console.warn(`Language "${lang}" not supported, falling back to English`); lang = "en"; } currentLang = lang; localStorage.setItem("codeCrispies.language", lang); document.documentElement.lang = lang; // Set text direction for RTL languages document.documentElement.dir = rtlLanguages.includes(lang) ? "rtl" : "ltr"; } /** * Get a translation by key with optional interpolation * @param {string} key - Translation key * @param {Object} params - Optional parameters for interpolation */ export function t(key, params = {}) { const text = translations[currentLang]?.[key] || translations.en[key] || key; if (Object.keys(params).length === 0) { return text; } // Replace {param} placeholders return text.replace(/\{(\w+)\}/g, (match, param) => { return params[param] !== undefined ? params[param] : match; }); } /** * Apply translations to all elements with data-i18n attribute */ export function applyTranslations() { // Update page title document.title = t("pageTitle"); // Update elements with data-i18n attribute (text content) document.querySelectorAll("[data-i18n]").forEach((el) => { const key = el.dataset.i18n; el.textContent = t(key); }); // Update elements with data-i18n-html attribute (innerHTML) document.querySelectorAll("[data-i18n-html]").forEach((el) => { const key = el.dataset.i18nHtml; el.innerHTML = t(key); }); // Update elements with data-i18n-title attribute document.querySelectorAll("[data-i18n-title]").forEach((el) => { const key = el.dataset.i18nTitle; el.title = t(key); }); // Update elements with data-i18n-aria-label attribute document.querySelectorAll("[data-i18n-aria-label]").forEach((el) => { const key = el.dataset.i18nAriaLabel; el.setAttribute("aria-label", t(key)); }); // Update elements with data-i18n-placeholder attribute document.querySelectorAll("[data-i18n-placeholder]").forEach((el) => { const key = el.dataset.i18nPlaceholder; el.placeholder = t(key); }); } /** * Initialize i18n system */ export function initI18n() { const lang = detectLanguage(); setLanguage(lang); applyTranslations(); }