diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index bb6f94bd..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -name: Bug report -about: Found something not working as expected? Please file a detailed bug report so we can fix it quickly! -title: "[BUG] " -labels: bug -assignees: "" ---- - -🛑 **Note**: Please search existing issues before filing a new one. This helps avoid duplicates and ensures we can address your concern efficiently! - -## 🐞 Bug Description - -A clear and concise description of the issue. What went wrong? - -## 🔄 Steps to Reproduce - -Steps to reproduce the behavior: - -1. -2. -3. -4. Observe the error - -## ✅ Expected Behavior - -What did you expect AdventureLog to do? - -## 📸 Screenshots / Logs - -- Attach screenshots if it helps explain the problem. -- If possible, include **relevant log excerpts** (be sure to remove sensitive info). - -## 🐳 Environment Details - -- **Platform:** (Docker, Synology, Proxmox, TrueNAS, Unraid, etc.) -- **Install Method:** (Docker Compose, Quick Install Script, Manual, etc.) -- **AdventureLog Version:** (e.g., v0.12.0) -- **Reverse Proxy:** (e.g., Nginx, Traefik, Caddy, etc. or None) - -If using Docker and the issue is related to the container, it may be helpful to include your `docker-compose.yml` or relevant variables below. -⚠️ Please remember to redact any sensitive information such as passwords or API keys. - -## 📎 Additional Context - -Anything else that might be useful (custom configuration, network setup, database version, etc.)? - ---- - -Thank you for taking the time to report this issue! We appreciate your help in making AdventureLog better. We’ll look into it as soon as we can. 🙌 diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..45149b91 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,113 @@ +name: Bug report +description: Found something not working as expected? File a detailed bug report so we can fix it quickly. +title: "[BUG] " +labels: + - bug +body: + - type: markdown + attributes: + value: | + Please search existing issues before filing a new one. This helps avoid duplicates and ensures we can address your concern efficiently. + - type: textarea + id: bug-description + attributes: + label: Bug Description + description: A clear and concise description of the issue. What went wrong? + placeholder: Describe what happened. + validations: + required: true + - type: textarea + id: steps-to-reproduce + attributes: + label: Steps to Reproduce + description: Provide exact steps so we can reliably reproduce the issue. + placeholder: | + 1. Go to ... + 2. Click on ... + 3. Enter ... + 4. Observe the error + validations: + required: true + - type: textarea + id: expected-behavior + attributes: + label: Expected Behavior + description: What did you expect AdventureLog to do? + placeholder: Describe the expected result. + validations: + required: true + - type: textarea + id: screenshots-logs + attributes: + label: Screenshots / Logs + description: | + Attach screenshots if helpful. + Include relevant logs (remove sensitive information). + placeholder: Paste logs and add image links here. + validations: + required: false + - type: dropdown + id: platform + attributes: + label: Platform + description: Where are you running AdventureLog? + options: + - Docker + - Synology + - Proxmox + - TrueNAS + - Unraid + - Other + validations: + required: true + - type: dropdown + id: install-method + attributes: + label: Install Method + options: + - Docker Compose + - Quick Install Script + - Manual + - Other + validations: + required: true + - type: input + id: version + attributes: + label: AdventureLog Version + description: "Example: v0.12.0" + placeholder: v0.0.0 + validations: + required: true + - type: input + id: reverse-proxy + attributes: + label: Reverse Proxy + description: "Example: Nginx, Traefik, Caddy, or None" + placeholder: None + validations: + required: true + - type: textarea + id: docker-compose + attributes: + label: Docker Compose / Relevant Variables (Optional) + description: Include docker-compose content or environment variables if relevant. Remove sensitive data. + render: yaml + placeholder: Paste obfuscated configuration here. + validations: + required: false + - type: textarea + id: additional-context + attributes: + label: Additional Context + description: Any extra information that might help us debug this issue. + placeholder: Network setup, custom configuration, database details, etc. + validations: + required: false + - type: checkboxes + id: terms + attributes: + label: Confirmation + options: + - label: I searched existing issues and confirmed this is not a duplicate. + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..f5ea7233 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Discord Community + url: https://discord.gg/wRbQ9Egr8C + about: For general deployment troubleshooting and community support. diff --git a/.github/ISSUE_TEMPLATE/deployment-issue.md b/.github/ISSUE_TEMPLATE/deployment-issue.md deleted file mode 100644 index a926ed68..00000000 --- a/.github/ISSUE_TEMPLATE/deployment-issue.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -name: Deployment Issue -about: Request help deploying AdventureLog. For faster support, please use our Discord community! -title: "[DEPLOYMENT] " -labels: deployment -assignees: "" ---- - -⚠️ **Note:** GitHub issues are primarily for tracking bugs and feature requests. -For general deployment troubleshooting and faster community support, please visit our **Discord**: https://discord.gg/wRbQ9Egr8C - ---- - -## 🖥️ Describe Your Issue - -A clear and concise description of the deployment problem. - -## 🐳 Docker Compose (Obfuscated) - -Please include your `docker-compose.yml` or relevant variable configuration. -⚠️ Make sure to **remove or obfuscate sensitive information** (passwords, tokens, keys, etc.). - -```yaml -# Example (with secrets removed) -``` diff --git a/.github/ISSUE_TEMPLATE/deployment_issue.yml b/.github/ISSUE_TEMPLATE/deployment_issue.yml new file mode 100644 index 00000000..c608c5fc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/deployment_issue.yml @@ -0,0 +1,58 @@ +name: Deployment Issue +description: Request help deploying AdventureLog. +title: "[DEPLOYMENT] " +labels: + - deployment +body: + - type: markdown + attributes: + value: | + GitHub issues are primarily for tracking bugs and feature requests. + For general deployment troubleshooting and faster community support, visit Discord: https://discord.gg/wRbQ9Egr8C + - type: textarea + id: deployment-description + attributes: + label: Describe Your Issue + description: A clear and concise description of the deployment problem. + placeholder: Explain what is failing and when it happens. + validations: + required: true + - type: textarea + id: docker-compose + attributes: + label: Docker Compose (Obfuscated) + description: Include your docker-compose configuration or relevant variables. Remove secrets. + render: yaml + placeholder: Paste your obfuscated configuration. + validations: + required: false + - type: textarea + id: logs + attributes: + label: Logs / Error Output + description: Include relevant service logs or command output. + placeholder: Paste key logs here. + validations: + required: false + - type: input + id: version + attributes: + label: AdventureLog Version + description: "In the web app, click the 3-dot menu in the sidebar and select 'About' to find the version number." + placeholder: v0.0.0 + validations: + required: false + - type: input + id: reverse-proxy + attributes: + label: Reverse Proxy + placeholder: Nginx, Traefik, Caddy, None + validations: + required: false + - type: checkboxes + id: terms + attributes: + label: Confirmation + options: + - label: I removed or obfuscated all sensitive data from this issue. + required: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 20ef0d38..00000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -name: Feature request -about: Suggest a new idea, feature, or improvement for AdventureLog -title: "[REQUEST] " -labels: enhancement -assignees: "" ---- - -🛑 **Note**: Please search existing issues before creating a new request. This helps avoid duplicates and keeps discussions focused. - -## 💡 Feature Summary - -A clear and concise description of the feature or improvement you’d like to see. - -## 🤔 Problem Statement - -Is your request related to a specific problem? -(Example: “I’m always frustrated when…”) - -## 🛠️ Proposed Solution - -Describe the solution you’d like. -What should AdventureLog do differently? - -## 🔄 Alternatives Considered - -List any alternative solutions or workarounds you’ve tried or thought about. - -## 📸 Mockups / Examples (optional) - -If you would like, include screenshots, sketches, or links that illustrate the idea. - -## 📎 Additional Context - -Any other details that might help us understand the request (use cases, user stories, related features, etc.). - ---- - -✨ Thanks for suggesting improvements to AdventureLog! Your ideas help shape the future of the project. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000..2574c14f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,65 @@ +name: Feature request +description: Suggest a new idea, feature, or improvement for AdventureLog. +title: "[REQUEST] " +labels: + - enhancement +body: + - type: markdown + attributes: + value: | + Please search existing issues before creating a new request. This helps avoid duplicates and keeps discussions focused. + - type: textarea + id: feature-summary + attributes: + label: Feature Summary + description: A clear and concise description of the feature or improvement you want. + placeholder: Summarize your request. + validations: + required: true + - type: textarea + id: problem-statement + attributes: + label: Problem Statement + description: Is your request related to a specific problem? + placeholder: "Example: I am frustrated when ..." + validations: + required: true + - type: textarea + id: proposed-solution + attributes: + label: Proposed Solution + description: Describe the solution you want. + placeholder: What should AdventureLog do differently? + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: Alternatives Considered + description: List workarounds or alternative solutions you considered. + placeholder: Describe alternatives and why they are not ideal. + validations: + required: false + - type: textarea + id: mockups + attributes: + label: Mockups / Examples (Optional) + description: Include screenshots, sketches, or links that illustrate the idea. + placeholder: Add links or image references. + validations: + required: false + - type: textarea + id: additional-context + attributes: + label: Additional Context + description: Add any other details, use cases, or related features. + placeholder: Extra context. + validations: + required: false + - type: checkboxes + id: terms + attributes: + label: Confirmation + options: + - label: I searched existing issues and confirmed this request is not a duplicate. + required: true diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..fa1de9d1 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,44 @@ +## Related Issue + +Closes # + +If this PR does not resolve an issue, please explain why. + +--- + +## Description + +Explain what this PR changes and why. + +--- + +## Type of Change + +- [ ] Bug fix +- [ ] New feature +- [ ] Documentation update +- [ ] Refactor / code cleanup +- [ ] Performance improvement +- [ ] Other (please describe) + +--- + +## Checklist + +Before submitting this PR, please confirm: + +- [ ] I have linked an existing issue +- [ ] The issue is **approved or marked as ready** +- [ ] My changes follow the existing project structure and coding standards +- [ ] I have tested the changes locally +- [ ] Documentation has been updated if necessary + +--- + +## Screenshots (if applicable) + +If this PR includes UI changes, please include screenshots or GIFs. + +--- + +Thank you for contributing to **AdventureLog**! Your efforts help make this project better for everyone. We’ll review your PR as soon as we can. 🙌 diff --git a/.github/workflows/adventurelog-bot.yml b/.github/workflows/adventurelog-bot.yml new file mode 100644 index 00000000..fcc1ec71 --- /dev/null +++ b/.github/workflows/adventurelog-bot.yml @@ -0,0 +1,85 @@ +name: AdventureLog Bot + +on: + pull_request: + types: [opened, edited, synchronize] + +jobs: + enforce-ready: + permissions: + contents: read + issues: write + pull-requests: write + runs-on: ubuntu-latest + + steps: + - name: Validate linked issue + uses: actions/github-script@v7 + with: + script: | + const pr = context.payload.pull_request; + + // Ignore specific user + if (context.actor === "seanmorley15") { + console.log("Skipping maintainer PR"); + return; + } + + // Ignore PRs created before enforcement date + const cutoff = new Date("2026-03-16T00:00:00Z"); + const created = new Date(pr.created_at); + + if (created < cutoff) { + console.log("Skipping PR created before enforcement date"); + return; + } + + const body = pr.body || ""; + const match = body.match(/#(\d+)/); + + if (!match) { + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pr.number, + body: "🤖 **AdventureLog Bot**\n\n🚫 This PR was automatically closed because it does not reference an issue.\n\nPlease link an issue using `Closes #issue-number`." + }); + + await github.rest.pulls.update({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pr.number, + state: "closed" + }); + + return; + } + + const issueNumber = match[1]; + + const { data: issue } = await github.rest.issues.get({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber + }); + + const labels = issue.labels.map(l => l.name); + + if (!labels.includes("ready")) { + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pr.number, + body: "🤖 **AdventureLog Bot**\n\n🚫 This PR was automatically closed.\n\nPull requests may only be opened for issues labeled **ready**." + }); + + await github.rest.pulls.update({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pr.number, + state: "closed" + }); + + } diff --git a/.github/workflows/sync-project-status.yml b/.github/workflows/sync-project-status.yml new file mode 100644 index 00000000..63bef804 --- /dev/null +++ b/.github/workflows/sync-project-status.yml @@ -0,0 +1,85 @@ +name: Sync Project Status + +on: + issues: + types: [labeled] + +jobs: + update-project: + runs-on: ubuntu-latest + permissions: + contents: read + issues: write + + steps: + - name: Update project status from label + uses: actions/github-script@v7 + with: + script: | + + const labelMap = { + "backlog": "BACKLOG_OPTION_ID", + "needs discussion": "DISCUSSION_OPTION_ID", + "approved": "APPROVED_OPTION_ID", + "ready": "READY_OPTION_ID", + "in progress": "IN_PROGRESS_OPTION_ID", + "in review": "IN_REVIEW_OPTION_ID", + "done": "DONE_OPTION_ID" + }; + + const label = context.payload.label.name.toLowerCase(); + const optionId = labelMap[label]; + + if (!optionId) return; + + const issueNodeId = context.payload.issue.node_id; + + const projectId = "PVT_kwHOBeIeKs4AfmUO"; + const fieldId = "PVTSSF_lAHOBeIeKs4AfmUOzgU5pCI"; + + // find project item + const result = await github.graphql(` + query($issueId: ID!) { + node(id: $issueId) { + ... on Issue { + projectItems(first: 10) { + nodes { + id + } + } + } + } + } + `, { + issueId: issueNodeId + }); + + const itemId = result.node.projectItems.nodes[0]?.id; + + if (!itemId) { + console.log("Issue not in project"); + return; + } + + // update status field + await github.graphql(` + mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $optionId: String!) { + updateProjectV2ItemFieldValue( + input: { + projectId: $projectId + itemId: $itemId + fieldId: $fieldId + value: { singleSelectOptionId: $optionId } + } + ) { + projectV2Item { + id + } + } + } + `, { + projectId: projectId, + itemId: itemId, + fieldId: fieldId, + optionId: optionId + }); diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b6013008..806ec340 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,52 +1,258 @@ # Contributing to AdventureLog -We’re excited to have you contribute to AdventureLog! To ensure that this community remains welcoming and productive for all users and developers, please follow this simple Code of Conduct. +Thank you for your interest in contributing to **AdventureLog**! +AdventureLog is an open-source project built by and for people who love travel, exploration, and self-hosting. Contributions of all kinds are welcome — whether that’s fixing bugs, improving documentation, suggesting features, or writing code. -## Pull Request Process +Our goal is to keep the project **open, welcoming, and organized** so that contributors can collaborate effectively and the codebase remains maintainable long-term. -1. **Open an Issue First**: Discuss any changes or features you plan to implement by opening an issue. This helps to clarify your idea and ensures there’s a shared understanding. -2. **Document Changes**: If your changes impact the user interface, add new environment variables, or introduce new container configurations, make sure to update the documentation accordingly. The documentation is located in the `documentation` folder. -3. **Pull Request**: Submit a pull request with your changes directed towards the `development` branch. Make sure to reference the issue you opened in the description. After your pull request is submitted, it will be reviewed by the maintainers. -4. **Review Process**: The maintainers will review your pull request. They may suggest changes or improvements. Please be open to feedback and ready to make adjustments as needed. -5. **Merge**: Once your pull request is approved, it will be merged into the `development` branch. This branch is where all new features and changes are integrated before being released to the main branch. +This document explains how to contribute and the workflow we use. -## Code of Conduct +--- -### Our Pledge +# How Contributions Work -At AdventureLog, we are committed to creating a community that fosters adventure, exploration, and innovation. We encourage diverse participation and strive to maintain a space where everyone feels welcome to contribute, regardless of their background or experience level. We ask that you contribute with respect and kindness, making sure to prioritize collaboration and mutual growth. +AdventureLog uses a structured workflow to keep development organized and to make it easier for contributors to collaborate. -### Our Standards +All development follows this process: -In order to maintain a positive environment, we encourage the following behaviors: +``` +Issue → Discussion → Approved → Ready → Development → Review → Merge +``` -- **Inclusivity**: Use welcoming and inclusive language that fosters collaboration across all perspectives and experiences. -- **Respect**: Respect differing opinions and engage with empathy, understanding that each person’s perspective is valuable. -- **Constructive Feedback**: Offer feedback that helps improve the project and allows contributors to grow from it. -- **Adventure Spirit**: Bring the same sense of curiosity, discovery, and positivity that drives AdventureLog into all interactions with the community. +### 1. Open or Find an Issue + +Before starting work, **please open an issue or find an existing one**. + +Issues allow us to: + +- discuss ideas before development begins +- coordinate work between contributors +- prevent duplicate efforts +- maintain a clear roadmap for the project + +If you have an idea for a new feature or improvement, feel free to open an issue describing it. + +--- + +### 2. Wait for Approval / Ready Status + +Issues move through several stages: + +**Backlog** +An idea or request that has not yet been reviewed. + +**Needs Discussion** +The idea requires maintainer feedback or design discussion. + +**Approved** +The concept has been accepted but may require planning. + +**Ready** +The issue is ready for contributors to begin working on it. + +⚠️ **Pull Requests should only be opened for issues marked `Ready`.** + +This helps ensure contributors work on changes that are aligned with the project’s roadmap. + +--- + +### 3. Start Working on the Issue + +Once an issue is marked **Ready**, you can begin working on it. + +If you plan to work on a larger issue, feel free to comment on the issue to let others know. + +This helps prevent duplicate work. + +--- + +### 4. Create a Pull Request + +When your changes are ready, open a pull request targeting the **`development` branch**. + +Your pull request must include a reference to the issue it resolves: + +``` +Closes #issue-number +``` + +This allows the project automation to track progress and update the project board. + +Example PR description: + +``` +Closes #123 + +Adds support for exporting trips as GPX files. +``` + +--- + +### 5. Review Process + +Once submitted, maintainers will review your pull request. + +Reviews may include: + +- code quality improvements +- consistency with the existing architecture +- performance considerations +- documentation updates + +Please be open to feedback — reviews are intended to **improve the project and help contributors grow**. + +--- + +### 6. Merge + +After approval, your pull request will be merged into the **`development` branch**. + +From there, it will eventually be included in the next release. + +Thank you for helping improve AdventureLog! + +--- + +# AI / LLM Assistance + +Using AI tools (such as ChatGPT, Copilot, or other LLMs) **is allowed** when contributing to AdventureLog. + +However, contributors are responsible for ensuring that generated code: + +- is **correct and fully understood** +- follows the **project’s coding standards** +- integrates properly with the existing architecture +- does not introduce unnecessary complexity + +AI-generated code that does not meet these standards may be rejected or the pull request may be closed. + +Please review and clean up any AI-generated code before submitting it. + +--- + +# Code Quality Expectations + +To keep the project maintainable, all contributions should: + +- follow the existing **code structure and architecture** +- use clear and readable code +- avoid unnecessary dependencies +- include documentation updates when relevant +- maintain compatibility with the existing system + +AdventureLog currently includes: + +- **Django** for the backend +- **SvelteKit** for the frontend +- **Docker-based deployments** + +When contributing, please try to match the **style and patterns already used in the project**. + +--- + +# Documentation Changes + +If your changes affect: + +- user workflows +- environment variables +- deployment setup +- API behavior +- configuration + +please update the documentation in the: + +``` +/documentation +``` + +folder accordingly. + +Keeping documentation accurate is extremely important. + +--- + +# Good Issues for New Contributors + +If you are new to the project, look for issues labeled: + +``` +good first issue +help wanted +``` + +These are great starting points for new contributors. + +--- + +# Code of Conduct + +## Our Pledge + +At AdventureLog, we are committed to creating a community that fosters adventure, exploration, and innovation. + +We welcome contributors of all experience levels and backgrounds. Everyone should feel comfortable participating and sharing ideas. + +--- + +## Our Standards + +To maintain a positive environment, we encourage the following behaviors: + +- **Inclusivity** — Use welcoming and inclusive language. +- **Respect** — Respect differing viewpoints and experiences. +- **Constructive Feedback** — Provide helpful and actionable feedback. +- **Collaboration** — Work together to improve the project. Examples of unacceptable behavior include: -- Personal attacks, trolling, or any form of harassment. -- Insensitive or discriminatory language, including sexualized comments or imagery. -- Spamming or misusing project spaces for personal gain. -- Publishing or using others’ private information without permission. -- Anything else that could be seen as disrespectful or unprofessional in a collaborative environment. +- Personal attacks or harassment +- Discriminatory language +- Spamming or promotional misuse of project spaces +- Sharing private information without consent -### Our Responsibilities +--- -As maintainers of AdventureLog, we are committed to enforcing this Code of Conduct and taking corrective action when necessary. This may involve moderating comments, pulling code, or banning users who engage in harmful behaviors. +## Maintainer Responsibilities -We strive to foster a community that balances open collaboration with respect for all contributors. +The AdventureLog maintainers are responsible for enforcing this Code of Conduct and maintaining a respectful community. -### Scope +If necessary, maintainers may: -This Code of Conduct applies in all spaces related to AdventureLog. This includes our GitHub repository, discussions, documentation, social media accounts, and events—both online and in person. +- moderate comments +- close pull requests +- remove contributions +- restrict participation -### Enforcement +These actions will only be taken when necessary to protect the community and the project. -If you experience or witness unacceptable behavior, please report it to the project team at `contact@adventurelog.app`. All reports will be confidential and handled swiftly. The maintainers will investigate the issue and take appropriate action as needed. +--- -### Attribution +## Scope -This Code of Conduct is inspired by the [Contributor Covenant](http://contributor-covenant.org), version 1.4, and adapted to fit the unique spirit of AdventureLog. +This Code of Conduct applies to all spaces related to AdventureLog, including: + +- GitHub repositories +- GitHub Discussions +- documentation +- social media +- community spaces + +--- + +## Reporting Issues + +If you experience or witness unacceptable behavior, please contact the maintainers at: + +``` +contact@adventurelog.app +``` + +All reports will be handled confidentially. + +--- + +## Attribution + +This Code of Conduct is inspired by the +Contributor Covenant (v1.4) and adapted for the AdventureLog community. diff --git a/backend/server/adventures/views/import_export_view.py b/backend/server/adventures/views/import_export_view.py index 191d118b..39dc303c 100644 --- a/backend/server/adventures/views/import_export_view.py +++ b/backend/server/adventures/views/import_export_view.py @@ -3,6 +3,8 @@ import json import zipfile import tempfile import os +import re +from decimal import Decimal, InvalidOperation from datetime import datetime from django.http import HttpResponse from django.core.files.storage import default_storage @@ -31,6 +33,73 @@ class BackupViewSet(viewsets.ViewSet): """ Simple ViewSet for handling backup and import operations """ + + def _normalize_money_amount(self, value): + """Return a Decimal amount from legacy or canonical backup values.""" + if value is None: + return None + + if isinstance(value, Decimal): + return value + + if isinstance(value, (int, float)): + try: + return Decimal(str(value)) + except (InvalidOperation, ValueError): + return None + + if not isinstance(value, str): + return None + + text = value.strip() + if not text: + return None + + # Accept values like "$1,553.59" and "USD 1,553.59". + cleaned = re.sub(r'[^0-9,\.\-]', '', text) + if not cleaned: + return None + + if ',' in cleaned and '.' in cleaned: + if cleaned.rfind(',') > cleaned.rfind('.'): + cleaned = cleaned.replace('.', '').replace(',', '.') + else: + cleaned = cleaned.replace(',', '') + elif ',' in cleaned: + parts = cleaned.split(',') + if len(parts) == 2 and len(parts[-1]) in (1, 2): + cleaned = cleaned.replace(',', '.') + else: + cleaned = cleaned.replace(',', '') + + try: + return Decimal(cleaned) + except InvalidOperation: + return None + + def _parse_money(self, value, currency=None, default_currency='USD'): + """Parse a backup money value and return schema-safe (amount, currency).""" + parsed_currency = currency + parsed_value = value + + if isinstance(value, dict): + parsed_value = value.get('amount') + parsed_currency = parsed_currency or value.get('currency') + + amount = self._normalize_money_amount(parsed_value) + if amount is None: + # django-money stores currency in a separate non-null column even when + # the monetary amount itself is empty, so imports must preserve a + # default currency for blank legacy values. + return None, default_currency + + normalized_currency = (parsed_currency or default_currency) + if isinstance(normalized_currency, str): + normalized_currency = normalized_currency.strip().upper() or default_currency + else: + normalized_currency = default_currency + + return amount, normalized_currency @action(detail=False, methods=['get']) def export(self, request): @@ -106,6 +175,8 @@ class BackupViewSet(viewsets.ViewSet): 'tags': location.tags, 'description': location.description, 'rating': location.rating, + 'price': str(location.price.amount) if location.price else None, + 'price_currency': str(location.price.currency) if location.price else None, 'link': location.link, 'is_public': location.is_public, 'longitude': str(location.longitude) if location.longitude else None, @@ -227,6 +298,8 @@ class BackupViewSet(viewsets.ViewSet): 'name': transport.name, 'description': transport.description, 'rating': transport.rating, + 'price': str(transport.price.amount) if transport.price else None, + 'price_currency': str(transport.price.currency) if transport.price else None, 'link': transport.link, 'date': transport.date.isoformat() if transport.date else None, 'end_date': transport.end_date.isoformat() if transport.end_date else None, @@ -300,7 +373,8 @@ class BackupViewSet(viewsets.ViewSet): '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, + 'price': str(lodging.price.amount) if lodging.price else None, + 'price_currency': str(lodging.price.currency) 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, @@ -572,6 +646,11 @@ class BackupViewSet(viewsets.ViewSet): # Import Locations for adv_data in backup_data.get('locations', []): + location_price, location_price_currency = self._parse_money( + adv_data.get('price'), + adv_data.get('price_currency') + ) + city = None if adv_data.get('city'): try: @@ -600,6 +679,8 @@ class BackupViewSet(viewsets.ViewSet): tags=adv_data.get('tags', []), description=adv_data.get('description'), rating=adv_data.get('rating'), + price=location_price, + price_currency=location_price_currency, link=adv_data.get('link'), is_public=adv_data.get('is_public', False), longitude=adv_data.get('longitude'), @@ -779,6 +860,11 @@ class BackupViewSet(viewsets.ViewSet): collection = None if trans_data.get('collection_export_id') is not None: collection = collection_map.get(trans_data['collection_export_id']) + + transport_price, transport_price_currency = self._parse_money( + trans_data.get('price'), + trans_data.get('price_currency') + ) transportation = Transportation.objects.create( user=user, @@ -786,6 +872,8 @@ class BackupViewSet(viewsets.ViewSet): name=trans_data['name'], description=trans_data.get('description'), rating=trans_data.get('rating'), + price=transport_price, + price_currency=transport_price_currency, link=trans_data.get('link'), date=trans_data.get('date'), end_date=trans_data.get('end_date'), @@ -863,6 +951,11 @@ class BackupViewSet(viewsets.ViewSet): collection = None if lodg_data.get('collection_export_id') is not None: collection = collection_map.get(lodg_data['collection_export_id']) + + lodging_price, lodging_price_currency = self._parse_money( + lodg_data.get('price'), + lodg_data.get('price_currency') + ) lodging = Lodging.objects.create( user=user, @@ -875,7 +968,8 @@ class BackupViewSet(viewsets.ViewSet): check_out=lodg_data.get('check_out'), timezone=lodg_data.get('timezone'), reservation_number=lodg_data.get('reservation_number'), - price=lodg_data.get('price'), + price=lodging_price, + price_currency=lodging_price_currency, latitude=lodg_data.get('latitude'), longitude=lodg_data.get('longitude'), location=lodg_data.get('location'), diff --git a/backend/server/adventures/views/stats_view.py b/backend/server/adventures/views/stats_view.py index 6694dc6c..f699dab3 100644 --- a/backend/server/adventures/views/stats_view.py +++ b/backend/server/adventures/views/stats_view.py @@ -29,7 +29,34 @@ class StatsViewSet(viewsets.ViewSet): return visited_count - def _get_activity_stats_by_category(self, user_activities): + def _can_view_location(self, request_user, profile_user, location): + if not location: + return False + + if request_user.is_authenticated and request_user.id == profile_user.id: + return True + + return bool(location.is_public) + + def _build_activity_record(self, activity, metric_key, metric_value, profile_user, request_user): + if not activity: + return None + + location = activity.visit.location if activity.visit_id and activity.visit else None + can_view_location = self._can_view_location(request_user, profile_user, location) + + return { + 'metric_key': metric_key, + 'metric_value': round(float(metric_value or 0), 2), + 'activity_id': str(activity.id), + 'activity_name': activity.name if activity.name else None, + 'sport_type': activity.sport_type, + 'start_date': activity.start_date.isoformat() if activity.start_date else None, + 'location_id': str(location.id) if (location and can_view_location) else None, + 'location_name': location.name if (location and can_view_location) else None, + } + + def _get_activity_stats_by_category(self, user_activities, profile_user, request_user): """Calculate detailed stats for each sport category""" category_stats = {} @@ -87,12 +114,42 @@ class StatsViewSet(viewsets.ViewSet): 'avg_speed': round(stats['avg_speed'] or 0, 2), 'max_speed': round(stats['max_speed'] or 0, 2), 'total_calories': round(stats['total_calories'] or 0, 2), - 'sports': sport_breakdown + 'sports': sport_breakdown, + 'record_holders': { + 'max_distance': self._build_activity_record( + activities.exclude(distance__isnull=True).select_related('visit__location').order_by('-distance', '-start_date').first(), + 'distance', + stats['max_distance'], + profile_user, + request_user, + ), + 'max_speed': self._build_activity_record( + activities.exclude(max_speed__isnull=True).select_related('visit__location').order_by('-max_speed', '-start_date').first(), + 'max_speed', + stats['max_speed'], + profile_user, + request_user, + ), + 'max_elevation_gain': self._build_activity_record( + activities.exclude(elevation_gain__isnull=True).select_related('visit__location').order_by('-elevation_gain', '-start_date').first(), + 'elevation_gain', + stats['max_elevation_gain'], + profile_user, + request_user, + ), + 'max_calories': self._build_activity_record( + activities.exclude(calories__isnull=True).select_related('visit__location').order_by('-calories', '-start_date').first(), + 'calories', + activities.exclude(calories__isnull=True).aggregate(value=Max('calories'))['value'], + profile_user, + request_user, + ), + } } return category_stats - def _get_overall_activity_stats(self, user_activities): + def _get_overall_activity_stats(self, user_activities, profile_user, request_user): """Calculate overall activity statistics""" if not user_activities.exists(): return { @@ -101,7 +158,13 @@ class StatsViewSet(viewsets.ViewSet): 'total_moving_time': 0, 'total_elevation_gain': 0, 'total_elevation_loss': 0, - 'total_calories': 0 + 'total_calories': 0, + 'record_holders': { + 'max_distance': None, + 'max_speed': None, + 'max_elevation_gain': None, + 'max_calories': None, + }, } stats = user_activities.aggregate( @@ -124,7 +187,37 @@ class StatsViewSet(viewsets.ViewSet): 'total_moving_time': total_moving_seconds, 'total_elevation_gain': round(stats['total_elevation_gain'] or 0, 2), 'total_elevation_loss': round(stats['total_elevation_loss'] or 0, 2), - 'total_calories': round(stats['total_calories'] or 0, 2) + 'total_calories': round(stats['total_calories'] or 0, 2), + 'record_holders': { + 'max_distance': self._build_activity_record( + user_activities.exclude(distance__isnull=True).select_related('visit__location').order_by('-distance', '-start_date').first(), + 'distance', + user_activities.exclude(distance__isnull=True).aggregate(value=Max('distance'))['value'], + profile_user, + request_user, + ), + 'max_speed': self._build_activity_record( + user_activities.exclude(max_speed__isnull=True).select_related('visit__location').order_by('-max_speed', '-start_date').first(), + 'max_speed', + user_activities.exclude(max_speed__isnull=True).aggregate(value=Max('max_speed'))['value'], + profile_user, + request_user, + ), + 'max_elevation_gain': self._build_activity_record( + user_activities.exclude(elevation_gain__isnull=True).select_related('visit__location').order_by('-elevation_gain', '-start_date').first(), + 'elevation_gain', + user_activities.exclude(elevation_gain__isnull=True).aggregate(value=Max('elevation_gain'))['value'], + profile_user, + request_user, + ), + 'max_calories': self._build_activity_record( + user_activities.exclude(calories__isnull=True).select_related('visit__location').order_by('-calories', '-start_date').first(), + 'calories', + user_activities.exclude(calories__isnull=True).aggregate(value=Max('calories'))['value'], + profile_user, + request_user, + ), + }, } @action(detail=False, methods=['get'], url_path=r'counts/(?P[\w.@+-]+)') @@ -153,8 +246,8 @@ class StatsViewSet(viewsets.ViewSet): user_activities = Activity.objects.filter(user=user.id) # Get enhanced activity statistics - overall_activity_stats = self._get_overall_activity_stats(user_activities) - activity_stats_by_category = self._get_activity_stats_by_category(user_activities) + overall_activity_stats = self._get_overall_activity_stats(user_activities, user, request.user) + activity_stats_by_category = self._get_activity_stats_by_category(user_activities, user, request.user) return Response({ # Travel stats diff --git a/backend/server/main/settings.py b/backend/server/main/settings.py index 5a858152..19d75315 100644 --- a/backend/server/main/settings.py +++ b/backend/server/main/settings.py @@ -389,9 +389,9 @@ PUBLIC_URL = getenv('PUBLIC_URL', 'http://localhost:8000') ADVENTURELOG_RELEASE_VERSION = 'v0.12.0' # https://github.com/dr5hn/countries-states-cities-database/tags -COUNTRY_REGION_JSON_VERSION = 'v3.0' +COUNTRY_REGION_JSON_VERSION = 'v3.1' # External service keys (do not hardcode secrets) GOOGLE_MAPS_API_KEY = getenv('GOOGLE_MAPS_API_KEY', '') STRAVA_CLIENT_ID = getenv('STRAVA_CLIENT_ID', '') -STRAVA_CLIENT_SECRET = getenv('STRAVA_CLIENT_SECRET', '') \ No newline at end of file +STRAVA_CLIENT_SECRET = getenv('STRAVA_CLIENT_SECRET', '') diff --git a/backend/server/users/views.py b/backend/server/users/views.py index 2b5548f6..8652f216 100644 --- a/backend/server/users/views.py +++ b/backend/server/users/views.py @@ -42,6 +42,10 @@ class ChangeEmailView(APIView): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) class IsRegistrationDisabled(APIView): + # This endpoint is requested on auth pages and should not be globally throttled. + # A 429 here can break signup UX even for legitimate users. + throttle_classes = [] + @swagger_auto_schema( responses={ 200: openapi.Response('Registration is disabled'), @@ -112,6 +116,9 @@ class PublicUserDetailView(APIView): class UserMetadataView(APIView): permission_classes = [IsAuthenticated] + # This endpoint is used by the frontend auth hook to hydrate user state. + # Global throttling can cause an auth loop and forced logout behavior. + throttle_classes = [] @swagger_auto_schema( responses={ diff --git a/frontend/src/hooks.server.ts b/frontend/src/hooks.server.ts index 12cd0171..d4f2fcb1 100644 --- a/frontend/src/hooks.server.ts +++ b/frontend/src/hooks.server.ts @@ -5,6 +5,11 @@ const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL']; export const authHook: Handle = async ({ event, resolve }) => { event.cookies.delete('csrftoken', { path: '/' }); try { + // Image proxy requests can be very high-volume and do not need locals.user. + if (event.url.pathname.startsWith('/immich/')) { + return await resolve(event); + } + let sessionid = event.cookies.get('sessionid'); if (!sessionid) { @@ -23,6 +28,13 @@ export const authHook: Handle = async ({ event, resolve }) => { }); if (!userFetch.ok) { + // Preserve the session on transient backend failures (e.g. 429 throttling) + // to avoid forcing users into a logout loop. + if (userFetch.status === 429 || userFetch.status >= 500) { + event.locals.user = null; + return await resolve(event); + } + event.locals.user = null; event.cookies.delete('sessionid', { path: '/', secure: event.url.protocol === 'https:' }); return await resolve(event); diff --git a/frontend/src/lib/components/CategoryFilterDropdown.svelte b/frontend/src/lib/components/CategoryFilterDropdown.svelte index 1eeaabea..e6bffd4e 100644 --- a/frontend/src/lib/components/CategoryFilterDropdown.svelte +++ b/frontend/src/lib/components/CategoryFilterDropdown.svelte @@ -1,23 +1,29 @@ -
- - -
+
+
+ -
- + {#if isOpen} +
+ -
    - {#each adventure_types as type} -
  • - -
  • - {/each} -
-
+
    + {#each adventure_types as type} +
  • + +
  • + {/each} +
+
+ {/if}
diff --git a/frontend/src/lib/config.ts b/frontend/src/lib/config.ts index d79955d5..31d95bb0 100644 --- a/frontend/src/lib/config.ts +++ b/frontend/src/lib/config.ts @@ -1,4 +1,4 @@ -export let appVersion = 'v0.12.0-main-022726'; +export let appVersion = 'v0.12.0-main-031526'; export let versionChangelog = 'https://github.com/seanmorley15/AdventureLog/releases/tag/v0.12.0'; export let appTitle = 'AdventureLog'; export let copyrightYear = '2023-2026'; diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 86b7e92e..78f9f94c 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -1,1145 +1,1146 @@ { - "navbar": { - "collections": "Collections", - "map": "Map", - "users": "Users", - "search": "Search", - "profile": "Profile", - "greeting": "Hi", - "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", - "worldtravel": "World Travel", - "themes": { - "light": "Light", - "dark": "Dark", - "night": "Night", - "forest": "Forest", - "aestheticLight": "Aesthetic Light", - "aestheticDark": "Aesthetic Dark", - "aqua": "Aqua", - "northernLights": "Northern Lights", - "dim": "Dim" - }, - "navigation": "Navigation" - }, - "about": { - "about": "About", - "license": "Licensed under the GPL-3.0 License.", - "message": "Made with ❤️ in the United States.", - "nominatim_1": "Location Search and Geocoding is provided by", - "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", - "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_location": "Edit Location", - "remove_from_collection": "Remove from Collection", - "add_to_collection": "Add to Collection", - "click_map_add_marker": "Click the map to drop a marker, then add it here.", - "add_here": "Add here", - "folder_view": "Folder view - showing all data", - "footprints": "Footprints", - "locations_visited": "Locations visited", - "images_captured": "Images captured", - "geographic_breakdown": "Geographic Breakdown", - "travelers": "Travelers", - "on_this_trip": "On this trip", - "trip_timeline": "Trip Timeline", - "total_days": "Total Days", - "trip_window": "Trip window", - "active_days": "Active Days", - "with_activities": "With activities", - "total_visits": "Total visits", - "distance_traveled": "Distance Traveled", - "traveled": "traveled", - "physical_activities": "Physical Activities", - "gained": "gained", - "burned": "burned", - "content_media": "Content & Media", - "written": "Written", - "lists": "Lists", - "segments": "Segments", - "places": "Places", - "files": "Files", - "more_details": "More Details", - "avg_rating": "Avg Rating", - "of_locations": "of locations", - "tasks_done": "Tasks Done", - "items": "items", - "lodging_types": "Lodging Types", - "delete": "Delete", - "duplicate": "Duplicate", - "duplicate_location": "Duplicate Location", - "location_duplicate_success": "Location duplicated successfully!", - "location_duplicate_error": "Failed to duplicate location.", - "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", - "sort": "Sort", - "order_by": "Order By", - "order_direction": "Order Direction", - "ascending": "Ascending", - "descending": "Descending", - "updated": "Updated", - "name": "Name", - "date": "Date", - "tags": "Tags", - "add_a_tag": "Add a tag", - "date_constrain": "Constrain to collection dates", - "rating": "Rating", - "distance": "Distance", - "share_collection": "Share this Collection!", - "copy_link": "Copy Link", - "sun_times": "Sun Times", - "sunrise": "Sunrise", - "sunset": "Sunset", - "open_in_maps": "Open in Maps", - "fetch_image": "Fetch Image", - "wikipedia": "Wikipedia", - "add": "Add", - "end_date": "End Date", - "start_date": "Start Date", - "remove": "Remove", - "location": "Location", - "search_for_location": "Search for a location", - "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", - "attachments": "Attachments", - "attachment": "Attachment", - "images": "Images", - "image_modal_navigate": "Use arrow keys or click to navigate", - "generate_desc": "Generate Description", - "public_location": "Public Location", - "link": "Link", - "links": "Links", - "description": "Description", - "sources": "Sources", - "collection_locations": "Include Collection Locations", - "filter": "Filter", - "category_filter": "Category Filter", - "category": "Category", - "categories": "Categories", - "routes_and_activities": "Routes & Activities", - "gpx_routes": "GPX Routes", - "transport_activity_paths": "Transport & activity paths", - "clear": "Clear", - "my_collections": "My Collections", - "show_filters": "Show Filters", - "hide_filters": "Hide Filters", - "pins": "pins", - "reset_filters": "Reset", - "clear_search": "Clear search", - "archived_collections": "Archived Collections", - "share": "Share", - "private": "Private", - "public": "Public", - "archived": "Archived", - "name_location": "name, location", - "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", - "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", - "folder": "Folder", - "upcoming": "Upcoming", - "in_progress": "In Progress", - "completed": "Completed", - "day": "Day", - "days": "days", - "country": "country", - "in": "in", - "status_filter": "Status Filter", - "filters_and_sort": "Filters & Sort", - "all": "All", - "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", - "image_fetch_failed": "Failed to fetch image", - "lodging": "Lodging", - "create_location": "Create Location", - "new_location": "New Location", - "basic_information": "Basic Information", - "adventure_not_found": "There are no adventures to display. Add some using the plus button at the bottom right or try changing filters!", - "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", - "update_visited_regions": "Update Visited Regions", - "link_new": "Link New...", - "add_new": "Add New...", - "transportation": "Transportation", - "note": "Note", - "checklist": "Checklist", - "notes": "Notes", - "checklists": "Checklists", - "transportations": "Transportations", - "visit_calendar": "Visit Calendar", - "copied_to_clipboard": "Copied to clipboard!", - "copy_failed": "Copy failed", - "view_profile": "View Profile", - "joined": "Joined", - "from": "From", - "to": "To", - "start": "Start", - "end": "End", - "download_calendar": "Download Calendar", - "all_day": "All Day", - "itinerary": "Itinerary", - "itinerary_link_modal": { - "title": "Link Items to {date}", - "items_available": "{count} items available to link", - "items_on_this_day": "Items on this day", - "already_added_on_this_day": "Already added on this day", - "already_added_on_this_day_desc": "These items are already scheduled for this day.", - "already_added_other_days": "Already added on other days", - "already_added_other_days_desc": "These items are scheduled on different dates. Adding them here will update their date or add them as-is.", - "items_on_other_days": "Items on other days", - "items_on_other_days_desc": "These items have different dates. You can add them and optionally update their date to match.", - "no_unscheduled_items": "No unscheduled items available", - "no_unscheduled_items_desc": "All items have been added to the itinerary or there are no items to add.", - "add_to_itinerary": "Add to Itinerary", - "already_added": "Already Added", - "add_here": "Add Here", - "add_here_keep_date": "Add (Keep Date)" - }, - "date_information": "Date Information", - "out_of_range": "Not in itinerary date range", - "preview": "Preview", - "city": "City", - "region": "Region", - "md_instructions": "Write your markdown here...", - "attachment_upload_success": "Attachment uploaded successfully!", - "attachment_upload_error": "Error uploading attachment", - "upload": "Upload", - "attachment_delete_success": "Attachment deleted 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": {}, - "price": "Price", - "filters_and_stats": "Filters & Stats", - "travel_progress": "Travel Progress", - "left_collection_message": "Successfully left collection", - "leave_collection": "Leave Collection", - "leave": "Leave", - "leave_collection_warning": "Are you sure you want to leave this collection? Any locations you added will be unlinked and remain in your account.", - "loading_collections": "Loading collections...", - "quick_start": "Quick Start", - "details": "Details", - "search_location": "Search for a location", - "search_placeholder": "Enter city, location, or landmark...", - "searching": "Searching", - "use_current_location": "Use Current Location", - "select_on_map": "Select on Map", - "click_map": "Click on the map to select a location", - "getting_location_details": "Getting location details", - "location_selected": "Location Selected", - "continue": "Continue", - "update_location_details": "Update location details", - "create_new_location": "Create new location", - "wikipedia_error": "Failed to generate description from Wikipedia", - "public_location_description": "Make this location visible to other users", - "location_map": "Location & Map", - "location_display_name": "Location Display Name", - "or": "OR", - "click_on_map": "Click on the map to select a location", - "enter_location_display_name": "Enter location display name", - "airport_code_examples": "JFK, LAX, LHR...", - "back": "Back", - "processing": "Processing", - "no_file_selected": "No file selected", - "attachment_name_required": "Attachment name required", - "attachment_updated": "Attachment updated successfully", - "attachment_removed": "Attachment removed successfully", - "attachment_remove_error": "An error occured while removing the attachment", - "trail_link_required": "A trail link is required", - "trail_created_successfully": "Trail created successfully", - "trail_creation_failed": "Failed to create trail", - "trail_fetch_failed": "Failed to fetch Wanderer trails", - "trail_updated_successfully": "Trail updated successfully", - "trail_update_failed": "Failed to update trail", - "trail_removed_successfully": "Trail removed successfully", - "trail_removal_failed": "Failed to remove trail", - "image_management": "Image Management", - "upload_from_device": "Upload From Device", - "upload_from_url": "Upload From URL", - "no_images_uploaded_yet": "No images uploaded yet", - "upload_first_image": "Upload your first image using one of the options above", - "attachment_management": "Attachment Management", - "upload_attachment": "Upload Attachment", - "no_attachments_uploaded_yet": "No attachments uploaded yet", - "upload_first_attachment": "Upload your first attachment using the options above", - "trails_management": "Trails Management", - "trails_management_description": "Manage trails associated with this location. Trails can be linked to external services like AllTrails or link to Wanderer trails.", - "add_new_trail": "Add New Trail", - "create_trail": "Create Trail", - "add_wanderer_trail": "Add Wanderer Trail", - "select_wanderer_trail": "Select a trail from your Wanderer account", - "trails_found_for": "trails found for", - "no_trails_found_matching": "No trails found matching", - "no_trails_available": "No Trails Available", - "wanderer_integration_error": "Wanderer integration is not enabled or has expired.", - "no_external_link": "No external link available", - "no_trails_added": "No trails added yet", - "add_first_trail": "Add your first trail using the button above", - "search_trails_placeholder": "Search trails by name", - "trail_name": "Trail Name", - "external_link": "External Link", - "add_trail": "Add Trail", - "created": "Created", - "photos": "Photos", - "view_gpx": "View GPX", - "gain": "gain", - "likes": "Likes", - "view_trail": "View Trail", - "time": "Time", - "trail": "Trail", - "import_activity": "Import activity", - "activity_options": "Activity options", - "export_gpx": "Export GPX", - "export_original": "Export Original", - "view_on": "View on", - "moving_time": "Moving Time", - "avg_speed": "Average Speed", - "elevation": "Elevation", - "cadence": "Cadence", - "calories": "Calories", - "achievements": "Achievements", - "pace": "Pace", - "max_speed": "Max Speed", - "previous_image": "Previous image", - "next_image": "Next image", - "trails": "Trails", - "date_selection": "Date Selection", - "notes_placeholder": "Add notes about this visit", - "no_visits_description": "Create your first visit by selecting dates above", - "saved_activities": "Saved Activities", - "view_strava_activities": "View Strava activities", - "complete_strava_import": "Complete Strava Import", - "add_new_activity": "Add New Activity", - "strava_activity_ready": "Strava Activity Ready", - "gpx_file_downloaded": "GPX file downloaded. Please upload it below to complete the import.", - "gpx_file_required": "GPX File Required", - "upload_gpx_file": "Upload the GPX file that was just downloaded to complete the Strava import", - "activity_name": "Activity Name", - "activity_name_placeholder": "Morning Run", - "sport_type": "Sport Type", - "elapsed_time": "Elapsed Time", - "elevation_gain": "Elevation Gain", - "elevation_loss": "Elevation Loss", - "elevation_high": "Elevation High", - "elevation_low": "Elevation Low", - "rest_time": "Rest Time", - "start_lat": "Start Latitude", - "start_lng": "Start Longitude", - "end_lat": "End Latitude", - "end_lng": "End Longitude", - "average_speed": "Average Speed", - "average_cadence": "Average Cadence", - "gpx_file": "GPX File", - "importing": "Importing", - "uploading": "Uploading", - "upload_activity": "Upload Activity", - "complete_import": "Complete Import", - "loading_activities": "Loading activities", - "no_strava_activities": "No Strava activities found during this visit", - "show_strava_activities": "Show Strava Activities", - "hide_strava_activities": "Hide Strava Activities", - "current_attachments": "Current Attachments", - "connect_to_wanderer": "Connect to Wanderer", - "activity_statistics": "Activity Statistics", - "activity_statistics_description": "Your fitness and activity achievements", - "total_recorded": "Total recorded", - "active_duration": "Active duration", - "activity_name_required": "Activity name is required", - "confirm_delete_activity": "Are you sure you want to delete this activity?", - "update_visit": "Update Visit", - "add_visit": "Add Visit", - "add_activity": "Add Activity", - "edit_visit": "Edit Visit", - "remove_visit": "Remove Visit", - "download_gpx": "Download GPX", - "strava_activities_during_visit": "Strava Activities During Visit", - "total": "Total", - "rest": "Rest", - "high": "High", - "low": "Low", - "lodging_save_error": "Error saving lodging", - "activities_text": "activities", - "total_climbed": "Total climbed", - "distance_covered": "Distance covered", - "total_distance": "Total Distance", - "total_activities": "Total Activities", - "recorded_sessions": "Recorded sessions", - "activity_breakdown_by_category": "Activity Breakdown by Category", - "dates_not_saved": "Visit Not Added Yet", - "dates_not_saved_description": "Click add visit to save", - "link_copied": "Link Copied", - "wiki_results_found": "Wikipedia Results", - "select": "Select", - "lodging_not_found": "Lodging Not Found", - "nights": "Nights", - "reservation": "Reservation", - "flight": "Flight", - "route": "Route", - "my_locations": "My Locations", - "no_linkable_locations": "No locations found that can be linked to this collection.", - "all_locations_already_linked": "All locations are already linked to this collection.", - "transportation_gpx_tip": "Upload GPX files here to have them displayed on the map", - "view": "View", - "overnight": "Overnight", - "saving": "Saving", - "countries": "Countries", - "regions": "Regions", - "cities": "Cities", - "stays": "Stays", - "airport_search_mode": "Airport Search Mode", - "location_search_mode": "Location Search Mode", - "departure_airport": "Departure Airport", - "start_location": "Start Location", - "arrival_airport": "Arrival Airport", - "end_location": "End Location", - "route_selected": "Route Selected", - "search_start_end_locations": "Search start and end locations", - "search_start_location": "Search start location", - "search_end_location": "Search end location", - "import_success": "Import Success", - "import_failed": "Import Failed", - "import_from_file": "Import from file", - "export_zip": "Export ZIP", - "export_failed": "Export failed", - "export_success": "Exported collection", - "location_actions": "Location actions", - "collection_duplicate_success": "Collection duplicated successfully! Redirecting...", - "collection_duplicate_error": "Failed to duplicate collection." - }, - "worldtravel": { - "country_list": "Country List", - "cities_in": "Cities in", - "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", - "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", - "getting_location_details": "Getting location details", - "hide_globe_spin": "Hide Globe Spin", - "show_globe_spin": "Show Globe Spin", - "loading_globe_spin": "Loading Globe Spin", - "spinning_globe": "Spinning Globe", - "destination_revealed": "Destination Revealed!", - "your_random_adventure_awaits": "Your Random Adventure Awaits!", - "exploration_progress": "Exploration Progress", - "dive_deeper": "Dive Deeper", - "cities_available": "Cities Available", - "in": "in", - "explore_country": "Explore Country", - "spin_again": "Spin Again", - "globe_spin_error_desc": "Error fetching globe spin data", - "try_again": "Try Again", - "no_globe_spin_data": "No Globe Spin Data", - "show_less": "Show Less", - "show_more": "Show More", - "about_country": "About Country", - "about_region": "About Region", - "all_locations_visited": "All locations visited!" - }, - "auth": { - "username": "Username", - "password": "Password", - "forgot_password": "Forgot Password?", - "signup": "Signup", - "user_email_verification_required": "Email verification required. Please check your email for a verification link.", - "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", - "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", - "enter_username": "Enter your username", - "enter_password": "Enter your password", - "totp": "Two-Factor Code", - "logging_in": "Logging in" - }, - "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.", - "use_imperial": "Use Imperial Units", - "use_imperial_desc": "Use imperial units (feet, inches, pounds) instead of metric units", - "trails": "Trails", - "activities": "Activities" - }, - "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.", - "update_collection_details": "Updte collection details", - "create_new_collection": "Create new collection", - "public_collection_description": "Allow anyone with the link to view", - "enter_collection_name": "Enter collection name", - "changing_date_title": "Changing dates will affect itinerary items", - "changing_date_warning": "Any itinerary items outside the new date range will be removed from the itinerary and placed back into the collection's undated items.", - "cover_image": "Cover image", - "cover_image_hint": "Choose a cover from images in this collection.", - "no_images_available": "No images available from linked locations yet.", - "cover": "Cover", - "location_primary": "Location cover", - "set_cover": "Set cover", - "clear_cover": "Clear cover", - "collaborators": "Collaborators" - }, - "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.", - "invalid_url": "Invalid URL", - "enter_note_title": "Enter note title", - "update_note_details": "Editing note", - "create_new_note": "Create new note", - "viewing_note": "Viewing note" - }, - "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", - "editing_checklist": "Editing Checklist", - "checklist_viewer": "Checklist Viewer", - "update_checklist_details": "Editing checklist", - "viewing_checklist": "Viewing checklist", - "enter_checklist_title": "Enter checklist title", - "add_new_item": "Add New Item", - "current_items": "Current Items", - "completed": "Completed", - "no_items_yet": "No Items Yet", - "add_your_first_item": "Add your first item" - }, - "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", - "edit": "Edit", - "modes": { - "car": "Car", - "plane": "Plane", - "train": "Train", - "bus": "Bus", - "boat": "Boat", - "bike": "Bike", - "walking": "Walking", - "other": "Other" - }, - "edit_transportation": "Edit Transportation", - "update_transportation_details": "Update Transportation Details", - "create_new_transportation": "New Transportation", - "enter_transportation_name": "Enter transportation name", - "select_type": "Select Type", - "enter_link": "Enter link", - "enter_flight_number": "Enter flight number", - "enter_from_location": "Enter from location", - "enter_to_location": "Enter to location", - "arrival_code": "Arrival Code", - "departure_code": "Departure Code", - "arrival_date": "Arrival Date", - "departure_timezone": "Departure Timezone", - "arrival_timezone": "Arrival Timezone", - "departure_date": "Departure Date" - }, - "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", - "update_lodging_details": "Update Lodging Details", - "create_new_lodging": "New Lodging", - "enter_lodging_name": "Enter lodging name", - "enter_reservation_number": "Enter reservation number", - "invalid_link": "Please enter a valid URL (e.g. https://example.com).", - "save_failed": "Failed to save lodging. Please try again." - }, - "search": { - "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", - "location_map": "Location Map", - "add_location_at_marker": "Add New Location at Marker", - "clear_marker": "Clear Marker", - "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", - "show_visited_cities": "Visited Cities", - "search_locations": "Search locations..." - }, - "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", - "revoke_invite": "Revoke Invite", - "send_invite": "Send Invite", - "available": "Available", - "pending": "Pending", - "available_users": "Available Users", - "revoke_failed": "Revoke Failed", - "invite_revoked": "Invite Revoked", - "unshare_failed": "Unshare Failed", - "invite_failed": "Invite Failed", - "invite_sent": "Invite Sent" - }, - "languages": {}, - "profile": { - "member_since": "Member since", - "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?", - "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.", - "fetch_error": "Error fetching data from the Immich integration", - "error_no_object_id": "No object ID was provided", - "by_date": "By Date", - "by_album": "By Album", - "image_search_placeholder": "Search using Immich", - "select_album": "Select Album", - "loading_albums": "Loading Albums", - "loading": "Loading", - "no_images": "No Images Found", - "try_different_date": "Try a different date" - }, - "google_maps": { - "google_maps_integration_desc": "Connect your Google Maps account to get high-quality location search results and recommendations.", - "google_maps_integration_desc_no_staff": "This integration must first be enabled by the admin on this server." - }, - "recomendations": { - "recommendations": "Recommendations", - "food": "Food", - "tourism": "Tourism", - "discover_places": "Discover Places", - "search_around_location": "Search Around Location", - "search_by_address": "Search by Address", - "lodging": "Hotels & Lodging", - "search_radius_label": "Search Radius:", - "searching": "Searching...", - "minimum_rating": "Minimum Rating", - "minimum_reviews": "Minimum Reviews", - "open_now_only": "Open Now Only", - "total_results": "Total Results", - "average_rating": "Average Rating", - "map_view": "Map View", - "no_results_yet": "No Results Yet", - "select_location_or_query": "Select a location or enter a search query to discover amazing places nearby!", - "use_search_instead": "Use search instead", - "any": "Any", - "add_location": "Add Location", - "add_lodging": "Add Lodging", - "hours": "Hours", - "open": "Open", - "away": "away", - "your_location": "Your Location" - }, - "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", - "event timezone": "Event timezone", - "your timezone": "Your timezone" - }, - "locations": { - "location": "Location", - "locations": "Locations", - "my_locations": "My Locations" - }, - "settings_download_backup": "Download Backup", - "invites": { - "accepted": "Invite accepted", - "accept_failed": "Failed to accept invite", - "declined": "Invite declined", - "decline_failed": "Failed to decline invite", - "title": "Invites", - "pending_invites": "Pending Invites", - "no_invites": "No invites", - "decline": "Decline", - "accept": "Accept", - "invited_on": "Invited on", - "no_invites_desc": "Make sure your profile is public so users can invite you.", - "by": "by" - }, - "strava": { - "strava_integration_desc": "Connect to Strava to easily import your activties into locations and visits", - "connect_account": "Connect Account", - "disconnect": "Disconnect", - "authorization_error": "Error redirecting to strava authorization URL", - "disconnected": "Successfully disconnected from Strava", - "disconnect_error": "Error disconnecting from Strava", - "gpx_required": "Please upload the GPX file to complete the Strava import", - "not_enabled": "Strava integration is not enabled on this instance." - }, - "wanderer": { - "wanderer_integration_desc": "Connect to Wanderer to easily import and view your trails in locations", - "connection_error": "Error connecting to Wanderer", - "connected": "Successfully connected to Wanderer" - }, - "itinerary": { - "remove_from_itinerary": "Remove from Day", - "item_remove_success": "Item removed from itinerary", - "item_remove_error": "Error removing item from itinerary", - "auto_generate_itinerary": "Auto-Generate Itinerary", - "auto_generate_itinerary_desc": "This collection has dated items but no itinerary yet. Would you like to automatically organize them by date?", - "no_itinerary_yet": "No Itinerary Yet", - "start_planning": "Start planning your trip by adding items to specific days.", - "generating": "Generating", - "auto_generate": "Auto-Generate", - "link_existing_item": "Link existing item", - "no_plans_for_day": "No plans for this day", - "multi_day": "Multi-day", - "item_not_found": "Item not found", - "staying_overnight": "Staying overnight", - "unscheduled_items": "Unscheduled Items", - "unscheduled_items_desc": "These items are linked to this trip but haven't been added to a specific day yet.", - "change_day": "Change Day", - "trip_context": "Trip Context", - "move_to_trip_context": "Move to Trip Context", - "trip_context_info": "Trip context items apply to the whole trip — for example locations that are the destination itself, general notes, or packing lists that are important for the entire trip.", - "add_to_trip_context": "Add trip context", - "no_trip_context_items": "No trip context items yet.", - "add_description": "Add description", - "moved_to_trip_context": "Moved to trip context", - "failed_to_move_to_trip_context": "Failed to move to trip context", - "item_already_in_trip_context": "Items already in trip context", - "added_to_trip_context": "Added to trip context", - "failed_to_add_to_trip_context": "Failed to add item to trip context", - "remove_from_trip_context": "Remove from Context", - "drag_to_reorder": "Drag to reorder", - "add_to_day": "Add to day" - }, - "common": { - "show_less": "Hide details", - "show_more": "Show more" - }, - "collections": { - "not_found": "Collection Not Found", - "all_items": "All Items", - "no_calendar_events": "No visits are scheduled for this collection yet.", - "events": "events", - "times_shown_in": "Times shown in", - "event_timezone": "Event timezone", - "local_timezone": "My timezone", - "event_timezone_desc": "Event timezone uses the location or item timezone when available. My timezone uses", - "trip_costs": "Trip Costs", - "currency": "Currency", - "currencies": "Currencies", - "no_priced_items": "Add prices to locations, lodging, or transportation to see trip totals by currency.", - "statistics": "Statistics" - }, - "currencies": { - "USD": "US Dollar", - "EUR": "Euro", - "GBP": "British Pound", - "JPY": "Japanese Yen", - "AUD": "Australian Dollar", - "CAD": "Canadian Dollar", - "CHF": "Swiss Franc", - "CNY": "Chinese Yuan", - "HKD": "Hong Kong Dollar", - "SGD": "Singapore Dollar", - "SEK": "Swedish Krona", - "NOK": "Norwegian Krone", - "DKK": "Danish Krone", - "NZD": "New Zealand Dollar", - "INR": "Indian Rupee", - "MXN": "Mexican Peso", - "BRL": "Brazilian Real", - "ZAR": "South African Rand", - "AED": "UAE Dirham", - "TRY": "Turkish Lira", - "select_currency": "Select currency", - "search": "Search currency", - "no_matches": "No matches" - } + "navbar": { + "collections": "Collections", + "map": "Map", + "users": "Users", + "search": "Search", + "profile": "Profile", + "greeting": "Hi", + "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", + "worldtravel": "World Travel", + "themes": { + "light": "Light", + "dark": "Dark", + "night": "Night", + "forest": "Forest", + "aestheticLight": "Aesthetic Light", + "aestheticDark": "Aesthetic Dark", + "aqua": "Aqua", + "northernLights": "Northern Lights", + "dim": "Dim" + }, + "navigation": "Navigation" + }, + "about": { + "about": "About", + "license": "Licensed under the GPL-3.0 License.", + "message": "Made with ❤️ in the United States.", + "nominatim_1": "Location Search and Geocoding is provided by", + "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", + "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_location": "Edit Location", + "remove_from_collection": "Remove from Collection", + "add_to_collection": "Add to Collection", + "click_map_add_marker": "Click the map to drop a marker, then add it here.", + "add_here": "Add here", + "folder_view": "Folder view - showing all data", + "footprints": "Footprints", + "locations_visited": "Locations visited", + "images_captured": "Images captured", + "geographic_breakdown": "Geographic Breakdown", + "travelers": "Travelers", + "on_this_trip": "On this trip", + "trip_timeline": "Trip Timeline", + "total_days": "Total Days", + "trip_window": "Trip window", + "active_days": "Active Days", + "with_activities": "With activities", + "total_visits": "Total visits", + "distance_traveled": "Distance Traveled", + "traveled": "traveled", + "physical_activities": "Physical Activities", + "gained": "gained", + "burned": "burned", + "content_media": "Content & Media", + "written": "Written", + "lists": "Lists", + "segments": "Segments", + "places": "Places", + "files": "Files", + "more_details": "More Details", + "avg_rating": "Avg Rating", + "of_locations": "of locations", + "tasks_done": "Tasks Done", + "items": "items", + "lodging_types": "Lodging Types", + "delete": "Delete", + "duplicate": "Duplicate", + "duplicate_location": "Duplicate Location", + "location_duplicate_success": "Location duplicated successfully!", + "location_duplicate_error": "Failed to duplicate location.", + "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", + "sort": "Sort", + "order_by": "Order By", + "order_direction": "Order Direction", + "ascending": "Ascending", + "descending": "Descending", + "updated": "Updated", + "name": "Name", + "date": "Date", + "tags": "Tags", + "add_a_tag": "Add a tag", + "date_constrain": "Constrain to collection dates", + "rating": "Rating", + "distance": "Distance", + "share_collection": "Share this Collection!", + "copy_link": "Copy Link", + "sun_times": "Sun Times", + "sunrise": "Sunrise", + "sunset": "Sunset", + "open_in_maps": "Open in Maps", + "fetch_image": "Fetch Image", + "wikipedia": "Wikipedia", + "add": "Add", + "end_date": "End Date", + "start_date": "Start Date", + "remove": "Remove", + "location": "Location", + "search_for_location": "Search for a location", + "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", + "attachments": "Attachments", + "attachment": "Attachment", + "images": "Images", + "image_modal_navigate": "Use arrow keys or click to navigate", + "generate_desc": "Generate Description", + "public_location": "Public Location", + "link": "Link", + "links": "Links", + "description": "Description", + "sources": "Sources", + "collection_locations": "Include Collection Locations", + "filter": "Filter", + "category_filter": "Category Filter", + "category": "Category", + "categories": "Categories", + "routes_and_activities": "Routes & Activities", + "gpx_routes": "GPX Routes", + "transport_activity_paths": "Transport & activity paths", + "clear": "Clear", + "my_collections": "My Collections", + "show_filters": "Show Filters", + "hide_filters": "Hide Filters", + "pins": "pins", + "reset_filters": "Reset", + "clear_search": "Clear search", + "archived_collections": "Archived Collections", + "share": "Share", + "private": "Private", + "public": "Public", + "archived": "Archived", + "name_location": "name, location", + "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", + "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", + "folder": "Folder", + "upcoming": "Upcoming", + "in_progress": "In Progress", + "completed": "Completed", + "day": "Day", + "days": "days", + "country": "country", + "in": "in", + "status_filter": "Status Filter", + "filters_and_sort": "Filters & Sort", + "all": "All", + "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", + "image_fetch_failed": "Failed to fetch image", + "lodging": "Lodging", + "create_location": "Create Location", + "new_location": "New Location", + "basic_information": "Basic Information", + "adventure_not_found": "There are no adventures to display. Add some using the plus button at the bottom right or try changing filters!", + "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", + "update_visited_regions": "Update Visited Regions", + "link_new": "Link New...", + "add_new": "Add New...", + "transportation": "Transportation", + "note": "Note", + "checklist": "Checklist", + "notes": "Notes", + "checklists": "Checklists", + "transportations": "Transportations", + "visit_calendar": "Visit Calendar", + "copied_to_clipboard": "Copied to clipboard!", + "copy_failed": "Copy failed", + "view_profile": "View Profile", + "joined": "Joined", + "from": "From", + "to": "To", + "start": "Start", + "end": "End", + "download_calendar": "Download Calendar", + "all_day": "All Day", + "itinerary": "Itinerary", + "itinerary_link_modal": { + "title": "Link Items to {date}", + "items_available": "{count} items available to link", + "items_on_this_day": "Items on this day", + "already_added_on_this_day": "Already added on this day", + "already_added_on_this_day_desc": "These items are already scheduled for this day.", + "already_added_other_days": "Already added on other days", + "already_added_other_days_desc": "These items are scheduled on different dates. Adding them here will update their date or add them as-is.", + "items_on_other_days": "Items on other days", + "items_on_other_days_desc": "These items have different dates. You can add them and optionally update their date to match.", + "no_unscheduled_items": "No unscheduled items available", + "no_unscheduled_items_desc": "All items have been added to the itinerary or there are no items to add.", + "add_to_itinerary": "Add to Itinerary", + "already_added": "Already Added", + "add_here": "Add Here", + "add_here_keep_date": "Add (Keep Date)" + }, + "date_information": "Date Information", + "out_of_range": "Not in itinerary date range", + "preview": "Preview", + "city": "City", + "region": "Region", + "md_instructions": "Write your markdown here...", + "attachment_upload_success": "Attachment uploaded successfully!", + "attachment_upload_error": "Error uploading attachment", + "upload": "Upload", + "attachment_delete_success": "Attachment deleted 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": {}, + "price": "Price", + "filters_and_stats": "Filters & Stats", + "travel_progress": "Travel Progress", + "left_collection_message": "Successfully left collection", + "leave_collection": "Leave Collection", + "leave": "Leave", + "leave_collection_warning": "Are you sure you want to leave this collection? Any locations you added will be unlinked and remain in your account.", + "loading_collections": "Loading collections...", + "quick_start": "Quick Start", + "details": "Details", + "search_location": "Search for a location", + "search_placeholder": "Enter city, location, or landmark...", + "searching": "Searching", + "use_current_location": "Use Current Location", + "select_on_map": "Select on Map", + "click_map": "Click on the map to select a location", + "getting_location_details": "Getting location details", + "location_selected": "Location Selected", + "continue": "Continue", + "update_location_details": "Update location details", + "create_new_location": "Create new location", + "wikipedia_error": "Failed to generate description from Wikipedia", + "public_location_description": "Make this location visible to other users", + "location_map": "Location & Map", + "location_display_name": "Location Display Name", + "or": "OR", + "click_on_map": "Click on the map to select a location", + "enter_location_display_name": "Enter location display name", + "airport_code_examples": "JFK, LAX, LHR...", + "back": "Back", + "processing": "Processing", + "no_file_selected": "No file selected", + "attachment_name_required": "Attachment name required", + "attachment_updated": "Attachment updated successfully", + "attachment_removed": "Attachment removed successfully", + "attachment_remove_error": "An error occured while removing the attachment", + "trail_link_required": "A trail link is required", + "trail_created_successfully": "Trail created successfully", + "trail_creation_failed": "Failed to create trail", + "trail_fetch_failed": "Failed to fetch Wanderer trails", + "trail_updated_successfully": "Trail updated successfully", + "trail_update_failed": "Failed to update trail", + "trail_removed_successfully": "Trail removed successfully", + "trail_removal_failed": "Failed to remove trail", + "image_management": "Image Management", + "upload_from_device": "Upload From Device", + "upload_from_url": "Upload From URL", + "no_images_uploaded_yet": "No images uploaded yet", + "upload_first_image": "Upload your first image using one of the options above", + "attachment_management": "Attachment Management", + "upload_attachment": "Upload Attachment", + "no_attachments_uploaded_yet": "No attachments uploaded yet", + "upload_first_attachment": "Upload your first attachment using the options above", + "trails_management": "Trails Management", + "trails_management_description": "Manage trails associated with this location. Trails can be linked to external services like AllTrails or link to Wanderer trails.", + "add_new_trail": "Add New Trail", + "create_trail": "Create Trail", + "add_wanderer_trail": "Add Wanderer Trail", + "select_wanderer_trail": "Select a trail from your Wanderer account", + "trails_found_for": "trails found for", + "no_trails_found_matching": "No trails found matching", + "no_trails_available": "No Trails Available", + "wanderer_integration_error": "Wanderer integration is not enabled or has expired.", + "no_external_link": "No external link available", + "no_trails_added": "No trails added yet", + "add_first_trail": "Add your first trail using the button above", + "search_trails_placeholder": "Search trails by name", + "trail_name": "Trail Name", + "external_link": "External Link", + "add_trail": "Add Trail", + "created": "Created", + "photos": "Photos", + "view_gpx": "View GPX", + "gain": "gain", + "likes": "Likes", + "view_trail": "View Trail", + "time": "Time", + "trail": "Trail", + "import_activity": "Import activity", + "activity_options": "Activity options", + "export_gpx": "Export GPX", + "export_original": "Export Original", + "view_on": "View on", + "moving_time": "Moving Time", + "avg_speed": "Average Speed", + "elevation": "Elevation", + "cadence": "Cadence", + "calories": "Calories", + "achievements": "Achievements", + "pace": "Pace", + "max_speed": "Max Speed", + "previous_image": "Previous image", + "next_image": "Next image", + "trails": "Trails", + "date_selection": "Date Selection", + "notes_placeholder": "Add notes about this visit", + "no_visits_description": "Create your first visit by selecting dates above", + "saved_activities": "Saved Activities", + "view_strava_activities": "View Strava activities", + "complete_strava_import": "Complete Strava Import", + "add_new_activity": "Add New Activity", + "strava_activity_ready": "Strava Activity Ready", + "gpx_file_downloaded": "GPX file downloaded. Please upload it below to complete the import.", + "gpx_file_required": "GPX File Required", + "upload_gpx_file": "Upload the GPX file that was just downloaded to complete the Strava import", + "activity_name": "Activity Name", + "activity_name_placeholder": "Morning Run", + "sport_type": "Sport Type", + "elapsed_time": "Elapsed Time", + "elevation_gain": "Elevation Gain", + "elevation_loss": "Elevation Loss", + "elevation_high": "Elevation High", + "elevation_low": "Elevation Low", + "rest_time": "Rest Time", + "start_lat": "Start Latitude", + "start_lng": "Start Longitude", + "end_lat": "End Latitude", + "end_lng": "End Longitude", + "average_speed": "Average Speed", + "average_cadence": "Average Cadence", + "gpx_file": "GPX File", + "importing": "Importing", + "uploading": "Uploading", + "upload_activity": "Upload Activity", + "complete_import": "Complete Import", + "loading_activities": "Loading activities", + "no_strava_activities": "No Strava activities found during this visit", + "show_strava_activities": "Show Strava Activities", + "hide_strava_activities": "Hide Strava Activities", + "current_attachments": "Current Attachments", + "connect_to_wanderer": "Connect to Wanderer", + "activity_statistics": "Activity Statistics", + "activity_statistics_description": "Your fitness and activity achievements", + "total_recorded": "Total recorded", + "active_duration": "Active duration", + "activity_name_required": "Activity name is required", + "confirm_delete_activity": "Are you sure you want to delete this activity?", + "update_visit": "Update Visit", + "add_visit": "Add Visit", + "add_activity": "Add Activity", + "edit_visit": "Edit Visit", + "remove_visit": "Remove Visit", + "download_gpx": "Download GPX", + "strava_activities_during_visit": "Strava Activities During Visit", + "total": "Total", + "rest": "Rest", + "high": "High", + "low": "Low", + "lodging_save_error": "Error saving lodging", + "activities_text": "activities", + "total_climbed": "Total climbed", + "distance_covered": "Distance covered", + "total_distance": "Total Distance", + "total_activities": "Total Activities", + "recorded_sessions": "Recorded sessions", + "activity_breakdown_by_category": "Activity Breakdown by Category", + "dates_not_saved": "Visit Not Added Yet", + "dates_not_saved_description": "Click add visit to save", + "link_copied": "Link Copied", + "wiki_results_found": "Wikipedia Results", + "select": "Select", + "lodging_not_found": "Lodging Not Found", + "nights": "Nights", + "reservation": "Reservation", + "flight": "Flight", + "route": "Route", + "my_locations": "My Locations", + "no_linkable_locations": "No locations found that can be linked to this collection.", + "all_locations_already_linked": "All locations are already linked to this collection.", + "transportation_gpx_tip": "Upload GPX files here to have them displayed on the map", + "view": "View", + "overnight": "Overnight", + "saving": "Saving", + "countries": "Countries", + "regions": "Regions", + "cities": "Cities", + "stays": "Stays", + "airport_search_mode": "Airport Search Mode", + "location_search_mode": "Location Search Mode", + "departure_airport": "Departure Airport", + "start_location": "Start Location", + "arrival_airport": "Arrival Airport", + "end_location": "End Location", + "route_selected": "Route Selected", + "search_start_end_locations": "Search start and end locations", + "search_start_location": "Search start location", + "search_end_location": "Search end location", + "import_success": "Import Success", + "import_failed": "Import Failed", + "import_from_file": "Import from file", + "export_zip": "Export ZIP", + "export_failed": "Export failed", + "export_success": "Exported collection", + "location_actions": "Location actions", + "collection_duplicate_success": "Collection duplicated successfully! Redirecting...", + "collection_duplicate_error": "Failed to duplicate collection." + }, + "worldtravel": { + "country_list": "Country List", + "cities_in": "Cities in", + "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", + "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", + "getting_location_details": "Getting location details", + "hide_globe_spin": "Hide Globe Spin", + "show_globe_spin": "Show Globe Spin", + "loading_globe_spin": "Loading Globe Spin", + "spinning_globe": "Spinning Globe", + "destination_revealed": "Destination Revealed!", + "your_random_adventure_awaits": "Your Random Adventure Awaits!", + "exploration_progress": "Exploration Progress", + "dive_deeper": "Dive Deeper", + "cities_available": "Cities Available", + "in": "in", + "explore_country": "Explore Country", + "spin_again": "Spin Again", + "globe_spin_error_desc": "Error fetching globe spin data", + "try_again": "Try Again", + "no_globe_spin_data": "No Globe Spin Data", + "show_less": "Show Less", + "show_more": "Show More", + "about_country": "About Country", + "about_region": "About Region", + "all_locations_visited": "All locations visited!" + }, + "auth": { + "username": "Username", + "password": "Password", + "forgot_password": "Forgot Password?", + "signup": "Signup", + "user_email_verification_required": "Email verification required. Please check your email for a verification link.", + "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", + "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", + "enter_username": "Enter your username", + "enter_password": "Enter your password", + "totp": "Two-Factor Code", + "logging_in": "Logging in" + }, + "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.", + "use_imperial": "Use Imperial Units", + "use_imperial_desc": "Use imperial units (feet, inches, pounds) instead of metric units", + "trails": "Trails", + "activities": "Activities" + }, + "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.", + "update_collection_details": "Updte collection details", + "create_new_collection": "Create new collection", + "public_collection_description": "Allow anyone with the link to view", + "enter_collection_name": "Enter collection name", + "changing_date_title": "Changing dates will affect itinerary items", + "changing_date_warning": "Any itinerary items outside the new date range will be removed from the itinerary and placed back into the collection's undated items.", + "cover_image": "Cover image", + "cover_image_hint": "Choose a cover from images in this collection.", + "no_images_available": "No images available from linked locations yet.", + "cover": "Cover", + "location_primary": "Location cover", + "set_cover": "Set cover", + "clear_cover": "Clear cover", + "collaborators": "Collaborators" + }, + "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.", + "invalid_url": "Invalid URL", + "enter_note_title": "Enter note title", + "update_note_details": "Editing note", + "create_new_note": "Create new note", + "viewing_note": "Viewing note" + }, + "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", + "editing_checklist": "Editing Checklist", + "checklist_viewer": "Checklist Viewer", + "update_checklist_details": "Editing checklist", + "viewing_checklist": "Viewing checklist", + "enter_checklist_title": "Enter checklist title", + "add_new_item": "Add New Item", + "current_items": "Current Items", + "completed": "Completed", + "no_items_yet": "No Items Yet", + "add_your_first_item": "Add your first item" + }, + "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", + "edit": "Edit", + "modes": { + "car": "Car", + "plane": "Plane", + "train": "Train", + "bus": "Bus", + "boat": "Boat", + "bike": "Bike", + "walking": "Walking", + "other": "Other" + }, + "edit_transportation": "Edit Transportation", + "update_transportation_details": "Update Transportation Details", + "create_new_transportation": "New Transportation", + "enter_transportation_name": "Enter transportation name", + "select_type": "Select Type", + "enter_link": "Enter link", + "enter_flight_number": "Enter flight number", + "enter_from_location": "Enter from location", + "enter_to_location": "Enter to location", + "arrival_code": "Arrival Code", + "departure_code": "Departure Code", + "arrival_date": "Arrival Date", + "departure_timezone": "Departure Timezone", + "arrival_timezone": "Arrival Timezone", + "departure_date": "Departure Date" + }, + "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", + "update_lodging_details": "Update Lodging Details", + "create_new_lodging": "New Lodging", + "enter_lodging_name": "Enter lodging name", + "enter_reservation_number": "Enter reservation number", + "invalid_link": "Please enter a valid URL (e.g. https://example.com).", + "save_failed": "Failed to save lodging. Please try again." + }, + "search": { + "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", + "location_map": "Location Map", + "add_location_at_marker": "Add New Location at Marker", + "clear_marker": "Clear Marker", + "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", + "show_visited_cities": "Visited Cities", + "search_locations": "Search locations..." + }, + "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", + "revoke_invite": "Revoke Invite", + "send_invite": "Send Invite", + "available": "Available", + "pending": "Pending", + "available_users": "Available Users", + "revoke_failed": "Revoke Failed", + "invite_revoked": "Invite Revoked", + "unshare_failed": "Unshare Failed", + "invite_failed": "Invite Failed", + "invite_sent": "Invite Sent" + }, + "languages": {}, + "profile": { + "member_since": "Member since", + "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?", + "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.", + "fetch_error": "Error fetching data from the Immich integration", + "error_no_object_id": "No object ID was provided", + "by_date": "By Date", + "by_album": "By Album", + "image_search_placeholder": "Search using Immich", + "select_album": "Select Album", + "loading_albums": "Loading Albums", + "loading": "Loading", + "no_images": "No Images Found", + "try_different_date": "Try a different date" + }, + "google_maps": { + "google_maps_integration_desc": "Connect your Google Maps account to get high-quality location search results and recommendations.", + "google_maps_integration_desc_no_staff": "This integration must first be enabled by the admin on this server." + }, + "recomendations": { + "recommendations": "Recommendations", + "food": "Food", + "tourism": "Tourism", + "discover_places": "Discover Places", + "search_around_location": "Search Around Location", + "search_by_address": "Search by Address", + "lodging": "Hotels & Lodging", + "search_radius_label": "Search Radius:", + "searching": "Searching...", + "minimum_rating": "Minimum Rating", + "minimum_reviews": "Minimum Reviews", + "open_now_only": "Open Now Only", + "total_results": "Total Results", + "average_rating": "Average Rating", + "map_view": "Map View", + "no_results_yet": "No Results Yet", + "select_location_or_query": "Select a location or enter a search query to discover amazing places nearby!", + "use_search_instead": "Use search instead", + "any": "Any", + "add_location": "Add Location", + "add_lodging": "Add Lodging", + "hours": "Hours", + "open": "Open", + "away": "away", + "your_location": "Your Location" + }, + "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", + "event timezone": "Event timezone", + "your timezone": "Your timezone" + }, + "locations": { + "location": "Location", + "locations": "Locations", + "my_locations": "My Locations", + "best_happened_at": "Best happened at" + }, + "settings_download_backup": "Download Backup", + "invites": { + "accepted": "Invite accepted", + "accept_failed": "Failed to accept invite", + "declined": "Invite declined", + "decline_failed": "Failed to decline invite", + "title": "Invites", + "pending_invites": "Pending Invites", + "no_invites": "No invites", + "decline": "Decline", + "accept": "Accept", + "invited_on": "Invited on", + "no_invites_desc": "Make sure your profile is public so users can invite you.", + "by": "by" + }, + "strava": { + "strava_integration_desc": "Connect to Strava to easily import your activties into locations and visits", + "connect_account": "Connect Account", + "disconnect": "Disconnect", + "authorization_error": "Error redirecting to strava authorization URL", + "disconnected": "Successfully disconnected from Strava", + "disconnect_error": "Error disconnecting from Strava", + "gpx_required": "Please upload the GPX file to complete the Strava import", + "not_enabled": "Strava integration is not enabled on this instance." + }, + "wanderer": { + "wanderer_integration_desc": "Connect to Wanderer to easily import and view your trails in locations", + "connection_error": "Error connecting to Wanderer", + "connected": "Successfully connected to Wanderer" + }, + "itinerary": { + "remove_from_itinerary": "Remove from Day", + "item_remove_success": "Item removed from itinerary", + "item_remove_error": "Error removing item from itinerary", + "auto_generate_itinerary": "Auto-Generate Itinerary", + "auto_generate_itinerary_desc": "This collection has dated items but no itinerary yet. Would you like to automatically organize them by date?", + "no_itinerary_yet": "No Itinerary Yet", + "start_planning": "Start planning your trip by adding items to specific days.", + "generating": "Generating", + "auto_generate": "Auto-Generate", + "link_existing_item": "Link existing item", + "no_plans_for_day": "No plans for this day", + "multi_day": "Multi-day", + "item_not_found": "Item not found", + "staying_overnight": "Staying overnight", + "unscheduled_items": "Unscheduled Items", + "unscheduled_items_desc": "These items are linked to this trip but haven't been added to a specific day yet.", + "change_day": "Change Day", + "trip_context": "Trip Context", + "move_to_trip_context": "Move to Trip Context", + "trip_context_info": "Trip context items apply to the whole trip — for example locations that are the destination itself, general notes, or packing lists that are important for the entire trip.", + "add_to_trip_context": "Add trip context", + "no_trip_context_items": "No trip context items yet.", + "add_description": "Add description", + "moved_to_trip_context": "Moved to trip context", + "failed_to_move_to_trip_context": "Failed to move to trip context", + "item_already_in_trip_context": "Items already in trip context", + "added_to_trip_context": "Added to trip context", + "failed_to_add_to_trip_context": "Failed to add item to trip context", + "remove_from_trip_context": "Remove from Context", + "drag_to_reorder": "Drag to reorder", + "add_to_day": "Add to day" + }, + "common": { + "show_less": "Hide details", + "show_more": "Show more" + }, + "collections": { + "not_found": "Collection Not Found", + "all_items": "All Items", + "no_calendar_events": "No visits are scheduled for this collection yet.", + "events": "events", + "times_shown_in": "Times shown in", + "event_timezone": "Event timezone", + "local_timezone": "My timezone", + "event_timezone_desc": "Event timezone uses the location or item timezone when available. My timezone uses", + "trip_costs": "Trip Costs", + "currency": "Currency", + "currencies": "Currencies", + "no_priced_items": "Add prices to locations, lodging, or transportation to see trip totals by currency.", + "statistics": "Statistics" + }, + "currencies": { + "USD": "US Dollar", + "EUR": "Euro", + "GBP": "British Pound", + "JPY": "Japanese Yen", + "AUD": "Australian Dollar", + "CAD": "Canadian Dollar", + "CHF": "Swiss Franc", + "CNY": "Chinese Yuan", + "HKD": "Hong Kong Dollar", + "SGD": "Singapore Dollar", + "SEK": "Swedish Krona", + "NOK": "Norwegian Krone", + "DKK": "Danish Krone", + "NZD": "New Zealand Dollar", + "INR": "Indian Rupee", + "MXN": "Mexican Peso", + "BRL": "Brazilian Real", + "ZAR": "South African Rand", + "AED": "UAE Dirham", + "TRY": "Turkish Lira", + "select_currency": "Select currency", + "search": "Search currency", + "no_matches": "No matches" + } } diff --git a/frontend/src/routes/locations/+page.svelte b/frontend/src/routes/locations/+page.svelte index ed5be53d..b1ed0ca2 100644 --- a/frontend/src/routes/locations/+page.svelte +++ b/frontend/src/routes/locations/+page.svelte @@ -1,10 +1,11 @@