UI and Translation Improvements (#889)

* Update translations from Weblate

* Translated using Weblate (German)

Currently translated at 100.0% (979 of 979 strings)

Translation: AdventureLog/Web App
Translate-URL: https://hosted.weblate.org/projects/adventurelog/web-app/de/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (979 of 979 strings)

Translation: AdventureLog/Web App
Translate-URL: https://hosted.weblate.org/projects/adventurelog/web-app/sk/

* Added translation using Weblate (Ukrainian)

* Bump version to 0.11.0 and enhance hero section responsiveness in adventure details

* Update Django version to 5.2.7 in requirements

* feature added hu translation (#885)

* feature added hu translation

* Update dependencies and add Hungarian translation support

---------

Co-authored-by: Petrekanics Máté <mate.petrekanics@webcapital.hu>
Co-authored-by: Sean Morley <mail@seanmorley.com>

* Fix "back" FR traduction (#858)

Co-authored-by: Sean Morley <98704938+seanmorley15@users.noreply.github.com>

---------

Co-authored-by: Alex <div@alexe.at>
Co-authored-by: fantastron27 <fantastron27@gmail.com>
Co-authored-by: Максим Горпиніч <gorpinicmaksim0@gmail.com>
Co-authored-by: petrekanics <75931275+petrekanics@users.noreply.github.com>
Co-authored-by: Petrekanics Máté <mate.petrekanics@webcapital.hu>
Co-authored-by: Sebastien Laithier <kirby@hyrule.ovh>
This commit is contained in:
Sean Morley
2025-10-11 16:52:22 -04:00
committed by GitHub
parent 8a0f7310b0
commit a8d3a29991
13 changed files with 4333 additions and 3271 deletions

View File

@@ -1,4 +1,4 @@
Django==5.2.6
Django==5.2.7
djangorestframework>=3.15.2
django-allauth==0.63.3
drf-yasg==1.21.4

View File

@@ -1,6 +1,6 @@
{
"name": "adventurelog-frontend",
"version": "0.10.0",
"version": "0.11.0",
"scripts": {
"dev": "vite dev",
"django": "cd .. && cd backend/server && python3 manage.py runserver",
@@ -12,43 +12,43 @@
"format": "prettier --write ."
},
"devDependencies": {
"@event-calendar/core": "^3.7.1",
"@event-calendar/day-grid": "^3.7.1",
"@event-calendar/core": "^3.12.0",
"@event-calendar/day-grid": "^3.12.0",
"@event-calendar/interaction": "^3.12.0",
"@event-calendar/time-grid": "^3.7.1",
"@iconify-json/mdi": "^1.1.67",
"@sveltejs/adapter-node": "^5.2.0",
"@sveltejs/adapter-vercel": "^5.4.1",
"@sveltejs/kit": "^2.8.3",
"@sveltejs/vite-plugin-svelte": "^3.1.1",
"@tailwindcss/typography": "^0.5.13",
"@types/node": "^22.5.4",
"@event-calendar/time-grid": "^3.12.0",
"@iconify-json/mdi": "^1.2.3",
"@sveltejs/adapter-node": "^5.2.12",
"@sveltejs/adapter-vercel": "^5.7.0",
"@sveltejs/kit": "^2.20.7",
"@sveltejs/vite-plugin-svelte": "^3.1.2",
"@tailwindcss/typography": "^0.5.16",
"@types/node": "^22.15.2",
"@types/qrcode": "^1.5.5",
"autoprefixer": "^10.4.19",
"daisyui": "^4.12.6",
"postcss": "^8.4.38",
"prettier": "^3.3.2",
"prettier-plugin-svelte": "^3.2.5",
"autoprefixer": "^10.4.21",
"daisyui": "^4.12.24",
"postcss": "^8.5.3",
"prettier": "^3.5.3",
"prettier-plugin-svelte": "^3.3.3",
"svelte": "^4.2.19",
"svelte-check": "^3.8.1",
"tailwindcss": "^3.4.4",
"tslib": "^2.6.3",
"typescript": "^5.5.2",
"unplugin-icons": "^0.19.0",
"svelte-check": "^3.8.6",
"tailwindcss": "^3.4.17",
"tslib": "^2.8.1",
"typescript": "^5.8.3",
"unplugin-icons": "^0.19.3",
"vite": "^5.4.19"
},
"type": "module",
"dependencies": {
"@lukulent/svelte-umami": "^0.0.3",
"dompurify": "^3.2.4",
"emoji-picker-element": "^1.26.0",
"dompurify": "^3.2.5",
"emoji-picker-element": "^1.26.3",
"gsap": "^3.12.7",
"luxon": "^3.6.1",
"marked": "^15.0.4",
"marked": "^15.0.11",
"psl": "^1.15.0",
"qrcode": "^1.5.4",
"svelte-i18n": "^4.0.1",
"svelte-maplibre": "^0.9.8"
"svelte-maplibre": "^0.9.14"
},
"overrides": {
"esbuild": "^0.25.9"

View File

@@ -15,10 +15,10 @@ importers:
specifier: ^0.0.3
version: 0.0.3(svelte@4.2.19)
dompurify:
specifier: ^3.2.4
specifier: ^3.2.5
version: 3.2.5
emoji-picker-element:
specifier: ^1.26.0
specifier: ^1.26.3
version: 1.26.3
gsap:
specifier: ^3.12.7
@@ -27,7 +27,7 @@ importers:
specifier: ^3.6.1
version: 3.6.1
marked:
specifier: ^15.0.4
specifier: ^15.0.11
version: 15.0.11
psl:
specifier: ^1.15.0
@@ -39,77 +39,77 @@ importers:
specifier: ^4.0.1
version: 4.0.1(svelte@4.2.19)
svelte-maplibre:
specifier: ^0.9.8
specifier: ^0.9.14
version: 0.9.14(svelte@4.2.19)
devDependencies:
'@event-calendar/core':
specifier: ^3.7.1
specifier: ^3.12.0
version: 3.12.0
'@event-calendar/day-grid':
specifier: ^3.7.1
specifier: ^3.12.0
version: 3.12.0
'@event-calendar/interaction':
specifier: ^3.12.0
version: 3.12.0
'@event-calendar/time-grid':
specifier: ^3.7.1
specifier: ^3.12.0
version: 3.12.0
'@iconify-json/mdi':
specifier: ^1.1.67
specifier: ^1.2.3
version: 1.2.3
'@sveltejs/adapter-node':
specifier: ^5.2.0
specifier: ^5.2.12
version: 5.2.12(@sveltejs/kit@2.20.7(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2)))(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2)))
'@sveltejs/adapter-vercel':
specifier: ^5.4.1
specifier: ^5.7.0
version: 5.7.0(@sveltejs/kit@2.20.7(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2)))(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2)))(rollup@4.40.2)
'@sveltejs/kit':
specifier: ^2.8.3
specifier: ^2.20.7
version: 2.20.7(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2)))(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2))
'@sveltejs/vite-plugin-svelte':
specifier: ^3.1.1
specifier: ^3.1.2
version: 3.1.2(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2))
'@tailwindcss/typography':
specifier: ^0.5.13
specifier: ^0.5.16
version: 0.5.16(tailwindcss@3.4.17)
'@types/node':
specifier: ^22.5.4
specifier: ^22.15.2
version: 22.15.2
'@types/qrcode':
specifier: ^1.5.5
version: 1.5.5
autoprefixer:
specifier: ^10.4.19
specifier: ^10.4.21
version: 10.4.21(postcss@8.5.3)
daisyui:
specifier: ^4.12.6
specifier: ^4.12.24
version: 4.12.24(postcss@8.5.3)
postcss:
specifier: ^8.4.38
specifier: ^8.5.3
version: 8.5.3
prettier:
specifier: ^3.3.2
specifier: ^3.5.3
version: 3.5.3
prettier-plugin-svelte:
specifier: ^3.2.5
specifier: ^3.3.3
version: 3.3.3(prettier@3.5.3)(svelte@4.2.19)
svelte:
specifier: ^4.2.19
version: 4.2.19
svelte-check:
specifier: ^3.8.1
specifier: ^3.8.6
version: 3.8.6(postcss-load-config@4.0.2(postcss@8.5.3))(postcss@8.5.3)(svelte@4.2.19)
tailwindcss:
specifier: ^3.4.4
specifier: ^3.4.17
version: 3.4.17
tslib:
specifier: ^2.6.3
specifier: ^2.8.1
version: 2.8.1
typescript:
specifier: ^5.5.2
specifier: ^5.8.3
version: 5.8.3
unplugin-icons:
specifier: ^0.19.0
specifier: ^0.19.3
version: 0.19.3
vite:
specifier: ^5.4.19
@@ -733,8 +733,8 @@ packages:
resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
engines: {node: '>=6'}
caniuse-lite@1.0.30001715:
resolution: {integrity: sha512-7ptkFGMm2OAOgvZpwgA4yjQ5SQbrNVGdRjzH0pBdy1Fasvcr+KAeECmbCAECzTuDuoX0FCY8KzUxjf9+9kfZEw==}
caniuse-lite@1.0.30001750:
resolution: {integrity: sha512-cuom0g5sdX6rw00qOoLNSFCJ9/mYIsuSOA+yzpDw8eopiFqcVwQvZHqov0vmEighRxX++cfC0Vg1G+1Iy/mSpQ==}
chokidar@3.6.0:
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
@@ -2376,7 +2376,7 @@ snapshots:
autoprefixer@10.4.21(postcss@8.5.3):
dependencies:
browserslist: 4.24.4
caniuse-lite: 1.0.30001715
caniuse-lite: 1.0.30001750
fraction.js: 4.3.7
normalize-range: 0.1.2
picocolors: 1.1.1
@@ -2408,7 +2408,7 @@ snapshots:
browserslist@4.24.4:
dependencies:
caniuse-lite: 1.0.30001715
caniuse-lite: 1.0.30001750
electron-to-chromium: 1.5.143
node-releases: 2.0.19
update-browserslist-db: 1.1.3(browserslist@4.24.4)
@@ -2419,7 +2419,7 @@ snapshots:
camelcase@5.3.1: {}
caniuse-lite@1.0.30001715: {}
caniuse-lite@1.0.30001750: {}
chokidar@3.6.0:
dependencies:

