This commit is contained in:
2025-10-12 18:05:31 +02:00
parent 57d49bcf78
commit 7a71715183
130 changed files with 30010 additions and 50631 deletions

View File

@@ -1,236 +1,236 @@
/**
* 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 = `<i class="fas fa-times"></i><span>${this.getText('deselect-all')}</span>`;
} else {
selectAllBtn.innerHTML = `<i class="fas fa-check-double"></i><span>${this.getText('select-all')}</span>`;
}
}
}
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
/**
* 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 = `<i class="fas fa-times"></i><span>${this.getText('deselect-all')}</span>`;
} else {
selectAllBtn.innerHTML = `<i class="fas fa-check-double"></i><span>${this.getText('select-all')}</span>`;
}
}
}
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;