Internationalization
Sonarqube Scanner / Prepare environment (push) Successful in 20s
Sonarqube Scanner / Build and test (push) Failing after 36s
Sonarqube Scanner / SonarQube analysis (push) Skipped
Sonarqube Scanner / Deploy to Server (push) Successful in 9s

This commit is contained in:
Florian Zumpe
2026-10-01 09:41:01 +02:00
parent 607e5450e1
commit aff88eba1b
41 changed files with 1368 additions and 636 deletions
+49 -49
View File
@@ -58,7 +58,7 @@
if (state.routeState === 'paused') {
$('#tracking-status')
.prop('hidden', false)
.text('Gespeicherter Streckenfortschritt gefunden. Die Route kann fortgesetzt werden.');
.text(ns.I18n.t("app.du.kannst.dort.weitermachen.wo.du.aufgehort"));
} else {
$('#tracking-status').prop('hidden', true).text('');
}
@@ -82,7 +82,7 @@
function navigate(pageSelector, options = {}) {
const target = $(pageSelector);
if (!target.length) throw new Error(`Unbekannte Ansicht: ${pageSelector}`);
if (!target.length) throw new Error(ns.I18n.t("app.page.missing"));
const previous = state.activePage;
$('.app-page').attr('hidden', true);
@@ -115,7 +115,7 @@
const proximity = route.proximityM == null
? ''
: `<small>${escapeHtml(ns.Distance.format(route.proximityM))} entfernt</small>`;
const sourceLabel = source === 'local' ? '<small>Lokaler Entwurf</small>' : '';
const sourceLabel = source === 'local' ? ("<small>" + escapeHtml(ns.I18n.t("app.eigener.entwurf")) + "</small>") : '';
return `
<li>
<button type="button" data-route-select data-route-id="${escapeHtml(route.id)}" data-route-source="${source}">
@@ -157,21 +157,21 @@
function updateRouteControlSummary() {
if (!state.route) {
setRouteControlSummary('Route wählen', 'Aktive und lokale Strecken');
setRouteControlSummary(ns.I18n.t("app.strecke.wahlen"), ns.I18n.t("app.strecken.fur.alle.und.meine.entwurfe"));
return;
}
const titleText = state.route.name || 'Route';
const titleText = state.route.name || ns.I18n.t("app.strecke");
const metric = nextPoiMetric();
if (!metric) {
const detailText = (state.route.pois || []).length
? 'Alle Streckenpunkte erreicht'
: 'Keine Streckenpunkte vorhanden';
? ns.I18n.t("app.alle.stationen.erreicht")
: ns.I18n.t("app.keine.stationen.vorhanden");
setRouteControlSummary(titleText, detailText);
return;
}
const distance = metric.distance == null ? '' : ` · ${Math.round(metric.distance)} m`;
setRouteControlSummary(titleText, `Nächster Streckenpunkt: ${metric.poi.title}${distance}`);
setRouteControlSummary(titleText, ns.I18n.t("app.nachste.station", { value1: metric.poi.title, value2: distance }));
}
function renderRouteControlList(selector, routes, source, emptyText) {
@@ -187,7 +187,7 @@
<li>
<button type="button" data-route-select data-route-id="${escapeHtml(route.id)}" data-route-source="${source}" ${current ? 'aria-current="true"' : ''}>
<strong>${escapeHtml(route.name)}</strong>
<small>${source === 'local' ? 'Lokaler Entwurf' : 'Veröffentlicht'}</small>
<small>${source === 'local' ? ns.I18n.t("app.eigener.entwurf") : ns.I18n.t("app.veroffentlicht")}</small>
</button>
</li>
`);
@@ -198,22 +198,22 @@
const routeControlLists = $('.route-control-list');
routeControlLists.attr('aria-busy', 'true');
$('#route-control-server-title').text(`Aktive Strecken (${state.routes.length})`);
$('#route-control-local-title').text(`Lokale Strecken (${state.localRoutes.length})`);
$('#local-route-title').text(`Lokale Strecken (${state.localRoutes.length})`);
$('#route-control-server-title').text(ns.I18n.t("app.strecken.fur.alle", { value1: state.routes.length }));
$('#route-control-local-title').text(ns.I18n.t("app.meine.entwurfe", { value1: state.localRoutes.length }));
$('#local-route-title').text(ns.I18n.t("app.meine.entwurfe", { value1: state.localRoutes.length }));
updateRouteControlSummary();
renderRouteControlList(
'#route-control-server-list',
state.routes,
'server',
'Keine veröffentlichte Strecke vorhanden.'
ns.I18n.t("app.hier.gibt.es.noch.keine.strecken.fur")
);
renderRouteControlList(
'#route-control-local-list',
state.localRoutes,
'local',
state.localRoutesError || 'Noch keine lokale Strecke vorhanden.'
state.localRoutesError || ns.I18n.t("app.du.hast.noch.keine.eigene.strecke.angelegt")
);
routeControlLists.attr('aria-busy', 'false');
@@ -239,26 +239,26 @@
.sort((a, b) => String(a.name ?? '').localeCompare(String(b.name ?? ''), 'de'));
const nearbyEmptyText = state.position
? `Keine Route im Umkreis von ${ns.Config.routeRadiusKm} km gefunden. Wähle unten eine beliebige Route aus.`
: 'Für Empfehlungen in der Nähe ist ein Standort erforderlich. Alle Routen bleiben unten auswählbar.';
? ns.I18n.t("app.keine.strecke.im.umkreis.von.km.gefunden", { value1: ns.Config.routeRadiusKm })
: ns.I18n.t("app.erlaube.deinen.standort.um.strecken.in.deiner");
renderRouteItems('#nearby-route-list', nearbyRoutes, nearbyEmptyText);
renderRouteItems(
'#all-route-list',
allRoutes,
searchTerm ? 'Keine Route entspricht dem Suchbegriff.' : 'Es sind noch keine veröffentlichten Routen vorhanden.'
searchTerm ? ns.I18n.t("app.keine.passende.strecke.gefunden.versuche.ein.anderes") : ns.I18n.t("app.hier.gibt.es.noch.keine.strecken.fur")
);
renderRouteItems(
'#local-route-list',
localRoutes,
state.localRoutesError || 'Noch keine lokalen Strecken gespeichert.',
state.localRoutesError || ns.I18n.t("app.du.hast.noch.keine.eigene.strecke.angelegt"),
'local'
);
$('#all-routes-summary').text(
searchTerm
? `${allRoutes.length} von ${routes.length} veröffentlichten Routen gefunden.`
: `${routes.length} veröffentlichte Routen verfügbar.`
? ns.I18n.t("app.von.strecken.gefunden", { value1: allRoutes.length, value2: routes.length })
: ns.I18n.t("app.strecken.zur.auswahl", { value1: routes.length })
);
renderRouteControl();
}
@@ -288,7 +288,7 @@
$('#app-shell').removeClass('has-navigation');
}
function showNavigation(message = 'Position wird ermittelt', distance = '–') {
function showNavigation(message = ns.I18n.t("app.location.searching"), distance = '–') {
$('#navigation-mode').text(message);
$('#navigation-distance').text(distance);
$('#navigation-footer').prop('hidden', false);
@@ -305,7 +305,7 @@
function updateRouteControls() {
const running = state.routeState === 'running';
const paused = state.routeState === 'paused';
const startLabel = paused ? 'Route fortsetzen' : 'Route starten';
const startLabel = paused ? ns.I18n.t("app.strecke.fortsetzen") : ns.I18n.t("app.strecke.starten");
$('#start-route')
.prop('disabled', running)
.attr({ 'aria-label': startLabel, title: startLabel });
@@ -333,7 +333,7 @@
setActivePoi(null);
hideNavigation();
if (announce && wasActive && !returnToRoutes) {
$('#tracking-status').prop('hidden', false).text('Route beendet.');
$('#tracking-status').prop('hidden', false).text(ns.I18n.t("app.strecke.beendet"));
} else {
$('#tracking-status').prop('hidden', true).text('');
}
@@ -357,8 +357,8 @@
ns.AudioPlayer.pause();
$('#tracking-status')
.prop('hidden', false)
.text('Route pausiert. Die Standortverfolgung ist angehalten.');
showNavigation('Route pausiert', $('#navigation-distance').text() || '–');
.text(ns.I18n.t("app.du.machst.eine.pause.die.app.sucht"));
showNavigation(ns.I18n.t("app.strecke.pausiert"), $('#navigation-distance').text() || '–');
updateRouteControls();
updateRouteControlSummary();
}
@@ -384,7 +384,7 @@
updateRouteControls();
const list = $('#poi-list').empty();
if (!route.pois.length) {
list.append('<li><p>Diese Route enthält noch keine Streckenpunkte.</p></li>');
list.append(("<li><p>" + escapeHtml(ns.I18n.t("app.diese.strecke.hat.noch.keine.stationen")) + "</p></li>"));
updateRouteControlSummary();
return;
}
@@ -484,8 +484,8 @@
const indicator = $(`#poi-list [data-poi-id="${metric.poi.id}"] .poi-proximity`);
const distance = Math.round(metric.distance);
indicator
.attr('aria-label', `${distance} Meter entfernt`)
.attr('title', `${distance} m entfernt`);
.attr('aria-label', ns.I18n.t("app.distance.spoken", { value1: distance }))
.attr('title', ns.I18n.t("app.distance.short", { value1: distance }));
indicator.find('svg').css('transform', `rotate(${metric.rotation.toFixed(1)}deg)`);
indicator.find('small').text(`${distance} m`);
});
@@ -537,27 +537,27 @@
$('#navigation-arrow').css('transform', `rotate(${metric.rotation.toFixed(1)}deg)`);
showNavigation(
heading == null
? `Norden oben · nächster Streckenpunkt: ${metric.poi.title}`
: `Nächster Streckenpunkt: ${metric.poi.title}`,
? ns.I18n.t("app.norden.oben.nachste.station", { value1: metric.poi.title })
: ns.I18n.t("app.nachste.station.2", { value1: metric.poi.title }),
`${distance} m`
);
$('#navigation-footer').attr(
'aria-label',
`Nächster Streckenpunkt ${metric.poi.title} in ${distance} Metern`
ns.I18n.t("app.nachste.station.in.metern", { value1: metric.poi.title, value2: distance })
);
}
function showRoutePointNavigation(position, progress) {
if (!progress) {
$('#navigation-arrow').css('transform', 'rotate(0deg)');
showNavigation('Keine Navigationspunkte vorhanden', '–');
showNavigation(ns.I18n.t("app.path.missing"), '–');
return;
}
if (progress.reached) {
$('#navigation-arrow').css('transform', 'rotate(0deg)');
showNavigation('Alle Streckenpunkte und das Routenziel erreicht', '0 m');
$('#navigation-footer').attr('aria-label', 'Alle Streckenpunkte und das Routenziel erreicht');
showNavigation(ns.I18n.t("app.alle.stationen.und.das.ziel.erreicht"), '0 m');
$('#navigation-footer').attr('aria-label', ns.I18n.t("app.alle.stationen.und.das.ziel.erreicht"));
return;
}
const bearing = ns.Distance.bearing(position, progress.target);
@@ -568,12 +568,12 @@
const distance = Math.round(progress.distance);
$('#navigation-arrow').css('transform', `rotate(${rotation.toFixed(1)}deg)`);
showNavigation(
heading == null ? 'Norden oben · nächster Routenpunkt' : 'Nächster Routenpunkt',
heading == null ? ns.I18n.t("app.norden.oben.nachster.punkt.auf.dem.weg") : ns.I18n.t("app.nachster.punkt.auf.dem.weg"),
`${distance} m`
);
$('#navigation-footer').attr(
'aria-label',
`Nächster Routenpunkt in ${distance} Metern`
ns.I18n.t("app.nachster.punkt.auf.dem.weg.in.metern", { value1: distance })
);
}
@@ -608,8 +608,8 @@
const nearest = nearestPendingPoi(metrics);
updateNavigation(position);
$('#tracking-status').text(
`GPS aktiv · Genauigkeit ${Math.round(position.accuracy)} m`
+ (nearest ? ` · nächster Streckenpunkt ${Math.round(nearest.distance)} m` : ' · alle Streckenpunkte erreicht')
ns.I18n.t("app.dein.standort.wird.gesucht.auf.etwa.m", { value1: Math.round(position.accuracy) })
+ (nearest ? ns.I18n.t("app.nachste.station.m", { value1: Math.round(nearest.distance) }) : ns.I18n.t("app.alle.stationen.erreicht.2"))
);
if (nearest && nearest.distance <= nearest.poi.triggerRadiusM) {
state.triggered.add(nearest.poi.id);
@@ -636,11 +636,11 @@
updateRouteControlSummary();
$('#tracking-status')
.prop('hidden', false)
.text(resumed ? 'Route wird fortgesetzt.' : 'Route wird gestartet. GPS wird aktiviert.');
showNavigation(resumed ? 'Route wird fortgesetzt' : 'Position wird ermittelt');
.text(resumed ? ns.I18n.t("app.strecke.wird.fortgesetzt") : ns.I18n.t("app.location.start"));
showNavigation(resumed ? ns.I18n.t("app.strecke.wird.fortgesetzt.2") : ns.I18n.t("app.location.searching"));
ns.Geo.start(
evaluatePosition,
error => $('#tracking-status').text(`GPS-Fehler: ${error.message}`)
() => $('#tracking-status').text(ns.I18n.t("app.dein.standort.konnte.nicht.gefunden.werden.prufe"))
);
const orientationAvailable = await ns.Orientation.start(updateHeading);
@@ -648,12 +648,12 @@
ns.Orientation.stop();
return;
}
if (!orientationAvailable) $('#navigation-mode').text('Norden oben · Navigation');
if (!orientationAvailable) $('#navigation-mode').text(ns.I18n.t("app.norden.oben.navigation"));
}
function routesFromResponse(response) {
if (!Array.isArray(response?.routes)) {
throw new TypeError('Die Routen-API hat keine gültige Routenliste geliefert.');
throw new TypeError(ns.I18n.t("app.die.strecken.konnten.nicht.geladen.werden.bitte"));
}
return response.routes
.filter(route => route?.id != null && route?.name != null)
@@ -666,7 +666,7 @@
state.localRoutesError = null;
} catch (error) {
state.localRoutes = [];
state.localRoutesError = error.message || 'Lokale Strecken konnten nicht gelesen werden.';
state.localRoutesError = ns.UserMessages?.error(error, ns.I18n.t("app.deine.entwurfe.konnten.nicht.geoffnet.werden.bitte")) || ns.I18n.t("app.deine.entwurfe.konnten.nicht.geoffnet.werden.bitte");
}
}
@@ -685,12 +685,12 @@
state.position = position;
updateRouteProximity(position);
$('#location-hint').text(
`Standort ermittelt (Genauigkeit ${Math.round(position.accuracy)} m). `
+ `Nahe Routen werden bis ${ns.Config.routeRadiusKm} km empfohlen.`
ns.I18n.t("app.dein.standort.wurde.auf.etwa.m.genau", { value1: Math.round(position.accuracy) })
+ ns.I18n.t("app.wir.zeigen.dir.strecken.bis.km.entfernung", { value1: ns.Config.routeRadiusKm })
);
} catch (error) {
} catch {
$('#location-hint').text(
`Standort nicht verfügbar: ${error.message}. Alle Routen können weiterhin ausgewählt werden.`
ns.I18n.t("app.dein.standort.konnte.nicht.gefunden.werden.du")
);
} finally {
context.markStep('location', 'done');
@@ -746,7 +746,7 @@
try {
await selectRoute(String(button.data('route-source') || 'server'), String(button.data('route-id')));
} catch (error) {
alert(error.responseJSON?.message || error.statusText || error.message);
alert(ns.UserMessages?.error(error, ns.I18n.t("app.die.strecke.konnte.nicht.geoffnet.werden.prufe")) || ns.I18n.t("app.die.strecke.konnte.nicht.geoffnet.werden.bitte"));
} finally {
button.prop('disabled', false);
}