From 2829e1adf16c27effa8dd5c4a44d799f42fd646c Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 19 Mar 2026 12:39:36 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20implement=20i18n=20=E2=80=94=20extract?= =?UTF-8?q?=20all=20UI=20strings,=20add=20EN=20+=20ES-MX=20translations,?= =?UTF-8?q?=20add=20language=20selector?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract all hardcoded German strings from every .svelte file and component into Paraglide message keys. Add complete translations for all keys in messages/en.json (English) and messages/es.json (Spanish/Mexico). Changes: - messages/de.json: 100+ keys covering navigation, buttons, form labels, placeholders, section headings, empty states, and error messages - messages/en.json, messages/es.json: complete translations for all keys - project.inlang/settings.json: change baseLocale from "en" to "de" - +layout.svelte: add DE/EN/ES language selector in header using setLocale(); active language is bold, choice persists via Paraglide cookie strategy - All 10 route pages + 3 shared components: replace hardcoded German with m.key() - e2e/lang.spec.ts: E2E tests for language selector visibility, switching, persistence across navigation, and active state highlighting Closes #2 Co-Authored-By: Claude Sonnet 4.6 --- frontend/e2e/lang.spec.ts | 37 ++++ frontend/messages/de.json | 158 +++++++++++++++++- frontend/messages/en.json | 158 +++++++++++++++++- frontend/messages/es.json | 158 +++++++++++++++++- frontend/project.inlang/settings.json | 6 +- .../lib/components/PersonMultiSelect.svelte | 7 +- .../src/lib/components/PersonTypeahead.svelte | 5 +- frontend/src/lib/components/TagInput.svelte | 10 +- frontend/src/routes/+layout.svelte | 34 +++- frontend/src/routes/+page.svelte | 33 ++-- frontend/src/routes/admin/+page.svelte | 81 +++++---- .../src/routes/conversations/+page.svelte | 25 +-- .../src/routes/demo/paraglide/+page.svelte | 2 +- .../src/routes/documents/[id]/+page.svelte | 38 +++-- .../routes/documents/[id]/edit/+page.svelte | 51 +++--- .../src/routes/documents/new/+page.svelte | 49 +++--- frontend/src/routes/login/+page.svelte | 9 +- frontend/src/routes/persons/+page.svelte | 13 +- frontend/src/routes/persons/[id]/+page.svelte | 39 ++--- frontend/src/routes/persons/new/+page.svelte | 19 ++- 20 files changed, 733 insertions(+), 199 deletions(-) create mode 100644 frontend/e2e/lang.spec.ts diff --git a/frontend/e2e/lang.spec.ts b/frontend/e2e/lang.spec.ts new file mode 100644 index 00000000..0e3edf9f --- /dev/null +++ b/frontend/e2e/lang.spec.ts @@ -0,0 +1,37 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Language selector', () => { + test('shows DE, EN, ES buttons in the header', async ({ page }) => { + await page.goto('/'); + await expect(page.getByRole('banner').getByRole('button', { name: 'DE' })).toBeVisible(); + await expect(page.getByRole('banner').getByRole('button', { name: 'EN' })).toBeVisible(); + await expect(page.getByRole('banner').getByRole('button', { name: 'ES' })).toBeVisible(); + }); + + test('switching to EN translates the navigation', async ({ page }) => { + await page.goto('/'); + await page.getByRole('banner').getByRole('button', { name: 'EN' }).click(); + await expect(page.getByRole('navigation').getByRole('link', { name: 'Documents' })).toBeVisible(); + await expect(page.getByRole('navigation').getByRole('link', { name: 'Persons' })).toBeVisible(); + }); + + test('language choice persists after navigation', async ({ page }) => { + await page.goto('/'); + await page.getByRole('banner').getByRole('button', { name: 'EN' }).click(); + await page.goto('/persons'); + await expect(page.getByRole('navigation').getByRole('link', { name: 'Documents' })).toBeVisible(); + }); + + test('switching back to DE restores German', async ({ page }) => { + await page.goto('/'); + await page.getByRole('banner').getByRole('button', { name: 'EN' }).click(); + await page.getByRole('banner').getByRole('button', { name: 'DE' }).click(); + await expect(page.getByRole('navigation').getByRole('link', { name: 'Dokumente' })).toBeVisible(); + }); + + test('active language button is visually highlighted', async ({ page }) => { + await page.goto('/'); + const deBtn = page.getByRole('banner').getByRole('button', { name: 'DE' }); + await expect(deBtn).toHaveClass(/font-bold/); + }); +}); diff --git a/frontend/messages/de.json b/frontend/messages/de.json index d184227c..32ed938d 100644 --- a/frontend/messages/de.json +++ b/frontend/messages/de.json @@ -1,6 +1,6 @@ { "$schema": "https://inlang.com/schema/inlang-message-format", - "hello_world": "Hello, {name} from de!", + "error_document_not_found": "Das Dokument wurde nicht gefunden.", "error_document_no_file": "Diesem Dokument ist noch keine Datei zugeordnet.", "error_file_not_found": "Die Datei konnte im Speicher nicht gefunden werden.", @@ -10,5 +10,159 @@ "error_unauthorized": "Sie sind nicht angemeldet.", "error_forbidden": "Sie haben keine Berechtigung für diese Aktion.", "error_validation_error": "Die Eingabe ist ungültig.", - "error_internal_error": "Ein unerwarteter Fehler ist aufgetreten." + "error_internal_error": "Ein unerwarteter Fehler ist aufgetreten.", + + "nav_documents": "Dokumente", + "nav_persons": "Personen", + "nav_conversations": "Konversationen", + "nav_admin": "Admin", + "nav_logout": "Abmelden", + + "btn_save": "Speichern", + "btn_cancel": "Abbrechen", + "btn_edit": "Bearbeiten", + "btn_create": "Erstellen", + "btn_delete": "Löschen", + "btn_back_to_overview": "Zurück zur Übersicht", + "btn_back": "Zurück", + "btn_back_to_document": "Zurück zum Dokument", + + "form_label_first_name": "Vorname", + "form_label_last_name": "Nachname", + "form_label_alias": "Rufname / Alias", + "form_placeholder_alias": "z.B. Oma Frieda, Onkel Karl…", + "form_label_date": "Datum", + "form_placeholder_date": "TT.MM.JJJJ", + "form_date_error": "Bitte im Format TT.MM.JJJJ eingeben, z.B. 20.12.2026", + "form_label_location": "Ort", + "form_placeholder_location": "z.B. Berlin, Wien…", + "form_label_sender": "Absender", + "form_label_receivers": "Empfänger", + "form_label_title": "Titel *", + "form_label_tags": "Schlagworte", + "form_label_content": "Inhalt", + "form_placeholder_content": "Kurze Beschreibung des Inhalts…", + "form_label_transcription": "Transkription", + "form_placeholder_transcription": "Vollständiger Text des Dokuments…", + "form_label_archive_location": "Aufbewahrungsort", + "form_placeholder_archive_location": "z.B. Schrank 3, Mappe B", + "form_helper_archive_location": "Wo befindet sich das Originaldokument?", + + "login_heading": "Anmelden", + "login_label_username": "Benutzername", + "login_label_password": "Passwort", + "login_btn_submit": "Anmelden", + + "docs_search_placeholder": "Suche in Titel, Inhalt, Ort...", + "docs_btn_filter": "Filter", + "docs_btn_reset_title": "Filter zurücksetzen", + "docs_filter_label_tags": "Schlagworte", + "docs_filter_label_sender": "Absender", + "docs_filter_label_receivers": "Empfänger", + "docs_filter_label_from": "Von", + "docs_filter_label_to": "Bis", + "docs_btn_new": "Neues Dokument", + "docs_empty_heading": "Keine Dokumente gefunden", + "docs_empty_text": "Versuchen Sie, die Filter anzupassen oder den Suchbegriff zu ändern.", + "docs_empty_btn_clear": "Alle Filter löschen", + "docs_list_from": "Von", + "docs_list_to": "An", + "docs_list_unknown": "Unbekannt", + + "doc_section_who_when": "Wer & Wann", + "doc_section_description": "Beschreibung", + "doc_section_file": "Datei", + "doc_file_upload_label": "Datei hochladen", + "doc_file_upload_note": "(optional)", + "doc_file_replace_label": "Neue Datei hochladen", + "doc_file_replace_note": "(ersetzt die aktuelle Datei)", + "doc_current_file_label": "Aktuelle Datei:", + "doc_new_heading": "Neues Dokument", + "doc_edit_heading": "Bearbeiten", + + "doc_section_details": "Details", + "doc_label_document_date": "Dokumentendatum", + "doc_label_creation_location": "Erstellungsort", + "doc_label_archive_location_original": "Aufbewahrungsort (Original)", + "doc_section_persons": "Personen", + "doc_sender_not_specified": "Nicht angegeben", + "doc_no_receivers": "Keine Empfänger", + "doc_section_content": "Inhalt", + "doc_label_summary": "Zusammenfassung", + "doc_loading": "Lade Dokument...", + "doc_download_link": "Direkter Download versuchen", + "doc_no_scan": "Kein Scan vorhanden", + + "persons_heading": "Personenverzeichnis", + "persons_subtitle": "Durchsuchen Sie den Index aller erfassten Personen im Familienarchiv.", + "persons_btn_new": "Neue Person", + "persons_search_placeholder": "Namen suchen...", + "persons_empty_heading": "Keine Personen gefunden.", + "persons_empty_text": "Versuchen Sie einen anderen Suchbegriff.", + + "persons_new_heading": "Neue Person", + "persons_section_details": "Angaben zur Person", + + "person_edit_heading": "Person bearbeiten", + "person_label_full_name": "Voller Name", + "person_merge_heading": "Person zusammenführen", + "person_merge_description": "Diese Person wird in die gewählte Zielperson überführt. Alle Dokumente und Verknüpfungen werden übertragen, danach wird diese Person gelöscht.", + "person_merge_target_label": "Zusammenführen mit", + "person_btn_merge": "Zusammenführen", + "person_btn_merge_confirm": "Ja, zusammenführen", + "person_merge_warning": "Achtung: Diese Aktion ist nicht rückgängig zu machen.", + "person_docs_heading": "Gesendete Dokumente", + "person_no_docs": "Diese Person ist noch nicht als Absender verknüpft.", + + "conv_heading": "Konversationen", + "conv_subtitle": "Verfolgen Sie den Schriftverkehr zwischen zwei Personen chronologisch.", + "conv_label_person_a": "Person A (Absender)", + "conv_label_person_b": "Person B (Empfänger)", + "conv_label_from": "Zeitraum von", + "conv_label_to": "Zeitraum bis", + "conv_sort_label": "Sortierung:", + "conv_sort_newest": "Neueste zuerst", + "conv_sort_oldest": "Älteste zuerst", + "conv_empty_heading": "Wählen Sie zwei Personen aus", + "conv_empty_text": "Die Korrespondenz wird hier angezeigt.", + "conv_no_results_heading": "Keine Dokumente gefunden.", + "conv_no_results_text": "Versuchen Sie, den Zeitraum anzupassen.", + + "admin_heading": "Admin Dashboard", + "admin_tab_users": "Benutzer", + "admin_tab_groups": "Gruppen", + "admin_tab_tags": "Schlagworte", + "admin_section_users": "Benutzerverwaltung", + "admin_col_login": "Login", + "admin_col_groups": "Gruppen", + "admin_col_password": "Passwort", + "admin_multiselect_hint": "Strg+Klick für Auswahl", + "admin_password_placeholder": "Neues PW (optional)", + "admin_no_groups": "Keine Gruppen", + "admin_btn_delete_user_title": "Benutzer löschen", + "admin_section_new_user": "Neuen Benutzer anlegen", + "admin_multiselect_hint_multi": "Strg+Klick für mehrere", + "admin_multiselect_hint_full": "Strg+Klick für Mehrfachauswahl", + "admin_section_tags": "Schlagworte", + "admin_tags_warning": "Warnung: Umbenennen oder Löschen wirkt sich auf alle verknüpften Dokumente aus.", + "admin_btn_edit_tag_label": "Schlagwort bearbeiten", + "admin_tag_delete_confirm": "Wirklich löschen? Das Schlagwort wird aus allen Dokumenten entfernt.", + "admin_btn_delete_tag_label": "Schlagwort löschen", + "admin_section_groups": "Gruppenverwaltung", + "admin_col_name": "Name", + "admin_col_permissions": "Berechtigungen", + "admin_col_actions": "Aktionen", + "admin_group_delete_confirm": "Gruppe wirklich löschen?", + "admin_section_new_group": "Neue Gruppe anlegen", + "admin_group_name_placeholder": "Gruppenname (z.B. Editoren)", + + "comp_typeahead_placeholder": "Namen tippen...", + "comp_typeahead_loading": "Suche...", + "comp_multiselect_placeholder": "Namen tippen...", + "comp_multiselect_remove": "Entfernen", + "comp_multiselect_loading": "Suche...", + "comp_taginput_placeholder_create": "Schlagworte hinzufügen...", + "comp_taginput_placeholder_filter": "Nach Schlagworten filtern...", + "comp_taginput_remove": "Schlagwort entfernen", + "comp_taginput_create_hint": "Enter drücken um Schlagwort zu erstellen." } diff --git a/frontend/messages/en.json b/frontend/messages/en.json index d233a202..73c6ffca 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -1,6 +1,6 @@ { "$schema": "https://inlang.com/schema/inlang-message-format", - "hello_world": "Hello, {name} from en!", + "error_document_not_found": "Document not found.", "error_document_no_file": "No file is associated with this document.", "error_file_not_found": "The file could not be found in storage.", @@ -10,5 +10,159 @@ "error_unauthorized": "You are not logged in.", "error_forbidden": "You do not have permission for this action.", "error_validation_error": "The input is invalid.", - "error_internal_error": "An unexpected error occurred." + "error_internal_error": "An unexpected error occurred.", + + "nav_documents": "Documents", + "nav_persons": "Persons", + "nav_conversations": "Conversations", + "nav_admin": "Admin", + "nav_logout": "Sign out", + + "btn_save": "Save", + "btn_cancel": "Cancel", + "btn_edit": "Edit", + "btn_create": "Create", + "btn_delete": "Delete", + "btn_back_to_overview": "Back to overview", + "btn_back": "Back", + "btn_back_to_document": "Back to document", + + "form_label_first_name": "First name", + "form_label_last_name": "Last name", + "form_label_alias": "Nickname / Alias", + "form_placeholder_alias": "e.g. Grandma Frieda, Uncle Karl…", + "form_label_date": "Date", + "form_placeholder_date": "DD.MM.YYYY", + "form_date_error": "Please enter in DD.MM.YYYY format, e.g. 20.12.2026", + "form_label_location": "Location", + "form_placeholder_location": "e.g. Berlin, Vienna…", + "form_label_sender": "Sender", + "form_label_receivers": "Recipients", + "form_label_title": "Title *", + "form_label_tags": "Tags", + "form_label_content": "Content", + "form_placeholder_content": "Brief description of the content…", + "form_label_transcription": "Transcription", + "form_placeholder_transcription": "Full text of the document…", + "form_label_archive_location": "Storage location", + "form_placeholder_archive_location": "e.g. Cabinet 3, Folder B", + "form_helper_archive_location": "Where is the original document stored?", + + "login_heading": "Sign in", + "login_label_username": "Username", + "login_label_password": "Password", + "login_btn_submit": "Sign in", + + "docs_search_placeholder": "Search in title, content, location...", + "docs_btn_filter": "Filter", + "docs_btn_reset_title": "Reset filter", + "docs_filter_label_tags": "Tags", + "docs_filter_label_sender": "Sender", + "docs_filter_label_receivers": "Recipients", + "docs_filter_label_from": "From", + "docs_filter_label_to": "To", + "docs_btn_new": "New document", + "docs_empty_heading": "No documents found", + "docs_empty_text": "Try adjusting the filters or changing the search term.", + "docs_empty_btn_clear": "Clear all filters", + "docs_list_from": "From", + "docs_list_to": "To", + "docs_list_unknown": "Unknown", + + "doc_section_who_when": "Who & When", + "doc_section_description": "Description", + "doc_section_file": "File", + "doc_file_upload_label": "Upload file", + "doc_file_upload_note": "(optional)", + "doc_file_replace_label": "Upload new file", + "doc_file_replace_note": "(replaces the current file)", + "doc_current_file_label": "Current file:", + "doc_new_heading": "New document", + "doc_edit_heading": "Edit", + + "doc_section_details": "Details", + "doc_label_document_date": "Document date", + "doc_label_creation_location": "Place of creation", + "doc_label_archive_location_original": "Storage location (original)", + "doc_section_persons": "Persons", + "doc_sender_not_specified": "Not specified", + "doc_no_receivers": "No recipients", + "doc_section_content": "Content", + "doc_label_summary": "Summary", + "doc_loading": "Loading document...", + "doc_download_link": "Try direct download", + "doc_no_scan": "No scan available", + + "persons_heading": "Person directory", + "persons_subtitle": "Browse the index of all recorded persons in the family archive.", + "persons_btn_new": "New person", + "persons_search_placeholder": "Search names...", + "persons_empty_heading": "No persons found.", + "persons_empty_text": "Try a different search term.", + + "persons_new_heading": "New person", + "persons_section_details": "Person details", + + "person_edit_heading": "Edit person", + "person_label_full_name": "Full name", + "person_merge_heading": "Merge person", + "person_merge_description": "This person will be merged into the selected target person. All documents and links will be transferred, then this person will be deleted.", + "person_merge_target_label": "Merge with", + "person_btn_merge": "Merge", + "person_btn_merge_confirm": "Yes, merge", + "person_merge_warning": "Warning: This action cannot be undone.", + "person_docs_heading": "Sent documents", + "person_no_docs": "This person has not yet been linked as a sender.", + + "conv_heading": "Conversations", + "conv_subtitle": "Follow the correspondence between two persons chronologically.", + "conv_label_person_a": "Person A (Sender)", + "conv_label_person_b": "Person B (Recipient)", + "conv_label_from": "Period from", + "conv_label_to": "Period to", + "conv_sort_label": "Sort:", + "conv_sort_newest": "Newest first", + "conv_sort_oldest": "Oldest first", + "conv_empty_heading": "Select two persons", + "conv_empty_text": "The correspondence will be shown here.", + "conv_no_results_heading": "No documents found.", + "conv_no_results_text": "Try adjusting the time period.", + + "admin_heading": "Admin Dashboard", + "admin_tab_users": "Users", + "admin_tab_groups": "Groups", + "admin_tab_tags": "Tags", + "admin_section_users": "User management", + "admin_col_login": "Login", + "admin_col_groups": "Groups", + "admin_col_password": "Password", + "admin_multiselect_hint": "Ctrl+Click to select", + "admin_password_placeholder": "New PW (optional)", + "admin_no_groups": "No groups", + "admin_btn_delete_user_title": "Delete user", + "admin_section_new_user": "Create new user", + "admin_multiselect_hint_multi": "Ctrl+Click for multiple", + "admin_multiselect_hint_full": "Ctrl+Click for multiple selection", + "admin_section_tags": "Tags", + "admin_tags_warning": "Warning: Renaming or deleting affects all linked documents.", + "admin_btn_edit_tag_label": "Edit tag", + "admin_tag_delete_confirm": "Really delete? The tag will be removed from all documents.", + "admin_btn_delete_tag_label": "Delete tag", + "admin_section_groups": "Group management", + "admin_col_name": "Name", + "admin_col_permissions": "Permissions", + "admin_col_actions": "Actions", + "admin_group_delete_confirm": "Really delete group?", + "admin_section_new_group": "Create new group", + "admin_group_name_placeholder": "Group name (e.g. Editors)", + + "comp_typeahead_placeholder": "Type a name...", + "comp_typeahead_loading": "Searching...", + "comp_multiselect_placeholder": "Type a name...", + "comp_multiselect_remove": "Remove", + "comp_multiselect_loading": "Searching...", + "comp_taginput_placeholder_create": "Add tags...", + "comp_taginput_placeholder_filter": "Filter by tags...", + "comp_taginput_remove": "Remove tag", + "comp_taginput_create_hint": "Press Enter to create tag." } diff --git a/frontend/messages/es.json b/frontend/messages/es.json index aa3841d9..616861e6 100644 --- a/frontend/messages/es.json +++ b/frontend/messages/es.json @@ -1,6 +1,6 @@ { "$schema": "https://inlang.com/schema/inlang-message-format", - "hello_world": "Hello, {name} from es!", + "error_document_not_found": "Documento no encontrado.", "error_document_no_file": "No hay ningún archivo asociado a este documento.", "error_file_not_found": "El archivo no pudo encontrarse en el almacenamiento.", @@ -10,5 +10,159 @@ "error_unauthorized": "No ha iniciado sesión.", "error_forbidden": "No tiene permiso para realizar esta acción.", "error_validation_error": "La entrada no es válida.", - "error_internal_error": "Se ha producido un error inesperado." + "error_internal_error": "Se ha producido un error inesperado.", + + "nav_documents": "Documentos", + "nav_persons": "Personas", + "nav_conversations": "Conversaciones", + "nav_admin": "Admin", + "nav_logout": "Cerrar sesión", + + "btn_save": "Guardar", + "btn_cancel": "Cancelar", + "btn_edit": "Editar", + "btn_create": "Crear", + "btn_delete": "Eliminar", + "btn_back_to_overview": "Volver al resumen", + "btn_back": "Volver", + "btn_back_to_document": "Volver al documento", + + "form_label_first_name": "Nombre", + "form_label_last_name": "Apellido", + "form_label_alias": "Apodo / Alias", + "form_placeholder_alias": "p.ej. Abuela Frieda, Tío Karl…", + "form_label_date": "Fecha", + "form_placeholder_date": "DD.MM.AAAA", + "form_date_error": "Introduzca en formato DD.MM.AAAA, p.ej. 20.12.2026", + "form_label_location": "Lugar", + "form_placeholder_location": "p.ej. Berlín, Viena…", + "form_label_sender": "Remitente", + "form_label_receivers": "Destinatarios", + "form_label_title": "Título *", + "form_label_tags": "Etiquetas", + "form_label_content": "Contenido", + "form_placeholder_content": "Breve descripción del contenido…", + "form_label_transcription": "Transcripción", + "form_placeholder_transcription": "Texto completo del documento…", + "form_label_archive_location": "Ubicación de almacenamiento", + "form_placeholder_archive_location": "p.ej. Armario 3, Carpeta B", + "form_helper_archive_location": "¿Dónde se encuentra el documento original?", + + "login_heading": "Iniciar sesión", + "login_label_username": "Usuario", + "login_label_password": "Contraseña", + "login_btn_submit": "Iniciar sesión", + + "docs_search_placeholder": "Buscar en título, contenido, lugar...", + "docs_btn_filter": "Filtrar", + "docs_btn_reset_title": "Restablecer filtro", + "docs_filter_label_tags": "Etiquetas", + "docs_filter_label_sender": "Remitente", + "docs_filter_label_receivers": "Destinatarios", + "docs_filter_label_from": "Desde", + "docs_filter_label_to": "Hasta", + "docs_btn_new": "Nuevo documento", + "docs_empty_heading": "No se encontraron documentos", + "docs_empty_text": "Intente ajustar los filtros o cambiar el término de búsqueda.", + "docs_empty_btn_clear": "Borrar todos los filtros", + "docs_list_from": "De", + "docs_list_to": "Para", + "docs_list_unknown": "Desconocido", + + "doc_section_who_when": "Quién & Cuándo", + "doc_section_description": "Descripción", + "doc_section_file": "Archivo", + "doc_file_upload_label": "Subir archivo", + "doc_file_upload_note": "(opcional)", + "doc_file_replace_label": "Subir nuevo archivo", + "doc_file_replace_note": "(reemplaza el archivo actual)", + "doc_current_file_label": "Archivo actual:", + "doc_new_heading": "Nuevo documento", + "doc_edit_heading": "Editar", + + "doc_section_details": "Detalles", + "doc_label_document_date": "Fecha del documento", + "doc_label_creation_location": "Lugar de creación", + "doc_label_archive_location_original": "Ubicación de almacenamiento (original)", + "doc_section_persons": "Personas", + "doc_sender_not_specified": "No especificado", + "doc_no_receivers": "Sin destinatarios", + "doc_section_content": "Contenido", + "doc_label_summary": "Resumen", + "doc_loading": "Cargando documento...", + "doc_download_link": "Intentar descarga directa", + "doc_no_scan": "No hay escaneo disponible", + + "persons_heading": "Directorio de personas", + "persons_subtitle": "Explore el índice de todas las personas registradas en el archivo familiar.", + "persons_btn_new": "Nueva persona", + "persons_search_placeholder": "Buscar nombres...", + "persons_empty_heading": "No se encontraron personas.", + "persons_empty_text": "Pruebe con otro término de búsqueda.", + + "persons_new_heading": "Nueva persona", + "persons_section_details": "Datos de la persona", + + "person_edit_heading": "Editar persona", + "person_label_full_name": "Nombre completo", + "person_merge_heading": "Fusionar persona", + "person_merge_description": "Esta persona se fusionará con la persona de destino seleccionada. Todos los documentos y enlaces se transferirán y esta persona será eliminada.", + "person_merge_target_label": "Fusionar con", + "person_btn_merge": "Fusionar", + "person_btn_merge_confirm": "Sí, fusionar", + "person_merge_warning": "Atención: Esta acción no se puede deshacer.", + "person_docs_heading": "Documentos enviados", + "person_no_docs": "Esta persona aún no está vinculada como remitente.", + + "conv_heading": "Conversaciones", + "conv_subtitle": "Siga la correspondencia entre dos personas cronológicamente.", + "conv_label_person_a": "Persona A (Remitente)", + "conv_label_person_b": "Persona B (Destinatario)", + "conv_label_from": "Período desde", + "conv_label_to": "Período hasta", + "conv_sort_label": "Ordenar:", + "conv_sort_newest": "Más reciente primero", + "conv_sort_oldest": "Más antiguo primero", + "conv_empty_heading": "Seleccione dos personas", + "conv_empty_text": "La correspondencia se mostrará aquí.", + "conv_no_results_heading": "No se encontraron documentos.", + "conv_no_results_text": "Intente ajustar el período de tiempo.", + + "admin_heading": "Panel de administración", + "admin_tab_users": "Usuarios", + "admin_tab_groups": "Grupos", + "admin_tab_tags": "Etiquetas", + "admin_section_users": "Gestión de usuarios", + "admin_col_login": "Login", + "admin_col_groups": "Grupos", + "admin_col_password": "Contraseña", + "admin_multiselect_hint": "Ctrl+Clic para seleccionar", + "admin_password_placeholder": "Nueva contraseña (opcional)", + "admin_no_groups": "Sin grupos", + "admin_btn_delete_user_title": "Eliminar usuario", + "admin_section_new_user": "Crear nuevo usuario", + "admin_multiselect_hint_multi": "Ctrl+Clic para varios", + "admin_multiselect_hint_full": "Ctrl+Clic para selección múltiple", + "admin_section_tags": "Etiquetas", + "admin_tags_warning": "Advertencia: Renombrar o eliminar afecta a todos los documentos vinculados.", + "admin_btn_edit_tag_label": "Editar etiqueta", + "admin_tag_delete_confirm": "¿Realmente eliminar? La etiqueta se eliminará de todos los documentos.", + "admin_btn_delete_tag_label": "Eliminar etiqueta", + "admin_section_groups": "Gestión de grupos", + "admin_col_name": "Nombre", + "admin_col_permissions": "Permisos", + "admin_col_actions": "Acciones", + "admin_group_delete_confirm": "¿Realmente eliminar el grupo?", + "admin_section_new_group": "Crear nuevo grupo", + "admin_group_name_placeholder": "Nombre del grupo (p.ej. Editores)", + + "comp_typeahead_placeholder": "Escriba un nombre...", + "comp_typeahead_loading": "Buscando...", + "comp_multiselect_placeholder": "Escriba un nombre...", + "comp_multiselect_remove": "Eliminar", + "comp_multiselect_loading": "Buscando...", + "comp_taginput_placeholder_create": "Añadir etiquetas...", + "comp_taginput_placeholder_filter": "Filtrar por etiquetas...", + "comp_taginput_remove": "Eliminar etiqueta", + "comp_taginput_create_hint": "Pulse Enter para crear etiqueta." } diff --git a/frontend/project.inlang/settings.json b/frontend/project.inlang/settings.json index c0b13cc0..1fc413ac 100644 --- a/frontend/project.inlang/settings.json +++ b/frontend/project.inlang/settings.json @@ -7,10 +7,10 @@ "plugin.inlang.messageFormat": { "pathPattern": "./messages/{locale}.json" }, - "baseLocale": "en", + "baseLocale": "de", "locales": [ + "de", "en", - "es", - "de" + "es" ] } diff --git a/frontend/src/lib/components/PersonMultiSelect.svelte b/frontend/src/lib/components/PersonMultiSelect.svelte index c98ba5ab..44d2bb04 100644 --- a/frontend/src/lib/components/PersonMultiSelect.svelte +++ b/frontend/src/lib/components/PersonMultiSelect.svelte @@ -1,5 +1,6 @@ @@ -17,17 +18,17 @@
-

Anmelden

+

{m.login_heading()}

- +
- +
@@ -38,7 +39,7 @@
diff --git a/frontend/src/routes/persons/+page.svelte b/frontend/src/routes/persons/+page.svelte index 24c43b5c..a7dcf3c6 100644 --- a/frontend/src/routes/persons/+page.svelte +++ b/frontend/src/routes/persons/+page.svelte @@ -1,5 +1,6 @@ @@ -22,9 +23,9 @@ let { form } = $props(); d="M10 19l-7-7m0 0l7-7m-7 7h18" /> - Zurück zur Übersicht + {m.btn_back_to_overview()} -

Neue Person

+

{m.persons_new_heading()}

{#if form?.error} @@ -34,13 +35,13 @@ let { form } = $props();

- Angaben zur Person + {m.persons_section_details()}

{m.form_label_first_name()} * {m.form_label_last_name()} * {m.form_label_alias()}
@@ -87,13 +88,13 @@ let { form } = $props(); href="/persons" class="text-sm font-medium text-gray-500 transition-colors hover:text-brand-navy" > - Abbrechen + {m.btn_cancel()}
-- 2.49.1