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 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
- ### **WORK IN PROGRESS**
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. localStorage
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 ls = localStorage.getItem('App.language') || localStorage.getItem('ioBroker.locale');
1006
- if (ls) return normLang(ls);
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. Parent frame
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 && (p.sysLang || p.systemLang)) return normLang(p.sysLang || p.systemLang);
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
- // 4. Navigator
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
- const LANG = (function() {
1025
- const detected = detectLang();
1026
- return TRANSLATIONS[detected] ? detected : 'en';
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('Keine passende Datei im Archiv gefunden (objects.json, objects.jsonl, scripts.json)');
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 + ' Skripte geladen aus: ' + file.name, 'success');
1546
+ setStatus(t('scriptsLoaded').replace('{n}', scripts.length).replace('{f}', file.name), 'success');
1359
1547
  } catch(e) {
1360
1548
  hideLoader();
1361
- setStatus('Fehler beim Parsen: ' + e.message, 'error');
1549
+ setStatus(t('errorParsing') + e.message, 'error');
1362
1550
  }
1363
1551
  };
1364
- reader.onerror = () => { hideLoader(); setStatus('Fehler beim Lesen der Datei.', 'error'); };
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 + ' Skripte geladen aus: ' + file.name, 'success');
1569
+ setStatus(t('scriptsLoaded').replace('{n}', scripts.length).replace('{f}', file.name), 'success');
1382
1570
  } catch(e) {
1383
1571
  hideLoader();
1384
- setStatus('Fehler: ' + e.message, 'error');
1572
+ setStatus(t('errorPrefix') + e.message, 'error');
1385
1573
  }
1386
1574
  };
1387
1575
  archiveReader.onerror = function() {
1388
1576
  hideLoader();
1389
- setStatus('Fehler beim Lesen der Datei.', 'error');
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">⏳ Lade Dateiliste...</div>';
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('Fehler: ' + result.error, 'error');
1672
+ setStatus(t('errorPrefix') + result.error, 'error');
1485
1673
  } else if (result && result.scripts) {
1486
1674
  loadScripts(result.scripts);
1487
- setStatus(result.scripts.length + ' Skripte geladen aus: ' + filename, 'success');
1675
+ setStatus(t('scriptsLoaded').replace('{n}', result.scripts.length).replace('{f}', filename), 'success');
1488
1676
  } else {
1489
- setStatus('Keine Skripte gefunden.', 'error');
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('Fehler: Keine Antwort vom Adapter. Läuft script-restore.' + instance + '?', 'error');
1691
+ setStatus(t('adapterTimeout').replace('{i}', instance), 'error');
1504
1692
  } else if (result.error) {
1505
- setStatus('Fehler: ' + result.error, 'error');
1506
- alert('Fehler beim Laden: ' + result.error);
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 + ' Skripte geladen von URL', 'success');
1697
+ setStatus(t('scriptsLoadedUrl').replace('{n}', result.scripts.length), 'success');
1510
1698
  } else {
1511
- setStatus('Keine Skripte gefunden.', 'error');
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
- ? '// Skript im Baum links auswählen… oder mehrere mit Strg+Klick für ZIP'
1559
- : '<span style="color:#dc3545">Keine Skripte in diesem Backup gefunden.</span>';
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.2",
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": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.script-restore",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Restore ioBroker scripts from backup archives",
5
5
  "author": {
6
6
  "name": "ipod86",