iobroker.shoppingroute 0.4.3 → 0.4.4

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.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ![ShoppingRoute](admin/shoppingroute.png)
4
4
 
5
- **Current version: 0.4.0**
5
+ **Current version: 0.4.4**
6
6
 
7
7
  ShoppingRoute sorts Alexa shopping-list entries by market, product group and each store's individual walking route. It assigns visible two-digit keys such as `20> Bananas` and `40> ═════ ALDI ═════`; managed lists must therefore be set to **A–Z** in the Alexa app. ShoppingRoute reuses the local Alexa2 authentication for direct item updates, deletes and batch creates, while Alexa2 list states remain the external change trigger.
8
8
 
@@ -40,6 +40,9 @@ See `README_DE.md` for the detailed German documentation.
40
40
 
41
41
  ## Changelog
42
42
 
43
+ ### 0.4.4 (2026-09-25)
44
+ - (RaviniZib) Use `No Market` for new fallback-market defaults, add all 11 admin languages to the backup utility, and remove six unused translation keys. Existing market names, routes and product data remain unchanged.
45
+
43
46
  ### 0.4.3 (2026-09-25)
44
47
  - (RaviniZib) Route Alexa callback timeouts through ioBroker-managed timers so stalled callbacks stay bounded without plain Node.js `setTimeout()` calls in adapter source.
45
48
  - (RaviniZib) Keep `common.news` within the seven entries supported by the repository builder.
package/README_DE.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ![ShoppingRoute](admin/shoppingroute.png)
4
4
 
5
- **Aktuelle Version: 0.4.0**
5
+ **Aktuelle Version: 0.4.4**
6
6
 
7
7
  ShoppingRoute sortiert Alexa-Einkaufslisteneinträge nach Markt, Produktgruppe und dem individuellen Laufweg durch den jeweiligen Markt. Dazu vergibt es sichtbare zweistellige Schlüssel wie `20> Bananen` und `40> ═════ ALDI ═════`; verwaltete Listen müssen deshalb in der Alexa-App auf **A–Z** stehen. ShoppingRoute übernimmt lokal die Alexa2-Authentifizierung für direkte Updates, Deletes und Batch-Creates; Alexa2-Listenstates bleiben die Triggerquelle für externe Änderungen.
8
8
 
@@ -73,6 +73,9 @@ ShoppingRoute wird unter der **[MIT-Lizenz](LICENSE)** veröffentlicht. Frühere
73
73
 
74
74
  ## Changelog
75
75
 
76
+ ### 0.4.4 (2026-09-25)
77
+ - (RaviniZib) `No Market` als Standard-Ausweichmarkt für neue Konfigurationen, Backup-Seite in allen 11 Admin-Sprachen und sechs ungenutzte Übersetzungsschlüssel entfernt. Vorhandene Marktnamen, Laufwege und Produktdaten bleiben unverändert.
78
+
76
79
  ### 0.4.3 (2026-09-25)
77
80
  - Alexa-Callback-Timeouts verwenden jetzt ioBroker-verwaltete Timer; hängende Aufrufe bleiben damit begrenzt, ohne nacktes Node.js-`setTimeout()` im Adapter-Quellcode.
78
81
  - `common.news` wird auf die vom Repository-Builder unterstützten sieben Einträge begrenzt.
