Übersetzung Teil 1

This commit is contained in:
Ashikagi
2026-03-23 23:02:51 +01:00
parent cd77f88d96
commit 46d468575a
21 changed files with 1605 additions and 333 deletions

View File

@@ -10,8 +10,7 @@ import {
Trash2,
User
} from 'lucide-react';
const CALENDAR_DAY_LABELS = ['M', 'D', 'M', 'D', 'F', 'S', 'S'];
import { useTranslation } from '../../i18n/LanguageContext';
const toIsoDate = (date) => {
const year = date.getFullYear();
@@ -56,6 +55,7 @@ const PatList = ({
getPatTypeColor,
getAchievement
}) => {
const t = useTranslation();
const datePickerRef = useRef(null);
const [showCreateMenu, setShowCreateMenu] = useState(false);
const [activeDatePicker, setActiveDatePicker] = useState(null);
@@ -217,7 +217,7 @@ const PatList = ({
<div className="px-6 py-4 border-b border-gray-200 dark:border-gray-800 bg-gray-50 dark:bg-gray-800/70">
<h2 className="text-lg font-bold text-gray-800 dark:text-gray-100">{title}</h2>
<p className="text-sm text-gray-500 dark:text-gray-400 mt-1">
Anzahl: {entries.length}
{t('patlist.count', { count: entries.length })}
{entries.length !== totalEntries ? ` von ${totalEntries}` : ''}
</p>
</div>
@@ -236,14 +236,14 @@ const PatList = ({
onClick={() => handleSort('name')}
className="inline-flex items-center gap-2 hover:text-gray-900 dark:hover:text-white transition"
>
Name
{t('patlist.col_name')}
{renderSortIcon('name')}
</button>
<input
type="text"
value={filters.name}
onChange={(event) => handleFilterChange('name', event.target.value)}
aria-label="Nach Name filtern"
aria-label={t('patlist.filter_name')}
className="w-40 rounded border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-950 px-3 py-2 text-sm font-normal text-gray-700 dark:text-gray-200"
/>
</div>
@@ -255,18 +255,18 @@ const PatList = ({
onClick={() => handleSort('datum')}
className="inline-flex items-center gap-2 hover:text-gray-900 dark:hover:text-white transition"
>
Datum
{t('patlist.col_date')}
{renderSortIcon('datum')}
</button>
<div ref={isDatePickerOpen ? datePickerRef : null} className="relative">
<button
type="button"
onClick={() => toggleDatePicker(tableKey)}
aria-label="Nach Datum filtern"
aria-label={t('patlist.filter_date')}
aria-expanded={isDatePickerOpen}
className="flex w-36 items-center justify-between rounded border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-950 px-3 py-2 text-left text-sm font-normal text-gray-700 dark:text-gray-200"
>
<span>{formatDateForFilter(filters.datum) || 'tt.mm.jjjj'}</span>
<span>{formatDateForFilter(filters.datum) || t('patlist.date_placeholder')}</span>
<Calendar className="h-4 w-4 text-gray-500 dark:text-gray-400" aria-hidden="true" />
</button>
@@ -281,7 +281,7 @@ const PatList = ({
)
}
className="rounded-lg p-2 text-gray-500 transition hover:bg-gray-100 hover:text-gray-900 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-white"
aria-label="Vorheriger Monat"
aria-label={t('patlist.prev_month')}
>
<ChevronLeft className="h-4 w-4" aria-hidden="true" />
</button>
@@ -296,14 +296,14 @@ const PatList = ({
)
}
className="rounded-lg p-2 text-gray-500 transition hover:bg-gray-100 hover:text-gray-900 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-white"
aria-label="Nächster Monat"
aria-label={t('patlist.next_month')}
>
<ChevronRight className="h-4 w-4" aria-hidden="true" />
</button>
</div>
<div className="grid grid-cols-7 gap-1 text-center text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">
{CALENDAR_DAY_LABELS.map((label, index) => (
{t('patlist.days').split(' ').map((label, index) => (
<span key={`${label}-${index}`}>{label}</span>
))}
</div>
@@ -337,14 +337,14 @@ const PatList = ({
onClick={selectToday}
className="rounded-lg bg-gray-100 px-3 py-2 text-sm font-medium text-gray-700 transition hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700"
>
Heute
{t('patlist.today')}
</button>
<button
type="button"
onClick={clearDate}
className="rounded-lg bg-gray-100 px-3 py-2 text-sm font-medium text-gray-700 transition hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700"
>
Löschen
{t('patlist.delete')}
</button>
</div>
</div>
@@ -359,7 +359,7 @@ const PatList = ({
onClick={() => handleSort('result')}
className="inline-flex items-center gap-2 hover:text-gray-900 dark:hover:text-white transition"
>
Ergebnis
{t('patlist.col_result')}
{renderSortIcon('result')}
</button>
</div>
@@ -401,12 +401,12 @@ const PatList = ({
? 'bg-sky-100 text-sky-800 dark:bg-sky-900/40 dark:text-sky-200'
: 'bg-amber-100 text-amber-800 dark:bg-amber-900/40 dark:text-amber-200'
}`}>
{testsVisible ? 'Freigabe aktiv' : 'Freigabe pausiert'}
{testsVisible ? t('patlist.share_active') : t('patlist.share_paused')}
</span>
)}
{assessment.isFinalized && (
<span className="px-3 py-1 rounded-full text-xs font-semibold bg-emerald-100 text-emerald-800 dark:bg-emerald-900/40 dark:text-emerald-200">
Final abgeschlossen
{t('patlist.finalized')}
</span>
)}
</div>
@@ -433,7 +433,7 @@ const PatList = ({
onClick={() => onEdit(assessment)}
className="bg-indigo-600 text-white px-4 py-2 rounded hover:bg-indigo-700 transition text-sm"
>
Öffnen
{t('patlist.open')}
</button>
<button
onClick={(e) => {
@@ -441,7 +441,7 @@ const PatList = ({
onDelete(assessment.id);
}}
className="text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 p-2"
title="Löschen"
title={t('patlist.delete')}
>
<Trash2 className="w-5 h-5" aria-hidden="true" />
</button>
@@ -462,15 +462,15 @@ const PatList = ({
<div className="min-h-screen bg-gradient-to-br from-green-50 to-blue-100 dark:from-gray-950 dark:to-gray-900 p-6 text-gray-900 dark:text-gray-100">
<div className="max-w-7xl mx-auto">
<div className="bg-white dark:bg-gray-900 rounded-lg shadow-lg p-6 mb-6 border border-gray-100 dark:border-gray-800">
<div className="flex items-center justify-between mb-6">
<h1 className="text-3xl font-bold text-gray-800 dark:text-gray-100">PAT Test Manager</h1>
<div className="flex items-center justify-between">
<h1 className="text-3xl font-bold text-gray-800 dark:text-gray-100">{t('patlist.title')}</h1>
<div className="relative">
<button
onClick={() => setShowCreateMenu((current) => !current)}
className="bg-green-600 text-white px-4 py-2 rounded-lg hover:bg-green-700 transition flex items-center justify-center gap-2"
>
<Plus className="w-4 h-4" />
Neue Bewertung
{t('patlist.new_assessment')}
</button>
{showCreateMenu && (
@@ -501,17 +501,17 @@ const PatList = ({
<div className="space-y-6">
{renderAssessmentTable(
'open',
'Noch offene Bewertungen',
t('patlist.open_assessments'),
sortedOpenAssessments,
openAssessments.length,
'Keine offenen Bewertungen mit den aktuellen Suchfiltern gefunden'
t('patlist.no_open')
)}
{renderAssessmentTable(
'finalized',
'Abgeschlossene Bewertungen',
t('patlist.closed_assessments'),
sortedFinalizedAssessments,
finalizedAssessments.length,
'Keine abgeschlossenen Bewertungen mit den aktuellen Suchfiltern gefunden'
t('patlist.no_closed')
)}
</div>
)}
@@ -519,8 +519,8 @@ const PatList = ({
{assessments.length === 0 && (
<div className="bg-white dark:bg-gray-900 rounded-lg shadow-lg p-12 text-center border border-gray-100 dark:border-gray-800">
<User className="w-16 h-16 mx-auto mb-4 text-gray-300 dark:text-gray-700" aria-hidden="true" />
<p className="text-gray-600 dark:text-gray-300 mb-4">Noch keine Bewertungen vorhanden</p>
<p className="text-sm text-gray-500 dark:text-gray-400">Lege oben eine neue Bewertung an und wähle dabei PAT Start, PAT 1, PAT 2 oder PAT 3</p>
<p className="text-gray-600 dark:text-gray-300 mb-4">{t('patlist.no_assessments')}</p>
<p className="text-sm text-gray-500 dark:text-gray-400">{t('patlist.no_assessments_hint')}</p>
</div>
)}
</div>