/** * Localization support for AniWorld Manager * Implements resource-based text management for easy translation */ class Localization { constructor() { this.currentLanguage = 'en'; this.fallbackLanguage = 'en'; this.translations = {}; this.loadTranslations(); } loadTranslations() { // English (default) this.translations.en = { // Header 'config-title': 'Configuration', 'toggle-theme': 'Toggle theme', 'rescan': 'Rescan', // Search 'search-placeholder': 'Search for anime...', 'search-results': 'Search Results', 'no-results': 'No results found', 'add': 'Add', // Series 'series-collection': 'Series Collection', 'select-all': 'Select All', 'deselect-all': 'Deselect All', 'download-selected': 'Download Selected', 'missing-episodes': 'missing episodes', // Configuration 'anime-directory': 'Anime Directory', 'series-count': 'Series Count', 'connection-status': 'Connection Status', 'connected': 'Connected', 'disconnected': 'Disconnected', // Download controls 'pause': 'Pause', 'resume': 'Resume', 'cancel': 'Cancel', 'downloading': 'Downloading', 'paused': 'Paused', // Download queue 'download-queue': 'Download Queue', 'currently-downloading': 'Currently Downloading', 'queued-series': 'Queued Series', // Status messages 'connected-server': 'Connected to server', 'disconnected-server': 'Disconnected from server', 'scan-started': 'Scan started', 'scan-completed': 'Scan completed successfully', 'download-started': 'Download started', 'download-completed': 'Download completed successfully', 'series-added': 'Series added successfully', // Error messages 'search-failed': 'Search failed', 'download-failed': 'Download failed', 'scan-failed': 'Scan failed', 'connection-failed': 'Connection failed', // General 'loading': 'Loading...', 'close': 'Close', 'ok': 'OK', 'cancel-action': 'Cancel' }; // German this.translations.de = { // Header 'config-title': 'Konfiguration', 'toggle-theme': 'Design wechseln', 'rescan': 'Neu scannen', // Search 'search-placeholder': 'Nach Anime suchen...', 'search-results': 'Suchergebnisse', 'no-results': 'Keine Ergebnisse gefunden', 'add': 'Hinzufügen', // Series 'series-collection': 'Serien-Sammlung', 'select-all': 'Alle auswählen', 'deselect-all': 'Alle abwählen', 'download-selected': 'Ausgewählte herunterladen', 'missing-episodes': 'fehlende Episoden', // Configuration 'anime-directory': 'Anime-Verzeichnis', 'series-count': 'Anzahl Serien', 'connection-status': 'Verbindungsstatus', 'connected': 'Verbunden', 'disconnected': 'Getrennt', // Download controls 'pause': 'Pausieren', 'resume': 'Fortsetzen', 'cancel': 'Abbrechen', 'downloading': 'Herunterladen', 'paused': 'Pausiert', // Download queue 'download-queue': 'Download-Warteschlange', 'currently-downloading': 'Wird heruntergeladen', 'queued-series': 'Warteschlange', // Status messages 'connected-server': 'Mit Server verbunden', 'disconnected-server': 'Verbindung zum Server getrennt', 'scan-started': 'Scan gestartet', 'scan-completed': 'Scan erfolgreich abgeschlossen', 'download-started': 'Download gestartet', 'download-completed': 'Download erfolgreich abgeschlossen', 'series-added': 'Serie erfolgreich hinzugefügt', // Error messages 'search-failed': 'Suche fehlgeschlagen', 'download-failed': 'Download fehlgeschlagen', 'scan-failed': 'Scan fehlgeschlagen', 'connection-failed': 'Verbindung fehlgeschlagen', // General 'loading': 'Wird geladen...', 'close': 'Schließen', 'ok': 'OK', 'cancel-action': 'Abbrechen' }; // Load saved language preference const savedLanguage = localStorage.getItem('language') || this.detectLanguage(); this.setLanguage(savedLanguage); } detectLanguage() { const browserLang = navigator.language || navigator.userLanguage; const langCode = browserLang.split('-')[0]; return this.translations[langCode] ? langCode : this.fallbackLanguage; } setLanguage(langCode) { if (this.translations[langCode]) { this.currentLanguage = langCode; localStorage.setItem('language', langCode); this.updatePageTexts(); } } getText(key, fallback = key) { const translation = this.translations[this.currentLanguage]; if (translation && translation[key]) { return translation[key]; } // Try fallback language const fallbackTranslation = this.translations[this.fallbackLanguage]; if (fallbackTranslation && fallbackTranslation[key]) { return fallbackTranslation[key]; } return fallback; } updatePageTexts() { // Update all elements with data-text attributes document.querySelectorAll('[data-text]').forEach(element => { const key = element.getAttribute('data-text'); const text = this.getText(key); if (element.tagName === 'INPUT' && element.type === 'text') { element.placeholder = text; } else { element.textContent = text; } }); // Update specific elements that need special handling this.updateSearchPlaceholder(); this.updateDynamicTexts(); } updateSearchPlaceholder() { const searchInput = document.getElementById('search-input'); if (searchInput) { searchInput.placeholder = this.getText('search-placeholder'); } } updateDynamicTexts() { // Update any dynamically generated content const selectAllBtn = document.getElementById('select-all'); if (selectAllBtn && window.app) { const selectedCount = window.app.selectedSeries ? window.app.selectedSeries.size : 0; const totalCount = window.app.seriesData ? window.app.seriesData.length : 0; if (selectedCount === totalCount && totalCount > 0) { selectAllBtn.innerHTML = `${this.getText('deselect-all')}`; } else { selectAllBtn.innerHTML = `${this.getText('select-all')}`; } } } getAvailableLanguages() { return Object.keys(this.translations).map(code => ({ code: code, name: this.getLanguageName(code) })); } getLanguageName(code) { const names = { 'en': 'English', 'de': 'Deutsch' }; return names[code] || code.toUpperCase(); } formatMessage(key, ...args) { let message = this.getText(key); args.forEach((arg, index) => { message = message.replace(`{${index}}`, arg); }); return message; } } // Export for use in other modules window.Localization = Localization;