Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Ignore everything in the .venv folder
.venv/
.vscode/settings.json
.pnpm-store/
13 changes: 12 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,16 @@
"backend/server/backend/lib/python3.12/site-packages/django/contrib/sites/locale",
"backend/server/backend/lib/python3.12/site-packages/rest_framework/templates/rest_framework/docs/langs"
],
"i18n-ally.keystyle": "nested"
"i18n-ally.keystyle": "nested",
"sqltools.connections": [
{
"previewLimit": 50,
"server": "localhost",
"port": 5432,
"driver": "PostgreSQL",
"name": "default",
"database": "adventurelog",
"username": "admin"
}
]
}
64 changes: 64 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Makefile for AdventureLog project
DOCKER_COMPOSE = docker compose -f docker-compose.yml
DOCKER_COMPOSE_TRAEFIK = docker compose -f docker-compose-traefik.yaml

.PHONY: help
help: ## Display this help
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n\nTargets:\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 }' $(MAKEFILE_LIST)

all: dev ## Build all services (alias for dev)

download-countries: ## Download countries
@cd backend/server && mkdir -p media
@cd backend/server && python manage.py download-countries --force
@echo "Countries downloaded"

download-cities: ## Download cities
@cd backend/server && mkdir -p media/cities_data
@cd backend/server && python manage.py cities --import=country
# @cd backend/server && python manage.py cities --import=region
# @cd backend/server && python manage.py cities --import=city
@cd backend/server && python manage.py cities --import=alt_name
@echo "Cities downloaded"

import-translations: ## Import translations
@cd backend/server && python manage.py get-translations
@echo "Translations Imported"

dev-db: dev ## Start development database
@if [ ! "$$(docker ps -q -f name=adventurelog-development)" ]; then \
if [ "$$(docker ps -aq -f status=exited -f name=adventurelog-development)" ]; then \
docker rm adventurelog-development; \
fi; \
docker run --name adventurelog-development \
-e POSTGRES_USER=admin \
-e POSTGRES_PASSWORD=admin \
-e POSTGRES_DB=adventurelog \
-p 5432:5432 \
-d postgis/postgis:15-3.3; \
echo "Development database started. Please wait a few seconds for it to initialize."; \
echo "Please update the backend/.env file with these credentials:"; \
echo "PGHOST=localhost"; \
echo "PGDATABASE=adventurelog"; \
echo "PGUSER=admin"; \
echo "PGPASSWORD=admin"; \
else \
echo "Development database is already running."; \
fi

web: dev ## Start web service
@cd frontend && pnpm dev

django: dev ## Start Django server
@cd backend/server && python manage.py migrate
@cd frontend && pnpm django

dev: ## Setup Development Environment
@[ -f backend/server/.env ] || cp backend/server/.env.example backend/server/.env
@[ -f frontend/.env ] || cp frontend/.env.example frontend/.env
@cd frontend && pnpm install
@[ -d .venv ] || python -m venv .venv
. .venv/bin/activate
@pip install -r backend/server/requirements.txt


2 changes: 1 addition & 1 deletion backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ RUN pip install --upgrade pip \
&& pip install -r requirements.txt

# Create necessary directories
RUN mkdir -p /code/static /code/media
RUN mkdir -p /code/static /code/media /code/media/cities_data
# RUN mkdir -p /code/staticfiles /code/media

# Copy the Django project code into the Docker image
Expand Down
13 changes: 11 additions & 2 deletions backend/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,24 @@ else:
EOF
fi


# Sync the countries and world travel regions
# Sync the countries and world travel regions
python manage.py download-countries
if [ $? -eq 137 ]; then
>&2 echo "WARNING: The download-countries command was interrupted. This is likely due to lack of memory allocated to the container or the host. Please try again with more memory."
exit 1
fi

# Sync the translations if $COUNTRY_TRANSLATIONS is true
if [ -n "$COUNTRY_TRANSLATIONS" -a "$COUNTRY_TRANSLATIONS" = "true" ]; then
echo "Syncing translations for countries..."
# Get the translations for all countries
python manage.py cities --import=country
# Get the translations for all alt names
python manage.py cities --import=alt_name
# Get the translations for all countries
python manage.py get-translations
fi