@@ -0,0 +1,281 @@
1
+ 'use strict';
2
+
3
+ // Standalone backup utility: keep its translations separate from JSON Config keys.
4
+ (() => {
5
+ const translations = {
6
+ "de": {
7
+ "configTitle": "Komplette Konfiguration",
8
+ "configText": "Alle ShoppingRoute-Einstellungen direkt als JSON-Datei sichern oder aus einer Sicherungsdatei wiederherstellen.",
9
+ "downloadConfig": "↓ Sicherung herunterladen",
10
+ "uploadConfig": "↑ Sicherung wiederherstellen",
11
+ "marketTitle": "Marktprofil teilen",
12
+ "marketText": "Einen einzelnen Markt mit seinem Laufweg herunterladen oder ein Marktprofil aus einer JSON-Datei importieren.",
13
+ "downloadMarket": "↓ Marktprofil herunterladen",
14
+ "uploadMarket": "↑ Marktprofil importieren",
15
+ "loading": "Marktprofile werden geladen …",
16
+ "noMarkets": "Keine Marktprofile vorhanden",
17
+ "backupDownloaded": "Sicherung wurde heruntergeladen.",
18
+ "profileDownloaded": "Marktprofil wurde heruntergeladen.",
19
+ "configImportStarted": "Sicherung wurde übergeben. ShoppingRoute übernimmt die Konfiguration und startet anschließend neu.",
20
+ "profileImportStarted": "Marktprofil wurde übergeben und wird von ShoppingRoute übernommen.",
21
+ "invalidBackup": "Die gewählte Datei ist keine gültige JSON-Sicherung.",
22
+ "invalidProfile": "Die gewählte Datei ist kein gültiges ShoppingRoute-Marktprofil.",
23
+ "noInstance": "ShoppingRoute-Instanz konnte nicht ermittelt werden.",
24
+ "connectionError": "Verbindung zu ioBroker konnte nicht hergestellt werden.",
25
+ "exportError": "Sicherung konnte nicht erstellt werden.",
26
+ "profileError": "Marktprofile konnten nicht geladen werden.",
27
+ "title": "ShoppingRoute – Sicherung / Teilen",
28
+ "genericMarket": "Markt"
29
+ },
30
+ "en": {
31
+ "configTitle": "Complete configuration",
32
+ "configText": "Download all ShoppingRoute settings directly as a JSON file or restore them from a backup file.",
33
+ "downloadConfig": "↓ Download backup",
34
+ "uploadConfig": "↑ Restore backup",
35
+ "marketTitle": "Share market profile",
36
+ "marketText": "Download one market with its walking route or import a market profile from a JSON file.",
37
+ "downloadMarket": "↓ Download market profile",
38
+ "uploadMarket": "↑ Import market profile",
39
+ "loading": "Loading market profiles …",
40
+ "noMarkets": "No market profiles available",
41
+ "backupDownloaded": "Backup downloaded.",
42
+ "profileDownloaded": "Market profile downloaded.",
43
+ "configImportStarted": "Backup submitted. ShoppingRoute will apply the configuration and restart afterwards.",
44
+ "profileImportStarted": "Market profile submitted and will be applied by ShoppingRoute.",
45
+ "invalidBackup": "The selected file is not a valid JSON backup.",
46
+ "invalidProfile": "The selected file is not a valid ShoppingRoute market profile.",
47
+ "noInstance": "ShoppingRoute instance could not be determined.",
48
+ "connectionError": "Could not connect to ioBroker.",
49
+ "exportError": "Backup could not be created.",
50
+ "profileError": "Market profiles could not be loaded.",
51
+ "title": "ShoppingRoute – Backup / Share",
52
+ "genericMarket": "Market"
53
+ },
54
+ "es": {
55
+ "configTitle": "Configuración completa",
56
+ "configText": "Descargue todos los ajustes de ShoppingRoute como archivo JSON o restáurelos desde una copia de seguridad.",
57
+ "downloadConfig": "↓ Descargar copia de seguridad",
58
+ "uploadConfig": "↑ Restaurar copia de seguridad",
59
+ "marketTitle": "Compartir perfil de tienda",
60
+ "marketText": "Descargue una tienda con su recorrido o importe un perfil de tienda desde un archivo JSON.",
61
+ "downloadMarket": "↓ Descargar perfil de tienda",
62
+ "uploadMarket": "↑ Importar perfil de tienda",
63
+ "loading": "Cargando perfiles de tienda …",
64
+ "noMarkets": "No hay perfiles de tienda disponibles",
65
+ "backupDownloaded": "Copia de seguridad descargada.",
66
+ "profileDownloaded": "Perfil de tienda descargado.",
67
+ "configImportStarted": "Copia de seguridad enviada. ShoppingRoute aplicará la configuración y se reiniciará.",
68
+ "profileImportStarted": "Perfil de tienda enviado. ShoppingRoute lo aplicará.",
69
+ "invalidBackup": "El archivo seleccionado no es una copia de seguridad JSON válida.",
70
+ "invalidProfile": "El archivo seleccionado no es un perfil de tienda de ShoppingRoute válido.",
71
+ "noInstance": "No se pudo determinar la instancia de ShoppingRoute.",
72
+ "connectionError": "No se pudo conectar con ioBroker.",
73
+ "exportError": "No se pudo crear la copia de seguridad.",
74
+ "profileError": "No se pudieron cargar los perfiles de tienda.",
75
+ "title": "ShoppingRoute – Copias de seguridad / Compartir",
76
+ "genericMarket": "Tienda"
77
+ },
78
+ "fr": {
79
+ "configTitle": "Configuration complète",
80
+ "configText": "Téléchargez tous les paramètres de ShoppingRoute dans un fichier JSON ou restaurez-les depuis une sauvegarde.",
81
+ "downloadConfig": "↓ Télécharger la sauvegarde",
82
+ "uploadConfig": "↑ Restaurer une sauvegarde",
83
+ "marketTitle": "Partager un profil de magasin",
84
+ "marketText": "Téléchargez un magasin avec son parcours ou importez un profil de magasin depuis un fichier JSON.",
85
+ "downloadMarket": "↓ Télécharger le profil de magasin",
86
+ "uploadMarket": "↑ Importer un profil de magasin",
87
+ "loading": "Chargement des profils de magasin …",
88
+ "noMarkets": "Aucun profil de magasin disponible",
89
+ "backupDownloaded": "Sauvegarde téléchargée.",
90
+ "profileDownloaded": "Profil de magasin téléchargé.",
91
+ "configImportStarted": "Sauvegarde transmise. ShoppingRoute appliquera la configuration puis redémarrera.",
92
+ "profileImportStarted": "Profil de magasin transmis. ShoppingRoute va l’appliquer.",
93
+ "invalidBackup": "Le fichier sélectionné n’est pas une sauvegarde JSON valide.",
94
+ "invalidProfile": "Le fichier sélectionné n’est pas un profil de magasin ShoppingRoute valide.",
95
+ "noInstance": "Impossible de déterminer l’instance ShoppingRoute.",
96
+ "connectionError": "Impossible de se connecter à ioBroker.",
97
+ "exportError": "Impossible de créer la sauvegarde.",
98
+ "profileError": "Impossible de charger les profils de magasin.",
99
+ "title": "ShoppingRoute – Sauvegarde / Partage",
100
+ "genericMarket": "Magasin"
101
+ },
102
+ "it": {
103
+ "configTitle": "Configurazione completa",
104
+ "configText": "Scarica tutte le impostazioni di ShoppingRoute come file JSON o ripristinale da un backup.",
105
+ "downloadConfig": "↓ Scarica backup",
106
+ "uploadConfig": "↑ Ripristina backup",
107
+ "marketTitle": "Condividi profilo del negozio",
108
+ "marketText": "Scarica un negozio con il suo percorso o importa un profilo del negozio da un file JSON.",
109
+ "downloadMarket": "↓ Scarica profilo del negozio",
110
+ "uploadMarket": "↑ Importa profilo del negozio",
111
+ "loading": "Caricamento dei profili dei negozi …",
112
+ "noMarkets": "Nessun profilo del negozio disponibile",
113
+ "backupDownloaded": "Backup scaricato.",
114
+ "profileDownloaded": "Profilo del negozio scaricato.",
115
+ "configImportStarted": "Backup inviato. ShoppingRoute applicherà la configurazione e si riavvierà.",
116
+ "profileImportStarted": "Profilo del negozio inviato. ShoppingRoute lo applicherà.",
117
+ "invalidBackup": "Il file selezionato non è un backup JSON valido.",
118
+ "invalidProfile": "Il file selezionato non è un profilo del negozio ShoppingRoute valido.",
119
+ "noInstance": "Impossibile determinare l’istanza ShoppingRoute.",
120
+ "connectionError": "Impossibile connettersi a ioBroker.",
121
+ "exportError": "Impossibile creare il backup.",
122
+ "profileError": "Impossibile caricare i profili dei negozi.",
123
+ "title": "ShoppingRoute – Backup / Condivisione",
124
+ "genericMarket": "Negozio"
125
+ },
126
+ "nl": {
127
+ "configTitle": "Volledige configuratie",
128
+ "configText": "Download alle ShoppingRoute-instellingen als JSON-bestand of herstel ze vanuit een back-up.",
129
+ "downloadConfig": "↓ Back-up downloaden",
130
+ "uploadConfig": "↑ Back-up herstellen",
131
+ "marketTitle": "Winkelprofiel delen",
132
+ "marketText": "Download een winkel met de bijbehorende looproute of importeer een winkelprofiel uit een JSON-bestand.",
133
+ "downloadMarket": "↓ Winkelprofiel downloaden",
134
+ "uploadMarket": "↑ Winkelprofiel importeren",
135
+ "loading": "Winkelprofielen laden …",
136
+ "noMarkets": "Geen winkelprofielen beschikbaar",
137
+ "backupDownloaded": "Back-up gedownload.",
138
+ "profileDownloaded": "Winkelprofiel gedownload.",
139
+ "configImportStarted": "Back-up verzonden. ShoppingRoute past de configuratie toe en start daarna opnieuw.",
140
+ "profileImportStarted": "Winkelprofiel verzonden. ShoppingRoute zal het toepassen.",
141
+ "invalidBackup": "Het geselecteerde bestand is geen geldige JSON-back-up.",
142
+ "invalidProfile": "Het geselecteerde bestand is geen geldig ShoppingRoute-winkelprofiel.",
143
+ "noInstance": "De ShoppingRoute-instantie kon niet worden bepaald.",
144
+ "connectionError": "Verbinding met ioBroker is mislukt.",
145
+ "exportError": "De back-up kon niet worden gemaakt.",
146
+ "profileError": "De winkelprofielen konden niet worden geladen.",
147
+ "title": "ShoppingRoute – Back-up / Delen",
148
+ "genericMarket": "Winkel"
149
+ },
150
+ "pt": {
151
+ "configTitle": "Configuração completa",
152
+ "configText": "Transfira todas as definições do ShoppingRoute como ficheiro JSON ou restaure-as a partir de uma cópia de segurança.",
153
+ "downloadConfig": "↓ Transferir cópia de segurança",
154
+ "uploadConfig": "↑ Restaurar cópia de segurança",
155
+ "marketTitle": "Partilhar perfil de loja",
156
+ "marketText": "Transfira uma loja com o respetivo percurso ou importe um perfil de loja a partir de um ficheiro JSON.",
157
+ "downloadMarket": "↓ Transferir perfil de loja",
158
+ "uploadMarket": "↑ Importar perfil de loja",
159
+ "loading": "A carregar perfis de loja …",
160
+ "noMarkets": "Não existem perfis de loja disponíveis",
161
+ "backupDownloaded": "Cópia de segurança transferida.",
162
+ "profileDownloaded": "Perfil de loja transferido.",
163
+ "configImportStarted": "Cópia de segurança enviada. O ShoppingRoute aplicará a configuração e reiniciará.",
164
+ "profileImportStarted": "Perfil de loja enviado. O ShoppingRoute irá aplicá-lo.",
165
+ "invalidBackup": "O ficheiro selecionado não é uma cópia de segurança JSON válida.",
166
+ "invalidProfile": "O ficheiro selecionado não é um perfil de loja ShoppingRoute válido.",
167
+ "noInstance": "Não foi possível determinar a instância do ShoppingRoute.",
168
+ "connectionError": "Não foi possível ligar ao ioBroker.",
169
+ "exportError": "Não foi possível criar a cópia de segurança.",
170
+ "profileError": "Não foi possível carregar os perfis de loja.",
171
+ "title": "ShoppingRoute – Cópia de segurança / Partilha",
172
+ "genericMarket": "Loja"
173
+ },
174
+ "pl": {
175
+ "configTitle": "Pełna konfiguracja",
176
+ "configText": "Pobierz wszystkie ustawienia ShoppingRoute jako plik JSON lub przywróć je z kopii zapasowej.",
177
+ "downloadConfig": "↓ Pobierz kopię zapasową",
178
+ "uploadConfig": "↑ Przywróć kopię zapasową",
179
+ "marketTitle": "Udostępnij profil sklepu",
180
+ "marketText": "Pobierz sklep wraz z trasą zakupów lub zaimportuj profil sklepu z pliku JSON.",
181
+ "downloadMarket": "↓ Pobierz profil sklepu",
182
+ "uploadMarket": "↑ Importuj profil sklepu",
183
+ "loading": "Ładowanie profili sklepów …",
184
+ "noMarkets": "Brak dostępnych profili sklepów",
185
+ "backupDownloaded": "Pobrano kopię zapasową.",
186
+ "profileDownloaded": "Pobrano profil sklepu.",
187
+ "configImportStarted": "Przesłano kopię zapasową. ShoppingRoute zastosuje konfigurację i uruchomi się ponownie.",
188
+ "profileImportStarted": "Przesłano profil sklepu. ShoppingRoute go zastosuje.",
189
+ "invalidBackup": "Wybrany plik nie jest prawidłową kopią zapasową JSON.",
190
+ "invalidProfile": "Wybrany plik nie jest prawidłowym profilem sklepu ShoppingRoute.",
191
+ "noInstance": "Nie udało się ustalić instancji ShoppingRoute.",
192
+ "connectionError": "Nie udało się połączyć z ioBroker.",
193
+ "exportError": "Nie udało się utworzyć kopii zapasowej.",
194
+ "profileError": "Nie udało się załadować profili sklepów.",
195
+ "title": "ShoppingRoute – Kopia zapasowa / Udostępnianie",
196
+ "genericMarket": "Sklep"
197
+ },
198
+ "ru": {
199
+ "configTitle": "Полная конфигурация",
200
+ "configText": "Скачайте все настройки ShoppingRoute в виде файла JSON или восстановите их из резервной копии.",
201
+ "downloadConfig": "↓ Скачать резервную копию",
202
+ "uploadConfig": "↑ Восстановить резервную копию",
203
+ "marketTitle": "Поделиться профилем магазина",
204
+ "marketText": "Скачайте профиль магазина с маршрутом или импортируйте профиль из файла JSON.",
205
+ "downloadMarket": "↓ Скачать профиль магазина",
206
+ "uploadMarket": "↑ Импортировать профиль магазина",
207
+ "loading": "Загрузка профилей магазинов …",
208
+ "noMarkets": "Нет доступных профилей магазинов",
209
+ "backupDownloaded": "Резервная копия скачана.",
210
+ "profileDownloaded": "Профиль магазина скачан.",
211
+ "configImportStarted": "Резервная копия передана. ShoppingRoute применит конфигурацию и перезапустится.",
212
+ "profileImportStarted": "Профиль магазина передан. ShoppingRoute применит его.",
213
+ "invalidBackup": "Выбранный файл не является корректной резервной копией JSON.",
214
+ "invalidProfile": "Выбранный файл не является корректным профилем магазина ShoppingRoute.",
215
+ "noInstance": "Не удалось определить экземпляр ShoppingRoute.",
216
+ "connectionError": "Не удалось подключиться к ioBroker.",
217
+ "exportError": "Не удалось создать резервную копию.",
218
+ "profileError": "Не удалось загрузить профили магазинов.",
219
+ "title": "ShoppingRoute – Резервная копия / Обмен",
220
+ "genericMarket": "Магазин"
221
+ },
222
+ "uk": {
223
+ "configTitle": "Повна конфігурація",
224
+ "configText": "Завантажте всі налаштування ShoppingRoute у файлі JSON або відновіть їх із резервної копії.",
225
+ "downloadConfig": "↓ Завантажити резервну копію",
226
+ "uploadConfig": "↑ Відновити резервну копію",
227
+ "marketTitle": "Поділитися профілем магазину",
228
+ "marketText": "Завантажте профіль магазину з маршрутом або імпортуйте профіль із файлу JSON.",
229
+ "downloadMarket": "↓ Завантажити профіль магазину",
230
+ "uploadMarket": "↑ Імпортувати профіль магазину",
231
+ "loading": "Завантаження профілів магазинів …",
232
+ "noMarkets": "Немає доступних профілів магазинів",
233
+ "backupDownloaded": "Резервну копію завантажено.",
234
+ "profileDownloaded": "Профіль магазину завантажено.",
235
+ "configImportStarted": "Резервну копію передано. ShoppingRoute застосує конфігурацію та перезапуститься.",
236
+ "profileImportStarted": "Профіль магазину передано. ShoppingRoute застосує його.",
237
+ "invalidBackup": "Вибраний файл не є коректною резервною копією JSON.",
238
+ "invalidProfile": "Вибраний файл не є коректним профілем магазину ShoppingRoute.",
239
+ "noInstance": "Не вдалося визначити екземпляр ShoppingRoute.",
240
+ "connectionError": "Не вдалося підключитися до ioBroker.",
241
+ "exportError": "Не вдалося створити резервну копію.",
242
+ "profileError": "Не вдалося завантажити профілі магазинів.",
243
+ "title": "ShoppingRoute – Резервна копія / Обмін",
244
+ "genericMarket": "Магазин"
245
+ },
246
+ "zh-cn": {
247
+ "configTitle": "完整配置",
248
+ "configText": "将所有 ShoppingRoute 设置下载为 JSON 文件,或从备份文件恢复。",
249
+ "downloadConfig": "↓ 下载备份",
250
+ "uploadConfig": "↑ 恢复备份",
251
+ "marketTitle": "分享商店配置",
252
+ "marketText": "下载包含购物路线的商店配置,或从 JSON 文件导入商店配置。",
253
+ "downloadMarket": "↓ 下载商店配置",
254
+ "uploadMarket": "↑ 导入商店配置",
255
+ "loading": "正在加载商店配置 …",
256
+ "noMarkets": "没有可用的商店配置",
257
+ "backupDownloaded": "备份已下载。",
258
+ "profileDownloaded": "商店配置已下载。",
259
+ "configImportStarted": "备份已提交。ShoppingRoute 将应用配置并重新启动。",
260
+ "profileImportStarted": "商店配置已提交,ShoppingRoute 将应用该配置。",
261
+ "invalidBackup": "所选文件不是有效的 JSON 备份。",
262
+ "invalidProfile": "所选文件不是有效的 ShoppingRoute 商店配置。",
263
+ "noInstance": "无法确定 ShoppingRoute 实例。",
264
+ "connectionError": "无法连接到 ioBroker。",
265
+ "exportError": "无法创建备份。",
266
+ "profileError": "无法加载商店配置。",
267
+ "title": "ShoppingRoute – 备份 / 分享",
268
+ "genericMarket": "商店"
269
+ }
270
+ };
271
+
272
+ function resolveLanguage(value) {
273
+ const locale = String(value || '').trim().toLowerCase().replace(/_/g, '-');
274
+ if (Object.prototype.hasOwnProperty.call(translations, locale)) return locale;
275
+ const base = locale.split('-')[0];
276
+ if (base === 'zh') return 'zh-cn';
277
+ return Object.prototype.hasOwnProperty.call(translations, base) ? base : 'en';
278
+ }
279
+
280
+ window.ShoppingRouteBackupI18n = { translations, resolveLanguage };
281
+ })();
@@ -1,9 +1,9 @@
1
1
  <!doctype html>
