localization v1
This commit is contained in:
1
frontend/src/app.d.ts
vendored
1
frontend/src/app.d.ts
vendored
@@ -16,6 +16,7 @@ declare global {
|
||||
uuid: string;
|
||||
public_profile: boolean;
|
||||
} | null;
|
||||
locale: string;
|
||||
}
|
||||
// interface PageData {}
|
||||
// interface PageState {}
|
||||
|
||||
@@ -107,4 +107,20 @@ export const themeHook: Handle = async ({ event, resolve }) => {
|
||||
return await resolve(event);
|
||||
};
|
||||
|
||||
export const handle = sequence(authHook, themeHook);
|
||||
// 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: '/'
|
||||
});
|
||||
}
|
||||
event.locals.locale = lang; // Store the locale in locals
|
||||
return await resolve(event);
|
||||
};
|
||||
|
||||
export const handle = sequence(authHook, themeHook, i18nHook);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
const dispatch = createEventDispatcher();
|
||||
import { onMount } from 'svelte';
|
||||
let modal: HTMLDialogElement;
|
||||
import { t } from 'svelte-i18n';
|
||||
import { appVersion, copyrightYear, versionChangelog } from '$lib/config';
|
||||
|
||||
onMount(() => {
|
||||
@@ -28,7 +29,7 @@
|
||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||
<div class="modal-box" role="dialog" on:keydown={handleKeydown} tabindex="0">
|
||||
<h3 class="font-bold text-lg">
|
||||
About AdventureLog<span class=" inline-block"
|
||||
{$t('about.about')} AdventureLog<span class=" inline-block"
|
||||
><img src="/favicon.png" alt="Map Logo" class="w-10 -mb-3 ml-2" /></span
|
||||
>
|
||||
</h3>
|
||||
@@ -49,28 +50,30 @@
|
||||
class="text-primary-500 underline">Sean Morley</a
|
||||
>
|
||||
</p>
|
||||
<p class="py-1">Licensed under the GPL-3.0 License.</p>
|
||||
<p class="py-1">{$t('about.license')}</p>
|
||||
<p class="py-1">
|
||||
<a
|
||||
href="https://github.com/seanmorley15/AdventureLog"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-primary-500 underline">Source Code</a
|
||||
class="text-primary-500 underline">{$t('about.source_code')}</a
|
||||
>
|
||||
</p>
|
||||
<p class="py-1">Made with ❤️ in the United States.</p>
|
||||
<p class="py-1">{$t('about.message')}</p>
|
||||
<div class="divider"></div>
|
||||
<h3 class="font-bold text-md">Open Source Attributions</h3>
|
||||
<h3 class="font-bold text-md">{$t('about.oss_attributions')}</h3>
|
||||
<p class="py-1 mb-4">
|
||||
Location Search and Geocoding is provided by <a
|
||||
{$t('about.nominatim_1')}
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-primary-500 underline"
|
||||
href="https://operations.osmfoundation.org/policies/nominatim/">OpenStreepMap</a
|
||||
>. Their data is liscensed under the ODbL license.
|
||||
<br /> Additional attributions can be found in the README file.
|
||||
>. {$t('about.nominatim_2')}
|
||||
<br />
|
||||
{$t('about.other_attributions')}
|
||||
</p>
|
||||
|
||||
<button class="btn btn-primary" on:click={close}>Close</button>
|
||||
<button class="btn btn-primary" on:click={close}>{$t('about.close')}</button>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
export let user: any;
|
||||
|
||||
@@ -28,14 +29,17 @@
|
||||
>
|
||||
<!-- svelte-ignore a11y-missing-attribute -->
|
||||
<!-- svelte-ignore a11y-missing-attribute -->
|
||||
<p class="text-lg ml-4 font-bold">Hi, {user.first_name} {user.last_name}</p>
|
||||
<li><button on:click={() => goto('/profile')}>Profile</button></li>
|
||||
<li><button on:click={() => goto('/adventures')}>My Adventures</button></li>
|
||||
<li><button on:click={() => goto('/activities')}>My Activities</button></li>
|
||||
<li><button on:click={() => goto('/shared')}>Shared With Me</button></li>
|
||||
<li><button on:click={() => goto('/settings')}>User Settings</button></li>
|
||||
<p class="text-lg ml-4 font-bold">
|
||||
{$t('navbar.greeting')}, {user.first_name}
|
||||
{user.last_name}
|
||||
</p>
|
||||
<li><button on:click={() => goto('/profile')}>{$t('navbar.profile')}</button></li>
|
||||
<li><button on:click={() => goto('/adventures')}>{$t('navbar.my_adventures')}</button></li>
|
||||
<li><button on:click={() => goto('/activities')}>{$t('navbar.my_activities')}</button></li>
|
||||
<li><button on:click={() => goto('/shared')}>{$t('navbar.shared_with_me')}</button></li>
|
||||
<li><button on:click={() => goto('/settings')}>{$t('navbar.settings')}</button></li>
|
||||
<form method="post">
|
||||
<li><button formaction="/?/logout">Logout</button></li>
|
||||
<li><button formaction="/?/logout">{$t('navbar.logout')}</button></li>
|
||||
</form>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
import Avatar from './Avatar.svelte';
|
||||
import PaletteOutline from '~icons/mdi/palette-outline';
|
||||
import { page } from '$app/stores';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
let query: string = '';
|
||||
|
||||
@@ -72,28 +73,32 @@
|
||||
>
|
||||
{#if data.user}
|
||||
<li>
|
||||
<button on:click={() => goto('/adventures')}>Adventures</button>
|
||||
<button on:click={() => goto('/adventures')}>{$t('navbar.adventures')}</button>
|
||||
</li>
|
||||
<li>
|
||||
<button on:click={() => goto('/collections')}>Collections</button>
|
||||
<button on:click={() => goto('/collections')}>{$t('navbar.collections')}</button>
|
||||
</li>
|
||||
<li>
|
||||
<button on:click={() => goto('/worldtravel')}>World Travel</button>
|
||||
<button on:click={() => goto('/worldtravel')}>{$t('navbar.worldtravel')}</button>
|
||||
</li>
|
||||
<li>
|
||||
<button on:click={() => goto('/map')}>Map</button>
|
||||
<button on:click={() => goto('/map')}>{$t('navbar.map')}</button>
|
||||
</li>
|
||||
<li>
|
||||
<button on:click={() => goto('/users')}>Users</button>
|
||||
<button on:click={() => goto('/users')}>{$t('navbar.users')}</button>
|
||||
</li>
|
||||
{/if}
|
||||
|
||||
{#if !data.user}
|
||||
<li>
|
||||
<button class="btn btn-primary" on:click={() => goto('/login')}>Login</button>
|
||||
<button class="btn btn-primary" on:click={() => goto('/login')}
|
||||
>{$t('navbar.login')}</button
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<button class="btn btn-primary" on:click={() => goto('/signup')}>Signup</button>
|
||||
<button class="btn btn-primary" on:click={() => goto('/signup')}
|
||||
>{$t('navbar.signup')}</button
|
||||
>
|
||||
</li>
|
||||
{/if}
|
||||
|
||||
@@ -114,7 +119,9 @@
|
||||
/>
|
||||
</svg>
|
||||
</label>
|
||||
<button on:click={searchGo} type="submit" class="btn btn-primary">Search</button>
|
||||
<button on:click={searchGo} type="submit" class="btn btn-primary"
|
||||
>{$t('navbar.search')}</button
|
||||
>
|
||||
</form>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -126,17 +133,22 @@
|
||||
<ul class="menu menu-horizontal px-1 gap-2">
|
||||
{#if data.user}
|
||||
<li>
|
||||
<button class="btn btn-neutral" on:click={() => goto('/adventures')}>Adventures</button>
|
||||
</li>
|
||||
<li>
|
||||
<button class="btn btn-neutral" on:click={() => goto('/collections')}>Collections</button>
|
||||
</li>
|
||||
<li>
|
||||
<button class="btn btn-neutral" on:click={() => goto('/worldtravel')}>World Travel</button
|
||||
<button class="btn btn-neutral" on:click={() => goto('/adventures')}
|
||||
>{$t('navbar.adventures')}</button
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<button class="btn btn-neutral" on:click={() => goto('/map')}>Map</button>
|
||||
<button class="btn btn-neutral" on:click={() => goto('/collections')}
|
||||
>{$t('navbar.collections')}</button
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<button class="btn btn-neutral" on:click={() => goto('/worldtravel')}
|
||||
>{$t('navbar.worldtravel')}</button
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<button class="btn btn-neutral" on:click={() => goto('/map')}>{$t('navbar.map')}</button>
|
||||
</li>
|
||||
<li>
|
||||
<button class="btn btn-neutral" on:click={() => goto('/users')}
|
||||
@@ -147,10 +159,14 @@
|
||||
|
||||
{#if !data.user}
|
||||
<li>
|
||||
<button class="btn btn-primary" on:click={() => goto('/login')}>Login</button>
|
||||
<button class="btn btn-primary" on:click={() => goto('/login')}
|
||||
>{$t('navbar.login')}</button
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<button class="btn btn-primary" on:click={() => goto('/signup')}>Signup</button>
|
||||
<button class="btn btn-primary" on:click={() => goto('/signup')}
|
||||
>{$t('navbar.signup')}</button
|
||||
>
|
||||
</li>
|
||||
{/if}
|
||||
|
||||
@@ -171,7 +187,9 @@
|
||||
/>
|
||||
</svg>
|
||||
</label>
|
||||
<button on:click={searchGo} type="submit" class="btn btn-neutral">Search</button>
|
||||
<button on:click={searchGo} type="submit" class="btn btn-neutral"
|
||||
>{$t('navbar.search')}</button
|
||||
>
|
||||
</form>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
44
frontend/src/locales/en.json
Normal file
44
frontend/src/locales/en.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"navbar": {
|
||||
"adventures": "Adventures",
|
||||
"collections": "Collections",
|
||||
"worldtravel": "World Travel",
|
||||
"map": "Map",
|
||||
"users": "Users",
|
||||
"login": "Login",
|
||||
"signup": "Signup",
|
||||
"search": "Search",
|
||||
"profile": "Profile",
|
||||
"greeting": "Hi",
|
||||
"my_adventures": "My Adventures",
|
||||
"my_activities": "My Activities",
|
||||
"shared_with_me": "Shared With Me",
|
||||
"settings": "Settings",
|
||||
"logout": "Logout"
|
||||
},
|
||||
"about": {
|
||||
"about": "About",
|
||||
"license": "Licensed under the GPL-3.0 License.",
|
||||
"source_code": "Source Code",
|
||||
"message": "Made with ❤️ in the United States.",
|
||||
"oss_attributions": "Open Source Attributions",
|
||||
"nominatim_1": "Location Search and Geocoding is provided by",
|
||||
"nominatim_2": "Their data is liscensed under the ODbL license.",
|
||||
"other_attributions": "Additional attributions can be found in the README file.",
|
||||
"close": "Close"
|
||||
},
|
||||
"home": {
|
||||
"hero_1": "Discover the World's Most Thrilling Adventures",
|
||||
"hero_2": "Discover and plan your next adventure with AdventureLog. Explore breathtaking destinations, create custom itineraries, and stay connected on the go.",
|
||||
"go_to": "Go To AdventureLog",
|
||||
"key_features": "Key Features",
|
||||
"desc_1": "Discover, Plan, and Explore with Ease",
|
||||
"desc_2": "AdventureLog is designed to simplify your journey, providing you with the tools and resources to plan, pack, and navigate your next unforgettable adventure.",
|
||||
"feature_1": "Travel Log",
|
||||
"feature_1_desc": "Keep track of your adventures with a personalized travel log and share your experiences with friends and family.",
|
||||
"feature_2": "Trip Planning",
|
||||
"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."
|
||||
}
|
||||
}
|
||||
37
frontend/src/locales/es.json
Normal file
37
frontend/src/locales/es.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"navbar": {
|
||||
"adventures": "Aventuras",
|
||||
"collections": "Colecciones",
|
||||
"worldtravel": "Viaje Mundial",
|
||||
"map": "Mapa",
|
||||
"users": "Usuarios",
|
||||
"login": "Iniciar sesión",
|
||||
"signup": "Registrarse",
|
||||
"search": "Buscar"
|
||||
},
|
||||
"about": {
|
||||
"about": "Acerca de",
|
||||
"license": "Licenciado bajo la Licencia GPL-3.0.",
|
||||
"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_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.",
|
||||
"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.",
|
||||
"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."
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,15 @@
|
||||
import { locale } from 'svelte-i18n';
|
||||
import type { LayoutServerLoad } from './$types';
|
||||
|
||||
export const load: LayoutServerLoad = async (event) => {
|
||||
if (event.locals.user) {
|
||||
return {
|
||||
user: event.locals.user
|
||||
user: event.locals.user,
|
||||
locale: event.locals.locale
|
||||
};
|
||||
}
|
||||
return {
|
||||
user: null
|
||||
user: null,
|
||||
locale: event.locals.locale
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,11 +1,39 @@
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { browser } from '$app/environment';
|
||||
import { register, init, locale, waitLocale } from 'svelte-i18n';
|
||||
export let data;
|
||||
|
||||
// Register your translations for each locale
|
||||
register('en', () => import('../locales/en.json'));
|
||||
register('es', () => import('../locales/es.json'));
|
||||
|
||||
if (browser) {
|
||||
init({
|
||||
fallbackLocale: navigator.language.split('-')[0],
|
||||
initialLocale: data.locale
|
||||
});
|
||||
// get the locale cookie if it exists and set it as the initial locale if it exists
|
||||
const localeCookie = document.cookie
|
||||
.split(';')
|
||||
.find((cookie) => cookie.trim().startsWith('locale='));
|
||||
if (localeCookie) {
|
||||
const localeValue = localeCookie.split('=')[1];
|
||||
locale.set(localeValue);
|
||||
}
|
||||
}
|
||||
|
||||
import Navbar from '$lib/components/Navbar.svelte';
|
||||
import Toast from '$lib/components/Toast.svelte';
|
||||
import 'tailwindcss/tailwind.css';
|
||||
export let data;
|
||||
|
||||
// Create a promise that resolves when the locale is ready
|
||||
export const localeLoaded = browser ? waitLocale() : Promise.resolve();
|
||||
</script>
|
||||
|
||||
<Navbar {data} />
|
||||
<Toast />
|
||||
|
||||
<slot></slot>
|
||||
{#await localeLoaded}
|
||||
<!-- You can add a loading indicator here if needed -->
|
||||
{:then}
|
||||
<Navbar {data} />
|
||||
<Toast />
|
||||
<slot />
|
||||
{/await}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import AdventureOverlook from '$lib/assets/AdventureOverlook.webp';
|
||||
import MapWithPins from '$lib/assets/MapWithPins.webp';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
export let data;
|
||||
</script>
|
||||
@@ -18,35 +19,38 @@
|
||||
class="text-3xl font-bold tracking-tighter sm:text-5xl xl:text-6xl/none bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent pb-4"
|
||||
>
|
||||
{data.user.first_name.charAt(0).toUpperCase() + data.user.first_name.slice(1)},
|
||||
Discover the World's Most Thrilling Adventures
|
||||
{$t('home.hero_1')}
|
||||
</h1>
|
||||
{:else}
|
||||
<h1
|
||||
class="text-3xl font-bold tracking-tighter sm:text-5xl xl:text-6xl/none bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent pb-4"
|
||||
>
|
||||
Discover the World's Most Thrilling Adventures
|
||||
{$t('home.hero_1')}
|
||||
</h1>
|
||||
{/if}
|
||||
{:else}
|
||||
<h1
|
||||
class="text-3xl font-bold tracking-tighter sm:text-5xl xl:text-6xl/none bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent pb-4"
|
||||
>
|
||||
Discover the World's Most Thrilling Adventures
|
||||
{$t('home.hero_1')}
|
||||
</h1>
|
||||
{/if}
|
||||
<p class="max-w-[600px] text-gray-500 md:text-xl dark:text-gray-400">
|
||||
Discover and plan your next adventure with AdventureLog. Explore breathtaking
|
||||
destinations, create custom itineraries, and stay connected on the go.
|
||||
{$t('home.hero_2')}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 min-[400px]:flex-row">
|
||||
{#if data.user}
|
||||
<button on:click={() => goto('/adventures')} class="btn btn-primary">
|
||||
Go To AdventureLog
|
||||
{$t('home.go_to')}
|
||||
</button>
|
||||
{:else}
|
||||
<button on:click={() => goto('/login')} class="btn btn-primary"> Log In </button>
|
||||
<button on:click={() => goto('/signup')} class="btn btn-neutral"> Sign Up </button>
|
||||
<button on:click={() => goto('/login')} class="btn btn-primary">
|
||||
{$t('navbar.login')}
|
||||
</button>
|
||||
<button on:click={() => goto('/signup')} class="btn btn-neutral">
|
||||
{$t('navbar.signup')}
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
@@ -69,18 +73,17 @@
|
||||
<div
|
||||
class="inline-block rounded-lg bg-gray-100 px-3 py-1 text-md dark:bg-gray-800 dark:text-gray-400"
|
||||
>
|
||||
Key Features
|
||||
{$t('home.key_features')}
|
||||
</div>
|
||||
<h2
|
||||
class="text-3xl font-bold tracking-tighter sm:text-5xl bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent"
|
||||
>
|
||||
Discover, Plan, and Explore with Ease
|
||||
{$t('home.desc_1')}
|
||||
</h2>
|
||||
<p
|
||||
class="max-w-[900px] text-gray-500 md:text-xl/relaxed lg:text-base/relaxed xl:text-xl/relaxed dark:text-gray-400"
|
||||
>
|
||||
AdventureLog is designed to simplify your journey, providing you with the tools and
|
||||
resources to plan, pack, and navigate your next unforgettable adventure.
|
||||
{$t('home.desc_2')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -97,27 +100,25 @@
|
||||
<ul class="grid gap-6">
|
||||
<li>
|
||||
<div class="grid gap-1">
|
||||
<h3 class="text-xl font-bold dark:text-gray-400">Travel Log</h3>
|
||||
<h3 class="text-xl font-bold dark:text-gray-400">{$t('home.feature_1')}</h3>
|
||||
<p class="text-gray-500 dark:text-gray-400">
|
||||
Keep track of your adventures with a personalized travel log and share your
|
||||
experiences with friends and family.
|
||||
{$t('home.feature_1_desc')}
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="grid gap-1">
|
||||
<h3 class="text-xl font-bold dark:text-gray-400">Trip Planning</h3>
|
||||
<h3 class="text-xl font-bold dark:text-gray-400">{$t('home.feature_2')}</h3>
|
||||
<p class="text-gray-500 dark:text-gray-400">
|
||||
Easily create custom itineraries and get a day-by-day breakdown of your trip.
|
||||
{$t('home.feature_2_desc')}
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="grid gap-1">
|
||||
<h3 class="text-xl font-bold dark:text-gray-400">Travel Map</h3>
|
||||
<h3 class="text-xl font-bold dark:text-gray-400">{$t('home.feature_3')}</h3>
|
||||
<p class="text-gray-500 dark:text-gray-400">
|
||||
View your travels throughout the world with an interactive map and explore new
|
||||
destinations.
|
||||
{$t('home.feature_3_desc')}
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
Reference in New Issue
Block a user