From af789b7f7cfd98ac6c407405817c5237f8683dc8 Mon Sep 17 00:00:00 2001 From: Isaias Tavares Date: Sun, 12 Apr 2026 17:29:11 -0300 Subject: [PATCH] fix(i18n): translate hardcoded strings in JourneyDetailPage and fix ellipsis in all languages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace all remaining hardcoded strings in JourneyDetailPage JourneySettingsDialog with t() calls - Add 14 missing translation keys to all 13 non-English language files (trips.member*, common.expand/collapse, inspector.remove, memories.*, journey.*) - Fix common.loading and common.saving to use Unicode ellipsis (…) instead of three dots (...) - Update 4 test files that expected three-dot ellipsis to use Unicode ellipsis - All 2541 tests passing --- .../components/Admin/AuditLogPanel.test.tsx | 2 +- .../src/components/Admin/GitHubPanel.test.tsx | 4 +- .../src/components/Planner/DayDetailPanel.tsx | 2 +- .../src/components/Planner/PlaceInspector.tsx | 2 +- .../components/Settings/NotificationsTab.tsx | 4 +- .../Settings/PhotoProvidersSection.tsx | 4 +- .../components/Trips/TripFormModal.test.tsx | 2 +- client/src/components/Trips/TripFormModal.tsx | 8 +-- client/src/hooks/useDayNotes.ts | 8 ++- client/src/i18n/translations/ar.ts | 15 ++++- client/src/i18n/translations/br.ts | 15 ++++- client/src/i18n/translations/cs.ts | 15 ++++- client/src/i18n/translations/de.ts | 15 ++++- client/src/i18n/translations/en.ts | 19 +++++- client/src/i18n/translations/es.ts | 18 ++++-- client/src/i18n/translations/fr.ts | 18 ++++-- client/src/i18n/translations/hu.ts | 15 ++++- client/src/i18n/translations/it.ts | 15 ++++- client/src/i18n/translations/nl.ts | 15 ++++- client/src/i18n/translations/pl.ts | 15 ++++- client/src/i18n/translations/ru.ts | 15 ++++- client/src/i18n/translations/zh.ts | 15 ++++- client/src/i18n/translations/zhTw.ts | 15 ++++- client/src/pages/JourneyDetailPage.tsx | 62 +++++++++---------- 24 files changed, 248 insertions(+), 70 deletions(-) diff --git a/client/src/components/Admin/AuditLogPanel.test.tsx b/client/src/components/Admin/AuditLogPanel.test.tsx index 4d076f0e..a6e73507 100644 --- a/client/src/components/Admin/AuditLogPanel.test.tsx +++ b/client/src/components/Admin/AuditLogPanel.test.tsx @@ -47,7 +47,7 @@ describe('AuditLogPanel', () => { }), ); render(); - expect(screen.getByText('Loading...')).toBeInTheDocument(); + expect(screen.getByText('Loading…')).toBeInTheDocument(); expect(document.querySelector('table')).not.toBeInTheDocument(); }); diff --git a/client/src/components/Admin/GitHubPanel.test.tsx b/client/src/components/Admin/GitHubPanel.test.tsx index 617bdd88..429bd042 100644 --- a/client/src/components/Admin/GitHubPanel.test.tsx +++ b/client/src/components/Admin/GitHubPanel.test.tsx @@ -55,7 +55,7 @@ describe('GitHubPanel', () => { it('FE-ADMIN-GH-002: all support links have correct href and target=_blank', async () => { render(); - await waitFor(() => expect(screen.queryByText('Loading...')).not.toBeInTheDocument()); + await waitFor(() => expect(screen.queryByText('Loading…')).not.toBeInTheDocument()); const kofi = screen.getByText('Ko-fi').closest('a')!; expect(kofi).toHaveAttribute('href', 'https://ko-fi.com/mauriceboe'); @@ -272,7 +272,7 @@ describe('GitHubPanel', () => { it('FE-ADMIN-GH-016: support card hover effects fire without error', async () => { render(); - await waitFor(() => expect(screen.queryByText('Loading...')).not.toBeInTheDocument()); + await waitFor(() => expect(screen.queryByText('Loading…')).not.toBeInTheDocument()); const kofiLink = screen.getByText('Ko-fi').closest('a')!; fireEvent.mouseEnter(kofiLink); diff --git a/client/src/components/Planner/DayDetailPanel.tsx b/client/src/components/Planner/DayDetailPanel.tsx index 827ae454..c1a4acc3 100644 --- a/client/src/components/Planner/DayDetailPanel.tsx +++ b/client/src/components/Planner/DayDetailPanel.tsx @@ -189,7 +189,7 @@ export default function DayDetailPanel({ day, days, places, categories = [], tri {!collapsed && formattedDate &&
{formattedDate}
} - @@ -261,7 +261,7 @@ export default function PhotoProvidersSection(): React.ReactElement { onClick={() => handleTestProvider(provider)} disabled={!canTest || testing} className="flex items-center gap-2 px-4 py-2 border border-slate-200 rounded-lg text-sm hover:bg-slate-50" - title={!canTest ? 'Test route is not configured for this provider' : ''} + title={!canTest ? t('memories.testRouteNotConfigured') : ''} > {testing ?
diff --git a/client/src/components/Trips/TripFormModal.test.tsx b/client/src/components/Trips/TripFormModal.test.tsx index ed5bbac9..9c1042a8 100644 --- a/client/src/components/Trips/TripFormModal.test.tsx +++ b/client/src/components/Trips/TripFormModal.test.tsx @@ -284,6 +284,6 @@ describe('TripFormModal', () => { const submitBtns = screen.getAllByText('Create New Trip'); const submitBtn = submitBtns.find(el => el.closest('button'))!; await user.click(submitBtn.closest('button')!); - await waitFor(() => expect(screen.getByText('Saving...')).toBeInTheDocument()); + await waitFor(() => expect(screen.getByText('Saving…')).toBeInTheDocument()); }); }); diff --git a/client/src/components/Trips/TripFormModal.tsx b/client/src/components/Trips/TripFormModal.tsx index 4b2865ce..47f1184a 100644 --- a/client/src/components/Trips/TripFormModal.tsx +++ b/client/src/components/Trips/TripFormModal.tsx @@ -385,8 +385,8 @@ export default function TripFormModal({ isOpen, onClose, onSave, trip, onCoverUp try { await tripsApi.removeMember(trip!.id, m.id) setExistingMembers(prev => prev.filter(x => x.id !== m.id)) - toast.success(`${m.username} removed`) - } catch { toast.error('Failed to remove') } + toast.success(t('trips.memberRemoved', { username: m.username })) + } catch { toast.error(t('trips.memberRemoveError')) } }} style={{ display: 'flex', alignItems: 'center', gap: 5, padding: '4px 10px', borderRadius: 99, @@ -431,8 +431,8 @@ export default function TripFormModal({ isOpen, onClose, onSave, trip, onCoverUp try { await tripsApi.addMember(trip.id, user.username) setExistingMembers(prev => [...prev, { id: user.id, username: user.username }]) - toast.success(`${user.username} added`) - } catch { toast.error('Failed to add') } + toast.success(t('trips.memberAdded', { username: user.username })) + } catch { toast.error(t('trips.memberAddError')) } } } else { setSelectedMembers(prev => prev.includes(Number(value)) ? prev : [...prev, Number(value)]) diff --git a/client/src/hooks/useDayNotes.ts b/client/src/hooks/useDayNotes.ts index 296c197a..ef03154b 100644 --- a/client/src/hooks/useDayNotes.ts +++ b/client/src/hooks/useDayNotes.ts @@ -1,6 +1,7 @@ import { useState, useRef } from 'react' import { useTripStore } from '../store/tripStore' import { useToast } from '../components/shared/Toast' +import { useTranslation } from '../i18n' import type { MergedItem, DayNotesMap, DayNote } from '../types' interface NoteUiState { @@ -21,6 +22,7 @@ export function useDayNotes(tripId: number | string) { const noteInputRef = useRef(null) const tripStore = useTripStore() const toast = useToast() + const { t } = useTranslation() const dayNotes: DayNotesMap = tripStore.dayNotes || {} const openAddNote = (dayId: number, getMergedItems: (dayId: number) => MergedItem[], expandDay?: (dayId: number) => void) => { @@ -50,12 +52,12 @@ export function useDayNotes(tripId: number | string) { await tripStore.updateDayNote(tripId, dayId, ui.noteId!, { text: ui.text.trim(), time: ui.time || null, icon: ui.icon || 'FileText' }) } cancelNote(dayId) - } catch (err: unknown) { toast.error(err instanceof Error ? err.message : 'Unknown error') } + } catch (err: unknown) { toast.error(err instanceof Error ? err.message : t('common.unknownError')) } } const deleteNote = async (dayId: number, noteId: number) => { try { await tripStore.deleteDayNote(tripId, dayId, noteId) } - catch (err: unknown) { toast.error(err instanceof Error ? err.message : 'Unknown error') } + catch (err: unknown) { toast.error(err instanceof Error ? err.message : t('common.unknownError')) } } const moveNote = async (dayId: number, noteId: number, direction: 'up' | 'down', getMergedItems: (dayId: number) => MergedItem[]) => { @@ -71,7 +73,7 @@ export function useDayNotes(tripId: number | string) { newSortOrder = idx < merged.length - 2 ? (merged[idx + 1].sortKey + merged[idx + 2].sortKey) / 2 : merged[idx + 1].sortKey + 1 } try { await tripStore.updateDayNote(tripId, dayId, noteId, { sort_order: newSortOrder }) } - catch (err: unknown) { toast.error(err instanceof Error ? err.message : 'Unknown error') } + catch (err: unknown) { toast.error(err instanceof Error ? err.message : t('common.unknownError')) } } return { noteUi, setNoteUi, noteInputRef, dayNotes, openAddNote, openEditNote, cancelNote, saveNote, deleteNote, moveNote } diff --git a/client/src/i18n/translations/ar.ts b/client/src/i18n/translations/ar.ts index 5fbb3234..cff3eb41 100644 --- a/client/src/i18n/translations/ar.ts +++ b/client/src/i18n/translations/ar.ts @@ -33,6 +33,12 @@ const ar: Record = { 'common.password': 'كلمة المرور', 'common.saving': 'جارٍ الحفظ...', 'common.saved': 'تم الحفظ', + 'common.expand': 'توسيع', + 'common.collapse': 'طي', + 'trips.memberRemoved': '{username} تمت إزالته', + 'trips.memberRemoveError': 'فشل في الإزالة', + 'trips.memberAdded': '{username} تمت إضافته', + 'trips.memberAddError': 'فشل في الإضافة', 'trips.reminder': 'تذكير', 'trips.reminderNone': 'بدون', 'trips.reminderDay': 'يوم', @@ -940,6 +946,7 @@ const ar: Record = { 'inspector.files': 'الملفات', 'inspector.filesCount': '{count} ملفات', 'inspector.removeFromDay': 'إزالة من اليوم', + 'inspector.remove': 'إزالة', 'inspector.addToDay': 'إضافة إلى اليوم', 'inspector.confirmedRes': 'حجز مؤكد', 'inspector.pendingRes': 'حجز قيد الانتظار', @@ -1175,7 +1182,6 @@ const ar: Record = { 'packing.menuCheckAll': 'تحديد الكل', 'packing.menuUncheckAll': 'إلغاء تحديد الكل', 'packing.menuDeleteCat': 'حذف الفئة', - 'packing.assignUser': 'تعيين مستخدم', 'packing.noMembers': 'لا أعضاء', 'packing.addItem': 'إضافة عنصر', 'packing.addItemPlaceholder': 'اسم العنصر...', @@ -1502,6 +1508,9 @@ const ar: Record = { 'memories.saved': 'تم حفظ إعدادات {provider_name}', 'memories.providerDisconnectedBanner': 'اتصالك بـ {provider_name} مفقود. أعد الاتصال في الإعدادات لعرض الصور.', 'memories.saveError': 'تعذّر حفظ إعدادات {provider_name}', + 'memories.saveRouteNotConfigured': 'مسار الحفظ غير مهيأ لهذا المزود', + 'memories.testRouteNotConfigured': 'مسار الاختبار غير مهيأ لهذا المزود', + 'memories.fillRequiredFields': 'يرجى ملء جميع الحقول المطلوبة', 'memories.oldest': 'الأقدم أولاً', 'memories.newest': 'الأحدث أولاً', 'memories.allLocations': 'جميع المواقع', @@ -1526,6 +1535,10 @@ const ar: Record = { 'memories.confirmShareTitle': 'مشاركة مع أعضاء الرحلة؟', 'memories.confirmShareHint': '{count} صور ستكون مرئية لجميع أعضاء هذه الرحلة. يمكنك جعل الصور الفردية خاصة لاحقًا.', 'memories.confirmShareButton': 'مشاركة الصور', + 'journey.settings.failedToDelete': 'فشل في الحذف', + 'journey.entries.deleteTitle': 'حذف الإدخال', + 'journey.photosUploaded': 'تم رفع {count} صورة', + 'journey.photosAdded': 'تمت إضافة {count} صورة', // Collab Addon 'collab.tabs.chat': 'الدردشة', diff --git a/client/src/i18n/translations/br.ts b/client/src/i18n/translations/br.ts index c6a3ea8a..437096d2 100644 --- a/client/src/i18n/translations/br.ts +++ b/client/src/i18n/translations/br.ts @@ -29,11 +29,17 @@ const br: Record = { 'common.password': 'Senha', 'common.saving': 'Salvando...', 'common.saved': 'Salvo', + 'common.expand': 'Expandir', + 'common.collapse': 'Recolher', 'trips.reminder': 'Lembrete', 'trips.reminderNone': 'Nenhum', 'trips.reminderDay': 'dia', 'trips.reminderDays': 'dias', 'trips.reminderCustom': 'Personalizado', + 'trips.memberRemoved': '{username} removido', + 'trips.memberRemoveError': 'Falha ao remover', + 'trips.memberAdded': '{username} adicionado', + 'trips.memberAddError': 'Falha ao adicionar', 'trips.reminderDaysBefore': 'dias antes da partida', 'trips.reminderDisabledHint': 'Os lembretes de viagem estão desativados. Ative-os em Admin > Configurações > Notificações.', 'common.update': 'Atualizar', @@ -909,6 +915,7 @@ const br: Record = { 'inspector.files': 'Arquivos', 'inspector.filesCount': '{count} arquivos', 'inspector.removeFromDay': 'Remover do dia', + 'inspector.remove': 'Remover', 'inspector.addToDay': 'Adicionar ao dia', 'inspector.confirmedRes': 'Reserva confirmada', 'inspector.pendingRes': 'Reserva pendente', @@ -1134,7 +1141,6 @@ const br: Record = { 'packing.allPacked': 'Tudo na mala!', 'packing.addPlaceholder': 'Adicionar item...', 'packing.categoryPlaceholder': 'Categoria...', - 'packing.assignUser': 'Atribuir usuário', 'packing.saveAsTemplate': 'Salvar como modelo', 'packing.templateName': 'Nome do modelo', 'packing.templateSaved': 'Lista de bagagem salva como modelo', @@ -1808,6 +1814,9 @@ const br: Record = { 'memories.providerUsername': 'Nome de usuário', 'memories.providerPassword': 'Senha', 'memories.saveError': 'Não foi possível salvar as configurações de {provider_name}', + 'memories.saveRouteNotConfigured': 'A rota de salvamento não está configurada para este provedor', + 'memories.testRouteNotConfigured': 'A rota de teste não está configurada para este provedor', + 'memories.fillRequiredFields': 'Por favor preencha todos os campos obrigatórios', 'memories.selectAlbumMultiple': 'Selecionar álbum', 'memories.selectPhotosMultiple': 'Selecionar fotos', 'journey.title': 'Jornada', @@ -1977,6 +1986,10 @@ const br: Record = { 'journey.settings.saveFailed': 'Não foi possível salvar', 'journey.settings.coverUpdated': 'Capa atualizada', 'journey.settings.coverFailed': 'Falha no envio', + 'journey.settings.failedToDelete': 'Falha ao excluir', + 'journey.entries.deleteTitle': 'Excluir entrada', + 'journey.photosUploaded': '{count} fotos enviadas', + 'journey.photosAdded': '{count} fotos adicionadas', 'journey.public.notFound': 'Não encontrado', 'journey.public.notFoundMessage': 'Esta jornada não existe ou o link expirou.', 'journey.public.readOnly': 'Somente leitura · Jornada pública', diff --git a/client/src/i18n/translations/cs.ts b/client/src/i18n/translations/cs.ts index 626b02d5..87b35787 100644 --- a/client/src/i18n/translations/cs.ts +++ b/client/src/i18n/translations/cs.ts @@ -28,6 +28,12 @@ const cs: Record = { 'common.email': 'E-mail', 'common.password': 'Heslo', 'common.saving': 'Ukládání...', + 'trips.memberRemoved': '{username} odebrán', + 'trips.memberRemoveError': 'Odebrání se nezdařilo', + 'trips.memberAdded': '{username} přidán', + 'trips.memberAddError': 'Přidání se nezdařilo', + 'common.expand': 'Rozbalit', + 'common.collapse': 'Sbalit', 'common.saved': 'Uloženo', 'trips.reminder': 'Připomínka', 'trips.reminderNone': 'Žádná', @@ -938,6 +944,7 @@ const cs: Record = { 'inspector.files': 'Soubory', 'inspector.filesCount': '{count} souborů', 'inspector.removeFromDay': 'Odebrat ze dne', + 'inspector.remove': 'Odstranit', 'inspector.addToDay': 'Přidat ke dni', 'inspector.confirmedRes': 'Potvrzená rezervace', 'inspector.pendingRes': 'Čekající rezervace', @@ -1173,7 +1180,6 @@ const cs: Record = { 'packing.menuCheckAll': 'Označit vše', 'packing.menuUncheckAll': 'Odznačit vše', 'packing.menuDeleteCat': 'Smazat kategorii', - 'packing.assignUser': 'Přiřadit uživatele', 'packing.noMembers': 'Žádní členové cesty', 'packing.addItem': 'Přidat položku', 'packing.addItemPlaceholder': 'Název položky...', @@ -1810,6 +1816,9 @@ const cs: Record = { 'memories.providerUsername': 'Uživatelské jméno', 'memories.providerPassword': 'Heslo', 'memories.saveError': 'Nepodařilo se uložit nastavení {provider_name}', + 'memories.saveRouteNotConfigured': 'Trasa uložení není nakonfigurována pro tohoto poskytovatele', + 'memories.testRouteNotConfigured': 'Testovací trasa není nakonfigurována pro tohoto poskytovatele', + 'memories.fillRequiredFields': 'Prosím vyplňte všechna povinná pole', 'memories.selectAlbumMultiple': 'Vybrat album', 'memories.selectPhotosMultiple': 'Vybrat fotky', 'journey.title': 'Cestovní deník', @@ -1979,6 +1988,10 @@ const cs: Record = { 'journey.settings.saveFailed': 'Uložení selhalo', 'journey.settings.coverUpdated': 'Obal aktualizován', 'journey.settings.coverFailed': 'Nahrávání selhalo', + 'journey.settings.failedToDelete': 'Smazání se nezdařilo', + 'journey.entries.deleteTitle': 'Smazat záznam', + 'journey.photosUploaded': '{count} fotografií nahráno', + 'journey.photosAdded': '{count} fotografií přidáno', 'journey.public.notFound': 'Nenalezeno', 'journey.public.notFoundMessage': 'Tento cestovní deník neexistuje nebo odkaz vypršel.', 'journey.public.readOnly': 'Pouze ke čtení · Veřejný cestovní deník', diff --git a/client/src/i18n/translations/de.ts b/client/src/i18n/translations/de.ts index 649fa891..f2aaffb6 100644 --- a/client/src/i18n/translations/de.ts +++ b/client/src/i18n/translations/de.ts @@ -28,6 +28,8 @@ const de: Record = { 'common.email': 'E-Mail', 'common.password': 'Passwort', 'common.saving': 'Speichern...', + 'common.expand': 'Erweitern', + 'common.collapse': 'Einklappen', 'common.justNow': 'gerade eben', 'common.hoursAgo': 'vor {count}h', 'common.daysAgo': 'vor {count}T', @@ -37,6 +39,10 @@ const de: Record = { 'trips.reminderDay': 'Tag', 'trips.reminderDays': 'Tage', 'trips.reminderCustom': 'Benutzerdefiniert', + 'trips.memberRemoved': '{username} entfernt', + 'trips.memberRemoveError': 'Entfernen fehlgeschlagen', + 'trips.memberAdded': '{username} hinzugefügt', + 'trips.memberAddError': 'Hinzufügen fehlgeschlagen', 'trips.reminderDaysBefore': 'Tage vor Abreise', 'trips.reminderDisabledHint': 'Reiseerinnerungen sind deaktiviert. Aktivieren Sie sie unter Admin > Einstellungen > Benachrichtigungen.', 'common.update': 'Aktualisieren', @@ -940,6 +946,7 @@ const de: Record = { 'inspector.files': 'Dateien', 'inspector.filesCount': '{count} Dateien', 'inspector.removeFromDay': 'Vom Tag entfernen', + 'inspector.remove': 'Entfernen', 'inspector.addToDay': 'Zum Tag hinzufügen', 'inspector.confirmedRes': 'Bestätigte Reservierung', 'inspector.pendingRes': 'Ausstehende Reservierung', @@ -1174,7 +1181,6 @@ const de: Record = { 'packing.menuCheckAll': 'Alle abhaken', 'packing.menuUncheckAll': 'Alle Haken entfernen', 'packing.menuDeleteCat': 'Kategorie löschen', - 'packing.assignUser': 'Benutzer zuweisen', 'packing.noMembers': 'Keine Mitglieder', 'packing.addItem': 'Eintrag hinzufügen', 'packing.addItemPlaceholder': 'Artikelname...', @@ -1501,6 +1507,9 @@ const de: Record = { 'memories.saved': '{provider_name}-Einstellungen gespeichert', 'memories.providerDisconnectedBanner': 'Deine {provider_name}-Verbindung wurde getrennt. Verbinde erneut in den Einstellungen, um Fotos anzuzeigen.', 'memories.saveError': '{provider_name}-Einstellungen konnten nicht gespeichert werden', + 'memories.saveRouteNotConfigured': 'Speicherroute ist für diesen Anbieter nicht konfiguriert', + 'memories.testRouteNotConfigured': 'Testroute ist für diesen Anbieter nicht konfiguriert', + 'memories.fillRequiredFields': 'Bitte füllen Sie alle Pflichtfelder aus', 'memories.addPhotos': 'Fotos hinzufügen', 'memories.linkAlbum': 'Album verknüpfen', 'memories.selectAlbum': 'Immich-Album auswählen', @@ -1967,6 +1976,10 @@ const de: Record = { 'journey.settings.saveFailed': 'Speichern fehlgeschlagen', 'journey.settings.coverUpdated': 'Titelbild aktualisiert', 'journey.settings.coverFailed': 'Upload fehlgeschlagen', + 'journey.settings.failedToDelete': 'Löschen fehlgeschlagen', + 'journey.entries.deleteTitle': 'Eintrag löschen', + 'journey.photosUploaded': '{count} Fotos hochgeladen', + 'journey.photosAdded': '{count} Fotos hinzugefügt', 'journey.public.notFound': 'Nicht gefunden', 'journey.public.notFoundMessage': 'Diese Journey existiert nicht oder der Link ist abgelaufen.', 'journey.public.readOnly': 'Nur lesen · Öffentliche Journey', diff --git a/client/src/i18n/translations/en.ts b/client/src/i18n/translations/en.ts index 89c2fad1..f34f9475 100644 --- a/client/src/i18n/translations/en.ts +++ b/client/src/i18n/translations/en.ts @@ -5,7 +5,7 @@ const en: Record = { 'common.delete': 'Delete', 'common.edit': 'Edit', 'common.add': 'Add', - 'common.loading': 'Loading...', + 'common.loading': 'Loading…', 'common.import': 'Import', 'common.error': 'Error', 'common.unknownError': 'Unknown error', @@ -27,11 +27,15 @@ const en: Record = { 'common.name': 'Name', 'common.email': 'Email', 'common.password': 'Password', - 'common.saving': 'Saving...', + 'common.saving': 'Saving…', 'common.justNow': 'just now', 'common.hoursAgo': '{count}h ago', 'common.daysAgo': '{count}d ago', 'common.saved': 'Saved', + 'trips.memberRemoved': '{username} removed', + 'trips.memberRemoveError': 'Failed to remove', + 'trips.memberAdded': '{username} added', + 'trips.memberAddError': 'Failed to add', 'trips.reminder': 'Reminder', 'trips.reminderNone': 'None', 'trips.reminderDay': 'day', @@ -44,6 +48,8 @@ const en: Record = { 'common.uploading': 'Uploading…', 'common.backToPlanning': 'Back to Planning', 'common.reset': 'Reset', + 'common.expand': 'Expand', + 'common.collapse': 'Collapse', // Navbar 'nav.trip': 'Trip', @@ -961,6 +967,7 @@ const en: Record = { 'inspector.showHours': 'Show opening hours', 'inspector.files': 'Files', 'inspector.filesCount': '{count} files', + 'inspector.remove': 'Remove', 'inspector.removeFromDay': 'Remove from Day', 'inspector.addToDay': 'Add to Day', 'inspector.confirmedRes': 'Confirmed Reservation', @@ -1196,7 +1203,6 @@ const en: Record = { 'packing.menuCheckAll': 'Check All', 'packing.menuUncheckAll': 'Uncheck All', 'packing.menuDeleteCat': 'Delete Category', - 'packing.assignUser': 'Assign user', 'packing.noMembers': 'No trip members', 'packing.addItem': 'Add item', 'packing.addItemPlaceholder': 'Item name...', @@ -1558,6 +1564,9 @@ const en: Record = { 'memories.error.addPhotos': 'Failed to add photos', 'memories.error.removePhoto': 'Failed to remove photo', 'memories.error.toggleSharing': 'Failed to update sharing', + 'memories.saveRouteNotConfigured': 'Save route is not configured for this provider', + 'memories.testRouteNotConfigured': 'Test route is not configured for this provider', + 'memories.fillRequiredFields': 'Please fill all required fields', // Collab Addon 'collab.tabs.chat': 'Chat', @@ -1990,6 +1999,10 @@ const en: Record = { 'journey.settings.saveFailed': 'Failed to save', 'journey.settings.coverUpdated': 'Cover updated', 'journey.settings.coverFailed': 'Upload failed', + 'journey.settings.failedToDelete': 'Failed to delete', + 'journey.entries.deleteTitle': 'Delete Entry', + 'journey.photosUploaded': '{count} photos uploaded', + 'journey.photosAdded': '{count} photos added', // Journey — Public Page 'journey.public.notFound': 'Not Found', diff --git a/client/src/i18n/translations/es.ts b/client/src/i18n/translations/es.ts index 44793bc6..1b68816f 100644 --- a/client/src/i18n/translations/es.ts +++ b/client/src/i18n/translations/es.ts @@ -29,11 +29,17 @@ const es: Record = { 'common.password': 'Contraseña', 'common.saving': 'Guardando...', 'common.saved': 'Guardado', + 'common.expand': 'Expandir', + 'common.collapse': 'Contraer', 'trips.reminder': 'Recordatorio', 'trips.reminderNone': 'Ninguno', 'trips.reminderDay': 'día', 'trips.reminderDays': 'días', 'trips.reminderCustom': 'Personalizado', + 'trips.memberRemoved': '{username} eliminado', + 'trips.memberRemoveError': 'Error al eliminar', + 'trips.memberAdded': '{username} añadido', + 'trips.memberAddError': 'Error al añadir', 'trips.reminderDaysBefore': 'días antes de la salida', 'trips.reminderDisabledHint': 'Los recordatorios de viaje están desactivados. Actívalos en Admin > Configuración > Notificaciones.', 'common.update': 'Actualizar', @@ -913,6 +919,7 @@ const es: Record = { 'inspector.files': 'Archivos', 'inspector.filesCount': '{count} archivos', 'inspector.removeFromDay': 'Quitar del día', + 'inspector.remove': 'Eliminar', 'inspector.addToDay': 'Añadir al día', 'inspector.confirmedRes': 'Reserva confirmada', 'inspector.pendingRes': 'Reserva pendiente', @@ -1120,10 +1127,6 @@ const es: Record = { 'packing.saveAsTemplate': 'Guardar como plantilla', 'packing.templateName': 'Nombre de la plantilla', 'packing.templateSaved': 'Lista de equipaje guardada como plantilla', - 'packing.assignUser': 'Asignar usuario', - 'packing.saveAsTemplate': 'Guardar como plantilla', - 'packing.templateName': 'Nombre de la plantilla', - 'packing.templateSaved': 'Lista de equipaje guardada como plantilla', 'packing.noMembers': 'Sin miembros', 'packing.bags': 'Equipaje', 'packing.noBag': 'Sin asignar', @@ -1454,6 +1457,9 @@ const es: Record = { 'memories.saved': 'Configuración de {provider_name} guardada', 'memories.providerDisconnectedBanner': 'Se perdió la conexión con {provider_name}. Vuelve a conectar en Configuración para ver las fotos.', 'memories.saveError': 'No se pudieron guardar los ajustes de {provider_name}', + 'memories.saveRouteNotConfigured': 'La ruta de guardado no está configurada para este proveedor', + 'memories.testRouteNotConfigured': 'La ruta de prueba no está configurada para este proveedor', + 'memories.fillRequiredFields': 'Por favor complete todos los campos requeridos', 'memories.oldest': 'Más antiguas', 'memories.newest': 'Más recientes', 'memories.allLocations': 'Todas las ubicaciones', @@ -1984,6 +1990,10 @@ const es: Record = { 'journey.settings.saveFailed': 'No se pudo guardar', 'journey.settings.coverUpdated': 'Portada actualizada', 'journey.settings.coverFailed': 'Error al subir', + 'journey.settings.failedToDelete': 'Error al eliminar', + 'journey.entries.deleteTitle': 'Eliminar entrada', + 'journey.photosUploaded': '{count} fotos subidas', + 'journey.photosAdded': '{count} fotos añadidas', 'journey.public.notFound': 'No encontrado', 'journey.public.notFoundMessage': 'Esta travesía no existe o el enlace ha expirado.', 'journey.public.readOnly': 'Solo lectura · Travesía pública', diff --git a/client/src/i18n/translations/fr.ts b/client/src/i18n/translations/fr.ts index 1e0d02b5..d424831d 100644 --- a/client/src/i18n/translations/fr.ts +++ b/client/src/i18n/translations/fr.ts @@ -29,6 +29,12 @@ const fr: Record = { 'common.password': 'Mot de passe', 'common.saving': 'Enregistrement…', 'common.saved': 'Enregistré', + 'trips.memberRemoved': '{username} supprimé', + 'trips.memberRemoveError': 'Échec de la suppression', + 'trips.memberAdded': '{username} ajouté', + 'trips.memberAddError': "Échec de l'ajout", + 'common.expand': 'Développer', + 'common.collapse': 'Réduire', 'trips.reminder': 'Rappel', 'trips.reminderNone': 'Aucun', 'trips.reminderDay': 'jour', @@ -936,6 +942,7 @@ const fr: Record = { 'inspector.files': 'Fichiers', 'inspector.filesCount': '{count} fichiers', 'inspector.removeFromDay': 'Retirer du jour', + 'inspector.remove': 'Supprimer', 'inspector.addToDay': 'Ajouter au jour', 'inspector.confirmedRes': 'Réservation confirmée', 'inspector.pendingRes': 'Réservation en attente', @@ -1182,10 +1189,6 @@ const fr: Record = { 'packing.saveAsTemplate': 'Enregistrer comme modèle', 'packing.templateName': 'Nom du modèle', 'packing.templateSaved': 'Liste de voyage enregistrée comme modèle', - 'packing.assignUser': 'Assigner un utilisateur', - 'packing.saveAsTemplate': 'Enregistrer comme modèle', - 'packing.templateName': 'Nom du modèle', - 'packing.templateSaved': 'Liste de bagages enregistrée comme modèle', 'packing.noMembers': 'Aucun membre', 'packing.bags': 'Bagages', 'packing.noBag': 'Non assigné', @@ -1501,6 +1504,9 @@ const fr: Record = { 'memories.saved': 'Paramètres {provider_name} enregistrés', 'memories.providerDisconnectedBanner': 'Votre connexion {provider_name} est perdue. Reconnectez-vous dans les Paramètres pour voir les photos.', 'memories.saveError': 'Impossible d\'enregistrer les paramètres de {provider_name}', + 'memories.saveRouteNotConfigured': "La route de sauvegarde n'est pas configurée pour ce fournisseur", + 'memories.testRouteNotConfigured': "La route de test n'est pas configurée pour ce fournisseur", + 'memories.fillRequiredFields': 'Veuillez remplir tous les champs obligatoires', 'memories.oldest': 'Plus anciennes', 'memories.newest': 'Plus récentes', 'memories.allLocations': 'Tous les lieux', @@ -1978,6 +1984,10 @@ const fr: Record = { 'journey.settings.saveFailed': 'Échec de l\'enregistrement', 'journey.settings.coverUpdated': 'Couverture mise à jour', 'journey.settings.coverFailed': 'Échec du téléversement', + 'journey.settings.failedToDelete': 'Échec de la suppression', + 'journey.entries.deleteTitle': "Supprimer l'entrée", + 'journey.photosUploaded': '{count} photos téléversées', + 'journey.photosAdded': '{count} photos ajoutées', 'journey.public.notFound': 'Introuvable', 'journey.public.notFoundMessage': 'Ce journal n\'existe pas ou le lien a expiré.', 'journey.public.readOnly': 'Lecture seule · Journal public', diff --git a/client/src/i18n/translations/hu.ts b/client/src/i18n/translations/hu.ts index 8738ca93..759ef703 100644 --- a/client/src/i18n/translations/hu.ts +++ b/client/src/i18n/translations/hu.ts @@ -28,6 +28,12 @@ const hu: Record = { 'common.email': 'E-mail', 'common.password': 'Jelszó', 'common.saving': 'Mentés...', + 'trips.memberRemoved': '{username} eltávolítva', + 'trips.memberRemoveError': 'Eltávolítás sikertelen', + 'trips.memberAdded': '{username} hozzáadva', + 'trips.memberAddError': 'Hozzáadás sikertelen', + 'common.expand': 'Kibontás', + 'common.collapse': 'Összecsukás', 'common.saved': 'Mentve', 'trips.reminder': 'Emlékeztető', 'trips.reminderNone': 'Nincs', @@ -937,6 +943,7 @@ const hu: Record = { 'inspector.files': 'Fájlok', 'inspector.filesCount': '{count} fájl', 'inspector.removeFromDay': 'Eltávolítás a napról', + 'inspector.remove': 'Eltávolítás', 'inspector.addToDay': 'Hozzáadás a naphoz', 'inspector.confirmedRes': 'Megerősített foglalás', 'inspector.pendingRes': 'Függőben lévő foglalás', @@ -1172,7 +1179,6 @@ const hu: Record = { 'packing.menuCheckAll': 'Összes kipipálása', 'packing.menuUncheckAll': 'Összes jelölés törlése', 'packing.menuDeleteCat': 'Kategória törlése', - 'packing.assignUser': 'Felhasználó hozzárendelése', 'packing.noMembers': 'Nincsenek utazási tagok', 'packing.addItem': 'Tétel hozzáadása', 'packing.addItemPlaceholder': 'Tétel neve...', @@ -1807,6 +1813,9 @@ const hu: Record = { 'memories.providerUsername': 'Felhasználónév', 'memories.providerPassword': 'Jelszó', 'memories.saveError': 'Nem sikerült menteni a(z) {provider_name} beállításait', + 'memories.saveRouteNotConfigured': 'A mentési útvonal nincs konfigurálva ehhez a szolgáltatóhoz', + 'memories.testRouteNotConfigured': 'A tesztútvonal nincs konfigurálva ehhez a szolgáltatóhoz', + 'memories.fillRequiredFields': 'Kérjük töltse ki az összes kötelező mezőt', 'memories.selectAlbumMultiple': 'Album kiválasztása', 'memories.selectPhotosMultiple': 'Fotók kiválasztása', 'journey.title': 'Útinaplók', @@ -1976,6 +1985,10 @@ const hu: Record = { 'journey.settings.saveFailed': 'Nem sikerült menteni', 'journey.settings.coverUpdated': 'Borítókép frissítve', 'journey.settings.coverFailed': 'A feltöltés sikertelen', + 'journey.settings.failedToDelete': 'Törlés sikertelen', + 'journey.entries.deleteTitle': 'Bejegyzés törlése', + 'journey.photosUploaded': '{count} fotó feltöltve', + 'journey.photosAdded': '{count} fotó hozzáadva', 'journey.public.notFound': 'Nem található', 'journey.public.notFoundMessage': 'Ez az útinapló nem létezik vagy a link lejárt.', 'journey.public.readOnly': 'Csak olvasható · Nyilvános útinapló', diff --git a/client/src/i18n/translations/it.ts b/client/src/i18n/translations/it.ts index 2aa39f1b..dabaf43a 100644 --- a/client/src/i18n/translations/it.ts +++ b/client/src/i18n/translations/it.ts @@ -29,6 +29,12 @@ const it: Record = { 'common.password': 'Password', 'common.saving': 'Salvataggio...', 'common.saved': 'Salvato', + 'trips.memberRemoved': '{username} rimosso', + 'trips.memberRemoveError': 'Rimozione non riuscita', + 'trips.memberAdded': '{username} aggiunto', + 'trips.memberAddError': 'Aggiunta non riuscita', + 'common.expand': 'Espandi', + 'common.collapse': 'Comprimi', 'trips.reminder': 'Promemoria', 'trips.reminderNone': 'Nessuno', 'trips.reminderDay': 'giorno', @@ -937,6 +943,7 @@ const it: Record = { 'inspector.files': 'File', 'inspector.filesCount': '{count} file', 'inspector.removeFromDay': 'Rimuovi dal giorno', + 'inspector.remove': 'Rimuovi', 'inspector.addToDay': 'Aggiungi al giorno', 'inspector.confirmedRes': 'Prenotazione confermata', 'inspector.pendingRes': 'Prenotazione in attesa', @@ -1172,7 +1179,6 @@ const it: Record = { 'packing.menuCheckAll': 'Seleziona tutti', 'packing.menuUncheckAll': 'Deseleziona tutti', 'packing.menuDeleteCat': 'Elimina categoria', - 'packing.assignUser': 'Assegna utente', 'packing.noMembers': 'Nessun membro del viaggio', 'packing.addItem': 'Aggiungi elemento', 'packing.addItemPlaceholder': 'Nome elemento...', @@ -1499,6 +1505,9 @@ const it: Record = { 'memories.saved': 'Impostazioni {provider_name} salvate', 'memories.providerDisconnectedBanner': 'La connessione a {provider_name} è persa. Riconnetti nelle Impostazioni per visualizzare le foto.', 'memories.saveError': 'Impossibile salvare le impostazioni di {provider_name}', + 'memories.saveRouteNotConfigured': 'La route di salvataggio non è configurata per questo provider', + 'memories.testRouteNotConfigured': 'La route di test non è configurata per questo provider', + 'memories.fillRequiredFields': 'Per favore compila tutti i campi obbligatori', 'memories.addPhotos': 'Aggiungi foto', 'memories.linkAlbum': 'Collega album', 'memories.selectAlbum': 'Seleziona album Immich', @@ -1976,6 +1985,10 @@ const it: Record = { 'journey.settings.saveFailed': 'Salvataggio fallito', 'journey.settings.coverUpdated': 'Copertina aggiornata', 'journey.settings.coverFailed': 'Caricamento fallito', + 'journey.settings.failedToDelete': 'Eliminazione non riuscita', + 'journey.entries.deleteTitle': 'Elimina voce', + 'journey.photosUploaded': '{count} foto caricate', + 'journey.photosAdded': '{count} foto aggiunte', 'journey.public.notFound': 'Non trovato', 'journey.public.notFoundMessage': 'Questo diario non esiste o il link è scaduto.', 'journey.public.readOnly': 'Sola lettura · Diario pubblico', diff --git a/client/src/i18n/translations/nl.ts b/client/src/i18n/translations/nl.ts index 6c9ac879..3f077d72 100644 --- a/client/src/i18n/translations/nl.ts +++ b/client/src/i18n/translations/nl.ts @@ -29,6 +29,12 @@ const nl: Record = { 'common.password': 'Wachtwoord', 'common.saving': 'Opslaan...', 'common.saved': 'Opgeslagen', + 'trips.memberRemoved': '{username} verwijderd', + 'trips.memberRemoveError': 'Verwijderen mislukt', + 'trips.memberAdded': '{username} toegevoegd', + 'trips.memberAddError': 'Toevoegen mislukt', + 'common.expand': 'Uitvouwen', + 'common.collapse': 'Inklappen', 'trips.reminder': 'Herinnering', 'trips.reminderNone': 'Geen', 'trips.reminderDay': 'dag', @@ -936,6 +942,7 @@ const nl: Record = { 'inspector.files': 'Bestanden', 'inspector.filesCount': '{count} bestanden', 'inspector.removeFromDay': 'Verwijderen van dag', + 'inspector.remove': 'Verwijderen', 'inspector.addToDay': 'Toevoegen aan dag', 'inspector.confirmedRes': 'Bevestigde reservering', 'inspector.pendingRes': 'Reservering in behandeling', @@ -1171,7 +1178,6 @@ const nl: Record = { 'packing.menuCheckAll': 'Alles aanvinken', 'packing.menuUncheckAll': 'Alles uitvinken', 'packing.menuDeleteCat': 'Categorie verwijderen', - 'packing.assignUser': 'Gebruiker toewijzen', 'packing.addItem': 'Item toevoegen', 'packing.addItemPlaceholder': 'Itemnaam...', 'packing.addCategory': 'Categorie toevoegen', @@ -1498,6 +1504,9 @@ const nl: Record = { 'memories.saved': '{provider_name}-instellingen opgeslagen', 'memories.providerDisconnectedBanner': 'Je {provider_name}-verbinding is verbroken. Maak opnieuw verbinding in Instellingen om foto\'s te bekijken.', 'memories.saveError': '{provider_name}-instellingen konden niet worden opgeslagen', + 'memories.saveRouteNotConfigured': 'Opslagroute is niet geconfigureerd voor deze provider', + 'memories.testRouteNotConfigured': 'Testroute is niet geconfigureerd voor deze provider', + 'memories.fillRequiredFields': 'Vul alle verplichte velden in', 'memories.oldest': 'Oudste eerst', 'memories.newest': 'Nieuwste eerst', 'memories.allLocations': 'Alle locaties', @@ -1975,6 +1984,10 @@ const nl: Record = { 'journey.settings.saveFailed': 'Opslaan mislukt', 'journey.settings.coverUpdated': 'Omslag bijgewerkt', 'journey.settings.coverFailed': 'Uploaden mislukt', + 'journey.settings.failedToDelete': 'Verwijderen mislukt', + 'journey.entries.deleteTitle': 'Vermelding verwijderen', + 'journey.photosUploaded': "{count} foto's geüpload", + 'journey.photosAdded': "{count} foto's toegevoegd", 'journey.public.notFound': 'Niet gevonden', 'journey.public.notFoundMessage': 'Dit reisverslag bestaat niet of de link is verlopen.', 'journey.public.readOnly': 'Alleen-lezen · Openbaar reisverslag', diff --git a/client/src/i18n/translations/pl.ts b/client/src/i18n/translations/pl.ts index 9f508922..8d96eb66 100644 --- a/client/src/i18n/translations/pl.ts +++ b/client/src/i18n/translations/pl.ts @@ -27,6 +27,12 @@ const pl: Record = { 'common.email': 'E-mail', 'common.password': 'Hasło', 'common.saving': 'Zapisywanie...', + 'trips.memberRemoved': '{username} usunięty', + 'trips.memberRemoveError': 'Nie udało się usunąć', + 'trips.memberAdded': '{username} dodany', + 'trips.memberAddError': 'Nie udało się dodać', + 'common.expand': 'Rozwiń', + 'common.collapse': 'Zwiń', 'common.update': 'Aktualizuj', 'common.change': 'Zmień', 'common.uploading': 'Przesyłanie...', @@ -898,6 +904,7 @@ const pl: Record = { 'inspector.files': 'Pliki', 'inspector.filesCount': '{count} plików', 'inspector.removeFromDay': 'Usuń z dnia', + 'inspector.remove': 'Usuń', 'inspector.addToDay': 'Dodaj do dnia', 'inspector.confirmedRes': 'Potwierdzona rezerwacja', 'inspector.pendingRes': 'Oczekująca rezerwacja', @@ -1129,7 +1136,6 @@ const pl: Record = { 'packing.menuCheckAll': 'Zaznacz wszystko', 'packing.menuUncheckAll': 'Odznacz wszystko', 'packing.menuDeleteCat': 'Usuń kategorię', - 'packing.assignUser': 'Przypisz użytkownika', 'packing.saveAsTemplate': 'Zapisz jako szablon', 'packing.templateName': 'Nazwa szablonu', 'packing.templateSaved': 'Lista pakowania zapisana jako szablon', @@ -1802,6 +1808,9 @@ const pl: Record = { 'memories.providerUsername': 'Nazwa użytkownika', 'memories.providerPassword': 'Hasło', 'memories.saveError': 'Nie udało się zapisać ustawień {provider_name}', + 'memories.saveRouteNotConfigured': 'Trasa zapisu nie jest skonfigurowana dla tego dostawcy', + 'memories.testRouteNotConfigured': 'Trasa testowa nie jest skonfigurowana dla tego dostawcy', + 'memories.fillRequiredFields': 'Proszę wypełnić wszystkie wymagane pola', 'memories.selectAlbumMultiple': 'Wybierz album', 'memories.selectPhotosMultiple': 'Wybierz zdjęcia', 'journey.title': 'Dziennik podróży', @@ -1971,6 +1980,10 @@ const pl: Record = { 'journey.settings.saveFailed': 'Zapisywanie nie powiodło się', 'journey.settings.coverUpdated': 'Okładka zaktualizowana', 'journey.settings.coverFailed': 'Przesyłanie nie powiodło się', + 'journey.settings.failedToDelete': 'Nie udało się usunąć', + 'journey.entries.deleteTitle': 'Usuń wpis', + 'journey.photosUploaded': '{count} zdjęć przesłanych', + 'journey.photosAdded': '{count} zdjęć dodanych', 'journey.public.notFound': 'Nie znaleziono', 'journey.public.notFoundMessage': 'Ten dziennik podróży nie istnieje lub link wygasł.', 'journey.public.readOnly': 'Tylko do odczytu · Publiczny dziennik podróży', diff --git a/client/src/i18n/translations/ru.ts b/client/src/i18n/translations/ru.ts index 21f99ab2..8a88c721 100644 --- a/client/src/i18n/translations/ru.ts +++ b/client/src/i18n/translations/ru.ts @@ -29,6 +29,12 @@ const ru: Record = { 'common.password': 'Пароль', 'common.saving': 'Сохранение...', 'common.saved': 'Сохранено', + 'common.expand': 'Развернуть', + 'common.collapse': 'Свернуть', + 'trips.memberRemoved': '{username} удалён', + 'trips.memberRemoveError': 'Не удалось удалить', + 'trips.memberAdded': '{username} добавлен', + 'trips.memberAddError': 'Не удалось добавить', 'trips.reminder': 'Напоминание', 'trips.reminderNone': 'Нет', 'trips.reminderDay': 'день', @@ -936,6 +942,7 @@ const ru: Record = { 'inspector.files': 'Файлы', 'inspector.filesCount': '{count} файлов', 'inspector.removeFromDay': 'Убрать из дня', + 'inspector.remove': 'Удалить', 'inspector.addToDay': 'Добавить в день', 'inspector.confirmedRes': 'Подтверждённое бронирование', 'inspector.pendingRes': 'Ожидающее бронирование', @@ -1182,7 +1189,6 @@ const ru: Record = { 'packing.saveAsTemplate': 'Сохранить как шаблон', 'packing.templateName': 'Название шаблона', 'packing.templateSaved': 'Список вещей сохранён как шаблон', - 'packing.assignUser': 'Назначить пользователя', 'packing.noMembers': 'Нет участников', 'packing.bags': 'Багаж', 'packing.noBag': 'Не назначено', @@ -1806,6 +1812,9 @@ const ru: Record = { 'memories.providerUsername': 'Имя пользователя', 'memories.providerPassword': 'Пароль', 'memories.saveError': 'Не удалось сохранить настройки {provider_name}', + 'memories.saveRouteNotConfigured': 'Маршрут сохранения не настроен для этого провайдера', + 'memories.testRouteNotConfigured': 'Маршрут тестирования не настроен для этого провайдера', + 'memories.fillRequiredFields': 'Пожалуйста, заполните все обязательные поля', 'memories.selectAlbumMultiple': 'Выбрать альбом', 'memories.selectPhotosMultiple': 'Выбрать фото', 'journey.title': 'Путешествие', @@ -1975,6 +1984,10 @@ const ru: Record = { 'journey.settings.saveFailed': 'Не удалось сохранить', 'journey.settings.coverUpdated': 'Обложка обновлена', 'journey.settings.coverFailed': 'Загрузка не удалась', + 'journey.settings.failedToDelete': 'Не удалось удалить', + 'journey.entries.deleteTitle': 'Удалить запись', + 'journey.photosUploaded': '{count} фото загружено', + 'journey.photosAdded': '{count} фото добавлено', 'journey.public.notFound': 'Не найдено', 'journey.public.notFoundMessage': 'Это путешествие не существует или ссылка устарела.', 'journey.public.readOnly': 'Только для чтения · Публичное путешествие', diff --git a/client/src/i18n/translations/zh.ts b/client/src/i18n/translations/zh.ts index ca4878e8..93ad9772 100644 --- a/client/src/i18n/translations/zh.ts +++ b/client/src/i18n/translations/zh.ts @@ -29,6 +29,12 @@ const zh: Record = { 'common.password': '密码', 'common.saving': '保存中...', 'common.saved': '已保存', + 'common.expand': '展开', + 'common.collapse': '折叠', + 'trips.memberRemoved': '{username} 已移除', + 'trips.memberRemoveError': '移除失败', + 'trips.memberAdded': '{username} 已添加', + 'trips.memberAddError': '添加失败', 'trips.reminder': '提醒', 'trips.reminderNone': '无', 'trips.reminderDay': '天', @@ -936,6 +942,7 @@ const zh: Record = { 'inspector.files': '文件', 'inspector.filesCount': '{count} 个文件', 'inspector.removeFromDay': '从当天移除', + 'inspector.remove': '删除', 'inspector.addToDay': '添加到当天', 'inspector.confirmedRes': '已确认预订', 'inspector.pendingRes': '待确认预订', @@ -1182,7 +1189,6 @@ const zh: Record = { 'packing.saveAsTemplate': '保存为模板', 'packing.templateName': '模板名称', 'packing.templateSaved': '行李清单已保存为模板', - 'packing.assignUser': '分配用户', 'packing.noMembers': '无成员', 'packing.bags': '行李', 'packing.noBag': '未分配', @@ -1806,6 +1812,9 @@ const zh: Record = { 'memories.providerUsername': '用户名', 'memories.providerPassword': '密码', 'memories.saveError': '无法保存 {provider_name} 设置', + 'memories.saveRouteNotConfigured': '此提供商未配置保存路由', + 'memories.testRouteNotConfigured': '此提供商未配置测试路由', + 'memories.fillRequiredFields': '请填写所有必填字段', 'memories.selectAlbumMultiple': '选择相册', 'memories.selectPhotosMultiple': '选择照片', 'journey.title': '旅程', @@ -1975,6 +1984,10 @@ const zh: Record = { 'journey.settings.saveFailed': '保存失败', 'journey.settings.coverUpdated': '封面已更新', 'journey.settings.coverFailed': '上传失败', + 'journey.settings.failedToDelete': '删除失败', + 'journey.entries.deleteTitle': '删除条目', + 'journey.photosUploaded': '{count} 张照片已上传', + 'journey.photosAdded': '{count} 张照片已添加', 'journey.public.notFound': '未找到', 'journey.public.notFoundMessage': '此旅程不存在或链接已过期。', 'journey.public.readOnly': '只读 · 公开旅程', diff --git a/client/src/i18n/translations/zhTw.ts b/client/src/i18n/translations/zhTw.ts index 4f8bbad5..49e38eba 100644 --- a/client/src/i18n/translations/zhTw.ts +++ b/client/src/i18n/translations/zhTw.ts @@ -29,6 +29,12 @@ const zhTw: Record = { 'common.password': '密碼', 'common.saving': '儲存中...', 'common.saved': '已儲存', + 'common.expand': '展開', + 'common.collapse': '折疊', + 'trips.memberRemoved': '{username} 已移除', + 'trips.memberRemoveError': '移除失敗', + 'trips.memberAdded': '{username} 已新增', + 'trips.memberAddError': '新增失敗', 'trips.reminder': '提醒', 'trips.reminderNone': '無', 'trips.reminderDay': '天', @@ -963,6 +969,7 @@ const zhTw: Record = { 'inspector.files': '檔案', 'inspector.filesCount': '{count} 個檔案', 'inspector.removeFromDay': '從當天移除', + 'inspector.remove': '刪除', 'inspector.addToDay': '新增到當天', 'inspector.confirmedRes': '已確認預訂', 'inspector.pendingRes': '待確認預訂', @@ -1197,7 +1204,6 @@ const zhTw: Record = { 'packing.menuCheckAll': '全部勾選', 'packing.menuUncheckAll': '取消全部勾選', 'packing.menuDeleteCat': '刪除分類', - 'packing.assignUser': '指派使用者', 'packing.addItem': '新增物品', 'packing.addItemPlaceholder': '物品名稱...', 'packing.addCategory': '新增分類', @@ -1767,6 +1773,9 @@ const zhTw: Record = { 'memories.providerUsername': '使用者名稱', 'memories.providerPassword': '密碼', 'memories.saveError': '無法儲存 {provider_name} 設定', + 'memories.saveRouteNotConfigured': '此提供商未設定儲存路由', + 'memories.testRouteNotConfigured': '此提供商未設定測試路由', + 'memories.fillRequiredFields': '請填寫所有必填欄位', 'memories.selectAlbumMultiple': '選擇相簿', 'memories.selectPhotosMultiple': '選擇照片', 'journey.title': '旅程', @@ -1936,6 +1945,10 @@ const zhTw: Record = { 'journey.settings.saveFailed': '儲存失敗', 'journey.settings.coverUpdated': '封面已更新', 'journey.settings.coverFailed': '上傳失敗', + 'journey.settings.failedToDelete': '刪除失敗', + 'journey.entries.deleteTitle': '刪除條目', + 'journey.photosUploaded': '{count} 張照片已上傳', + 'journey.photosAdded': '{count} 張照片已新增', 'journey.public.notFound': '未找到', 'journey.public.notFoundMessage': '此旅程不存在或連結已過期。', 'journey.public.readOnly': '唯讀 · 公開旅程', diff --git a/client/src/pages/JourneyDetailPage.tsx b/client/src/pages/JourneyDetailPage.tsx index 684d59e7..ba50255f 100644 --- a/client/src/pages/JourneyDetailPage.tsx +++ b/client/src/pages/JourneyDetailPage.tsx @@ -559,9 +559,9 @@ export default function JourneyDetailPage() { setDeleteTarget(null) loadJourney(Number(id)) }} - title="Delete Entry" - message={`Delete "${deleteTarget?.title || 'this entry'}"? This cannot be undone.`} - confirmLabel="Delete" + title={t('journey.entries.deleteTitle')} + message={t('journey.deleteConfirmMessage', { title: deleteTarget?.title || 'this entry' })} + confirmLabel={t('common.delete')} danger /> @@ -580,9 +580,9 @@ export default function JourneyDetailPage() { toast.error(t('journey.trips.unlinkFailed')) } }} - title="Unlink Trip" - message={`Unlink "${unlinkTrip?.title}"? All synced entries and photos from this trip will be permanently deleted. This cannot be undone.`} - confirmLabel="Unlink" + title={t('journey.trips.unlinkTrip')} + message={t('journey.trips.unlinkMessage', { title: unlinkTrip?.title })} + confirmLabel={t('journey.trips.unlink')} danger /> @@ -807,7 +807,7 @@ function GalleryView({ entries, journeyId, userId, trips, onPhotoClick, onRefres for (const f of files) formData.append('photos', f) try { await journeyApi.uploadPhotos(entryId, formData) - toast.success(`${files.length} photos uploaded`) + toast.success(t('journey.photosUploaded', { count: files.length })) onRefresh() } catch { toast.error(t('journey.settings.coverFailed')) @@ -934,7 +934,7 @@ function GalleryView({ entries, journeyId, userId, trips, onPhotoClick, onRefres } catch {} } if (added > 0) { - toast.success(`${added} photos added`) + toast.success(t('journey.photosAdded', { count: added })) onRefresh() } setShowPicker(false) @@ -1175,8 +1175,8 @@ function EntryCard({ entry, onEdit, onDelete, onPhotoClick }: { <>
setMenuOpen(false)} />
- - + +
)} @@ -2173,7 +2173,7 @@ function EntryEditor({ entry, journeyId, tripDates, galleryPhotos, onClose, onSa
@@ -2542,7 +2542,7 @@ function JourneySettingsDialog({ journey, onClose, onSaved, onOpenInvite }: { await updateJourney(journey.id, { title, subtitle: subtitle || null }) onSaved() } catch { - toast.error('Failed to save') + toast.error(t('journey.settings.saveFailed')) } finally { setSaving(false) } @@ -2555,10 +2555,10 @@ function JourneySettingsDialog({ journey, onClose, onSaved, onOpenInvite }: { formData.append('cover', file) try { await journeyApi.uploadCover(journey.id, formData) - toast.success('Cover updated') + toast.success(t('journey.settings.coverUpdated')) onSaved() } catch { - toast.error('Upload failed') + toast.error(t('journey.settings.coverFailed')) } } @@ -2569,7 +2569,7 @@ function JourneySettingsDialog({ journey, onClose, onSaved, onOpenInvite }: { await deleteJourney(journey.id) navigate('/journey') } catch { - toast.error('Failed to delete') + toast.error(t('journey.settings.failedToDelete')) } } @@ -2629,14 +2629,14 @@ function JourneySettingsDialog({ journey, onClose, onSaved, onOpenInvite }: { {/* Synced Trips */}
- +
{journey.trips.map((trip: any) => (
{trip.title}
-
{trip.place_count || 0} places
+
{trip.place_count || 0} {t('journey.synced.places')}
))} - {journey.trips.length === 0 &&

No trips linked

} + {journey.trips.length === 0 &&

{t('journey.trips.noTripsLinkedSettings')}

}
{/* Contributors */}
- +
{journey.contributors.map((c: any) => (
@@ -2674,7 +2674,7 @@ function JourneySettingsDialog({ journey, onClose, onSaved, onOpenInvite }: { onClick={onOpenInvite} className="w-full mt-1 flex items-center justify-center gap-1.5 py-2.5 rounded-lg border border-dashed border-zinc-300 dark:border-zinc-600 text-[12px] font-medium text-zinc-500 hover:border-zinc-400 hover:text-zinc-700 dark:hover:border-zinc-500 dark:hover:text-zinc-300 transition-colors" > - Invite Contributor + {t('journey.contributors.invite')}
@@ -2693,11 +2693,11 @@ function JourneySettingsDialog({ journey, onClose, onSaved, onOpenInvite }: { className="flex items-center gap-1.5 text-[12px] font-medium text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/20 rounded-lg px-2.5 py-2 mr-auto" > - Delete + {t('journey.settings.delete')}
@@ -2710,16 +2710,16 @@ function JourneySettingsDialog({ journey, onClose, onSaved, onOpenInvite }: { if (!unlinkTarget) return try { await journeyApi.removeTrip(journey.id, unlinkTarget.trip_id) - toast.success('Trip unlinked') + toast.success(t('journey.trips.tripUnlinked')) setUnlinkTarget(null) onSaved() } catch { - toast.error('Failed to unlink trip') + toast.error(t('journey.trips.unlinkFailed')) } }} - title="Unlink Trip" - message={`Unlink "${unlinkTarget?.title}"? All synced entries and photos from this trip will be permanently deleted. This cannot be undone.`} - confirmLabel="Unlink" + title={t('journey.trips.unlinkTrip')} + message={t('journey.trips.unlinkMessage', { title: unlinkTarget?.title })} + confirmLabel={t('journey.trips.unlink')} danger /> @@ -2737,9 +2737,9 @@ function JourneySettingsDialog({ journey, onClose, onSaved, onOpenInvite }: { isOpen={showDeleteConfirm} onClose={() => setShowDeleteConfirm(false)} onConfirm={handleDelete} - title="Delete Journey" - message={`Delete "${journey.title}"? All entries and photos will be lost.`} - confirmLabel="Delete" + title={t('journey.settings.deleteJourney')} + message={t('journey.settings.deleteMessage', { title: journey.title })} + confirmLabel={t('common.delete')} danger />