2
- <html lang="de">
2
+ <html lang="en">
3
3
  <head>
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width,initial-scale=1">
6
- <title>ShoppingRoute – Sicherung / Teilen</title>
6
+ <title>ShoppingRoute – Backup / Share</title>
7
7
 
8
8
  <style>
9
9
  :root {
@@ -124,36 +124,37 @@
124
124
  <input id="marketFile" type="file" accept=".json,application/json">
125
125
 
126
126
  <div class="section">
127
- <h2 id="configTitle">Komplette Konfiguration</h2>
127
+ <h2 id="configTitle">Complete configuration</h2>
128
128
  <p id="configText">
129
- Alle ShoppingRoute-Einstellungen direkt als JSON-Datei sichern oder aus einer Sicherungsdatei wiederherstellen.
129
+ Download all ShoppingRoute settings directly as a JSON file or restore them from a backup file.
130
130
  </p>
131
131
 
132
132
  <div class="row">
133
- <button id="downloadConfig" type="button">↓ Sicherung herunterladen</button>
134
- <button id="uploadConfig" type="button">↑ Sicherung wiederherstellen</button>
133
+ <button id="downloadConfig" type="button">↓ Download backup</button>
134
+ <button id="uploadConfig" type="button">↑ Restore backup</button>
135
135
  </div>
136
136
  </div>
137
137
 
138
138
  <div class="section">
139
- <h2 id="marketTitle">Marktprofil teilen</h2>
139
+ <h2 id="marketTitle">Share market profile</h2>
140
140
  <p id="marketText">
141
- Einen einzelnen Markt mit seinem Laufweg herunterladen oder ein Marktprofil aus einer JSON-Datei importieren.
141
+ Download one market with its walking route or import a market profile from a JSON file.
142
142
  </p>
143
143
 
144
144
  <div class="row">
145
145
  <select id="marketSelect" disabled>
146
- <option value="">Marktprofile werden geladen …</option>
146
+ <option value="">Loading market profiles …</option>
147
147
  </select>
148
148
 
149
- <button id="downloadMarket" type="button" disabled>↓ Marktprofil herunterladen</button>
150
- <button id="uploadMarket" type="button">↑ Marktprofil importieren</button>
149
+ <button id="downloadMarket" type="button" disabled>↓ Download market profile</button>
150
+ <button id="uploadMarket" type="button">↑ Import market profile</button>
151
151
  </div>
152
152
  </div>
153
153
 
154
154
  <div id="status"></div>
155
155
 
156
156
  <script src="../../lib/js/socket.io.js"></script>
157
+ <script src="backup-i18n.js"></script>
157
158
 
158
159
  <script>
159
160
  'use strict';
@@ -168,51 +169,12 @@
168
169
 
169
170
  let marketProfiles = [];
170
171
 
171
- const de = String(navigator.language || '').toLowerCase().startsWith('de');
172
-
173
- const texts = de ? {
174
- configTitle: 'Komplette Konfiguration',
175
- configText: 'Alle ShoppingRoute-Einstellungen direkt als JSON-Datei sichern oder aus einer Sicherungsdatei wiederherstellen.',
176
- downloadConfig: '↓ Sicherung herunterladen',
177
- uploadConfig: '↑ Sicherung wiederherstellen',
178
- marketTitle: 'Marktprofil teilen',
179
- marketText: 'Einen einzelnen Markt mit seinem Laufweg herunterladen oder ein Marktprofil aus einer JSON-Datei importieren.',
180
- downloadMarket: '↓ Marktprofil herunterladen',
181
- uploadMarket: '↑ Marktprofil importieren',
182
- loading: 'Marktprofile werden geladen …',
183
- noMarkets: 'Keine Marktprofile vorhanden',
184
- backupDownloaded: 'Sicherung wurde heruntergeladen.',
185
- profileDownloaded: 'Marktprofil wurde heruntergeladen.',
186
- configImportStarted: 'Sicherung wurde übergeben. ShoppingRoute übernimmt die Konfiguration und startet anschließend neu.',
187
- profileImportStarted: 'Marktprofil wurde übergeben und wird von ShoppingRoute übernommen.',
188
- invalidBackup: 'Die gewählte Datei ist keine gültige JSON-Sicherung.',
189
- invalidProfile: 'Die gewählte Datei ist kein gültiges ShoppingRoute-Marktprofil.',
190
- noInstance: 'ShoppingRoute-Instanz konnte nicht ermittelt werden.',
191
- connectionError: 'Verbindung zu ioBroker konnte nicht hergestellt werden.',
192
- exportError: 'Sicherung konnte nicht erstellt werden.',
193
- profileError: 'Marktprofile konnten nicht geladen werden.'
194
- } : {
195
- configTitle: 'Complete configuration',
196
- configText: 'Download all ShoppingRoute settings directly as a JSON file or restore them from a backup file.',
197
- downloadConfig: '↓ Download backup',
198
- uploadConfig: '↑ Restore backup',
199
- marketTitle: 'Share market profile',
200
- marketText: 'Download one market with its walking route or import a market profile from a JSON file.',
201
- downloadMarket: '↓ Download market profile',
202
- uploadMarket: '↑ Import market profile',
203
- loading: 'Loading market profiles …',
204
- noMarkets: 'No market profiles available',
205
- backupDownloaded: 'Backup downloaded.',
206
- profileDownloaded: 'Market profile downloaded.',
207
- configImportStarted: 'Backup submitted. ShoppingRoute will apply the configuration and restart afterwards.',
208
- profileImportStarted: 'Market profile submitted and will be applied by ShoppingRoute.',
209
- invalidBackup: 'The selected file is not a valid JSON backup.',
210
- invalidProfile: 'The selected file is not a valid ShoppingRoute market profile.',
211
- noInstance: 'ShoppingRoute instance could not be determined.',
212
- connectionError: 'Could not connect to ioBroker.',
213
- exportError: 'Backup could not be created.',
214
- profileError: 'Market profiles could not be loaded.'
215
- };
172
+ const { translations, resolveLanguage } = window.ShoppingRouteBackupI18n;
173
+ const language = resolveLanguage(params.get('lang') || navigator.language);
174
+ const texts = translations[language];
175
+ document.documentElement.lang = language;
176
+ document.title = texts.title;
177
+ marketSelect.options[0].textContent = texts.loading;
216
178
 
217
179
  for (const id of [
218
180
  'configTitle', 'configText', 'downloadConfig', 'uploadConfig',
@@ -268,7 +230,7 @@
268
230
  ) {
269
231
  reject(
270
232
  new Error(
271
- 'ioBroker Socket-Bibliothek ist nicht verfügbar'
233
+ 'ioBroker socket library is not available'
272
234
  )
273
235
  );
274
236
  return;
@@ -295,7 +257,7 @@
295
257
  reject(
296
258
  error instanceof Error
297
259
  ? error
298
- : new Error(String(error || 'Socket-Verbindungsfehler'))
260
+ : new Error(String(error || 'Socket connection error'))
299
261
  );
300
262
  };
301
263
 
@@ -376,7 +338,7 @@
376
338
  function getState(id) {
377
339
  return new Promise((resolve, reject) => {
378
340
  if (!socket) {
379
- reject(new Error('Keine ioBroker-Verbindung'));
341
+ reject(new Error('No ioBroker connection'));
380
342
  return;
381
343
  }
382
344
 
@@ -398,7 +360,7 @@
398
360
  function setState(id, val) {
399
361
  return new Promise((resolve, reject) => {
400
362
  if (!socket) {
401
- reject(new Error('Keine ioBroker-Verbindung'));
363
+ reject(new Error('No ioBroker connection'));
402
364
  return;
403
365
  }
404
366
 
@@ -426,10 +388,10 @@
426
388
  }
427
389
 
428
390
  function safeFilename(value) {
429
- return String(value || 'markt')
391
+ return String(value || 'market')
430
392
  .trim()
431
393
  .replace(/[^a-zA-Z0-9._-]+/g, '-')
432
- .replace(/^-+|-+$/g, '') || 'markt';
394
+ .replace(/^-+|-+$/g, '') || 'market';
433
395
  }
434
396
 
435
397
  function datePart() {
@@ -517,7 +479,7 @@
517
479
  String(a && a.market && a.market.name || '')
518
480
  .localeCompare(
519
481
  String(b && b.market && b.market.name || ''),
520
- 'de',
482
+ language,
521
483
  { sensitivity: 'base' }
522
484
  )
523
485
  );
@@ -540,7 +502,7 @@
540
502
 
541
503
  option.value = String(index);
542
504
  option.textContent =
543
- String(profile && profile.market && profile.market.name || 'Markt');
505
+ String(profile && profile.market && profile.market.name || texts.genericMarket);
544
506
 
545
507
  marketSelect.appendChild(option);
546
508
  }
@@ -649,7 +611,7 @@
649
611
  }
650
612
 
651
613
  downloadText(
652
- 'shoppingroute-marktprofil-' +
614
+ 'shoppingroute-market-profile-' +
653
615
  safeFilename(profile.market.name) +
654
616
  '-' +
655
617
  datePart() +
@@ -29,19 +29,13 @@
29
29
  "ui.items.liststab.items.lists.items.1.title": "Alexa-Liste",
30
30
  "ui.items.liststab.items.lists.items.2.title": "Standardmarkt dieser Liste",
31
31
  "ui.items.marketstab.label": "Märkte",
32
- "ui.items.marketstab.items.marketshelp.text": "Die Marktreihenfolge ist die oberste Sortierebene. Aliase werden kommagetrennt angegeben. Häufige Varianten für ALDI, LIDL, REWE und PENNY werden zusätzlich automatisch erkannt.",
33
- "ui.items.marketstab.items.markets.label": "Märkte / Hauptkategorien",
34
32
  "ui.items.marketstab.items.markets.items.1.title": "Reihenfolge",
35
33
  "ui.items.marketstab.items.markets.items.2.title": "Markt",
36
34
  "ui.items.marketstab.items.markets.items.3.title": "Aliase",
37
35
  "ui.items.productgroupstab.label": "Produktgruppen",
38
- "ui.items.productgroupstab.items.productgroupshelp.text": "Zentrale Liste für alle Produktgruppen-Pulldowns. Die Pulldown-Werte werden alphabetisch sortiert.",
39
36
  "ui.items.productgroupstab.items.productgroups.items.0.title": "Produktgruppe",
40
37
  "ui.items.routestab.label": "Laufwege",
41
- "ui.items.routestab.items.routemarketfilter.label": "Markt auswählen",
42
- "ui.items.routestab.items.routemarketfilter.help": "Einen aktiven Markt auswählen. Darunter wird ausschließlich der Laufweg dieses Marktes angezeigt.",
43
38
  "ui.items.routestab.items.routeshelp.text": "Oben einen Markt auswählen und anschließend dessen Produktgruppen mit ↑/↓ in die tatsächliche Laufreihenfolge bringen. Die vollständige Laufwegliste bleibt intern erhalten und wird automatisch aktualisiert.",
44
- "ui.items.routestab.items.routeeditorrows.label": "Laufweg des ausgewählten Marktes",
45
39
  "ui.items.productstab.label": "Artikel",
46
40
  "ui.items.productstab.items.productshelp.text": "Über die Spaltenköpfe Artikel, Produktgruppe oder Standardmarkt sortieren. Der Artikelstamm wird grundsätzlich alphabetisch gespeichert, dadurch bleiben gleiche Gruppen/Märkte innerhalb der Sortierung alphabetisch. Verfügbare Märkte werden kommagetrennt angegeben; der Standardmarkt ist die bevorzugte Ausnahme von globalen/Listen-Prioritäten.",
47
41
  "ui.items.productstab.items.products.label": "Artikelstamm",
@@ -29,19 +29,13 @@
29
29
  "ui.items.liststab.items.lists.items.1.title": "Alexa list",
30
30
  "ui.items.liststab.items.lists.items.2.title": "Default market for this list",
31
31
  "ui.items.marketstab.label": "Markets",
32
- "ui.items.marketstab.items.marketshelp.text": "Market order is the top sorting level. Aliases are comma-separated. Common variants for ALDI, LIDL, REWE and PENNY are additionally recognized automatically.",
33
- "ui.items.marketstab.items.markets.label": "Markets",
34
32
  "ui.items.marketstab.items.markets.items.1.title": "Order",
35
33
  "ui.items.marketstab.items.markets.items.2.title": "Market",
36
34
  "ui.items.marketstab.items.markets.items.3.title": "Aliases",
37
35
  "ui.items.productgroupstab.label": "Product groups",
38
- "ui.items.productgroupstab.items.productgroupshelp.text": "Central list for all product-group drop-downs. Drop-down values are alphabetically sorted.",
39
36
  "ui.items.productgroupstab.items.productgroups.items.0.title": "Product group",
40
37
  "ui.items.routestab.label": "Walking routes",
41
- "ui.items.routestab.items.routemarketfilter.label": "Select market",
42
- "ui.items.routestab.items.routemarketfilter.help": "Select one active market. Only this market’s walking route is shown below.",
43
38
  "ui.items.routestab.items.routeshelp.text": "Choose a market above, then move its product groups with ↑/↓ into the actual walking order. The complete route list remains stored internally and is updated automatically.",
44
- "ui.items.routestab.items.routeeditorrows.label": "Walking route for selected market",
45
39
  "ui.items.productstab.label": "Products",
46
40
  "ui.items.productstab.items.productshelp.text": "Click Product, Product group or Default market headers to sort. The catalogue itself is stored alphabetically, so equal groups/markets stay alphabetic. Available markets are comma-separated; the default market is the preferred exception to global/list priorities.",
47
41
  "ui.items.productstab.items.products.label": "Product catalogue",
@@ -29,19 +29,13 @@
29
29
  "ui.items.liststab.items.lists.items.1.title": "Lista de Alexa",
30
30
  "ui.items.liststab.items.lists.items.2.title": "Tienda predeterminada para esta lista",
31
31
  "ui.items.marketstab.label": "Tiendas",
32
- "ui.items.marketstab.items.marketshelp.text": "El orden de las tiendas es el nivel superior de clasificación. Los alias se separan por comas. Las variantes comunes de ALDI, LIDL, REWE y PENNY también se reconocen automáticamente.",
33
- "ui.items.marketstab.items.markets.label": "Tiendas / categorías principales",
34
32
  "ui.items.marketstab.items.markets.items.1.title": "Orden",
35
33
  "ui.items.marketstab.items.markets.items.2.title": "Tienda",
36
34
  "ui.items.marketstab.items.markets.items.3.title": "Alias",
37
35
  "ui.items.productgroupstab.label": "Grupos de productos",
38
- "ui.items.productgroupstab.items.productgroupshelp.text": "Lista central para todos los menús desplegables de grupos de productos. Los valores de los menús se ordenan alfabéticamente.",
39
36
  "ui.items.productgroupstab.items.productgroups.items.0.title": "Grupo de productos",
40
37
  "ui.items.routestab.label": "Recorridos por la tienda",
41
- "ui.items.routestab.items.routemarketfilter.label": "Seleccionar tienda",
42
- "ui.items.routestab.items.routemarketfilter.help": "Seleccione una tienda activa. Debajo se mostrará únicamente el recorrido de esa tienda.",
43
38
  "ui.items.routestab.items.routeshelp.text": "Seleccione una tienda arriba y después ordene sus grupos de productos con ↑/↓ según el recorrido real. La lista completa de recorridos permanece guardada internamente y se actualiza automáticamente.",
44
- "ui.items.routestab.items.routeeditorrows.label": "Recorrido de la tienda seleccionada",
45
39
  "ui.items.productstab.label": "Productos",
46
40
  "ui.items.productstab.items.productshelp.text": "Haga clic en los encabezados Producto, Grupo de productos o Tienda predeterminada para ordenar. El catálogo se guarda alfabéticamente, por lo que los grupos y tiendas iguales permanecen en orden alfabético. Las tiendas disponibles se separan por comas; la tienda predeterminada es la excepción preferida frente a las prioridades globales y de lista.",
47
41
  "ui.items.productstab.items.products.label": "Catálogo de productos",
@@ -29,19 +29,13 @@
29
29
  "ui.items.liststab.items.lists.items.1.title": "Liste Alexa",
30
30
  "ui.items.liststab.items.lists.items.2.title": "Magasin par défaut pour cette liste",
31
31
  "ui.items.marketstab.label": "Magasins",
32
- "ui.items.marketstab.items.marketshelp.text": "L’ordre des magasins constitue le niveau de tri supérieur. Les alias sont séparés par des virgules. Les variantes courantes d’ALDI, LIDL, REWE et PENNY sont également reconnues automatiquement.",
33
- "ui.items.marketstab.items.markets.label": "Magasins / catégories principales",
34
32
  "ui.items.marketstab.items.markets.items.1.title": "Ordre",
35
33
  "ui.items.marketstab.items.markets.items.2.title": "Magasin",
36
34
  "ui.items.marketstab.items.markets.items.3.title": "Alias",
37
35
  "ui.items.productgroupstab.label": "Groupes de produits",
38
- "ui.items.productgroupstab.items.productgroupshelp.text": "Liste centrale pour tous les menus déroulants de groupes de produits. Les valeurs des menus sont triées par ordre alphabétique.",
39
36
  "ui.items.productgroupstab.items.productgroups.items.0.title": "Groupe de produits",
40
37
  "ui.items.routestab.label": "Parcours en magasin",
41
- "ui.items.routestab.items.routemarketfilter.label": "Sélectionner un magasin",
42
- "ui.items.routestab.items.routemarketfilter.help": "Sélectionnez un magasin actif. Seul le parcours de ce magasin est affiché ci-dessous.",
43
38
  "ui.items.routestab.items.routeshelp.text": "Choisissez un magasin ci-dessus, puis placez ses groupes de produits avec ↑/↓ dans l’ordre réel du parcours. La liste complète des parcours reste enregistrée en interne et est mise à jour automatiquement.",
44
- "ui.items.routestab.items.routeeditorrows.label": "Parcours du magasin sélectionné",
45
39
  "ui.items.productstab.label": "Produits",
46
40
  "ui.items.productstab.items.productshelp.text": "Cliquez sur les en-têtes Produit, Groupe de produits ou Magasin par défaut pour trier. Le catalogue lui-même est stocké par ordre alphabétique, de sorte que les groupes et magasins identiques restent alphabétiques. Les magasins disponibles sont séparés par des virgules ; le magasin par défaut constitue l’exception prioritaire aux priorités globales et de liste.",
47
41
  "ui.items.productstab.items.products.label": "Catalogue de produits",
@@ -29,19 +29,13 @@
29
29
  "ui.items.liststab.items.lists.items.1.title": "Lista Alexa",
30
30
  "ui.items.liststab.items.lists.items.2.title": "Negozio predefinito per questa lista",
31
31
  "ui.items.marketstab.label": "Negozi",
32
- "ui.items.marketstab.items.marketshelp.text": "L’ordine dei negozi è il livello di ordinamento più alto. Gli alias sono separati da virgole. Le varianti comuni di ALDI, LIDL, REWE e PENNY vengono inoltre riconosciute automaticamente.",
33
- "ui.items.marketstab.items.markets.label": "Negozi / categorie principali",
34
32
  "ui.items.marketstab.items.markets.items.1.title": "Ordine",
35
33
  "ui.items.marketstab.items.markets.items.2.title": "Negozio",
36
34
  "ui.items.marketstab.items.markets.items.3.title": "Alias",
37
35
  "ui.items.productgroupstab.label": "Gruppi di prodotti",
38
- "ui.items.productgroupstab.items.productgroupshelp.text": "Elenco centrale per tutti i menu a discesa dei gruppi di prodotti. I valori dei menu vengono ordinati alfabeticamente.",
39
36
  "ui.items.productgroupstab.items.productgroups.items.0.title": "Gruppo di prodotti",
40
37
  "ui.items.routestab.label": "Percorsi nel negozio",
41
- "ui.items.routestab.items.routemarketfilter.label": "Seleziona negozio",
42
- "ui.items.routestab.items.routemarketfilter.help": "Seleziona un negozio attivo. Sotto viene mostrato solo il percorso di questo negozio.",
43
38
  "ui.items.routestab.items.routeshelp.text": "Seleziona un negozio sopra, quindi sposta i suoi gruppi di prodotti con ↑/↓ nell’ordine reale del percorso. L’elenco completo dei percorsi resta memorizzato internamente e viene aggiornato automaticamente.",
44
- "ui.items.routestab.items.routeeditorrows.label": "Percorso del negozio selezionato",
45
39
  "ui.items.productstab.label": "Prodotti",
46
40
  "ui.items.productstab.items.productshelp.text": "Fai clic sulle intestazioni Prodotto, Gruppo di prodotti o Negozio predefinito per ordinare. Il catalogo viene memorizzato in ordine alfabetico, quindi gruppi e negozi uguali restano alfabetici. I negozi disponibili sono separati da virgole; il negozio predefinito è l’eccezione preferita alle priorità globali e della lista.",
47
41
  "ui.items.productstab.items.products.label": "Catalogo prodotti",
@@ -29,19 +29,13 @@
29
29
  "ui.items.liststab.items.lists.items.1.title": "Alexa-lijst",
30
30
  "ui.items.liststab.items.lists.items.2.title": "Standaardwinkel voor deze lijst",
31
31
  "ui.items.marketstab.label": "Winkels",
32
- "ui.items.marketstab.items.marketshelp.text": "De winkelvolgorde is het hoogste sorteerniveau. Aliassen worden door komma's gescheiden. Veelvoorkomende varianten van ALDI, LIDL, REWE en PENNY worden daarnaast automatisch herkend.",
33
- "ui.items.marketstab.items.markets.label": "Winkels / hoofdcategorieën",
34
32
  "ui.items.marketstab.items.markets.items.1.title": "Volgorde",
35
33
  "ui.items.marketstab.items.markets.items.2.title": "Winkel",
36
34
  "ui.items.marketstab.items.markets.items.3.title": "Aliassen",
37
35
  "ui.items.productgroupstab.label": "Productgroepen",
38
- "ui.items.productgroupstab.items.productgroupshelp.text": "Centrale lijst voor alle keuzelijsten met productgroepen. De waarden in de keuzelijsten worden alfabetisch gesorteerd.",
39
36
  "ui.items.productgroupstab.items.productgroups.items.0.title": "Productgroep",
40
37
  "ui.items.routestab.label": "Looproutes",
41
- "ui.items.routestab.items.routemarketfilter.label": "Winkel selecteren",
42
- "ui.items.routestab.items.routemarketfilter.help": "Selecteer één actieve winkel. Hieronder wordt alleen de looproute van deze winkel weergegeven.",
43
38
  "ui.items.routestab.items.routeshelp.text": "Kies hierboven een winkel en zet daarna de productgroepen met ↑/↓ in de werkelijke loopvolgorde. De volledige routelijst blijft intern opgeslagen en wordt automatisch bijgewerkt.",
44
- "ui.items.routestab.items.routeeditorrows.label": "Looproute voor geselecteerde winkel",
45
39
  "ui.items.productstab.label": "Producten",
46
40
  "ui.items.productstab.items.productshelp.text": "Klik op de kolomkoppen Product, Productgroep of Standaardwinkel om te sorteren. De catalogus zelf wordt alfabetisch opgeslagen, zodat gelijke groepen en winkels alfabetisch blijven. Beschikbare winkels worden door komma's gescheiden; de standaardwinkel is de voorkeursuitzondering op globale en lijstprioriteiten.",
47
41
  "ui.items.productstab.items.products.label": "Productcatalogus",
@@ -29,19 +29,13 @@
29
29
  "ui.items.liststab.items.lists.items.1.title": "Lista Alexa",
30
30
  "ui.items.liststab.items.lists.items.2.title": "Domyślny sklep dla tej listy",
31
31
  "ui.items.marketstab.label": "Sklepy",
32
- "ui.items.marketstab.items.marketshelp.text": "Kolejność sklepów jest najwyższym poziomem sortowania. Aliasy są rozdzielane przecinkami. Typowe warianty ALDI, LIDL, REWE i PENNY są dodatkowo rozpoznawane automatycznie.",
33
- "ui.items.marketstab.items.markets.label": "Sklepy / kategorie główne",
34
32
  "ui.items.marketstab.items.markets.items.1.title": "Kolejność",
35
33
  "ui.items.marketstab.items.markets.items.2.title": "Sklep",
36
34
  "ui.items.marketstab.items.markets.items.3.title": "Aliasy",
37
35
  "ui.items.productgroupstab.label": "Grupy produktów",
38
- "ui.items.productgroupstab.items.productgroupshelp.text": "Centralna lista dla wszystkich list rozwijanych grup produktów. Wartości na listach są sortowane alfabetycznie.",
39
36
  "ui.items.productgroupstab.items.productgroups.items.0.title": "Grupa produktów",
40
37
  "ui.items.routestab.label": "Trasy przejścia",
41
- "ui.items.routestab.items.routemarketfilter.label": "Wybierz sklep",
42
- "ui.items.routestab.items.routemarketfilter.help": "Wybierz jeden aktywny sklep. Poniżej zostanie wyświetlona tylko trasa dla tego sklepu.",
43
38
  "ui.items.routestab.items.routeshelp.text": "Wybierz sklep powyżej, a następnie ustaw jego grupy produktów za pomocą ↑/↓ w rzeczywistej kolejności przejścia. Pełna lista tras pozostaje zapisana wewnętrznie i jest aktualizowana automatycznie.",
44
- "ui.items.routestab.items.routeeditorrows.label": "Trasa dla wybranego sklepu",
45
39
  "ui.items.productstab.label": "Produkty",
46
40
  "ui.items.productstab.items.productshelp.text": "Kliknij nagłówki Produkt, Grupa produktów lub Domyślny sklep, aby sortować. Sam katalog jest przechowywany alfabetycznie, dlatego identyczne grupy i sklepy pozostają ułożone alfabetycznie. Dostępne sklepy są rozdzielane przecinkami; domyślny sklep jest preferowanym wyjątkiem od priorytetów globalnych i listy.",
47
41
  "ui.items.productstab.items.products.label": "Katalog produktów",
@@ -29,19 +29,13 @@
29
29
  "ui.items.liststab.items.lists.items.1.title": "Lista Alexa",
30
30
  "ui.items.liststab.items.lists.items.2.title": "Mercado padrão desta lista",
31
31
  "ui.items.marketstab.label": "Mercados",
32
- "ui.items.marketstab.items.marketshelp.text": "A ordem dos mercados é o nível mais alto de classificação. Os aliases são separados por vírgulas. Variações comuns de ALDI, LIDL, REWE e PENNY também são reconhecidas automaticamente.",
33
- "ui.items.marketstab.items.markets.label": "Mercados / categorias principais",
34
32
  "ui.items.marketstab.items.markets.items.1.title": "Ordem",
35
33
  "ui.items.marketstab.items.markets.items.2.title": "Mercado",
36
34
  "ui.items.marketstab.items.markets.items.3.title": "Nomes alternativos",
37
35
  "ui.items.productgroupstab.label": "Grupos de produtos",
38
- "ui.items.productgroupstab.items.productgroupshelp.text": "Lista central para todos os menus de grupos de produtos. Os valores dos menus são ordenados alfabeticamente.",
39
36
  "ui.items.productgroupstab.items.productgroups.items.0.title": "Grupo de produtos",
40
37
  "ui.items.routestab.label": "Percursos na loja",
41
- "ui.items.routestab.items.routemarketfilter.label": "Selecionar mercado",
42
- "ui.items.routestab.items.routemarketfilter.help": "Selecione um mercado ativo. Abaixo será mostrado apenas o percurso desse mercado.",
43
38
  "ui.items.routestab.items.routeshelp.text": "Selecione um mercado acima e depois mova seus grupos de produtos com ↑/↓ para a ordem real do percurso. A lista completa de percursos permanece armazenada internamente e é atualizada automaticamente.",
44
- "ui.items.routestab.items.routeeditorrows.label": "Percurso do mercado selecionado",
45
39
  "ui.items.productstab.label": "Produtos",
46
40
  "ui.items.productstab.items.productshelp.text": "Clique nos cabeçalhos Produto, Grupo de produtos ou Mercado padrão para ordenar. O próprio catálogo é armazenado em ordem alfabética, portanto grupos e mercados iguais permanecem em ordem alfabética. Os mercados disponíveis são separados por vírgulas; o mercado padrão é a exceção preferida às prioridades globais e da lista.",
47
41
  "ui.items.productstab.items.products.label": "Catálogo de produtos",
@@ -29,19 +29,13 @@
29
29
  "ui.items.liststab.items.lists.items.1.title": "Список Alexa",
30
30
  "ui.items.liststab.items.lists.items.2.title": "Магазин по умолчанию для этого списка",
31
31
  "ui.items.marketstab.label": "Магазины",
32
- "ui.items.marketstab.items.marketshelp.text": "Порядок магазинов — верхний уровень сортировки. Псевдонимы указываются через запятую. Распространённые варианты ALDI, LIDL, REWE и PENNY также распознаются автоматически.",
33
- "ui.items.marketstab.items.markets.label": "Магазины / основные категории",
34
32
  "ui.items.marketstab.items.markets.items.1.title": "Порядок",
35
33
  "ui.items.marketstab.items.markets.items.2.title": "Магазин",
36
34
  "ui.items.marketstab.items.markets.items.3.title": "Псевдонимы",
37
35
  "ui.items.productgroupstab.label": "Группы товаров",
38
- "ui.items.productgroupstab.items.productgroupshelp.text": "Центральный список для всех выпадающих списков групп товаров. Значения в выпадающих списках сортируются по алфавиту.",
39
36
  "ui.items.productgroupstab.items.productgroups.items.0.title": "Группа товаров",
40
37
  "ui.items.routestab.label": "Маршруты по магазину",
41
- "ui.items.routestab.items.routemarketfilter.label": "Выбрать магазин",
42
- "ui.items.routestab.items.routemarketfilter.help": "Выберите один активный магазин. Ниже будет показан только маршрут по этому магазину.",
43
38
  "ui.items.routestab.items.routeshelp.text": "Выберите магазин выше, затем с помощью ↑/↓ расположите его группы товаров в фактическом порядке обхода. Полный список маршрутов остаётся сохранённым внутри и обновляется автоматически.",
44
- "ui.items.routestab.items.routeeditorrows.label": "Маршрут для выбранного магазина",
45
39
  "ui.items.productstab.label": "Товары",
46
40
  "ui.items.productstab.items.productshelp.text": "Нажмите заголовки «Товар», «Группа товаров» или «Магазин по умолчанию» для сортировки. Сам каталог хранится по алфавиту, поэтому одинаковые группы и магазины также остаются отсортированными по алфавиту. Доступные магазины указываются через запятую; магазин по умолчанию является предпочтительным исключением из глобальных и списочных приоритетов.",
47
41
  "ui.items.productstab.items.products.label": "Каталог товаров",
@@ -29,19 +29,13 @@
29
29
  "ui.items.liststab.items.lists.items.1.title": "Список Alexa",
30
30
  "ui.items.liststab.items.lists.items.2.title": "Магазин за замовчуванням для цього списку",
31
31
  "ui.items.marketstab.label": "Магазини",
32
- "ui.items.marketstab.items.marketshelp.text": "Порядок магазинів — верхній рівень сортування. Псевдоніми вказуються через кому. Поширені варіанти ALDI, LIDL, REWE та PENNY також розпізнаються автоматично.",
33
- "ui.items.marketstab.items.markets.label": "Магазини / основні категорії",
34
32
  "ui.items.marketstab.items.markets.items.1.title": "Порядок",
35
33
  "ui.items.marketstab.items.markets.items.2.title": "Магазин",
36
34
  "ui.items.marketstab.items.markets.items.3.title": "Псевдоніми",
37
35
  "ui.items.productgroupstab.label": "Групи товарів",
38
- "ui.items.productgroupstab.items.productgroupshelp.text": "Центральний список для всіх випадаючих списків груп товарів. Значення у випадаючих списках сортуються за абеткою.",
39
36
  "ui.items.productgroupstab.items.productgroups.items.0.title": "Група товарів",
40
37
  "ui.items.routestab.label": "Маршрути по магазину",
41
- "ui.items.routestab.items.routemarketfilter.label": "Вибрати магазин",
42
- "ui.items.routestab.items.routemarketfilter.help": "Виберіть один активний магазин. Нижче буде показано лише маршрут по цьому магазину.",
43
38
  "ui.items.routestab.items.routeshelp.text": "Виберіть магазин вище, а потім за допомогою ↑/↓ розташуйте його групи товарів у фактичному порядку проходження. Повний список маршрутів зберігається всередині та оновлюється автоматично.",
44
- "ui.items.routestab.items.routeeditorrows.label": "Маршрут для вибраного магазину",
45
39
  "ui.items.productstab.label": "Товари",
46
40
  "ui.items.productstab.items.productshelp.text": "Натисніть заголовки «Товар», «Група товарів» або «Магазин за замовчуванням» для сортування. Сам каталог зберігається за абеткою, тому однакові групи й магазини також залишаються впорядкованими за абеткою. Доступні магазини вказуються через кому; магазин за замовчуванням є пріоритетним винятком із глобальних і спискових пріоритетів.",
47
41
  "ui.items.productstab.items.products.label": "Каталог товарів",
@@ -29,19 +29,13 @@
29
29
  "ui.items.liststab.items.lists.items.1.title": "Alexa 列表",
30
30
  "ui.items.liststab.items.lists.items.2.title": "此列表的默认商店",
31
31
  "ui.items.marketstab.label": "商店",
32
- "ui.items.marketstab.items.marketshelp.text": "商店顺序是最高级别的排序依据。别名以逗号分隔。ALDI、LIDL、REWE 和 PENNY 的常见变体也会自动识别。",
33
- "ui.items.marketstab.items.markets.label": "商店 / 主分类",
34
32
  "ui.items.marketstab.items.markets.items.1.title": "顺序",
35
33
  "ui.items.marketstab.items.markets.items.2.title": "商店",
36
34
  "ui.items.marketstab.items.markets.items.3.title": "别名",
37
35
  "ui.items.productgroupstab.label": "商品组",
38
- "ui.items.productgroupstab.items.productgroupshelp.text": "所有商品组下拉菜单的中央列表。下拉菜单中的值按字母顺序排序。",
39
36
  "ui.items.productgroupstab.items.productgroups.items.0.title": "商品组",
40
37
  "ui.items.routestab.label": "店内路线",
41
- "ui.items.routestab.items.routemarketfilter.label": "选择商店",
42
- "ui.items.routestab.items.routemarketfilter.help": "选择一个已启用的商店。下方只显示该商店的店内路线。",
43
38
  "ui.items.routestab.items.routeshelp.text": "在上方选择商店,然后使用 ↑/↓ 将其商品组按实际行走顺序排列。完整路线列表会保留在内部并自动更新。",
44
- "ui.items.routestab.items.routeeditorrows.label": "所选商店的店内路线",
45
39
  "ui.items.productstab.label": "商品",
46
40
  "ui.items.productstab.items.productshelp.text": "点击“商品”“商品组”或“默认商店”列标题进行排序。目录本身按字母顺序存储,因此相同的商品组和商店也会保持字母顺序。可用商店以逗号分隔;默认商店是相对于全局和列表优先级的首选例外。",
47
41
  "ui.items.productstab.items.products.label": "商品目录",
package/build/main.js CHANGED
@@ -49,7 +49,7 @@ const review_tools_1 = require("./lib/review-tools");
49
49
  const manual_order_1 = require("./lib/manual-order");
50
50
  const state_change_1 = require("./lib/state-change");
51
51
  const direct_sort_lifecycle_1 = require("./lib/direct-sort-lifecycle");
52
- const VERSION = '0.4.3';
52
+ const VERSION = '0.4.4';
53
53
  const COLLECT_WINDOW_MS = 5000;
54
54
  const MAX_ACTIVE_ITEMS = 99;
55
55
  const OWN_REFRESH_MAX_MS = 30000;
@@ -183,7 +183,7 @@ class ShoppingRoute extends utils.Adapter {
183
183
  }).sort((a, b) => a.name.localeCompare(b.name, 'de', { sensitivity: 'base' }));
184
184
  }
185
185
  get products() { return this.runtimeProducts.filter(product => product?.name); }
186
- get fallbackMarket() { return String(this.cfg.fallbackMarket || 'Ohne Markt').trim() || 'Ohne Markt'; }
186
+ get fallbackMarket() { return String(this.cfg.fallbackMarket || 'No Market').trim() || 'No Market'; }
187
187
  get priorityMarket() { return String(this.cfg.priorityMarket || '').trim(); }
188
188
  priorityMarketForList(listName) {
189
189
  if (this.temporaryPriorityMarket)
package/io-package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "shoppingroute",
4
- "version": "0.4.3",
4
+ "version": "0.4.4",
5
5
  "titleLang": {
6
6
  "en": "ShoppingRoute",
7
7
  "de": "ShoppingRoute",
@@ -60,6 +60,19 @@
60
60
  "icon": "shoppingroute.png",
61
61
  "tier": 3,
62
62
  "news": {
63
+ "0.4.4": {
64
+ "en": "Use No Market as the default fallback market, translate the backup utility into all 11 admin languages, and remove six unused translation keys. Existing configuration is preserved.",
65
+ "de": "No Market als Standard-Ausweichmarkt, Backup-Seite in allen 11 Admin-Sprachen und sechs ungenutzte Übersetzungsschlüssel entfernt. Bestehende Konfigurationen bleiben erhalten.",
66
+ "ru": "Резервный магазин по умолчанию: No Market. Страница резервного копирования переведена на 11 языков, удалены шесть неиспользуемых ключей перевода. Существующая конфигурация сохранена.",
67
+ "pt": "No Market como loja predefinida de recurso, página de cópia de segurança nos 11 idiomas e remoção de seis chaves de tradução não utilizadas. A configuração existente é preservada.",
68
+ "nl": "No Market als standaard terugvalwinkel, de back-uppagina in alle 11 beheertalen en zes ongebruikte vertaalsleutels verwijderd. Bestaande configuratie blijft behouden.",
69
+ "fr": "No Market comme magasin de secours par défaut, outil de sauvegarde traduit dans les 11 langues et suppression de six clés de traduction inutilisées. La configuration existante est préservée.",
70
+ "it": "No Market come negozio predefinito di riserva, pagina di backup in tutte le 11 lingue e rimozione di sei chiavi di traduzione inutilizzate. La configurazione esistente viene preservata.",
71
+ "es": "No Market como tienda predeterminada de respaldo, página de copias de seguridad en los 11 idiomas y eliminación de seis claves de traducción sin uso. Se conserva la configuración existente.",
72
+ "pl": "No Market jako domyślny sklep zastępczy, strona kopii zapasowej w 11 językach i usunięcie sześciu nieużywanych kluczy tłumaczeń. Istniejąca konfiguracja zostaje zachowana.",
73
+ "uk": "Резервний магазин за замовчуванням: No Market. Сторінку резервного копіювання перекладено 11 мовами, видалено шість невикористаних ключів перекладу. Наявну конфігурацію збережено.",
74
+ "zh-cn": "默认备用商店名称改为 No Market,备份页面支持全部 11 种管理语言,并移除六个未使用的翻译键。保留现有配置。"
75
+ },
63
76
  "0.4.3": {
64
77
  "en": "Uses ioBroker-managed timers for Alexa callback timeouts and keeps repository news metadata within the supported seven-entry limit.",
65
78
  "de": "Verwendet ioBroker-verwaltete Timer für Alexa-Callback-Timeouts und hält die Repository-News-Metadaten innerhalb des unterstützten Limits von sieben Einträgen.",
@@ -137,19 +150,6 @@
137
150
  "pl": "Dodaje interaktywną listę zakupów w Adminie z przeciąganiem/ręcznym przenoszeniem między sklepami, trwałymi ręcznymi nadpisaniami pozycji, poprawkami responsywności oraz zachowanym statusem „zaakceptowano” po zwykłym zapisie.",
138
151
  "uk": "Додано інтерактивний список покупок в Admin із перетягуванням і ручним переміщенням між магазинами, постійними ручними налаштуваннями елементів, покращеною адаптивністю та збереженням статусу «прийнято» після звичайного збереження.",
139
152
  "zh-cn": "新增 Admin 交互式购物清单,可拖放/手动在商店之间移动商品,保存逐项手动覆盖,改进响应式布局,并在普通保存后保留“已接受”状态。"
140
- },
141
- "0.3.6": {
142
- "en": "Repository checker cleanup: explicit JSON Config i18n mode and translation files, obsolete prepublish protection removed, and older changelog entries archived. Sorting and runtime behavior are unchanged.",
143
- "de": "Repository-Checker-Bereinigung: expliziter JSON-Config-i18n-Modus mit Übersetzungsdateien, veralteter Prepublish-Schutz entfernt und ältere Changelog-Einträge archiviert. Sortier- und Laufzeitverhalten bleiben unverändert.",
144
- "ru": "Очистка для проверки репозитория: явный режим i18n JSON Config и файлы переводов, удалена устаревшая защита prepublish, старые записи журнала изменений архивированы. Сортировка и поведение во время выполнения не изменены.",
145
- "pt": "Limpeza do verificador do repositório: modo i18n explícito do JSON Config e ficheiros de tradução, proteção prepublish obsoleta removida e entradas antigas do changelog arquivadas. A ordenação e o comportamento em execução não mudaram.",
146
- "nl": "Opschoning voor de repositorychecker: expliciete JSON Config-i18n-modus en vertaalbestanden, verouderde prepublish-beveiliging verwijderd en oudere changelog-items gearchiveerd. Sorteer- en runtimegedrag zijn ongewijzigd.",
147
- "fr": "Nettoyage pour le vérificateur du dépôt : mode i18n JSON Config explicite et fichiers de traduction, ancienne protection prepublish supprimée et anciennes entrées du changelog archivées. Le tri et le comportement à l’exécution sont inchangés.",
148
- "it": "Pulizia per il controllo del repository: modalità i18n JSON Config esplicita e file di traduzione, protezione prepublish obsoleta rimossa e voci meno recenti del changelog archiviate. Ordinamento e comportamento runtime invariati.",
149
- "es": "Limpieza para el verificador del repositorio: modo i18n explícito de JSON Config y archivos de traducción, protección prepublish obsoleta eliminada y entradas antiguas del changelog archivadas. La ordenación y el comportamiento en ejecución no cambiaron.",
150
- "pl": "Porządki dla kontrolera repozytorium: jawny tryb i18n JSON Config i pliki tłumaczeń, usunięta przestarzała ochrona prepublish oraz zarchiwizowane starsze wpisy changeloga. Sortowanie i działanie w czasie wykonywania pozostają bez zmian.",
151
- "uk": "Очищення для перевірки репозиторію: явний режим i18n JSON Config і файли перекладів, застарілий захист prepublish видалено, старі записи журналу змін заархівовано. Сортування й поведінка під час виконання не змінилися.",
152
- "zh-cn": "仓库检查器清理:明确启用 JSON Config i18n 模式并使用翻译文件,移除过时的 prepublish 保护,归档旧版更新日志。排序和运行时行为均未改变。"
153
153
  }
154
154
  },
155
155
  "extIcon": "https://raw.githubusercontent.com/RaviniZib/ioBroker.shoppingroute/main/admin/shoppingroute.png",
@@ -167,7 +167,7 @@
167
167
  "dryRun": true,
168
168
  "autoLearnProducts": true,
169
169
  "logSortSummary": true,
170
- "fallbackMarket": "Ohne Markt",
170
+ "fallbackMarket": "No Market",
171
171
  "priorityMarket": "",
172
172
  "markets": [
173
173
  {
@@ -195,7 +195,7 @@
195
195
  "enabled": true
196
196
  },
197
197
  {
198
- "name": "Ohne Markt",
198
+ "name": "No Market",
199
199
  "aliases": "",
200
200
  "order": 999,
201
201
  "enabled": true
@@ -503,77 +503,77 @@
503
503
  "order": 150
504
504
  },
505
505
  {
506
- "market": "Ohne Markt",
506
+ "market": "No Market",
507
507
  "category": "Obst/Gemüse",
508
508
  "order": 10
509
509
  },
510
510
  {
511
- "market": "Ohne Markt",
511
+ "market": "No Market",
512
512
  "category": "Tee/Kaffee",
513
513
  "order": 20
514
514
  },
515
515
  {
516
- "market": "Ohne Markt",
516
+ "market": "No Market",
517
517
  "category": "Brot/Gebäck",
518
518
  "order": 30
519
519
  },
520
520
  {
521
- "market": "Ohne Markt",
521
+ "market": "No Market",
522
522
  "category": "Schokolade/Naschen",
523
523
  "order": 40
524
524
  },
525
525
  {
526
- "market": "Ohne Markt",
526
+ "market": "No Market",
527
527
  "category": "Fleisch/Fisch",
528
528
  "order": 50
529
529
  },
530
530
  {
531
- "market": "Ohne Markt",
531
+ "market": "No Market",
532
532
  "category": "Wurst/Salate/Teigwaren",
533
533
  "order": 60
534
534
  },
535
535
  {
536
- "market": "Ohne Markt",
536
+ "market": "No Market",
537
537
  "category": "Milchprodukte",
538
538
  "order": 70
539
539
  },
540
540
  {
541
- "market": "Ohne Markt",
541
+ "market": "No Market",
542
542
  "category": "Haushalt/Hygiene",
543
543
  "order": 80
544
544
  },
545
545
  {
546
- "market": "Ohne Markt",
546
+ "market": "No Market",
547
547
  "category": "Nonfood",
548
548
  "order": 90
549
549
  },
550
550
  {
551
- "market": "Ohne Markt",
551
+ "market": "No Market",
552
552
  "category": "Konserven",
553
553
  "order": 100
554
554
  },
555
555
  {
556
- "market": "Ohne Markt",
556
+ "market": "No Market",
557
557
  "category": "H-Milch/Nudeln",
558
558
  "order": 110
559
559
  },
560
560
  {
561
- "market": "Ohne Markt",
561
+ "market": "No Market",
562
562
  "category": "Kosmetikartikel",
563
563
  "order": 120
564
564
  },
565
565
  {
566
- "market": "Ohne Markt",
566
+ "market": "No Market",
567
567
  "category": "Getränke",
568
568
  "order": 130
569
569
  },
570
570
  {
571
- "market": "Ohne Markt",
571
+ "market": "No Market",
572
572
  "category": "TK-Produkte",
573
573
  "order": 140
574
574
  },
575
575
  {
576
- "market": "Ohne Markt",
576
+ "market": "No Market",
577
577
  "category": "Sonstiges",
578
578
  "order": 150
579
579
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.shoppingroute",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "description": "Smart Alexa shopping-list routing by market, product group and individual store walking route with learning, multi-list support and API protection.",
5
5
  "author": "RaviniZib <zib@ravini.org>",
6
6
  "license": "MIT",