Internationalization
This commit is contained in:
+25
-25
@@ -44,7 +44,7 @@ globalThis.Wegwichtel ??= {};
|
||||
await new Promise((resolve, reject) => {
|
||||
const timeout = setTimeout(() => {
|
||||
navigator.serviceWorker.removeEventListener('controllerchange', changed);
|
||||
reject(new Error('Offline-Speicher konnte nicht aktiviert werden. Bitte erneut versuchen.'));
|
||||
reject(new Error(ns.I18n.t("offline-cache.das.speichern.fur.unterwegs.hat.nicht.geklappt")));
|
||||
}, 10000);
|
||||
function changed() {
|
||||
clearTimeout(timeout);
|
||||
@@ -64,32 +64,32 @@ globalThis.Wegwichtel ??= {};
|
||||
let stage = null;
|
||||
try {
|
||||
await worker();
|
||||
announce('Offline-Dateien werden vorbereitet …');
|
||||
announce(ns.I18n.t("offline-cache.wegwichtel.wird.fur.unterwegs.vorbereitet"));
|
||||
// Persistent storage is best-effort; a denial must not block downloads.
|
||||
await navigator.storage?.persist?.().catch(() => false);
|
||||
const options = { cache: 'no-store', headers: { 'X-Wegwichtel-Offline-Download': 'true' }, signal };
|
||||
const manifestResponse = await fetch('/api/offline-manifest', options);
|
||||
if (!manifestResponse.ok) throw new Error('Die Liste der Offline-Dateien konnte nicht geladen werden.');
|
||||
if (!manifestResponse.ok) throw new Error(ns.I18n.t("offline-cache.wegwichtel.konnte.nicht.fur.unterwegs.geladen.werden"));
|
||||
const manifest = await manifestResponse.json();
|
||||
if (!Array.isArray(manifest.resources) || !manifest.resources.length) throw new TypeError('Ungültige Offline-Dateiliste.');
|
||||
if (!Array.isArray(manifest.resources) || !manifest.resources.length) throw new TypeError(ns.I18n.t("offline-cache.wegwichtel.konnte.nicht.fur.unterwegs.geladen.werden.2"));
|
||||
stage = await ns.OfflineStore.create();
|
||||
const urls = [...new Set(manifest.resources.map(url => ns.OfflineStore.key(url)))];
|
||||
for (const [index, url] of urls.entries()) {
|
||||
signal.throwIfAborted();
|
||||
announce(`Offline-Dateien speichern: ${index + 1} von ${urls.length}`);
|
||||
announce(ns.I18n.t("offline-cache.fur.unterwegs.speichern.von", { value1: index + 1, value2: urls.length }));
|
||||
const response = await fetch(url, options);
|
||||
await ns.OfflineStore.put(stage, url, response);
|
||||
}
|
||||
signal.throwIfAborted();
|
||||
const saved = await ns.OfflineStore.commit(stage);
|
||||
announce(`${saved.count} Dateien sind offline verfügbar.`);
|
||||
announce(ns.I18n.t("offline-cache.wegwichtel.ist.fur.unterwegs.bereit"));
|
||||
return saved;
|
||||
} catch (error) {
|
||||
if (stage) await ns.OfflineStore.remove(stage.id);
|
||||
ns.StorageUsage?.reportError(error);
|
||||
const saved = await ns.OfflineStore.state();
|
||||
if (error.name === 'AbortError') announce('Download abgebrochen.');
|
||||
else announce(`${error.message} ${saved ? 'Der bisherige Offline-Stand bleibt erhalten.' : 'Die App ist noch nicht vollständig offline verfügbar.'}`);
|
||||
if (error.name === 'AbortError') announce(ns.I18n.t("offline-cache.speichern.abgebrochen"));
|
||||
else announce(`${ns.UserMessages?.error(error) || ns.I18n.t("offline-cache.das.speichern.hat.nicht.geklappt")} ${saved ? ns.I18n.t("offline.previous.kept") : ns.I18n.t("offline-cache.wegwichtel.ist.noch.nicht.ohne.internet.bereit")}`);
|
||||
throw error;
|
||||
} finally {
|
||||
task = null;
|
||||
@@ -109,7 +109,7 @@ globalThis.Wegwichtel ??= {};
|
||||
if (task) await task.catch(() => {});
|
||||
await ns.OfflineStore.clear();
|
||||
await updateControls();
|
||||
announce('App-Dateien wurden entfernt. Gespeicherte Strecken bleiben erhalten.');
|
||||
announce(ns.I18n.t("offline-cache.die.gespeicherte.app.wurde.entfernt.deine.strecken"));
|
||||
await ns.StorageUsage?.refresh();
|
||||
}
|
||||
|
||||
@@ -126,12 +126,12 @@ globalThis.Wegwichtel ??= {};
|
||||
const message = document.getElementById('route-offline-status');
|
||||
if (save) {
|
||||
save.disabled = !supported() || Boolean(routeTask);
|
||||
save.textContent = saved ? 'Offline-Strecke aktualisieren' : 'Strecke offline speichern';
|
||||
save.textContent = saved ? ns.I18n.t("offline-cache.gespeicherte.strecke.erneuern") : ns.I18n.t("offline-cache.fur.unterwegs.speichern");
|
||||
}
|
||||
if (remove) { remove.hidden = !saved; remove.disabled = Boolean(routeTask); }
|
||||
if (message && !routeTask) message.textContent = saved
|
||||
? `${saved.count} Dateien dieser Strecke sind auf dem Gerät gespeichert.`
|
||||
: 'Speichert nur diese Strecke mit GPX, Bildern und Audio für die App.';
|
||||
? ns.I18n.t("offline-cache.diese.strecke.ist.fur.unterwegs.bereit")
|
||||
: ns.I18n.t("offline-cache.nimm.diese.strecke.mit.bildern.und.tonaufnahmen");
|
||||
updateGpxDownload(route);
|
||||
}
|
||||
|
||||
@@ -147,9 +147,9 @@ globalThis.Wegwichtel ??= {};
|
||||
}
|
||||
|
||||
async function saveRoute(route) {
|
||||
if (routeTask) throw new Error('Eine Strecke wird bereits gespeichert.');
|
||||
if (!supported()) throw new Error('Offline-Speicherung benötigt HTTPS und Browser-Unterstützung.');
|
||||
if (!route || route.source === 'local') throw new Error('Bitte eine veröffentlichte Strecke auswählen.');
|
||||
if (routeTask) throw new Error(ns.I18n.t("offline-cache.bitte.warte.bis.die.andere.strecke.gespeichert"));
|
||||
if (!supported()) throw new Error(ns.I18n.t("offline-cache.auf.diesem.gerat.kannst.du.strecken.gerade"));
|
||||
if (!route || route.source === 'local') throw new Error(ns.I18n.t("offline-cache.wahle.zuerst.eine.strecke.die.fur.alle"));
|
||||
routeTask = (async () => {
|
||||
let stage = null;
|
||||
const message = document.getElementById('route-offline-status');
|
||||
@@ -159,10 +159,10 @@ globalThis.Wegwichtel ??= {};
|
||||
const url = `/api/routes/${encodeURIComponent(route.id)}`;
|
||||
const options = { cache: 'no-store', headers: { 'X-Wegwichtel-Offline-Download': 'true' } };
|
||||
const response = await fetch(url, options);
|
||||
if (!response.ok) throw new Error('Die Strecke konnte nicht geladen werden.');
|
||||
if (!response.ok) throw new Error(ns.I18n.t("offline-cache.die.strecke.konnte.nicht.geladen.werden"));
|
||||
const fresh = await response.clone().json();
|
||||
if (String(fresh.id) !== String(route.id) || !fresh.activatedAt) throw new Error('Nur veröffentlichte Strecken können offline gespeichert werden.');
|
||||
if (!fresh.gpxUrl) throw new Error('Für diese Strecke ist keine GPX-Datei vorhanden.');
|
||||
if (String(fresh.id) !== String(route.id) || !fresh.activatedAt) throw new Error(ns.I18n.t("offline-cache.du.kannst.nur.freigegebene.strecken.fur.unterwegs"));
|
||||
if (!fresh.gpxUrl) throw new Error(ns.I18n.t("offline-cache.der.weg.dieser.strecke.fehlt.sie.kann"));
|
||||
const resources = [fresh.gpxUrl];
|
||||
for (const poi of fresh.pois || []) {
|
||||
if (poi.audioUrl) resources.push(poi.audioUrl);
|
||||
@@ -172,7 +172,7 @@ globalThis.Wegwichtel ??= {};
|
||||
stage = await ns.OfflineStore.create();
|
||||
await ns.OfflineStore.put(stage, url, response);
|
||||
for (const [index, resource] of urls.entries()) {
|
||||
if (message && selectedRoute === route) message.textContent = `Strecke speichern: ${index + 1} von ${urls.length}`;
|
||||
if (message && selectedRoute === route) message.textContent = ns.I18n.t("offline-cache.strecke.speichern.von", { value1: index + 1, value2: urls.length });
|
||||
await ns.OfflineStore.put(stage, resource, await fetch(resource, options));
|
||||
await ns.StorageUsage?.refresh();
|
||||
}
|
||||
@@ -193,7 +193,7 @@ globalThis.Wegwichtel ??= {};
|
||||
}
|
||||
|
||||
async function removeRoute(route) {
|
||||
if (routeTask) throw new Error('Bitte den laufenden Download abwarten.');
|
||||
if (routeTask) throw new Error(ns.I18n.t("offline-cache.bitte.warte.bis.das.speichern.fertig.ist"));
|
||||
await ns.OfflineStore.clearRoute(route.id);
|
||||
await renderRoute(selectedRoute);
|
||||
await ns.StorageUsage?.refresh();
|
||||
@@ -206,7 +206,7 @@ globalThis.Wegwichtel ??= {};
|
||||
return action(route).catch(error => {
|
||||
if (selectedRoute !== route) return;
|
||||
const message = document.getElementById('route-offline-status');
|
||||
if (message) message.textContent = `${error.message} Ein bisheriger Offline-Stand bleibt erhalten.`;
|
||||
if (message) message.textContent = ns.I18n.t("offline.error.previous.kept", { value1: ns.UserMessages?.error(error) || ns.I18n.t("offline-cache.das.speichern.hat.nicht.geklappt") });
|
||||
});
|
||||
};
|
||||
document.getElementById('route-offline-save')?.addEventListener('click', () => run(saveRoute));
|
||||
@@ -226,7 +226,7 @@ globalThis.Wegwichtel ??= {};
|
||||
}
|
||||
if (!supported()) {
|
||||
if (toggle) toggle.disabled = true;
|
||||
announce('Offline-Speicherung ist in diesem Browser oder ohne HTTPS nicht verfügbar.');
|
||||
announce(ns.I18n.t("offline-cache.auf.diesem.gerat.lasst.sich.wegwichtel.gerade"));
|
||||
return;
|
||||
}
|
||||
// Remove old v1 snapshots that also included all routes automatically.
|
||||
@@ -236,9 +236,9 @@ globalThis.Wegwichtel ??= {};
|
||||
if ((await ns.OfflineStore.routes()).length) await worker();
|
||||
if (saved) {
|
||||
await worker();
|
||||
announce(`${saved.count} Dateien sind offline verfügbar.`);
|
||||
announce(ns.I18n.t("offline-cache.wegwichtel.ist.fur.unterwegs.bereit"));
|
||||
} else {
|
||||
announce('Speichert nur die App-Dateien auf diesem Gerät. Strecken kannst du einzeln speichern.');
|
||||
announce(ns.I18n.t("offline-cache.speichere.zuerst.die.app.danach.kannst.du"));
|
||||
}
|
||||
toggle?.addEventListener('change', () => {
|
||||
toggle.setAttribute('aria-checked', String(toggle.checked));
|
||||
@@ -249,7 +249,7 @@ globalThis.Wegwichtel ??= {};
|
||||
|
||||
ns.OfflineCache = Object.freeze({
|
||||
initialize() {
|
||||
initialized ??= setup().catch(error => { announce(error.message); });
|
||||
initialized ??= setup().catch(error => { announce(ns.UserMessages?.error(error) || ns.I18n.t("offline-cache.das.speichern.fur.unterwegs.hat.nicht.geklappt")); });
|
||||
return initialized;
|
||||
},
|
||||
download,
|
||||
|
||||
Reference in New Issue
Block a user