iobroker.script-restore 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.de.md CHANGED
@@ -78,6 +78,9 @@ Das Archiv wird vollständig im Browser geparst — beim Durchsuchen werden kein
78
78
  ### **WORK IN PROGRESS**
79
79
  -->
80
80
  ### **WORK IN PROGRESS**
81
+ * (ipod86) Vollständige i18n im Admin-Tab: alle Strings in de/en/fr/es/it/nl/pl/pt/ru/uk/zh-cn übersetzt
82
+
83
+ ### 0.1.1 (2026-05-24)
81
84
  * (ipod86) Überschreiben bestehender Skripte beim Restore erlaubt (Bestätigungs-Dialog mit Pfadanzeige)
82
85
  * (ipod86) Leeres Suffix erlaubt, um Skript unter dem Originalnamen wiederherzustellen
83
86
  * (ipod86) Hinweis nach erfolgreichem Restore: Skript direkt starten?
package/README.md CHANGED
@@ -75,6 +75,9 @@ The archive is parsed entirely in the browser — no files are written to disk d
75
75
  Placeholder for the next version (at the beginning of the line):
76
76
  ### **WORK IN PROGRESS**
77
77
  -->
78
+ ### 0.1.2 (2026-05-24)
79
+ * (ipod86) add full i18n to tab UI: all strings translated into de/en/fr/es/it/nl/pl/pt/ru/uk/zh-cn
80
+
78
81
  ### 0.1.1 (2026-05-24)
79
82
  * (ipod86) allow overwriting existing scripts during restore (confirmation dialog with path display)
80
83
  * (ipod86) allow empty suffix to restore script under its original name
@@ -95,11 +98,6 @@ The archive is parsed entirely in the browser — no files are written to disk d
95
98
  * (ipod86) add direct restore into ioBroker with suffix input and confirm modal
96
99
  * (ipod86) remove obsolete admin/words.js and .prettierignore
97
100
 
98
- ### 0.0.10 (2026-04-08)
99
- * (ipod86) fix jsonConfig responsive sizes lg/xl for backupPath (E5509)
100
- * (ipod86) trim news entries to 7 (W1032)
101
- * (ipod86) add Dependabot npm cooldown of 7 days (W8915)
102
-
103
101
  ## License
104
102
  MIT License
105
103
 
package/admin/tab_m.html CHANGED
@@ -393,39 +393,39 @@
393
393
  <div class="progress-inner"><span id="progressPercent">0%</span></div>
394
394
  </div>
395
395
  <div id="spinnerEl" class="spinner" style="display:none;"></div>
396
- <div id="loaderText">Lade Backup...</div>
396
+ <div id="loaderText" data-i18n="loaderLoading">Lade Backup...</div>
397
397
  </div>
398
398
 
399
399
  <!-- Restore Modal -->
400
400
  <div class="restore-modal-overlay" id="restoreModalOverlay" onclick="closeRestoreModal(event)">
401
401
  <div class="restore-modal" onclick="event.stopPropagation()">
402
- <h3>In ioBroker laden</h3>
402
+ <h3 data-i18n="modalTitle">In ioBroker laden</h3>
403
403
  <div class="restore-modal-meta">
404
- <div>Skript: <strong id="rmScriptName"></strong></div>
405
- <div>Pfad: <strong id="rmScriptPath"></strong></div>
406
- <div>Typ: <strong id="rmScriptType"></strong></div>
404
+ <div><span data-i18n="modalLabelScript">Skript:</span> <strong id="rmScriptName"></strong></div>
405
+ <div><span data-i18n="modalLabelPath">Pfad:</span> <strong id="rmScriptPath"></strong></div>
406
+ <div><span data-i18n="modalLabelType">Typ:</span> <strong id="rmScriptType"></strong></div>
407
407
  </div>
408
- <label for="rmSuffix">Suffix (wird an den Skriptnamen angehängt)</label>
408
+ <label for="rmSuffix" data-i18n="modalLabelSuffix">Suffix (wird an den Skriptnamen angehängt)</label>
409
409
  <input type="text" id="rmSuffix" value="_rcvr" oninput="updateRestorePreview();">
410
410
  <div class="restore-modal-preview" id="rmPreview"></div>
411
411
  <div class="restore-modal-warning" id="rmWarning">
412
- <p>⚠ Ein Skript unter <code id="rmWarningPath"></code> existiert bereits.<br>Soll es wirklich überschrieben werden?</p>
412
+ <p></p>
413
413
  <div class="rmw-actions">
414
- <button class="btn btn-outline" onclick="dismissWarning()">Anderen Suffix wählen</button>
415
- <button class="btn btn-danger" onclick="overwriteConfirmed()">Überschreiben</button>
414
+ <button class="btn btn-outline" onclick="dismissWarning()" data-i18n="modalDismiss">Anderen Suffix wählen</button>
415
+ <button class="btn btn-danger" onclick="overwriteConfirmed()" data-i18n="modalOverwrite">Überschreiben</button>
416
416
  </div>
417
417
  </div>
418
418
  <div class="restore-modal-success" id="rmSuccess">
419
- <p>✓ Erfolgreich wiederhergestellt.<br>Soll das Skript jetzt gestartet werden?</p>
419
+ <p data-i18n-html="modalSuccess">✓ Erfolgreich wiederhergestellt.<br>Soll das Skript jetzt gestartet werden?</p>
420
420
  <div class="rmw-actions">
421
- <button class="btn btn-outline" onclick="closeRestoreModal()">Nein danke</button>
422
- <button class="btn btn-outline-success" id="rmStartBtn" onclick="startRestoredScript()">Jetzt starten</button>
421
+ <button class="btn btn-outline" onclick="closeRestoreModal()" data-i18n="modalClose">Nein danke</button>
422
+ <button class="btn btn-outline-success" id="rmStartBtn" onclick="startRestoredScript()" data-i18n="modalStart">Jetzt starten</button>
423
423
  </div>
424
424
  </div>
425
425
  <div class="restore-modal-footer">
426
426
  <span id="rmMsg" style="flex:1;font-size:0.85rem;align-self:center;"></span>
427
427
  <button class="btn btn-outline" onclick="closeRestoreModal()">Schließen</button>
428
- <button class="btn btn-outline-success" id="rmConfirmBtn" onclick="confirmRestoreScript(false)">Laden</button>
428
+ <button class="btn btn-outline-success" id="rmConfirmBtn" onclick="confirmRestoreScript(false)" data-i18n="modalLoad">Laden</button>
429
429
  </div>
430
430
  </div>
431
431
  </div>
@@ -433,56 +433,56 @@
433
433
  <div class="toolbar">
434
434
  <div class="toolbar-left">
435
435
  <label class="file-input-label">
436
- 📂 Backup hochladen
436
+ <span data-i18n="uploadLabel">📂 Backup hochladen</span>
437
437
  <input type="file" id="fileInput" accept=".tar,.gz,.tar.gz,.json,.jsonl">
438
438
  </label>
439
439
  <div class="dropdown-wrapper" id="localDropdown">
440
- <button class="btn btn-outline" onclick="toggleDropdown('local')">
440
+ <button class="btn btn-outline" onclick="toggleDropdown('local')" data-i18n="localDropdown">
441
441
  🗂️ Lokale Backups ▾