cat /code/adventurelog.txt

# Start gunicorn
Expand Down
2 changes: 2 additions & 0 deletions backend/server/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ FRONTEND_URL='http://localhost:3000'

EMAIL_BACKEND='console'

COUNTRY_TRANSLATIONS=True

# EMAIL_BACKEND='email'
# EMAIL_HOST='smtp.gmail.com'
# EMAIL_USE_TLS=False
Expand Down
12 changes: 11 additions & 1 deletion backend/server/main/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
'users',
'integrations',
'django.contrib.gis',
'cities',
# 'achievements', # Not done yet, will be added later in a future update
# 'widget_tweaks',
# 'slippers',
Expand Down Expand Up @@ -167,6 +168,7 @@
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media' # This path must match the NGINX root
STATICFILES_DIRS = [BASE_DIR / 'static']
CITIES_DATA_DIR = MEDIA_ROOT / 'cities_data'

STORAGES = {
"staticfiles": {
Expand Down Expand Up @@ -304,4 +306,12 @@
# ADVENTURELOG_CDN_URL = getenv('ADVENTURELOG_CDN_URL', 'https://cdn.adventurelog.app')

# https://github.com/dr5hn/countries-states-cities-database/tags
COUNTRY_REGION_JSON_VERSION = 'v2.5'
COUNTRY_REGION_JSON_VERSION = 'v2.5'

# English, Spanish, French, German, Italian, Chinese, Dutch, Swedish
CITIES_LOCALES = ['en', 'es', 'fr', 'de', 'it', 'zh', 'nl', 'sv', 'LANGUAGES']
CITIES_POSTAL_CODES = []
CITIES_PLUGINS = [
# Reduce memory usage when importing large datasets (e.g. "allCountries.zip")
'cities.plugin.reset_queries.Plugin',
]
3 changes: 2 additions & 1 deletion backend/server/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ icalendar==6.1.0
ijson==3.3.0
tqdm==4.67.1
overpy==0.7
publicsuffix2==2.20191221
publicsuffix2==2.20191221
django-cities==0.6.2
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ def handle(self, **options):
country_capital = country['capital']
longitude = round(float(country['longitude']), 6) if country['longitude'] else None
latitude = round(float(country['latitude']), 6) if country['latitude'] else None
translations = country['translations']

processed_country_codes.add(country_code)

Expand All @@ -102,6 +103,7 @@ def handle(self, **options):
country_obj.capital = country_capital
country_obj.longitude = longitude
country_obj.latitude = latitude
country_obj.translations = translations
countries_to_update.append(country_obj)
else:
country_obj = Country(
Expand All @@ -110,7 +112,8 @@ def handle(self, **options):
subregion=country_subregion,
capital=country_capital,
longitude=longitude,
latitude=latitude
latitude=latitude,
translations=translations
)
countries_to_create.append(country_obj)

Expand Down Expand Up @@ -213,7 +216,7 @@ def handle(self, **options):
batch = countries_to_update[i:i + batch_size]
for i in tqdm(range(0, len(countries_to_update), batch_size), desc="Updating countries"):
batch = countries_to_update[i:i + batch_size]
Country.objects.bulk_update(batch, ['name', 'subregion', 'capital', 'longitude', 'latitude'])
Country.objects.bulk_update(batch, ['name', 'subregion', 'capital', 'longitude', 'latitude', 'translations'])

for i in tqdm(range(0, len(regions_to_update), batch_size), desc="Updating regions"):
batch = regions_to_update[i:i + batch_size]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from main.settings import CITIES_LOCALES
from worldtravel.models import Country
from django.core.management.base import BaseCommand

class Command(BaseCommand):
help = 'Get translations for all countries'

def handle(self, *args, **options):
countries = Country.objects.all()
countries_to_update = []
for country in countries:
updated = country.get_translations(CITIES_LOCALES)
if updated:
countries_to_update.append(country)
# Bulk update the translations
Country.objects.bulk_update(countries_to_update, ['translations'])
print(f"Updated translations for {len(countries_to_update)} countries")
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.0.8 on 2025-01-13 17:50

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('worldtravel', '0015_city_insert_id_country_insert_id_region_insert_id'),
]

operations = [
migrations.AddField(
model_name='country',
name='translations',
field=models.JSONField(default=dict, blank=True),
),
]
22 changes: 20 additions & 2 deletions backend/server/worldtravel/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from django.contrib.gis.db import models as gis_models

from cities.models import Country as CityCountry, City as CityCity, Region as CityRegion

User = get_user_model()

Expand All @@ -18,13 +18,31 @@ class Country(models.Model):
longitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
latitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
insert_id = models.UUIDField(unique=False, blank=True, null=True)

translations = models.JSONField(default=dict, blank=True)

class Meta:
verbose_name = "Country"
verbose_name_plural = "Countries"

def __str__(self):
return self.name

def get_translations(self, languages: list[str])->bool:
# get the translations for the country
translations = self.translations
try:
# get the preferred alt names for the country
alt_names = CityCountry.objects.get(code=self.country_code).alt_names.filter(language_code__in=languages, is_preferred=True)
for alt_name in alt_names:
translations[alt_name.language_code] = alt_name.name

if self.translations != translations:
self.translations = translations
return True
return False
except CityCountry.DoesNotExist:
print(f"Country {self.name} ({self.country_code}) not found in cities.models.Country")
return False

class Region(models.Model):
id = models.CharField(primary_key=True)
Expand Down
2 changes: 1 addition & 1 deletion backend/server/worldtravel/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def get_num_visits(self, obj):
class Meta:
model = Country
fields = '__all__'
read_only_fields = ['id', 'name', 'country_code', 'subregion', 'flag_url', 'num_regions', 'num_visits', 'longitude', 'latitude', 'capital']
read_only_fields = ['id', 'name', 'country_code', 'subregion', 'flag_url', 'num_regions', 'num_visits', 'longitude', 'latitude', 'capital', 'translations']


class RegionSerializer(serializers.ModelSerializer):
Expand Down
10 changes: 10 additions & 0 deletions documentation/docs/configuration/updating.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,13 @@ Once you are in the container run the following command to resync the region dat
```bash
python manage.py download-countries --force
```

## Updating the Country Translations

If you would like to get translations for country names, you can run the following command. This will get the translations for all countries and save them to the database.

```bash
python manage.py cities --import=country
python manage.py cities --import=alt_name # This takes a while to run, around 20 - 30 minutes, but only needs to be done once
python manage.py get-translations
```
34 changes: 31 additions & 3 deletions frontend/src/lib/components/CountryCard.svelte
Original file line number Diff line number Diff line change
@@ -1,13 +1,41 @@
<script lang="ts">
import { goto } from '$app/navigation';
import type { Country } from '$lib/types';
import { createEventDispatcher } from 'svelte';
import { t } from 'svelte-i18n';
import { t, locale } from 'svelte-i18n';

import MapMarkerStar from '~icons/mdi/map-marker-star';

export let country: Country;

// Country name in the current locale
let country_name_locale = country.name;

// Subscribe to locale changes
locale.subscribe((lang) => {
country_name_locale = get_country_name(lang);
});

/**
* Get the country name in the current locale
* @param lang - The current locale
* @returns The country name in the current locale
*/
function get_country_name(lang: string | null | undefined) {
if (!lang) {
return country.name;
}
const translations = country.translations;
if (translations[lang]) {
return translations[lang];
}
for (const [key, value] of Object.entries(translations)) {
if (key.toLowerCase().includes(lang.toLowerCase())) {
return value;
}
}
return country.name;
}

async function nav() {
goto(`/worldtravel/${country.country_code}`);
}
Expand All @@ -21,7 +49,7 @@
<img src={country.flag_url} alt="No image available" class="w-full h-48 object-cover" />
</figure>
<div class="card-body">
<h2 class="card-title overflow-ellipsis">{country.name}</h2>
<h2 class="card-title overflow-ellipsis">{country_name_locale}</h2>
<div class="flex flex-wrap gap-2">
{#if country.subregion}
<div class="badge badge-primary">{country.subregion}</div>
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export type Country = {
num_visits: number;
longitude: number | null;
latitude: number | null;
translations: Record<string, string>;
};

export type Region = {
Expand Down