diff --git a/VERSION b/VERSION
index 16b7561..568efca 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1300
\ No newline at end of file
+1301
\ No newline at end of file
diff --git a/backend/static/css/components.css b/backend/static/css/components.css
index 91dfd2d..f3e34e7 100644
--- a/backend/static/css/components.css
+++ b/backend/static/css/components.css
@@ -8307,6 +8307,10 @@ svg.empty-state-icon {
.album-title { font-size: var(--text-base); font-weight: 700; color: var(--c-text); }
.album-subtitle { font-size: var(--text-xs); color: var(--c-text-muted); margin-top: 2px; }
.album-close { background: none; border: none; font-size: 1.5rem; line-height: 1; color: var(--c-text-muted); cursor: pointer; padding: 0 4px; }
+.album-head-actions { display: flex; align-items: center; gap: var(--space-2); flex-shrink: 0; }
+.album-lang { display: inline-flex; border: 1px solid var(--c-border); border-radius: var(--radius-full); overflow: hidden; background: var(--c-surface-2); }
+.album-lang-btn { background: none; border: none; cursor: pointer; padding: 4px 10px; font-size: var(--text-xs); font-weight: 700; color: var(--c-text-muted); line-height: 1.4; transition: background .15s, color .15s; }
+.album-lang-btn.is-active { background: var(--c-primary); color: #fff; }
.album-list { display: flex; flex-direction: column; gap: var(--space-2); }
.album-song { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3); border-radius: var(--radius-md); background: var(--c-surface-2); cursor: pointer; transition: background .15s; }
.album-song:active { background: var(--c-border); }
diff --git a/backend/static/index.html b/backend/static/index.html
index eaec6fc..dd33421 100644
--- a/backend/static/index.html
+++ b/backend/static/index.html
@@ -86,14 +86,14 @@
Ban Yaro
-
+
-
-
-
-
-
+
+
+
+
+
@@ -624,12 +624,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
@@ -639,7 +639,7 @@
-
+
diff --git a/backend/static/js/app.js b/backend/static/js/app.js
index 1a76c84..1b8fc75 100644
--- a/backend/static/js/app.js
+++ b/backend/static/js/app.js
@@ -3,7 +3,7 @@
Router, State-Management, Navigation, Initialisierung.
============================================================ */
-const APP_VER = '1300'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
+const APP_VER = '1301'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const APP_VERSION = '1.6.0'; // ← semantische Version, wird bei make release gesetzt
window.APP_VER = APP_VER; // global verfügbar für andere Module (z.B. offline-indicator)
window.APP_VERSION = APP_VERSION;
diff --git a/backend/static/js/worlds.js b/backend/static/js/worlds.js
index c3bf861..17946de 100644
--- a/backend/static/js/worlds.js
+++ b/backend/static/js/worlds.js
@@ -1941,7 +1941,11 @@ window.Worlds = (() => {
// zentral in index.html → übersteht Re-Renders & Welt-Wechsel.
const _anthem = (() => {
const KEY = 'by_anthem_heard';
- const SONGS = [
+ const LANG_KEY = 'by_album_lang';
+ // EN-Album erst sichtbar, wenn die 7 *-en.mp3 in static/sounds/ liegen.
+ // Aktivieren: Dateien ablegen → EN_READY = true → make bump → deploy.
+ const EN_READY = true; // 2026-06-16: 7 *-en.mp3 liegen in static/sounds/
+ const SONGS_DE = [
{ title: 'Ban Yaro Blues', sub: 'Die Hymne', file: '/sounds/ban-yaro-blues.mp3?v=2' },
{ title: 'Ban Yaro Mobil', sub: 'Erste Fahrt im Anhänger', file: '/sounds/ban-yaro-mobil.mp3' },
{ title: 'Amy', sub: 'Eine Liebesromanze', file: '/sounds/amy.mp3' },
@@ -1950,6 +1954,19 @@ window.Worlds = (() => {
{ title: 'Platsch!', sub: 'Ab ins kühle Nass', file: '/sounds/platsch.mp3' },
{ title: 'Bester Freund', sub: 'Du und ich', file: '/sounds/bester-freund.mp3' },
];
+ const SONGS_EN = [
+ { title: 'Ban Yaro Blues', sub: 'The anthem', file: '/sounds/ban-yaro-blues-en.mp3' },
+ { title: 'Ban Yaro Mobile', sub: 'First ride in the trailer', file: '/sounds/ban-yaro-mobil-en.mp3' },
+ { title: 'Amy', sub: 'A love duet', file: '/sounds/amy-en.mp3' },
+ { title: "At the Groomer's", sub: 'Half the fur, all the energy', file: '/sounds/at-the-groomers-en.mp3' },
+ { title: 'Treat Paradise', sub: 'Full bowl, full heart', file: '/sounds/treat-paradise-en.mp3' },
+ { title: 'Splash!', sub: 'Into the cool water', file: '/sounds/splash-en.mp3' },
+ { title: 'Best Friend', sub: 'You and me', file: '/sounds/best-friend-en.mp3' },
+ ];
+ let _lang = (() => {
+ try { return (EN_READY && localStorage.getItem(LANG_KEY) === 'en') ? 'en' : 'de'; } catch (_) { return 'de'; }
+ })();
+ const _songs = () => (_lang === 'en' && EN_READY) ? SONGS_EN : SONGS_DE;
let _bound = false, _curIdx = -1;
const _audio = () => document.getElementById('anthem-audio');
// Entdeckt? Server-Flag (geräteübergreifend, deploy-fest) ODER lokal (sofort/offline).
@@ -1988,17 +2005,18 @@ window.Worlds = (() => {
a.addEventListener('play', _sync);
a.addEventListener('pause', _sync);
a.addEventListener('ended', () => { // automatisch zum nächsten Song
- if (_curIdx >= 0 && _curIdx < SONGS.length - 1) _play(_curIdx + 1);
+ if (_curIdx >= 0 && _curIdx < _songs().length - 1) _play(_curIdx + 1);
else { _curIdx = -1; _sync(); }
});
}
function _play(i) {
const a = _audio();
- if (!a || !SONGS[i]) return;
+ const songs = _songs();
+ if (!a || !songs[i]) return;
if (i === _curIdx && !a.paused) { a.pause(); return; } // aktiven Song pausieren
_curIdx = i;
- a.src = SONGS[i].file;
+ a.src = songs[i].file;
a.play().catch(() => {});
_markHeard();
_sync();
@@ -2006,39 +2024,67 @@ window.Worlds = (() => {
function _closeAlbum() { document.getElementById('album-modal')?.remove(); }
+ // Sprache wechseln: aktuelle Wiedergabe stoppen (andere Datei) und Liste neu zeichnen.
+ function _setLang(l) {
+ if (l === _lang || !EN_READY) return;
+ _lang = l;
+ try { localStorage.setItem(LANG_KEY, l); } catch (_) {}
+ const a = _audio(); if (a) a.pause();
+ _curIdx = -1;
+ _fillAlbum();
+ _sync();
+ }
+
+ // Inhalt des Sheets (neu) rendern + innere Controls binden — auch bei Sprachwechsel.
+ function _fillAlbum() {
+ const sheet = document.querySelector('#album-modal .album-sheet');
+ if (!sheet) return;
+ const songs = _songs();
+ const en = _lang === 'en';
+ sheet.innerHTML = `
+
+
+
${en ? 'Ban Yaro — The Album' : 'Ban Yaro — das Album'}
+
${songs.length} ${en ? 'songs · homemade' : 'Songs · selbst gemacht'} 🎸
+
+
+ ${EN_READY ? `
+
+
+
+
` : ''}
+
+
+
+
+ ${songs.map((s, i) => `
+
+
+
+ ${_esc(s.title)}
+ ${_esc(s.sub)}
+
+
`).join('')}
+
`;
+ sheet.querySelector('.album-close').addEventListener('click', _closeAlbum);
+ sheet.querySelectorAll('.album-lang-btn').forEach(b =>
+ b.addEventListener('click', () => _setLang(b.dataset.lang)));
+ sheet.querySelectorAll('.album-song').forEach(row => {
+ const i = parseInt(row.dataset.i, 10);
+ row.addEventListener('click', () => _play(i));
+ row.addEventListener('keydown', e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); _play(i); } });
+ });
+ }
+
function openAlbum() {
_markHeard();
if (document.getElementById('album-modal')) return;
const ov = document.createElement('div');
ov.id = 'album-modal';
- ov.innerHTML = `
-
-
-
-
Ban Yaro — das Album
-
${SONGS.length} Songs · selbst gemacht 🎸
-
-
-
-
- ${SONGS.map((s, i) => `
-
-
-
- ${_esc(s.title)}
- ${_esc(s.sub)}
-
-
`).join('')}
-
-
`;
+ ov.innerHTML = ``;
document.body.appendChild(ov);
ov.addEventListener('click', e => { if (e.target === ov) _closeAlbum(); });
- ov.querySelector('.album-close').addEventListener('click', _closeAlbum);
- ov.querySelectorAll('.album-song').forEach(row => {
- const i = parseInt(row.dataset.i, 10);
- row.addEventListener('click', () => _play(i));
- row.addEventListener('keydown', e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); _play(i); } });
- });
+ _fillAlbum();
_sync();
}
@@ -2061,7 +2107,7 @@ window.Worlds = (() => {
updateButton();
}
- return { heard, toggle: openAlbum, updateButton, initWelt, count: SONGS.length };
+ return { heard, toggle: openAlbum, updateButton, initWelt, count: SONGS_DE.length };
})();
function _renderWelt() {
diff --git a/backend/static/landing.html b/backend/static/landing.html
index e92f60f..3a4af0c 100644
--- a/backend/static/landing.html
+++ b/backend/static/landing.html
@@ -4,7 +4,7 @@
-
+
Ban Yaro — Die Hunde-App für Deutschland, Österreich & Schweiz
diff --git a/backend/static/sounds/amy-en.mp3 b/backend/static/sounds/amy-en.mp3
new file mode 100644
index 0000000..2d42126
Binary files /dev/null and b/backend/static/sounds/amy-en.mp3 differ
diff --git a/backend/static/sounds/at-the-groomers-en.mp3 b/backend/static/sounds/at-the-groomers-en.mp3
new file mode 100644
index 0000000..0d1fe2f
Binary files /dev/null and b/backend/static/sounds/at-the-groomers-en.mp3 differ
diff --git a/backend/static/sounds/ban-yaro-blues-en.mp3 b/backend/static/sounds/ban-yaro-blues-en.mp3
new file mode 100644
index 0000000..28811ca
Binary files /dev/null and b/backend/static/sounds/ban-yaro-blues-en.mp3 differ
diff --git a/backend/static/sounds/ban-yaro-mobil-en.mp3 b/backend/static/sounds/ban-yaro-mobil-en.mp3
new file mode 100644
index 0000000..3f4fc3f
Binary files /dev/null and b/backend/static/sounds/ban-yaro-mobil-en.mp3 differ
diff --git a/backend/static/sounds/best-friend-en.mp3 b/backend/static/sounds/best-friend-en.mp3
new file mode 100644
index 0000000..314e45a
Binary files /dev/null and b/backend/static/sounds/best-friend-en.mp3 differ
diff --git a/backend/static/sounds/splash-en.mp3 b/backend/static/sounds/splash-en.mp3
new file mode 100644
index 0000000..e732615
Binary files /dev/null and b/backend/static/sounds/splash-en.mp3 differ
diff --git a/backend/static/sounds/treat-paradise-en.mp3 b/backend/static/sounds/treat-paradise-en.mp3
new file mode 100644
index 0000000..c08deaa
Binary files /dev/null and b/backend/static/sounds/treat-paradise-en.mp3 differ
diff --git a/backend/static/sw.js b/backend/static/sw.js
index 3eca1b4..5aa9323 100644
--- a/backend/static/sw.js
+++ b/backend/static/sw.js
@@ -4,7 +4,7 @@
============================================================ */
// ← EINZIGE Stelle für die Version — STATIC_ASSETS und CACHE_VERSION leiten sich ab
-const VER = '1300';
+const VER = '1301';
const CACHE_VERSION = `by-v${VER}`;
const CACHE_STATIC = `${CACHE_VERSION}-static`;
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten