feat: add Google Maps list import

Import places from shared Google Maps lists via URL.
Button in places sidebar next to GPX import opens a modal
where users can paste a shared list link. Server fetches
list data from Google Maps and creates places with name,
coordinates and notes. i18n keys added for all 12 languages.

Closes #205
This commit is contained in:
Maurice
2026-04-01 10:13:35 +02:00
parent 8c85ea3644
commit 040840917c
15 changed files with 285 additions and 25 deletions
+6 -1
View File
@@ -10,6 +10,7 @@ const ar: Record<string, string | { name: string; category: string }[]> = {
'common.edit': 'تعديل',
'common.add': 'إضافة',
'common.loading': 'جارٍ التحميل...',
'common.import': 'استيراد',
'common.error': 'خطأ',
'common.back': 'رجوع',
'common.all': 'الكل',
@@ -783,9 +784,13 @@ const ar: Record<string, string | { name: string; category: string }[]> = {
// Places Sidebar
'places.addPlace': 'إضافة مكان/نشاط',
'places.importGpx': 'استيراد GPX',
'places.importGpx': 'GPX',
'places.gpxImported': 'تم استيراد {count} مكان من GPX',
'places.gpxError': 'فشل استيراد GPX',
'places.importGoogleList': 'قائمة Google',
'places.googleListHint': 'الصق رابط قائمة Google Maps المشتركة لاستيراد جميع الأماكن.',
'places.googleListImported': 'تم استيراد {count} أماكن من "{list}"',
'places.googleListError': 'فشل استيراد قائمة Google Maps',
'places.urlResolved': 'تم استيراد المكان من الرابط',
'places.assignToDay': 'إلى أي يوم تريد الإضافة؟',
'places.all': 'الكل',
+6 -1
View File
@@ -6,6 +6,7 @@ const br: Record<string, string | { name: string; category: string }[]> = {
'common.edit': 'Editar',
'common.add': 'Adicionar',
'common.loading': 'Carregando...',
'common.import': 'Importar',
'common.error': 'Erro',
'common.back': 'Voltar',
'common.all': 'Todos',
@@ -763,9 +764,13 @@ const br: Record<string, string | { name: string; category: string }[]> = {
// Places Sidebar
'places.addPlace': 'Adicionar lugar/atividade',
'places.importGpx': 'Importar GPX',
'places.importGpx': 'GPX',
'places.gpxImported': '{count} lugares importados do GPX',
'places.gpxError': 'Falha ao importar GPX',
'places.importGoogleList': 'Lista Google',
'places.googleListHint': 'Cole um link compartilhado de uma lista do Google Maps para importar todos os lugares.',
'places.googleListImported': '{count} lugares importados de "{list}"',
'places.googleListError': 'Falha ao importar lista do Google Maps',
'places.urlResolved': 'Lugar importado da URL',
'places.assignToDay': 'Adicionar a qual dia?',
'places.all': 'Todos',
+6 -1
View File
@@ -6,6 +6,7 @@ const cs: Record<string, string | { name: string; category: string }[]> = {
'common.edit': 'Upravit',
'common.add': 'Přidat',
'common.loading': 'Načítání...',
'common.import': 'Importovat',
'common.error': 'Chyba',
'common.back': 'Zpět',
'common.all': 'Vše',
@@ -783,10 +784,14 @@ const cs: Record<string, string | { name: string; category: string }[]> = {
// Boční panel míst (Places Sidebar)
'places.addPlace': 'Přidat místo/aktivitu',
'places.importGpx': 'Importovat GPX',
'places.importGpx': 'GPX',
'places.gpxImported': '{count} míst importováno z GPX',
'places.urlResolved': 'Místo importováno z URL',
'places.gpxError': 'Import GPX se nezdařil',
'places.importGoogleList': 'Google Seznam',
'places.googleListHint': 'Vložte sdílený odkaz na seznam Google Maps pro import všech míst.',
'places.googleListImported': '{count} míst importováno ze seznamu "{list}"',
'places.googleListError': 'Import seznamu Google Maps se nezdařil',
'places.assignToDay': 'Přidat do kterého dne?',
'places.all': 'Vše',
'places.unplanned': 'Nezařazené',
+6 -1
View File
@@ -6,6 +6,7 @@ const de: Record<string, string | { name: string; category: string }[]> = {
'common.edit': 'Bearbeiten',
'common.add': 'Hinzufügen',
'common.loading': 'Laden...',
'common.import': 'Importieren',
'common.error': 'Fehler',
'common.back': 'Zurück',
'common.all': 'Alle',
@@ -781,10 +782,14 @@ const de: Record<string, string | { name: string; category: string }[]> = {
// Places Sidebar
'places.addPlace': 'Ort/Aktivität hinzufügen',
'places.importGpx': 'GPX importieren',
'places.importGpx': 'GPX',
'places.gpxImported': '{count} Orte aus GPX importiert',
'places.urlResolved': 'Ort aus URL importiert',
'places.gpxError': 'GPX-Import fehlgeschlagen',
'places.importGoogleList': 'Google Liste',
'places.googleListHint': 'Geteilten Google Maps Listen-Link einfügen, um alle Orte zu importieren.',
'places.googleListImported': '{count} Orte aus "{list}" importiert',
'places.googleListError': 'Google Maps Liste konnte nicht importiert werden',
'places.assignToDay': 'Zu welchem Tag hinzufügen?',
'places.all': 'Alle',
'places.unplanned': 'Ungeplant',
+6 -1
View File
@@ -6,6 +6,7 @@ const en: Record<string, string | { name: string; category: string }[]> = {
'common.edit': 'Edit',
'common.add': 'Add',
'common.loading': 'Loading...',
'common.import': 'Import',
'common.error': 'Error',
'common.back': 'Back',
'common.all': 'All',
@@ -777,10 +778,14 @@ const en: Record<string, string | { name: string; category: string }[]> = {
// Places Sidebar
'places.addPlace': 'Add Place/Activity',
'places.importGpx': 'Import GPX',
'places.importGpx': 'GPX',
'places.gpxImported': '{count} places imported from GPX',
'places.urlResolved': 'Place imported from URL',
'places.gpxError': 'GPX import failed',
'places.importGoogleList': 'Google List',
'places.googleListHint': 'Paste a shared Google Maps list link to import all places.',
'places.googleListImported': '{count} places imported from "{list}"',
'places.googleListError': 'Failed to import Google Maps list',
'places.assignToDay': 'Add to which day?',
'places.all': 'All',
'places.unplanned': 'Unplanned',
+6 -1
View File
@@ -6,6 +6,7 @@ const es: Record<string, string> = {
'common.edit': 'Editar',
'common.add': 'Añadir',
'common.loading': 'Cargando...',
'common.import': 'Importar',
'common.error': 'Error',
'common.back': 'Atrás',
'common.all': 'Todo',
@@ -757,9 +758,13 @@ const es: Record<string, string> = {
// Places Sidebar
'places.addPlace': 'Añadir lugar/actividad',
'places.importGpx': 'Importar GPX',
'places.importGpx': 'GPX',
'places.gpxImported': '{count} lugares importados desde GPX',
'places.gpxError': 'Error al importar GPX',
'places.importGoogleList': 'Lista Google',
'places.googleListHint': 'Pega un enlace compartido de una lista de Google Maps para importar todos los lugares.',
'places.googleListImported': '{count} lugares importados de "{list}"',
'places.googleListError': 'Error al importar la lista de Google Maps',
'places.urlResolved': 'Lugar importado desde URL',
'places.assignToDay': '¿A qué día añadirlo?',
'places.all': 'Todo',
+6 -1
View File
@@ -6,6 +6,7 @@ const fr: Record<string, string> = {
'common.edit': 'Modifier',
'common.add': 'Ajouter',
'common.loading': 'Chargement…',
'common.import': 'Importer',
'common.error': 'Erreur',
'common.back': 'Retour',
'common.all': 'Tout',
@@ -780,9 +781,13 @@ const fr: Record<string, string> = {
// Places Sidebar
'places.addPlace': 'Ajouter un lieu/activité',
'places.importGpx': 'Importer GPX',
'places.importGpx': 'GPX',
'places.gpxImported': '{count} lieux importés depuis GPX',
'places.gpxError': 'L\'import GPX a échoué',
'places.importGoogleList': 'Liste Google',
'places.googleListHint': 'Collez un lien de liste Google Maps partagée pour importer tous les lieux.',
'places.googleListImported': '{count} lieux importés depuis "{list}"',
'places.googleListError': 'Impossible d\'importer la liste Google Maps',
'places.urlResolved': 'Lieu importé depuis l\'URL',
'places.assignToDay': 'Ajouter à quel jour ?',
'places.all': 'Tous',
+6 -1
View File
@@ -6,6 +6,7 @@ const hu: Record<string, string | { name: string; category: string }[]> = {
'common.edit': 'Szerkesztés',
'common.add': 'Hozzáadás',
'common.loading': 'Betöltés...',
'common.import': 'Importálás',
'common.error': 'Hiba',
'common.back': 'Vissza',
'common.all': 'Összes',
@@ -779,10 +780,14 @@ const hu: Record<string, string | { name: string; category: string }[]> = {
// Helyek oldalsáv
'places.addPlace': 'Hely/Tevékenység hozzáadása',
'places.importGpx': 'GPX importálás',
'places.importGpx': 'GPX',
'places.gpxImported': '{count} hely importálva GPX-ből',
'places.urlResolved': 'Hely importálva URL-ből',
'places.gpxError': 'GPX importálás sikertelen',
'places.importGoogleList': 'Google Lista',
'places.googleListHint': 'Illessz be egy megosztott Google Maps lista linket az osszes hely importalasahoz.',
'places.googleListImported': '{count} hely importalva a(z) "{list}" listabol',
'places.googleListError': 'Google Maps lista importalasa sikertelen',
'places.assignToDay': 'Melyik naphoz adod?',
'places.all': 'Összes',
'places.unplanned': 'Nem tervezett',
+6 -1
View File
@@ -6,6 +6,7 @@ const it: Record<string, string | { name: string; category: string }[]> = {
'common.edit': 'Modifica',
'common.add': 'Aggiungi',
'common.loading': 'Caricamento...',
'common.import': 'Importa',
'common.error': 'Errore',
'common.back': 'Indietro',
'common.all': 'Tutti',
@@ -779,10 +780,14 @@ const it: Record<string, string | { name: string; category: string }[]> = {
// Places Sidebar
'places.addPlace': 'Aggiungi Luogo/Attività',
'places.importGpx': 'Importa GPX',
'places.importGpx': 'GPX',
'places.gpxImported': '{count} luoghi importati da GPX',
'places.urlResolved': 'Luogo importato dall\'URL',
'places.gpxError': 'Importazione GPX non riuscita',
'places.importGoogleList': 'Lista Google',
'places.googleListHint': 'Incolla un link condiviso di una lista Google Maps per importare tutti i luoghi.',
'places.googleListImported': '{count} luoghi importati da "{list}"',
'places.googleListError': 'Importazione lista Google Maps non riuscita',
'places.assignToDay': 'A quale giorno aggiungere?',
'places.all': 'Tutti',
'places.unplanned': 'Non pianificati',
+6 -1
View File
@@ -6,6 +6,7 @@ const nl: Record<string, string> = {
'common.edit': 'Bewerken',
'common.add': 'Toevoegen',
'common.loading': 'Laden...',
'common.import': 'Importeren',
'common.error': 'Fout',
'common.back': 'Terug',
'common.all': 'Alles',
@@ -780,9 +781,13 @@ const nl: Record<string, string> = {
// Places Sidebar
'places.addPlace': 'Plaats/activiteit toevoegen',
'places.importGpx': 'GPX importeren',
'places.importGpx': 'GPX',
'places.gpxImported': '{count} plaatsen geïmporteerd uit GPX',
'places.gpxError': 'GPX-import mislukt',
'places.importGoogleList': 'Google Lijst',
'places.googleListHint': 'Plak een gedeelde Google Maps lijstlink om alle plaatsen te importeren.',
'places.googleListImported': '{count} plaatsen geimporteerd uit "{list}"',
'places.googleListError': 'Google Maps lijst importeren mislukt',
'places.urlResolved': 'Plaats geïmporteerd van URL',
'places.assignToDay': 'Aan welke dag toevoegen?',
'places.all': 'Alle',
+6 -1
View File
@@ -6,6 +6,7 @@ const ru: Record<string, string> = {
'common.edit': 'Редактировать',
'common.add': 'Добавить',
'common.loading': 'Загрузка...',
'common.import': 'Импорт',
'common.error': 'Ошибка',
'common.back': 'Назад',
'common.all': 'Все',
@@ -780,9 +781,13 @@ const ru: Record<string, string> = {
// Places Sidebar
'places.addPlace': 'Добавить место/активность',
'places.importGpx': 'Импорт GPX',
'places.importGpx': 'GPX',
'places.gpxImported': '{count} мест импортировано из GPX',
'places.gpxError': 'Ошибка импорта GPX',
'places.importGoogleList': 'Список Google',
'places.googleListHint': 'Вставьте ссылку на общий список Google Maps для импорта всех мест.',
'places.googleListImported': '{count} мест импортировано из "{list}"',
'places.googleListError': 'Не удалось импортировать список Google Maps',
'places.urlResolved': 'Место импортировано из URL',
'places.assignToDay': 'Добавить в какой день?',
'places.all': 'Все',
+6 -1
View File
@@ -6,6 +6,7 @@ const zh: Record<string, string> = {
'common.edit': '编辑',
'common.add': '添加',
'common.loading': '加载中...',
'common.import': '导入',
'common.error': '错误',
'common.back': '返回',
'common.all': '全部',
@@ -780,9 +781,13 @@ const zh: Record<string, string> = {
// Places Sidebar
'places.addPlace': '添加地点/活动',
'places.importGpx': '导入 GPX',
'places.importGpx': 'GPX',
'places.gpxImported': '已从 GPX 导入 {count} 个地点',
'places.gpxError': 'GPX 导入失败',
'places.importGoogleList': 'Google 列表',
'places.googleListHint': '粘贴共享的 Google Maps 列表链接以导入所有地点。',
'places.googleListImported': '已从"{list}"导入 {count} 个地点',
'places.googleListError': 'Google Maps 列表导入失败',
'places.urlResolved': '已从 URL 导入地点',
'places.assignToDay': '添加到哪一天?',
'places.all': '全部',