View File

@@ -65,7 +65,8 @@
ar: 'العربية',
'pt-br': 'Português (Brasil)',
sk: 'Slovenský',
tr: 'Türkçe'
tr: 'Türkçe',
hu: 'Magyar'
};
const submitLocaleChange = (event: Event) => {

View File

@@ -1,4 +1,4 @@
export let appVersion = 'v0.11.0-main-09212025';
export let appVersion = 'v0.11.0-main-10112025';
export let versionChangelog = 'https://github.com/seanmorley15/AdventureLog/releases/tag/v0.11.0';
export let appTitle = 'AdventureLog';
export let copyrightYear = '2023-2025';

View File

File diff suppressed because it is too large Load Diff

View File

@@ -290,7 +290,7 @@
"average_cadence": "Cadence moyenne",
"average_speed": "Vitesse moyenne",
"avg_speed": "Vitesse moyenne",
"back": "Dos",
"back": "Retour",
"cadence": "Cadence",
"calories": "Calories",
"click_map": "Cliquez sur la carte pour sélectionner un emplacement",

1041
frontend/src/locales/hu.json Normal file
View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
{}

View File

@@ -22,6 +22,7 @@
register('pt-br', () => import('../locales/pt-br.json'));
register('sk', () => import('../locales/sk.json'));
register('tr', () => import('../locales/tr.json'));
register('hu', () => import('../locales/hu.json'));
let locales = [
'en',
@@ -40,7 +41,8 @@
'ar',
'pt-br',
'sk',
'tr'
'tr',
'hu'
];
if (browser) {

View File

@@ -173,8 +173,12 @@
<!-- Hero Section -->
<div class="relative">
{#if adventure.images && adventure.images.length > 0}
<div class="hero min-h-[60vh] relative overflow-hidden">
<div
class="hero min-h-[60vh] relative overflow-hidden"
class:min-h-[30vh]={!adventure.images || adventure.images.length === 0}
>
<!-- Background: Images or Gradient -->
{#if adventure.images && adventure.images.length > 0}
<div class="hero-overlay bg-gradient-to-t from-black/70 via-black/20 to-transparent"></div>
{#each adventure.images as image, i}
<div
@@ -191,137 +195,129 @@
</button>
</div>
{/each}
{:else}
<div class="absolute inset-0 bg-gradient-to-br from-primary/20 to-secondary/20"></div>
{/if}
<div class="hero-content relative z-10 text-center text-white">
<div class="max-w-4xl">
<h1 class="text-6xl font-bold mb-4 drop-shadow-lg">{adventure.name}</h1>
<!-- Content -->
<div
class="hero-content relative z-10 text-center"
class:text-white={adventure.images?.length > 0}
>
<div class="max-w-4xl">
<h1 class="text-6xl font-bold mb-4 drop-shadow-lg">{adventure.name}</h1>
<!-- Rating -->
{#if adventure.rating !== undefined && adventure.rating !== null}
<div class="flex justify-center mb-6">
<div class="rating rating-lg">
{#each Array.from({ length: 5 }, (_, i) => i + 1) as star}
<input
type="radio"
name="rating-hero"
class="mask mask-star-2 bg-warning"
checked={star <= adventure.rating}
disabled
/>
{/each}
</div>
<!-- Rating -->
{#if adventure.rating !== undefined && adventure.rating !== null}
<div class="flex justify-center mb-6">
<div class="rating rating-lg">
{#each Array.from({ length: 5 }, (_, i) => i + 1) as star}
<input
type="radio"
name="rating-hero"
class="mask mask-star-2 bg-warning"
checked={star <= adventure.rating}
disabled
/>
{/each}
</div>
</div>
{/if}
<!-- Quick Info Badges -->
<div class="flex flex-wrap justify-center gap-4 mb-6">
<a
href="/locations?types={adventure.category?.name}"
class="badge badge-lg badge-primary font-semibold px-4 py-3 cursor-pointer hover:brightness-110 transition-all"
>
{adventure.category?.display_name}
{adventure.category?.icon}
</a>
{#if adventure.location}
<div class="badge badge-lg badge-secondary font-semibold px-4 py-3">
📍 {adventure.location}
</div>
{/if}
<!-- Quick Info Cards -->
<div class="flex flex-wrap justify-center gap-4 mb-6">
<div class="badge badge-lg badge-primary font-semibold px-4 py-3">
{adventure.category?.display_name}
{adventure.category?.icon}
{#if adventure.visits.length > 0}
<div class="badge badge-lg badge-accent font-semibold px-4 py-3">
🎯 {adventure.visits.length}
{adventure.visits.length === 1 ? $t('adventures.visit') : $t('adventures.visits')}
</div>
{#if adventure.location}
<div class="badge badge-lg badge-secondary font-semibold px-4 py-3">
📍 {adventure.location}
{/if}
{#if adventure.is_visited}
<div class="badge badge-lg badge-success font-semibold px-4 py-3">
{$t('adventures.visited')}
</div>
{:else}
<div class="badge badge-lg badge-warning font-semibold px-4 py-3">
{$t('adventures.not_visited')}
</div>
{/if}
{#if adventure.trails && adventure.trails.length > 0}
<div class="badge badge-lg badge-info font-semibold px-4 py-3">
🥾 {adventure.trails.length} Trail{adventure.trails.length === 1 ? '' : 's'}
</div>
{/if}
</div>
<!-- Image Navigation (only shown when multiple images exist) -->
{#if adventure.images && adventure.images.length > 1}
<div class="w-full max-w-md mx-auto">
<!-- Navigation arrows and current position -->
<div class="flex items-center justify-center gap-4 mb-3">
<button
on:click={() =>
goToSlide(currentSlide > 0 ? currentSlide - 1 : adventure.images.length - 1)}
class="btn btn-circle btn-sm btn-primary"
aria-label={$t('adventures.previous_image')}
>
</button>
<div class="text-sm font-medium bg-black/50 px-3 py-1 rounded-full">
{currentSlide + 1} / {adventure.images.length}
</div>
{/if}
{#if adventure.visits.length > 0}
<div class="badge badge-lg badge-accent font-semibold px-4 py-3">
🎯 {adventure.visits.length}
{adventure.visits.length === 1 ? $t('adventures.visit') : $t('adventures.visits')}
<button
on:click={() =>
goToSlide(currentSlide < adventure.images.length - 1 ? currentSlide + 1 : 0)}
class="btn btn-circle btn-sm btn-primary"
aria-label={$t('adventures.next_image')}
>
</button>
</div>
<!-- Dot navigation -->
{#if adventure.images.length <= 12}
<div class="flex justify-center gap-2 flex-wrap">
{#each adventure.images as _, i}
<button
on:click={() => goToSlide(i)}
class="btn btn-circle btn-xs transition-all duration-200"
class:btn-primary={i === currentSlide}
class:btn-outline={i !== currentSlide}
class:opacity-50={i !== currentSlide}
>
{i + 1}
</button>
{/each}
</div>
{/if}
{#if adventure.trails && adventure.trails.length > 0}
<div class="badge badge-lg badge-info font-semibold px-4 py-3">
🥾 {adventure.trails.length} Trail{adventure.trails.length === 1 ? '' : 's'}
{:else}
<div class="relative">
<div
class="absolute left-0 top-0 bottom-2 w-4 bg-gradient-to-r from-black/30 to-transparent pointer-events-none"
></div>
<div
class="absolute right-0 top-0 bottom-2 w-4 bg-gradient-to-l from-black/30 to-transparent pointer-events-none"
></div>
</div>
{/if}
</div>
<!-- Image Navigation -->
{#if adventure.images.length > 1}
<div class="w-full max-w-md mx-auto">
<!-- Navigation arrows and current position indicator -->
<div class="flex items-center justify-center gap-4 mb-3">
<button
on:click={() =>
goToSlide(currentSlide > 0 ? currentSlide - 1 : adventure.images.length - 1)}
class="btn btn-circle btn-sm btn-primary"
aria-label={$t('adventures.previous_image')}
>
</button>
<div class="text-sm font-medium bg-black/50 px-3 py-1 rounded-full">
{currentSlide + 1} / {adventure.images.length}
</div>
<button
on:click={() =>
goToSlide(currentSlide < adventure.images.length - 1 ? currentSlide + 1 : 0)}
class="btn btn-circle btn-sm btn-primary"
aria-label={$t('adventures.next_image')}
>
</button>
</div>
<!-- Scrollable dot navigation for many images -->
{#if adventure.images.length <= 12}
<!-- Show all dots for 12 or fewer images -->
<div class="flex justify-center gap-2 flex-wrap">
{#each adventure.images as _, i}
<button
on:click={() => goToSlide(i)}
class="btn btn-circle btn-xs transition-all duration-200"
class:btn-primary={i === currentSlide}
class:btn-outline={i !== currentSlide}
class:opacity-50={i !== currentSlide}
>
{i + 1}
</button>
{/each}
</div>
{:else}
<!-- Scrollable navigation for many images -->
<div class="relative">
<div
class="absolute left-0 top-0 bottom-2 w-4 bg-gradient-to-r from-black/30 to-transparent pointer-events-none"
></div>
<div
class="absolute right-0 top-0 bottom-2 w-4 bg-gradient-to-l from-black/30 to-transparent pointer-events-none"
></div>
</div>
{/if}
</div>
{/if}
</div>
{/if}
</div>
</div>
{:else}
<!-- No image hero -->
<div class="hero min-h-[40vh] bg-gradient-to-br from-primary/20 to-secondary/20">
<div class="hero-content text-center">
<div class="max-w-4xl">
<h1 class="text-6xl font-bold mb-6">{adventure.name}</h1>
{#if adventure.rating !== undefined && adventure.rating !== null}
<div class="flex justify-center mb-6">
<div class="rating rating-lg">
{#each Array.from({ length: 5 }, (_, i) => i + 1) as star}
<input
type="radio"
name="rating-hero-no-img"
class="mask mask-star-2 bg-warning"
checked={star <= adventure.rating}
disabled
/>
{/each}
</div>
</div>
{/if}
</div>
</div>
</div>
{/if}
</div>
</div>
<!-- Main Content -->
@@ -841,7 +837,7 @@
{/if}
<!-- Additional Images -->
{#if adventure.images && adventure.images.length > 1}
{#if adventure.images}
<div class="card bg-base-200 shadow-xl">
<div class="card-body">
<h3 class="card-title text-lg mb-4">🖼️ {$t('adventures.images')}</h3>