refactor(i18n): rename importKmlKmz to importKeyholeMarkup across all locales

This commit is contained in:
Yannis Biasutti
2026-04-06 22:26:22 +02:00
parent 2f4e067a65
commit 81851d8367
16 changed files with 273 additions and 85 deletions
@@ -121,7 +121,7 @@ const PlacesSidebar = React.memo(function PlacesSidebar({
if (result.places?.length > 0) { if (result.places?.length > 0) {
const importedIds: number[] = result.places.map((p: { id: number }) => p.id) const importedIds: number[] = result.places.map((p: { id: number }) => p.id)
pushUndo?.(t('undo.importKmlKmz'), async () => { pushUndo?.(t('undo.importKeyholeMarkup'), async () => {
for (const id of importedIds) { for (const id of importedIds) {
try { await placesApi.delete(tripId, id) } catch {} try { await placesApi.delete(tripId, id) } catch {}
} }
@@ -239,7 +239,7 @@ const PlacesSidebar = React.memo(function PlacesSidebar({
cursor: 'pointer', fontFamily: 'inherit', cursor: 'pointer', fontFamily: 'inherit',
}} }}
> >
<Upload size={11} strokeWidth={2} /> {t('places.importKmlKmz')} <Upload size={11} strokeWidth={2} /> {t('places.importKeyholeMarkup')}
</button> </button>
<button <button
onClick={() => setGoogleListOpen(true)} onClick={() => setGoogleListOpen(true)}
@@ -597,7 +597,7 @@ const PlacesSidebar = React.memo(function PlacesSidebar({
style={{ background: 'var(--bg-card)', borderRadius: 16, width: '100%', maxWidth: 520, padding: 24, boxShadow: '0 8px 32px rgba(0,0,0,0.2)' }} style={{ background: 'var(--bg-card)', borderRadius: 16, width: '100%', maxWidth: 520, padding: 24, boxShadow: '0 8px 32px rgba(0,0,0,0.2)' }}
> >
<div style={{ fontSize: 15, fontWeight: 700, color: 'var(--text-primary)', marginBottom: 6 }}> <div style={{ fontSize: 15, fontWeight: 700, color: 'var(--text-primary)', marginBottom: 6 }}>
{t('places.importKmlKmz')} {t('places.importKeyholeMarkup')}
</div> </div>
<div style={{ fontSize: 12, color: 'var(--text-faint)', marginBottom: 14, lineHeight: 1.45 }}> <div style={{ fontSize: 12, color: 'var(--text-faint)', marginBottom: 14, lineHeight: 1.45 }}>
{t('places.kmlKmzHint')} {t('places.kmlKmzHint')}
+7 -6
View File
@@ -1,4 +1,4 @@
import en from './en' import en from './en'
const ar: Record<string, string | { name: string; category: string }[]> = { const ar: Record<string, string | { name: string; category: string }[]> = {
...en, ...en,
@@ -812,25 +812,25 @@ const ar: Record<string, string | { name: string; category: string }[]> = {
// Places Sidebar // Places Sidebar
'places.addPlace': 'إضافة مكان/نشاط', 'places.addPlace': 'إضافة مكان/نشاط',
'places.importGpx': 'GPX', 'places.importGpx': 'GPX',
'places.importKmlKmz': 'KMZ / KML', 'places.importKeyholeMarkup': 'KMZ / KML',
'places.kmlKmzSelectFile': 'اختيار ملف', 'places.gpxImported': 'تم استيراد {count} مكان من GPX',
'places.kmlKmzImported': 'تم استيراد {count} مكان من KMZ/KML', 'places.kmlKmzImported': 'تم استيراد {count} مكان من KMZ/KML',
'places.urlResolved': 'تم استيراد المكان من الرابط',
'places.gpxError': 'فشل استيراد GPX',
'places.kmlKmzImportError': 'فشل استيراد KMZ/KML', 'places.kmlKmzImportError': 'فشل استيراد KMZ/KML',
'places.kmlKmzInvalidType': 'يرجى اختيار ملف .kml أو .kmz.', 'places.kmlKmzInvalidType': 'يرجى اختيار ملف .kml أو .kmz.',
'places.kmlKmzTooLarge': 'الملف كبير جدًا. الحد الأقصى لحجم الرفع هو {maxMb} MB.', 'places.kmlKmzTooLarge': 'الملف كبير جدًا. الحد الأقصى لحجم الرفع هو {maxMb} MB.',
'places.kmlKmzHint': 'استورد ملفات الخرائط من أدوات مثل Google My Maps وGoogle Earth.', 'places.kmlKmzHint': 'استورد ملفات الخرائط من أدوات مثل Google My Maps وGoogle Earth.',
'places.kmlKmzSizeHint': 'الحد الأقصى لحجم الملف: {maxMb} MB', 'places.kmlKmzSizeHint': 'الحد الأقصى لحجم الملف: {maxMb} MB',
'places.kmlKmzSelectFile': 'اختيار ملف',
'places.kmlKmzSelectedFile': 'الملف المحدد: {name}', 'places.kmlKmzSelectedFile': 'الملف المحدد: {name}',
'places.kmlKmzSummaryTitle': 'ملخص الاستيراد', 'places.kmlKmzSummaryTitle': 'ملخص الاستيراد',
'places.kmlKmzSummaryValues': 'علامات المواضع: {total} • تم الاستيراد: {created} • تم التجاوز: {skipped}', 'places.kmlKmzSummaryValues': 'علامات المواضع: {total} • تم الاستيراد: {created} • تم التجاوز: {skipped}',
'places.gpxImported': 'تم استيراد {count} مكان من GPX',
'places.gpxError': 'فشل استيراد GPX',
'places.importGoogleList': 'قائمة Google', 'places.importGoogleList': 'قائمة Google',
'places.googleListHint': 'الصق رابط قائمة Google Maps المشتركة لاستيراد جميع الأماكن.', 'places.googleListHint': 'الصق رابط قائمة Google Maps المشتركة لاستيراد جميع الأماكن.',
'places.googleListImported': 'تم استيراد {count} أماكن من "{list}"', 'places.googleListImported': 'تم استيراد {count} أماكن من "{list}"',
'places.googleListError': 'فشل استيراد قائمة Google Maps', 'places.googleListError': 'فشل استيراد قائمة Google Maps',
'places.viewDetails': 'عرض التفاصيل', 'places.viewDetails': 'عرض التفاصيل',
'places.urlResolved': 'تم استيراد المكان من الرابط',
'places.assignToDay': 'إلى أي يوم تريد الإضافة؟', 'places.assignToDay': 'إلى أي يوم تريد الإضافة؟',
'places.all': 'الكل', 'places.all': 'الكل',
'places.unplanned': 'غير مخطط', 'places.unplanned': 'غير مخطط',
@@ -1563,6 +1563,7 @@ const ar: Record<string, string | { name: string; category: string }[]> = {
'undo.moveDay': 'تم نقل المكان إلى يوم آخر', 'undo.moveDay': 'تم نقل المكان إلى يوم آخر',
'undo.lock': 'تم تبديل قفل المكان', 'undo.lock': 'تم تبديل قفل المكان',
'undo.importGpx': 'استيراد GPX', 'undo.importGpx': 'استيراد GPX',
'undo.importKeyholeMarkup': 'استيراد KMZ/KML',
'undo.importGoogleList': 'استيراد خرائط Google', 'undo.importGoogleList': 'استيراد خرائط Google',
// Notifications // Notifications
+7 -6
View File
@@ -1,4 +1,4 @@
const br: Record<string, string | { name: string; category: string }[]> = { const br: Record<string, string | { name: string; category: string }[]> = {
// Common // Common
'common.save': 'Salvar', 'common.save': 'Salvar',
'common.cancel': 'Cancelar', 'common.cancel': 'Cancelar',
@@ -794,25 +794,25 @@ const br: Record<string, string | { name: string; category: string }[]> = {
// Places Sidebar // Places Sidebar
'places.addPlace': 'Adicionar lugar/atividade', 'places.addPlace': 'Adicionar lugar/atividade',
'places.importGpx': 'GPX', 'places.importGpx': 'GPX',
'places.importKmlKmz': 'KMZ / KML', 'places.importKeyholeMarkup': 'KMZ / KML',
'places.kmlKmzSelectFile': 'Selecionar arquivo', 'places.gpxImported': '{count} lugares importados do GPX',
'places.kmlKmzImported': '{count} lugares importados de KMZ/KML', 'places.kmlKmzImported': '{count} lugares importados de KMZ/KML',
'places.urlResolved': 'Lugar importado da URL',
'places.gpxError': 'Falha ao importar GPX',
'places.kmlKmzImportError': 'Falha na importação de KMZ/KML', 'places.kmlKmzImportError': 'Falha na importação de KMZ/KML',
'places.kmlKmzInvalidType': 'Selecione um arquivo .kml ou .kmz.', 'places.kmlKmzInvalidType': 'Selecione um arquivo .kml ou .kmz.',
'places.kmlKmzTooLarge': 'O arquivo é muito grande. O tamanho máximo de upload é {maxMb} MB.', 'places.kmlKmzTooLarge': 'O arquivo é muito grande. O tamanho máximo de upload é {maxMb} MB.',
'places.kmlKmzHint': 'Importe arquivos de mapa de ferramentas como Google My Maps e Google Earth.', 'places.kmlKmzHint': 'Importe arquivos de mapa de ferramentas como Google My Maps e Google Earth.',
'places.kmlKmzSizeHint': 'Tamanho máximo do arquivo: {maxMb} MB', 'places.kmlKmzSizeHint': 'Tamanho máximo do arquivo: {maxMb} MB',
'places.kmlKmzSelectFile': 'Selecionar arquivo',
'places.kmlKmzSelectedFile': 'Arquivo selecionado: {name}', 'places.kmlKmzSelectedFile': 'Arquivo selecionado: {name}',
'places.kmlKmzSummaryTitle': 'Resumo da importação', 'places.kmlKmzSummaryTitle': 'Resumo da importação',
'places.kmlKmzSummaryValues': 'Placemarks: {total} • Importados: {created} • Ignorados: {skipped}', 'places.kmlKmzSummaryValues': 'Placemarks: {total} • Importados: {created} • Ignorados: {skipped}',
'places.gpxImported': '{count} lugares importados do GPX',
'places.gpxError': 'Falha ao importar GPX',
'places.importGoogleList': 'Lista Google', 'places.importGoogleList': 'Lista Google',
'places.googleListHint': 'Cole um link compartilhado de uma lista do Google Maps para importar todos os lugares.', '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.googleListImported': '{count} lugares importados de "{list}"',
'places.googleListError': 'Falha ao importar lista do Google Maps', 'places.googleListError': 'Falha ao importar lista do Google Maps',
'places.viewDetails': 'Ver detalhes', 'places.viewDetails': 'Ver detalhes',
'places.urlResolved': 'Lugar importado da URL',
'places.assignToDay': 'Adicionar a qual dia?', 'places.assignToDay': 'Adicionar a qual dia?',
'places.all': 'Todos', 'places.all': 'Todos',
'places.unplanned': 'Não planejados', 'places.unplanned': 'Não planejados',
@@ -1558,6 +1558,7 @@ const br: Record<string, string | { name: string; category: string }[]> = {
'undo.moveDay': 'Local movido para outro dia', 'undo.moveDay': 'Local movido para outro dia',
'undo.lock': 'Bloqueio do local alternado', 'undo.lock': 'Bloqueio do local alternado',
'undo.importGpx': 'Importação de GPX', 'undo.importGpx': 'Importação de GPX',
'undo.importKeyholeMarkup': 'Importação de KMZ/KML',
'undo.importGoogleList': 'Importação do Google Maps', 'undo.importGoogleList': 'Importação do Google Maps',
// Notifications // Notifications
+7 -6
View File
@@ -1,4 +1,4 @@
const cs: Record<string, string | { name: string; category: string }[]> = { const cs: Record<string, string | { name: string; category: string }[]> = {
// Společné (Common) // Společné (Common)
'common.save': 'Uložit', 'common.save': 'Uložit',
'common.cancel': 'Zrušit', 'common.cancel': 'Zrušit',
@@ -810,20 +810,20 @@ const cs: Record<string, string | { name: string; category: string }[]> = {
// Boční panel míst (Places Sidebar) // Boční panel míst (Places Sidebar)
'places.addPlace': 'Přidat místo/aktivitu', 'places.addPlace': 'Přidat místo/aktivitu',
'places.importGpx': 'GPX', 'places.importGpx': 'GPX',
'places.importKmlKmz': 'KMZ / KML', 'places.importKeyholeMarkup': 'KMZ / KML',
'places.kmlKmzSelectFile': 'Vybrat soubor', 'places.gpxImported': '{count} míst importováno z GPX',
'places.kmlKmzImported': 'Importováno {count} míst z KMZ/KML', 'places.kmlKmzImported': 'Importováno {count} míst z KMZ/KML',
'places.urlResolved': 'Místo importováno z URL',
'places.gpxError': 'Import GPX se nezdařil',
'places.kmlKmzImportError': 'Import KMZ/KML selhal', 'places.kmlKmzImportError': 'Import KMZ/KML selhal',
'places.kmlKmzInvalidType': 'Vyberte soubor .kml nebo .kmz.', 'places.kmlKmzInvalidType': 'Vyberte soubor .kml nebo .kmz.',
'places.kmlKmzTooLarge': 'Soubor je příliš velký. Maximální velikost nahrání je {maxMb} MB.', 'places.kmlKmzTooLarge': 'Soubor je příliš velký. Maximální velikost nahrání je {maxMb} MB.',
'places.kmlKmzHint': 'Importujte mapové soubory z nástrojů jako Google My Maps a Google Earth.', 'places.kmlKmzHint': 'Importujte mapové soubory z nástrojů jako Google My Maps a Google Earth.',
'places.kmlKmzSizeHint': 'Maximální velikost souboru: {maxMb} MB', 'places.kmlKmzSizeHint': 'Maximální velikost souboru: {maxMb} MB',
'places.kmlKmzSelectFile': 'Vybrat soubor',
'places.kmlKmzSelectedFile': 'Vybraný soubor: {name}', 'places.kmlKmzSelectedFile': 'Vybraný soubor: {name}',
'places.kmlKmzSummaryTitle': 'Souhrn importu', 'places.kmlKmzSummaryTitle': 'Souhrn importu',
'places.kmlKmzSummaryValues': 'Placemarks: {total} • Importováno: {created} • Přeskočeno: {skipped}', 'places.kmlKmzSummaryValues': 'Placemarks: {total} • Importováno: {created} • Přeskočeno: {skipped}',
'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.importGoogleList': 'Google Seznam',
'places.googleListHint': 'Vložte sdílený odkaz na seznam Google Maps pro import všech míst.', '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.googleListImported': '{count} míst importováno ze seznamu "{list}"',
@@ -1561,6 +1561,7 @@ const cs: Record<string, string | { name: string; category: string }[]> = {
'undo.moveDay': 'Místo přesunuto na jiný den', 'undo.moveDay': 'Místo přesunuto na jiný den',
'undo.lock': 'Zámek místa přepnut', 'undo.lock': 'Zámek místa přepnut',
'undo.importGpx': 'Import GPX', 'undo.importGpx': 'Import GPX',
'undo.importKeyholeMarkup': 'Import KMZ/KML',
'undo.importGoogleList': 'Import z Google Maps', 'undo.importGoogleList': 'Import z Google Maps',
// Notifications // Notifications
+7 -6
View File
@@ -1,4 +1,4 @@
const de: Record<string, string | { name: string; category: string }[]> = { const de: Record<string, string | { name: string; category: string }[]> = {
// Allgemein // Allgemein
'common.save': 'Speichern', 'common.save': 'Speichern',
'common.cancel': 'Abbrechen', 'common.cancel': 'Abbrechen',
@@ -810,20 +810,20 @@ const de: Record<string, string | { name: string; category: string }[]> = {
// Places Sidebar // Places Sidebar
'places.addPlace': 'Ort/Aktivität hinzufügen', 'places.addPlace': 'Ort/Aktivität hinzufügen',
'places.importGpx': 'GPX', 'places.importGpx': 'GPX',
'places.importKmlKmz': 'KMZ / KML', 'places.importKeyholeMarkup': 'KMZ / KML',
'places.kmlKmzSelectFile': 'Datei auswählen', 'places.gpxImported': '{count} Orte aus GPX importiert',
'places.kmlKmzImported': '{count} Orte aus KMZ/KML importiert', 'places.kmlKmzImported': '{count} Orte aus KMZ/KML importiert',
'places.urlResolved': 'Ort aus URL importiert',
'places.gpxError': 'GPX-Import fehlgeschlagen',
'places.kmlKmzImportError': 'KMZ/KML-Import fehlgeschlagen', 'places.kmlKmzImportError': 'KMZ/KML-Import fehlgeschlagen',
'places.kmlKmzInvalidType': 'Bitte eine .kml- oder .kmz-Datei auswählen.', 'places.kmlKmzInvalidType': 'Bitte eine .kml- oder .kmz-Datei auswählen.',
'places.kmlKmzTooLarge': 'Datei ist zu groß. Maximale Upload-Größe ist {maxMb} MB.', 'places.kmlKmzTooLarge': 'Datei ist zu groß. Maximale Upload-Größe ist {maxMb} MB.',
'places.kmlKmzHint': 'Importiere Kartendateien aus Tools wie Google My Maps und Google Earth.', 'places.kmlKmzHint': 'Importiere Kartendateien aus Tools wie Google My Maps und Google Earth.',
'places.kmlKmzSizeHint': 'Max. Dateigröße: {maxMb} MB', 'places.kmlKmzSizeHint': 'Max. Dateigröße: {maxMb} MB',
'places.kmlKmzSelectFile': 'Datei auswählen',
'places.kmlKmzSelectedFile': 'Ausgewählte Datei: {name}', 'places.kmlKmzSelectedFile': 'Ausgewählte Datei: {name}',
'places.kmlKmzSummaryTitle': 'Importzusammenfassung', 'places.kmlKmzSummaryTitle': 'Importzusammenfassung',
'places.kmlKmzSummaryValues': 'Placemarks: {total} • Importiert: {created} • Übersprungen: {skipped}', 'places.kmlKmzSummaryValues': 'Placemarks: {total} • Importiert: {created} • Übersprungen: {skipped}',
'places.gpxImported': '{count} Orte aus GPX importiert',
'places.urlResolved': 'Ort aus URL importiert',
'places.gpxError': 'GPX-Import fehlgeschlagen',
'places.importGoogleList': 'Google Liste', 'places.importGoogleList': 'Google Liste',
'places.googleListHint': 'Geteilten Google Maps Listen-Link einfügen, um alle Orte zu importieren.', 'places.googleListHint': 'Geteilten Google Maps Listen-Link einfügen, um alle Orte zu importieren.',
'places.googleListImported': '{count} Orte aus "{list}" importiert', 'places.googleListImported': '{count} Orte aus "{list}" importiert',
@@ -1565,6 +1565,7 @@ const de: Record<string, string | { name: string; category: string }[]> = {
'undo.moveDay': 'Ort zu anderem Tag verschoben', 'undo.moveDay': 'Ort zu anderem Tag verschoben',
'undo.lock': 'Ortssperre umgeschaltet', 'undo.lock': 'Ortssperre umgeschaltet',
'undo.importGpx': 'GPX-Import', 'undo.importGpx': 'GPX-Import',
'undo.importKeyholeMarkup': 'KMZ/KML-Import',
'undo.importGoogleList': 'Google Maps-Import', 'undo.importGoogleList': 'Google Maps-Import',
// Notifications // Notifications
+3 -3
View File
@@ -1,4 +1,4 @@
const en: Record<string, string | { name: string; category: string }[]> = { const en: Record<string, string | { name: string; category: string }[]> = {
// Common // Common
'common.save': 'Save', 'common.save': 'Save',
'common.cancel': 'Cancel', 'common.cancel': 'Cancel',
@@ -829,7 +829,7 @@ const en: Record<string, string | { name: string; category: string }[]> = {
// Places Sidebar // Places Sidebar
'places.addPlace': 'Add Place/Activity', 'places.addPlace': 'Add Place/Activity',
'places.importGpx': 'GPX', 'places.importGpx': 'GPX',
'places.importKmlKmz': 'KMZ / KML', 'places.importKeyholeMarkup': 'KMZ / KML',
'places.gpxImported': '{count} places imported from GPX', 'places.gpxImported': '{count} places imported from GPX',
'places.kmlKmzImported': '{count} places imported from KMZ/KML', 'places.kmlKmzImported': '{count} places imported from KMZ/KML',
'places.urlResolved': 'Place imported from URL', 'places.urlResolved': 'Place imported from URL',
@@ -1602,7 +1602,7 @@ const en: Record<string, string | { name: string; category: string }[]> = {
'undo.moveDay': 'Place moved to another day', 'undo.moveDay': 'Place moved to another day',
'undo.lock': 'Place lock toggled', 'undo.lock': 'Place lock toggled',
'undo.importGpx': 'GPX import', 'undo.importGpx': 'GPX import',
'undo.importKmlKmz': 'KMZ/KML import', 'undo.importKeyholeMarkup': 'KMZ/KML import',
'undo.importGoogleList': 'Google Maps import', 'undo.importGoogleList': 'Google Maps import',
'undo.addPlace': 'Place added', 'undo.addPlace': 'Place added',
'undo.done': 'Undone: {action}', 'undo.done': 'Undone: {action}',
+7 -6
View File
@@ -1,4 +1,4 @@
const es: Record<string, string> = { const es: Record<string, string> = {
// Common // Common
'common.save': 'Guardar', 'common.save': 'Guardar',
'common.cancel': 'Cancelar', 'common.cancel': 'Cancelar',
@@ -786,25 +786,25 @@ const es: Record<string, string> = {
// Places Sidebar // Places Sidebar
'places.addPlace': 'Añadir lugar/actividad', 'places.addPlace': 'Añadir lugar/actividad',
'places.importGpx': 'GPX', 'places.importGpx': 'GPX',
'places.importKmlKmz': 'KMZ / KML', 'places.importKeyholeMarkup': 'KMZ / KML',
'places.kmlKmzSelectFile': 'Seleccionar archivo', 'places.gpxImported': '{count} lugares importados desde GPX',
'places.kmlKmzImported': '{count} lugares importados desde KMZ/KML', 'places.kmlKmzImported': '{count} lugares importados desde KMZ/KML',
'places.urlResolved': 'Lugar importado desde URL',
'places.gpxError': 'Error al importar GPX',
'places.kmlKmzImportError': 'La importación KMZ/KML falló', 'places.kmlKmzImportError': 'La importación KMZ/KML falló',
'places.kmlKmzInvalidType': 'Selecciona un archivo .kml o .kmz.', 'places.kmlKmzInvalidType': 'Selecciona un archivo .kml o .kmz.',
'places.kmlKmzTooLarge': 'El archivo es demasiado grande. El tamaño máximo de carga es {maxMb} MB.', 'places.kmlKmzTooLarge': 'El archivo es demasiado grande. El tamaño máximo de carga es {maxMb} MB.',
'places.kmlKmzHint': 'Importa archivos de mapa desde herramientas como Google My Maps y Google Earth.', 'places.kmlKmzHint': 'Importa archivos de mapa desde herramientas como Google My Maps y Google Earth.',
'places.kmlKmzSizeHint': 'Tamaño máximo de archivo: {maxMb} MB', 'places.kmlKmzSizeHint': 'Tamaño máximo de archivo: {maxMb} MB',
'places.kmlKmzSelectFile': 'Seleccionar archivo',
'places.kmlKmzSelectedFile': 'Archivo seleccionado: {name}', 'places.kmlKmzSelectedFile': 'Archivo seleccionado: {name}',
'places.kmlKmzSummaryTitle': 'Resumen de importación', 'places.kmlKmzSummaryTitle': 'Resumen de importación',
'places.kmlKmzSummaryValues': 'Placemarks: {total} • Importados: {created} • Omitidos: {skipped}', 'places.kmlKmzSummaryValues': 'Placemarks: {total} • Importados: {created} • Omitidos: {skipped}',
'places.gpxImported': '{count} lugares importados desde GPX',
'places.gpxError': 'Error al importar GPX',
'places.importGoogleList': 'Lista Google', 'places.importGoogleList': 'Lista Google',
'places.googleListHint': 'Pega un enlace compartido de una lista de Google Maps para importar todos los lugares.', '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.googleListImported': '{count} lugares importados de "{list}"',
'places.googleListError': 'Error al importar la lista de Google Maps', 'places.googleListError': 'Error al importar la lista de Google Maps',
'places.viewDetails': 'Ver detalles', 'places.viewDetails': 'Ver detalles',
'places.urlResolved': 'Lugar importado desde URL',
'places.assignToDay': '¿A qué día añadirlo?', 'places.assignToDay': '¿A qué día añadirlo?',
'places.all': 'Todo', 'places.all': 'Todo',
'places.unplanned': 'Sin planificar', 'places.unplanned': 'Sin planificar',
@@ -1565,6 +1565,7 @@ const es: Record<string, string> = {
'undo.moveDay': 'Lugar movido a otro día', 'undo.moveDay': 'Lugar movido a otro día',
'undo.lock': 'Bloqueo de lugar activado/desactivado', 'undo.lock': 'Bloqueo de lugar activado/desactivado',
'undo.importGpx': 'Importación GPX', 'undo.importGpx': 'Importación GPX',
'undo.importKeyholeMarkup': 'Importación KMZ/KML',
'undo.importGoogleList': 'Importación de Google Maps', 'undo.importGoogleList': 'Importación de Google Maps',
// Notifications // Notifications
+7 -6
View File
@@ -1,4 +1,4 @@
const fr: Record<string, string> = { const fr: Record<string, string> = {
// Common // Common
'common.save': 'Enregistrer', 'common.save': 'Enregistrer',
'common.cancel': 'Annuler', 'common.cancel': 'Annuler',
@@ -809,25 +809,25 @@ const fr: Record<string, string> = {
// Places Sidebar // Places Sidebar
'places.addPlace': 'Ajouter un lieu/activité', 'places.addPlace': 'Ajouter un lieu/activité',
'places.importGpx': 'GPX', 'places.importGpx': 'GPX',
'places.importKmlKmz': 'KMZ / KML', 'places.importKeyholeMarkup': 'KMZ / KML',
'places.kmlKmzSelectFile': 'Sélectionner un fichier', 'places.gpxImported': '{count} lieux importés depuis GPX',
'places.kmlKmzImported': '{count} lieux importés depuis KMZ/KML', 'places.kmlKmzImported': '{count} lieux importés depuis KMZ/KML',
'places.urlResolved': 'Lieu importé depuis l\'URL',
'places.gpxError': 'L\'import GPX a échoué',
'places.kmlKmzImportError': 'L\'import KMZ/KML a échoué', 'places.kmlKmzImportError': 'L\'import KMZ/KML a échoué',
'places.kmlKmzInvalidType': 'Veuillez sélectionner un fichier .kml ou .kmz.', 'places.kmlKmzInvalidType': 'Veuillez sélectionner un fichier .kml ou .kmz.',
'places.kmlKmzTooLarge': 'Le fichier est trop volumineux. La taille maximale est de {maxMb} MB.', 'places.kmlKmzTooLarge': 'Le fichier est trop volumineux. La taille maximale est de {maxMb} MB.',
'places.kmlKmzHint': 'Importez des fichiers de carte depuis des outils comme Google My Maps et Google Earth.', 'places.kmlKmzHint': 'Importez des fichiers de carte depuis des outils comme Google My Maps et Google Earth.',
'places.kmlKmzSizeHint': 'Taille maximale du fichier : {maxMb} MB', 'places.kmlKmzSizeHint': 'Taille maximale du fichier : {maxMb} MB',
'places.kmlKmzSelectFile': 'Sélectionner un fichier',
'places.kmlKmzSelectedFile': 'Fichier sélectionné : {name}', 'places.kmlKmzSelectedFile': 'Fichier sélectionné : {name}',
'places.kmlKmzSummaryTitle': 'Résumé d\'import', 'places.kmlKmzSummaryTitle': 'Résumé d\'import',
'places.kmlKmzSummaryValues': 'Placemarks : {total} • Importés : {created} • Ignorés : {skipped}', 'places.kmlKmzSummaryValues': 'Placemarks : {total} • Importés : {created} • Ignorés : {skipped}',
'places.gpxImported': '{count} lieux importés depuis GPX',
'places.gpxError': 'L\'import GPX a échoué',
'places.importGoogleList': 'Liste Google', 'places.importGoogleList': 'Liste Google',
'places.googleListHint': 'Collez un lien de liste Google Maps partagée pour importer tous les lieux.', '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.googleListImported': '{count} lieux importés depuis "{list}"',
'places.googleListError': 'Impossible d\'importer la liste Google Maps', 'places.googleListError': 'Impossible d\'importer la liste Google Maps',
'places.viewDetails': 'Voir les détails', 'places.viewDetails': 'Voir les détails',
'places.urlResolved': 'Lieu importé depuis l\'URL',
'places.assignToDay': 'Ajouter à quel jour ?', 'places.assignToDay': 'Ajouter à quel jour ?',
'places.all': 'Tous', 'places.all': 'Tous',
'places.unplanned': 'Non planifiés', 'places.unplanned': 'Non planifiés',
@@ -1559,6 +1559,7 @@ const fr: Record<string, string> = {
'undo.moveDay': 'Lieu déplacé vers un autre jour', 'undo.moveDay': 'Lieu déplacé vers un autre jour',
'undo.lock': 'Verrouillage du lieu modifié', 'undo.lock': 'Verrouillage du lieu modifié',
'undo.importGpx': 'Import GPX', 'undo.importGpx': 'Import GPX',
'undo.importKeyholeMarkup': 'Import KMZ/KML',
'undo.importGoogleList': 'Import Google Maps', 'undo.importGoogleList': 'Import Google Maps',
// Notifications // Notifications
+7 -6
View File
@@ -1,4 +1,4 @@
const hu: Record<string, string | { name: string; category: string }[]> = { const hu: Record<string, string | { name: string; category: string }[]> = {
// Általános // Általános
'common.save': 'Mentés', 'common.save': 'Mentés',
'common.cancel': 'Mégse', 'common.cancel': 'Mégse',
@@ -810,20 +810,20 @@ const hu: Record<string, string | { name: string; category: string }[]> = {
// Helyek oldalsáv // Helyek oldalsáv
'places.addPlace': 'Hely/Tevékenység hozzáadása', 'places.addPlace': 'Hely/Tevékenység hozzáadása',
'places.importGpx': 'GPX', 'places.importGpx': 'GPX',
'places.importKmlKmz': 'KMZ / KML', 'places.importKeyholeMarkup': 'KMZ / KML',
'places.kmlKmzSelectFile': 'Fájl kiválasztása', 'places.gpxImported': '{count} hely importálva GPX-ből',
'places.kmlKmzImported': '{count} hely importálva KMZ/KML-ből', 'places.kmlKmzImported': '{count} hely importálva KMZ/KML-ből',
'places.urlResolved': 'Hely importálva URL-ből',
'places.gpxError': 'GPX importálás sikertelen',
'places.kmlKmzImportError': 'A KMZ/KML importálás sikertelen', 'places.kmlKmzImportError': 'A KMZ/KML importálás sikertelen',
'places.kmlKmzInvalidType': 'Válassz egy .kml vagy .kmz fájlt.', 'places.kmlKmzInvalidType': 'Válassz egy .kml vagy .kmz fájlt.',
'places.kmlKmzTooLarge': 'A fájl túl nagy. A maximális feltöltési méret {maxMb} MB.', 'places.kmlKmzTooLarge': 'A fájl túl nagy. A maximális feltöltési méret {maxMb} MB.',
'places.kmlKmzHint': 'Térképfájlok importálása olyan eszközökből, mint a Google My Maps és a Google Earth.', 'places.kmlKmzHint': 'Térképfájlok importálása olyan eszközökből, mint a Google My Maps és a Google Earth.',
'places.kmlKmzSizeHint': 'Maximális fájlméret: {maxMb} MB', 'places.kmlKmzSizeHint': 'Maximális fájlméret: {maxMb} MB',
'places.kmlKmzSelectFile': 'Fájl kiválasztása',
'places.kmlKmzSelectedFile': 'Kiválasztott fájl: {name}', 'places.kmlKmzSelectedFile': 'Kiválasztott fájl: {name}',
'places.kmlKmzSummaryTitle': 'Import összegzés', 'places.kmlKmzSummaryTitle': 'Import összegzés',
'places.kmlKmzSummaryValues': 'Placemarks: {total} • Importálva: {created} • Kihagyva: {skipped}', 'places.kmlKmzSummaryValues': 'Placemarks: {total} • Importálva: {created} • Kihagyva: {skipped}',
'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.importGoogleList': 'Google Lista',
'places.googleListHint': 'Illessz be egy megosztott Google Maps lista linket az osszes hely importalasahoz.', 'places.googleListHint': 'Illessz be egy megosztott Google Maps lista linket az osszes hely importalasahoz.',
'places.googleListImported': '{count} hely importalva a(z) "{list}" listabol', 'places.googleListImported': '{count} hely importalva a(z) "{list}" listabol',
@@ -1560,6 +1560,7 @@ const hu: Record<string, string | { name: string; category: string }[]> = {
'undo.moveDay': 'Hely áthelyezve másik napra', 'undo.moveDay': 'Hely áthelyezve másik napra',
'undo.lock': 'Hely zárolása váltva', 'undo.lock': 'Hely zárolása váltva',
'undo.importGpx': 'GPX importálás', 'undo.importGpx': 'GPX importálás',
'undo.importKeyholeMarkup': 'KMZ/KML importálás',
'undo.importGoogleList': 'Google Maps importálás', 'undo.importGoogleList': 'Google Maps importálás',
// Notifications // Notifications
+7 -6
View File
@@ -1,4 +1,4 @@
const it: Record<string, string | { name: string; category: string }[]> = { const it: Record<string, string | { name: string; category: string }[]> = {
// Common // Common
'common.save': 'Salva', 'common.save': 'Salva',
'common.cancel': 'Annulla', 'common.cancel': 'Annulla',
@@ -810,20 +810,20 @@ const it: Record<string, string | { name: string; category: string }[]> = {
// Places Sidebar // Places Sidebar
'places.addPlace': 'Aggiungi Luogo/Attività', 'places.addPlace': 'Aggiungi Luogo/Attività',
'places.importGpx': 'GPX', 'places.importGpx': 'GPX',
'places.importKmlKmz': 'KMZ / KML', 'places.importKeyholeMarkup': 'KMZ / KML',
'places.kmlKmzSelectFile': 'Seleziona file', 'places.gpxImported': '{count} luoghi importati da GPX',
'places.kmlKmzImported': '{count} luoghi importati da KMZ/KML', 'places.kmlKmzImported': '{count} luoghi importati da KMZ/KML',
'places.urlResolved': 'Luogo importato dall\'URL',
'places.gpxError': 'Importazione GPX non riuscita',
'places.kmlKmzImportError': 'Importazione KMZ/KML non riuscita', 'places.kmlKmzImportError': 'Importazione KMZ/KML non riuscita',
'places.kmlKmzInvalidType': 'Seleziona un file .kml o .kmz.', 'places.kmlKmzInvalidType': 'Seleziona un file .kml o .kmz.',
'places.kmlKmzTooLarge': 'Il file è troppo grande. La dimensione massima di caricamento è {maxMb} MB.', 'places.kmlKmzTooLarge': 'Il file è troppo grande. La dimensione massima di caricamento è {maxMb} MB.',
'places.kmlKmzHint': 'Importa file mappa da strumenti come Google My Maps e Google Earth.', 'places.kmlKmzHint': 'Importa file mappa da strumenti come Google My Maps e Google Earth.',
'places.kmlKmzSizeHint': 'Dimensione massima file: {maxMb} MB', 'places.kmlKmzSizeHint': 'Dimensione massima file: {maxMb} MB',
'places.kmlKmzSelectFile': 'Seleziona file',
'places.kmlKmzSelectedFile': 'File selezionato: {name}', 'places.kmlKmzSelectedFile': 'File selezionato: {name}',
'places.kmlKmzSummaryTitle': 'Riepilogo importazione', 'places.kmlKmzSummaryTitle': 'Riepilogo importazione',
'places.kmlKmzSummaryValues': 'Placemarks: {total} • Importati: {created} • Saltati: {skipped}', 'places.kmlKmzSummaryValues': 'Placemarks: {total} • Importati: {created} • Saltati: {skipped}',
'places.gpxImported': '{count} luoghi importati da GPX',
'places.urlResolved': 'Luogo importato dall\'URL',
'places.gpxError': 'Importazione GPX non riuscita',
'places.importGoogleList': 'Lista Google', 'places.importGoogleList': 'Lista Google',
'places.googleListHint': 'Incolla un link condiviso di una lista Google Maps per importare tutti i luoghi.', 'places.googleListHint': 'Incolla un link condiviso di una lista Google Maps per importare tutti i luoghi.',
'places.googleListImported': '{count} luoghi importati da "{list}"', 'places.googleListImported': '{count} luoghi importati da "{list}"',
@@ -1561,6 +1561,7 @@ const it: Record<string, string | { name: string; category: string }[]> = {
'undo.moveDay': 'Luogo spostato in altro giorno', 'undo.moveDay': 'Luogo spostato in altro giorno',
'undo.lock': 'Blocco luogo modificato', 'undo.lock': 'Blocco luogo modificato',
'undo.importGpx': 'Importazione GPX', 'undo.importGpx': 'Importazione GPX',
'undo.importKeyholeMarkup': 'Importazione KMZ/KML',
'undo.importGoogleList': 'Importazione Google Maps', 'undo.importGoogleList': 'Importazione Google Maps',
'undo.addPlace': 'Luogo aggiunto', 'undo.addPlace': 'Luogo aggiunto',
'undo.done': 'Annullato: {action}', 'undo.done': 'Annullato: {action}',
+7 -6
View File
@@ -1,4 +1,4 @@
const nl: Record<string, string> = { const nl: Record<string, string> = {
// Common // Common
'common.save': 'Opslaan', 'common.save': 'Opslaan',
'common.cancel': 'Annuleren', 'common.cancel': 'Annuleren',
@@ -809,25 +809,25 @@ const nl: Record<string, string> = {
// Places Sidebar // Places Sidebar
'places.addPlace': 'Plaats/activiteit toevoegen', 'places.addPlace': 'Plaats/activiteit toevoegen',
'places.importGpx': 'GPX', 'places.importGpx': 'GPX',
'places.importKmlKmz': 'KMZ / KML', 'places.importKeyholeMarkup': 'KMZ / KML',
'places.kmlKmzSelectFile': 'Bestand selecteren', 'places.gpxImported': '{count} plaatsen geïmporteerd uit GPX',
'places.kmlKmzImported': '{count} plaatsen geïmporteerd uit KMZ/KML', 'places.kmlKmzImported': '{count} plaatsen geïmporteerd uit KMZ/KML',
'places.urlResolved': 'Plaats geïmporteerd van URL',
'places.gpxError': 'GPX-import mislukt',
'places.kmlKmzImportError': 'KMZ/KML-import mislukt', 'places.kmlKmzImportError': 'KMZ/KML-import mislukt',
'places.kmlKmzInvalidType': 'Selecteer een .kml- of .kmz-bestand.', 'places.kmlKmzInvalidType': 'Selecteer een .kml- of .kmz-bestand.',
'places.kmlKmzTooLarge': 'Bestand is te groot. Maximale uploadgrootte is {maxMb} MB.', 'places.kmlKmzTooLarge': 'Bestand is te groot. Maximale uploadgrootte is {maxMb} MB.',
'places.kmlKmzHint': 'Importeer kaartbestanden uit tools zoals Google My Maps en Google Earth.', 'places.kmlKmzHint': 'Importeer kaartbestanden uit tools zoals Google My Maps en Google Earth.',
'places.kmlKmzSizeHint': 'Max. bestandsgrootte: {maxMb} MB', 'places.kmlKmzSizeHint': 'Max. bestandsgrootte: {maxMb} MB',
'places.kmlKmzSelectFile': 'Bestand selecteren',
'places.kmlKmzSelectedFile': 'Geselecteerd bestand: {name}', 'places.kmlKmzSelectedFile': 'Geselecteerd bestand: {name}',
'places.kmlKmzSummaryTitle': 'Importoverzicht', 'places.kmlKmzSummaryTitle': 'Importoverzicht',
'places.kmlKmzSummaryValues': 'Placemarks: {total} • Geïmporteerd: {created} • Overgeslagen: {skipped}', 'places.kmlKmzSummaryValues': 'Placemarks: {total} • Geïmporteerd: {created} • Overgeslagen: {skipped}',
'places.gpxImported': '{count} plaatsen geïmporteerd uit GPX',
'places.gpxError': 'GPX-import mislukt',
'places.importGoogleList': 'Google Lijst', 'places.importGoogleList': 'Google Lijst',
'places.googleListHint': 'Plak een gedeelde Google Maps lijstlink om alle plaatsen te importeren.', 'places.googleListHint': 'Plak een gedeelde Google Maps lijstlink om alle plaatsen te importeren.',
'places.googleListImported': '{count} plaatsen geimporteerd uit "{list}"', 'places.googleListImported': '{count} plaatsen geimporteerd uit "{list}"',
'places.googleListError': 'Google Maps lijst importeren mislukt', 'places.googleListError': 'Google Maps lijst importeren mislukt',
'places.viewDetails': 'Details bekijken', 'places.viewDetails': 'Details bekijken',
'places.urlResolved': 'Plaats geïmporteerd van URL',
'places.assignToDay': 'Aan welke dag toevoegen?', 'places.assignToDay': 'Aan welke dag toevoegen?',
'places.all': 'Alle', 'places.all': 'Alle',
'places.unplanned': 'Ongepland', 'places.unplanned': 'Ongepland',
@@ -1559,6 +1559,7 @@ const nl: Record<string, string> = {
'undo.moveDay': 'Locatie naar andere dag verplaatst', 'undo.moveDay': 'Locatie naar andere dag verplaatst',
'undo.lock': 'Vergrendeling locatie gewijzigd', 'undo.lock': 'Vergrendeling locatie gewijzigd',
'undo.importGpx': 'GPX-import', 'undo.importGpx': 'GPX-import',
'undo.importKeyholeMarkup': 'KMZ/KML-import',
'undo.importGoogleList': 'Google Maps-import', 'undo.importGoogleList': 'Google Maps-import',
// Notifications // Notifications
+8 -7
View File
@@ -1,4 +1,4 @@
const pl: Record<string, string | { name: string; category: string }[]> = { const pl: Record<string, string | { name: string; category: string }[]> = {
// Common // Common
'common.save': 'Zapisz', 'common.save': 'Zapisz',
'common.cancel': 'Anuluj', 'common.cancel': 'Anuluj',
@@ -773,20 +773,21 @@ const pl: Record<string, string | { name: string; category: string }[]> = {
// Places Sidebar // Places Sidebar
'places.addPlace': 'Dodaj miejsce/atrakcję', 'places.addPlace': 'Dodaj miejsce/atrakcję',
'places.importGpx': 'Importuj GPX', 'places.importGpx': 'Importuj GPX',
'places.importKmlKmz': 'KMZ / KML', 'places.importKeyholeMarkup': 'KMZ / KML',
'places.kmlKmzSelectFile': 'Wybierz plik', 'places.gpxImported': '{count} miejsc zaimportowanych z GPX',
'places.kmlKmzImported': 'Zaimportowano {count} miejsc z KMZ/KML', 'places.kmlKmzImported': 'Zaimportowano {count} miejsc z KMZ/KML',
'places.urlResolved': 'Miejsce zaimportowane z URL',
'places.gpxError': 'Nie udało się zaimportować pliku GPX',
'places.kmlKmzImportError': 'Import KMZ/KML nie powiódł się', 'places.kmlKmzImportError': 'Import KMZ/KML nie powiódł się',
'places.kmlKmzInvalidType': 'Wybierz plik .kml lub .kmz.', 'places.kmlKmzInvalidType': 'Wybierz plik .kml lub .kmz.',
'places.kmlKmzTooLarge': 'Plik jest za duży. Maksymalny rozmiar przesyłania to {maxMb} MB.', 'places.kmlKmzTooLarge': 'Plik jest za duży. Maksymalny rozmiar przesyłania to {maxMb} MB.',
'places.kmlKmzHint': 'Importuj pliki map z narzędzi takich jak Google My Maps i Google Earth.', 'places.kmlKmzHint': 'Importuj pliki map z narzędzi takich jak Google My Maps i Google Earth.',
'places.kmlKmzSizeHint': 'Maksymalny rozmiar pliku: {maxMb} MB', 'places.kmlKmzSizeHint': 'Maksymalny rozmiar pliku: {maxMb} MB',
'places.kmlKmzSelectFile': 'Wybierz plik',
'places.kmlKmzSelectedFile': 'Wybrany plik: {name}', 'places.kmlKmzSelectedFile': 'Wybrany plik: {name}',
'places.kmlKmzSummaryTitle': 'Podsumowanie importu', 'places.kmlKmzSummaryTitle': 'Podsumowanie importu',
'places.kmlKmzSummaryValues': 'Placemarks: {total} • Zaimportowano: {created} • Pominięto: {skipped}', 'places.kmlKmzSummaryValues': 'Placemarks: {total} • Zaimportowano: {created} • Pominięto: {skipped}',
'places.gpxImported': '{count} miejsc zaimportowanych z GPX', 'places.importGoogleList': 'Lista Google',
'places.urlResolved': 'Miejsce zaimportowane z URL',
'places.gpxError': 'Nie udało się zaimportować pliku GPX',
'places.assignToDay': 'Do którego dnia dodać?', 'places.assignToDay': 'Do którego dnia dodać?',
'places.all': 'Wszystkie', 'places.all': 'Wszystkie',
'places.unplanned': 'Niezaplanowane', 'places.unplanned': 'Niezaplanowane',
@@ -1506,7 +1507,6 @@ const pl: Record<string, string | { name: string; category: string }[]> = {
'login.setNewPasswordHint': 'Musisz zmienić hasło.', 'login.setNewPasswordHint': 'Musisz zmienić hasło.',
'atlas.searchCountry': 'Szukaj kraju...', 'atlas.searchCountry': 'Szukaj kraju...',
'trip.loadingPhotos': 'Ładowanie zdjęć...', 'trip.loadingPhotos': 'Ładowanie zdjęć...',
'places.importGoogleList': 'Lista Google',
'places.googleListHint': 'Wklej link do listy Google Maps.', 'places.googleListHint': 'Wklej link do listy Google Maps.',
'places.googleListImported': 'Zaimportowano {count} miejsc', 'places.googleListImported': 'Zaimportowano {count} miejsc',
'places.googleListError': 'Nie udało się zaimportować listy', 'places.googleListError': 'Nie udało się zaimportować listy',
@@ -1587,6 +1587,7 @@ const pl: Record<string, string | { name: string; category: string }[]> = {
'undo.moveDay': 'Miejsce przeniesione', 'undo.moveDay': 'Miejsce przeniesione',
'undo.lock': 'Blokada przełączona', 'undo.lock': 'Blokada przełączona',
'undo.importGpx': 'Import GPX', 'undo.importGpx': 'Import GPX',
'undo.importKeyholeMarkup': 'Import KMZ/KML',
'undo.importGoogleList': 'Import Google Maps', 'undo.importGoogleList': 'Import Google Maps',
'undo.addPlace': 'Miejsce dodane', 'undo.addPlace': 'Miejsce dodane',
'undo.done': 'Cofnięto: {action}', 'undo.done': 'Cofnięto: {action}',
+7 -6
View File
@@ -1,4 +1,4 @@
const ru: Record<string, string> = { const ru: Record<string, string> = {
// Common // Common
'common.save': 'Сохранить', 'common.save': 'Сохранить',
'common.cancel': 'Отмена', 'common.cancel': 'Отмена',
@@ -809,25 +809,25 @@ const ru: Record<string, string> = {
// Places Sidebar // Places Sidebar
'places.addPlace': 'Добавить место/активность', 'places.addPlace': 'Добавить место/активность',
'places.importGpx': 'GPX', 'places.importGpx': 'GPX',
'places.importKmlKmz': 'KMZ / KML', 'places.importKeyholeMarkup': 'KMZ / KML',
'places.kmlKmzSelectFile': 'Выбрать файл', 'places.gpxImported': '{count} мест импортировано из GPX',
'places.kmlKmzImported': '{count} мест импортировано из KMZ/KML', 'places.kmlKmzImported': '{count} мест импортировано из KMZ/KML',
'places.urlResolved': 'Место импортировано из URL',
'places.gpxError': 'Ошибка импорта GPX',
'places.kmlKmzImportError': 'Ошибка импорта KMZ/KML', 'places.kmlKmzImportError': 'Ошибка импорта KMZ/KML',
'places.kmlKmzInvalidType': 'Выберите файл .kml или .kmz.', 'places.kmlKmzInvalidType': 'Выберите файл .kml или .kmz.',
'places.kmlKmzTooLarge': 'Файл слишком большой. Максимальный размер загрузки — {maxMb} MB.', 'places.kmlKmzTooLarge': 'Файл слишком большой. Максимальный размер загрузки — {maxMb} MB.',
'places.kmlKmzHint': 'Импортируйте файлы карт из инструментов, таких как Google My Maps и Google Earth.', 'places.kmlKmzHint': 'Импортируйте файлы карт из инструментов, таких как Google My Maps и Google Earth.',
'places.kmlKmzSizeHint': 'Максимальный размер файла: {maxMb} MB', 'places.kmlKmzSizeHint': 'Максимальный размер файла: {maxMb} MB',
'places.kmlKmzSelectFile': 'Выбрать файл',
'places.kmlKmzSelectedFile': 'Выбранный файл: {name}', 'places.kmlKmzSelectedFile': 'Выбранный файл: {name}',
'places.kmlKmzSummaryTitle': 'Сводка импорта', 'places.kmlKmzSummaryTitle': 'Сводка импорта',
'places.kmlKmzSummaryValues': 'Placemarks: {total} • Импортировано: {created} • Пропущено: {skipped}', 'places.kmlKmzSummaryValues': 'Placemarks: {total} • Импортировано: {created} • Пропущено: {skipped}',
'places.gpxImported': '{count} мест импортировано из GPX',
'places.gpxError': 'Ошибка импорта GPX',
'places.importGoogleList': 'Список Google', 'places.importGoogleList': 'Список Google',
'places.googleListHint': 'Вставьте ссылку на общий список Google Maps для импорта всех мест.', 'places.googleListHint': 'Вставьте ссылку на общий список Google Maps для импорта всех мест.',
'places.googleListImported': '{count} мест импортировано из "{list}"', 'places.googleListImported': '{count} мест импортировано из "{list}"',
'places.googleListError': 'Не удалось импортировать список Google Maps', 'places.googleListError': 'Не удалось импортировать список Google Maps',
'places.viewDetails': 'Подробности', 'places.viewDetails': 'Подробности',
'places.urlResolved': 'Место импортировано из URL',
'places.assignToDay': 'Добавить в какой день?', 'places.assignToDay': 'Добавить в какой день?',
'places.all': 'Все', 'places.all': 'Все',
'places.unplanned': 'Незапланированные', 'places.unplanned': 'Незапланированные',
@@ -1559,6 +1559,7 @@ const ru: Record<string, string> = {
'undo.moveDay': 'Место перемещено в другой день', 'undo.moveDay': 'Место перемещено в другой день',
'undo.lock': 'Блокировка места изменена', 'undo.lock': 'Блокировка места изменена',
'undo.importGpx': 'Импорт GPX', 'undo.importGpx': 'Импорт GPX',
'undo.importKeyholeMarkup': 'Импорт KMZ/KML',
'undo.importGoogleList': 'Импорт из Google Maps', 'undo.importGoogleList': 'Импорт из Google Maps',
// Notifications // Notifications
+7 -6
View File
@@ -1,4 +1,4 @@
const zh: Record<string, string> = { const zh: Record<string, string> = {
// Common // Common
'common.save': '保存', 'common.save': '保存',
'common.cancel': '取消', 'common.cancel': '取消',
@@ -809,25 +809,25 @@ const zh: Record<string, string> = {
// Places Sidebar // Places Sidebar
'places.addPlace': '添加地点/活动', 'places.addPlace': '添加地点/活动',
'places.importGpx': 'GPX', 'places.importGpx': 'GPX',
'places.importKmlKmz': 'KMZ / KML', 'places.importKeyholeMarkup': 'KMZ / KML',
'places.kmlKmzSelectFile': '选择文件', 'places.gpxImported': '已从 GPX 导入 {count} 个地点',
'places.kmlKmzImported': '已从 KMZ/KML 导入 {count} 个地点', 'places.kmlKmzImported': '已从 KMZ/KML 导入 {count} 个地点',
'places.urlResolved': '已从 URL 导入地点',
'places.gpxError': 'GPX 导入失败',
'places.kmlKmzImportError': 'KMZ/KML 导入失败', 'places.kmlKmzImportError': 'KMZ/KML 导入失败',
'places.kmlKmzInvalidType': '请选择 .kml 或 .kmz 文件。', 'places.kmlKmzInvalidType': '请选择 .kml 或 .kmz 文件。',
'places.kmlKmzTooLarge': '文件过大。最大上传大小为 {maxMb} MB。', 'places.kmlKmzTooLarge': '文件过大。最大上传大小为 {maxMb} MB。',
'places.kmlKmzHint': '可从 Google My Maps、Google Earth 等工具导入地图文件。', 'places.kmlKmzHint': '可从 Google My Maps、Google Earth 等工具导入地图文件。',
'places.kmlKmzSizeHint': '最大文件大小:{maxMb} MB', 'places.kmlKmzSizeHint': '最大文件大小:{maxMb} MB',
'places.kmlKmzSelectFile': '选择文件',
'places.kmlKmzSelectedFile': '已选择文件:{name}', 'places.kmlKmzSelectedFile': '已选择文件:{name}',
'places.kmlKmzSummaryTitle': '导入摘要', 'places.kmlKmzSummaryTitle': '导入摘要',
'places.kmlKmzSummaryValues': 'Placemarks{total} • 已导入:{created} • 已跳过:{skipped}', 'places.kmlKmzSummaryValues': 'Placemarks{total} • 已导入:{created} • 已跳过:{skipped}',
'places.gpxImported': '已从 GPX 导入 {count} 个地点',
'places.gpxError': 'GPX 导入失败',
'places.importGoogleList': 'Google 列表', 'places.importGoogleList': 'Google 列表',
'places.googleListHint': '粘贴共享的 Google Maps 列表链接以导入所有地点。', 'places.googleListHint': '粘贴共享的 Google Maps 列表链接以导入所有地点。',
'places.googleListImported': '已从"{list}"导入 {count} 个地点', 'places.googleListImported': '已从"{list}"导入 {count} 个地点',
'places.googleListError': 'Google Maps 列表导入失败', 'places.googleListError': 'Google Maps 列表导入失败',
'places.viewDetails': '查看详情', 'places.viewDetails': '查看详情',
'places.urlResolved': '已从 URL 导入地点',
'places.assignToDay': '添加到哪一天?', 'places.assignToDay': '添加到哪一天?',
'places.all': '全部', 'places.all': '全部',
'places.unplanned': '未规划', 'places.unplanned': '未规划',
@@ -1559,6 +1559,7 @@ const zh: Record<string, string> = {
'undo.moveDay': '地点已移至另一天', 'undo.moveDay': '地点已移至另一天',
'undo.lock': '地点锁定已切换', 'undo.lock': '地点锁定已切换',
'undo.importGpx': 'GPX 导入', 'undo.importGpx': 'GPX 导入',
'undo.importKeyholeMarkup': 'KMZ/KML 导入',
'undo.importGoogleList': 'Google 地图导入', 'undo.importGoogleList': 'Google 地图导入',
// Notifications // Notifications
+7 -6
View File
@@ -1,4 +1,4 @@
const zhTw: Record<string, string> = { const zhTw: Record<string, string> = {
// Common // Common
'common.save': '儲存', 'common.save': '儲存',
'common.cancel': '取消', 'common.cancel': '取消',
@@ -789,25 +789,25 @@ const zhTw: Record<string, string> = {
// Places Sidebar // Places Sidebar
'places.addPlace': '新增地點/活動', 'places.addPlace': '新增地點/活動',
'places.importGpx': 'GPX', 'places.importGpx': 'GPX',
'places.importKmlKmz': 'KMZ / KML', 'places.importKeyholeMarkup': 'KMZ / KML',
'places.kmlKmzSelectFile': '選擇檔案', 'places.gpxImported': '已從 GPX 匯入 {count} 個地點',
'places.kmlKmzImported': '已從 KMZ/KML 匯入 {count} 個地點', 'places.kmlKmzImported': '已從 KMZ/KML 匯入 {count} 個地點',
'places.urlResolved': '已從 URL 匯入地點',
'places.gpxError': 'GPX 匯入失敗',
'places.kmlKmzImportError': 'KMZ/KML 匯入失敗', 'places.kmlKmzImportError': 'KMZ/KML 匯入失敗',
'places.kmlKmzInvalidType': '請選擇 .kml 或 .kmz 檔案。', 'places.kmlKmzInvalidType': '請選擇 .kml 或 .kmz 檔案。',
'places.kmlKmzTooLarge': '檔案過大。最大上傳大小為 {maxMb} MB。', 'places.kmlKmzTooLarge': '檔案過大。最大上傳大小為 {maxMb} MB。',
'places.kmlKmzHint': '可從 Google My Maps、Google Earth 等工具匯入地圖檔案。', 'places.kmlKmzHint': '可從 Google My Maps、Google Earth 等工具匯入地圖檔案。',
'places.kmlKmzSizeHint': '最大檔案大小:{maxMb} MB', 'places.kmlKmzSizeHint': '最大檔案大小:{maxMb} MB',
'places.kmlKmzSelectFile': '選擇檔案',
'places.kmlKmzSelectedFile': '已選擇檔案:{name}', 'places.kmlKmzSelectedFile': '已選擇檔案:{name}',
'places.kmlKmzSummaryTitle': '匯入摘要', 'places.kmlKmzSummaryTitle': '匯入摘要',
'places.kmlKmzSummaryValues': 'Placemarks{total} • 已匯入:{created} • 已略過:{skipped}', 'places.kmlKmzSummaryValues': 'Placemarks{total} • 已匯入:{created} • 已略過:{skipped}',
'places.gpxImported': '已從 GPX 匯入 {count} 個地點',
'places.gpxError': 'GPX 匯入失敗',
'places.importGoogleList': 'Google 列表', 'places.importGoogleList': 'Google 列表',
'places.googleListHint': '貼上共享的 Google Maps 列表連結以匯入所有地點。', 'places.googleListHint': '貼上共享的 Google Maps 列表連結以匯入所有地點。',
'places.googleListImported': '已從"{list}"匯入 {count} 個地點', 'places.googleListImported': '已從"{list}"匯入 {count} 個地點',
'places.googleListError': 'Google Maps 列表匯入失敗', 'places.googleListError': 'Google Maps 列表匯入失敗',
'places.viewDetails': '檢視詳情', 'places.viewDetails': '檢視詳情',
'places.urlResolved': '已從 URL 匯入地點',
'places.assignToDay': '新增到哪一天?', 'places.assignToDay': '新增到哪一天?',
'places.all': '全部', 'places.all': '全部',
'places.unplanned': '未規劃', 'places.unplanned': '未規劃',
@@ -1513,6 +1513,7 @@ const zhTw: Record<string, string> = {
'undo.moveDay': '地點已移至另一天', 'undo.moveDay': '地點已移至另一天',
'undo.lock': '地點鎖定已切換', 'undo.lock': '地點鎖定已切換',
'undo.importGpx': 'GPX 匯入', 'undo.importGpx': 'GPX 匯入',
'undo.importKeyholeMarkup': 'KMZ/KML 匯入',
'undo.importGoogleList': 'Google 地圖匯入', 'undo.importGoogleList': 'Google 地圖匯入',
// Notifications // Notifications
+175
View File
@@ -0,0 +1,175 @@
import { TextDecoder } from 'util';
export interface ParsedKmlPlacemark {
name: string | null;
description: string | null;
lat: number | null;
lng: number | null;
folderName: string | null;
}
export interface KmlPlacemarkNode {
placemark: any;
folderName: string | null;
}
export interface KmlImportSummary {
totalPlacemarks: number;
createdCount: number;
skippedCount: number;
warnings: string[];
errors: string[];
}
const UTF8_DECODER_FATAL = new TextDecoder('utf-8', { fatal: true });
const UTF8_DECODER_LOOSE = new TextDecoder('utf-8');
const ENTITY_MAP: Record<string, string> = {
'&amp;': '&',
'&lt;': '<',
'&gt;': '>',
'&quot;': '"',
'&#39;': "'",
'&nbsp;': ' ',
};
function asArray<T>(value: T | T[] | null | undefined): T[] {
if (value == null) return [];
return Array.isArray(value) ? value : [value];
}
function asTrimmedString(value: unknown): string | null {
if (value == null) return null;
const text = String(value).trim();
return text.length > 0 ? text : null;
}
function decodeHtmlEntities(value: string): string {
const withNamedEntities = value.replace(/&(amp|lt|gt|quot|#39|nbsp);/g, (m) => ENTITY_MAP[m] || m);
return withNamedEntities
.replace(/&#(\d+);/g, (_, dec) => {
const code = Number(dec);
return Number.isFinite(code) ? String.fromCharCode(code) : _;
})
.replace(/&#x([0-9a-fA-F]+);/g, (_, hex) => {
const code = Number.parseInt(hex, 16);
return Number.isFinite(code) ? String.fromCharCode(code) : _;
});
}
export function stripXmlNamespaces(xml: string): string {
// KML exports vary heavily; stripping namespace declarations/prefixes makes parsing resilient.
return xml
.replace(/\sxmlns(:\w+)?="[^"]*"/g, '')
.replace(/\sxmlns(:\w+)?='[^']*'/g, '')
.replace(/<(\/?)\w+:/g, '<$1');
}
export function decodeUtf8WithWarning(fileBuffer: Buffer): { text: string; warning: string | null } {
try {
return { text: UTF8_DECODER_FATAL.decode(fileBuffer), warning: null };
} catch {
return {
text: UTF8_DECODER_LOOSE.decode(fileBuffer),
warning: 'The uploaded file is not valid UTF-8. Some characters may be shown incorrectly.',
};
}
}
export function sanitizeKmlDescription(value: unknown): string | null {
const raw = asTrimmedString(value);
if (!raw) return null;
const withLineBreaks = raw.replace(/<br\s*\/?>/gi, '\n');
const stripped = withLineBreaks.replace(/<[^>]+>/g, '');
const decoded = decodeHtmlEntities(stripped)
.replace(/\r\n/g, '\n')
.replace(/\r/g, '\n')
.replace(/[\t\f\v]+/g, ' ')
.replace(/\n{3,}/g, '\n\n')
.trim();
return decoded || null;
}
export function parseKmlPointCoordinates(value: unknown): { lat: number; lng: number } | null {
const coordinates = asTrimmedString(value);
if (!coordinates) return null;
const firstCoordinate = coordinates.split(/\s+/)[0];
const [lngRaw, latRaw] = firstCoordinate.split(',');
if (lngRaw == null || latRaw == null) return null;
const lng = Number.parseFloat(lngRaw);
const lat = Number.parseFloat(latRaw);
if (!Number.isFinite(lat) || !Number.isFinite(lng)) return null;
return { lat, lng };
}
export function createKmlImportSummary(totalPlacemarks: number): KmlImportSummary {
return {
totalPlacemarks,
createdCount: 0,
skippedCount: 0,
warnings: [],
errors: [],
};
}
export function buildCategoryNameLookup(categories: { id: number; name: string }[]): Map<string, number> {
const lookup = new Map<string, number>();
for (const category of categories) {
const normalizedName = category.name.trim().toLowerCase();
if (!normalizedName) continue;
if (!lookup.has(normalizedName)) {
lookup.set(normalizedName, category.id);
}
}
return lookup;
}
export function resolveCategoryIdForFolder(folderName: string | null, lookup: Map<string, number>): number | null {
if (!folderName) return null;
const normalizedFolder = folderName.trim().toLowerCase();
if (!normalizedFolder) return null;
return lookup.get(normalizedFolder) ?? null;
}
export function extractKmlPlacemarkNodes(kmlRoot: any): KmlPlacemarkNode[] {
const nodes: KmlPlacemarkNode[] = [];
const visitNode = (node: any, currentFolderName: string | null): void => {
if (!node || typeof node !== 'object') return;
for (const placemark of asArray(node.Placemark)) {
nodes.push({ placemark, folderName: currentFolderName });
}
for (const folder of asArray(node.Folder)) {
// Nested folders inherit/override folder context used for category matching.
const folderName = asTrimmedString(folder?.name) || currentFolderName;
visitNode(folder, folderName);
}
for (const childDocument of asArray(node.Document)) {
visitNode(childDocument, currentFolderName);
}
};
visitNode(kmlRoot, null);
return nodes;
}
export function parsePlacemarkNode(node: KmlPlacemarkNode): ParsedKmlPlacemark {
const coordinates = parseKmlPointCoordinates(node.placemark?.Point?.coordinates);
return {
name: asTrimmedString(node.placemark?.name),
description: sanitizeKmlDescription(node.placemark?.description),
lat: coordinates?.lat ?? null,
lng: coordinates?.lng ?? null,
folderName: node.folderName,
};
}