442
442
  </button>
443
443
  <div class="dropdown-menu" id="localMenu"></div>
444
444
  </div>
445
445
  <div class="dropdown-wrapper" id="ftpDropdown" style="display:none;">
446
- <button class="btn btn-outline" onclick="toggleDropdown('ftp')">
446
+ <button class="btn btn-outline" onclick="toggleDropdown('ftp')" data-i18n="ftpDropdown">
447
447
  🌐 FTP Backups ▾
448
448
  </button>
449
449
  <div class="dropdown-menu" id="ftpMenu"></div>
450
450
  </div>
451
451
  <div class="dropdown-wrapper" id="smbDropdown" style="display:none;">
452
- <button class="btn btn-outline" onclick="toggleDropdown('smb')">
452
+ <button class="btn btn-outline" onclick="toggleDropdown('smb')" data-i18n="smbDropdown">
453
453
  🗄️ SMB Backups ▾
454
454
  </button>
455
455
  <div class="dropdown-menu" id="smbMenu"></div>
456
456
  </div>
457
457
  <div class="dropdown-wrapper" id="sftpDropdown" style="display:none;">
458
- <button class="btn btn-outline" onclick="toggleDropdown('sftp')">
458
+ <button class="btn btn-outline" onclick="toggleDropdown('sftp')" data-i18n="sftpDropdown">
459
459
  🔒 SFTP Backups ▾
460
460
  </button>
461
461
  <div class="dropdown-menu" id="sftpMenu"></div>
462
462
  </div>
463
463
  <div class="dropdown-wrapper" id="webdavDropdown" style="display:none;">
464
- <button class="btn btn-outline" onclick="toggleDropdown('webdav')">
464
+ <button class="btn btn-outline" onclick="toggleDropdown('webdav')" data-i18n="webdavDropdown">
465
465
  ☁️ WebDAV Backups ▾
466
466
  </button>
467
467
  <div class="dropdown-menu" id="webdavMenu"></div>
468
468
  </div>
469
469
  <div id="httpInputWrapper" style="display:none; align-items:center; gap:6px;">
470
470
  <input type="text" id="httpUrlInput" placeholder="https://..." style="padding:0.35rem 0.6rem; border:1px solid #ced4da; border-radius:4px; font-size:0.875rem; min-width:260px; font-family:inherit;">
471
- <button type="button" class="btn btn-outline" onclick="loadHttpUrl()">🌐 URL laden</button>
471
+ <button type="button" class="btn btn-outline" onclick="loadHttpUrl()" data-i18n="httpLoadBtn">🌐 URL laden</button>
472
472
  </div>
473
- <button id="zipBtn" class="btn btn-outline" onclick="downloadZip()" style="display:none;">📦 ZIP</button>
474
- <span class="status-msg" id="statusMsg">Backup laden oder Quelle wählen</span>
473
+ <button id="zipBtn" class="btn btn-outline" onclick="downloadZip()" style="display:none;" data-i18n="zipBtn">📦 ZIP</button>
474
+ <span class="status-msg" id="statusMsg" data-i18n="statusDefault">Backup laden oder Quelle wählen</span>
475
475
  </div>
476
476
  </div>
477
477
 
478
478
  <div class="main-container">
479
479
  <div class="sidebar" id="sidebar">
480
480
  <div class="sidebar-header">
481
- <input type="text" id="q" placeholder="Suche in Namen, Ordner &amp; Code...">
482
- <button class="btn-icon" id="expandToggleBtn" onclick="toggleExpandAll()" title="Alle Ordner aufklappen">📂</button>
481
+ <input type="text" id="q" data-i18n-placeholder="searchPlaceholder" placeholder="Suche in Namen, Ordner &amp; Code...">
482
+ <button class="btn-icon" id="expandToggleBtn" onclick="toggleExpandAll()" data-i18n-title="expandAll" title="Alle Ordner aufklappen">📂</button>
483
483
  </div>
484
484
  <div class="type-filter-bar">
485
- <button class="btn-type active" onclick="setTypeFilter(this, '')">Alle</button>
485
+ <button class="btn-type active" onclick="setTypeFilter(this, '')" data-i18n="typeAll">Alle</button>
486
486
  <button class="btn-type" onclick="setTypeFilter(this, 'JS')"><span class="type-badge badge-JS">JS</span></button>
487
487
  <button class="btn-type" onclick="setTypeFilter(this, 'TypeScript')"><span class="type-badge badge-TypeScript">TS</span></button>
488
488
  <button class="btn-type" onclick="setTypeFilter(this, 'Blockly')"><span class="type-badge badge-Blockly">Blockly</span></button>
@@ -498,17 +498,16 @@
498
498
  <div class="action-bar-inner">
499
499
  <div class="btn-group" id="viewSwitcher"></div>
500
500
  <div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;">
501
- <button onclick="openRestoreModal()" class="btn btn-outline-success" id="restoreBtn">In ioBroker laden</button>
502
- <button onclick="copyCode(this)" class="btn btn-outline-light">Code Kopieren</button>
503
- <button onclick="downloadActive()" class="btn btn-primary" id="dlBtn">Download</button>
501
+ <button onclick="openRestoreModal()" class="btn btn-outline-success" id="restoreBtn" data-i18n="restoreBtn">In ioBroker laden</button>
502
+ <button onclick="copyCode(this)" class="btn btn-outline-light" data-i18n="copyBtnAction">Code Kopieren</button>
503
+ <button onclick="downloadActive()" class="btn btn-primary" id="dlBtn" data-i18n="downloadBtn">Download</button>
504
504
  </div>
505
505
  </div>
506
506
  </div>
507
507
  <div id="codeContainer" class="code-empty">
508
508
  <div>
509
509
  <strong>ioBroker Script Restore</strong><br><br>
510
- Lade ein Backup hoch oder wähle eine lokale Datei,<br>
511
- um Skripte anzuzeigen und wiederherzustellen.
510
+ <span data-i18n-html="welcomeText">Lade ein Backup hoch oder wähle eine lokale Datei,<br>um Skripte anzuzeigen und wiederherzustellen.</span>
512
511
  </div>
513
512
  </div>
514
513
  </div>
@@ -520,6 +519,534 @@
520
519
  const instance = urlParams.get('instance') || '0';
521
520
  const adapterInst = 'script-restore.' + instance;
522
521
 
