From c461f7b1059220850aae4f8d240508142f242951 Mon Sep 17 00:00:00 2001 From: Sean Morley <98704938+seanmorley15@users.noreply.github.com> Date: Thu, 26 Jun 2025 10:23:37 -0400 Subject: [PATCH] Import and Export Functionality (#698) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(backup): add BackupViewSet for data export and import functionality * Fixed frontend returning corrupt binary data * feat(import): enhance import functionality with confirmation check and improved city/region/country handling * Potential fix for code scanning alert no. 29: Information exposure through an exception Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Refactor response handling to use arrayBuffer instead of bytes * Refactor image cleanup command to use LocationImage model and update import/export view to include backup and restore functionality * Update backup export versioning and improve data restore warning message * Enhance image navigation and localization support in modal components * Refactor location handling in Immich integration components for consistency * Enhance backup and restore functionality with improved localization and error handling * Improve accessibility by adding 'for' attribute to backup file input label --------- Co-authored-by: Christian Zäske Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .../management/commands/image_cleanup.py | 6 +- backend/server/adventures/urls.py | 3 +- backend/server/adventures/views/__init__.py | 3 +- .../adventures/views/import_export_view.py | 630 +++++++ backend/server/integrations/views.py | 4 +- backend/server/main/settings.py | 3 + frontend/src/lib/components/AboutModal.svelte | 293 ++-- .../src/lib/components/CardCarousel.svelte | 100 +- .../lib/components/ImageDisplayModal.svelte | 259 ++- .../src/lib/components/ImageFetcher.svelte | 91 - .../src/lib/components/ImmichSelect.svelte | 10 +- .../src/lib/components/LocationCard.svelte | 2 +- .../src/lib/components/LocationModal.svelte | 2 +- frontend/src/lib/components/TOTPModal.svelte | 266 ++- frontend/src/lib/config.ts | 2 +- frontend/src/locales/de.json | 47 +- frontend/src/locales/en.json | 1487 +++++++++-------- frontend/src/locales/es.json | 47 +- frontend/src/locales/fr.json | 47 +- frontend/src/locales/it.json | 47 +- frontend/src/locales/ko.json | 47 +- frontend/src/locales/nl.json | 47 +- frontend/src/locales/no.json | 47 +- frontend/src/locales/pl.json | 47 +- frontend/src/locales/ru.json | 47 +- frontend/src/locales/sv.json | 47 +- frontend/src/locales/zh.json | 47 +- frontend/src/routes/api/[...path]/+server.ts | 2 +- .../src/routes/locations/[id]/+page.svelte | 33 +- frontend/src/routes/settings/+page.server.ts | 49 +- frontend/src/routes/settings/+page.svelte | 195 ++- 31 files changed, 2843 insertions(+), 1114 deletions(-) create mode 100644 backend/server/adventures/views/import_export_view.py delete mode 100644 frontend/src/lib/components/ImageFetcher.svelte diff --git a/backend/server/adventures/management/commands/image_cleanup.py b/backend/server/adventures/management/commands/image_cleanup.py index 0f5e1ad..c6c7f26 100644 --- a/backend/server/adventures/management/commands/image_cleanup.py +++ b/backend/server/adventures/management/commands/image_cleanup.py @@ -1,7 +1,7 @@ import os from django.core.management.base import BaseCommand from django.conf import settings -from adventures.models import AdventureImage, Attachment +from adventures.models import LocationImage, Attachment from users.models import CustomUser @@ -21,8 +21,8 @@ class Command(BaseCommand): # Get all image and attachment file paths from database used_files = set() - # Get AdventureImage file paths - for img in AdventureImage.objects.all(): + # Get LocationImage file paths + for img in LocationImage.objects.all(): if img.image and img.image.name: used_files.add(os.path.join(settings.MEDIA_ROOT, img.image.name)) diff --git a/backend/server/adventures/urls.py b/backend/server/adventures/urls.py index 717446c..7d165d4 100644 --- a/backend/server/adventures/urls.py +++ b/backend/server/adventures/urls.py @@ -18,7 +18,8 @@ router.register(r'ics-calendar', IcsCalendarGeneratorViewSet, basename='ics-cale router.register(r'search', GlobalSearchView, basename='search') router.register(r'attachments', AttachmentViewSet, basename='attachments') router.register(r'lodging', LodgingViewSet, basename='lodging') -router.register(r'recommendations', RecommendationsViewSet, basename='recommendations') +router.register(r'recommendations', RecommendationsViewSet, basename='recommendations'), +router.register(r'backup', BackupViewSet, basename='backup') urlpatterns = [ # Include the router under the 'api/' prefix diff --git a/backend/server/adventures/views/__init__.py b/backend/server/adventures/views/__init__.py index 4045b45..1ae7b6d 100644 --- a/backend/server/adventures/views/__init__.py +++ b/backend/server/adventures/views/__init__.py @@ -13,4 +13,5 @@ from .transportation_view import * from .global_search_view import * from .attachment_view import * from .lodging_view import * -from .recommendations_view import * \ No newline at end of file +from .recommendations_view import * +from .import_export_view import * \ No newline at end of file diff --git a/backend/server/adventures/views/import_export_view.py b/backend/server/adventures/views/import_export_view.py new file mode 100644 index 0000000..2e88480 --- /dev/null +++ b/backend/server/adventures/views/import_export_view.py @@ -0,0 +1,630 @@ +# views.py +import json +import zipfile +import tempfile +import os +from datetime import datetime +from django.http import HttpResponse +from django.core.files.storage import default_storage +from django.core.files.base import ContentFile +from django.db import transaction +from django.contrib.auth import get_user_model +from rest_framework import viewsets, status +from rest_framework.decorators import action +from rest_framework.response import Response +from rest_framework.parsers import MultiPartParser +from rest_framework.permissions import IsAuthenticated +from django.conf import settings + +from adventures.models import ( + Location, Collection, Transportation, Note, Checklist, ChecklistItem, + LocationImage, Attachment, Category, Lodging, Visit +) +from worldtravel.models import VisitedCity, VisitedRegion, City, Region, Country + +User = get_user_model() + +class BackupViewSet(viewsets.ViewSet): + permission_classes = [IsAuthenticated] + """ + Simple ViewSet for handling backup and import operations + """ + + @action(detail=False, methods=['get']) + def export(self, request): + """ + Export all user data as a ZIP file containing JSON data and files + """ + user = request.user + + # Build export data structure + export_data = { + 'version': settings.ADVENTURELOG_RELEASE_VERSION, + 'export_date': datetime.now().isoformat(), + 'user_email': user.email, + 'user_username': user.username, + 'categories': [], + 'collections': [], + 'locations': [], + 'transportation': [], + 'notes': [], + 'checklists': [], + 'lodging': [], + 'visited_cities': [], + 'visited_regions': [] + } + + # Export Visited Cities + for visited_city in user.visitedcity_set.all(): + export_data['visited_cities'].append({ + 'city': visited_city.city.id, + }) + + # Export Visited Regions + for visited_region in user.visitedregion_set.all(): + export_data['visited_regions'].append({ + 'region': visited_region.region.id, + }) + + # Export Categories + for category in user.category_set.all(): + export_data['categories'].append({ + 'name': category.name, + 'display_name': category.display_name, + 'icon': category.icon, + }) + + # Export Collections + for idx, collection in enumerate(user.collection_set.all()): + export_data['collections'].append({ + 'export_id': idx, # Add unique identifier for this export + 'name': collection.name, + 'description': collection.description, + 'is_public': collection.is_public, + 'start_date': collection.start_date.isoformat() if collection.start_date else None, + 'end_date': collection.end_date.isoformat() if collection.end_date else None, + 'is_archived': collection.is_archived, + 'link': collection.link, + 'shared_with_user_ids': [str(uuid) for uuid in collection.shared_with.values_list('uuid', flat=True)] + }) + + # Create collection name to export_id mapping + collection_name_to_id = {col.name: idx for idx, col in enumerate(user.collection_set.all())} + + # Export locations with related data + for idx, location in enumerate(user.location_set.all()): + location_data = { + 'export_id': idx, # Add unique identifier for this export + 'name': location.name, + 'location': location.location, + 'tags': location.tags, + 'description': location.description, + 'rating': location.rating, + 'link': location.link, + 'is_public': location.is_public, + 'longitude': str(location.longitude) if location.longitude else None, + 'latitude': str(location.latitude) if location.latitude else None, + 'city': location.city_id, + 'region': location.region_id, + 'country': location.country_id, + 'category_name': location.category.name if location.category else None, + 'collection_export_ids': [collection_name_to_id[col_name] for col_name in location.collections.values_list('name', flat=True) if col_name in collection_name_to_id], + 'visits': [], + 'images': [], + 'attachments': [] + } + + # Add visits + for visit in location.visits.all(): + location_data['visits'].append({ + 'start_date': visit.start_date.isoformat() if visit.start_date else None, + 'end_date': visit.end_date.isoformat() if visit.end_date else None, + 'timezone': visit.timezone, + 'notes': visit.notes + }) + + # Add images + for image in location.images.all(): + image_data = { + 'immich_id': image.immich_id, + 'is_primary': image.is_primary, + 'filename': None + } + if image.image: + image_data['filename'] = image.image.name.split('/')[-1] + location_data['images'].append(image_data) + + # Add attachments + for attachment in location.attachments.all(): + attachment_data = { + 'name': attachment.name, + 'filename': None + } + if attachment.file: + attachment_data['filename'] = attachment.file.name.split('/')[-1] + location_data['attachments'].append(attachment_data) + + export_data['locations'].append(location_data) + + # Export Transportation + for transport in user.transportation_set.all(): + collection_export_id = None + if transport.collection: + collection_export_id = collection_name_to_id.get(transport.collection.name) + + export_data['transportation'].append({ + 'type': transport.type, + 'name': transport.name, + 'description': transport.description, + 'rating': transport.rating, + 'link': transport.link, + 'date': transport.date.isoformat() if transport.date else None, + 'end_date': transport.end_date.isoformat() if transport.end_date else None, + 'start_timezone': transport.start_timezone, + 'end_timezone': transport.end_timezone, + 'flight_number': transport.flight_number, + 'from_location': transport.from_location, + 'origin_latitude': str(transport.origin_latitude) if transport.origin_latitude else None, + 'origin_longitude': str(transport.origin_longitude) if transport.origin_longitude else None, + 'destination_latitude': str(transport.destination_latitude) if transport.destination_latitude else None, + 'destination_longitude': str(transport.destination_longitude) if transport.destination_longitude else None, + 'to_location': transport.to_location, + 'is_public': transport.is_public, + 'collection_export_id': collection_export_id + }) + + # Export Notes + for note in user.note_set.all(): + collection_export_id = None + if note.collection: + collection_export_id = collection_name_to_id.get(note.collection.name) + + export_data['notes'].append({ + 'name': note.name, + 'content': note.content, + 'links': note.links, + 'date': note.date.isoformat() if note.date else None, + 'is_public': note.is_public, + 'collection_export_id': collection_export_id + }) + + # Export Checklists + for checklist in user.checklist_set.all(): + collection_export_id = None + if checklist.collection: + collection_export_id = collection_name_to_id.get(checklist.collection.name) + + checklist_data = { + 'name': checklist.name, + 'date': checklist.date.isoformat() if checklist.date else None, + 'is_public': checklist.is_public, + 'collection_export_id': collection_export_id, + 'items': [] + } + + # Add checklist items + for item in checklist.checklistitem_set.all(): + checklist_data['items'].append({ + 'name': item.name, + 'is_checked': item.is_checked + }) + + export_data['checklists'].append(checklist_data) + + # Export Lodging + for lodging in user.lodging_set.all(): + collection_export_id = None + if lodging.collection: + collection_export_id = collection_name_to_id.get(lodging.collection.name) + + export_data['lodging'].append({ + 'name': lodging.name, + 'type': lodging.type, + 'description': lodging.description, + 'rating': lodging.rating, + 'link': lodging.link, + 'check_in': lodging.check_in.isoformat() if lodging.check_in else None, + 'check_out': lodging.check_out.isoformat() if lodging.check_out else None, + 'timezone': lodging.timezone, + 'reservation_number': lodging.reservation_number, + 'price': str(lodging.price) if lodging.price else None, + 'latitude': str(lodging.latitude) if lodging.latitude else None, + 'longitude': str(lodging.longitude) if lodging.longitude else None, + 'location': lodging.location, + 'is_public': lodging.is_public, + 'collection_export_id': collection_export_id + }) + + # Create ZIP file + with tempfile.NamedTemporaryFile(delete=False, suffix='.zip') as tmp_file: + with zipfile.ZipFile(tmp_file.name, 'w', zipfile.ZIP_DEFLATED) as zip_file: + # Add JSON data + zip_file.writestr('data.json', json.dumps(export_data, indent=2)) + + # Add images and attachments + files_added = set() + + for location in user.location_set.all(): + # Add images + for image in location.images.all(): + if image.image and image.image.name not in files_added: + try: + image_content = default_storage.open(image.image.name).read() + filename = image.image.name.split('/')[-1] + zip_file.writestr(f'images/{filename}', image_content) + files_added.add(image.image.name) + except Exception as e: + print(f"Error adding image {image.image.name}: {e}") + + # Add attachments + for attachment in location.attachments.all(): + if attachment.file and attachment.file.name not in files_added: + try: + file_content = default_storage.open(attachment.file.name).read() + filename = attachment.file.name.split('/')[-1] + zip_file.writestr(f'attachments/{filename}', file_content) + files_added.add(attachment.file.name) + except Exception as e: + print(f"Error adding attachment {attachment.file.name}: {e}") + + # Return ZIP file as response + with open(tmp_file.name, 'rb') as zip_file: + response = HttpResponse(zip_file.read(), content_type='application/zip') + filename = f"adventurelog_backup_{user.username}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.zip" + response['Content-Disposition'] = f'attachment; filename="{filename}"' + + # Clean up + os.unlink(tmp_file.name) + return response + + @action( + detail=False, + methods=['post'], + parser_classes=[MultiPartParser], + url_path='import', # changes the URL path to /import + url_name='import' # changes the reverse name to 'import' + ) + def import_data(self, request): + """ + Import data from a ZIP backup file + """ + if 'file' not in request.FILES: + return Response({'error': 'No file provided'}, status=status.HTTP_400_BAD_REQUEST) + + if 'confirm' not in request.data or request.data['confirm'] != 'yes': + return Response({'error': 'Confirmation required to proceed with import'}, + status=status.HTTP_400_BAD_REQUEST) + + backup_file = request.FILES['file'] + user = request.user + + # Save file temporarily + with tempfile.NamedTemporaryFile(delete=False, suffix='.zip') as tmp_file: + for chunk in backup_file.chunks(): + tmp_file.write(chunk) + tmp_file_path = tmp_file.name + + try: + with zipfile.ZipFile(tmp_file_path, 'r') as zip_file: + # Validate backup structure + if 'data.json' not in zip_file.namelist(): + return Response({'error': 'Invalid backup file - missing data.json'}, + status=status.HTTP_400_BAD_REQUEST) + + # Load data + backup_data = json.loads(zip_file.read('data.json').decode('utf-8')) + + # Import with transaction + with transaction.atomic(): + # Clear existing data first + self._clear_user_data(user) + summary = self._import_data(backup_data, zip_file, user) + + return Response({ + 'success': True, + 'message': 'Data imported successfully', + 'summary': summary + }, status=status.HTTP_200_OK) + + except json.JSONDecodeError: + return Response({'error': 'Invalid JSON in backup file'}, + status=status.HTTP_400_BAD_REQUEST) + except Exception: + import logging + logging.error("Import failed", exc_info=True) + return Response({'error': 'An internal error occurred during import'}, + status=status.HTTP_400_BAD_REQUEST) + finally: + os.unlink(tmp_file_path) + + def _clear_user_data(self, user): + """Clear all existing user data before import""" + # Delete in reverse order of dependencies + user.checklistitem_set.all().delete() + user.checklist_set.all().delete() + user.note_set.all().delete() + user.transportation_set.all().delete() + user.lodging_set.all().delete() + + # Delete location-related data + user.locationimage_set.all().delete() + user.attachment_set.all().delete() + # Visits are deleted via cascade when locations are deleted + user.location_set.all().delete() + + # Delete collections and categories last + user.collection_set.all().delete() + user.category_set.all().delete() + + # Clear visited cities and regions + user.visitedcity_set.all().delete() + user.visitedregion_set.all().delete() + + def _import_data(self, backup_data, zip_file, user): + """Import backup data and return summary""" + # Track mappings and counts + category_map = {} + collection_map = {} # Map export_id to actual collection object + summary = { + 'categories': 0, 'collections': 0, 'locations': 0, + 'transportation': 0, 'notes': 0, 'checklists': 0, + 'checklist_items': 0, 'lodging': 0, 'images': 0, 'attachments': 0, 'visited_cities': 0, 'visited_regions': 0 + } + + # Import Visited Cities + for city_data in backup_data.get('visited_cities', []): + try: + city_obj = City.objects.get(id=city_data['city']) + VisitedCity.objects.create(user=user, city=city_obj) + summary['visited_cities'] += 1 + except City.DoesNotExist: + # If city does not exist, we can skip or log it + pass + + # Import Visited Regions + for region_data in backup_data.get('visited_regions', []): + try: + region_obj = Region.objects.get(id=region_data['region']) + VisitedRegion.objects.create(user=user, region=region_obj) + summary['visited_regions'] += 1 + except Region.DoesNotExist: + # If region does not exist, we can skip or log it + pass + + # Import Categories + for cat_data in backup_data.get('categories', []): + category = Category.objects.create( + user=user, + name=cat_data['name'], + display_name=cat_data['display_name'], + icon=cat_data.get('icon', '🌍') + ) + category_map[cat_data['name']] = category + summary['categories'] += 1 + + # Import Collections + for col_data in backup_data.get('collections', []): + collection = Collection.objects.create( + user=user, + name=col_data['name'], + description=col_data.get('description', ''), + is_public=col_data.get('is_public', False), + start_date=col_data.get('start_date'), + end_date=col_data.get('end_date'), + is_archived=col_data.get('is_archived', False), + link=col_data.get('link') + ) + collection_map[col_data['export_id']] = collection + summary['collections'] += 1 + + # Handle shared users + for uuid in col_data.get('shared_with_user_ids', []): + try: + shared_user = User.objects.get(uuid=uuid) + if shared_user.public_profile: + collection.shared_with.add(shared_user) + except User.DoesNotExist: + pass + + # Import Locations + for adv_data in backup_data.get('locations', []): + + city = None + if adv_data.get('city'): + try: + city = City.objects.get(id=adv_data['city']) + except City.DoesNotExist: + city = None + + region = None + if adv_data.get('region'): + try: + region = Region.objects.get(id=adv_data['region']) + except Region.DoesNotExist: + region = None + + country = None + if adv_data.get('country'): + try: + country = Country.objects.get(id=adv_data['country']) + except Country.DoesNotExist: + country = None + + location = Location( + user=user, + name=adv_data['name'], + location=adv_data.get('location'), + tags=adv_data.get('tags', []), + description=adv_data.get('description'), + rating=adv_data.get('rating'), + link=adv_data.get('link'), + is_public=adv_data.get('is_public', False), + longitude=adv_data.get('longitude'), + latitude=adv_data.get('latitude'), + city=city, + region=region, + country=country, + category=category_map.get(adv_data.get('category_name')) + ) + location.save(_skip_geocode=True) # Skip geocoding for now + + # Add to collections using export_ids + for collection_export_id in adv_data.get('collection_export_ids', []): + if collection_export_id in collection_map: + location.collections.add(collection_map[collection_export_id]) + + # Import visits + for visit_data in adv_data.get('visits', []): + Visit.objects.create( + location=location, + start_date=visit_data.get('start_date'), + end_date=visit_data.get('end_date'), + timezone=visit_data.get('timezone'), + notes=visit_data.get('notes') + ) + + # Import images + for img_data in adv_data.get('images', []): + immich_id = img_data.get('immich_id') + if immich_id: + LocationImage.objects.create( + user=user, + location=location, + immich_id=immich_id, + is_primary=img_data.get('is_primary', False) + ) + summary['images'] += 1 + else: + filename = img_data.get('filename') + if filename: + try: + img_content = zip_file.read(f'images/{filename}') + img_file = ContentFile(img_content, name=filename) + LocationImage.objects.create( + user=user, + location=location, + image=img_file, + is_primary=img_data.get('is_primary', False) + ) + summary['images'] += 1 + except KeyError: + pass + + # Import attachments + for att_data in adv_data.get('attachments', []): + filename = att_data.get('filename') + if filename: + try: + att_content = zip_file.read(f'attachments/{filename}') + att_file = ContentFile(att_content, name=filename) + Attachment.objects.create( + user=user, + location=location, + file=att_file, + name=att_data.get('name') + ) + summary['attachments'] += 1 + except KeyError: + pass + + summary['locations'] += 1 + + # Import Transportation + for trans_data in backup_data.get('transportation', []): + collection = None + if trans_data.get('collection_export_id') is not None: + collection = collection_map.get(trans_data['collection_export_id']) + + Transportation.objects.create( + user=user, + type=trans_data['type'], + name=trans_data['name'], + description=trans_data.get('description'), + rating=trans_data.get('rating'), + link=trans_data.get('link'), + date=trans_data.get('date'), + end_date=trans_data.get('end_date'), + start_timezone=trans_data.get('start_timezone'), + end_timezone=trans_data.get('end_timezone'), + flight_number=trans_data.get('flight_number'), + from_location=trans_data.get('from_location'), + origin_latitude=trans_data.get('origin_latitude'), + origin_longitude=trans_data.get('origin_longitude'), + destination_latitude=trans_data.get('destination_latitude'), + destination_longitude=trans_data.get('destination_longitude'), + to_location=trans_data.get('to_location'), + is_public=trans_data.get('is_public', False), + collection=collection + ) + summary['transportation'] += 1 + + # Import Notes + for note_data in backup_data.get('notes', []): + collection = None + if note_data.get('collection_export_id') is not None: + collection = collection_map.get(note_data['collection_export_id']) + + Note.objects.create( + user=user, + name=note_data['name'], + content=note_data.get('content'), + links=note_data.get('links', []), + date=note_data.get('date'), + is_public=note_data.get('is_public', False), + collection=collection + ) + summary['notes'] += 1 + + # Import Checklists + for check_data in backup_data.get('checklists', []): + collection = None + if check_data.get('collection_export_id') is not None: + collection = collection_map.get(check_data['collection_export_id']) + + checklist = Checklist.objects.create( + user=user, + name=check_data['name'], + date=check_data.get('date'), + is_public=check_data.get('is_public', False), + collection=collection + ) + + # Import checklist items + for item_data in check_data.get('items', []): + ChecklistItem.objects.create( + user=user, + checklist=checklist, + name=item_data['name'], + is_checked=item_data.get('is_checked', False) + ) + summary['checklist_items'] += 1 + + summary['checklists'] += 1 + + # Import Lodging + for lodg_data in backup_data.get('lodging', []): + collection = None + if lodg_data.get('collection_export_id') is not None: + collection = collection_map.get(lodg_data['collection_export_id']) + + Lodging.objects.create( + user=user, + name=lodg_data['name'], + type=lodg_data.get('type', 'other'), + description=lodg_data.get('description'), + rating=lodg_data.get('rating'), + link=lodg_data.get('link'), + check_in=lodg_data.get('check_in'), + check_out=lodg_data.get('check_out'), + timezone=lodg_data.get('timezone'), + reservation_number=lodg_data.get('reservation_number'), + price=lodg_data.get('price'), + latitude=lodg_data.get('latitude'), + longitude=lodg_data.get('longitude'), + location=lodg_data.get('location'), + is_public=lodg_data.get('is_public', False), + collection=collection + ) + summary['lodging'] += 1 + + return summary \ No newline at end of file diff --git a/backend/server/integrations/views.py b/backend/server/integrations/views.py index 35e5551..36739b8 100644 --- a/backend/server/integrations/views.py +++ b/backend/server/integrations/views.py @@ -297,7 +297,7 @@ class ImmichIntegrationView(viewsets.ViewSet): is_authorized = True # Level 3: Owner access - elif request.user.is_authenticated and request.user.id == owner_id: + elif request.user.is_authenticated and request.user == owner_id: is_authorized = True # Level 4: Shared collection access - check if user has access to any collection @@ -314,7 +314,7 @@ class ImmichIntegrationView(viewsets.ViewSet): }, status=status.HTTP_403_FORBIDDEN) else: # No LocationImage exists; allow only the integration owner - if not request.user.is_authenticated or request.user.id != owner_id: + if not request.user.is_authenticated or request.user != owner_id: return Response({ 'message': 'Image is not linked to any location and you are not the owner.', 'error': True, diff --git a/backend/server/main/settings.py b/backend/server/main/settings.py index f707af4..339c8f4 100644 --- a/backend/server/main/settings.py +++ b/backend/server/main/settings.py @@ -326,6 +326,9 @@ LOGGING = { # ADVENTURELOG_CDN_URL = getenv('ADVENTURELOG_CDN_URL', 'https://cdn.adventurelog.app') +# Major release version of AdventureLog, not including the patch version date. +ADVENTURELOG_RELEASE_VERSION = 'v0.10.0' + # https://github.com/dr5hn/countries-states-cities-database/tags COUNTRY_REGION_JSON_VERSION = 'v2.6' diff --git a/frontend/src/lib/components/AboutModal.svelte b/frontend/src/lib/components/AboutModal.svelte index 46ad5aa..e437bc8 100644 --- a/frontend/src/lib/components/AboutModal.svelte +++ b/frontend/src/lib/components/AboutModal.svelte @@ -33,146 +33,201 @@ } - + - - diff --git a/frontend/src/lib/components/CardCarousel.svelte b/frontend/src/lib/components/CardCarousel.svelte index 8b98787..f8b947b 100644 --- a/frontend/src/lib/components/CardCarousel.svelte +++ b/frontend/src/lib/components/CardCarousel.svelte @@ -6,7 +6,8 @@ export let adventures: Location[] = []; let currentSlide = 0; - let image_url: string | null = null; + let showImageModal = false; + let modalInitialIndex = 0; $: adventure_images = adventures.flatMap((adventure) => adventure.images.map((image) => ({ @@ -42,13 +43,22 @@ currentSlide = currentSlide - 1; } } + + function openImageModal(initialIndex: number = currentSlide) { + modalInitialIndex = initialIndex; + showImageModal = true; + } + + function closeImageModal() { + showImageModal = false; + } -{#if image_url} +{#if showImageModal && adventure_images.length > 0} (image_url = null)} + images={adventure_images} + initialIndex={modalInitialIndex} + on:close={closeImageModal} /> {/if} @@ -61,14 +71,46 @@ (image_url = adventure_images[currentSlide].image)} - class="cursor-pointer" + on:click|stopPropagation={() => openImageModal(currentSlide)} + class="cursor-pointer relative group" > {adventure_images[currentSlide].adventure.name} + + + + + + {#if adventure_images.length > 1} @@ -76,8 +118,18 @@ {#if currentSlide > 0} + + + + {:else}
{/if} @@ -85,17 +137,43 @@ {#if currentSlide < adventure_images.length - 1} + + + + {:else}
{/if} + + + {/if} {:else} - +
- - + - diff --git a/frontend/src/lib/config.ts b/frontend/src/lib/config.ts index 6772b0a..7d9889e 100644 --- a/frontend/src/lib/config.ts +++ b/frontend/src/lib/config.ts @@ -1,4 +1,4 @@ -export let appVersion = 'v0.10.0-main-06192025'; +export let appVersion = 'v0.10.0-main-06252025'; export let versionChangelog = 'https://github.com/seanmorley15/AdventureLog/releases/tag/v0.10.0'; export let appTitle = 'AdventureLog'; export let copyrightYear = '2023-2025'; diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index 750958f..7d2ed84 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -9,7 +9,15 @@ "oss_attributions": "Open Source Quellenangaben", "other_attributions": "Weitere Hinweise finden Sie in der README-Datei.", "source_code": "Quellcode", - "generic_attributions": "Melden Sie sich bei Adventurelog an, um Zuschreibungen für aktivierte Integrationen und Dienste anzuzeigen." + "generic_attributions": "Melden Sie sich bei Adventurelog an, um Zuschreibungen für aktivierte Integrationen und Dienste anzuzeigen.", + "attributions": "Zuschreibungen", + "developer": "Entwickler", + "license_info": "Lizenz", + "sponsor": "Sponsor", + "thank_you": "Vielen Dank, dass Sie Adventurelog verwendet haben!", + "version": "Version", + "view_changelog": "ChangeLog anzeigen", + "view_license": "Lizenz anzeigen" }, "adventures": { "activities": {}, @@ -250,7 +258,8 @@ "visit_calendar": "Besuchen Sie den Kalender", "wiki_location_desc": "Zieht Auszug aus dem Wikipedia -Artikel, der dem Namen des Standorts entspricht.", "will_be_marked_location": "wird als besucht markiert, sobald der Standort gespeichert ist.", - "no_locations_found": "Keine Standorte gefunden" + "no_locations_found": "Keine Standorte gefunden", + "image_modal_navigate": "Verwenden Sie Pfeiltasten oder klicken Sie, um zu navigieren" }, "home": { "desc_1": "Entdecken, planen und erkunden Sie mühelos", @@ -497,7 +506,36 @@ "staff_status": "Personalstatus", "staff_user": "Personalbenutzer", "profile_info_desc": "Aktualisieren Sie Ihre persönlichen Daten und Ihr Profilbild", - "invalid_credentials": "Ungültige Anmeldeinformationen" + "invalid_credentials": "Ungültige Anmeldeinformationen", + "backup_restore": "Backup", + "backup_restore_desc": "Speichern Sie Ihre Daten oder stellen Sie sie in einer früheren Sicherungsdatei wieder her.", + "complete_setup_to_enable": "Komplettes Setup, um MFA zu aktivieren", + "copy_all": "Alle kopieren", + "enter_6_digit_code": "Geben Sie einen 6 -stelligen Code ein", + "enter_code_from_app": "Geben Sie den Code aus der App ein", + "error_occurred": "Es ist ein Fehler aufgetreten", + "important": "Wichtig", + "manual_entry": "Manueller Eintritt", + "mfa_already_enabled": "MFA bereits aktiviert", + "mfa_required": "MFA erforderlich", + "scan_qr_code": "Scannen QR -Code", + "scan_with_authenticator_app": "Scannen Sie mit Authenticator App", + "secure_your_account": "Sichern Sie Ihr Konto", + "setup_required": "Setup erforderlich", + "verify_setup": "Überprüfen Sie das Setup", + "whats_included": "Was ist enthalten", + "backup_your_data": "Sichern Sie Ihre Daten", + "backup_your_data_desc": "Laden Sie eine vollständige Sicherung Ihrer Kontodaten, einschließlich Standorte, Sammlungen, Medien und Besuchen, herunter.", + "data_override_acknowledge": "Ich bestätige, dass dies alle meine vorhandenen Daten überschreibt", + "data_override_acknowledge_desc": "Diese Aktion ist irreversibel und ersetzt alle Standorte, Sammlungen und Besuche in Ihrem Konto.", + "data_override_warning": "Daten überschreiben Warnung", + "data_override_warning_desc": "Das Wiederherstellen von Daten ersetzt alle vorhandenen Daten (die in der Sicherung enthalten sind) in Ihrem Konto vollständig. \nDiese Aktion kann nicht rückgängig gemacht werden.", + "integrations_settings": "Integrationseinstellungen", + "media": "Medien", + "restore_data": "Daten wiederherstellen", + "restore_data_desc": "Laden Sie eine Sicherungsdatei hoch, um Ihre Daten wiederherzustellen.", + "select_backup_file": "Wählen Sie Sicherungsdatei", + "world_travel_visits": "Weltreisebesuche" }, "checklist": { "checklist_delete_error": "Fehler beim Löschen der Checkliste", @@ -722,5 +760,6 @@ "location": "Standort", "locations": "Standorte", "my_locations": "Meine Standorte" - } + }, + "settings_download_backup": "Backup herunterladen" } diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index c547b07..0193113 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -1,726 +1,765 @@ { - "navbar": { - "adventures": "Adventures", - "collections": "Collections", - "worldtravel": "World Travel", - "map": "Map", - "users": "Users", - "search": "Search", - "profile": "Profile", - "greeting": "Hi", - "my_adventures": "My Adventures", - "my_tags": "My Tags", - "tag": "Tag", - "shared_with_me": "Shared With Me", - "settings": "Settings", - "logout": "Logout", - "about": "About AdventureLog", - "documentation": "Documentation", - "language_selection": "Language", - "support": "Support", - "calendar": "Calendar", - "theme_selection": "Theme Selection", - "admin_panel": "Admin Panel", - "themes": { - "light": "Light", - "dark": "Dark", - "night": "Night", - "forest": "Forest", - "aestheticLight": "Aesthetic Light", - "aestheticDark": "Aesthetic Dark", - "aqua": "Aqua", - "northernLights": "Northern Lights", - "dim": "Dim" - } - }, - "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.", - "generic_attributions": "Login to AdventureLog to view attributions for enabled integrations and services.", - "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.", - "start_your_journey": "Start Your Journey", - "of_world": "of the world", - "explore_world": "Explore World", - "latest_travel_experiences": "Your latest travel experiences" - }, - "adventures": { - "collection_remove_location_success": "Location removed from collection successfully!", - "collection_remove_location_error": "Error removing location from collection", - "collection_link_location_success": "Location linked to collection successfully!", - "invalid_date_range": "Invalid date range", - "timezone": "Timezone", - "no_visits": "No visits", - "departure_timezone": "Departure Timezone", - "arrival_timezone": "Arrival Timezone", - "departure_date": "Departure Date", - "arrival_date": "Arrival Date", - "no_image_found": "No image found", - "collection_link_location_error": "Error linking location to collection", - "location_delete_confirm": "Are you sure you want to delete this location? This action cannot be undone.", - "checklist_delete_confirm": "Are you sure you want to delete this checklist? This action cannot be undone.", - "note_delete_confirm": "Are you sure you want to delete this note? This action cannot be undone.", - "transportation_delete_confirm": "Are you sure you want to delete this transportation? This action cannot be undone.", - "lodging_delete_confirm": "Are you sure you want to delete this lodging location? This action cannot be undone.", - "delete_checklist": "Delete Checklist", - "delete_note": "Delete Note", - "delete_transportation": "Delete Transportation", - "delete_lodging": "Delete Lodging", - "open_details": "Open Details", - "edit_adventure": "Edit Adventure", - "edit_location": "Edit Location", - "remove_from_collection": "Remove from Collection", - "add_to_collection": "Add to Collection", - "delete": "Delete", - "location_not_found": "Location not found", - "location_not_found_desc": "The location you were looking for could not be found. Please try a different location or check back later.", - "homepage": "Homepage", - "collection": "Collection", - "longitude": "Longitude", - "latitude": "Latitude", - "visit": "Visit", - "timed": "Timed", - "check_in": "Check In", - "check_out": "Check Out", - "coordinates": "Coordinates", - "copy_coordinates": "Copy Coordinates", - "visits": "Visits", - "create_new": "Create New...", - "adventure": "Adventure", - "count_txt": "results matching your search", - "sort": "Sort", - "order_by": "Order By", - "order_direction": "Order Direction", - "ascending": "Ascending", - "descending": "Descending", - "updated": "Updated", - "name": "Name", - "date": "Date", - "activity_types": "Activity Types", - "tags": "Tags", - "add_a_tag": "Add a tag", - "date_constrain": "Constrain to collection dates", - "rating": "Rating", - "my_images": "My Images", - "no_images": "No Images", - "distance": "Distance", - "share_location": "Share this Location!", - "share_collection": "Share this Collection!", - "copy_link": "Copy Link", - "sun_times": "Sun Times", - "sunrise": "Sunrise", - "sunset": "Sunset", - "image": "Image", - "upload_image": "Upload Image", - "open_in_maps": "Open in Maps", - "url": "URL", - "fetch_image": "Fetch Image", - "wikipedia": "Wikipedia", - "add_notes": "Add notes", - "warning": "Warning", - "my_adventures": "My Adventures", - "no_linkable_adventures": "No adventures found that can be linked to this collection.", - "add": "Add", - "save_next": "Save & Next", - "end_date": "End Date", - "start_date": "Start Date", - "remove": "Remove", - "location": "Location", - "search_for_location": "Search for a location", - "clear_map": "Clear map", - "search_results": "Search results", - "collection_no_start_end_date": "Adding a start and end date to the collection will unlock itinerary planning features in the collection page.", - "no_results": "No results found", - "wiki_location_desc": "Pulls excerpt from Wikipedia article matching the name of the location.", - "attachments": "Attachments", - "attachment": "Attachment", - "images": "Images", - "generate_desc": "Generate Description", - "public_location": "Public Location", - "location_information": "Location Information", - "link": "Link", - "links": "Links", - "description": "Description", - "sources": "Sources", - "collection_adventures": "Include Collection Adventures", - "collection_locations": "Include Collection Locations", - "filter": "Filter", - "category_filter": "Category Filter", - "category": "Category", - "clear": "Clear", - "my_collections": "My Collections", - "open_filters": "Open Filters", - "archived_collections": "Archived Collections", - "share": "Share", - "private": "Private", - "public": "Public", - "archived": "Archived", - "name_location": "name, location", - "loading_adventures": "Loading adventures...", - "all_adventures_already_linked": "All adventures are already linked to this collection.", - "edit_collection": "Edit Collection", - "unarchive": "Unarchive", - "archive": "Archive", - "no_collections_to_add_location": "No collections found to add this location to.", - "create_collection_first": "Create a collection first to organize your adventures and memories.", - "done": "Done", - "adventures_available": "Adventures Available", - "collections_linked": "Collections Linked", - "not_visited": "Not Visited", - "archived_collection_message": "Collection archived successfully!", - "unarchived_collection_message": "Collection unarchived successfully!", - "delete_collection_success": "Collection deleted successfully!", - "cancel": "Cancel", - "delete_collection_warning": "Are you sure you want to delete this collection? This action cannot be undone.", - "delete_collection": "Delete Collection", - "delete_location": "Delete Location", - "location_delete_success": "Location deleted successfully!", - "visited": "Visited", - "planned": "Planned", - "duration": "Duration", - "all": "All", - "image_removed_success": "Image removed successfully!", - "image_removed_error": "Error removing image", - "no_image_url": "No image found at that URL.", - "image_upload_success": "Image uploaded successfully!", - "image_upload_error": "Error uploading image", - "dates": "Dates", - "wiki_image_error": "Error fetching image from Wikipedia", - "start_before_end_error": "Start date must be before end date", - "actions": "Actions", - "see_adventures": "See Adventures", - "image_fetch_failed": "Failed to fetch image", - "no_location": "Please enter a location", - "no_description_found": "No description found", - "location_created": "Location created", - "location_create_error": "Failed to create location", - "lodging": "Lodging", - "create_location": "Create Location", - "location_updated": "Location updated", - "location_update_error": "Failed to update location", - "set_to_pin": "Set to Pin", - "category_fetch_error": "Error fetching categories", - "new_location": "New Location", - "basic_information": "Basic Information", - "no_locations_to_recommendations": "No locations found. Add at least one location to get recommendations.", - "display_name": "Display Name", - "adventure_not_found": "There are no adventures to display. Add some using the plus button at the bottom right or try changing filters!", - "collection_contents": "Collection Contents", - "no_adventures_found": "No adventures found", - "no_locations_found": "No locations found", - "no_adventures_message": "Start documenting your adventures and planning new ones. Every journey has a story worth telling.", - "mark_visited": "Mark Visited", - "error_updating_regions": "Error updating regions", - "regions_updated": "regions updated", - "cities_updated": "cities updated", - "visited_region_check": "Visited Region Check", - "visited_region_check_desc": "By selecting this, the server will check all of your visited adventures and mark the regions they are located in as visited in world travel.", - "update_visited_regions": "Update Visited Regions", - "update_visited_regions_disclaimer": "This may take a while depending on the number of adventures you have visited.", - "link_new": "Link New...", - "add_new": "Add New...", - "transportation": "Transportation", - "note": "Note", - "checklist": "Checklist", - "collection_archived": "This collection has been archived.", - "visit_link": "Visit Link", - "collection_completed": "You've completed this collection!", - "collection_stats": "Collection Stats", - "keep_exploring": "Keep Exploring!", - "linked_adventures": "Linked Adventures", - "notes": "Notes", - "checklists": "Checklists", - "transportations": "Transportations", - "adventure_calendar": "Adventure Calendar", - "visit_calendar": "Visit Calendar", - "day": "Day", - "itineary_by_date": "Itinerary by Date", - "nothing_planned": "Nothing planned for this day. Enjoy the journey!", - "copied_to_clipboard": "Copied to clipboard!", - "copy_failed": "Copy failed", - "show": "Show", - "hide": "Hide", - "clear_location": "Clear Location", - "starting_airport": "Starting Airport", - "view_profile": "View Profile", - "joined": "Joined", - "ending_airport": "Ending Airport", - "no_location_found": "No location found", - "from": "From", - "to": "To", - "will_be_marked_location": "will be marked as visited once the location is saved.", - "start": "Start", - "end": "End", - "emoji_picker": "Emoji Picker", - "download_calendar": "Download Calendar", - "all_day": "All Day", - "ordered_itinerary": "Ordered Itinerary", - "itinerary": "Itinerary", - "all_linked_items": "All Linked Items", - "date_itinerary": "Date Itinerary", - "no_ordered_items": "Add items with dates to the collection to see them here.", - "date_information": "Date Information", - "flight_information": "Flight Information", - "out_of_range": "Not in itinerary date range", - "preview": "Preview", - "finding_recommendations": "Discovering hidden gems for your next adventure", - "location_details": "Location Details", - "city": "City", - "region": "Region", - "md_instructions": "Write your markdown here...", - "days": "days", - "attachment_upload_success": "Attachment uploaded successfully!", - "attachment_upload_error": "Error uploading attachment", - "upload": "Upload", - "attachment_delete_success": "Attachment deleted successfully!", - "attachment_update_success": "Attachment updated successfully!", - "attachment_name": "Attachment Name", - "gpx_tip": "Upload GPX files to attachments to view them on the map!", - "attachment_update_error": "Error updating attachment", - "activities": {}, - "lodging_information": "Lodging Information", - "price": "Price", - "reservation_number": "Reservation Number", - "filters_and_sort": "Filters & Sort", - "filters_and_stats": "Filters & Stats", - "travel_progress": "Travel Progress" - }, - "worldtravel": { - "country_list": "Country List", - "num_countries": "countries found", - "cities_in": "Cities in", - "all": "All", - "partially_visited": "Partially Visited", - "not_visited": "Not Visited", - "completely_visited": "Completely Visited", - "all_subregions": "All Subregions", - "clear_search": "Clear Search", - "no_countries_found": "No countries found", - "no_countries_found_desc": "Try adjusting your search terms or filters to find the countries you're looking for.", - "clear_filters": "Clear Filters", - "view_cities": "View Cities", - "no_cities_found": "No cities found", - "visit_to": "Visit to", - "region_failed_visited": "Failed to mark region as visited", - "failed_to_mark_visit": "Failed to mark visit to", - "visit_remove_failed": "Failed to remove visit", - "removed": "removed", - "failed_to_remove_visit": "Failed to remove visit to", - "marked_visited": "marked as visited", - "regions_in": "Regions in", - "cities": "cities", - "remaining": "Remaining", - "of": "of", - "countries": "countries", - "show_map": "Show Map", - "hide_map": "Hide Map", - "complete": "Complete", - "partial": "Partial", - "clear_all": "Clear All", - "no_country_data_available": "No country data available", - "no_country_data_available_desc": "Please check the documentation for updating region data.", - "total_countries": "Total Countries", - "available_to_explore": "Available to Explore", - "progress": "Progress", - "filter_by_region": "Filter by Region", - "all_regions": "All Regions", - "clear_all_filters": "Clear All Filters", - "total_items": "Total Items", - "filter_by": "Filter by", - "interactive_map": "Interactive Map", - "no_regions_found": "No regions found", - "progress_and_stats": "Progress & Stats", - "total_regions": "Total Regions", - "country_completed": "Country completed", - "show_map_labels": "Show Map Labels", - "hide_map_labels": "Hide Map Labels", - "total_cities": "Total Cities", - "region_completed": "Region completed", - "newest_first": "Newest First", - "oldest_first": "Oldest First", - "visited_first": "Visited First", - "unvisited_first": "Unvisited First" - }, - "auth": { - "username": "Username", - "password": "Password", - "forgot_password": "Forgot Password?", - "signup": "Signup", - "login_error": "Unable to login with the provided credentials.", - "login": "Login", - "email": "Email", - "first_name": "First Name", - "last_name": "Last Name", - "confirm_password": "Confirm Password", - "registration_disabled": "Registration is currently disabled.", - "profile_picture": "Profile Picture", - "public_profile": "Public Profile", - "public_tooltip": "With a public profile, users can share collections with you and view your profile on the users page.", - "new_password": "New Password (6+ characters)", - "or_3rd_party": "Or login with a third-party service", - "no_public_locations": "No public locations found", - "no_public_collections": "No public collections found", - "user_locations": "User Locations", - "user_collections": "User Collections" - }, - "users": { - "no_users_found": "No users found with public profiles." - }, - "settings": { - "update_error": "Error updating settings", - "update_success": "Settings updated successfully!", - "settings_page": "Settings Page", - "account_settings": "User Account Settings", - "update": "Update", - "no_verified_email_warning": "You must have a verified email address to enable two-factor authentication.", - "social_auth": "Social Authentication", - "social_auth_desc_1": "Manage social login options and password settings", - "password_auth": "Password Authentication", - "password_login_enabled": "Password login enabled", - "password_login_disabled": "Password login disabled", - "password_change": "Change Password", - "new_password": "New Password", - "confirm_new_password": "Confirm New Password", - "email_change": "Change Email", - "no_email_set": "No email set", - "email_management": "Email Management", - "email_management_desc": "Manage your email addresses and verification status", - "add_new_email": "Add New Email", - "add_new_email_address": "Add New Email Address", - "enter_new_email": "Enter new email address", - "new_email": "New Email", - "change_password": "Change Password", - "reset_password": "Reset Password", - "possible_reset": "If the email address you provided is associated with an account, you will receive an email with instructions to reset your password!", - "about_this_background": "About this background", - "photo_by": "Photo by", - "join_discord": "Join the Discord", - "join_discord_desc": "to share your own photos. Post them in the #travel-share channel.", - "current_password": "Current Password", - "password_change_lopout_warning": "You will be logged out after changing your password.", - "generic_error": "An error occurred while processing your request.", - "email_removed": "Email removed successfully!", - "email_removed_error": "Error removing email", - "verify_email_success": "Email verification sent successfully!", - "verify_email_error": "Error verifying email. Try again in a few minutes.", - "email_added": "Email added successfully!", - "email_set_primary": "Email set as primary successfully!", - "email_set_primary_error": "Error setting email as primary", - "verified": "Verified", - "primary": "Primary", - "not_verified": "Not Verified", - "make_primary": "Make Primary", - "verify": "Verify", - "no_emai_set": "No email set", - "mfa_disabled": "Multi-factor authentication disabled successfully!", - "mfa_page_title": "Multi-Factor Authentication", - "mfa_desc": "Add an extra layer of security to your account", - "enable_mfa": "Enable MFA", - "disable_mfa": "Disable MFA", - "enabled": "Enabled", - "disabled": "Disabled", - "mfa_not_enabled": "MFA is not enabled", - "mfa_is_enabled": "MFA is enabled", - "mfa_enabled": "Multi-factor authentication enabled successfully!", - "copy": "Copy", - "recovery_codes": "Recovery Codes", - "recovery_codes_desc": "These are your recovery codes. Keep them safe. You will not be able to see them again.", - "reset_session_error": "Please logout and back in to refresh your session and try again.", - "authenticator_code": "Authenticator Code", - "email_verified": "Email verified successfully!", - "email_verified_success": "Your email has been verified. You can now log in.", - "email_verified_error": "Error verifying email", - "email_verified_erorr_desc": "Your email could not be verified. Please try again.", - "launch_administration_panel": "Launch Administration Panel", - "administration": "Administration", - "admin_panel_desc": "Access the full administration interface", - "region_updates": "Region Updates", - "debug_information": "Debug Information", - "staff_status": "Staff Status", - "staff_user": "Staff User", - "regular_user": "Regular User", - "app_version": "App Version", - "quick_actions": "Quick Actions", - "license": "License", - "all_rights_reserved": "All rights reserved.", - "region_updates_desc": "Update visited regions and cities", - "access_restricted": "Access Restricted", - "access_restricted_desc": "Administrative features are only available to staff members.", - "advanced_settings": "Advanced Settings", - "advanced_settings_desc": "Advanced configuration and development tools", - "social_auth_setup": "Social Authentication Setup", - "administration_desc": "Administrative tools and settings", - "social_auth_desc": "Enable or disable social and OIDC authentication providers for your account. These connections allow you to sign in with self hosted authentication identity providers like Authentik or 3rd party providers like GitHub.", - "social_auth_desc_2": "These settings are managed in the AdventureLog server and must be manually enabled by the administrator.", - "documentation_link": "Documentation Link", - "launch_account_connections": "Launch Account Connections", - "add_email": "Add Email", - "password_enabled": "Password authentication enabled", - "password_disabled": "Password authentication disabled", - "password_disable_warning": "Currently, password authentication is disabled. Login via a social or OIDC provider is required.", - "password_disabled_error": "Error disabling password authentication. Make sure a social or OIDC provider is linked to your account.", - "password_enabled_error": "Error enabling password authentication.", - "settings_menu": "Settings Menu", - "security": "Security", - "emails": "Emails", - "integrations": "Integrations", - "integrations_desc": "Connect external services to enhance your experience", - "admin": "Admin", - "advanced": "Advanced", - "profile_info": "Profile Information", - "profile_info_desc": "Update your personal details and profile picture", - "public_profile_desc": "Make your profile visible to other users", - "pass_change_desc": "Update your account password for better security", - "enter_first_name": "Enter your first name", - "enter_last_name": "Enter your last name", - "enter_username": "Enter your username", - "enter_current_password": "Enter current password", - "enter_new_password": "Enter new password", - "connected": "Connected", - "disconnected": "Disconnected", - "invalid_credentials": "Invalid credentials" - }, - "collection": { - "collection_created": "Collection created successfully!", - "error_creating_collection": "Error creating collection", - "new_collection": "New Collection", - "create": "Create", - "collection_edit_success": "Collection edited successfully!", - "error_editing_collection": "Error editing collection", - "public_collection": "Public Collection", - "manage_collections": "Manage Collections", - "no_collections_yet": "No collections yet", - "no_shared_collections": "No shared collections.", - "shared_collections": "Shared Collections", - "no_archived_collections": "No archived collections.", - "create_first": "Create your first collection to organize your adventures and memories.", - "make_sure_public": "Make sure your profile is public so others can share with you.", - "archived_appear_here": "Archived collections will appear here.", - "linked": "Linked", - "available": "Available", - "try_different_search": "Try a different search or filter." - }, - "notes": { - "note_deleted": "Note deleted successfully!", - "note_delete_error": "Error deleting note", - "open": "Open", - "failed_to_save": "Failed to save note", - "note_editor": "Note Editor", - "note_viewer": "Note Viewer", - "editing_note": "Editing note", - "content": "Content", - "save": "Save", - "note_public": "This note is public because it is in a public collection.", - "add_a_link": "Add a link", - "invalid_url": "Invalid URL" - }, - "checklist": { - "checklist_deleted": "Checklist deleted successfully!", - "checklist_delete_error": "Error deleting checklist", - "checklist_editor": "Checklist Editor", - "new_checklist": "New Checklist", - "item": "Item", - "items": "Items", - "new_item": "New Item", - "checklist_public": "This checklist is public because it is in a public collection.", - "item_cannot_be_empty": "Item cannot be empty", - "item_already_exists": "Item already exists" - }, - "transportation": { - "transportation_deleted": "Transportation deleted successfully!", - "transportation_delete_error": "Error deleting transportation", - "type": "Type", - "new_transportation": "New Transportation", - "flight_number": "Flight Number", - "from_location": "From Location", - "to_location": "To Location", - "fetch_location_information": "Fetch Location Information", - "starting_airport_desc": "Enter starting airport code (e.g., JFK)", - "ending_airport_desc": "Enter ending airport code (e.g., LAX)", - "edit": "Edit", - "modes": { - "car": "Car", - "plane": "Plane", - "train": "Train", - "bus": "Bus", - "boat": "Boat", - "bike": "Bike", - "walking": "Walking", - "other": "Other" - }, - "edit_transportation": "Edit Transportation" - }, - "lodging": { - "new_lodging": "New Lodging", - "edit": "Edit", - "edit_lodging": "Edit Lodging", - "hotel": "Hotel", - "hostel": "Hostel", - "resort": "Resort", - "bnb": "Bed and Breakfast", - "campground": "Campground", - "cabin": "Cabin", - "apartment": "Apartment", - "house": "House", - "villa": "Villa", - "motel": "Motel", - "other": "Other", - "reservation_number": "Reservation Number" - }, - "search": { - "adventurelog_results": "AdventureLog Results", - "public_adventures": "Public Adventures", - "online_results": "Online Results", - "result": "Result", - "results": "Results", - "found": "found", - "try_searching_desc": "Try searching for adventures, collections, countries, regions, cities, or users.", - "countries": "Countries", - "cities": "Cities" - }, - "map": { - "view_details": "View Details", - "adventure_map": "Adventure Map", - "location_map": "Location Map", - "map_options": "Map Options", - "show_visited_regions": "Show Visited Regions", - "add_adventure_at_marker": "Add New Adventure at Marker", - "add_location_at_marker": "Add New Location at Marker", - "clear_marker": "Clear Marker", - "add_adventure": "Add New Adventure", - "add_location": "Add New Location", - "adventure_stats": "Adventure Stats", - "map_controls": "Map Controls", - "regions": "Regions", - "completion": "Completion", - "display_options": "Display Options", - "marker_placed_on_map": "Marker placed on map", - "place_marker_desc_location": "Click on the map to place a marker.", - "locations_shown": "locations shown" - }, - "share": { - "shared": "Shared", - "with": "with", - "unshared": "Unshared", - "share_desc": "Share this collection with other users.", - "shared_with": "Shared With", - "no_users_shared": "No users shared with", - "not_shared_with": "Not Shared With", - "no_shared_found": "No collections found that are shared with you.", - "set_public": "In order to allow users to share with you, you need your profile set to public.", - "go_to_settings": "Go to settings" - }, - "languages": {}, - "profile": { - "member_since": "Member since", - "user_stats": "User Stats", - "visited_countries": "Visited Countries", - "visited_regions": "Visited Regions", - "visited_cities": "Visited Cities", - "travel_statistics": "Travel Statistics", - "your_journey_at_a_glance": "Your adventure journey at a glance", - "planned_trips": "Planned trips", - "discovered": "discovered", - "explored": "explored", - "public_location_experiences": "Public location experiences", - "no_shared_adventures": "This user hasn't shared any public adventures yet.", - "no_shared_collections": "This user hasn't shared any public collections yet." - }, - "categories": { - "manage_categories": "Manage Categories", - "no_categories_found": "No categories found.", - "edit_category": "Edit Category", - "icon": "Icon", - "location_update_after_refresh": "The location cards will be updated once you refresh the page.", - "select_category": "Select Category", - "category_name": "Category Name", - "add_new_category": "Add New Category", - "name_required": "Category name is required" - }, - "dashboard": { - "welcome_back": "Welcome back", - "countries_visited": "Countries Visited", - "total_adventures": "Total Adventures", - "total_visited_regions": "Total Visited Regions", - "total_visited_cities": "Total Visited Cities", - "recent_adventures": "Recent Adventures", - "no_recent_adventures": "No recent adventures?", - "add_some": "Why not start planning your next adventure? You can add a new adventure by clicking the button below.", - "document_some_adventures": "Start documenting your travels and build your personal adventure map!", - "view_all": "View All", - "welcome_text_1": "You've been on", - "welcome_text_2": "adventures so far", - "welcome_text_3": "Keep exploring and documenting your travels!" - }, - "immich": { - "immich": "Immich", - "integration_fetch_error": "Error fetching data from the Immich integration", - "no_items_found": "No items found", - "load_more": "Load More", - "immich_error": "Error updating Immich integration", - "immich_disabled": "Immich integration disabled successfully!", - "disable": "Disable", - "server_url": "Immich Server URL", - "api_note": "Note: this must be the URL to the Immich API server so it likely ends with /api unless you have a custom config.", - "api_key": "Immich API Key", - "enable_integration": "Enable Integration", - "update_integration": "Update Integration", - "immich_integration_desc": "Connect your Immich photo management server", - "localhost_note": "Note: localhost will most likely not work unless you have setup docker networks accordingly. It is recommended to use the IP address of the server or the domain name.", - "api_key_placeholder": "Enter your Immich API key", - "need_help": "Need help setting this up? Check out the", - "copy_locally": "Copy Images Locally", - "copy_locally_desc": "Copy images to the server for offline access. Uses more disk space.", - "error_saving_image": "Error saving image", - "connection_error": "Error connecting to Immich server", - "integration_already_exists": "An Immich integration already exists. You can only have one integration at a time.", - "integration_not_found": "Immich integration not found. Please create a new integration.", - "validation_error": "An error occurred while validating the Immich integration. Please check your server URL and API key.", - "network_error": "Network error while connecting to the Immich server. Please check your connection and try again." - }, - "google_maps": { - "google_maps_integration_desc": "Connect your Google Maps account to get high-quality location search results and recommendations." - }, - "recomendations": { - "recommendation": "Recommendation", - "recommendations": "Recommendations", - "location_recommendations": "Location Recommendations", - "food": "Food", - "tourism": "Tourism" - }, - "calendar": { - "today": "Today", - "month": "Month", - "week": "Week", - "day": "Day", - "events_scheduled": "events scheduled", - "total_events": "Total Events", - "calendar_overview": "Calendar Overview", - "filtered_results": "Filtered Results", - "all_day_event": "All Day Event" - }, - "locations": { - "location": "Location", - "locations": "Locations", - "my_locations": "My Locations" - } + "navbar": { + "adventures": "Adventures", + "collections": "Collections", + "worldtravel": "World Travel", + "map": "Map", + "users": "Users", + "search": "Search", + "profile": "Profile", + "greeting": "Hi", + "my_adventures": "My Adventures", + "my_tags": "My Tags", + "tag": "Tag", + "shared_with_me": "Shared With Me", + "settings": "Settings", + "logout": "Logout", + "about": "About AdventureLog", + "documentation": "Documentation", + "language_selection": "Language", + "support": "Support", + "calendar": "Calendar", + "theme_selection": "Theme Selection", + "admin_panel": "Admin Panel", + "themes": { + "light": "Light", + "dark": "Dark", + "night": "Night", + "forest": "Forest", + "aestheticLight": "Aesthetic Light", + "aestheticDark": "Aesthetic Dark", + "aqua": "Aqua", + "northernLights": "Northern Lights", + "dim": "Dim" + } + }, + "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.", + "generic_attributions": "Login to AdventureLog to view attributions for enabled integrations and services.", + "close": "Close", + "thank_you": "Thank you for using AdventureLog!", + "version": "Version", + "view_changelog": "View Changelog", + "developer": "Developer", + "attributions": "Attributions", + "license_info": "License", + "view_license": "View License", + "sponsor": "Sponsor" + }, + "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.", + "start_your_journey": "Start Your Journey", + "of_world": "of the world", + "explore_world": "Explore World", + "latest_travel_experiences": "Your latest travel experiences" + }, + "adventures": { + "collection_remove_location_success": "Location removed from collection successfully!", + "collection_remove_location_error": "Error removing location from collection", + "collection_link_location_success": "Location linked to collection successfully!", + "invalid_date_range": "Invalid date range", + "timezone": "Timezone", + "no_visits": "No visits", + "departure_timezone": "Departure Timezone", + "arrival_timezone": "Arrival Timezone", + "departure_date": "Departure Date", + "arrival_date": "Arrival Date", + "no_image_found": "No image found", + "collection_link_location_error": "Error linking location to collection", + "location_delete_confirm": "Are you sure you want to delete this location? This action cannot be undone.", + "checklist_delete_confirm": "Are you sure you want to delete this checklist? This action cannot be undone.", + "note_delete_confirm": "Are you sure you want to delete this note? This action cannot be undone.", + "transportation_delete_confirm": "Are you sure you want to delete this transportation? This action cannot be undone.", + "lodging_delete_confirm": "Are you sure you want to delete this lodging location? This action cannot be undone.", + "delete_checklist": "Delete Checklist", + "delete_note": "Delete Note", + "delete_transportation": "Delete Transportation", + "delete_lodging": "Delete Lodging", + "open_details": "Open Details", + "edit_adventure": "Edit Adventure", + "edit_location": "Edit Location", + "remove_from_collection": "Remove from Collection", + "add_to_collection": "Add to Collection", + "delete": "Delete", + "location_not_found": "Location not found", + "location_not_found_desc": "The location you were looking for could not be found. Please try a different location or check back later.", + "homepage": "Homepage", + "collection": "Collection", + "longitude": "Longitude", + "latitude": "Latitude", + "visit": "Visit", + "timed": "Timed", + "check_in": "Check In", + "check_out": "Check Out", + "coordinates": "Coordinates", + "copy_coordinates": "Copy Coordinates", + "visits": "Visits", + "create_new": "Create New...", + "adventure": "Adventure", + "count_txt": "results matching your search", + "sort": "Sort", + "order_by": "Order By", + "order_direction": "Order Direction", + "ascending": "Ascending", + "descending": "Descending", + "updated": "Updated", + "name": "Name", + "date": "Date", + "activity_types": "Activity Types", + "tags": "Tags", + "add_a_tag": "Add a tag", + "date_constrain": "Constrain to collection dates", + "rating": "Rating", + "my_images": "My Images", + "no_images": "No Images", + "distance": "Distance", + "share_location": "Share this Location!", + "share_collection": "Share this Collection!", + "copy_link": "Copy Link", + "sun_times": "Sun Times", + "sunrise": "Sunrise", + "sunset": "Sunset", + "image": "Image", + "upload_image": "Upload Image", + "open_in_maps": "Open in Maps", + "url": "URL", + "fetch_image": "Fetch Image", + "wikipedia": "Wikipedia", + "add_notes": "Add notes", + "warning": "Warning", + "my_adventures": "My Adventures", + "no_linkable_adventures": "No adventures found that can be linked to this collection.", + "add": "Add", + "save_next": "Save & Next", + "end_date": "End Date", + "start_date": "Start Date", + "remove": "Remove", + "location": "Location", + "search_for_location": "Search for a location", + "clear_map": "Clear map", + "search_results": "Search results", + "collection_no_start_end_date": "Adding a start and end date to the collection will unlock itinerary planning features in the collection page.", + "no_results": "No results found", + "wiki_location_desc": "Pulls excerpt from Wikipedia article matching the name of the location.", + "attachments": "Attachments", + "attachment": "Attachment", + "images": "Images", + "image_modal_navigate": "Use arrow keys or click to navigate", + "generate_desc": "Generate Description", + "public_location": "Public Location", + "location_information": "Location Information", + "link": "Link", + "links": "Links", + "description": "Description", + "sources": "Sources", + "collection_adventures": "Include Collection Adventures", + "collection_locations": "Include Collection Locations", + "filter": "Filter", + "category_filter": "Category Filter", + "category": "Category", + "clear": "Clear", + "my_collections": "My Collections", + "open_filters": "Open Filters", + "archived_collections": "Archived Collections", + "share": "Share", + "private": "Private", + "public": "Public", + "archived": "Archived", + "name_location": "name, location", + "loading_adventures": "Loading adventures...", + "all_adventures_already_linked": "All adventures are already linked to this collection.", + "edit_collection": "Edit Collection", + "unarchive": "Unarchive", + "archive": "Archive", + "no_collections_to_add_location": "No collections found to add this location to.", + "create_collection_first": "Create a collection first to organize your adventures and memories.", + "done": "Done", + "adventures_available": "Adventures Available", + "collections_linked": "Collections Linked", + "not_visited": "Not Visited", + "archived_collection_message": "Collection archived successfully!", + "unarchived_collection_message": "Collection unarchived successfully!", + "delete_collection_success": "Collection deleted successfully!", + "cancel": "Cancel", + "delete_collection_warning": "Are you sure you want to delete this collection? This action cannot be undone.", + "delete_collection": "Delete Collection", + "delete_location": "Delete Location", + "location_delete_success": "Location deleted successfully!", + "visited": "Visited", + "planned": "Planned", + "duration": "Duration", + "all": "All", + "image_removed_success": "Image removed successfully!", + "image_removed_error": "Error removing image", + "no_image_url": "No image found at that URL.", + "image_upload_success": "Image uploaded successfully!", + "image_upload_error": "Error uploading image", + "dates": "Dates", + "wiki_image_error": "Error fetching image from Wikipedia", + "start_before_end_error": "Start date must be before end date", + "actions": "Actions", + "see_adventures": "See Adventures", + "image_fetch_failed": "Failed to fetch image", + "no_location": "Please enter a location", + "no_description_found": "No description found", + "location_created": "Location created", + "location_create_error": "Failed to create location", + "lodging": "Lodging", + "create_location": "Create Location", + "location_updated": "Location updated", + "location_update_error": "Failed to update location", + "set_to_pin": "Set to Pin", + "category_fetch_error": "Error fetching categories", + "new_location": "New Location", + "basic_information": "Basic Information", + "no_locations_to_recommendations": "No locations found. Add at least one location to get recommendations.", + "display_name": "Display Name", + "adventure_not_found": "There are no adventures to display. Add some using the plus button at the bottom right or try changing filters!", + "collection_contents": "Collection Contents", + "no_adventures_found": "No adventures found", + "no_locations_found": "No locations found", + "no_adventures_message": "Start documenting your adventures and planning new ones. Every journey has a story worth telling.", + "mark_visited": "Mark Visited", + "error_updating_regions": "Error updating regions", + "regions_updated": "regions updated", + "cities_updated": "cities updated", + "visited_region_check": "Visited Region Check", + "visited_region_check_desc": "By selecting this, the server will check all of your visited adventures and mark the regions they are located in as visited in world travel.", + "update_visited_regions": "Update Visited Regions", + "update_visited_regions_disclaimer": "This may take a while depending on the number of adventures you have visited.", + "link_new": "Link New...", + "add_new": "Add New...", + "transportation": "Transportation", + "note": "Note", + "checklist": "Checklist", + "collection_archived": "This collection has been archived.", + "visit_link": "Visit Link", + "collection_completed": "You've completed this collection!", + "collection_stats": "Collection Stats", + "keep_exploring": "Keep Exploring!", + "linked_adventures": "Linked Adventures", + "notes": "Notes", + "checklists": "Checklists", + "transportations": "Transportations", + "adventure_calendar": "Adventure Calendar", + "visit_calendar": "Visit Calendar", + "day": "Day", + "itineary_by_date": "Itinerary by Date", + "nothing_planned": "Nothing planned for this day. Enjoy the journey!", + "copied_to_clipboard": "Copied to clipboard!", + "copy_failed": "Copy failed", + "show": "Show", + "hide": "Hide", + "clear_location": "Clear Location", + "starting_airport": "Starting Airport", + "view_profile": "View Profile", + "joined": "Joined", + "ending_airport": "Ending Airport", + "no_location_found": "No location found", + "from": "From", + "to": "To", + "will_be_marked_location": "will be marked as visited once the location is saved.", + "start": "Start", + "end": "End", + "emoji_picker": "Emoji Picker", + "download_calendar": "Download Calendar", + "all_day": "All Day", + "ordered_itinerary": "Ordered Itinerary", + "itinerary": "Itinerary", + "all_linked_items": "All Linked Items", + "date_itinerary": "Date Itinerary", + "no_ordered_items": "Add items with dates to the collection to see them here.", + "date_information": "Date Information", + "flight_information": "Flight Information", + "out_of_range": "Not in itinerary date range", + "preview": "Preview", + "finding_recommendations": "Discovering hidden gems for your next adventure", + "location_details": "Location Details", + "city": "City", + "region": "Region", + "md_instructions": "Write your markdown here...", + "days": "days", + "attachment_upload_success": "Attachment uploaded successfully!", + "attachment_upload_error": "Error uploading attachment", + "upload": "Upload", + "attachment_delete_success": "Attachment deleted successfully!", + "attachment_update_success": "Attachment updated successfully!", + "attachment_name": "Attachment Name", + "gpx_tip": "Upload GPX files to attachments to view them on the map!", + "attachment_update_error": "Error updating attachment", + "activities": {}, + "lodging_information": "Lodging Information", + "price": "Price", + "reservation_number": "Reservation Number", + "filters_and_sort": "Filters & Sort", + "filters_and_stats": "Filters & Stats", + "travel_progress": "Travel Progress" + }, + "worldtravel": { + "country_list": "Country List", + "num_countries": "countries found", + "cities_in": "Cities in", + "all": "All", + "partially_visited": "Partially Visited", + "not_visited": "Not Visited", + "completely_visited": "Completely Visited", + "all_subregions": "All Subregions", + "clear_search": "Clear Search", + "no_countries_found": "No countries found", + "no_countries_found_desc": "Try adjusting your search terms or filters to find the countries you're looking for.", + "clear_filters": "Clear Filters", + "view_cities": "View Cities", + "no_cities_found": "No cities found", + "visit_to": "Visit to", + "region_failed_visited": "Failed to mark region as visited", + "failed_to_mark_visit": "Failed to mark visit to", + "visit_remove_failed": "Failed to remove visit", + "removed": "removed", + "failed_to_remove_visit": "Failed to remove visit to", + "marked_visited": "marked as visited", + "regions_in": "Regions in", + "cities": "cities", + "remaining": "Remaining", + "of": "of", + "countries": "countries", + "show_map": "Show Map", + "hide_map": "Hide Map", + "complete": "Complete", + "partial": "Partial", + "clear_all": "Clear All", + "no_country_data_available": "No country data available", + "no_country_data_available_desc": "Please check the documentation for updating region data.", + "total_countries": "Total Countries", + "available_to_explore": "Available to Explore", + "progress": "Progress", + "filter_by_region": "Filter by Region", + "all_regions": "All Regions", + "clear_all_filters": "Clear All Filters", + "total_items": "Total Items", + "filter_by": "Filter by", + "interactive_map": "Interactive Map", + "no_regions_found": "No regions found", + "progress_and_stats": "Progress & Stats", + "total_regions": "Total Regions", + "country_completed": "Country completed", + "show_map_labels": "Show Map Labels", + "hide_map_labels": "Hide Map Labels", + "total_cities": "Total Cities", + "region_completed": "Region completed", + "newest_first": "Newest First", + "oldest_first": "Oldest First", + "visited_first": "Visited First", + "unvisited_first": "Unvisited First" + }, + "auth": { + "username": "Username", + "password": "Password", + "forgot_password": "Forgot Password?", + "signup": "Signup", + "login_error": "Unable to login with the provided credentials.", + "login": "Login", + "email": "Email", + "first_name": "First Name", + "last_name": "Last Name", + "confirm_password": "Confirm Password", + "registration_disabled": "Registration is currently disabled.", + "profile_picture": "Profile Picture", + "public_profile": "Public Profile", + "public_tooltip": "With a public profile, users can share collections with you and view your profile on the users page.", + "new_password": "New Password (6+ characters)", + "or_3rd_party": "Or login with a third-party service", + "no_public_locations": "No public locations found", + "no_public_collections": "No public collections found", + "user_locations": "User Locations", + "user_collections": "User Collections" + }, + "users": { + "no_users_found": "No users found with public profiles." + }, + "settings": { + "update_error": "Error updating settings", + "update_success": "Settings updated successfully!", + "settings_page": "Settings Page", + "account_settings": "User Account Settings", + "update": "Update", + "no_verified_email_warning": "You must have a verified email address to enable two-factor authentication.", + "social_auth": "Social Authentication", + "social_auth_desc_1": "Manage social login options and password settings", + "password_auth": "Password Authentication", + "password_login_enabled": "Password login enabled", + "password_login_disabled": "Password login disabled", + "password_change": "Change Password", + "new_password": "New Password", + "confirm_new_password": "Confirm New Password", + "email_change": "Change Email", + "no_email_set": "No email set", + "email_management": "Email Management", + "email_management_desc": "Manage your email addresses and verification status", + "add_new_email": "Add New Email", + "add_new_email_address": "Add New Email Address", + "enter_new_email": "Enter new email address", + "new_email": "New Email", + "change_password": "Change Password", + "reset_password": "Reset Password", + "possible_reset": "If the email address you provided is associated with an account, you will receive an email with instructions to reset your password!", + "about_this_background": "About this background", + "photo_by": "Photo by", + "join_discord": "Join the Discord", + "join_discord_desc": "to share your own photos. Post them in the #travel-share channel.", + "current_password": "Current Password", + "password_change_lopout_warning": "You will be logged out after changing your password.", + "generic_error": "An error occurred while processing your request.", + "email_removed": "Email removed successfully!", + "email_removed_error": "Error removing email", + "verify_email_success": "Email verification sent successfully!", + "verify_email_error": "Error verifying email. Try again in a few minutes.", + "email_added": "Email added successfully!", + "email_set_primary": "Email set as primary successfully!", + "email_set_primary_error": "Error setting email as primary", + "verified": "Verified", + "primary": "Primary", + "not_verified": "Not Verified", + "make_primary": "Make Primary", + "verify": "Verify", + "no_emai_set": "No email set", + "mfa_disabled": "Multi-factor authentication disabled successfully!", + "mfa_page_title": "Multi-Factor Authentication", + "mfa_desc": "Add an extra layer of security to your account", + "enable_mfa": "Enable MFA", + "disable_mfa": "Disable MFA", + "enabled": "Enabled", + "disabled": "Disabled", + "mfa_not_enabled": "MFA is not enabled", + "mfa_is_enabled": "MFA is enabled", + "mfa_enabled": "Multi-factor authentication enabled successfully!", + "copy": "Copy", + "recovery_codes": "Recovery Codes", + "recovery_codes_desc": "These are your recovery codes. Keep them safe. You will not be able to see them again.", + "reset_session_error": "Please logout and back in to refresh your session and try again.", + "authenticator_code": "Authenticator Code", + "email_verified": "Email verified successfully!", + "email_verified_success": "Your email has been verified. You can now log in.", + "email_verified_error": "Error verifying email", + "email_verified_erorr_desc": "Your email could not be verified. Please try again.", + "launch_administration_panel": "Launch Administration Panel", + "administration": "Administration", + "admin_panel_desc": "Access the full administration interface", + "region_updates": "Region Updates", + "debug_information": "Debug Information", + "staff_status": "Staff Status", + "staff_user": "Staff User", + "regular_user": "Regular User", + "app_version": "App Version", + "quick_actions": "Quick Actions", + "license": "License", + "all_rights_reserved": "All rights reserved.", + "region_updates_desc": "Update visited regions and cities", + "access_restricted": "Access Restricted", + "access_restricted_desc": "Administrative features are only available to staff members.", + "advanced_settings": "Advanced Settings", + "advanced_settings_desc": "Advanced configuration and development tools", + "social_auth_setup": "Social Authentication Setup", + "administration_desc": "Administrative tools and settings", + "social_auth_desc": "Enable or disable social and OIDC authentication providers for your account. These connections allow you to sign in with self hosted authentication identity providers like Authentik or 3rd party providers like GitHub.", + "social_auth_desc_2": "These settings are managed in the AdventureLog server and must be manually enabled by the administrator.", + "documentation_link": "Documentation Link", + "launch_account_connections": "Launch Account Connections", + "add_email": "Add Email", + "password_enabled": "Password authentication enabled", + "password_disabled": "Password authentication disabled", + "password_disable_warning": "Currently, password authentication is disabled. Login via a social or OIDC provider is required.", + "password_disabled_error": "Error disabling password authentication. Make sure a social or OIDC provider is linked to your account.", + "password_enabled_error": "Error enabling password authentication.", + "settings_menu": "Settings Menu", + "security": "Security", + "emails": "Emails", + "integrations": "Integrations", + "integrations_desc": "Connect external services to enhance your experience", + "admin": "Admin", + "advanced": "Advanced", + "profile_info": "Profile Information", + "profile_info_desc": "Update your personal details and profile picture", + "public_profile_desc": "Make your profile visible to other users", + "pass_change_desc": "Update your account password for better security", + "enter_first_name": "Enter your first name", + "enter_last_name": "Enter your last name", + "enter_username": "Enter your username", + "enter_current_password": "Enter current password", + "enter_new_password": "Enter new password", + "connected": "Connected", + "disconnected": "Disconnected", + "invalid_credentials": "Invalid credentials", + "backup_restore": "Backup & Restore", + "backup_restore_desc": "Save your data or restore it from a previous backup file.", + "whats_included": "What's included", + "mfa_required": "MFA Required", + "secure_your_account": "Secure your account", + "setup_required": "Setup Required", + "scan_qr_code": "Scan QR Code", + "scan_with_authenticator_app": "Scan with authenticator app", + "manual_entry": "Manual Entry", + "verify_setup": "Verify Setup", + "enter_6_digit_code": "Enter 6 digit code", + "enter_code_from_app": "Enter code from app", + "copy_all": "Copy all", + "important": "Important", + "error_occurred": "An error has occurred", + "mfa_already_enabled": "MFA already enabled", + "complete_setup_to_enable": "Complete setup to enable MFA", + "world_travel_visits": "World Travel Visits", + "media": "Media", + "integrations_settings": "Integrations Settings", + "backup_your_data": "Backup Your Data", + "backup_your_data_desc": "Download a complete backup of your account data including locations, \t\t\t\t\t\t\t\t\t\tcollections, media, and visits.", + "restore_data": "Restore Data", + "restore_data_desc": "Upload a backup file to restore your data.", + "data_override_warning": "Data Override Warning", + "data_override_warning_desc": "Restoring data will completely replace all existing data (that is included \t\t\t\t\t\t\t\t\t\t\t\tin the backup) in your account. This action cannot be undone.", + "select_backup_file": "Select backup file", + "data_override_acknowledge": "I acknowledge that this will override all my existing data", + "data_override_acknowledge_desc": "This action is irreversible and will replace all locations, collections, \t\t\t\t\t\t\t\t\t\t\t\t\t\tand visits in your account." + }, + "collection": { + "collection_created": "Collection created successfully!", + "error_creating_collection": "Error creating collection", + "new_collection": "New Collection", + "create": "Create", + "collection_edit_success": "Collection edited successfully!", + "error_editing_collection": "Error editing collection", + "public_collection": "Public Collection", + "manage_collections": "Manage Collections", + "no_collections_yet": "No collections yet", + "no_shared_collections": "No shared collections.", + "shared_collections": "Shared Collections", + "no_archived_collections": "No archived collections.", + "create_first": "Create your first collection to organize your adventures and memories.", + "make_sure_public": "Make sure your profile is public so others can share with you.", + "archived_appear_here": "Archived collections will appear here.", + "linked": "Linked", + "available": "Available", + "try_different_search": "Try a different search or filter." + }, + "notes": { + "note_deleted": "Note deleted successfully!", + "note_delete_error": "Error deleting note", + "open": "Open", + "failed_to_save": "Failed to save note", + "note_editor": "Note Editor", + "note_viewer": "Note Viewer", + "editing_note": "Editing note", + "content": "Content", + "save": "Save", + "note_public": "This note is public because it is in a public collection.", + "add_a_link": "Add a link", + "invalid_url": "Invalid URL" + }, + "checklist": { + "checklist_deleted": "Checklist deleted successfully!", + "checklist_delete_error": "Error deleting checklist", + "checklist_editor": "Checklist Editor", + "new_checklist": "New Checklist", + "item": "Item", + "items": "Items", + "new_item": "New Item", + "checklist_public": "This checklist is public because it is in a public collection.", + "item_cannot_be_empty": "Item cannot be empty", + "item_already_exists": "Item already exists" + }, + "transportation": { + "transportation_deleted": "Transportation deleted successfully!", + "transportation_delete_error": "Error deleting transportation", + "type": "Type", + "new_transportation": "New Transportation", + "flight_number": "Flight Number", + "from_location": "From Location", + "to_location": "To Location", + "fetch_location_information": "Fetch Location Information", + "starting_airport_desc": "Enter starting airport code (e.g., JFK)", + "ending_airport_desc": "Enter ending airport code (e.g., LAX)", + "edit": "Edit", + "modes": { + "car": "Car", + "plane": "Plane", + "train": "Train", + "bus": "Bus", + "boat": "Boat", + "bike": "Bike", + "walking": "Walking", + "other": "Other" + }, + "edit_transportation": "Edit Transportation" + }, + "lodging": { + "new_lodging": "New Lodging", + "edit": "Edit", + "edit_lodging": "Edit Lodging", + "hotel": "Hotel", + "hostel": "Hostel", + "resort": "Resort", + "bnb": "Bed and Breakfast", + "campground": "Campground", + "cabin": "Cabin", + "apartment": "Apartment", + "house": "House", + "villa": "Villa", + "motel": "Motel", + "other": "Other", + "reservation_number": "Reservation Number" + }, + "search": { + "adventurelog_results": "AdventureLog Results", + "public_adventures": "Public Adventures", + "online_results": "Online Results", + "result": "Result", + "results": "Results", + "found": "found", + "try_searching_desc": "Try searching for adventures, collections, countries, regions, cities, or users.", + "countries": "Countries", + "cities": "Cities" + }, + "map": { + "view_details": "View Details", + "adventure_map": "Adventure Map", + "location_map": "Location Map", + "map_options": "Map Options", + "show_visited_regions": "Show Visited Regions", + "add_adventure_at_marker": "Add New Adventure at Marker", + "add_location_at_marker": "Add New Location at Marker", + "clear_marker": "Clear Marker", + "add_adventure": "Add New Adventure", + "add_location": "Add New Location", + "adventure_stats": "Adventure Stats", + "map_controls": "Map Controls", + "regions": "Regions", + "completion": "Completion", + "display_options": "Display Options", + "marker_placed_on_map": "Marker placed on map", + "place_marker_desc_location": "Click on the map to place a marker.", + "locations_shown": "locations shown" + }, + "share": { + "shared": "Shared", + "with": "with", + "unshared": "Unshared", + "share_desc": "Share this collection with other users.", + "shared_with": "Shared With", + "no_users_shared": "No users shared with", + "not_shared_with": "Not Shared With", + "no_shared_found": "No collections found that are shared with you.", + "set_public": "In order to allow users to share with you, you need your profile set to public.", + "go_to_settings": "Go to settings" + }, + "languages": {}, + "profile": { + "member_since": "Member since", + "user_stats": "User Stats", + "visited_countries": "Visited Countries", + "visited_regions": "Visited Regions", + "visited_cities": "Visited Cities", + "travel_statistics": "Travel Statistics", + "your_journey_at_a_glance": "Your adventure journey at a glance", + "planned_trips": "Planned trips", + "discovered": "discovered", + "explored": "explored", + "public_location_experiences": "Public location experiences", + "no_shared_adventures": "This user hasn't shared any public adventures yet.", + "no_shared_collections": "This user hasn't shared any public collections yet." + }, + "categories": { + "manage_categories": "Manage Categories", + "no_categories_found": "No categories found.", + "edit_category": "Edit Category", + "icon": "Icon", + "location_update_after_refresh": "The location cards will be updated once you refresh the page.", + "select_category": "Select Category", + "category_name": "Category Name", + "add_new_category": "Add New Category", + "name_required": "Category name is required" + }, + "dashboard": { + "welcome_back": "Welcome back", + "countries_visited": "Countries Visited", + "total_adventures": "Total Adventures", + "total_visited_regions": "Total Visited Regions", + "total_visited_cities": "Total Visited Cities", + "recent_adventures": "Recent Adventures", + "no_recent_adventures": "No recent adventures?", + "add_some": "Why not start planning your next adventure? You can add a new adventure by clicking the button below.", + "document_some_adventures": "Start documenting your travels and build your personal adventure map!", + "view_all": "View All", + "welcome_text_1": "You've been on", + "welcome_text_2": "adventures so far", + "welcome_text_3": "Keep exploring and documenting your travels!" + }, + "immich": { + "immich": "Immich", + "integration_fetch_error": "Error fetching data from the Immich integration", + "no_items_found": "No items found", + "load_more": "Load More", + "immich_error": "Error updating Immich integration", + "immich_disabled": "Immich integration disabled successfully!", + "disable": "Disable", + "server_url": "Immich Server URL", + "api_note": "Note: this must be the URL to the Immich API server so it likely ends with /api unless you have a custom config.", + "api_key": "Immich API Key", + "enable_integration": "Enable Integration", + "update_integration": "Update Integration", + "immich_integration_desc": "Connect your Immich photo management server", + "localhost_note": "Note: localhost will most likely not work unless you have setup docker networks accordingly. It is recommended to use the IP address of the server or the domain name.", + "api_key_placeholder": "Enter your Immich API key", + "need_help": "Need help setting this up? Check out the", + "copy_locally": "Copy Images Locally", + "copy_locally_desc": "Copy images to the server for offline access. Uses more disk space.", + "error_saving_image": "Error saving image", + "connection_error": "Error connecting to Immich server", + "integration_already_exists": "An Immich integration already exists. You can only have one integration at a time.", + "integration_not_found": "Immich integration not found. Please create a new integration.", + "validation_error": "An error occurred while validating the Immich integration. Please check your server URL and API key.", + "network_error": "Network error while connecting to the Immich server. Please check your connection and try again." + }, + "google_maps": { + "google_maps_integration_desc": "Connect your Google Maps account to get high-quality location search results and recommendations." + }, + "recomendations": { + "recommendation": "Recommendation", + "recommendations": "Recommendations", + "location_recommendations": "Location Recommendations", + "food": "Food", + "tourism": "Tourism" + }, + "calendar": { + "today": "Today", + "month": "Month", + "week": "Week", + "day": "Day", + "events_scheduled": "events scheduled", + "total_events": "Total Events", + "calendar_overview": "Calendar Overview", + "filtered_results": "Filtered Results", + "all_day_event": "All Day Event" + }, + "locations": { + "location": "Location", + "locations": "Locations", + "my_locations": "My Locations" + }, + "settings_download_backup": "Download Backup" } diff --git a/frontend/src/locales/es.json b/frontend/src/locales/es.json index a213e33..d2ba774 100644 --- a/frontend/src/locales/es.json +++ b/frontend/src/locales/es.json @@ -43,7 +43,15 @@ "nominatim_2": "Sus datos están licenciados bajo la licencia ODbL.", "other_attributions": "Atribuciones adicionales se pueden encontrar en el archivo README.", "close": "Cerrar", - "generic_attributions": "Inicie sesión en AdventureLog para ver las atribuciones para integraciones y servicios habilitados." + "generic_attributions": "Inicie sesión en AdventureLog para ver las atribuciones para integraciones y servicios habilitados.", + "attributions": "Atribuciones", + "developer": "Revelador", + "license_info": "Licencia", + "sponsor": "Patrocinador", + "thank_you": "¡Gracias por usar Adventurelog!", + "version": "Versión", + "view_changelog": "Ver ChangeLog", + "view_license": "Ver licencia" }, "home": { "hero_1": "Descubre las Aventuras Más Emocionantes del Mundo", @@ -302,7 +310,8 @@ "visit_calendar": "Visitar el calendario", "wiki_location_desc": "Extrae extracto del artículo de Wikipedia que coincide con el nombre de la ubicación.", "will_be_marked_location": "se marcará según lo visitado una vez que se guarde la ubicación.", - "no_locations_found": "No se encontraron ubicaciones" + "no_locations_found": "No se encontraron ubicaciones", + "image_modal_navigate": "Use teclas de flecha o haga clic para navegar" }, "worldtravel": { "all": "Todo", @@ -497,7 +506,36 @@ "all_rights_reserved": "Reservados todos los derechos.", "email_verified_erorr_desc": "Su correo electrónico no pudo ser verificado. \nPor favor intente de nuevo.", "no_emai_set": "Sin conjunto de correo electrónico", - "invalid_credentials": "Credenciales no válidas" + "invalid_credentials": "Credenciales no válidas", + "backup_restore": "Respaldo", + "backup_restore_desc": "Guardar sus datos o restaurarlo desde un archivo de copia de seguridad anterior.", + "complete_setup_to_enable": "Configuración completa para habilitar MFA", + "copy_all": "Copiar todo", + "enter_6_digit_code": "Ingrese el código de 6 dígitos", + "enter_code_from_app": "Ingrese el código desde la aplicación", + "error_occurred": "Se ha producido un error", + "important": "Importante", + "manual_entry": "Entrada manual", + "mfa_already_enabled": "MFA ya habilitado", + "mfa_required": "MFA requerido", + "scan_qr_code": "Escanear el código QR", + "scan_with_authenticator_app": "Escanear con la aplicación Authenticator", + "secure_your_account": "Asegure su cuenta", + "setup_required": "Configuración requerida", + "verify_setup": "Verificar la configuración", + "whats_included": "¿Qué está incluido?", + "backup_your_data": "Haga una copia de seguridad de sus datos", + "backup_your_data_desc": "Descargue una copia de seguridad completa de los datos de su cuenta, incluidas ubicaciones, colecciones, medios y visitas.", + "data_override_acknowledge": "Reconozco que esto anulará todos mis datos existentes.", + "data_override_acknowledge_desc": "Esta acción es irreversible y reemplazará todas las ubicaciones, colecciones y visitas en su cuenta.", + "data_override_warning": "Advertencia de anulación de datos", + "data_override_warning_desc": "La restauración de datos reemplazará completamente todos los datos existentes (que se incluyen en la copia de seguridad) en su cuenta. \nEsta acción no se puede deshacer.", + "integrations_settings": "Configuración de integraciones", + "media": "Medios de comunicación", + "restore_data": "Restaurar datos", + "restore_data_desc": "Cargue un archivo de copia de seguridad para restaurar sus datos.", + "select_backup_file": "Seleccione el archivo de copia de seguridad", + "world_travel_visits": "Visitas de viajes mundiales" }, "checklist": { "checklist_delete_error": "Error al eliminar la lista de tareas", @@ -722,5 +760,6 @@ "location": "Ubicación", "locations": "Ubicación", "my_locations": "Mis ubicaciones" - } + }, + "settings_download_backup": "Descargar copia de seguridad" } diff --git a/frontend/src/locales/fr.json b/frontend/src/locales/fr.json index 1942756..cda8406 100644 --- a/frontend/src/locales/fr.json +++ b/frontend/src/locales/fr.json @@ -9,7 +9,15 @@ "oss_attributions": "Attributions Open Source", "other_attributions": "Des attributions supplémentaires peuvent être trouvées dans le fichier README.", "source_code": "Code source", - "generic_attributions": "Connectez-vous à AdventureLog pour afficher les attributions pour les intégrations et services activés." + "generic_attributions": "Connectez-vous à AdventureLog pour afficher les attributions pour les intégrations et services activés.", + "attributions": "Attributions", + "developer": "Promoteur", + "license_info": "Licence", + "sponsor": "Parrainer", + "thank_you": "Merci d'utiliser AdventureLog!", + "version": "Version", + "view_changelog": "Voir Changelog", + "view_license": "Afficher la licence" }, "adventures": { "activities": {}, @@ -250,7 +258,8 @@ "visit_calendar": "Visiter le calendrier", "wiki_location_desc": "Tire un extrait de l'article de Wikipedia correspondant au nom de l'emplacement.", "will_be_marked_location": "sera marqué comme visité une fois l'emplacement enregistré.", - "no_locations_found": "Aucun emplacement trouvé" + "no_locations_found": "Aucun emplacement trouvé", + "image_modal_navigate": "Utilisez des touches flèches ou cliquez pour naviguer" }, "home": { "desc_1": "Découvrez, planifiez et explorez en toute simplicité", @@ -497,7 +506,36 @@ "disconnected": "Déconnecté", "email_management": "Gestion des e-mails", "enter_last_name": "Entrez votre nom de famille", - "invalid_credentials": "Des références non valides" + "invalid_credentials": "Des références non valides", + "backup_restore": "Sauvegarde", + "backup_restore_desc": "Enregistrez vos données ou restaurez-les à partir d'un fichier de sauvegarde précédent.", + "complete_setup_to_enable": "Configuration complète pour activer le MFA", + "copy_all": "Copier tout", + "enter_6_digit_code": "Entrez le code à 6 chiffres", + "enter_code_from_app": "Entrez le code à partir de l'application", + "error_occurred": "Une erreur s'est produite", + "important": "Important", + "manual_entry": "Entrée manuelle", + "mfa_already_enabled": "MFA déjà activé", + "mfa_required": "MFA requis", + "scan_qr_code": "Scanner le code QR", + "scan_with_authenticator_app": "Scanner avec l'application Authenticatrice", + "secure_your_account": "Sécuriser votre compte", + "setup_required": "Configuration requise", + "verify_setup": "Vérifiez la configuration", + "whats_included": "Ce qui est inclus", + "backup_your_data": "Sauvegardez vos données", + "backup_your_data_desc": "Téléchargez une sauvegarde complète des données de votre compte, y compris les emplacements, les collections, les médias et les visites.", + "data_override_acknowledge": "Je reconnais que cela remplacera toutes mes données existantes", + "data_override_acknowledge_desc": "Cette action est irréversible et remplacera tous les emplacements, collections et visites dans votre compte.", + "data_override_warning": "Avertissement de remplacement des données", + "data_override_warning_desc": "La restauration des données remplacera complètement toutes les données existantes (incluses dans la sauvegarde) dans votre compte. \nCette action ne peut pas être annulée.", + "integrations_settings": "Paramètres des intégrations", + "media": "Médias", + "restore_data": "Restaurer les données", + "restore_data_desc": "Téléchargez un fichier de sauvegarde pour restaurer vos données.", + "select_backup_file": "Sélectionnez le fichier de sauvegarde", + "world_travel_visits": "Visites de voyage dans le monde" }, "checklist": { "checklist_delete_error": "Erreur lors de la suppression de la liste de contrôle", @@ -722,5 +760,6 @@ "location": "Emplacement", "locations": "Lieux", "my_locations": "Mes emplacements" - } + }, + "settings_download_backup": "Télécharger la sauvegarde" } diff --git a/frontend/src/locales/it.json b/frontend/src/locales/it.json index c2d4197..c294125 100644 --- a/frontend/src/locales/it.json +++ b/frontend/src/locales/it.json @@ -9,7 +9,15 @@ "oss_attributions": "Attribuzioni Open Source", "other_attributions": "Ulteriori attribuzioni possono essere trovate nel file README.", "source_code": "Codice sorgente", - "generic_attributions": "Accedi a AdventureLog per visualizzare le attribuzioni per integrazioni e servizi abilitati." + "generic_attributions": "Accedi a AdventureLog per visualizzare le attribuzioni per integrazioni e servizi abilitati.", + "attributions": "Attribuzioni", + "developer": "Sviluppatore", + "license_info": "Licenza", + "sponsor": "Sponsor", + "thank_you": "Grazie per aver usato AdventureLog!", + "version": "Versione", + "view_changelog": "Visualizza Changelog", + "view_license": "Visualizza la licenza" }, "adventures": { "activities": {}, @@ -250,7 +258,8 @@ "visit_calendar": "Visita il calendario", "wiki_location_desc": "Estratto dall'articolo di Wikipedia che corrisponde al nome della posizione.", "will_be_marked_location": "sarà contrassegnato come visitato una volta salvata la posizione.", - "no_locations_found": "Nessuna posizione trovata" + "no_locations_found": "Nessuna posizione trovata", + "image_modal_navigate": "Usa i tasti freccia o fai clic per navigare" }, "home": { "desc_1": "Scopri, pianifica ed esplora con facilità", @@ -497,7 +506,36 @@ "staff_status": "Stato del personale", "staff_user": "Utente del personale", "password_auth": "Autenticazione della password", - "invalid_credentials": "Credenziali non valide" + "invalid_credentials": "Credenziali non valide", + "backup_restore": "Backup", + "backup_restore_desc": "Salva i tuoi dati o ripristinarli da un precedente file di backup.", + "complete_setup_to_enable": "Setup completa per abilitare MFA", + "copy_all": "Copia tutto", + "enter_6_digit_code": "Immettere il codice a 6 cifre", + "enter_code_from_app": "Immettere il codice dall'app", + "error_occurred": "Si è verificato un errore", + "important": "Importante", + "manual_entry": "Ingresso manuale", + "mfa_already_enabled": "MFA già abilitato", + "mfa_required": "MFA richiesto", + "scan_qr_code": "Scansionare il codice QR", + "scan_with_authenticator_app": "Scansiona con l'app Authenticator", + "secure_your_account": "Proteggere il tuo account", + "setup_required": "Setup richiesto", + "verify_setup": "Verifica la configurazione", + "whats_included": "Cosa è incluso", + "backup_your_data": "Eseguire il backup dei tuoi dati", + "backup_your_data_desc": "Scarica un backup completo dei dati dell'account tra cui posizioni, raccolte, media e visite.", + "data_override_acknowledge": "Riconosco che questo sovrascriverà tutti i miei dati esistenti", + "data_override_acknowledge_desc": "Questa azione è irreversibile e sostituirà tutte le posizioni, le raccolte e le visite nel tuo account.", + "data_override_warning": "Avviso di sovraccarico dei dati", + "data_override_warning_desc": "Il ripristino dei dati sostituirà completamente tutti i dati esistenti (inclusi nel backup) nel tuo account. \nQuesta azione non può essere annullata.", + "integrations_settings": "Impostazioni integrazioni", + "media": "Media", + "restore_data": "Ripristina i dati", + "restore_data_desc": "Carica un file di backup per ripristinare i dati.", + "select_backup_file": "Seleziona il file di backup", + "world_travel_visits": "Visite di viaggio nel mondo" }, "checklist": { "checklist_delete_error": "Errore durante l'eliminazione della lista di controllo", @@ -722,5 +760,6 @@ "location": "Posizione", "locations": "Luoghi", "my_locations": "Le mie posizioni" - } + }, + "settings_download_backup": "Scarica il backup" } diff --git a/frontend/src/locales/ko.json b/frontend/src/locales/ko.json index 669f2a4..6e818cc 100644 --- a/frontend/src/locales/ko.json +++ b/frontend/src/locales/ko.json @@ -9,7 +9,15 @@ "oss_attributions": "오픈 소스 속성", "other_attributions": "추가 속성은 README 파일에서 찾을 수 있습니다.", "source_code": "소스 코드", - "generic_attributions": "Adventurelog에 로그인하여 활성화 된 통합 및 서비스에 대한 속성을보십시오." + "generic_attributions": "Adventurelog에 로그인하여 활성화 된 통합 및 서비스에 대한 속성을보십시오.", + "attributions": "속성", + "developer": "개발자", + "license_info": "특허", + "sponsor": "스폰서", + "thank_you": "Adventurelog를 사용해 주셔서 감사합니다!", + "version": "버전", + "view_changelog": "changelog를 봅니다", + "view_license": "라이센스보기" }, "adventures": { "actions": "행동", @@ -250,7 +258,8 @@ "visit_calendar": "캘린더를 방문하십시오", "wiki_location_desc": "위치 이름과 일치하는 Wikipedia 기사에서 발췌 한 내용을 가져옵니다.", "will_be_marked_location": "위치가 저장되면 방문한대로 표시됩니다.", - "no_locations_found": "발견 된 위치는 없습니다" + "no_locations_found": "발견 된 위치는 없습니다", + "image_modal_navigate": "화살표 키를 사용하거나 클릭하여 탐색하십시오" }, "auth": { "confirm_password": "비밀번호 확인", @@ -589,7 +598,36 @@ "social_auth_setup": "소셜 인증 설정", "staff_status": "직원 상태", "staff_user": "직원 사용자", - "invalid_credentials": "잘못된 자격 증명" + "invalid_credentials": "잘못된 자격 증명", + "backup_restore": "지원", + "backup_restore_desc": "데이터를 저장하거나 이전 백업 파일에서 복원하십시오.", + "complete_setup_to_enable": "MFA를 활성화하기위한 완전한 설정", + "copy_all": "모두 복사하십시오", + "enter_6_digit_code": "6 자리 코드를 입력하십시오", + "enter_code_from_app": "앱에서 코드를 입력하십시오", + "error_occurred": "오류가 발생했습니다", + "important": "중요한", + "manual_entry": "수동 입력", + "mfa_already_enabled": "MFA는 이미 활성화되었습니다", + "mfa_required": "MFA가 필요합니다", + "scan_qr_code": "QR 코드를 스캔하십시오", + "scan_with_authenticator_app": "Authenticator 앱으로 스캔하십시오", + "secure_your_account": "계정을 확보하십시오", + "setup_required": "설정이 필요합니다", + "verify_setup": "설정을 확인하십시오", + "whats_included": "포함 된 내용", + "backup_your_data": "데이터를 백업하십시오", + "backup_your_data_desc": "위치, 컬렉션, 미디어 및 방문을 포함하여 계정 데이터의 전체 백업을 다운로드하십시오.", + "data_override_acknowledge": "나는 이것이 나의 모든 기존 데이터를 무시할 것임을 인정합니다.", + "data_override_acknowledge_desc": "이 조치는 돌이킬 수 없으며 계정의 모든 위치, 컬렉션 및 방문을 대체합니다.", + "data_override_warning": "데이터를 우선적으로 경고합니다", + "data_override_warning_desc": "데이터 복원은 계정의 모든 기존 데이터 (백업에 포함 된)를 완전히 대체합니다. \n이 조치는 취소 할 수 없습니다.", + "integrations_settings": "통합 설정", + "media": "메디아", + "restore_data": "데이터를 복원하십시오", + "restore_data_desc": "백업 파일을 업로드하여 데이터를 복원하십시오.", + "select_backup_file": "백업 파일을 선택하십시오", + "world_travel_visits": "세계 여행 방문" }, "share": { "go_to_settings": "설정으로 이동", @@ -721,5 +759,6 @@ "location": "위치", "locations": "위치", "my_locations": "내 위치" - } + }, + "settings_download_backup": "백업을 다운로드하십시오" } diff --git a/frontend/src/locales/nl.json b/frontend/src/locales/nl.json index 93741c6..a04a27f 100644 --- a/frontend/src/locales/nl.json +++ b/frontend/src/locales/nl.json @@ -9,7 +9,15 @@ "oss_attributions": "Open source gebruik", "other_attributions": "Aanvullende vermeldingen zijn te vinden in het README-bestand.", "source_code": "Broncode", - "generic_attributions": "Log in op AdventUrelog om attributies te bekijken voor ingeschakelde integraties en services." + "generic_attributions": "Log in op AdventUrelog om attributies te bekijken voor ingeschakelde integraties en services.", + "attributions": "Attributies", + "developer": "Ontwikkelaar", + "license_info": "Licentie", + "sponsor": "Sponsor", + "thank_you": "Bedankt voor het gebruik van AdventUrelog!", + "version": "Versie", + "view_changelog": "Bekijk Changelog", + "view_license": "Bekijk licentie" }, "adventures": { "activities": {}, @@ -250,7 +258,8 @@ "visit_calendar": "Bezoek de agenda", "wiki_location_desc": "Haalt fragment uit het Wikipedia -artikel dat overeenkomt met de naam van de locatie.", "will_be_marked_location": "wordt gemarkeerd als bezocht zodra de locatie is opgeslagen.", - "no_locations_found": "Geen locaties gevonden" + "no_locations_found": "Geen locaties gevonden", + "image_modal_navigate": "Gebruik pijltoetsen of klik om te navigeren" }, "home": { "desc_1": "Ontdek, plan en verken met gemak", @@ -497,7 +506,36 @@ "staff_status": "Status", "staff_user": "Personeelsgebruiker", "connected": "Aangesloten", - "invalid_credentials": "Ongeldige referenties" + "invalid_credentials": "Ongeldige referenties", + "backup_restore": "Back -up", + "backup_restore_desc": "Sla uw gegevens op of herstel deze in een eerder back -upbestand.", + "complete_setup_to_enable": "Volledige installatie om MFA in te schakelen", + "copy_all": "Kopieer alles", + "enter_6_digit_code": "Voer 6 cijfercode in", + "enter_code_from_app": "Voer de code uit van app", + "error_occurred": "Er is een fout opgetreden", + "important": "Belangrijk", + "manual_entry": "Handmatig invoer", + "mfa_already_enabled": "MFA al ingeschakeld", + "setup_required": "Instellingen vereist", + "verify_setup": "Controleer de installatie", + "whats_included": "Wat is inbegrepen", + "mfa_required": "MFA vereist", + "scan_qr_code": "Scan QR -code", + "scan_with_authenticator_app": "Scan met authenticator -app", + "secure_your_account": "Beveilig uw account", + "backup_your_data": "Back -up van uw gegevens", + "backup_your_data_desc": "Download een volledige back -up van uw accountgegevens, inclusief locaties, collecties, media en bezoeken.", + "data_override_acknowledge": "Ik erken dat dit al mijn bestaande gegevens zal overschrijven", + "data_override_acknowledge_desc": "Deze actie is onomkeerbaar en vervangt alle locaties, collecties en bezoeken in uw account.", + "data_override_warning": "Gegevensoverschrijdende waarschuwing", + "data_override_warning_desc": "Het herstellen van gegevens zal alle bestaande gegevens (die in de back -up zijn opgenomen) in uw account volledig vervangen. \nDeze actie kan niet ongedaan worden gemaakt.", + "integrations_settings": "Instellingen integraties", + "media": "Media", + "restore_data": "Herstel gegevens", + "restore_data_desc": "Upload een back -upbestand om uw gegevens te herstellen.", + "select_backup_file": "Selecteer back -upbestand", + "world_travel_visits": "Wereldreizen bezoeken" }, "checklist": { "checklist_delete_error": "Fout bij het verwijderen van de checklist", @@ -722,5 +760,6 @@ "location": "Locatie", "locations": "Locaties", "my_locations": "Mijn locaties" - } + }, + "settings_download_backup": "Download back -up" } diff --git a/frontend/src/locales/no.json b/frontend/src/locales/no.json index 089a192..3e4d00f 100644 --- a/frontend/src/locales/no.json +++ b/frontend/src/locales/no.json @@ -43,7 +43,15 @@ "nominatim_2": "Deres data er lisensiert under ODbL-lisensen.", "other_attributions": "Ytterligere attribusjoner finnes i README-filen.", "close": "Lukk", - "generic_attributions": "Logg inn på Adventurelog for å se attribusjoner for aktiverte integrasjoner og tjenester." + "generic_attributions": "Logg inn på Adventurelog for å se attribusjoner for aktiverte integrasjoner og tjenester.", + "attributions": "Attribusjoner", + "developer": "Utvikler", + "license_info": "Tillatelse", + "sponsor": "Sponsor", + "thank_you": "Takk for at du bruker Adventurelog!", + "version": "Versjon", + "view_changelog": "Se Changelog", + "view_license": "Vis lisens" }, "home": { "hero_1": "Oppdag verdens mest spennende eventyr", @@ -302,7 +310,8 @@ "visit_calendar": "Besøk kalenderen", "wiki_location_desc": "Trekker utdrag fra Wikipedia -artikkelen som samsvarer med navnet på stedet.", "will_be_marked_location": "vil bli merket som besøkt når stedet er lagret.", - "no_locations_found": "Ingen steder funnet" + "no_locations_found": "Ingen steder funnet", + "image_modal_navigate": "Bruk piltastene eller klikk for å navigere" }, "worldtravel": { "country_list": "Liste over land", @@ -497,7 +506,36 @@ "social_auth_setup": "Sosial autentiseringsoppsett", "staff_status": "Personalstatus", "staff_user": "Personalbruker", - "invalid_credentials": "Ugyldig legitimasjon" + "invalid_credentials": "Ugyldig legitimasjon", + "backup_restore": "Sikkerhetskopi", + "backup_restore_desc": "Lagre dataene dine eller gjenopprett dem fra en tidligere sikkerhetskopifil.", + "complete_setup_to_enable": "Komplett oppsett for å aktivere MFA", + "copy_all": "Kopier alle", + "enter_6_digit_code": "Skriv inn 6 -sifret kode", + "enter_code_from_app": "Skriv inn kode fra appen", + "error_occurred": "Det har oppstått en feil", + "important": "Viktig", + "manual_entry": "Manuell oppføring", + "mfa_already_enabled": "MFA er allerede aktivert", + "mfa_required": "MFA kreves", + "scan_qr_code": "Skann QR -kode", + "scan_with_authenticator_app": "Skann med Authenticator -appen", + "secure_your_account": "Sikre kontoen din", + "setup_required": "Oppsett kreves", + "verify_setup": "Bekreft oppsett", + "whats_included": "Hva som er inkludert", + "backup_your_data": "Sikkerhetskopiere dataene dine", + "backup_your_data_desc": "Last ned en komplett sikkerhetskopi av kontodataene dine, inkludert steder, samlinger, media og besøk.", + "data_override_acknowledge": "Jeg erkjenner at dette vil overstyre alle mine eksisterende data", + "data_override_acknowledge_desc": "Denne handlingen er irreversibel og vil erstatte alle steder, samlinger og besøk på kontoen din.", + "data_override_warning": "Data overstyrer advarsel", + "data_override_warning_desc": "Gjenoppretting av data vil erstatte alle eksisterende data fullstendig (som er inkludert i sikkerhetskopien) på kontoen din. \nDenne handlingen kan ikke angres.", + "integrations_settings": "Integrasjoner Innstillinger", + "media": "Media", + "restore_data": "Gjenopprett data", + "restore_data_desc": "Last opp en sikkerhetskopifil for å gjenopprette dataene dine.", + "select_backup_file": "Velg sikkerhetskopifil", + "world_travel_visits": "Verdens reisebesøk" }, "collection": { "collection_created": "Samling opprettet!", @@ -722,5 +760,6 @@ "location": "Sted", "locations": "Lokasjoner", "my_locations": "Mine lokasjoner" - } + }, + "settings_download_backup": "Last ned sikkerhetskopi" } diff --git a/frontend/src/locales/pl.json b/frontend/src/locales/pl.json index 260e045..eab34eb 100644 --- a/frontend/src/locales/pl.json +++ b/frontend/src/locales/pl.json @@ -43,7 +43,15 @@ "nominatim_2": "Ich dane są licencjonowane na licencji ODbL.", "other_attributions": "Dodatkowe atrybucje można znaleźć w pliku README.", "close": "Zamknij", - "generic_attributions": "Zaloguj się do Adventurelog, aby wyświetlić atrybucje dla włączonych integracji i usług." + "generic_attributions": "Zaloguj się do Adventurelog, aby wyświetlić atrybucje dla włączonych integracji i usług.", + "attributions": "Przypisania", + "developer": "Wywoływacz", + "license_info": "Licencja", + "sponsor": "Sponsor", + "thank_you": "Dziękujemy za korzystanie z Adventurelog!", + "version": "Wersja", + "view_changelog": "Zobacz Changelog", + "view_license": "Wyświetl licencję" }, "home": { "hero_1": "Odkryj najbardziej ekscytujące podróże na świecie", @@ -302,7 +310,8 @@ "visit_calendar": "Odwiedź kalendarz", "wiki_location_desc": "Wyciąga fragment artykułu Wikipedii pasujący do nazwy lokalizacji.", "will_be_marked_location": "zostanie oznaczone jako odwiedzone po zapisaniu lokalizacji.", - "no_locations_found": "Nie znaleziono żadnych lokalizacji" + "no_locations_found": "Nie znaleziono żadnych lokalizacji", + "image_modal_navigate": "Użyj klawiszy strzałek lub kliknij, aby nawigować" }, "worldtravel": { "country_list": "Lista krajów", @@ -497,7 +506,36 @@ "social_auth_setup": "Konfiguracja uwierzytelniania społecznego", "staff_status": "Status personelu", "staff_user": "Użytkownik personelu", - "invalid_credentials": "Nieprawidłowe poświadczenia" + "invalid_credentials": "Nieprawidłowe poświadczenia", + "backup_restore": "Kopia zapasowa", + "backup_restore_desc": "Zapisz dane lub przywróć je z poprzedniego pliku kopii zapasowej.", + "complete_setup_to_enable": "Pełna konfiguracja, aby włączyć MFA", + "copy_all": "Kopiuj wszystko", + "enter_6_digit_code": "Wprowadź 6 -cyfrowy kod", + "enter_code_from_app": "Wprowadź kod z aplikacji", + "error_occurred": "Wystąpił błąd", + "important": "Ważny", + "manual_entry": "Wpis ręczny", + "mfa_already_enabled": "MFA już włączona", + "mfa_required": "Wymagane MSZ", + "scan_qr_code": "Skanuj kod QR", + "scan_with_authenticator_app": "Skanuj za pomocą aplikacji Authenticator", + "secure_your_account": "Zabezpiecz swoje konto", + "setup_required": "Wymagana konfiguracja", + "verify_setup": "Sprawdź konfigurację", + "whats_included": "Co jest uwzględnione", + "backup_your_data": "Kopie wykonuj kopię zapasową danych", + "backup_your_data_desc": "Pobierz pełną kopię zapasową danych konta, w tym lokalizacji, kolekcji, mediów i wizyt.", + "data_override_acknowledge": "Przyjmuję do wiadomości, że zastąpi to wszystkie moje istniejące dane", + "data_override_acknowledge_desc": "Ta akcja jest nieodwracalna i zastąpi wszystkie lokalizacje, kolekcje i wizyty na Twoim koncie.", + "data_override_warning": "Zastąpienie danych ostrzeżenia", + "data_override_warning_desc": "Przywracanie danych całkowicie zastąpi wszystkie istniejące dane (które są zawarte w tworzeniu kopii zapasowej) na Twoim koncie. \nTego działania nie można cofnąć.", + "integrations_settings": "Ustawienia integracji", + "media": "Głoska bezdźwięczna", + "restore_data": "Przywróć dane", + "restore_data_desc": "Prześlij plik kopii zapasowej, aby przywrócić dane.", + "select_backup_file": "Wybierz plik kopii zapasowej", + "world_travel_visits": "Wizyty podróży na świecie" }, "collection": { "collection_created": "Kolekcja została pomyślnie utworzona!", @@ -722,5 +760,6 @@ "location": "Lokalizacja", "locations": "Lokalizacje", "my_locations": "Moje lokalizacje" - } + }, + "settings_download_backup": "Pobierz kopię zapasową" } diff --git a/frontend/src/locales/ru.json b/frontend/src/locales/ru.json index 0511cbe..a9ddd2b 100644 --- a/frontend/src/locales/ru.json +++ b/frontend/src/locales/ru.json @@ -43,7 +43,15 @@ "nominatim_2": "Их данные лицензированы под лицензией ODbL.", "other_attributions": "Дополнительные атрибуции можно найти в файле README.", "generic_attributions": "Войдите в AdventureLog, чтобы просмотреть атрибуции для включённых интеграций и сервисов.", - "close": "Закрыть" + "close": "Закрыть", + "attributions": "Атрибуты", + "developer": "Разработчик", + "license_info": "Лицензия", + "sponsor": "Спонсор", + "thank_you": "Спасибо за использование AdventureLog!", + "version": "Версия", + "view_changelog": "Посмотреть изменение изменений", + "view_license": "Просмотреть лицензию" }, "home": { "hero_1": "Откройте для себя самые захватывающие приключения мира", @@ -302,7 +310,8 @@ "visit_calendar": "Посетите календарь", "wiki_location_desc": "Вытягивает отрывок из статьи Википедии, соответствующей названию места.", "will_be_marked_location": "будет отмечен по посещению после сохранения местоположения.", - "no_locations_found": "Никаких мест не найдено" + "no_locations_found": "Никаких мест не найдено", + "image_modal_navigate": "Используйте клавиши со стрелками или нажмите, чтобы перемещаться" }, "worldtravel": { "country_list": "Список стран", @@ -497,7 +506,36 @@ "enter_new_password": "Введите новый пароль", "connected": "Подключено", "disconnected": "Отключено", - "invalid_credentials": "Неверные полномочия" + "invalid_credentials": "Неверные полномочия", + "backup_restore": "Резервная копия", + "backup_restore_desc": "Сохраните данные или восстановите их из предыдущего файла резервного копирования.", + "complete_setup_to_enable": "Полная установка, чтобы включить MFA", + "copy_all": "Копировать все", + "enter_6_digit_code": "Введите 6 -значный код", + "enter_code_from_app": "Введите код из приложения", + "error_occurred": "Произошла ошибка", + "important": "Важный", + "manual_entry": "Ручная запись", + "mfa_already_enabled": "MFA уже включен", + "mfa_required": "MFA требуется", + "scan_qr_code": "Сканировать QR -код", + "scan_with_authenticator_app": "Сканирование с помощью приложения аутентификатора", + "secure_your_account": "Защитите свою учетную запись", + "setup_required": "Настройка требуется", + "verify_setup": "Проверьте настройку", + "whats_included": "Что включено", + "backup_your_data": "Резервную копию ваших данных", + "backup_your_data_desc": "Загрузите полное резервное копирование данных вашей учетной записи, включая местоположения, коллекции, медиа и посещения.", + "data_override_acknowledge": "Я признаю, что это переопределит все мои существующие данные", + "data_override_acknowledge_desc": "Это действие необратимо и заменит все местоположения, коллекции и посещения в вашем аккаунте.", + "data_override_warning": "Предупреждение о переопределении данных", + "data_override_warning_desc": "Восстановление данных полностью заменит все существующие данные (которые включены в резервную копию) в вашу учетную запись. \nЭто действие не может быть отменено.", + "integrations_settings": "Настройки интеграции", + "media": "СМИ", + "restore_data": "Восстановить данные", + "restore_data_desc": "Загрузите файл резервного копирования, чтобы восстановить ваши данные.", + "select_backup_file": "Выберите файл резервного копирования", + "world_travel_visits": "Всемирные поездки" }, "collection": { "collection_created": "Коллекция успешно создана!", @@ -722,5 +760,6 @@ "location": "Расположение", "locations": "Локации", "my_locations": "Мои локации" - } + }, + "settings_download_backup": "Скачать резервную копию" } diff --git a/frontend/src/locales/sv.json b/frontend/src/locales/sv.json index e84d5ad..ba4d8a7 100644 --- a/frontend/src/locales/sv.json +++ b/frontend/src/locales/sv.json @@ -9,7 +9,15 @@ "oss_attributions": "Tillskrivningar med öppen källkod", "other_attributions": "Ytterligare attributioner finns i README-filen.", "source_code": "Källkod", - "generic_attributions": "Logga in på AdventureLog för att visa attribut för aktiverade integrationer och tjänster." + "generic_attributions": "Logga in på AdventureLog för att visa attribut för aktiverade integrationer och tjänster.", + "attributions": "Tillskrivningar", + "developer": "Framkallare", + "license_info": "Licens", + "sponsor": "Sponsor", + "thank_you": "Tack för att du använder AdventureLog!", + "version": "Version", + "view_changelog": "Visa Changelog", + "view_license": "Visa licens" }, "adventures": { "activities": {}, @@ -250,7 +258,8 @@ "visit_calendar": "Besök kalendern", "wiki_location_desc": "Drar utdrag från Wikipedia -artikeln som matchar namnet på platsen.", "will_be_marked_location": "kommer att markeras som besöks när platsen har sparats.", - "no_locations_found": "Inga platser hittades" + "no_locations_found": "Inga platser hittades", + "image_modal_navigate": "Använd pilnycklar eller klicka för att navigera" }, "home": { "desc_1": "Upptäck, planera och utforska med lätthet", @@ -497,7 +506,36 @@ "social_auth_setup": "Social autentiseringsinställning", "staff_status": "Personalstatus", "staff_user": "Personalanvändare", - "invalid_credentials": "Ogiltiga referenser" + "invalid_credentials": "Ogiltiga referenser", + "backup_restore": "Säkerhetskopiering", + "backup_restore_desc": "Spara dina data eller återställa dem från en tidigare säkerhetskopieringsfil.", + "complete_setup_to_enable": "Komplett installation för att aktivera MFA", + "copy_all": "Kopiera alla", + "enter_6_digit_code": "Ange 6 siffror", + "enter_code_from_app": "Ange kod från appen", + "error_occurred": "Ett fel har inträffat", + "important": "Viktig", + "manual_entry": "Manuell inträde", + "mfa_already_enabled": "MFA redan aktiverat", + "mfa_required": "MFA krävs", + "scan_qr_code": "Skanna QR -kod", + "scan_with_authenticator_app": "Skanna med autentisatorapp", + "secure_your_account": "Säkra ditt konto", + "setup_required": "Installation krävs", + "verify_setup": "Verifiera installationen", + "whats_included": "Vad ingår", + "backup_your_data": "Säkerhetskopiera dina data", + "backup_your_data_desc": "Ladda ner en fullständig säkerhetskopia av dina kontodata inklusive platser, samlingar, media och besök.", + "data_override_acknowledge": "Jag erkänner att detta kommer att åsidosätta alla mina befintliga uppgifter", + "data_override_acknowledge_desc": "Denna åtgärd är irreversibel och kommer att ersätta alla platser, samlingar och besök på ditt konto.", + "data_override_warning": "Data åsidosättande varning", + "data_override_warning_desc": "Återställa data kommer helt att ersätta alla befintliga data (som ingår i säkerhetskopian) i ditt konto. \nDenna åtgärd kan inte ångras.", + "integrations_settings": "Integrationsinställningar", + "media": "Media", + "restore_data": "Återställa data", + "restore_data_desc": "Ladda upp en säkerhetskopieringsfil för att återställa dina data.", + "select_backup_file": "Välj säkerhetskopieringsfil", + "world_travel_visits": "Världsresebesök" }, "checklist": { "checklist_delete_error": "Ett fel uppstod vid borttagning av checklista", @@ -722,5 +760,6 @@ "location": "Plats", "locations": "Plats", "my_locations": "Mina platser" - } + }, + "settings_download_backup": "Ladda ner säkerhetskopiering" } diff --git a/frontend/src/locales/zh.json b/frontend/src/locales/zh.json index 721df19..ea7ecc9 100644 --- a/frontend/src/locales/zh.json +++ b/frontend/src/locales/zh.json @@ -43,7 +43,15 @@ "other_attributions": "其他声明可以在 README 文件中找到。", "source_code": "源代码", "close": "关闭", - "generic_attributions": "登录到AdventureLog以查看启用集成和服务的归因。" + "generic_attributions": "登录到AdventureLog以查看启用集成和服务的归因。", + "attributions": "归因", + "developer": "开发人员", + "license_info": "执照", + "sponsor": "赞助", + "thank_you": "感谢您使用冒险!", + "version": "版本", + "view_changelog": "查看ChangElog", + "view_license": "查看许可证" }, "home": { "desc_1": "轻松发现、规划和探索", @@ -302,7 +310,8 @@ "visit_calendar": "访问日历", "wiki_location_desc": "从Wikipedia文章中提取摘录,符合该位置的名称。", "will_be_marked_location": "保存位置后,将被标记为访问。", - "no_locations_found": "找不到位置" + "no_locations_found": "找不到位置", + "image_modal_navigate": "使用箭头键或单击以导航" }, "auth": { "forgot_password": "忘记密码?", @@ -497,7 +506,36 @@ "quick_actions": "快速动作", "region_updates": "区域更新", "region_updates_desc": "更新访问了地区和城市", - "invalid_credentials": "无效的凭据" + "invalid_credentials": "无效的凭据", + "backup_restore": "备份", + "backup_restore_desc": "保存数据或从以前的备份文件还原。", + "complete_setup_to_enable": "完整的设置以启用MFA", + "copy_all": "复制全部", + "enter_6_digit_code": "输入6位数代码", + "enter_code_from_app": "从应用程序输入代码", + "error_occurred": "发生了错误", + "important": "重要的", + "manual_entry": "手动输入", + "mfa_already_enabled": "MFA已经启用", + "mfa_required": "需要MFA", + "scan_qr_code": "扫描QR码", + "scan_with_authenticator_app": "使用身份验证器应用程序扫描", + "secure_your_account": "保护您的帐户", + "setup_required": "需要设置", + "verify_setup": "验证设置", + "whats_included": "包括什么", + "backup_your_data": "备份您的数据", + "backup_your_data_desc": "下载您的帐户数据的完整备份,包括位置,集合,媒体和访问。", + "data_override_acknowledge": "我承认这将覆盖我所有现有数据", + "data_override_acknowledge_desc": "此操作是不可逆转的,将取代您帐户中的所有位置,收集和访问。", + "data_override_warning": "数据覆盖警告", + "data_override_warning_desc": "还原数据将完全替换帐户中的所有现有数据(备份中包含在备份中)。\n该动作不能撤消。", + "integrations_settings": "集成设置", + "media": "媒体", + "restore_data": "还原数据", + "restore_data_desc": "上传备份文件以还原数据。", + "select_backup_file": "选择备份文件", + "world_travel_visits": "世界旅行访问" }, "checklist": { "checklist_delete_error": "删除清单时出错", @@ -722,5 +760,6 @@ "location": "地点", "locations": "位置", "my_locations": "我的位置" - } + }, + "settings_download_backup": "下载备份" } diff --git a/frontend/src/routes/api/[...path]/+server.ts b/frontend/src/routes/api/[...path]/+server.ts index 815d4a7..bef57d3 100644 --- a/frontend/src/routes/api/[...path]/+server.ts +++ b/frontend/src/routes/api/[...path]/+server.ts @@ -85,7 +85,7 @@ async function handleRequest( }); } - const responseData = await response.text(); + const responseData = await response.arrayBuffer(); // Create a new Headers object without the 'set-cookie' header const cleanHeaders = new Headers(response.headers); cleanHeaders.delete('set-cookie'); diff --git a/frontend/src/routes/locations/[id]/+page.svelte b/frontend/src/routes/locations/[id]/+page.svelte index 1bd3743..97e4716 100644 --- a/frontend/src/routes/locations/[id]/+page.svelte +++ b/frontend/src/routes/locations/[id]/+page.svelte @@ -88,7 +88,9 @@ let notFound: boolean = false; let isEditModalOpen: boolean = false; - let image_url: string | null = null; + let adventure_images: { image: string; adventure: AdditionalLocation | null }[] = []; + let modalInitialIndex: number = 0; + let isImageModalOpen: boolean = false; onMount(async () => { if (data.props.adventure) { @@ -114,6 +116,19 @@ geojson = null; await getGpxFiles(); } + + function closeImageModal() { + isImageModalOpen = false; + } + + function openImageModal(imageIndex: number) { + adventure_images = adventure.images.map(img => ({ + image: img.image, + adventure: adventure + })); + modalInitialIndex = imageIndex; + isImageModalOpen = true; + } {#if notFound} @@ -139,8 +154,12 @@ /> {/if} -{#if image_url} - (image_url = null)} {adventure} /> +{#if isImageModalOpen} + {/if} {#if !adventure && !notFound} @@ -176,7 +195,7 @@ > +
+ + + + + {/if} + {#if activeSection === 'admin' && user.is_staff}