1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-07-19 04:49:37 +02:00

localization v2

This commit is contained in:
Sean Morley 2024-10-28 13:56:57 -04:00
parent 6cf62cfb82
commit 91c0ec8c07
18 changed files with 432 additions and 101 deletions

28
.vscode/settings.json vendored
View file

@ -1,3 +1,29 @@
{
"git.ignoreLimitWarning": true
"git.ignoreLimitWarning": true,
"i18n-ally.localesPaths": [
"frontend/src/locales",
"backend/server/backend/lib/python3.12/site-packages/allauth/locale",
"backend/server/backend/lib/python3.12/site-packages/dj_rest_auth/locale",
"backend/server/backend/lib/python3.12/site-packages/rest_framework/locale",
"backend/server/backend/lib/python3.12/site-packages/rest_framework_simplejwt/locale",
"backend/server/backend/lib/python3.12/site-packages/django/conf/locale",
"backend/server/backend/lib/python3.12/site-packages/django/contrib/messages",
"backend/server/backend/lib/python3.12/site-packages/allauth/templates/account/messages",
"backend/server/backend/lib/python3.12/site-packages/allauth/templates/mfa/messages",
"backend/server/backend/lib/python3.12/site-packages/allauth/templates/socialaccount/messages",
"backend/server/backend/lib/python3.12/site-packages/allauth/templates/usersessions/messages",
"backend/server/backend/lib/python3.12/site-packages/django/contrib/admindocs/locale",
"backend/server/backend/lib/python3.12/site-packages/django/contrib/auth/locale",
"backend/server/backend/lib/python3.12/site-packages/django/contrib/admin/locale",
"backend/server/backend/lib/python3.12/site-packages/django/contrib/contenttypes/locale",
"backend/server/backend/lib/python3.12/site-packages/django/contrib/flatpages/locale",
"backend/server/backend/lib/python3.12/site-packages/django/contrib/humanize/locale",
"backend/server/backend/lib/python3.12/site-packages/django/contrib/gis/locale",
"backend/server/backend/lib/python3.12/site-packages/django/contrib/redirects/locale",
"backend/server/backend/lib/python3.12/site-packages/django/contrib/postgres/locale",
"backend/server/backend/lib/python3.12/site-packages/django/contrib/sessions/locale",
"backend/server/backend/lib/python3.12/site-packages/django/contrib/sites/locale",
"backend/server/backend/lib/python3.12/site-packages/rest_framework/templates/rest_framework/docs/langs"
],
"i18n-ally.keystyle": "nested"
}

View file