522
+ // === i18n ===
523
+ const TRANSLATIONS = {
524
+ de: {
525
+ loaderLoading: 'Lade Backup...',
526
+ loaderProcessing: 'Verarbeite...',
527
+ modalTitle: 'In ioBroker laden',
528
+ modalLabelScript: 'Skript:',
529
+ modalLabelPath: 'Pfad:',
530
+ modalLabelType: 'Typ:',
531
+ modalLabelSuffix: 'Suffix (wird an den Skriptnamen angehängt)',
532
+ modalWarning: '⚠ Ein Skript unter {path} existiert bereits.<br>Soll es wirklich überschrieben werden?',
533
+ modalDismiss: 'Anderen Suffix wählen',
534
+ modalOverwrite: 'Überschreiben',
535
+ modalSuccess: '✓ Erfolgreich wiederhergestellt.<br>Soll das Skript jetzt gestartet werden?',
536
+ modalClose: 'Nein danke',
537
+ modalStart: 'Jetzt starten',
538
+ modalLoad: 'Laden',
539
+ modalLoading: '⏳ Laden...',
540
+ modalStarting: '⏳ Starte...',
541
+ modalStarted: '✓ Gestartet',
542
+ modalLoaded: '✓ Geladen!',
543
+ modalUnknownError: '✗ Unbekannter Fehler',
544
+ modalErrorPrefix: '✗ Fehler: ',
545
+ copyBtn: 'Code Kopieren',
546
+ copyBtnAction: 'Code Kopieren',
547
+ copied: '✓ Kopiert!',
548
+ uploadLabel: '📂 Backup hochladen',
549
+ localDropdown: '🗂️ Lokale Backups ▾',
550
+ ftpDropdown: '🌐 FTP Backups ▾',
551
+ smbDropdown: '🗄️ SMB Backups ▾',
552
+ sftpDropdown: '🔒 SFTP Backups ▾',
553
+ webdavDropdown: '☁️ WebDAV Backups ▾',
554
+ httpLoadBtn: '🌐 URL laden',
555
+ zipBtn: '📦 ZIP',
556
+ statusDefault: 'Backup laden oder Quelle wählen',
557
+ searchPlaceholder: 'Suche in Namen, Ordner & Code...',
558
+ expandAll: 'Alle aufklappen',
559
+ collapseAll: 'Alle einklappen',
560
+ typeAll: 'Alle',
561
+ restoreBtn: 'In ioBroker laden',
562
+ downloadBtn: 'Download',
563
+ welcomeText: 'Lade ein Backup hoch oder wähle eine lokale Datei,<br>um Skripte anzuzeigen und wiederherzustellen.',
564
+ noSocket: 'Kein Socket. Bitte prüfen ob script-restore.{i} läuft.',
565
+ timeout: 'Timeout: Adapter antwortet nicht. Läuft script-restore.{i}?',
566
+ },
567
+ en: {
568
+ loaderLoading: 'Loading backup...',
569
+ loaderProcessing: 'Processing...',
570
+ modalTitle: 'Load into ioBroker',
571
+ modalLabelScript: 'Script:',
572
+ modalLabelPath: 'Path:',
573
+ modalLabelType: 'Type:',
574
+ modalLabelSuffix: 'Suffix (appended to the script name)',
575
+ modalWarning: '⚠ A script at {path} already exists.<br>Do you really want to overwrite it?',
576
+ modalDismiss: 'Choose different suffix',
577
+ modalOverwrite: 'Overwrite',
578
+ modalSuccess: '✓ Successfully restored.<br>Do you want to start the script now?',
579
+ modalClose: 'No thanks',
580
+ modalStart: 'Start now',
581
+ modalLoad: 'Load',
582
+ modalLoading: '⏳ Loading...',
583
+ modalStarting: '⏳ Starting...',
584
+ modalStarted: '✓ Started',
585
+ modalLoaded: '✓ Loaded!',
586
+ modalUnknownError: '✗ Unknown error',
587
+ modalErrorPrefix: '✗ Error: ',
588
+ copyBtn: 'Copy Code',
589
+ copyBtnAction: 'Copy Code',
590
+ copied: '✓ Copied!',
591
+ uploadLabel: '📂 Upload backup',
592
+ localDropdown: '🗂️ Local Backups ▾',
593
+ ftpDropdown: '🌐 FTP Backups ▾',
594
+ smbDropdown: '🗄️ SMB Backups ▾',
595
+ sftpDropdown: '🔒 SFTP Backups ▾',
596
+ webdavDropdown: '☁️ WebDAV Backups ▾',
597
+ httpLoadBtn: '🌐 Load URL',
598
+ zipBtn: '📦 ZIP',
599
+ statusDefault: 'Load a backup or select a source',
600
+ searchPlaceholder: 'Search in names, folders & code...',
601
+ expandAll: 'Expand all',
602
+ collapseAll: 'Collapse all',
603
+ typeAll: 'All',
604
+ restoreBtn: 'Load into ioBroker',
605
+ downloadBtn: 'Download',
606
+ welcomeText: 'Upload a backup or select a local file,<br>to view and restore scripts.',
607
+ noSocket: 'No socket. Please check if script-restore.{i} is running.',
608
+ timeout: 'Timeout: Adapter not responding. Is script-restore.{i} running?',
609
+ },
610
+ fr: {
611
+ loaderLoading: 'Chargement de la sauvegarde...',
612
+ loaderProcessing: 'Traitement en cours...',
613
+ modalTitle: 'Charger dans ioBroker',
614
+ modalLabelScript: 'Script :',
615
+ modalLabelPath: 'Chemin :',
616
+ modalLabelType: 'Type :',
617
+ modalLabelSuffix: 'Suffixe (ajouté au nom du script)',
618
+ modalWarning: '⚠ Un script à {path} existe déjà.<br>Voulez-vous vraiment l\'écraser ?',
619
+ modalDismiss: 'Choisir un autre suffixe',
620
+ modalOverwrite: 'Écraser',
621
+ modalSuccess: '✓ Restauré avec succès.<br>Voulez-vous démarrer le script maintenant ?',
622
+ modalClose: 'Non merci',
623
+ modalStart: 'Démarrer maintenant',
624
+ modalLoad: 'Charger',
625
+ modalLoading: '⏳ Chargement...',
626
+ modalStarting: '⏳ Démarrage...',
627
+ modalStarted: '✓ Démarré',
628
+ modalLoaded: '✓ Chargé !',
629
+ modalUnknownError: '✗ Erreur inconnue',
630
+ modalErrorPrefix: '✗ Erreur : ',
631
+ copyBtn: 'Copier le code',
632
+ copyBtnAction: 'Copier le code',
633
+ copied: '✓ Copié !',
634
+ uploadLabel: '📂 Téléverser une sauvegarde',
635
+ localDropdown: '🗂️ Sauvegardes locales ▾',
636
+ ftpDropdown: '🌐 Sauvegardes FTP ▾',
637
+ smbDropdown: '🗄️ Sauvegardes SMB ▾',
638
+ sftpDropdown: '🔒 Sauvegardes SFTP ▾',
639
+ webdavDropdown: '☁️ Sauvegardes WebDAV ▾',
640
+ httpLoadBtn: '🌐 Charger URL',
641
+ zipBtn: '📦 ZIP',
642
+ statusDefault: 'Charger une sauvegarde ou choisir une source',
643
+ searchPlaceholder: 'Rechercher dans les noms, dossiers et code...',
644
+ expandAll: 'Tout développer',
645
+ collapseAll: 'Tout réduire',
646
+ typeAll: 'Tous',
647
+ restoreBtn: 'Charger dans ioBroker',
648
+ downloadBtn: 'Télécharger',
649
+ welcomeText: 'Téléversez une sauvegarde ou sélectionnez un fichier local,<br>pour afficher et restaurer des scripts.',
650
+ noSocket: 'Pas de socket. Vérifiez si script-restore.{i} est en cours d\'exécution.',
651
+ timeout: 'Délai d\'attente : l\'adaptateur ne répond pas. script-restore.{i} est-il en cours d\'exécution ?',
652
+ },
653
+ es: {
654
+ loaderLoading: 'Cargando copia de seguridad...',
655
+ loaderProcessing: 'Procesando...',
656
+ modalTitle: 'Cargar en ioBroker',
657
+ modalLabelScript: 'Script:',
658
+ modalLabelPath: 'Ruta:',
659
+ modalLabelType: 'Tipo:',
660
+ modalLabelSuffix: 'Sufijo (se añade al nombre del script)',
661
+ modalWarning: '⚠ Ya existe un script en {path}.<br>¿Realmente desea sobrescribirlo?',
662
+ modalDismiss: 'Elegir otro sufijo',
663
+ modalOverwrite: 'Sobrescribir',
664
+ modalSuccess: '✓ Restaurado con éxito.<br>¿Desea iniciar el script ahora?',
665
+ modalClose: 'No gracias',
666
+ modalStart: 'Iniciar ahora',
667
+ modalLoad: 'Cargar',
668
+ modalLoading: '⏳ Cargando...',
669
+ modalStarting: '⏳ Iniciando...',
670
+ modalStarted: '✓ Iniciado',
671
+ modalLoaded: '✓ ¡Cargado!',
672
+ modalUnknownError: '✗ Error desconocido',
673
+ modalErrorPrefix: '✗ Error: ',
674
+ copyBtn: 'Copiar código',
675
+ copyBtnAction: 'Copiar código',
676
+ copied: '✓ ¡Copiado!',
677
+ uploadLabel: '📂 Subir copia de seguridad',
678
+ localDropdown: '🗂️ Copias locales ▾',
679
+ ftpDropdown: '🌐 Copias FTP ▾',
680
+ smbDropdown: '🗄️ Copias SMB ▾',
681
+ sftpDropdown: '🔒 Copias SFTP ▾',
682
+ webdavDropdown: '☁️ Copias WebDAV ▾',
683
+ httpLoadBtn: '🌐 Cargar URL',
684
+ zipBtn: '📦 ZIP',
685
+ statusDefault: 'Cargar una copia de seguridad o seleccionar una fuente',
686
+ searchPlaceholder: 'Buscar en nombres, carpetas y código...',
687
+ expandAll: 'Expandir todo',
688
+ collapseAll: 'Contraer todo',
689
+ typeAll: 'Todos',
690
+ restoreBtn: 'Cargar en ioBroker',
691
+ downloadBtn: 'Descargar',
692
+ welcomeText: 'Sube una copia de seguridad o selecciona un archivo local,<br>para ver y restaurar scripts.',
693
+ noSocket: 'Sin socket. Compruebe si script-restore.{i} está en ejecución.',
694
+ timeout: 'Tiempo de espera agotado: el adaptador no responde. ¿Está script-restore.{i} en ejecución?',
695
+ },
696
+ it: {
697
+ loaderLoading: 'Caricamento backup...',
698
+ loaderProcessing: 'Elaborazione...',
699
+ modalTitle: 'Carica in ioBroker',
700
+ modalLabelScript: 'Script:',
701
+ modalLabelPath: 'Percorso:',
702
+ modalLabelType: 'Tipo:',
703
+ modalLabelSuffix: 'Suffisso (aggiunto al nome dello script)',
704
+ modalWarning: '⚠ Esiste già uno script in {path}.<br>Vuoi davvero sovrascriverlo?',
705
+ modalDismiss: 'Scegli un suffisso diverso',
706
+ modalOverwrite: 'Sovrascrivi',
707
+ modalSuccess: '✓ Ripristinato con successo.<br>Vuoi avviare lo script adesso?',
708
+ modalClose: 'No grazie',
709
+ modalStart: 'Avvia ora',
710
+ modalLoad: 'Carica',
711
+ modalLoading: '⏳ Caricamento...',
712
+ modalStarting: '⏳ Avvio...',
713
+ modalStarted: '✓ Avviato',
714
+ modalLoaded: '✓ Caricato!',
715
+ modalUnknownError: '✗ Errore sconosciuto',
716
+ modalErrorPrefix: '✗ Errore: ',
717
+ copyBtn: 'Copia codice',
718
+ copyBtnAction: 'Copia codice',
719
+ copied: '✓ Copiato!',
720
+ uploadLabel: '📂 Carica backup',
721
+ localDropdown: '🗂️ Backup locali ▾',
722
+ ftpDropdown: '🌐 Backup FTP ▾',
723
+ smbDropdown: '🗄️ Backup SMB ▾',
724
+ sftpDropdown: '🔒 Backup SFTP ▾',
725
+ webdavDropdown: '☁️ Backup WebDAV ▾',
726
+ httpLoadBtn: '🌐 Carica URL',
727
+ zipBtn: '📦 ZIP',
728
+ statusDefault: 'Carica un backup o seleziona una sorgente',
729
+ searchPlaceholder: 'Cerca in nomi, cartelle e codice...',
730
+ expandAll: 'Espandi tutto',
731
+ collapseAll: 'Comprimi tutto',
732
+ typeAll: 'Tutti',
733
+ restoreBtn: 'Carica in ioBroker',
734
+ downloadBtn: 'Scarica',
735
+ welcomeText: 'Carica un backup o seleziona un file locale,<br>per visualizzare e ripristinare gli script.',
736
+ noSocket: 'Nessun socket. Verificare se script-restore.{i} è in esecuzione.',
737
+ timeout: 'Timeout: l\'adattatore non risponde. script-restore.{i} è in esecuzione?',
738
+ },
739
+ nl: {
740
+ loaderLoading: 'Back-up laden...',
741
+ loaderProcessing: 'Verwerken...',
742
+ modalTitle: 'Laden in ioBroker',
743
+ modalLabelScript: 'Script:',
744
+ modalLabelPath: 'Pad:',
745
+ modalLabelType: 'Type:',
746
+ modalLabelSuffix: 'Achtervoegsel (toegevoegd aan de scriptnaam)',
747
+ modalWarning: '⚠ Er bestaat al een script op {path}.<br>Wilt u het echt overschrijven?',
748
+ modalDismiss: 'Ander achtervoegsel kiezen',
749
+ modalOverwrite: 'Overschrijven',
750
+ modalSuccess: '✓ Succesvol hersteld.<br>Wilt u het script nu starten?',
751
+ modalClose: 'Nee bedankt',
752
+ modalStart: 'Nu starten',
753
+ modalLoad: 'Laden',
754
+ modalLoading: '⏳ Laden...',
755
+ modalStarting: '⏳ Starten...',
756
+ modalStarted: '✓ Gestart',
757
+ modalLoaded: '✓ Geladen!',
758
+ modalUnknownError: '✗ Onbekende fout',
759
+ modalErrorPrefix: '✗ Fout: ',
760
+ copyBtn: 'Code kopiëren',
761
+ copyBtnAction: 'Code kopiëren',
762
+ copied: '✓ Gekopieerd!',
763
+ uploadLabel: '📂 Back-up uploaden',
764
+ localDropdown: '🗂️ Lokale back-ups ▾',
765
+ ftpDropdown: '🌐 FTP back-ups ▾',
766
+ smbDropdown: '🗄️ SMB back-ups ▾',
767
+ sftpDropdown: '🔒 SFTP back-ups ▾',
768
+ webdavDropdown: '☁️ WebDAV back-ups ▾',
769
+ httpLoadBtn: '🌐 URL laden',
770
+ zipBtn: '📦 ZIP',
771
+ statusDefault: 'Laad een back-up of kies een bron',
772
+ searchPlaceholder: 'Zoeken in namen, mappen en code...',
773
+ expandAll: 'Alles uitklappen',
774
+ collapseAll: 'Alles inklappen',
775
+ typeAll: 'Alle',
776
+ restoreBtn: 'Laden in ioBroker',
777
+ downloadBtn: 'Downloaden',
778
+ welcomeText: 'Upload een back-up of selecteer een lokaal bestand,<br>om scripts te bekijken en te herstellen.',
779
+ noSocket: 'Geen socket. Controleer of script-restore.{i} actief is.',
780
+ timeout: 'Time-out: adapter reageert niet. Is script-restore.{i} actief?',
781
+ },
782
+ pl: {
783
+ loaderLoading: 'Wczytywanie kopii zapasowej...',
784
+ loaderProcessing: 'Przetwarzanie...',
785
+ modalTitle: 'Wczytaj do ioBroker',
786
+ modalLabelScript: 'Skrypt:',
787
+ modalLabelPath: 'Ścieżka:',
788
+ modalLabelType: 'Typ:',
789
+ modalLabelSuffix: 'Sufiks (dodawany do nazwy skryptu)',
790
+ modalWarning: '⚠ Skrypt w {path} już istnieje.<br>Czy na pewno chcesz go nadpisać?',
791
+ modalDismiss: 'Wybierz inny sufiks',
792
+ modalOverwrite: 'Nadpisz',
793
+ modalSuccess: '✓ Przywrócono pomyślnie.<br>Czy chcesz teraz uruchomić skrypt?',
794
+ modalClose: 'Nie, dziękuję',
795
+ modalStart: 'Uruchom teraz',
796
+ modalLoad: 'Wczytaj',
797
+ modalLoading: '⏳ Wczytywanie...',
798
+ modalStarting: '⏳ Uruchamianie...',
799
+ modalStarted: '✓ Uruchomiono',
800
+ modalLoaded: '✓ Wczytano!',
801
+ modalUnknownError: '✗ Nieznany błąd',
802
+ modalErrorPrefix: '✗ Błąd: ',
803
+ copyBtn: 'Kopiuj kod',
804
+ copyBtnAction: 'Kopiuj kod',
805
+ copied: '✓ Skopiowano!',
806
+ uploadLabel: '📂 Prześlij kopię zapasową',
807
+ localDropdown: '🗂️ Lokalne kopie ▾',
808
+ ftpDropdown: '🌐 Kopie FTP ▾',
809
+ smbDropdown: '🗄️ Kopie SMB ▾',
810
+ sftpDropdown: '🔒 Kopie SFTP ▾',
811
+ webdavDropdown: '☁️ Kopie WebDAV ▾',
812
+ httpLoadBtn: '🌐 Wczytaj URL',
813
+ zipBtn: '📦 ZIP',
814
+ statusDefault: 'Wczytaj kopię zapasową lub wybierz źródło',
815
+ searchPlaceholder: 'Szukaj w nazwach, folderach i kodzie...',
816
+ expandAll: 'Rozwiń wszystko',
817
+ collapseAll: 'Zwiń wszystko',
818
+ typeAll: 'Wszystkie',
819
+ restoreBtn: 'Wczytaj do ioBroker',
820
+ downloadBtn: 'Pobierz',
821
+ welcomeText: 'Prześlij kopię zapasową lub wybierz plik lokalny,<br>aby wyświetlić i przywrócić skrypty.',
822
+ noSocket: 'Brak gniazda. Sprawdź, czy script-restore.{i} działa.',
823
+ timeout: 'Przekroczono czas: adapter nie odpowiada. Czy script-restore.{i} działa?',
824
+ },
825
+ pt: {
826
+ loaderLoading: 'A carregar cópia de segurança...',
827
+ loaderProcessing: 'A processar...',
828
+ modalTitle: 'Carregar no ioBroker',
829
+ modalLabelScript: 'Script:',
830
+ modalLabelPath: 'Caminho:',
831
+ modalLabelType: 'Tipo:',
832
+ modalLabelSuffix: 'Sufixo (adicionado ao nome do script)',
833
+ modalWarning: '⚠ Já existe um script em {path}.<br>Tem a certeza que deseja substituí-lo?',
834
+ modalDismiss: 'Escolher outro sufixo',
835
+ modalOverwrite: 'Substituir',
836
+ modalSuccess: '✓ Restaurado com sucesso.<br>Deseja iniciar o script agora?',
837
+ modalClose: 'Não, obrigado',
838
+ modalStart: 'Iniciar agora',
839
+ modalLoad: 'Carregar',
840
+ modalLoading: '⏳ A carregar...',
841
+ modalStarting: '⏳ A iniciar...',
842
+ modalStarted: '✓ Iniciado',
843
+ modalLoaded: '✓ Carregado!',
844
+ modalUnknownError: '✗ Erro desconhecido',
845
+ modalErrorPrefix: '✗ Erro: ',
846
+ copyBtn: 'Copiar código',
847
+ copyBtnAction: 'Copiar código',
848
+ copied: '✓ Copiado!',
849
+ uploadLabel: '📂 Carregar cópia de segurança',
850
+ localDropdown: '🗂️ Cópias locais ▾',
851
+ ftpDropdown: '🌐 Cópias FTP ▾',
852
+ smbDropdown: '🗄️ Cópias SMB ▾',
853
+ sftpDropdown: '🔒 Cópias SFTP ▾',
854
+ webdavDropdown: '☁️ Cópias WebDAV ▾',
855
+ httpLoadBtn: '🌐 Carregar URL',
856
+ zipBtn: '📦 ZIP',
857
+ statusDefault: 'Carregar uma cópia de segurança ou selecionar uma fonte',
858
+ searchPlaceholder: 'Pesquisar em nomes, pastas e código...',
859
+ expandAll: 'Expandir tudo',
860
+ collapseAll: 'Recolher tudo',
861
+ typeAll: 'Todos',
862
+ restoreBtn: 'Carregar no ioBroker',
863
+ downloadBtn: 'Transferir',
864
+ welcomeText: 'Carregue uma cópia de segurança ou selecione um ficheiro local,<br>para ver e restaurar scripts.',
865
+ noSocket: 'Sem socket. Verifique se script-restore.{i} está em execução.',
866
+ timeout: 'Tempo esgotado: o adaptador não responde. O script-restore.{i} está em execução?',
867
+ },
868
+ ru: {
869
+ loaderLoading: 'Загрузка резервной копии...',
870
+ loaderProcessing: 'Обработка...',
871
+ modalTitle: 'Загрузить в ioBroker',
872
+ modalLabelScript: 'Скрипт:',
873
+ modalLabelPath: 'Путь:',
874
+ modalLabelType: 'Тип:',
875
+ modalLabelSuffix: 'Суффикс (добавляется к имени скрипта)',
876
+ modalWarning: '⚠ Скрипт по пути {path} уже существует.<br>Вы действительно хотите перезаписать его?',
877
+ modalDismiss: 'Выбрать другой суффикс',
878
+ modalOverwrite: 'Перезаписать',
879
+ modalSuccess: '✓ Успешно восстановлено.<br>Хотите запустить скрипт сейчас?',
880
+ modalClose: 'Нет, спасибо',
881
+ modalStart: 'Запустить сейчас',
882
+ modalLoad: 'Загрузить',
883
+ modalLoading: '⏳ Загрузка...',
884
+ modalStarting: '⏳ Запуск...',
885
+ modalStarted: '✓ Запущен',
886
+ modalLoaded: '✓ Загружен!',
887
+ modalUnknownError: '✗ Неизвестная ошибка',
888
+ modalErrorPrefix: '✗ Ошибка: ',
889
+ copyBtn: 'Копировать код',
890
+ copyBtnAction: 'Копировать код',
891
+ copied: '✓ Скопировано!',
892
+ uploadLabel: '📂 Загрузить резервную копию',
893
+ localDropdown: '🗂️ Локальные резервные копии ▾',
894
+ ftpDropdown: '🌐 Резервные копии FTP ▾',
895
+ smbDropdown: '🗄️ Резервные копии SMB ▾',
896
+ sftpDropdown: '🔒 Резервные копии SFTP ▾',
897
+ webdavDropdown: '☁️ Резервные копии WebDAV ▾',
898
+ httpLoadBtn: '🌐 Загрузить URL',
899
+ zipBtn: '📦 ZIP',
900
+ statusDefault: 'Загрузите резервную копию или выберите источник',
901
+ searchPlaceholder: 'Поиск по именам, папкам и коду...',
902
+ expandAll: 'Развернуть всё',
903
+ collapseAll: 'Свернуть всё',
904
+ typeAll: 'Все',
905
+ restoreBtn: 'Загрузить в ioBroker',
906
+ downloadBtn: 'Скачать',
907
+ welcomeText: 'Загрузите резервную копию или выберите локальный файл,<br>чтобы просмотреть и восстановить скрипты.',
908
+ noSocket: 'Нет сокета. Проверьте, запущен ли script-restore.{i}.',
909
+ timeout: 'Таймаут: адаптер не отвечает. Запущен ли script-restore.{i}?',
910
+ },
911
+ uk: {
912
+ loaderLoading: 'Завантаження резервної копії...',
913
+ loaderProcessing: 'Обробка...',
914
+ modalTitle: 'Завантажити в ioBroker',
915
+ modalLabelScript: 'Скрипт:',
916
+ modalLabelPath: 'Шлях:',
917
+ modalLabelType: 'Тип:',
918
+ modalLabelSuffix: 'Суфікс (додається до назви скрипта)',
919
+ modalWarning: '⚠ Скрипт за шляхом {path} вже існує.<br>Ви дійсно хочете його перезаписати?',
920
+ modalDismiss: 'Вибрати інший суфікс',
921
+ modalOverwrite: 'Перезаписати',
922
+ modalSuccess: '✓ Успішно відновлено.<br>Хочете запустити скрипт зараз?',
923
+ modalClose: 'Ні, дякую',
924
+ modalStart: 'Запустити зараз',
925
+ modalLoad: 'Завантажити',
926
+ modalLoading: '⏳ Завантаження...',
927
+ modalStarting: '⏳ Запуск...',
928
+ modalStarted: '✓ Запущено',
929
+ modalLoaded: '✓ Завантажено!',
930
+ modalUnknownError: '✗ Невідома помилка',
931
+ modalErrorPrefix: '✗ Помилка: ',
932
+ copyBtn: 'Копіювати код',
933
+ copyBtnAction: 'Копіювати код',
934
+ copied: '✓ Скопійовано!',
935
+ uploadLabel: '📂 Завантажити резервну копію',
936
+ localDropdown: '🗂️ Локальні резервні копії ▾',
937
+ ftpDropdown: '🌐 Резервні копії FTP ▾',
938
+ smbDropdown: '🗄️ Резервні копії SMB ▾',
939
+ sftpDropdown: '🔒 Резервні копії SFTP ▾',
940
+ webdavDropdown: '☁️ Резервні копії WebDAV ▾',
941
+ httpLoadBtn: '🌐 Завантажити URL',
942
+ zipBtn: '📦 ZIP',
943
+ statusDefault: 'Завантажте резервну копію або оберіть джерело',
944
+ searchPlaceholder: 'Пошук за іменами, папками та кодом...',
945
+ expandAll: 'Розгорнути все',
946
+ collapseAll: 'Згорнути все',
947
+ typeAll: 'Усі',
948
+ restoreBtn: 'Завантажити в ioBroker',
949
+ downloadBtn: 'Завантажити',
950
+ welcomeText: 'Завантажте резервну копію або оберіть локальний файл,<br>щоб переглянути та відновити скрипти.',
951
+ noSocket: 'Немає сокета. Перевірте, чи запущено script-restore.{i}.',
952
+ timeout: 'Час очікування вичерпано: адаптер не відповідає. Чи запущено script-restore.{i}?',
953
+ },
954
+ 'zh-cn': {
955
+ loaderLoading: '正在加载备份...',
956
+ loaderProcessing: '处理中...',
957
+ modalTitle: '加载到 ioBroker',
958
+ modalLabelScript: '脚本:',
959
+ modalLabelPath: '路径:',
960
+ modalLabelType: '类型:',
961
+ modalLabelSuffix: '后缀(附加到脚本名称)',
962
+ modalWarning: '⚠ {path} 下已存在一个脚本。<br>您真的要覆盖它吗?',
963
+ modalDismiss: '选择其他后缀',
964
+ modalOverwrite: '覆盖',
965
+ modalSuccess: '✓ 恢复成功。<br>是否立即启动该脚本?',
966
+ modalClose: '不,谢谢',
967
+ modalStart: '立即启动',
968
+ modalLoad: '加载',
969
+ modalLoading: '⏳ 加载中...',
970
+ modalStarting: '⏳ 启动中...',
971
+ modalStarted: '✓ 已启动',
972
+ modalLoaded: '✓ 已加载!',
973
+ modalUnknownError: '✗ 未知错误',
974
+ modalErrorPrefix: '✗ 错误:',
975
+ copyBtn: '复制代码',
976
+ copyBtnAction: '复制代码',
977
+ copied: '✓ 已复制!',
978
+ uploadLabel: '📂 上传备份',
979
+ localDropdown: '🗂️ 本地备份 ▾',
980
+ ftpDropdown: '🌐 FTP 备份 ▾',
981
+ smbDropdown: '🗄️ SMB 备份 ▾',
982
+ sftpDropdown: '🔒 SFTP 备份 ▾',
983
+ webdavDropdown: '☁️ WebDAV 备份 ▾',
984
+ httpLoadBtn: '🌐 加载 URL',
985
+ zipBtn: '📦 ZIP',
986
+ statusDefault: '加载备份或选择来源',
987
+ searchPlaceholder: '在名称、文件夹和代码中搜索...',
988
+ expandAll: '全部展开',
989
+ collapseAll: '全部折叠',
990
+ typeAll: '全部',
991
+ restoreBtn: '加载到 ioBroker',
992
+ downloadBtn: '下载',
993
+ welcomeText: '上传备份或选择本地文件,<br>以查看和恢复脚本。',
994
+ noSocket: '无套接字。请检查 script-restore.{i} 是否正在运行。',
995
+ timeout: '超时:适配器无响应。script-restore.{i} 是否正在运行?',
996
+ },
997
+ };
998
+
999
+ function detectLang() {
1000
+ // 1. URL parameter
1001
+ const lp = urlParams.get('lang') || urlParams.get('language');
1002
+ if (lp) return normLang(lp);
1003
+ // 2. localStorage
1004
+ try {
1005
+ const ls = localStorage.getItem('App.language') || localStorage.getItem('ioBroker.locale');
1006
+ if (ls) return normLang(ls);
1007
+ } catch(e) {}
1008
+ // 3. Parent frame
1009
+ try {
1010
+ const p = window.parent;
1011
+ if (p && (p.sysLang || p.systemLang)) return normLang(p.sysLang || p.systemLang);
1012
+ } catch(e) {}
1013
+ // 4. Navigator
1014
+ return normLang(navigator.language || 'en');
1015
+ }
1016
+
1017
+ function normLang(l) {
1018
+ if (!l) return 'en';
1019
+ const ll = l.toLowerCase().replace('_', '-');
1020
+ if (ll === 'zh-cn' || ll === 'zh') return 'zh-cn';
1021
+ return ll.slice(0, 2);
1022
+ }
1023
+
1024
+ const LANG = (function() {
1025
+ const detected = detectLang();
1026
+ return TRANSLATIONS[detected] ? detected : 'en';
1027
+ })();
1028
+
1029
+ function t(key) {
1030
+ return (TRANSLATIONS[LANG] && TRANSLATIONS[LANG][key]) || (TRANSLATIONS.en && TRANSLATIONS.en[key]) || key;
1031
+ }
1032
+
1033
+ function applyTranslations() {
1034
+ document.querySelectorAll('[data-i18n]').forEach(function(el) {
1035
+ el.textContent = t(el.getAttribute('data-i18n'));
1036
+ });
1037
+ document.querySelectorAll('[data-i18n-html]').forEach(function(el) {
1038
+ el.innerHTML = t(el.getAttribute('data-i18n-html'));
1039
+ });
1040
+ document.querySelectorAll('[data-i18n-placeholder]').forEach(function(el) {
1041
+ el.placeholder = t(el.getAttribute('data-i18n-placeholder'));
1042
+ });
1043
+ document.querySelectorAll('[data-i18n-title]').forEach(function(el) {
1044
+ el.title = t(el.getAttribute('data-i18n-title'));
1045
+ });
1046
+ }
1047
+
1048
+ applyTranslations();
1049
+
523
1050
  // === Dark Theme Detection ===
