feat(i18n): fix remaining hardcoded strings and add login page switcher
- Add 9 missing translation keys to de/en/es.json: doc_file_error_preview, doc_download_title, doc_tag_filter_title, doc_conversation_title, doc_preview_iframe_title, doc_image_alt, doc_no_date, person_merge_will_be_deleted, admin_user_delete_confirm - documents/[id]/+page.svelte: replace 6 hardcoded strings with m.*() - persons/[id]/+page.svelte: replace "wird gelöscht." and "Kein Datum" - admin/+page.svelte: replace confirm() string with m.admin_user_delete_confirm() - login/+page.svelte: add top-right DE/EN/ES language switcher (Option B) and wire existing login_* keys to the form labels Closes #12 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -155,6 +155,17 @@
|
|||||||
"admin_group_delete_confirm": "Gruppe wirklich löschen?",
|
"admin_group_delete_confirm": "Gruppe wirklich löschen?",
|
||||||
"admin_section_new_group": "Neue Gruppe anlegen",
|
"admin_section_new_group": "Neue Gruppe anlegen",
|
||||||
"admin_group_name_placeholder": "Gruppenname (z.B. Editoren)",
|
"admin_group_name_placeholder": "Gruppenname (z.B. Editoren)",
|
||||||
|
"admin_user_delete_confirm": "Benutzer {username} wirklich löschen?",
|
||||||
|
|
||||||
|
"doc_file_error_preview": "Vorschau konnte nicht geladen werden.",
|
||||||
|
"doc_download_title": "Herunterladen",
|
||||||
|
"doc_tag_filter_title": "Nach {name} filtern",
|
||||||
|
"doc_conversation_title": "Konversation anzeigen",
|
||||||
|
"doc_preview_iframe_title": "Dokumentvorschau",
|
||||||
|
"doc_image_alt": "Original-Scan",
|
||||||
|
"doc_no_date": "Kein Datum",
|
||||||
|
|
||||||
|
"person_merge_will_be_deleted": "wird gelöscht.",
|
||||||
|
|
||||||
"comp_typeahead_placeholder": "Namen tippen...",
|
"comp_typeahead_placeholder": "Namen tippen...",
|
||||||
"comp_typeahead_loading": "Suche...",
|
"comp_typeahead_loading": "Suche...",
|
||||||
|
|||||||
@@ -155,6 +155,17 @@
|
|||||||
"admin_group_delete_confirm": "Really delete group?",
|
"admin_group_delete_confirm": "Really delete group?",
|
||||||
"admin_section_new_group": "Create new group",
|
"admin_section_new_group": "Create new group",
|
||||||
"admin_group_name_placeholder": "Group name (e.g. Editors)",
|
"admin_group_name_placeholder": "Group name (e.g. Editors)",
|
||||||
|
"admin_user_delete_confirm": "Really delete user {username}?",
|
||||||
|
|
||||||
|
"doc_file_error_preview": "Could not load preview.",
|
||||||
|
"doc_download_title": "Download",
|
||||||
|
"doc_tag_filter_title": "Filter by {name}",
|
||||||
|
"doc_conversation_title": "Show conversation",
|
||||||
|
"doc_preview_iframe_title": "Document Preview",
|
||||||
|
"doc_image_alt": "Original scan",
|
||||||
|
"doc_no_date": "No date",
|
||||||
|
|
||||||
|
"person_merge_will_be_deleted": "will be deleted.",
|
||||||
|
|
||||||
"comp_typeahead_placeholder": "Type a name...",
|
"comp_typeahead_placeholder": "Type a name...",
|
||||||
"comp_typeahead_loading": "Searching...",
|
"comp_typeahead_loading": "Searching...",
|
||||||
|
|||||||
@@ -155,6 +155,17 @@
|
|||||||
"admin_group_delete_confirm": "¿Realmente eliminar el grupo?",
|
"admin_group_delete_confirm": "¿Realmente eliminar el grupo?",
|
||||||
"admin_section_new_group": "Crear nuevo grupo",
|
"admin_section_new_group": "Crear nuevo grupo",
|
||||||
"admin_group_name_placeholder": "Nombre del grupo (p.ej. Editores)",
|
"admin_group_name_placeholder": "Nombre del grupo (p.ej. Editores)",
|
||||||
|
"admin_user_delete_confirm": "¿Realmente eliminar al usuario {username}?",
|
||||||
|
|
||||||
|
"doc_file_error_preview": "No se pudo cargar la vista previa.",
|
||||||
|
"doc_download_title": "Descargar",
|
||||||
|
"doc_tag_filter_title": "Filtrar por {name}",
|
||||||
|
"doc_conversation_title": "Ver conversación",
|
||||||
|
"doc_preview_iframe_title": "Vista previa del documento",
|
||||||
|
"doc_image_alt": "Escaneado original",
|
||||||
|
"doc_no_date": "Sin fecha",
|
||||||
|
|
||||||
|
"person_merge_will_be_deleted": "será eliminado.",
|
||||||
|
|
||||||
"comp_typeahead_placeholder": "Escriba un nombre...",
|
"comp_typeahead_placeholder": "Escriba un nombre...",
|
||||||
"comp_typeahead_loading": "Buscando...",
|
"comp_typeahead_loading": "Buscando...",
|
||||||
|
|||||||
@@ -202,7 +202,7 @@
|
|||||||
method="POST"
|
method="POST"
|
||||||
action="?/deleteUser"
|
action="?/deleteUser"
|
||||||
use:enhance={({ cancel }) => {
|
use:enhance={({ cancel }) => {
|
||||||
if (!confirm(`Benutzer '${user.username}' wirklich löschen?`)) {
|
if (!confirm(m.admin_user_delete_confirm({ username: user.username }))) {
|
||||||
cancel();
|
cancel();
|
||||||
}
|
}
|
||||||
return async ({ update }) => {
|
return async ({ update }) => {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
error = 'Vorschau konnte nicht geladen werden.';
|
error = m.doc_file_error_preview();
|
||||||
} finally {
|
} finally {
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
}
|
}
|
||||||
@@ -87,7 +87,7 @@
|
|||||||
href={fileUrl}
|
href={fileUrl}
|
||||||
download={doc.originalFilename}
|
download={doc.originalFilename}
|
||||||
class="text-brand-navy bg-brand-sand/50 hover:bg-brand-mint border border-transparent p-2 rounded transition"
|
class="text-brand-navy bg-brand-sand/50 hover:bg-brand-mint border border-transparent p-2 rounded transition"
|
||||||
title="Download"
|
title={m.doc_download_title()}
|
||||||
>
|
>
|
||||||
<img src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Download-MD.svg" alt="" aria-hidden="true" class="w-5 h-5" />
|
<img src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Download-MD.svg" alt="" aria-hidden="true" class="w-5 h-5" />
|
||||||
</a>
|
</a>
|
||||||
@@ -163,7 +163,7 @@
|
|||||||
<a
|
<a
|
||||||
href="/?tag={encodeURIComponent(tag.name)}"
|
href="/?tag={encodeURIComponent(tag.name)}"
|
||||||
class="inline-flex items-center px-2 py-0.5 rounded text-xs font-bold uppercase tracking-wide bg-brand-sand/50 text-brand-navy hover:bg-brand-navy hover:text-white transition-colors"
|
class="inline-flex items-center px-2 py-0.5 rounded text-xs font-bold uppercase tracking-wide bg-brand-sand/50 text-brand-navy hover:bg-brand-navy hover:text-white transition-colors"
|
||||||
title="Nach '{tag.name}' filtern"
|
title={m.doc_tag_filter_title({ name: tag.name })}
|
||||||
>
|
>
|
||||||
{tag.name}
|
{tag.name}
|
||||||
</a>
|
</a>
|
||||||
@@ -241,7 +241,7 @@
|
|||||||
<a
|
<a
|
||||||
href="/conversations?senderId={doc.sender.id}&receiverId={receiver.id}"
|
href="/conversations?senderId={doc.sender.id}&receiverId={receiver.id}"
|
||||||
class="text-gray-300 hover:text-brand-mint transition"
|
class="text-gray-300 hover:text-brand-mint transition"
|
||||||
title="Konversation anzeigen"
|
title={m.doc_conversation_title()}
|
||||||
>
|
>
|
||||||
<img src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Chat-MD.svg" alt="" aria-hidden="true" class="w-5 h-5" />
|
<img src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Chat-MD.svg" alt="" aria-hidden="true" class="w-5 h-5" />
|
||||||
</a>
|
</a>
|
||||||
@@ -341,14 +341,14 @@
|
|||||||
{:else if fileUrl && doc.originalFilename.toLowerCase().endsWith('.pdf')}
|
{:else if fileUrl && doc.originalFilename.toLowerCase().endsWith('.pdf')}
|
||||||
<iframe
|
<iframe
|
||||||
src={fileUrl}
|
src={fileUrl}
|
||||||
title="Document Preview"
|
title={m.doc_preview_iframe_title()}
|
||||||
class="w-full h-full border-none bg-white"
|
class="w-full h-full border-none bg-white"
|
||||||
></iframe>
|
></iframe>
|
||||||
{:else if fileUrl}
|
{:else if fileUrl}
|
||||||
<div class="w-full h-full flex items-center justify-center overflow-auto p-8">
|
<div class="w-full h-full flex items-center justify-center overflow-auto p-8">
|
||||||
<img
|
<img
|
||||||
src={fileUrl}
|
src={fileUrl}
|
||||||
alt="Original Scan"
|
alt={m.doc_image_alt()}
|
||||||
class="max-w-full max-h-full object-contain shadow-2xl"
|
class="max-w-full max-h-full object-contain shadow-2xl"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,12 +1,34 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { m } from '$lib/paraglide/messages.js';
|
import { m } from '$lib/paraglide/messages.js';
|
||||||
|
import { setLocale, getLocale } from '$lib/paraglide/runtime';
|
||||||
|
|
||||||
let { form }: { form?: { error?: string; success?: boolean } } = $props();
|
let { form }: { form?: { error?: string; success?: boolean } } = $props();
|
||||||
|
|
||||||
|
const locales = ['DE', 'EN', 'ES'] as const;
|
||||||
|
const localeMap = { DE: 'de', EN: 'en', ES: 'es' } as const;
|
||||||
|
const activeLocale = $derived(getLocale().toUpperCase());
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="min-h-screen bg-white flex flex-col">
|
<div class="relative min-h-screen bg-white flex flex-col">
|
||||||
<!-- DGB purple accent strip -->
|
<!-- DGB purple accent strip -->
|
||||||
<div class="h-1 bg-brand-purple"></div>
|
<div class="h-1 bg-brand-purple"></div>
|
||||||
|
|
||||||
|
<!-- Language switcher -->
|
||||||
|
<div class="absolute top-4 right-4 flex items-center gap-1">
|
||||||
|
{#each locales as locale (locale)}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={() => setLocale(localeMap[locale])}
|
||||||
|
class="text-xs font-sans tracking-widest px-1.5 py-1 transition-colors
|
||||||
|
{activeLocale === locale
|
||||||
|
? 'font-bold text-brand-navy'
|
||||||
|
: 'font-normal text-gray-400 hover:text-brand-navy'}"
|
||||||
|
>
|
||||||
|
{locale}
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex-1 flex items-center justify-center px-4">
|
<div class="flex-1 flex items-center justify-center px-4">
|
||||||
<div class="w-full max-w-sm">
|
<div class="w-full max-w-sm">
|
||||||
<!-- Logo -->
|
<!-- Logo -->
|
||||||
|
|||||||
@@ -189,7 +189,7 @@
|
|||||||
|
|
||||||
{#if showMergeConfirm}
|
{#if showMergeConfirm}
|
||||||
<p class="mt-3 text-sm text-red-700 bg-red-50 border border-red-200 rounded px-3 py-2">
|
<p class="mt-3 text-sm text-red-700 bg-red-50 border border-red-200 rounded px-3 py-2">
|
||||||
{m.person_merge_warning()} <strong>{person.firstName} {person.lastName}</strong> wird gelöscht.
|
{m.person_merge_warning()} <strong>{person.firstName} {person.lastName}</strong> {m.person_merge_will_be_deleted()}
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
</form>
|
</form>
|
||||||
@@ -225,7 +225,7 @@
|
|||||||
{doc.title || doc.originalFilename}
|
{doc.title || doc.originalFilename}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center text-xs font-sans text-gray-500 mt-0.5 space-x-2">
|
<div class="flex items-center text-xs font-sans text-gray-500 mt-0.5 space-x-2">
|
||||||
<span>{doc.documentDate ? new Intl.DateTimeFormat('de-DE', { day: 'numeric', month: 'long', year: 'numeric' }).format(new Date(doc.documentDate + 'T12:00:00')) : 'Kein Datum'}</span>
|
<span>{doc.documentDate ? new Intl.DateTimeFormat('de-DE', { day: 'numeric', month: 'long', year: 'numeric' }).format(new Date(doc.documentDate + 'T12:00:00')) : m.doc_no_date()}</span>
|
||||||
{#if doc.location}
|
{#if doc.location}
|
||||||
<span class="text-brand-mint">•</span>
|
<span class="text-brand-mint">•</span>
|
||||||
<span>{doc.location}</span>
|
<span>{doc.location}</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user