@ -109,17 +109,11 @@ export const themeHook: Handle = async ({ event, resolve }) => {
// hook to get the langauge cookie and set the locale
export const i18nHook: Handle = async ({ event, resolve }) => {
let lang = event.cookies.get('lang');
if (!lang) {
lang = ''; // Set default locale
event.cookies.set('lang', lang, {
httpOnly: true,
sameSite: 'lax',
expires: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000), // 1 year
path: '/'
});
let locale = event.cookies.get('locale');
if (!locale) {
return await resolve(event);
}
event.locals.locale = lang; // Store the locale in locals
event.locals.locale = locale; // Store the locale in locals
return await resolve(event);
};

View file

@ -11,15 +11,14 @@
import MapMarker from '~icons/mdi/map-marker';
import { addToast } from '$lib/toasts';
import Link from '~icons/mdi/link-variant';
import CheckBold from '~icons/mdi/check-bold';
import FormatListBulletedSquare from '~icons/mdi/format-list-bulleted-square';
import LinkVariantRemove from '~icons/mdi/link-variant-remove';
import Plus from '~icons/mdi/plus';
import CollectionLink from './CollectionLink.svelte';
import DotsHorizontal from '~icons/mdi/dots-horizontal';
import DeleteWarning from './DeleteWarning.svelte';
import { isAdventureVisited, typeToString } from '$lib';
import { isAdventureVisited } from '$lib';
import CardCarousel from './CardCarousel.svelte';
import { t } from 'svelte-i18n';
export let type: string;
export let user: User | null;
@ -67,34 +66,13 @@
body: JSON.stringify({ collection: null })
});
if (res.ok) {
console.log('Adventure removed from collection');
addToast('info', 'Adventure removed from collection successfully!');
addToast('info', `${$t('adventures.collection_remove_success')}`);
dispatch('delete', adventure.id);
} else {
console.log('Error removing adventure from collection');
addToast('error', `${$t('adventures.collection_remove_error')}`);
}
}
function changeType(newType: string) {
return async () => {
let res = await fetch(`/api/adventures/${adventure.id}/`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ type: newType })
});
if (res.ok) {
console.log('Adventure type changed');
dispatch('typeChange', adventure.id);
addToast('info', 'Adventure type changed successfully!');
adventure.type = newType;
} else {
console.log('Error changing adventure type');
}
};
}
async function linkCollection(event: CustomEvent<number>) {
let collectionId = event.detail;
let res = await fetch(`/api/adventures/${adventure.id}`, {
@ -106,11 +84,11 @@
});
if (res.ok) {
console.log('Adventure linked to collection');
addToast('info', 'Adventure linked to collection successfully!');
addToast('info', `${$t('adventures.collection_link_success')}`);
isCollectionModalOpen = false;
dispatch('delete', adventure.id);
} else {
console.log('Error linking adventure to collection');
addToast('error', `${$t('adventures.collection_link_error')}`);
}
}
@ -131,7 +109,7 @@
<DeleteWarning
title="Delete Adventure"
button_text="Delete"
description="Are you sure you want to delete this adventure? This action cannot be undone."
description={$t('adventures.adventure_delete_confirm')}
is_warning={false}
on:close={() => (isWarningModalOpen = false)}
on:confirm={deleteAdventure}
@ -153,7 +131,7 @@
</button>
</div>
<div>
<div class="badge badge-primary">{typeToString(adventure.type)}</div>
<div class="badge badge-primary">{$t(`adventures.activities.${adventure.type}`)}</div>
<div class="badge badge-success">{isAdventureVisited(adventure) ? 'Visited' : 'Planned'}</div>
<div class="badge badge-secondary">{adventure.is_public ? 'Public' : 'Private'}</div>
</div>
@ -198,30 +176,24 @@
<button
class="btn btn-neutral mb-2"
on:click={() => goto(`/adventures/${adventure.id}`)}
><Launch class="w-6 h-6" />Open Details</button
><Launch class="w-6 h-6" />{$t('adventures.open_details')}</button
>
<button class="btn btn-neutral mb-2" on:click={editAdventure}>
<FileDocumentEdit class="w-6 h-6" />Edit Adventure
<FileDocumentEdit class="w-6 h-6" />
{$t('adventures.edit_adventure')}
</button>
{#if adventure.type == 'visited' && user?.pk == adventure.user_id}
<button class="btn btn-neutral mb-2" on:click={changeType('planned')}
><FormatListBulletedSquare class="w-6 h-6" />Change to Plan</button
>
{/if}
{#if adventure.type == 'planned' && user?.pk == adventure.user_id}
<button class="btn btn-neutral mb-2" on:click={changeType('visited')}
><CheckBold class="w-6 h-6" />Mark Visited</button
>
{/if}
<!-- remove from collection -->
{#if adventure.collection && user?.pk == adventure.user_id}
<button class="btn btn-neutral mb-2" on:click={removeFromCollection}
><LinkVariantRemove class="w-6 h-6" />Remove from Collection</button
><LinkVariantRemove class="w-6 h-6" />{$t(
'adventures.remove_from_collection'
)}</button
>
{/if}
{#if !adventure.collection}
<button class="btn btn-neutral mb-2" on:click={() => (isCollectionModalOpen = true)}
><Plus class="w-6 h-6" />Add to Collection</button
><Plus class="w-6 h-6" />{$t('adventures.add_to_collection')}</button
>
{/if}
<button
@ -229,7 +201,7 @@
data-umami-event="Delete Adventure"
class="btn btn-warning"
on:click={() => (isWarningModalOpen = true)}
><TrashCan class="w-6 h-6" />Delete</button
><TrashCan class="w-6 h-6" />{$t('adventures.delete')}</button
>
</ul>
</div>

View file

@ -15,7 +15,7 @@
<div class="avatar placeholder">
<div class="bg-neutral rounded-full text-neutral-200 w-10 ml-4">
{#if user.profile_pic}
<img src={user.profile_pic} alt="User Profile" />
<img src={user.profile_pic} alt={$t('navbar.profile')} />
{:else}
<span class="text-2xl -mt-1">{letter}</span>
{/if}

View file

@ -1,6 +1,7 @@
<script lang="ts">
import type { Adventure } from '$lib/types';
import ImageDisplayModal from './ImageDisplayModal.svelte';
import { t } from 'svelte-i18n';
export let adventures: Adventure[] = [];
@ -79,7 +80,7 @@
{:else}
<!-- svelte-ignore a11y-img-redundant-alt -->
<img
src={'https://placehold.co/300?text=No%20Image%20Found&font=roboto'}
src={`https://placehold.co/300?text=${$t('adventures.no_image_found')}&font=roboto`}
alt="No image available"
class="w-full h-48 object-cover"
/>

View file

@ -8,19 +8,25 @@
import WeatherSunny from '~icons/mdi/weather-sunny';
import WeatherNight from '~icons/mdi/weather-night';
import Forest from '~icons/mdi/forest';
import Flower from '~icons/mdi/flower';
import Water from '~icons/mdi/water';
import AboutModal from './AboutModal.svelte';
import AccountMultiple from '~icons/mdi/account-multiple';
import Avatar from './Avatar.svelte';
import PaletteOutline from '~icons/mdi/palette-outline';
import { page } from '$app/stores';
import { t } from 'svelte-i18n';
import { t, locale, locales } from 'svelte-i18n';
let query: string = '';
let isAboutModalOpen: boolean = false;
const submitLocaleChange = (event: Event) => {
const select = event.target as HTMLSelectElement;
const newLocale = select.value;
document.cookie = `locale=${newLocale}; path=/`;
locale.set(newLocale);
window.location.reload();
};
const submitUpdateTheme: SubmitFunction = ({ action }) => {
const theme = action.searchParams.get('theme');
console.log('theme', theme);
@ -104,7 +110,7 @@
<form class="flex gap-2">
<label class="input input-bordered flex items-center gap-2">
<input type="text" bind:value={query} class="grow" placeholder="Search" />
<input type="text" bind:value={query} class="grow" placeholder={$t('navbar.search')} />
<svg
xmlns="http://www.w3.org/2000/svg"
@ -172,7 +178,7 @@
<form class="flex gap-2">
<label class="input input-bordered flex items-center gap-2">
<input type="text" bind:value={query} class="grow" placeholder="Search" />
<input type="text" bind:value={query} class="grow" placeholder={$t('navbar.search')} />
<svg
xmlns="http://www.w3.org/2000/svg"
@ -206,41 +212,55 @@
tabindex="0"
class="dropdown-content bg-neutral text-neutral-content z-[1] menu p-2 shadow rounded-box w-52"
>
<button class="btn" on:click={() => (isAboutModalOpen = true)}>About AdventureLog</button>
<button class="btn" on:click={() => (isAboutModalOpen = true)}>{$t('navbar.about')}</button>
<button
class="btn btn-sm mt-2"
on:click={() => (window.location.href = 'https://docs.adventurelog.app/')}
>Documentation</button
>{$t('navbar.documentation')}</button
>
<button
class="btn btn-sm mt-2"
on:click={() => (window.location.href = 'https://discord.gg/wRbQ9Egr8C')}>Discord</button
on:click={() => (window.location.href = 'https://discord.gg/wRbQ9Egr8C')}
>{$t('navbar.discord')}</button
>
<p class="font-bold m-4 text-lg">Theme Selection</p>
<p class="font-bold m-4 text-lg">{$t('navbar.theme_selection')}</p>
<form method="POST" use:enhance={submitUpdateTheme}>
<li>
<button formaction="/?/setTheme&theme=light"
>Light<WeatherSunny class="w-6 h-6" />
>{$t('navbar.themes.light')}<WeatherSunny class="w-6 h-6" />
</button>
</li>
<li>
<button formaction="/?/setTheme&theme=dark">Dark<WeatherNight class="w-6 h-6" /></button
<button formaction="/?/setTheme&theme=dark"
>{$t('navbar.themes.dark')}<WeatherNight class="w-6 h-6" /></button
>
</li>
<li>
<button formaction="/?/setTheme&theme=night"
>Night<WeatherNight class="w-6 h-6" /></button
>{$t('navbar.themes.night')}<WeatherNight class="w-6 h-6" /></button
>
</li>
<li>
<button formaction="/?/setTheme&theme=forest">Forest<Forest class="w-6 h-6" /></button>
<button formaction="/?/setTheme&theme=forest"
>{$t('navbar.themes.forest')}<Forest class="w-6 h-6" /></button
>
<button formaction="/?/setTheme&theme=aestheticLight"
>Aesthetic Light<PaletteOutline class="w-6 h-6" /></button
>{$t('navbar.themes.aestetic-light')}<PaletteOutline class="w-6 h-6" /></button
>
<button formaction="/?/setTheme&theme=aestheticDark"
>Aesthetic Dark<PaletteOutline class="w-6 h-6" /></button
>{$t('navbar.themes.aestetic-dark')}<PaletteOutline class="w-6 h-6" /></button
>
<button formaction="/?/setTheme&theme=aqua">Aqua<Water class="w-6 h-6" /></button>
<button formaction="/?/setTheme&theme=aqua"
>{$t('navbar.themes.aqua')}<Water class="w-6 h-6" /></button
>
<form method="POST" use:enhance>
<select class="select" on:change={submitLocaleChange} bind:value={$locale}>
{#each $locales as loc}
<option value={loc}>{loc}</option>
{/each}
</select>
<input type="hidden" name="locale" value={$locale} />
</form>
</li>
</form>
</ul>

View file

@ -253,15 +253,6 @@ export let ADVENTURE_TYPES = [
{ type: 'other', label: 'Other' }
];
export function typeToString(type: string) {
const typeObj = ADVENTURE_TYPES.find((t) => t.type === type);
if (typeObj) {
return typeObj.label;
} else {
return 'Unknown';
}
}
/**
* Checks if an adventure has been visited.
*

View file

View file

@ -0,0 +1,93 @@
{
"about": {
"about": "Um",
"close": "Schließen",
"license": "Lizenziert unter der GPL-3.0-Lizenz.",
"message": "Hergestellt mit ❤️ in den Vereinigten Staaten.",
"nominatim_1": "Standortsuche und Geokodierung werden bereitgestellt von",
"nominatim_2": "Ihre Daten werden unter der ODbL-Lizenz lizenziert.",
"oss_attributions": "Open-Source-Zuschreibungen",
"other_attributions": "Weitere Hinweise finden Sie in der README-Datei.",
"source_code": "Quellcode"
},
"adventures": {
"activities": {
"activity": "Aktivität 🏄",
"art_museums": "Kunst",
"attraction": "Attraktion 🎢",
"culture": "Kultur 🎭",
"dining": "Essen 🍽️",
"event": "Veranstaltung 🎉",
"festivals": "Feste 🎪",
"fitness": "Fitness 🏋️",
"general": "Allgemein 🌍",
"hiking": "Wandern 🥾",
"historical_sites": "Historische Stätten 🏛️",
"lodging": "Unterkunft 🛌",
"music_concerts": "Musik",
"nightlife": "Nachtleben 🌃",
"other": "Andere",
"outdoor": "Draußen 🏞️",
"shopping": "Einkaufen 🛍️",
"spiritual_journeys": "Spirituelle Reisen 🧘‍♀️",
"transportation": "Transport 🚗",
"volunteer_work": "Freiwilligenarbeit 🤝",
"water_sports": "Wassersport 🚤",
"wildlife": "Wildtiere 🦒"
},
"add_to_collection": "Zur Sammlung hinzufügen",
"adventure_delete_confirm": "Sind Sie sicher, dass Sie dieses Abenteuer löschen möchten? \nDiese Aktion kann nicht rückgängig gemacht werden.",
"collection_link_error": "Fehler beim Verknüpfen des Abenteuers mit der Sammlung",
"collection_link_success": "Abenteuer erfolgreich mit Sammlung verknüpft!",
"collection_remove_error": "Beim Entfernen des Abenteuers aus der Sammlung ist ein Fehler aufgetreten",
"collection_remove_success": "Abenteuer erfolgreich aus der Sammlung entfernt!",
"delete": "Löschen",
"edit_adventure": "Abenteuer bearbeiten",
"no_image_found": "Kein Bild gefunden",
"open_details": "Details öffnen",
"remove_from_collection": "Aus der Sammlung entfernen"
},
"home": {
"desc_1": "Entdecken, planen und erkunden Sie mit Leichtigkeit",
"desc_2": "AdventureLog wurde entwickelt, um Ihre Reise zu vereinfachen und Ihnen die Tools und Ressourcen zur Verfügung zu stellen, mit denen Sie Ihr nächstes unvergessliches Abenteuer planen, packen und navigieren können.",
"feature_1": "Reisetagebuch",
"feature_1_desc": "Behalten Sie den Überblick über Ihre Abenteuer mit einem personalisierten Reisetagebuch und teilen Sie Ihre Erlebnisse mit Freunden und Familie.",
"feature_2": "Reiseplanung",
"feature_3": "Reisekarte",
"feature_3_desc": "Sehen Sie sich Ihre Reisen rund um die Welt mit einer interaktiven Karte an und erkunden Sie neue Reiseziele.",
"go_to": "Gehen Sie zu AdventureLog",
"hero_1": "Entdecken Sie die aufregendsten Abenteuer der Welt",
"hero_2": "Entdecken und planen Sie Ihr nächstes Abenteuer mit AdventureLog. \nEntdecken Sie atemberaubende Reiseziele, erstellen Sie individuelle Reiserouten und bleiben Sie unterwegs in Verbindung.",
"key_features": "Hauptmerkmale"
},
"navbar": {
"about": "Über AdventureLog",
"adventures": "Abenteuer",
"collections": "Sammlungen",
"discord": "Zwietracht",
"documentation": "Dokumentation",
"greeting": "Hallo",
"login": "Login",
"logout": "Abmelden",
"map": "Karte",
"my_activities": "Meine Aktivitäten",
"my_adventures": "Meine Abenteuer",
"profile": "Profil",
"search": "Suchen",
"settings": "Einstellungen",
"shared_with_me": "Mit mir geteilt",
"signup": "Melden Sie sich an",
"theme_selection": "Themenauswahl",
"themes": {
"aestetic-dark": "Ästhetisches Dunkel",
"aestetic-light": "Ästhetisches Licht",
"aqua": "Aqua",
"dark": "Dunkel",
"forest": "Wald",
"light": "Licht",
"night": "Nacht"
},
"users": "Benutzer",
"worldtravel": "Weltreisen"
}
}

View file

@ -14,7 +14,20 @@
"my_activities": "My Activities",
"shared_with_me": "Shared With Me",
"settings": "Settings",
"logout": "Logout"
"logout": "Logout",
"about": "About AdventureLog",
"documentation": "Documentation",
"discord": "Discord",
"theme_selection": "Theme Selection",
"themes": {
"light": "Light",
"dark": "Dark",
"night": "Night",
"forest": "Forest",
"aestetic-dark": "Aestetic Dark",
"aestetic-light": "Aestetic Light",
"aqua": "Aqua"
}
},
"about": {
"about": "About",
@ -40,5 +53,42 @@
"feature_2_desc": "Easily create custom itineraries and get a day-by-day breakdown of your trip.",
"feature_3": "Travel Map",
"feature_3_desc": "View your travels throughout the world with an interactive map and explore new destinations."
},
"adventures": {
"collection_remove_success": "Adventure removed from collection successfully!",
"collection_remove_error": "Error removing adventure from collection",
"collection_link_success": "Adventure linked to collection successfully!",
"no_image_found": "No image found",
"collection_link_error": "Error linking adventure to collection",
"adventure_delete_confirm": "Are you sure you want to delete this adventure? This action cannot be undone.",
"open_details": "Open Details",
"edit_adventure": "Edit Adventure",
"remove_from_collection": "Remove from Collection",
"add_to_collection": "Add to Collection",
"delete": "Delete",
"activities": {
"general": "General 🌍",
"outdoor": "Outdoor 🏞️",
"lodging": "Lodging 🛌",
"dining": "Dining 🍽️",
"activity": "Activity 🏄",
"attraction": "Attraction 🎢",
"shopping": "Shopping 🛍️",
"nightlife": "Nightlife 🌃",
"event": "Event 🎉",
"transportation": "Transportation 🚗",
"culture": "Culture 🎭",
"water_sports": "Water Sports 🚤",
"hiking": "Hiking 🥾",
"wildlife": "Wildlife 🦒",
"historical_sites": "Historical Sites 🏛️",
"music_concerts": "Music & Concerts 🎶",
"fitness": "Fitness 🏋️",
"art_museums": "Art & Museums 🎨",
"festivals": "Festivals 🎪",
"spiritual_journeys": "Spiritual Journeys 🧘‍♀️",
"volunteer_work": "Volunteer Work 🤝",
"other": "Other"
}
}
}

View file

@ -2,12 +2,32 @@
"navbar": {
"adventures": "Aventuras",
"collections": "Colecciones",
"worldtravel": "Viaje Mundial",
"worldtravel": "Viajar por el Mundo",
"map": "Mapa",
"users": "Usuarios",
"login": "Iniciar sesión",
"login": "Iniciar Sesión",
"signup": "Registrarse",
"search": "Buscar"
"search": "Buscar",
"profile": "Perfil",
"greeting": "Hola",
"my_adventures": "Mis Aventuras",
"my_activities": "Mis Actividades",
"shared_with_me": "Compartido Conmigo",
"settings": "Configuraciones",
"logout": "Cerrar Sesión",
"about": "Acerca de AdventureLog",
"documentation": "Documentación",
"discord": "Discord",
"theme_selection": "Selección de Tema",
"themes": {
"light": "Claro",
"dark": "Oscuro",
"night": "Noche",
"forest": "Bosque",
"aestetic-dark": "Estético Oscuro",
"aestetic-light": "Estético Claro",
"aqua": "Aqua"
}
},
"about": {
"about": "Acerca de",
@ -15,23 +35,60 @@
"source_code": "Código Fuente",
"message": "Hecho con ❤️ en los Estados Unidos.",
"oss_attributions": "Atribuciones de Código Abierto",
"nominatim_1": "La búsqueda de ubicación y la geocodificación son proporcionadas por",
"nominatim_1": "La búsqueda de ubicaciones y geocodificación es proporcionada por",
"nominatim_2": "Sus datos están licenciados bajo la licencia ODbL.",
"other_attributions": "Atribuciones adicionales se pueden encontrar en el archivo README.",
"close": "Cerrar"
},
"home": {
"hero_1": "Descubre las Aventuras Más Emocionantes del Mundo",
"hero_2": "Descubre y planifica tu próxima aventura con AdventureLog. Explora destinos impresionantes, crea itinerarios personalizados y mantente conectado sobre la marcha.",
"hero_2": "Descubre y planifica tu próxima aventura con AdventureLog. Explora destinos impresionantes, crea itinerarios personalizados y mantente conectado en todo momento.",
"go_to": "Ir a AdventureLog",
"key_features": "Características Clave",
"desc_1": "Descubre, Planifica y Explora con Facilidad",
"desc_2": "AdventureLog está diseñado para simplificar tu viaje, proporcionándote las herramientas y recursos para planificar, empacar y navegar tu próxima aventura inolvidable.",
"feature_1": "Diario de Viajes",
"feature_1_desc": "Lleva un registro de tus aventuras con un diario de viajes personalizado y comparte tus experiencias con amigos y familiares.",
"desc_1": "Descubre, Planifica y Explora Fácilmente",
"desc_2": "AdventureLog está diseñado para simplificar tu viaje, brindándote las herramientas y recursos para planificar, empacar y navegar tu próxima aventura inolvidable.",
"feature_1": "Registro de Viajes",
"feature_1_desc": "Mantén un registro de tus aventuras con un diario de viaje personalizado y comparte tus experiencias con amigos y familiares.",
"feature_2": "Planificación de Viajes",
"feature_2_desc": "Crea fácilmente itinerarios personalizados y obtén un desglose diario de tu viaje.",
"feature_3": "Mapa de Viajes",
"feature_3_desc": "Ve tus viajes por todo el mundo con un mapa interactivo y explora nuevos destinos."
"feature_3": "Mapa de Viaje",
"feature_3_desc": "Visualiza tus viajes por el mundo con un mapa interactivo y explora nuevos destinos."
},
"adventures": {
"collection_remove_success": "¡Aventura eliminada de la colección con éxito!",
"collection_remove_error": "Error al eliminar la aventura de la colección",
"collection_link_success": "¡Aventura vinculada a la colección con éxito!",
"collection_link_error": "Error al vincular la aventura a la colección",
"adventure_delete_confirm": "¿Estás seguro de que quieres eliminar esta aventura? Esta acción no se puede deshacer.",
"open_details": "Abrir Detalles",
"edit_adventure": "Editar Aventura",
"remove_from_collection": "Eliminar de la Colección",
"add_to_collection": "Añadir a la Colección",
"delete": "Eliminar",
"activities": {
"activity": "Actividad 🏄",
"art_museums": "Arte",
"attraction": "Atracción 🎢",
"culture": "Cultura 🎭",
"dining": "Cenar 🍽️",
"event": "Evento 🎉",
"festivals": "Festivales 🎪",
"fitness": "Fitness 🏋️",
"general": "Generales 🌍",
"hiking": "Senderismo 🥾",
"historical_sites": "Sitios Históricos 🏛️",
"lodging": "Alojamiento 🛌",
"music_concerts": "Música",
"nightlife": "Vida nocturna 🌃",
"other": "Otro",
"outdoor": "Al aire libre 🏞️",
"shopping": "Compras 🛍️",
"spiritual_journeys": "Viajes espirituales 🧘‍♀️",
"transportation": "Transporte 🚗",
"volunteer_work": "Trabajo voluntario 🤝",
"water_sports": "Deportes acuáticos 🚤",
"wildlife": "Vida silvestre 🦒"
},
"no_image_found": "No se encontró ninguna imagen"
}
}

View file

@ -0,0 +1,90 @@
{
"about": {
"about": "À propos",
"close": "Fermer",
"license": "Sous licence GPL-3.0.",
"message": "Fabriqué avec ❤️ aux États-Unis.",
"nominatim_1": "La recherche de localisation et le géocodage sont fournis par",
"nominatim_2": "Leurs données sont sous licence ODbL.",
"oss_attributions": "Attributions Open Source",
"other_attributions": "Des attributions supplémentaires peuvent être trouvées dans le fichier README.",
"source_code": "Code source"
},
"adventures": {
"activities": {
"activity": "Activité 🏄",
"art_museums": "Art",
"attraction": "Attraction 🎢",
"culture": "Culturel 🎭",
"dining": "Restauration 🍽️",
"event": "Événement 🎉",
"festivals": "Fêtes 🎪",
"fitness": "Remise en forme 🏋️",
"general": "Général 🌍",
"hiking": "Randonnée 🥾",
"historical_sites": "Sites historiques 🏛️",
"lodging": "Hébergement 🛌",
"music_concerts": "Musique",
"nightlife": "Vie nocturne 🌃",
"other": "Autre",
"outdoor": "En plein air 🏞️",
"shopping": "Shopping 🛍️",
"spiritual_journeys": "Voyages spirituels 🧘‍♀️",
"transportation": "Transport 🚗",
"volunteer_work": "Travail bénévole 🤝",
"water_sports": "Sports nautiques 🚤",
"wildlife": "Faune 🦒"
},
"add_to_collection": "Ajouter à la collection",
"adventure_delete_confirm": "Êtes-vous sûr de vouloir supprimer cette aventure ? \nCette action ne peut pas être annulée.",
"collection_link_error": "Erreur lors de la liaison de l'aventure à la collection",
"collection_link_success": "Aventure liée à la collection avec succès !",
"collection_remove_error": "Erreur lors de la suppression de l'aventure de la collection",
"collection_remove_success": "Aventure supprimée de la collection avec succès !",
"delete": "Supprimer",
"edit_adventure": "Modifier l'aventure",
"no_image_found": "Aucune image trouvée",
"open_details": "Ouvrir les détails",
"remove_from_collection": "Supprimer de la collection"
},
"home": {
"desc_1": "Découvrez, planifiez et explorez en toute simplicité",
"desc_2": "AdventureLog est conçu pour simplifier votre voyage, en vous fournissant les outils et les ressources nécessaires pour planifier, préparer et naviguer dans votre prochaine aventure inoubliable.",
"feature_1": "Carnet de voyage",
"feature_1_desc": "Gardez une trace de vos aventures avec un carnet de voyage personnalisé et partagez vos expériences avec vos amis et votre famille.",
"feature_2": "Planification du voyage",
"feature_2_desc": "Créez facilement des itinéraires personnalisés et obtenez un aperçu quotidien de votre voyage.",
"feature_3": "Carte de voyage",
"feature_3_desc": "Visualisez vos voyages à travers le monde avec une carte interactive et explorez de nouvelles destinations.",
"go_to": "Aller au journal d'aventure",
"hero_1": "Découvrez les aventures les plus palpitantes du monde",
"hero_2": "Découvrez et planifiez votre prochaine aventure avec AdventureLog. \nExplorez des destinations à couper le souffle, créez des itinéraires personnalisés et restez connecté lors de vos déplacements.",
"key_features": "Principales fonctionnalités"
},
"navbar": {
"about": "À propos de AdventureLog",
"adventures": "Aventures",
"collections": "Collections",
"discord": "Discorde",
"documentation": "Documentation",
"greeting": "Salut",
"login": "Se connecter",
"logout": "Déconnexion",
"map": "Carte",
"my_activities": "Mes activités",
"my_adventures": "Mes aventures",
"profile": "Profil",
"search": "Recherche",
"settings": "Paramètres",
"shared_with_me": "Partagé avec moi",
"signup": "S'inscrire",
"theme_selection": "Sélection de thèmes",
"themes": {
"forest": "Forêt",
"light": "Lumière",
"night": "Nuit"
},
"users": "Utilisateurs",
"worldtravel": "Voyage dans le monde"
}
}

View file

@ -6,6 +6,8 @@
// Register your translations for each locale
register('en', () => import('../locales/en.json'));
register('es', () => import('../locales/es.json'));
register('fr', () => import('../locales/fr.json'));
register('de', () => import('../locales/de.json'));
if (browser) {
init({
@ -37,3 +39,11 @@
<Toast />
<slot />
{/await}
<svelte:head>
<title>AdventureLog</title>
<meta
name="description"
content="Embark, explore, remember with AdventureLog. AdventureLog is the ultimate travel companion."
/>
</svelte:head>

View file

@ -49,5 +49,15 @@ export const actions: Actions = {
} else {
return redirect(302, '/');
}
},
setLocale: async ({ url, cookies }) => {
const locale = url.searchParams.get('locale');
// change the theme only if it is one of the allowed themes
if (locale && ['en', 'es'].includes(locale)) {
cookies.set('locale', locale, {
path: '/',
maxAge: 60 * 60 * 24 * 365
});
}
}
};

View file

@ -26,6 +26,13 @@
let resultsPerPage: number = 25;
let count = data.props.count || 0;
$: {
if (count != adventures.length) {
count = adventures.length;
}
}
let totalPages = Math.ceil(count / resultsPerPage);
let currentPage: number = 1;

View file

@ -5,6 +5,7 @@
import { goto } from '$app/navigation';
import Lost from '$lib/assets/undraw_lost.svg';
import { DefaultMarker, MapLibre, Popup } from 'svelte-maplibre';
import { t } from 'svelte-i18n';
export let data: PageData;
console.log(data);
@ -22,10 +23,8 @@
let image_url: string | null = null;
import ClipboardList from '~icons/mdi/clipboard-list';
import EditAdventure from '$lib/components/AdventureModal.svelte';
import AdventureModal from '$lib/components/AdventureModal.svelte';
import ImageDisplayModal from '$lib/components/ImageDisplayModal.svelte';
import { typeToString } from '$lib';
onMount(() => {
if (data.props.adventure) {
@ -265,7 +264,7 @@
<div>
<p class="text-sm text-muted-foreground">Adventure Type</p>
<p class="text-base font-medium">
{typeToString(adventure.type)}
{$t(`adventures.activities.${adventure.type}`)}
</p>
</div>
{#if data.props.collection}

View file

@ -27,6 +27,12 @@
let totalPages = Math.ceil(count / resultsPerPage);
let currentPage: number = 1;
$: {
if (count != collections.length) {
count = collections.length;
}
}
function handleChangePage() {
return async ({ result }: any) => {
if (result.type === 'success') {

View file

@ -1,7 +1,7 @@
<script>
// @ts-nocheck
import { isAdventureVisited, typeToString } from '$lib';
import { isAdventureVisited } from '$lib';
import AdventureModal from '$lib/components/AdventureModal.svelte';
import {
DefaultMarker,
@ -14,6 +14,7 @@
FillLayer,
SymbolLayer
} from 'svelte-maplibre';
import { t } from 'svelte-i18n';
export let data;
let clickedName = '';
@ -162,7 +163,9 @@
<Popup openOn="click" offset={[0, -10]}>
<div class="text-lg text-black font-bold">{marker.name}</div>
<p class="font-semibold text-black text-md">Visited</p>
<p class="font-semibold text-black text-md">{typeToString(marker.type)}</p>
<p class="font-semibold text-black text-md">
{$t(`adventures.activities.${marker.type}`)}
</p>
{#if marker.visits && marker.visits.length > 0}
<p class="text-black text-sm">
{#each marker.visits as visit}
@ -201,7 +204,9 @@
<Popup openOn="click" offset={[0, -10]}>
<div class="text-lg text-black font-bold">{marker.name}</div>
<p class="font-semibold text-black text-md">Planned</p>
<p class="font-semibold text-black text-md">{typeToString(marker.type)}</p>
<p class="font-semibold text-black text-md">
{$t(`adventures.activities.${marker.type}`)}}
</p>
{#if marker.visits && marker.visits.length > 0}
<p class="text-black text-sm">
{#each marker.visits as visit}