524
1051
  function applyTheme(isDark) {
525
1052
  document.body.classList.toggle('dark', !!isDark);
@@ -625,14 +1152,14 @@
625
1152
 
626
1153
  function sendTo(command, message, callback) {
627
1154
  if (!socket) {
628
- callback({ error: 'Kein Socket. Bitte prüfen ob script-restore.' + instance + ' läuft.' });
1155
+ callback({ error: t('noSocket').replace('{i}', instance) });
629
1156
  return;
630
1157
  }
631
1158
  let done = false;
632
1159
  const timer = setTimeout(() => {
633
1160
  if (done) return;
634
1161
  done = true;
635
- callback({ error: 'Timeout: Adapter antwortet nicht. Läuft script-restore.' + instance + '?' });
1162
+ callback({ error: t('timeout').replace('{i}', instance) });
636
1163
  }, 30000);
637
1164
  const wrapped = (result) => {
638
1165
  if (done) return;
@@ -1038,13 +1565,13 @@
1038
1565
  document.getElementById('spinnerEl').style.display = 'none';
1039
1566
  document.getElementById('progressCircle').style.background = 'conic-gradient(var(--primary) 0%, #e9ecef 0%)';
1040
1567
  document.getElementById('progressPercent').textContent = '0%';
1041
- document.getElementById('loaderText').textContent = text || 'Laden...';
1568
+ document.getElementById('loaderText').textContent = text || t('loaderLoading');
1042
1569
  document.getElementById('loader').style.display = 'flex';
1043
1570
  }
1044
1571
  function showLoaderSpinner(text) {
1045
1572
  document.getElementById('progressContainer').style.display = 'none';
1046
1573
  document.getElementById('spinnerEl').style.display = 'block';
1047
- document.getElementById('loaderText').textContent = text || 'Verarbeite...';
1574
+ document.getElementById('loaderText').textContent = text || t('loaderProcessing');
1048
1575
  document.getElementById('loader').style.display = 'flex';
1049
1576
  }
1050
1577
  function updateProgress(pct) {
@@ -1061,7 +1588,7 @@
1061
1588
  isAllExpanded = !isAllExpanded;
1062
1589
  const btn = document.getElementById('expandToggleBtn');
1063
1590
  btn.innerHTML = isAllExpanded ? '📁' : '📂';
1064
- btn.title = isAllExpanded ? 'Alle einklappen' : 'Alle aufklappen';
1591
+ btn.title = isAllExpanded ? t('collapseAll') : t('expandAll');
1065
1592
  document.querySelectorAll('.tree-folder').forEach(el => {
1066
1593
  const p = el.dataset.path;
1067
1594
  const icon = el.querySelector('.folder-icon');
@@ -1248,12 +1775,12 @@
1248
1775
  ta.value = txt; document.body.appendChild(ta); ta.select();
1249
1776
  try {
1250
1777
  document.execCommand('copy');
1251
- btn.textContent = '✓ Kopiert!';
1778
+ btn.textContent = t('copied');
1252
1779
  btn.classList.replace('btn-outline-light', 'btn-outline-success');
1253
1780
  } finally {
1254
1781
  document.body.removeChild(ta);
1255
1782
  setTimeout(() => {
1256
- btn.textContent = 'Code Kopieren';
1783
+ btn.textContent = t('copyBtnAction');
1257
1784
  btn.classList.replace('btn-outline-success', 'btn-outline-light');
1258
1785
  }, 1500);
1259
1786
  }
@@ -1325,18 +1852,18 @@
1325
1852
  const msgEl = document.getElementById('rmMsg');
1326
1853
  const confirmBtn = document.getElementById('rmConfirmBtn');
1327
1854
  msgEl.style.color = '#aaa';
1328
- msgEl.textContent = '⏳ Laden...';
1855
+ msgEl.textContent = t('modalLoading');
1329
1856
  confirmBtn.disabled = true;
1330
1857
  sendTo('restoreScript', { path: s.path, name: s.name, type: s.type, source: s.source, suffix, overwrite: !!overwrite }, function(result) {
1331
1858
  if (result && result.exists) {
1332
- document.getElementById('rmWarningPath').textContent = result.id.replace(/^script\.js\./, '');
1859
+ document.querySelector('#rmWarning p').innerHTML = t('modalWarning').replace('{path}', '<code>' + escapeHTML(result.id.replace(/^script\.js\./, '')) + '</code>');
1333
1860
  document.getElementById('rmWarning').style.display = 'block';
1334
1861
  document.getElementById('rmSuffix').disabled = true;
1335
1862
  msgEl.textContent = '';
1336
1863
  } else if (result && result.error) {
1337
1864
  confirmBtn.disabled = false;
1338
1865
  msgEl.style.color = '#dc3545';
1339
- msgEl.textContent = '' + result.error;
1866
+ msgEl.textContent = t('modalErrorPrefix') + result.error;
1340
1867
  } else if (result && result.success) {
1341
1868
  _restoredScriptId = result.id;
1342
1869
  msgEl.textContent = '';
@@ -1345,7 +1872,7 @@
1345
1872
  } else {
1346
1873
  confirmBtn.disabled = false;
1347
1874
  msgEl.style.color = '#dc3545';
1348
- msgEl.textContent = '✗ Unbekannter Fehler';
1875
+ msgEl.textContent = t('modalUnknownError');
1349
1876
  }
1350
1877
  });
1351
1878
  }
@@ -1367,16 +1894,16 @@
1367
1894
  if (!_restoredScriptId) return;
1368
1895
  const startBtn = document.getElementById('rmStartBtn');
1369
1896
  startBtn.disabled = true;
1370
- startBtn.textContent = '⏳ Starte...';
1897
+ startBtn.textContent = t('modalStarting');
1371
1898
  sendTo('enableScript', { id: _restoredScriptId }, function(result) {
1372
1899
  if (result && result.success) {
1373
- startBtn.textContent = '✓ Gestartet';
1900
+ startBtn.textContent = t('modalStarted');
1374
1901
  setTimeout(() => closeRestoreModal(), 1000);
1375
1902
  } else {
1376
1903
  startBtn.disabled = false;
1377
- startBtn.textContent = 'Jetzt starten';
1904
+ startBtn.textContent = t('modalStart');
1378
1905
  const p = document.querySelector('#rmSuccess p');
1379
- p.innerHTML = '✗ Fehler: ' + ((result && result.error) || 'Unbekannt');
1906
+ p.innerHTML = t('modalErrorPrefix') + escapeHTML((result && result.error) || 'Unbekannt');
1380
1907
  p.style.color = '#dc3545';
1381
1908
  }
1382
1909
  });
package/io-package.json CHANGED
@@ -1,8 +1,21 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "script-restore",
4
- "version": "0.1.1",
4
+ "version": "0.1.2",
5
5
  "news": {
6
+ "0.1.2": {
7
+ "en": "add full i18n to tab UI: all strings translated into de/en/fr/es/it/nl/pl/pt/ru/uk/zh-cn",
8
+ "de": "fügen Sie volle i18n zu Tab UI: alle Strings übersetzt in de/en/fr/es/it/nl/pl/pt/ru/uk/zh-cn",
9
+ "ru": "добавить полный i18n в вкладку UI: все строки переведены в de/en/fr/es/it/nl/pl/pt/ru/uk/zh-cn",
10
+ "pt": "adicionar i18n completo à guia UI: todas as strings traduzidas para de/en/fr/es/it/nl/pl/pt/ru/uk/zh-cn",
11
+ "nl": "volledige i18n toevoegen aan tab UI: alle tekenreeksen vertaald in de/en/fr/es/it/nl/pl/pt/ru/uk/zh-cn",
12
+ "fr": "ajouter i18n complet à l'onglet UI: toutes les chaînes traduites en de/en/fr/es/it/nl/pl/pt/ru/uk/zh-cn",
13
+ "it": "aggiungere i18n completo alla scheda UI: tutte le stringhe tradotte in de/en/fr/es/it/nl/pl/pt/ru/uk/zh-cn",
14
+ "es": "añadir i18n completo a la pestaña UI: todas las cadenas traducidas a de/en/fr/es/it/nl/pl/pt/ru/uk/zh-cn",
15
+ "pl": "dodaj pełny i18n do zakładki Interfejs: wszystkie struny przetłumaczone na de / en / fr / es / it / nl / pl / pt / ru / uk / zh- cn",
16
+ "uk": "додати повну i18n в закладку UI: всі рядки, перекладені в de/en/fr/es/nl/pl/pt/ru/uk/zh-cn",
17
+ "zh-cn": "将完整 i18n 添加到 Tab UI 中: 所有字符串翻译为 de/en/fr/es/it/nl/pl/pt/ru/uk/zh-cn"
18
+ },
6
19
  "0.1.1": {
7
20
  "en": "allow overwriting existing scripts during restore (confirmation dialog with path display)\nallow empty suffix to restore script under its original name\nprompt to start script immediately after successful restore",
8
21
  "de": "das überschreiben bestehender skripte während der wiederherstellung (bestätigungsdialog mit pfadanzeige)\nleere suffix erlauben, skript unter seinem ursprünglichen namen wiederherzustellen\nscript sofort nach erfolgreicher wiederherstellung starten",
@@ -80,19 +93,6 @@
80
93
  "pl": "jsonConfig: atrybuty rozmiaru responsywnego, wsparcie i18n, usunięcie przestarzałych plików admin",
81
94
  "uk": "jsonConfig: атрибути розміру, підтримка i18n, видалення застарілих файлів admin",
82
95
  "zh-cn": "jsonConfig:添加响应式尺寸属性、i18n 支持,删除过时的 admin 文件"
83
- },
84
- "0.0.8": {
85
- "en": "Migrate settings UI to jsonConfig (admin 5+); fix node:fs import; update Dependabot schedule; migrate automerge workflow",
86
- "de": "Einstellungen auf jsonConfig (Admin 5+) migriert; node:fs-Import korrigiert; Dependabot-Schedule aktualisiert; Automerge-Workflow migriert",
87
- "ru": "Миграция UI настроек на jsonConfig (admin 5+); исправлен импорт node:fs; обновлён Dependabot; миграция automerge workflow",
88
- "pt": "Migrar UI de configurações para jsonConfig (admin 5+); corrigir importação node:fs; atualizar Dependabot; migrar workflow automerge",
89
- "nl": "Instellingen UI migreren naar jsonConfig (admin 5+); node:fs import gecorrigeerd; Dependabot schedule bijgewerkt; automerge workflow gemigreerd",
90
- "fr": "Migration de l'UI des paramètres vers jsonConfig (admin 5+); correction de l'import node:fs; mise à jour Dependabot; migration du workflow automerge",
91
- "it": "Migrazione UI impostazioni a jsonConfig (admin 5+); corretto import node:fs; aggiornato Dependabot; migrato workflow automerge",
92
- "es": "Migrar UI de configuración a jsonConfig (admin 5+); corregir importación node:fs; actualizar Dependabot; migrar workflow automerge",
93
- "pl": "Migracja UI ustawień do jsonConfig (admin 5+); poprawka importu node:fs; aktualizacja Dependabot; migracja workflow automerge",
94
- "uk": "Міграція UI налаштувань на jsonConfig (admin 5+); виправлено імпорт node:fs; оновлено Dependabot; міграція automerge workflow",
95
- "zh-cn": "将设置 UI 迁移至 jsonConfig(admin 5+);修复 node:fs 导入;更新 Dependabot 计划;迁移 automerge 工作流"
96
96
  }
97
97
  },
98
98
  "titleLang": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.script-restore",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Restore ioBroker scripts from backup archives",
5
5
  "author": {
6
6
  "name": "ipod86",