iobroker.script-restore 0.1.2 → 0.1.3
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 +6 -1
- package/README.md +5 -5
- package/admin/tab_m.html +218 -30
- package/io-package.json +14 -14
- package/package.json +1 -1
package/README.de.md
CHANGED
|
@@ -77,7 +77,12 @@ Das Archiv wird vollständig im Browser geparst — beim Durchsuchen werden kein
|
|
|
77
77
|
Placeholder for the next version (at the beginning of the line):
|
|
78
78
|
### **WORK IN PROGRESS**
|
|
79
79
|
-->
|
|
80
|
-
###
|
|
80
|
+
### 0.1.3 (2026-05-24)
|
|
81
|
+
* (ipod86) Language-Flash behoben: Socket-Override wird übersprungen wenn Sprache bereits vom Admin-Frame erkannt wurde
|
|
82
|
+
* (ipod86) Alle hardcodierten Statusmeldungen durch übersetzte t()-Aufrufe ersetzt
|
|
83
|
+
* (ipod86) codeHint-Übersetzungsschlüssel in alle 11 Sprachen ergänzt
|
|
84
|
+
|
|
85
|
+
### 0.1.2 (2026-05-24)
|
|
81
86
|
* (ipod86) Vollständige i18n im Admin-Tab: alle Strings in de/en/fr/es/it/nl/pl/pt/ru/uk/zh-cn übersetzt
|
|
82
87
|
|
|
83
88
|
### 0.1.1 (2026-05-24)
|
package/README.md
CHANGED
|
@@ -75,6 +75,11 @@ 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.3 (2026-05-24)
|
|
79
|
+
* (ipod86) fix language flash: skip socket override when language already detected from admin frame
|
|
80
|
+
* (ipod86) replace all hardcoded status strings with translated t() calls
|
|
81
|
+
* (ipod86) add codeHint translation key in all 11 languages
|
|
82
|
+
|
|
78
83
|
### 0.1.2 (2026-05-24)
|
|
79
84
|
* (ipod86) add full i18n to tab UI: all strings translated into de/en/fr/es/it/nl/pl/pt/ru/uk/zh-cn
|
|
80
85
|
|
|
@@ -93,11 +98,6 @@ The archive is parsed entirely in the browser — no files are written to disk d
|
|
|
93
98
|
* (ipod86) add common.singleton to prevent multiple instances
|
|
94
99
|
* (ipod86) complete i18n translations for all supported languages (fr, es, it, nl, pl, pt, ru, uk, zh-cn)
|
|
95
100
|
|
|
96
|
-
### 0.0.11 (2026-04-13)
|
|
97
|
-
* (ipod86) add type filter (JS/TS/Blockly/Rules) in script sidebar
|
|
98
|
-
* (ipod86) add direct restore into ioBroker with suffix input and confirm modal
|
|
99
|
-
* (ipod86) remove obsolete admin/words.js and .prettierignore
|
|
100
|
-
|
|
101
101
|
## License
|
|
102
102
|
MIT License
|
|
103
103
|
|
package/admin/tab_m.html
CHANGED
|
@@ -424,7 +424,7 @@
|
|
|
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
|
-
<button class="btn btn-outline" onclick="closeRestoreModal()">Schließen</button>
|
|
427
|
+
<button class="btn btn-outline" onclick="closeRestoreModal()" data-i18n="modalCancel">Schließen</button>
|
|
428
428
|
<button class="btn btn-outline-success" id="rmConfirmBtn" onclick="confirmRestoreScript(false)" data-i18n="modalLoad">Laden</button>
|
|
429
429
|
</div>
|
|
430
430
|
</div>
|
|
@@ -563,6 +563,19 @@
|
|
|
563
563
|
welcomeText: 'Lade ein Backup hoch oder wähle eine lokale Datei,<br>um Skripte anzuzeigen und wiederherzustellen.',
|
|
564
564
|
noSocket: 'Kein Socket. Bitte prüfen ob script-restore.{i} läuft.',
|
|
565
565
|
timeout: 'Timeout: Adapter antwortet nicht. Läuft script-restore.{i}?',
|
|
566
|
+
|
|
567
|
+
modalCancel: 'Schließen',
|
|
568
|
+
loadingFiles: '⏳ Lade Dateiliste...',
|
|
569
|
+
scriptsLoaded: '{n} Skripte geladen aus: {f}',
|
|
570
|
+
scriptsLoadedUrl: '{n} Skripte geladen von URL',
|
|
571
|
+
noScripts: 'Keine Skripte gefunden.',
|
|
572
|
+
noScriptsBackup: 'Keine Skripte in diesem Backup gefunden.',
|
|
573
|
+
noArchiveFile: 'Keine passende Datei im Archiv gefunden.',
|
|
574
|
+
errorParsing: 'Fehler beim Parsen: ',
|
|
575
|
+
errorReading: 'Fehler beim Lesen der Datei.',
|
|
576
|
+
errorPrefix: 'Fehler: ',
|
|
577
|
+
adapterTimeout: 'Fehler: Keine Antwort vom Adapter. Läuft script-restore.{i}?',
|
|
578
|
+
codeHint: '// Skript im Baum links auswählen… oder mehrere mit Strg+Klick für ZIP',
|
|
566
579
|
},
|
|
567
580
|
en: {
|
|
568
581
|
loaderLoading: 'Loading backup...',
|
|
@@ -606,6 +619,19 @@
|
|
|
606
619
|
welcomeText: 'Upload a backup or select a local file,<br>to view and restore scripts.',
|
|
607
620
|
noSocket: 'No socket. Please check if script-restore.{i} is running.',
|
|
608
621
|
timeout: 'Timeout: Adapter not responding. Is script-restore.{i} running?',
|
|
622
|
+
|
|
623
|
+
modalCancel: 'Close',
|
|
624
|
+
loadingFiles: '⏳ Loading file list...',
|
|
625
|
+
scriptsLoaded: '{n} scripts loaded from: {f}',
|
|
626
|
+
scriptsLoadedUrl: '{n} scripts loaded from URL',
|
|
627
|
+
noScripts: 'No scripts found.',
|
|
628
|
+
noScriptsBackup: 'No scripts found in this backup.',
|
|
629
|
+
noArchiveFile: 'No matching file found in archive.',
|
|
630
|
+
errorParsing: 'Error parsing: ',
|
|
631
|
+
errorReading: 'Error reading file.',
|
|
632
|
+
errorPrefix: 'Error: ',
|
|
633
|
+
adapterTimeout: 'Error: No response from adapter. Is script-restore.{i} running?',
|
|
634
|
+
codeHint: '// Select a script in the tree… or Ctrl+click multiple for ZIP',
|
|
609
635
|
},
|
|
610
636
|
fr: {
|
|
611
637
|
loaderLoading: 'Chargement de la sauvegarde...',
|
|
@@ -649,6 +675,19 @@
|
|
|
649
675
|
welcomeText: 'Téléversez une sauvegarde ou sélectionnez un fichier local,<br>pour afficher et restaurer des scripts.',
|
|
650
676
|
noSocket: 'Pas de socket. Vérifiez si script-restore.{i} est en cours d\'exécution.',
|
|
651
677
|
timeout: 'Délai d\'attente : l\'adaptateur ne répond pas. script-restore.{i} est-il en cours d\'exécution ?',
|
|
678
|
+
|
|
679
|
+
modalCancel: 'Fermer',
|
|
680
|
+
loadingFiles: '⏳ Chargement de la liste...',
|
|
681
|
+
scriptsLoaded: '{n} scripts chargés depuis : {f}',
|
|
682
|
+
scriptsLoadedUrl: '{n} scripts chargés depuis URL',
|
|
683
|
+
noScripts: 'Aucun script trouvé.',
|
|
684
|
+
noScriptsBackup: 'Aucun script trouvé dans cette sauvegarde.',
|
|
685
|
+
noArchiveFile: 'Aucun fichier correspondant trouvé dans l\'archive.',
|
|
686
|
+
errorParsing: 'Erreur d\'analyse : ',
|
|
687
|
+
errorReading: 'Erreur de lecture du fichier.',
|
|
688
|
+
errorPrefix: 'Erreur : ',
|
|
689
|
+
adapterTimeout: 'Erreur : pas de réponse de l\'adaptateur. script-restore.{i} est-il actif ?',
|
|
690
|
+
codeHint: '// Sélectionnez un script dans l\'arborescence… ou Ctrl+clic pour plusieurs en ZIP',
|
|
652
691
|
},
|
|
653
692
|
es: {
|
|
654
693
|
loaderLoading: 'Cargando copia de seguridad...',
|
|
@@ -692,6 +731,19 @@
|
|
|
692
731
|
welcomeText: 'Sube una copia de seguridad o selecciona un archivo local,<br>para ver y restaurar scripts.',
|
|
693
732
|
noSocket: 'Sin socket. Compruebe si script-restore.{i} está en ejecución.',
|
|
694
733
|
timeout: 'Tiempo de espera agotado: el adaptador no responde. ¿Está script-restore.{i} en ejecución?',
|
|
734
|
+
|
|
735
|
+
modalCancel: 'Cerrar',
|
|
736
|
+
loadingFiles: '⏳ Cargando lista de archivos...',
|
|
737
|
+
scriptsLoaded: '{n} scripts cargados desde: {f}',
|
|
738
|
+
scriptsLoadedUrl: '{n} scripts cargados desde URL',
|
|
739
|
+
noScripts: 'No se encontraron scripts.',
|
|
740
|
+
noScriptsBackup: 'No se encontraron scripts en este backup.',
|
|
741
|
+
noArchiveFile: 'No se encontró archivo compatible en el archivo.',
|
|
742
|
+
errorParsing: 'Error al analizar: ',
|
|
743
|
+
errorReading: 'Error al leer el archivo.',
|
|
744
|
+
errorPrefix: 'Error: ',
|
|
745
|
+
adapterTimeout: 'Error: sin respuesta del adaptador. ¿Está en ejecución script-restore.{i}?',
|
|
746
|
+
codeHint: '// Selecciona un script en el árbol… o Ctrl+clic para varios en ZIP',
|
|
695
747
|
},
|
|
696
748
|
it: {
|
|
697
749
|
loaderLoading: 'Caricamento backup...',
|
|
@@ -735,6 +787,19 @@
|
|
|
735
787
|
welcomeText: 'Carica un backup o seleziona un file locale,<br>per visualizzare e ripristinare gli script.',
|
|
736
788
|
noSocket: 'Nessun socket. Verificare se script-restore.{i} è in esecuzione.',
|
|
737
789
|
timeout: 'Timeout: l\'adattatore non risponde. script-restore.{i} è in esecuzione?',
|
|
790
|
+
|
|
791
|
+
modalCancel: 'Chiudi',
|
|
792
|
+
loadingFiles: '⏳ Caricamento lista file...',
|
|
793
|
+
scriptsLoaded: '{n} script caricati da: {f}',
|
|
794
|
+
scriptsLoadedUrl: '{n} script caricati da URL',
|
|
795
|
+
noScripts: 'Nessuno script trovato.',
|
|
796
|
+
noScriptsBackup: 'Nessuno script trovato in questo backup.',
|
|
797
|
+
noArchiveFile: 'Nessun file corrispondente trovato nell\'archivio.',
|
|
798
|
+
errorParsing: 'Errore di analisi: ',
|
|
799
|
+
errorReading: 'Errore nella lettura del file.',
|
|
800
|
+
errorPrefix: 'Errore: ',
|
|
801
|
+
adapterTimeout: 'Errore: nessuna risposta dall\'adattatore. script-restore.{i} è in esecuzione?',
|
|
802
|
+
codeHint: '// Seleziona uno script nell\'albero… o Ctrl+clic per più script in ZIP',
|
|
738
803
|
},
|
|
739
804
|
nl: {
|
|
740
805
|
loaderLoading: 'Back-up laden...',
|
|
@@ -778,6 +843,19 @@
|
|
|
778
843
|
welcomeText: 'Upload een back-up of selecteer een lokaal bestand,<br>om scripts te bekijken en te herstellen.',
|
|
779
844
|
noSocket: 'Geen socket. Controleer of script-restore.{i} actief is.',
|
|
780
845
|
timeout: 'Time-out: adapter reageert niet. Is script-restore.{i} actief?',
|
|
846
|
+
|
|
847
|
+
modalCancel: 'Sluiten',
|
|
848
|
+
loadingFiles: '⏳ Bestandslijst laden...',
|
|
849
|
+
scriptsLoaded: '{n} scripts geladen uit: {f}',
|
|
850
|
+
scriptsLoadedUrl: '{n} scripts geladen van URL',
|
|
851
|
+
noScripts: 'Geen scripts gevonden.',
|
|
852
|
+
noScriptsBackup: 'Geen scripts gevonden in deze backup.',
|
|
853
|
+
noArchiveFile: 'Geen overeenkomend bestand gevonden in het archief.',
|
|
854
|
+
errorParsing: 'Fout bij verwerken: ',
|
|
855
|
+
errorReading: 'Fout bij lezen van bestand.',
|
|
856
|
+
errorPrefix: 'Fout: ',
|
|
857
|
+
adapterTimeout: 'Fout: geen reactie van adapter. Is script-restore.{i} actief?',
|
|
858
|
+
codeHint: '// Selecteer een script in de boom… of Ctrl+klik voor meerdere als ZIP',
|
|
781
859
|
},
|
|
782
860
|
pl: {
|
|
783
861
|
loaderLoading: 'Wczytywanie kopii zapasowej...',
|
|
@@ -821,6 +899,19 @@
|
|
|
821
899
|
welcomeText: 'Prześlij kopię zapasową lub wybierz plik lokalny,<br>aby wyświetlić i przywrócić skrypty.',
|
|
822
900
|
noSocket: 'Brak gniazda. Sprawdź, czy script-restore.{i} działa.',
|
|
823
901
|
timeout: 'Przekroczono czas: adapter nie odpowiada. Czy script-restore.{i} działa?',
|
|
902
|
+
|
|
903
|
+
modalCancel: 'Zamknij',
|
|
904
|
+
loadingFiles: '⏳ Ładowanie listy plików...',
|
|
905
|
+
scriptsLoaded: '{n} skryptów załadowanych z: {f}',
|
|
906
|
+
scriptsLoadedUrl: '{n} skryptów załadowanych z URL',
|
|
907
|
+
noScripts: 'Nie znaleziono skryptów.',
|
|
908
|
+
noScriptsBackup: 'Nie znaleziono skryptów w tej kopii zapasowej.',
|
|
909
|
+
noArchiveFile: 'Nie znaleziono pasującego pliku w archiwum.',
|
|
910
|
+
errorParsing: 'Błąd analizy: ',
|
|
911
|
+
errorReading: 'Błąd odczytu pliku.',
|
|
912
|
+
errorPrefix: 'Błąd: ',
|
|
913
|
+
adapterTimeout: 'Błąd: brak odpowiedzi adaptera. Czy script-restore.{i} działa?',
|
|
914
|
+
codeHint: '// Wybierz skrypt w drzewie… lub Ctrl+klik dla wielu jako ZIP',
|
|
824
915
|
},
|
|
825
916
|
pt: {
|
|
826
917
|
loaderLoading: 'A carregar cópia de segurança...',
|
|
@@ -864,6 +955,19 @@
|
|
|
864
955
|
welcomeText: 'Carregue uma cópia de segurança ou selecione um ficheiro local,<br>para ver e restaurar scripts.',
|
|
865
956
|
noSocket: 'Sem socket. Verifique se script-restore.{i} está em execução.',
|
|
866
957
|
timeout: 'Tempo esgotado: o adaptador não responde. O script-restore.{i} está em execução?',
|
|
958
|
+
|
|
959
|
+
modalCancel: 'Fechar',
|
|
960
|
+
loadingFiles: '⏳ A carregar lista de ficheiros...',
|
|
961
|
+
scriptsLoaded: '{n} scripts carregados de: {f}',
|
|
962
|
+
scriptsLoadedUrl: '{n} scripts carregados de URL',
|
|
963
|
+
noScripts: 'Nenhum script encontrado.',
|
|
964
|
+
noScriptsBackup: 'Nenhum script encontrado neste backup.',
|
|
965
|
+
noArchiveFile: 'Nenhum ficheiro correspondente encontrado no arquivo.',
|
|
966
|
+
errorParsing: 'Erro ao analisar: ',
|
|
967
|
+
errorReading: 'Erro ao ler ficheiro.',
|
|
968
|
+
errorPrefix: 'Erro: ',
|
|
969
|
+
adapterTimeout: 'Erro: sem resposta do adaptador. O script-restore.{i} está em execução?',
|
|
970
|
+
codeHint: '// Selecione um script na árvore… ou Ctrl+clique para vários em ZIP',
|
|
867
971
|
},
|
|
868
972
|
ru: {
|
|
869
973
|
loaderLoading: 'Загрузка резервной копии...',
|
|
@@ -907,6 +1011,19 @@
|
|
|
907
1011
|
welcomeText: 'Загрузите резервную копию или выберите локальный файл,<br>чтобы просмотреть и восстановить скрипты.',
|
|
908
1012
|
noSocket: 'Нет сокета. Проверьте, запущен ли script-restore.{i}.',
|
|
909
1013
|
timeout: 'Таймаут: адаптер не отвечает. Запущен ли script-restore.{i}?',
|
|
1014
|
+
|
|
1015
|
+
modalCancel: 'Закрыть',
|
|
1016
|
+
loadingFiles: '⏳ Загрузка списка файлов...',
|
|
1017
|
+
scriptsLoaded: '{n} скриптов загружено из: {f}',
|
|
1018
|
+
scriptsLoadedUrl: '{n} скриптов загружено по URL',
|
|
1019
|
+
noScripts: 'Скрипты не найдены.',
|
|
1020
|
+
noScriptsBackup: 'Скрипты в этой резервной копии не найдены.',
|
|
1021
|
+
noArchiveFile: 'Подходящий файл в архиве не найден.',
|
|
1022
|
+
errorParsing: 'Ошибка разбора: ',
|
|
1023
|
+
errorReading: 'Ошибка чтения файла.',
|
|
1024
|
+
errorPrefix: 'Ошибка: ',
|
|
1025
|
+
adapterTimeout: 'Ошибка: адаптер не отвечает. Запущен ли script-restore.{i}?',
|
|
1026
|
+
codeHint: '// Выберите скрипт в дереве… или Ctrl+клик для нескольких в ZIP',
|
|
910
1027
|
},
|
|
911
1028
|
uk: {
|
|
912
1029
|
loaderLoading: 'Завантаження резервної копії...',
|
|
@@ -950,6 +1067,19 @@
|
|
|
950
1067
|
welcomeText: 'Завантажте резервну копію або оберіть локальний файл,<br>щоб переглянути та відновити скрипти.',
|
|
951
1068
|
noSocket: 'Немає сокета. Перевірте, чи запущено script-restore.{i}.',
|
|
952
1069
|
timeout: 'Час очікування вичерпано: адаптер не відповідає. Чи запущено script-restore.{i}?',
|
|
1070
|
+
|
|
1071
|
+
modalCancel: 'Закрити',
|
|
1072
|
+
loadingFiles: '⏳ Завантаження списку файлів...',
|
|
1073
|
+
scriptsLoaded: '{n} скриптів завантажено з: {f}',
|
|
1074
|
+
scriptsLoadedUrl: '{n} скриптів завантажено за URL',
|
|
1075
|
+
noScripts: 'Скрипти не знайдено.',
|
|
1076
|
+
noScriptsBackup: 'Скрипти у цій резервній копії не знайдено.',
|
|
1077
|
+
noArchiveFile: 'Відповідний файл в архіві не знайдено.',
|
|
1078
|
+
errorParsing: 'Помилка розбору: ',
|
|
1079
|
+
errorReading: 'Помилка читання файлу.',
|
|
1080
|
+
errorPrefix: 'Помилка: ',
|
|
1081
|
+
adapterTimeout: 'Помилка: адаптер не відповідає. Чи запущено script-restore.{i}?',
|
|
1082
|
+
codeHint: '// Оберіть скрипт у дереві… або Ctrl+клік для кількох у ZIP',
|
|
953
1083
|
},
|
|
954
1084
|
'zh-cn': {
|
|
955
1085
|
loaderLoading: '正在加载备份...',
|
|
@@ -993,25 +1123,48 @@
|
|
|
993
1123
|
welcomeText: '上传备份或选择本地文件,<br>以查看和恢复脚本。',
|
|
994
1124
|
noSocket: '无套接字。请检查 script-restore.{i} 是否正在运行。',
|
|
995
1125
|
timeout: '超时:适配器无响应。script-restore.{i} 是否正在运行?',
|
|
1126
|
+
|
|
1127
|
+
modalCancel: '关闭',
|
|
1128
|
+
loadingFiles: '⏳ 正在加载文件列表...',
|
|
1129
|
+
scriptsLoaded: '已从 {f} 加载 {n} 个脚本',
|
|
1130
|
+
scriptsLoadedUrl: '已从URL加载 {n} 个脚本',
|
|
1131
|
+
noScripts: '未找到脚本。',
|
|
1132
|
+
noScriptsBackup: '此备份中未找到脚本。',
|
|
1133
|
+
noArchiveFile: '在存档中未找到匹配的文件。',
|
|
1134
|
+
errorParsing: '解析错误:',
|
|
1135
|
+
errorReading: '读取文件时出错。',
|
|
1136
|
+
errorPrefix: '错误:',
|
|
1137
|
+
adapterTimeout: '错误:适配器无响应。script-restore.{i} 是否正在运行?',
|
|
1138
|
+
codeHint: '// 在左侧树中选择脚本…或按住 Ctrl 点击多个以下载 ZIP',
|
|
996
1139
|
},
|
|
997
1140
|
};
|
|
998
1141
|
|
|
999
1142
|
function detectLang() {
|
|
1000
1143
|
// 1. URL parameter
|
|
1001
1144
|
const lp = urlParams.get('lang') || urlParams.get('language');
|
|
1002
|
-
if (lp) return normLang(lp);
|
|
1003
|
-
// 2.
|
|
1145
|
+
if (lp) return { lang: normLang(lp), confident: true };
|
|
1146
|
+
// 2. <html lang="de"> des Parent-Frames (ioBroker Admin setzt das zuverlässig)
|
|
1004
1147
|
try {
|
|
1005
|
-
const
|
|
1006
|
-
if (
|
|
1148
|
+
const hl = window.parent.document.documentElement.lang;
|
|
1149
|
+
if (hl && hl.length >= 2) return { lang: normLang(hl), confident: true };
|
|
1007
1150
|
} catch(e) {}
|
|
1008
|
-
// 3.
|
|
1151
|
+
// 3. localStorage (verschiedene Admin-Versionen)
|
|
1152
|
+
try {
|
|
1153
|
+
for (const k of ['App.language', 'ioBroker.locale', 'lang', 'language']) {
|
|
1154
|
+
const v = localStorage.getItem(k);
|
|
1155
|
+
if (v && v.length >= 2) return { lang: normLang(v), confident: true };
|
|
1156
|
+
}
|
|
1157
|
+
} catch(e) {}
|
|
1158
|
+
// 4. Parent frame Variablen (Admin v5/v6/v7)
|
|
1009
1159
|
try {
|
|
1010
1160
|
const p = window.parent;
|
|
1011
|
-
if (p
|
|
1161
|
+
if (p.sysLang) return { lang: normLang(p.sysLang), confident: true };
|
|
1162
|
+
if (p.systemLang) return { lang: normLang(p.systemLang), confident: true };
|
|
1163
|
+
if (p.main && p.main.language) return { lang: normLang(p.main.language), confident: true };
|
|
1164
|
+
if (p.app && p.app.language) return { lang: normLang(p.app.language), confident: true };
|
|
1012
1165
|
} catch(e) {}
|
|
1013
|
-
//
|
|
1014
|
-
return normLang(navigator.language || 'en');
|
|
1166
|
+
// 5. Browser-Sprache (least reliable — socket may override)
|
|
1167
|
+
return { lang: normLang(navigator.language || 'en'), confident: false };
|
|
1015
1168
|
}
|
|
1016
1169
|
|
|
1017
1170
|
function normLang(l) {
|
|
@@ -1021,15 +1174,48 @@
|
|
|
1021
1174
|
return ll.slice(0, 2);
|
|
1022
1175
|
}
|
|
1023
1176
|
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1177
|
+
let LANG_CONFIDENT = false;
|
|
1178
|
+
let LANG = (function() {
|
|
1179
|
+
const { lang, confident } = detectLang();
|
|
1180
|
+
const resolved = TRANSLATIONS[lang] ? lang : 'en';
|
|
1181
|
+
LANG_CONFIDENT = confident && !!TRANSLATIONS[lang];
|
|
1182
|
+
return resolved;
|
|
1027
1183
|
})();
|
|
1028
1184
|
|
|
1029
1185
|
function t(key) {
|
|
1030
1186
|
return (TRANSLATIONS[LANG] && TRANSLATIONS[LANG][key]) || (TRANSLATIONS.en && TRANSLATIONS.en[key]) || key;
|
|
1031
1187
|
}
|
|
1032
1188
|
|
|
1189
|
+
function applyLangFromSocket() {
|
|
1190
|
+
// Skip: a reliable sync source (parent html, localStorage, URL) already resolved the language.
|
|
1191
|
+
// Applying the socket result on top would cause a visible flash.
|
|
1192
|
+
if (LANG_CONFIDENT) return;
|
|
1193
|
+
if (!socket) return;
|
|
1194
|
+
const apply = function(lang) {
|
|
1195
|
+
if (!lang) return;
|
|
1196
|
+
const l = normLang(lang);
|
|
1197
|
+
if (TRANSLATIONS[l] && l !== LANG) {
|
|
1198
|
+
LANG = l;
|
|
1199
|
+
applyTranslations();
|
|
1200
|
+
}
|
|
1201
|
+
};
|
|
1202
|
+
try {
|
|
1203
|
+
if (typeof socket.getSystemConfig === 'function') {
|
|
1204
|
+
Promise.resolve(socket.getSystemConfig()).then(function(cfg) {
|
|
1205
|
+
if (cfg && cfg.common) apply(cfg.common.language);
|
|
1206
|
+
}).catch(function() {});
|
|
1207
|
+
} else if (typeof socket.getObject === 'function') {
|
|
1208
|
+
Promise.resolve(socket.getObject('system.config')).then(function(obj) {
|
|
1209
|
+
if (obj && obj.common) apply(obj.common.language);
|
|
1210
|
+
}).catch(function() {});
|
|
1211
|
+
} else if (typeof socket.emit === 'function') {
|
|
1212
|
+
socket.emit('getObject', 'system.config', function(err, obj) {
|
|
1213
|
+
if (!err && obj && obj.common) apply(obj.common.language);
|
|
1214
|
+
});
|
|
1215
|
+
}
|
|
1216
|
+
} catch(e) {}
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1033
1219
|
function applyTranslations() {
|
|
1034
1220
|
document.querySelectorAll('[data-i18n]').forEach(function(el) {
|
|
1035
1221
|
el.textContent = t(el.getAttribute('data-i18n'));
|
|
@@ -1178,6 +1364,8 @@
|
|
|
1178
1364
|
}
|
|
1179
1365
|
|
|
1180
1366
|
initSocket();
|
|
1367
|
+
applyLangFromSocket();
|
|
1368
|
+
setTimeout(applyLangFromSocket, 800);
|
|
1181
1369
|
|
|
1182
1370
|
// === Resizer ===
|
|
1183
1371
|
const resizerEl = document.getElementById('resizer');
|
|
@@ -1334,7 +1522,7 @@
|
|
|
1334
1522
|
const entry = entries.find(e => e.name === target || e.name.endsWith('/' + target));
|
|
1335
1523
|
if (entry) return parseJsonContent(dec.decode(entry.content), target);
|
|
1336
1524
|
}
|
|
1337
|
-
throw new Error(
|
|
1525
|
+
throw new Error(t('noArchiveFile'));
|
|
1338
1526
|
}
|
|
1339
1527
|
|
|
1340
1528
|
// === File Upload ===
|
|
@@ -1355,13 +1543,13 @@
|
|
|
1355
1543
|
const scripts = parseJsonContent(reader.result, file.name);
|
|
1356
1544
|
hideLoader();
|
|
1357
1545
|
loadScripts(scripts);
|
|
1358
|
-
setStatus(scripts.length
|
|
1546
|
+
setStatus(t('scriptsLoaded').replace('{n}', scripts.length).replace('{f}', file.name), 'success');
|
|
1359
1547
|
} catch(e) {
|
|
1360
1548
|
hideLoader();
|
|
1361
|
-
setStatus('
|
|
1549
|
+
setStatus(t('errorParsing') + e.message, 'error');
|
|
1362
1550
|
}
|
|
1363
1551
|
};
|
|
1364
|
-
reader.onerror = () => { hideLoader(); setStatus('
|
|
1552
|
+
reader.onerror = () => { hideLoader(); setStatus(t('errorReading'), 'error'); };
|
|
1365
1553
|
reader.readAsText(file, 'utf-8');
|
|
1366
1554
|
this.value = '';
|
|
1367
1555
|
return;
|
|
@@ -1378,15 +1566,15 @@
|
|
|
1378
1566
|
const scripts = await parseArchiveInBrowser(archiveReader.result, file.name); // result is ArrayBuffer
|
|
1379
1567
|
hideLoader();
|
|
1380
1568
|
loadScripts(scripts);
|
|
1381
|
-
setStatus(scripts.length
|
|
1569
|
+
setStatus(t('scriptsLoaded').replace('{n}', scripts.length).replace('{f}', file.name), 'success');
|
|
1382
1570
|
} catch(e) {
|
|
1383
1571
|
hideLoader();
|
|
1384
|
-
setStatus('
|
|
1572
|
+
setStatus(t('errorPrefix') + e.message, 'error');
|
|
1385
1573
|
}
|
|
1386
1574
|
};
|
|
1387
1575
|
archiveReader.onerror = function() {
|
|
1388
1576
|
hideLoader();
|
|
1389
|
-
setStatus('
|
|
1577
|
+
setStatus(t('errorReading'), 'error');
|
|
1390
1578
|
};
|
|
1391
1579
|
archiveReader.readAsArrayBuffer(file);
|
|
1392
1580
|
this.value = '';
|
|
@@ -1437,7 +1625,7 @@
|
|
|
1437
1625
|
dropdownState[src] = !isOpen;
|
|
1438
1626
|
if (dropdownState[src]) {
|
|
1439
1627
|
menu.classList.add('open');
|
|
1440
|
-
menu.innerHTML = '<div class="dropdown-loading"
|
|
1628
|
+
menu.innerHTML = '<div class="dropdown-loading">' + t('loadingFiles') + '</div>';
|
|
1441
1629
|
sendTo(cfg.listCmd, {}, function(result) {
|
|
1442
1630
|
if (result && result.error) {
|
|
1443
1631
|
menu.innerHTML = '<div class="dropdown-empty">⚠️ ' + escapeHTML(result.error) + '</div>';
|
|
@@ -1481,12 +1669,12 @@
|
|
|
1481
1669
|
sendTo(cfg.parseCmd, { filename: filename }, function(result) {
|
|
1482
1670
|
hideLoader();
|
|
1483
1671
|
if (result && result.error) {
|
|
1484
|
-
setStatus('
|
|
1672
|
+
setStatus(t('errorPrefix') + result.error, 'error');
|
|
1485
1673
|
} else if (result && result.scripts) {
|
|
1486
1674
|
loadScripts(result.scripts);
|
|
1487
|
-
setStatus(result.scripts.length
|
|
1675
|
+
setStatus(t('scriptsLoaded').replace('{n}', result.scripts.length).replace('{f}', filename), 'success');
|
|
1488
1676
|
} else {
|
|
1489
|
-
setStatus('
|
|
1677
|
+
setStatus(t('noScripts'), 'error');
|
|
1490
1678
|
}
|
|
1491
1679
|
});
|
|
1492
1680
|
}
|
|
@@ -1500,15 +1688,15 @@
|
|
|
1500
1688
|
sendTo('parseHttpUrl', { url }, function(result) {
|
|
1501
1689
|
hideLoader();
|
|
1502
1690
|
if (!result) {
|
|
1503
|
-
setStatus('
|
|
1691
|
+
setStatus(t('adapterTimeout').replace('{i}', instance), 'error');
|
|
1504
1692
|
} else if (result.error) {
|
|
1505
|
-
setStatus('
|
|
1506
|
-
alert('
|
|
1693
|
+
setStatus(t('errorPrefix') + result.error, 'error');
|
|
1694
|
+
alert(t('errorPrefix') + result.error);
|
|
1507
1695
|
} else if (result.scripts) {
|
|
1508
1696
|
loadScripts(result.scripts);
|
|
1509
|
-
setStatus(result.scripts.length
|
|
1697
|
+
setStatus(t('scriptsLoadedUrl').replace('{n}', result.scripts.length), 'success');
|
|
1510
1698
|
} else {
|
|
1511
|
-
setStatus('
|
|
1699
|
+
setStatus(t('noScripts'), 'error');
|
|
1512
1700
|
}
|
|
1513
1701
|
});
|
|
1514
1702
|
}
|
|
@@ -1555,8 +1743,8 @@
|
|
|
1555
1743
|
document.getElementById('actionBar').style.display = 'none';
|
|
1556
1744
|
document.getElementById('codeContainer').className = 'code-empty';
|
|
1557
1745
|
document.getElementById('codeContainer').innerHTML = scripts.length > 0
|
|
1558
|
-
? '
|
|
1559
|
-
: '<span style="color:#dc3545">
|
|
1746
|
+
? t('codeHint')
|
|
1747
|
+
: '<span style="color:#dc3545">' + t('noScriptsBackup') + '</span>';
|
|
1560
1748
|
}
|
|
1561
1749
|
|
|
1562
1750
|
// === Loader ===
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "script-restore",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.3",
|
|
5
5
|
"news": {
|
|
6
|
+
"0.1.3": {
|
|
7
|
+
"en": "fix language flash: skip socket override when language already detected from admin frame\nreplace all hardcoded status strings with translated t() calls\nadd codeHint translation key in all 11 languages",
|
|
8
|
+
"de": "fixer sprachblitz: überschreiben der sprachfassung, wenn die sprache bereits von admin-rahmen erkannt wurde\nalle hardcoded status strings durch übersetzte t()-anrufe ersetzen\ncode hinzufügenHint Übersetzungsschlüssel in allen 11 Sprachen",
|
|
9
|
+
"ru": "исправьте языковую вспышку: пропустите переопределение сокетов, когда язык уже обнаружен в рамке администратора\nзаменить все жестко закодированные строки состояния переводными вызовами t()\nдобавить ключ перевода codeHint на все 11 языков",
|
|
10
|
+
"pt": "flash da linguagem de correção: sobreposição do socket pule quando a linguagem já detectada do frame do administrador\nsubstituir todas as cadeias de estado codificadas por chamadas traduzidas em t()\nadicionar códigoHint chave de tradução em todos os 11 idiomas",
|
|
11
|
+
"nl": "fix taalflits: sla socket over wanneer taal al gedetecteerd vanuit admin frame\nvervangen alle hardcoded status strings door vertaalde t() calls\ncodeHint vertaalsleutel toevoegen in alle 11 talen",
|
|
12
|
+
"fr": "résoudre le flash de langue: sauter la préséance socket lorsque la langue déjà détectée à partir du cadre admin\nremplacer toutes les chaînes d'état codées en dur par des appels traduit t()\najouter codeHint clé de traduction dans les 11 langues",
|
|
13
|
+
"it": "fix lingua flash: saltare socket override quando la lingua già rilevata dalla cornice di amministrazione\nsostituire tutte le stringhe di stato codificate con le chiamate t() tradotte\naggiungere codice Chiave di traduzione in tutte le 11 lingue",
|
|
14
|
+
"es": "solucionar el flash del lenguaje: saltar la anulación de la toma cuando el lenguaje ya detectado desde el marco de administración\nreemplazar todas las cadenas de estado codificadas con llamadas t( traducido)\nañadir codeHint traducción clave en los 11 idiomas",
|
|
15
|
+
"pl": "fix flash język: skip derogator gniazda, gdy język już wykryty z ramki admin\nzastąp wszystkie zaszyfrowane łańcuchy stanu translated t () calls\ndodaj klucz tłumaczeniowy CodeHint we wszystkich 11 językach",
|
|
16
|
+
"uk": "фіксувати мовне спалах: пропустити розетку перенапругою, коли мова вже виявлена з адмін кадру\nзамінити всі жорсткікодовані рядки стану з перекладеними t() дзвінки\nadd codeПеревірити ключ у всіх 11 мовах",
|
|
17
|
+
"zh-cn": "修补语言闪存: 当语言已经从管理员框架中检测到时跳过套接字覆盖\n将所有硬码状态字符串替换为已翻译的 t() 调用\n在所有11种语言中添加代码Hint翻译密钥"
|
|
18
|
+
},
|
|
6
19
|
"0.1.2": {
|
|
7
20
|
"en": "add full i18n to tab UI: all strings translated into de/en/fr/es/it/nl/pl/pt/ru/uk/zh-cn",
|
|
8
21
|
"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",
|
|
@@ -80,19 +93,6 @@
|
|
|
80
93
|
"pl": "Poprawiono rozmiary responsywne jsonConfig; aktualności ograniczone do 7; dodano cooldown Dependabot",
|
|
81
94
|
"uk": "Виправлено розміри jsonConfig; новини скорочено до 7; додано cooldown Dependabot",
|
|
82
95
|
"zh-cn": "修复 jsonConfig 响应式尺寸;新闻条目减少至 7 条;添加 Dependabot 冷却时间"
|
|
83
|
-
},
|
|
84
|
-
"0.0.9": {
|
|
85
|
-
"en": "Fix jsonConfig: add responsive size attributes, i18n support, remove outdated admin files",
|
|
86
|
-
"de": "jsonConfig: Responsive Size-Attribute ergänzt, i18n-Unterstützung, veraltete Admin-Dateien entfernt",
|
|
87
|
-
"ru": "jsonConfig: добавлены атрибуты размера, поддержка i18n, удалены устаревшие файлы admin",
|
|
88
|
-
"pt": "jsonConfig: atributos de tamanho responsivos, suporte i18n, remoção de ficheiros admin obsoletos",
|
|
89
|
-
"nl": "jsonConfig: responsieve grootteattributen, i18n-ondersteuning, verouderde adminbestanden verwijderd",
|
|
90
|
-
"fr": "jsonConfig: attributs de taille responsifs, support i18n, suppression fichiers admin obsolètes",
|
|
91
|
-
"it": "jsonConfig: attributi dimensione responsivi, supporto i18n, rimozione file admin obsoleti",
|
|
92
|
-
"es": "jsonConfig: atributos de tamaño responsivos, soporte i18n, eliminación de archivos admin obsoletos",
|
|
93
|
-
"pl": "jsonConfig: atrybuty rozmiaru responsywnego, wsparcie i18n, usunięcie przestarzałych plików admin",
|
|
94
|
-
"uk": "jsonConfig: атрибути розміру, підтримка i18n, видалення застарілих файлів admin",
|
|
95
|
-
"zh-cn": "jsonConfig:添加响应式尺寸属性、i18n 支持,删除过时的 admin 文件"
|
|
96
96
|
}
|
|
97
97
|
},
|
|
98
98
|
"titleLang": {
|