feat(tournamentService): add validation for stage advancements and enhance tournament configuration

- Implemented a new function `validateStageAdvancements` to ensure the integrity of stage advancements in tournaments, including checks for valid indices, types, and configurations.
- Enhanced the `isGroupAdvancementReady` function to verify group readiness based on match completion and participant positions.
- Updated the tournament management interface to include new validation logic, improving the overall robustness of tournament setup.
- Refactored tournament-related components to improve user experience and ensure accurate data handling across the application.
This commit is contained in:
Torsten Schulz (local)
2026-03-16 22:17:37 +01:00
parent e1dccb6ff0
commit 43f96b2491
27 changed files with 8198 additions and 996 deletions

View File

@@ -96,9 +96,13 @@
<div class="nav-section">
<h4 class="nav-title">{{ $t('navigation.competitions') }}</h4>
<router-link v-if="hasPermission('tournaments', 'read')" to="/tournaments" class="nav-link" title="Turniere">
<router-link v-if="hasPermission('tournaments', 'read')" to="/tournaments" class="nav-link" :title="$t('navigation.clubTournaments')">
<span class="nav-icon">🏆</span>
{{ $t('navigation.tournaments') }}
{{ $t('navigation.clubTournaments') }}
</router-link>
<router-link v-if="hasPermission('tournaments', 'read')" to="/tournament-participations" class="nav-link" :title="$t('navigation.tournamentParticipations')">
<span class="nav-icon">📄</span>
{{ $t('navigation.tournamentParticipations') }}
</router-link>
<router-link v-if="hasPermission('schedule', 'read')" to="/schedule" class="nav-link" title="Spielpläne">
<span class="nav-icon">📅</span>

View File

@@ -3,8 +3,8 @@
<div class="participants-class-filter">
<label>
{{ $t('tournaments.showClass') }}:
<select :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" class="class-filter-select">
<option :value="null">{{ $t('tournaments.allClasses') }}</option>
<select :value="normalizedValue" @change="handleChange" class="class-filter-select">
<option value="__all__">{{ $t('tournaments.allClasses') }}</option>
<option v-for="classItem in tournamentClasses" :key="classItem.id" :value="classItem.id">
{{ classItem.name }} ({{ classItem.isDoubles ? $t('tournaments.doubles') : $t('tournaments.singles') }})
</option>
@@ -32,7 +32,28 @@ export default {
default: null
}
},
emits: ['update:modelValue']
emits: ['update:modelValue'],
computed: {
normalizedValue() {
if (this.modelValue === null || this.modelValue === undefined || this.modelValue === '' || this.modelValue === '__all__') {
return '__all__';
}
return this.modelValue;
}
},
methods: {
handleChange(event) {
const value = event.target.value;
if (value === '__all__') {
this.$emit('update:modelValue', null);
return;
}
if (value === '__none__') {
this.$emit('update:modelValue', '__none__');
return;
}
this.$emit('update:modelValue', value === '' ? null : Number(value));
}
}
};
</script>

File diff suppressed because it is too large Load Diff

View File

@@ -8,96 +8,110 @@
@update:modelValue="$emit('update:selectedViewClass', $event)"
/>
<section v-if="isGroupTournament" class="group-controls">
<label>
{{ $t('tournaments.advancersPerGroup') }}:
<input type="number" :value="advancingPerGroup" @input="$emit('update:advancingPerGroup', parseInt($event.target.value))" min="1" @change="$emit('modus-change')" />
</label>
<label style="margin-left:1em">
{{ $t('tournaments.maxGroupSize') }}:
<input type="number" :value="maxGroupSize" @input="$emit('update:maxGroupSize', parseInt($event.target.value))" min="1" />
</label>
<div v-if="selectedViewClass !== null && selectedViewClass !== undefined" class="groups-per-class">
<h4>{{ $t('tournaments.groupsPerClass') }}</h4>
<p class="groups-per-class-hint">{{ $t('tournaments.groupsPerClassHint') }}</p>
<div class="class-group-config">
<label class="class-group-label">
<span class="class-group-name">
{{ selectedViewClass === '__none__' ? $t('tournaments.withoutClass') : getClassName(selectedViewClass) }}
</span>
<span v-if="selectedViewClass !== '__none__'" class="class-group-type" :class="{ 'doubles': isClassDoubles(selectedViewClass) }">
({{ isClassDoubles(selectedViewClass) ? $t('tournaments.doubles') : $t('tournaments.singles') }})
</span>
<input
type="number"
:value="groupsPerClassInput"
@input="$emit('update:groupsPerClassInput', parseInt($event.target.value))"
min="0"
@change="$emit('group-count-change')"
class="class-group-input"
:placeholder="$t('tournaments.numberOfGroups')"
<div class="groups-card">
<div class="groups-card-header">
<h4>{{ $t('tournaments.createGroups') }}</h4>
<div class="groups-card-meta">
<span class="groups-chip">{{ groups.length }} {{ $t('tournaments.group') }}</span>
<span class="groups-chip">{{ filteredGroupMatches.length }} {{ $t('tournaments.groupMatches') }}</span>
</div>
</div>
<div class="groups-settings">
<label>
{{ $t('tournaments.advancersPerGroup') }}:
<input type="number" :value="advancingPerGroup" @input="$emit('update:advancingPerGroup', parseInt($event.target.value))" min="1" @change="$emit('modus-change')" />
</label>
<label>
{{ $t('tournaments.maxGroupSize') }}:
<input type="number" :value="maxGroupSize" @input="$emit('update:maxGroupSize', parseInt($event.target.value))" min="1" />
</label>
</div>
<div v-if="selectedViewClass !== null && selectedViewClass !== undefined" class="groups-per-class">
<h4>{{ $t('tournaments.groupsPerClass') }}</h4>
<p class="groups-per-class-hint">{{ $t('tournaments.groupsPerClassHint') }}</p>
<div class="class-group-config">
<label class="class-group-label">
<span class="class-group-name">
{{ selectedViewClass === '__none__' ? $t('tournaments.withoutClass') : getClassName(selectedViewClass) }}
</span>
<span v-if="selectedViewClass !== '__none__'" class="class-group-type" :class="{ 'doubles': isClassDoubles(selectedViewClass) }">
({{ isClassDoubles(selectedViewClass) ? $t('tournaments.doubles') : $t('tournaments.singles') }})
</span>
<input
type="number"
:value="groupsPerClassInput"
@input="$emit('update:groupsPerClassInput', parseInt($event.target.value))"
min="0"
@change="$emit('group-count-change')"
class="class-group-input"
:placeholder="$t('tournaments.numberOfGroups')"
/>
<span class="class-group-unit">{{ $t('tournaments.group') }}</span>
</label>
</div>
</div>
<div v-else class="groups-per-class">
<label>
{{ $t('tournaments.numberOfGroups') }}:
<input
type="number"
:value="numberOfGroups"
min="1"
@input="$emit('update:numberOfGroups', Math.max(1, parseInt($event.target.value || '1', 10) || 1))"
@change="$emit('group-count-change')"
/>
<span class="class-group-unit">{{ $t('tournaments.group') }}</span>
</label>
</div>
</div>
<div v-else class="groups-per-class">
<label>
{{ $t('tournaments.numberOfGroups') }}:
<input
type="number"
:value="numberOfGroups"
min="1"
@input="$emit('update:numberOfGroups', Math.max(1, parseInt($event.target.value || '1', 10) || 1))"
@change="$emit('group-count-change')"
/>
</label>
</div>
<button @click="$emit('create-groups')">{{ $t('tournaments.createGroups') }}</button>
<button @click="$emit('randomize-groups')">{{ $t('tournaments.randomizeGroups') }}</button>
<button @click="$emit('reset-groups')">{{ $t('tournaments.resetGroups') }}</button>
<!-- Klassen zusammenlegen (Pools) -->
<div class="merge-pools-box">
<h4>{{ $t('tournaments.mergeClasses') || 'Klassen zusammenlegen (gemeinsame Gruppenphase)' }}</h4>
<div class="merge-pools-row">
<label>
{{ $t('tournaments.sourceClass') || 'Quelle' }}:
<select v-model="mergeSourceClassId">
<option :value="null"></option>
<option v-for="c in tournamentClasses" :key="c.id" :value="c.id">{{ c.name }}</option>
</select>
</label>
<label>
{{ $t('tournaments.targetClass') || 'Ziel' }}:
<select v-model="mergeTargetClassId">
<option :value="null"></option>
<option v-for="c in tournamentClasses" :key="c.id" :value="c.id">{{ c.name }}</option>
</select>
</label>
<template v-if="mergePoolsReady">
<label>
{{ $t('tournaments.strategy') || 'Strategie' }}:
<select v-model="mergeStrategy">
<option value="singleGroup">{{ $t('tournaments.mergeSingleGroup') || 'Alle Spieler aus A in eine Gruppe (bei B)' }}</option>
<option value="distribute">{{ $t('tournaments.mergeDistribute') || 'Spieler aus A auf alle Gruppen (von B) verteilen' }}</option>
</select>
</label>
<label>
<input type="checkbox" v-model="mergeSourceAsAK" />
{{ mergeOutOfCompetitionLabel }}
</label>
</template>
</div>
<div class="merge-pools-actions">
<button @click="requestMergePools" :disabled="!mergeSourceClassId || !mergeTargetClassId || String(mergeSourceClassId)===String(mergeTargetClassId)">
{{ $t('tournaments.apply') || 'Übernehmen' }}
</button>
<div class="groups-actions">
<button @click="$emit('create-groups')" class="btn-primary">{{ $t('tournaments.createGroups') }}</button>
<button @click="$emit('randomize-groups')" class="btn-secondary">{{ $t('tournaments.randomizeGroups') }}</button>
<button @click="$emit('reset-groups')" class="trash-btn">{{ $t('tournaments.resetGroups') }}</button>
</div>
</div>
</section>
<section v-if="groups.length" class="groups-overview">
<details v-if="isGroupTournament" class="merge-pools-box">
<summary class="merge-summary">{{ $t('tournaments.mergeClasses') }}</summary>
<div class="merge-pools-row">
<label>
{{ $t('tournaments.sourceClass') }}:
<select v-model="mergeSourceClassId">
<option :value="null"></option>
<option v-for="c in tournamentClasses" :key="c.id" :value="c.id">{{ c.name }}</option>
</select>
</label>
<label>
{{ $t('tournaments.targetClass') }}:
<select v-model="mergeTargetClassId">
<option :value="null"></option>
<option v-for="c in tournamentClasses" :key="c.id" :value="c.id">{{ c.name }}</option>
</select>
</label>
<template v-if="mergePoolsReady">
<label>
{{ $t('tournaments.strategy') }}:
<select v-model="mergeStrategy">
<option value="singleGroup">{{ $t('tournaments.mergeSingleGroup') }}</option>
<option value="distribute">{{ $t('tournaments.mergeDistribute') }}</option>
</select>
</label>
<label>
<input type="checkbox" v-model="mergeSourceAsAK" />
{{ mergeOutOfCompetitionLabel }}
</label>
</template>
</div>
<div class="merge-pools-actions">
<button @click="requestMergePools" :disabled="!mergeSourceClassId || !mergeTargetClassId || String(mergeSourceClassId)===String(mergeTargetClassId)">
{{ $t('tournaments.apply') }}
</button>
</div>
</details>
<section v-if="groups.length" class="groups-overview">
<h3>{{ $t('tournaments.groupsOverview') }}</h3>
<template v-for="(classGroups, classId) in groupsByClass" :key="classId">
<template v-if="shouldShowClass(classId === 'null' ? null : parseInt(classId))">
@@ -158,15 +172,15 @@
</div>
</template>
</template>
<div v-if="filteredGroupMatches.length === 0" class="reset-controls" style="margin-top:1rem">
<div v-if="filteredGroupMatches.length === 0" class="reset-controls reset-controls-spaced">
<button @click="$emit('create-matches')" class="btn-primary">
Gruppenspiele berechnen
{{ $t('tournaments.createGroupMatches') }}
</button>
<button @click="$emit('cleanup-orphaned-matches')" class="btn-secondary">
🧹 {{ $t('tournaments.cleanupOrphanedMatches') }}
</button>
</div>
<div v-if="filteredGroupMatches.length > 0" class="reset-controls" style="margin-top:1rem">
<div v-if="filteredGroupMatches.length > 0" class="reset-controls reset-controls-spaced">
<button @click="$emit('cleanup-orphaned-matches')" class="btn-secondary">
🧹 {{ $t('tournaments.cleanupOrphanedMatches') }}
</button>
@@ -298,7 +312,7 @@ export default {
},
filterMatchesByClass(matches) {
// Wenn keine Klasse ausgewählt ist (null), zeige alle
if (this.selectedViewClass === null || this.selectedViewClass === undefined) {
if (this.selectedViewClass === null || this.selectedViewClass === undefined || this.selectedViewClass === '' || this.selectedViewClass === '__all__' || this.selectedViewClass === 'all') {
return matches;
}
// Wenn "Ohne Klasse" ausgewählt ist
@@ -320,7 +334,7 @@ export default {
},
shouldShowClass(classId) {
// Wenn keine Klasse ausgewählt ist (null), zeige alle
if (this.selectedViewClass === null || this.selectedViewClass === undefined) {
if (this.selectedViewClass === null || this.selectedViewClass === undefined || this.selectedViewClass === '' || this.selectedViewClass === '__all__' || this.selectedViewClass === 'all') {
return true;
}
// Wenn "Ohne Klasse" ausgewählt ist
@@ -501,29 +515,122 @@ export default {
</script>
<style scoped>
.group-controls {
margin-bottom: 1rem;
}
.groups-card {
border: 1px solid #e5e7eb;
border-radius: 14px;
background: #fff;
padding: 1rem;
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.04);
}
.groups-card-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
margin-bottom: 1rem;
}
.groups-card-header h4 {
margin: 0;
color: #1f2937;
}
.groups-card-meta {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.groups-chip {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.2rem 0.55rem;
border-radius: 999px;
background: #f3f4f6;
color: #4b5563;
font-size: 0.85rem;
font-weight: 700;
}
.groups-settings {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 0.9rem;
margin-bottom: 1rem;
}
.groups-settings label,
.groups-per-class label {
display: flex;
flex-direction: column;
gap: 0.3rem;
}
.groups-settings input,
.groups-per-class input,
.merge-pools-row select {
padding: 0.55rem 0.7rem;
border: 1px solid #d1d5db;
border-radius: 10px;
font-size: 0.95rem;
}
.groups-actions {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
margin-top: 1rem;
}
.reset-controls-spaced {
margin-top: 1rem;
}
.merge-pools-box {
margin: 1rem 0 0 0;
padding: 0.75rem 1rem;
border: 1px solid #e0e0e0;
border-radius: 6px;
background: #fafafa;
padding: 0.9rem 1rem;
border: 1px solid #e5e7eb;
border-radius: 14px;
background: #fff;
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.04);
}
.merge-pools-box h4 {
margin: 0 0 0.5rem 0;
font-size: 1rem;
color: #333;
.merge-summary {
cursor: pointer;
font-weight: 700;
color: #1f2937;
}
.merge-pools-row {
display: flex;
gap: 0.75rem;
flex-wrap: wrap;
align-items: center;
margin-top: 0.9rem;
}
.merge-pools-row select {
padding: 0.25rem 0.5rem;
.merge-pools-row label {
display: flex;
flex-direction: column;
gap: 0.3rem;
}
.merge-pools-actions {
margin-top: 0.5rem;
margin-top: 0.9rem;
}
.groups-overview {
margin-top: 1rem;
}
.group-table {
border: 1px solid #e5e7eb;
border-radius: 14px;
background: #fff;
padding: 0.9rem;
margin-bottom: 1rem;
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.04);
overflow-x: auto;
}
.gave-up-badge {
margin-left: 0.35rem;
@@ -533,5 +640,11 @@ export default {
color: #721c24;
border-radius: 4px;
}
</style>
@media (max-width: 900px) {
.groups-card-header {
align-items: flex-start;
flex-direction: column;
}
}
</style>

View File

@@ -7,45 +7,43 @@
:selected-date="selectedDate"
@update:modelValue="$emit('update:selectedViewClass', $event)"
/>
<section v-if="Object.keys(finalPlacementsByClassFiltered).length > 0" class="final-placements">
<section v-if="Object.keys(visibleFinalPlacementsByClass).length > 0" class="final-placements">
<h3>{{ $t('tournaments.finalPlacements') }}</h3>
<template v-for="(classPlacements, classId) in finalPlacementsByClassFiltered" :key="`final-${classId}`">
<div v-if="isAllSelected || shouldShowClass(classId==='null'?null:Number(classId))" class="class-section">
<div class="placements-summary">
<span class="placements-chip">{{ Object.keys(visibleFinalPlacementsByClass).length }} {{ $t('tournaments.classes') }}</span>
<span class="placements-chip">{{ totalVisibleFinalPlacements }} {{ $t('tournaments.tabPlacements') }}</span>
</div>
<template v-for="(classPlacements, classId) in visibleFinalPlacementsByClass" :key="`final-${classId}`">
<div class="class-section">
<h4 class="class-header">
{{ getClassName(classId) }}
<span class="class-type-badge" v-if="classId!==null" :class="{ doubles: isDoubles(classId), singles: !isDoubles(classId) }">
{{ isDoubles(classId) ? $t('tournaments.doubles') : $t('tournaments.singles') }}
</span>
</h4>
<table>
<thead>
<tr>
<th class="col-place">{{ labelPlace }}</th>
<th>{{ $t('tournaments.player') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="(entry, entryIdx) in classPlacements" :key="`final-${classId}-${entryIdx}`">
<td class="col-place">{{ entry.position }}.</td>
<td>
<span
class="player-name-clickable"
@click="openPlayerDialog(entry)"
:title="$t('tournaments.showPlayerDetails')"
>
{{ getEntryPlayerName(entry) }}
</span>
</td>
</tr>
</tbody>
</table>
<div class="final-placement-list">
<div v-for="(entry, entryIdx) in classPlacements" :key="`final-${classId}-${entryIdx}`" class="final-placement-card">
<div class="final-placement-badge">{{ entry.position }}.</div>
<button
type="button"
class="final-placement-player"
@click="openPlayerDialog(entry)"
:title="$t('tournaments.showPlayerDetails')"
>
{{ getEntryPlayerName(entry) }}
</button>
</div>
</div>
</div>
</template>
</section>
<section v-if="groupPlacements.length > 0" class="group-placements">
<section v-if="Object.keys(visibleGroupPlacementsByClass).length > 0" class="group-placements">
<h3>{{ $t('tournaments.groupPlacements') }}</h3>
<template v-for="(classGroups, classId) in groupPlacementsByClass" :key="`group-${classId}`">
<div v-if="isAllSelected || shouldShowClass(classId==='null'?null:Number(classId))" class="class-section">
<div class="placements-summary">
<span class="placements-chip">{{ visibleGroupPlacementCount }} {{ $t('tournaments.group') }}</span>
</div>
<template v-for="(classGroups, classId) in visibleGroupPlacementsByClass" :key="`group-${classId}`">
<div class="class-section">
<h4 class="class-header">
{{ getClassName(classId) }}
<span class="class-type-badge" v-if="classId!==null" :class="{ doubles: isDoubles(classId), singles: !isDoubles(classId) }">
@@ -86,11 +84,12 @@
</div>
</template>
</section>
<div v-if="Object.keys(finalPlacementsByClassFiltered).length === 0 && groupPlacements.length === 0" class="no-placements">
<p>{{ $t('tournaments.noPlacementsYet') }}</p>
<div v-if="Object.keys(visibleFinalPlacementsByClass).length === 0 && Object.keys(visibleGroupPlacementsByClass).length === 0" class="no-placements">
<p class="no-placements-title">{{ $t('tournaments.placementsPendingTitle') }}</p>
<p class="no-placements-copy">{{ placementsEmptyMessage }}</p>
</div>
<div v-if="isMiniChampionship && (participants.length > 0 || externalParticipants.length > 0)" class="missing-data-pdf" style="margin-top: 1.5rem;">
<div v-if="isMiniChampionship && (participants.length > 0 || externalParticipants.length > 0)" class="missing-data-pdf">
<button @click="generateMissingDataPDF" class="btn-primary" :disabled="pdfLoading">
{{ pdfLoading ? $t('tournaments.generatingPDF') : $t('tournaments.missingDataPDF') }}
</button>
@@ -170,7 +169,7 @@ export default {
labelPlace() {
const t = this.$t && this.$t('tournaments.place');
if (t && typeof t === 'string' && t.trim().length > 0 && t !== 'tournaments.place') return t;
return 'Platz';
return this.$t('tournaments.position');
},
finalPlacementsByClass() {
const byClass = {};
@@ -500,6 +499,50 @@ export default {
}
}
return filtered;
},
visibleFinalPlacementsByClass() {
const visible = {};
for (const [classKey, placements] of Object.entries(this.finalPlacementsByClassFiltered)) {
const normalizedClassId = classKey === 'null' ? null : Number(classKey);
if (this.shouldShowClass(normalizedClassId)) {
visible[classKey] = placements;
}
}
return visible;
},
visibleGroupPlacementsByClass() {
const visible = {};
for (const [classKey, placements] of Object.entries(this.groupPlacementsByClass)) {
const normalizedClassId = classKey === 'null' ? null : Number(classKey);
if (this.shouldShowClass(normalizedClassId)) {
visible[classKey] = placements;
}
}
return visible;
},
totalFinalPlacements() {
return Object.values(this.finalPlacementsByClassFiltered).reduce((sum, entries) => sum + entries.length, 0);
},
totalVisibleFinalPlacements() {
return Object.values(this.visibleFinalPlacementsByClass).reduce((sum, entries) => sum + entries.length, 0);
},
visibleGroupPlacementCount() {
return Object.values(this.visibleGroupPlacementsByClass).reduce((sum, entries) => sum + entries.length, 0);
},
placementsEmptyMessage() {
if (!this.isAllSelected) {
return this.$t('tournaments.placementsPendingSelectedClass');
}
if (Object.keys(this.groupPlacementsByClass).length === 0 && Object.keys(this.finalPlacementsByClassFiltered).length === 0) {
return this.$t('tournaments.placementsPendingNoGroups');
}
if (Object.keys(this.groupPlacementsByClass).length === 0) {
return this.$t('tournaments.placementsPendingGroups');
}
if (Object.keys(this.finalPlacementsByClassFiltered).length === 0) {
return this.$t('tournaments.placementsPendingFinals');
}
return this.$t('tournaments.noPlacementsYet');
}
},
methods: {
@@ -918,6 +961,24 @@ export default {
margin-bottom: 2rem;
}
.placements-summary {
display: flex;
flex-wrap: wrap;
gap: 0.6rem;
margin-bottom: 0.9rem;
}
.placements-chip {
display: inline-flex;
align-items: center;
padding: 0.35rem 0.7rem;
border-radius: 999px;
background: #f3f4f6;
color: #4b5563;
font-size: 0.88rem;
font-weight: 600;
}
.class-section {
margin-bottom: 2rem;
}
@@ -928,6 +989,49 @@ export default {
font-size: 1.1em;
}
.final-placement-list {
display: grid;
gap: 0.6rem;
}
.final-placement-card {
display: flex;
align-items: center;
gap: 0.85rem;
padding: 0.8rem 0.95rem;
border: 1px solid #e5e7eb;
border-radius: 12px;
background: #fff;
}
.final-placement-badge {
display: inline-flex;
align-items: center;
justify-content: center;
width: 2.4rem;
height: 2.4rem;
border-radius: 999px;
background: #dbeafe;
color: #1d4ed8;
font-weight: 700;
}
.final-placement-player {
appearance: none;
border: 0;
background: transparent;
padding: 0;
color: #1976d2;
cursor: pointer;
text-align: left;
font-size: 1rem;
text-decoration: underline;
}
.final-placement-player:hover {
color: #1565c0;
}
.class-type-badge {
display: inline-block;
margin-left: 0.5rem;
@@ -990,7 +1094,25 @@ th {
.no-placements {
text-align: center;
padding: 2rem;
color: #666;
border: 1px dashed #d1d5db;
border-radius: 12px;
background: #f9fafb;
color: #6b7280;
}
.no-placements-title {
margin: 0 0 0.35rem 0;
font-weight: 700;
color: #374151;
}
.no-placements-copy {
margin: 0;
line-height: 1.45;
}
.missing-data-pdf {
margin-top: 1.5rem;
}
.player-name-clickable {
@@ -1008,5 +1130,3 @@ table thead th:first-child,
table tbody td:first-child {
width: 4em;
}

View File

@@ -7,13 +7,23 @@
:selected-date="selectedDate"
@update:modelValue="$emit('update:selectedViewClass', $event)"
/>
<div class="results-summary">
<span v-if="filteredGroupMatches.length" class="results-chip">{{ filteredGroupMatches.length }} {{ $t('tournaments.groupMatches') }}</span>
<span v-if="filteredKnockoutMatches.length" class="results-chip">{{ filteredKnockoutMatches.length }} {{ $t('tournaments.koRound') }}</span>
<span v-if="openMatchCount > 0" class="results-chip results-chip-open">{{ openMatchCount }} {{ $t('tournaments.statusOpen') }}</span>
<span v-if="liveMatchCount > 0" class="results-chip results-chip-live">{{ liveMatchCount }} {{ $t('tournaments.statusLive') }}</span>
<span v-if="finishedMatchCount > 0" class="results-chip">{{ finishedMatchCount }} {{ $t('tournaments.statusFinished') }}</span>
</div>
<div v-if="numberOfTables && (filteredGroupMatches.length || filteredKnockoutMatches.length)" class="distribute-tables-bar">
<button @click="$emit('distribute-tables')" class="btn-primary">
{{ $t('tournaments.distributeTables') }}
</button>
</div>
<section v-if="filteredGroupMatches.length" class="group-matches">
<h4>{{ $t('tournaments.groupMatches') }}</h4>
<div class="results-section-header">
<h4>{{ $t('tournaments.groupMatches') }}</h4>
<span class="results-chip">{{ filteredGroupMatches.length }}</span>
</div>
<table>
<thead>
<tr>
@@ -97,7 +107,7 @@
<div class="new-set-line">
<input
v-model="m.resultInput"
placeholder="Neuen Satz, z.B. 11:7"
:placeholder="$t('tournaments.newSetPlaceholder')"
@keyup.enter="$emit('save-match-result', m, m.resultInput)"
@blur="$emit('save-match-result', m, m.resultInput)"
class="inline-input"
@@ -112,37 +122,43 @@
{{ getSetsString(m) }}
</td>
<td>
<span :class="['status-badge', getMatchStateClass(m)]">{{ getMatchStateLabel(m) }}</span>
<template v-if="matchHasGaveUp(m)">
<span class="no-edit-hint">{{ $t('tournaments.gaveUp') }}</span>
</template>
<template v-else>
<button v-if="!m.isFinished" @click="$emit('finish-match', m)">Abschließen</button>
<button v-else @click="$emit('reopen-match', m)" class="btn-correct">Korrigieren</button>
<button v-if="!m.isFinished && !m.isActive" @click.stop="$emit('set-match-active', m, true)" class="btn-live" title="Als laufend markieren"></button>
<button v-if="!m.isFinished && m.isActive" @click.stop="$emit('set-match-active', m, false)" class="btn-live active" title="Laufend-Markierung entfernen"></button>
<div class="action-row">
<button v-if="!m.isFinished" @click="$emit('finish-match', m)">{{ $t('tournaments.finishMatch') }}</button>
<button v-else @click="$emit('reopen-match', m)" class="btn-correct">{{ $t('tournaments.correctMatch') }}</button>
<button v-if="!m.isFinished && !m.isActive" @click.stop="$emit('set-match-active', m, true)" class="btn-live" :title="$t('tournaments.markMatchLive')"></button>
<button v-if="!m.isFinished && m.isActive" @click.stop="$emit('set-match-active', m, false)" class="btn-live active" :title="$t('tournaments.unmarkMatchLive')"></button>
</div>
</template>
</td>
</tr>
</tbody>
</table>
</section>
<div v-if="participants.length > 1 && !filteredGroupMatches.length && !filteredKnockoutMatches.length" class="start-matches" style="margin-top:1.5rem">
<button @click="$emit('start-matches')">
<div v-if="participants.length > 1 && !filteredGroupMatches.length && !filteredKnockoutMatches.length" class="results-next-step">
<button @click="$emit('start-matches')" class="btn-primary">
{{ $t('tournaments.createMatches') }}
</button>
</div>
<div v-if="canStartKnockout && !showKnockout && numberOfGroupsForSelectedClass > 1" class="ko-start">
<button @click="$emit('start-knockout')">
<div v-if="canStartKnockout && !showKnockout && numberOfGroupsForSelectedClass > 1" class="results-next-step">
<button @click="$emit('start-knockout')" class="btn-primary">
{{ $t('tournaments.startKORound') }}
</button>
</div>
<div v-if="showKnockout && canResetKnockout && numberOfGroupsForSelectedClass > 1" class="ko-reset" style="margin-top:1rem">
<div v-if="showKnockout && canResetKnockout && numberOfGroupsForSelectedClass > 1" class="results-next-step results-next-step-muted">
<button @click="$emit('reset-knockout')" class="trash-btn">
🗑 {{ $t('tournaments.deleteKORound') }}
</button>
</div>
<section v-if="showKnockout && numberOfGroupsForSelectedClass > 1 && filteredKnockoutMatches.length" class="ko-round">
<h4>{{ $t('tournaments.koRound') }}</h4>
<div class="results-section-header">
<h4>{{ $t('tournaments.koRound') }}</h4>
<span class="results-chip">{{ filteredKnockoutMatches.length }}</span>
</div>
<table>
<thead>
<tr>
@@ -212,7 +228,7 @@
<div class="new-set-line">
<input
v-model="m.resultInput"
placeholder="Neuen Satz, z.B. 11:7"
:placeholder="$t('tournaments.newSetPlaceholder')"
@keyup.enter="$emit('save-match-result', m, m.resultInput)"
@blur="$emit('save-match-result', m, m.resultInput)"
class="inline-input"
@@ -227,29 +243,32 @@
{{ getSetsString(m) }}
</td>
<td>
<button v-if="!m.isFinished" @click="$emit('finish-match', m)">Fertig</button>
<button v-else @click="$emit('reopen-match', m)" class="btn-correct">Korrigieren</button>
<button v-if="!m.isFinished && !m.isActive" @click.stop="$emit('set-match-active', m, true)" class="btn-live" title="Als laufend markieren"></button>
<button v-if="!m.isFinished && m.isActive" @click.stop="$emit('set-match-active', m, false)" class="btn-live active" title="Laufend-Markierung entfernen"></button>
<span :class="['status-badge', getMatchStateClass(m)]">{{ getMatchStateLabel(m) }}</span>
<div class="action-row">
<button v-if="!m.isFinished" @click="$emit('finish-match', m)">{{ $t('tournaments.finishMatch') }}</button>
<button v-else @click="$emit('reopen-match', m)" class="btn-correct">{{ $t('tournaments.correctMatch') }}</button>
<button v-if="!m.isFinished && !m.isActive" @click.stop="$emit('set-match-active', m, true)" class="btn-live" :title="$t('tournaments.markMatchLive')"></button>
<button v-if="!m.isFinished && m.isActive" @click.stop="$emit('set-match-active', m, false)" class="btn-live active" :title="$t('tournaments.unmarkMatchLive')"></button>
</div>
</td>
</tr>
</tbody>
</table>
</section>
<section v-if="Object.keys(filteredGroupedRankingList).length > 0" class="ranking">
<h4>Rangliste</h4>
<h4>{{ $t('tournaments.resultsRanking') }}</h4>
<template v-for="(classKey, idx) in Object.keys(filteredGroupedRankingList).sort((a, b) => {
const aNum = a === 'null' ? 999999 : parseInt(a);
const bNum = b === 'null' ? 999999 : parseInt(b);
return aNum - bNum;
})" :key="`class-${classKey}`">
<div v-if="idx > 0" style="margin-top: 2rem;"></div>
<div v-if="idx > 0" class="ranking-class-gap"></div>
<h5 v-if="getClassName(classKey)">{{ getClassName(classKey) }}</h5>
<table>
<thead>
<tr>
<th>Platz</th>
<th>Spieler</th>
<th>{{ $t('tournaments.position') }}</th>
<th>{{ $t('tournaments.player') }}</th>
</tr>
</thead>
<tbody>
@@ -343,14 +362,14 @@ export default {
},
computed: {
filteredGroupMatches() {
return this.filterMatchesByClass(this.groupMatches);
return this.sortMatchesForDisplay(this.filterMatchesByClass(this.groupMatches));
},
filteredKnockoutMatches() {
return this.filterMatchesByClass(this.knockoutMatches);
return this.sortMatchesForDisplay(this.filterMatchesByClass(this.knockoutMatches));
},
filteredGroupedRankingList() {
// Wenn keine Klasse ausgewählt ist (null), zeige alle
if (this.selectedViewClass === null || this.selectedViewClass === undefined) {
if (this.selectedViewClass === null || this.selectedViewClass === undefined || this.selectedViewClass === '' || this.selectedViewClass === '__all__' || this.selectedViewClass === 'all') {
return this.groupedRankingList;
}
// Wenn "Ohne Klasse" ausgewählt ist
@@ -394,7 +413,7 @@ export default {
);
// Wenn keine Klasse ausgewählt ist, zähle alle Stage 1 Gruppen
if (this.selectedViewClass === null || this.selectedViewClass === undefined) {
if (this.selectedViewClass === null || this.selectedViewClass === undefined || this.selectedViewClass === '' || this.selectedViewClass === '__all__' || this.selectedViewClass === 'all') {
return groupsToCount.length;
}
@@ -415,6 +434,15 @@ export default {
}
return Number(g.classId) === selectedId;
}).length;
},
liveMatchCount() {
return [...this.filteredGroupMatches, ...this.filteredKnockoutMatches].filter(m => m.isActive && !m.isFinished).length;
},
openMatchCount() {
return [...this.filteredGroupMatches, ...this.filteredKnockoutMatches].filter(m => !m.isActive && !m.isFinished && !this.matchHasGaveUp(m)).length;
},
finishedMatchCount() {
return [...this.filteredGroupMatches, ...this.filteredKnockoutMatches].filter(m => m.isFinished).length;
}
},
emits: [
@@ -437,7 +465,7 @@ export default {
methods: {
filterMatchesByClass(matches) {
// Wenn keine Klasse ausgewählt ist (null), zeige alle
if (this.selectedViewClass === null || this.selectedViewClass === undefined) {
if (this.selectedViewClass === null || this.selectedViewClass === undefined || this.selectedViewClass === '' || this.selectedViewClass === '__all__' || this.selectedViewClass === 'all') {
return matches;
}
// Wenn "Ohne Klasse" ausgewählt ist
@@ -457,6 +485,25 @@ export default {
return Number(matchClassId) === selectedId;
});
},
sortMatchesForDisplay(matches) {
const stateWeight = (match) => {
if (match.isActive && !match.isFinished) return 0;
if (!match.isFinished && !this.matchHasGaveUp(match)) return 1;
if (this.matchHasGaveUp(match)) return 2;
return 3;
};
return [...matches].sort((a, b) => {
const byState = stateWeight(a) - stateWeight(b);
if (byState !== 0) return byState;
const roundA = Number(a.groupRound || 0);
const roundB = Number(b.groupRound || 0);
if (roundA !== roundB) return roundA - roundB;
const groupA = Number(a.groupNumber || 0);
const groupB = Number(b.groupNumber || 0);
if (groupA !== groupB) return groupA - groupB;
return Number(a.id || 0) - Number(b.id || 0);
});
},
getGroupClassName(groupId) {
if (!groupId) return '';
const group = this.groups.find(g => g.groupId === groupId);
@@ -570,12 +617,63 @@ export default {
},
matchHasGaveUp(match) {
return match.gaveUpMatch || match.player1?.gaveUp || match.player2?.gaveUp;
},
getMatchStateLabel(match) {
if (this.matchHasGaveUp(match)) return this.$t('tournaments.gaveUp');
if (match.isFinished) return this.$t('tournaments.statusFinished');
if (match.isActive) return this.$t('tournaments.statusLive');
return this.$t('tournaments.statusOpen');
},
getMatchStateClass(match) {
if (this.matchHasGaveUp(match)) return 'status-gaveup';
if (match.isFinished) return 'status-finished';
if (match.isActive) return 'status-live';
return 'status-open';
}
}
};
</script>
<style scoped>
.results-summary {
display: flex;
flex-wrap: wrap;
gap: 0.6rem;
margin-bottom: 1rem;
}
.results-chip {
display: inline-flex;
align-items: center;
padding: 0.35rem 0.7rem;
border-radius: 999px;
background: #f3f4f6;
color: #4b5563;
font-size: 0.9rem;
font-weight: 600;
}
.results-chip-live {
background: #dcfce7;
color: #166534;
}
.results-chip-open {
background: #fef3c7;
color: #92400e;
}
.results-section-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
}
.results-section-header h4 {
margin: 0 0 0.75rem 0;
}
/* Farbmarkierungen für Spiele */
.match-finished {
background-color: #e9ecef !important;
@@ -644,6 +742,66 @@ export default {
margin-bottom: 1rem;
}
.results-next-step {
display: flex;
justify-content: flex-start;
margin-top: 1rem;
margin-bottom: 1rem;
padding: 0.9rem 1rem;
border: 1px solid #dbeafe;
border-radius: 12px;
background: #f8fbff;
}
.results-next-step-muted {
border-color: #e5e7eb;
background: #f9fafb;
}
.ranking-class-gap {
margin-top: 2rem;
}
.status-badge {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 4.8rem;
padding: 0.2rem 0.45rem;
border-radius: 999px;
font-size: 0.78rem;
font-weight: 700;
margin-right: 0.5rem;
margin-bottom: 0.35rem;
}
.status-open {
background: #e5e7eb;
color: #4b5563;
}
.status-live {
background: #dcfce7;
color: #166534;
}
.status-finished {
background: #dbeafe;
color: #1d4ed8;
}
.status-gaveup {
background: #fee2e2;
color: #991b1b;
}
.action-row {
display: flex;
flex-wrap: wrap;
gap: 0.35rem;
align-items: center;
}
.table-select {
width: 3.5rem;
padding: 0.15rem 0.25rem;

View File

@@ -6,6 +6,7 @@
"common": {
"loading": "Lade...",
"save": "Speichere",
"saved": "Gespeichert",
"cancel": "Abbreche",
"delete": "Lösche",
"edit": "Bearbeite",
@@ -30,6 +31,8 @@
"approvals": "Freigabe",
"statistics": "Trainings-Statistik",
"tournaments": "Turnier",
"clubTournaments": "Vereinsturniere",
"tournamentParticipations": "Turnierteilnahmen",
"schedule": "Spielplän",
"clubSettings": "Vereinsiistellige",
"predefinedActivities": "Vordefinierte Aktivitäte",
@@ -120,11 +123,16 @@
"tournaments": {
"numberOfTables": "Aazahl Tisch",
"table": "Tisch",
"playerOne": "Spieler 1",
"playerTwo": "Spieler 2",
"groupsLabel": "Gruppe",
"knockoutLabel": "KO",
"distributeTables": "Freii Tisch verteile",
"distributeTablesResult": "Tischverteilig",
"noFreeTables": "Kei freie Tisch verfüegbar.",
"noAssignableMatches": "Kei Spiel verfüegbar, wo beidi Spieler frei sind.",
"tablesDistributed": "Tisch sind verteilt worde.",
"errorDistributingTables": "Fehler bim Verteile vo de Tisch.",
"missingDataPDF": "Fehlendi Date als PDF",
"missingDataPDFTitle": "Fehlendi Teilnehmerdaten Minimeisterschaft",
"missingDataPDFSubtitle": "Bitte d'fehlende Date (markiert mit ____) bi de Teilnehmer nochfroge und do notiere.",
@@ -137,7 +145,217 @@
"phone": "Telefon",
"generatingPDF": "PDF wird erstellt...",
"page": "Siite",
"dataNotRecorded": "No nid erfasst"
"statusOpen": "Offe",
"statusLive": "Live",
"statusFinished": "Fertig",
"dataNotRecorded": "No nid erfasst",
"resultsRanking": "Rangliste",
"newSetPlaceholder": "Neue Satz, z. B. 11:7",
"finishMatch": "Abschliesse",
"correctMatch": "Korrigiere",
"markMatchLive": "Als laufend markiere",
"unmarkMatchLive": "Laufend-Markierig entferne",
"stageConfigTitle": "Zwüscherunde & Endrunde",
"stageConfigLoading": "Lade Rundenkonfiguration …",
"stageConfigIntro": "D'Zwüscherunde isch optional. Wenn du sie aktiviersch, git's nacher immer e Endrunde. E KO-Endrunde wird als eis einzelnes Feld erzeugt.",
"useIntermediateStage": "Zwüscherunde verwände",
"intermediateRound": "Zwüscherunde (Runde 2)",
"finalRound": "Endrunde",
"playThirdPlace": "Um Platz 3 spiele",
"roundMode": "Modus",
"roundGroupCount": "Aazahl Gruppe",
"promotionRule12": "Wiitercho: Vorrunde → Zwüscherunde (1→2)",
"promotionRuleFinal": "Wiitercho: Runde {from} → Runde {to}",
"addPoolRule": "Pool-Regle hinzufüege",
"noPoolRulesYet12": "No kei Regle. Biispil: Plätz 1 und 2 -> oberi Runde-2-Gruppe.",
"noPoolRulesYetFinal": "No kei Regle. Biispil: Plätz 1 und 2 -> Endrunde.",
"placesFromEachGroup": "Plätz us jeder Gruppe (z. B. 1,2)",
"target": "Ziel",
"targetGroupCount": "Ziel-Gruppenaazahl",
"saveRounds": "Runde speichere",
"createFinalFromPreliminary": "Endrunde us Vorrunde erstelle",
"createIntermediateFromPreliminary": "Zwüscherunde us Vorrunde erstelle",
"createFinalFromIntermediate": "Endrunde us Zwüscherunde erstelle",
"stageConfigMissingIds": "Cha nid speichere: Vereins- oder Turnier-ID fählt.",
"stageConfigLoadError": "Fehler bim Lade vo de Rundenkonfiguration.",
"stageConfigBadServerResponse": "Ungültigi Server-Antwort.",
"stageCreated": "Runde {round} isch erstellt worde.",
"atLeastOnePoolRule": "Bitte mindestens e Pool-Regle für {label} aalege (z. B. Plätz 1,2).",
"enterMiniLocation": "Bitte gib en Ort ii.",
"selectTournamentFirst": "Bitte wähl zerscht es Turnier us.",
"errorGeneratingPdf": "Fehler bim Erstelle vom PDF.",
"orphanedMatchesRemoved": "{count} verwaisti Spiel entfernt.",
"noOrphanedMatchesFound": "Kei verwaisti Spiel gfunde.",
"enterClassName": "Bitte gib en Klassename ii.",
"deleteClassTitle": "Klass lösche",
"deleteClassConfirm": "Möchtsch d'Klass \"{name}\" würklich lösche?",
"deleteClassParticipantsDetached": "Alli Teilnehmer wärde vo dere Klass entfernt.",
"enterExternalParticipantName": "Bitte gib mindestens Vorname und Nachname ii.",
"noTournamentDate": "Kei Turnierdatum vorhande.",
"noValidTrainingParticipants": "Kei gültige Teilnehmer im Training für das Datum gfunde.",
"noTrainingParticipants": "Kei Teilnehmer im Training für das Datum gfunde.",
"noTrainingOnDate": "Kei Trainingstag für {date} gfunde.",
"selectTwoDifferentPlayers": "Bitte wähl zwei verschiedeni Spieler us.",
"pairingAlreadyExists": "Die Paarig git's scho.",
"participantNotFound": "Teilnehmer nid gfunde.",
"minimumParticipantsForPairings": "Mindestens 2 Teilnehmer für Paarige nötig.",
"deleteExistingPairingsConfirm": "Bestehendi Paarige wärde glöscht. Wiitermache?",
"pairingsCreatedWithErrors": "{successCount} Paarige erstellt, {errorCount} Fehler.",
"participantsNeedClassAssignment": "{count} Teilnehmendi sind no keire Klass zuegordnet. Die sötted zerscht zueteilt werde.",
"autoAssignableParticipantsHint": "{count} devo chönd automatisch zuegordnet werde.",
"assignmentReviewTitle": "{count} Teilnehmendi bruuched e Uuswahl:",
"conflictSuggestionLabel": "Vorschläg:",
"workspaceProblemsTitle": "{count} offeni Pünkt",
"problemConfigTitle": "Konfiguration unvollständig",
"problemConfigDescription": "Prüef Datum, Name, Gwünnsätz und mindestens e Klass.",
"problemUnassignedTitle": "{count} Teilnehmendi ohni Klass",
"problemUnassignedDescription": "Die Teilnehmendi bruuched no e manuelli Klassenzuewisig.",
"problemUnassignedAutoDescription": "{count} devo chönd direkt automatisch zuegordnet werde.",
"problemConflictsTitle": "{count} Teilnehmendi mit Konflikt",
"problemConflictsDescription": "Prüef unpassendi Klasse, fehlendi Date oder unklari Zuewisige.",
"problemDoublesTitle": "{count} offeni Doppelpartner",
"problemDoublesDescription": "Ei oder meh Doppelklasse bruuched no Partnerzuewisige.",
"problemDoublesAutoDescription": "D offeni Doppelklass cha direkt automatisch paarlet werde.",
"problemGroupsMissingTitle": "Gruppe no nid erstellt",
"problemGroupsMissingDescription": "Für s Gruppeturnier muesed zerscht Gruppe aagleit werde.",
"problemGroupMatchesTitle": "Gruppespiel no nid erzeugt",
"problemGroupMatchesDescription": "D Gruppe sind parat, aber d Spiel sind no nid erstellt.",
"problemKnockoutReadyTitle": "K.-o.-Rundi cha gstartet werde",
"problemKnockoutReadyDescription": "D Gruppenphase isch abgschlosse und d Endrundi cha jetzt erzeugt werde.",
"autoAssignEligible": "Automatisch zuewise",
"autoAssignEligibleNone": "Aktuell git es kei Teilnehmendi mit einderütiger automatische Klassenzuewisig.",
"autoAssignEligibleDone": "{count} Teilnehmendi sind automatisch zuegordnet worde.",
"autoAssignEligiblePartial": "{successCount} Teilnehmendi sind automatisch zuegordnet worde, bi {errorCount} hets nid klappt.",
"doublesPairingHint": "{count} Teilnehmendi i dere Doppelklass hend no kei Partner.",
"createSuggestedPairings": "Partner bilde",
"placementsPendingTitle": "Platzierige no nöd verfügbar",
"placementsPendingSelectedClass": "Für d aktuell usgwählti Klass git's no kei Platzierige.",
"placementsPendingNoGroups": "Sobald Gruppespiel oder e Schlussrundi vorhande sind, erschiined d Platzierige da.",
"placementsPendingGroups": "Gruppeplatzierige erschiined, sobald Gruppespiel vorhande sind.",
"placementsPendingFinals": "Endplatzierige erschiined, sobald mehri Gruppe oder e Schlussrundi vorhande sind.",
"showingTournamentCount": "{visible} vo {total}",
"noMatchingTournamentsTitle": "Kei passendie Turnier",
"noTournamentsCreatedYet": "Es sind no kei Turnier aagleit worde.",
"adjustTournamentSearch": "Pass de Suechbegriff aa oder leg es neus Turnier aa.",
"selectedTournament": "Aktivs Turnier",
"createGroupMatches": "Gruppespiel berechne",
"participantConflicts": "Konflikt",
"unpairedDoubles": "Doppel ohni Partner",
"noEligibleClass": "Kei passendi Klass gfunde.",
"eligibleClassesHint": "Passt zu: {classes}",
"warningClassMissing": "Klass nid gfunde",
"warningGenderMismatch": "Gschlächt passt nid zur Klass",
"warningGenderMissing": "Gschlächt fehlt für die Klass",
"warningBirthDateMissing": "Geburtsdatum fehlt für die Klass",
"warningTooOldForClass": "Z'alt für die Klass",
"warningTooYoungForClass": "Z'jung für die Klass",
"warningMissingPairing": "Doppelpartner fehlt",
"statusConfigReady": "Konfiguration parat",
"statusConfigIncomplete": "Konfiguration unvollständig",
"statusParticipantsUnassigned": "{count} ohni Klass",
"statusParticipantsConflicts": "{count} Teilnehmendi mit Konflikt",
"statusParticipantsReady": "{count} Teilnehmendi ohni Konflikt",
"statusUnpairedDoubles": "{count} Doppel ohni Partner",
"statusGroupsMissing": "Gruppe no nid erstellt",
"statusGroupsReady": "Gruppe parat",
"statusGroupsRunning": "Gruppenphase lauft",
"statusKnockoutReady": "K.-o.-Rundi startklar",
"statusKnockoutRunning": "K.-o.-Rundi lauft",
"statusTournamentCompleted": "Turnier abgeschlossen",
"statusActionAssign": "Zuewise",
"statusActionReview": "Prüefe",
"statusActionPair": "Paar bilde",
"statusActionCreate": "Erstelle",
"statusActionGenerate": "Erzüüge",
"statusActionStart": "Starte",
"stageConfigCurrentSetup": "Aktuelli Struktur",
"stageFlowWithIntermediate": "Vorrundi -> Zwüscherundi ({stage2}) -> Endrundi ({final})",
"stageFlowDirectFinal": "Vorrundi -> Endrundi ({final})",
"stageStep1": "Schritt 1",
"stageStep2": "Schritt 2",
"stageStep3": "Schritt 3",
"stageStep1Title": "Zwüscherundi festlege",
"stageStep1Description": "Entscheid zerscht, ob nach dr Vorrundi no e zusätzlechi Rundi gspielt werde söll.",
"stageStep2Description": "Leg fescht, wie d Zwüscherundi uusgseh söll und wie viu Gruppe sie brucht.",
"stageFinalDescription": "Leg fescht, ob d Endrundi als Gruppe oder direkt als K.-o.-Fäld gspielt wird.",
"promotionRuleDescription12": "Da legsch fescht, weli Plätz us jedere Vorrundegruppe i d Zwüscherundi chömed.",
"promotionRuleDescriptionFinalKnockout": "Da legsch fescht, weli Plätz us dr vorherige Rundi is K.-o.-Fäld chömed.",
"promotionRuleDescriptionFinalGroups": "Da legsch fescht, weli Plätz us dr vorherige Rundi i d Gruppe vo dr Endrundi chömed.",
"poolRuleLabel": "Regle {number}",
"poolRuleSummary": "Plätz {places} gönd i {target}",
"poolRulePlacesLabel": "Weli Plätz chömed wiiter?",
"poolRulePlacesHelp": "Bispiel: 1 oder 1,2",
"poolRuleTargetLabel": "Wohi gönd die Plätz?",
"poolRuleQuickExamples": "Schnällbispiel:",
"targetGroupsLabel": "{count} Gruppe",
"transferQualifiedToFinalFromPreliminary": "Qualifizierti Spieler direkt i d Endrundi übernäh",
"transferQualifiedToIntermediate": "Qualifizierti Spieler i d Zwüscherundi übernäh",
"transferQualifiedToFinalFromIntermediate": "Qualifizierti Spieler i d Endrundi übernäh",
"stageParticipantsTransferred": "Qualifizierti Spieler sind i {round} überno worde.",
"transferQualifiedToFinalFromPreliminaryDesc": "Brucht d Regle Vorrundi -> Endrundi und erstellt det direkt d qualifizierti Spieler.",
"transferQualifiedToIntermediateDesc": "Brucht d Regle Vorrundi -> Zwüscherundi und übernimmt d qualifizierti Spieler.",
"transferQualifiedToFinalFromIntermediateDesc": "Brucht d Regle Zwüscherundi -> Endrundi und übernimmt d qualifizierti Spieler.",
"stageActionsTitle": "Nächsti Aktione",
"stageActionsDescription": "Die Schritt übernähmed qualifizierti Spieler i d nöchsti Rundi.",
"stageActionRun": "Jetzt uusfüehre",
"stageActionMissingRulesHint": "Leg zerscht mindestens eini Witercho-Regle aa, bevor du Spieler i d nöchsti Rundi übernimmst.",
"poolRulePreview": "Us jedere Gruppe gönd Platz {places} wiiter i {target}.",
"poolRuleTargetKnockout": "s K.-o.-Fäld",
"poolRuleTargetGroupsDetailed": "{count} Zielgruppe",
"stageValidationTitle": "Konfiguration prüfen",
"stageValidationDescription": "Diese Punkte solltest du vor dem Speichern oder Übernehmen der nächsten Runde korrigieren.",
"stageValidationGroupCountRequired": "Bitte mindestens eine gültige Gruppenanzahl festlegen.",
"stageValidationNoRules": "Für {stage} ist noch keine Weiterkommens-Regel angelegt.",
"stageValidationRulePlacesRequired": "Trage mindestens einen Platz ein, der weiterkommen soll.",
"stageValidationRulePlacesInvalid": "Die Platzangabe ist ungültig. Erlaubt sind nur positive ganze Zahlen wie 1 oder 1,2.",
"stageValidationRulePlacesDuplicate": "Eine Regel darf denselben Platz nicht mehrfach enthalten.",
"stageValidationRulesOverlap": "Platz {place} ist doppelt vergeben, nämlich in Regel {first} und Regel {second}.",
"stageValidationTargetGroupsRequired": "Bitte eine gültige Anzahl von Zielgruppen angeben.",
"stageValidationKnockoutTargetOnly": "Bei einer K.-o.-Endrunde ist hier nur das K.-o.-Feld als Ziel erlaubt.",
"stageValidationSaveBlocked": "Bitte zuerst die markierten Konfigurationsfehler korrigieren.",
"stageValidationSuggestion": "Vorschlag: {value}",
"stageValidationRulePlacesGap": "Die Platzangabe hat Lücken. Meist ist eine zusammenhängende Reihenfolge wie 1,2 oder 1,2,3 gemeint.",
"stageValidationTargetGroupCountMismatch": "Die Anzahl der Zielgruppen muss zur Zielrunde passen. Erwartet: {expected}.",
"stageValidationTargetTypeMismatch": "Das Ziel dieser Regel muss zur Zielrunde passen. Erwartet: {target}.",
"stageValidationApplyFix": "Übernehmen",
"stageValidationApplyTargetTypeFix": "Auf {target} umstellen",
"stageValidationApplyTargetGroupFix": "Auf {count} Zielgruppen setzen",
"ruleStatusOk": "Passt",
"ruleStatusReview": "Prüfen",
"ruleStatusBlocked": "Blockiert",
"ruleStatusOkDescription": "Diese Regel passt zur aktuellen Zielrunde und kann so verwendet werden.",
"stageFlowHealthOk": "Rundenlogik passt",
"stageFlowHealthOkDescription": "Die Übergänge zwischen den Runden sind vollständig konfiguriert und aktuell stimmig.",
"stageFlowHealthIncomplete": "Rundenlogik unvollständig",
"stageFlowHealthMissingRules": "Mindestens ein Übergang hat noch keine vollständige Weiterkommens-Regel.",
"stageFlowHealthReviewDescription": "Die Rundenlogik ist grundsätzlich nutzbar, sollte aber wegen offener Hinweise noch geprüft werden.",
"stageFlowHealthBlocked": "Rundenlogik widersprüchlich",
"stageFlowHealthBlockedDescription": "Mindestens eine Regel passt aktuell nicht zur Zielrunde oder enthält blockierende Konfigurationsfehler.",
"stageFlowBreakdownOk": "Konfiguration passt",
"stageFlowBreakdownWarnings": "{count} Hinweis(e) offen",
"stageFlowBreakdownErrors": "{count} Fehler blockieren",
"stageFlowBreakdownMissingRule": "Noch keine vollständige Regel angelegt",
"stageFlowBreakdownActionAddRule": "Regel anlegen",
"stageFlowBreakdownActionReview": "Fehler prüfen",
"stageFlowBreakdownActionOpen": "Regel öffnen",
"stageValidationApplyAllFixes": "{count} Schnellkorrektur(en) anwenden",
"stageFlowRecommendationTitle": "Empfohlener nächster Schritt",
"stageFlowRecommendationFixes": "Die typischen Konfigurationsprobleme können direkt automatisch bereinigt werden.",
"stageFlowRecommendationMissingRule": "Für {label} fehlt noch eine vollständige Regel. Lege am besten damit los.",
"stageFlowRecommendationError": "Prüfe zuerst den ersten blockierenden Fehler in der Rundenlogik.",
"stageFlowRecommendationWarnings": "Es gibt noch Hinweise, die du vor dem Speichern prüfen solltest.",
"stageFlowRecommendationSave": "Die Rundenlogik ist stimmig. Du kannst die Konfiguration jetzt speichern.",
"stageFlowPreviewMissing": "Noch keine Regel konfiguriert.",
"stageFlowPreviewRule": "Plätze {places} gehen in {target}",
"stageValidationKnockoutNeedsTwoPlayers": "Für ein K.-o.-Feld werden mindestens 2 Qualifizierte benötigt.",
"stageValidationKnockoutByesLikely": "Mit voraussichtlich {count} Qualifizierten wird das K.-o.-Feld sehr wahrscheinlich Freilose enthalten.",
"stageValidationNotEnoughQualifiersForGroups": "Mit nur {count} Qualifizierten für {groups} Zielgruppen würden leere Gruppen entstehen.",
"stageValidationThinGroupsLikely": "Mit {count} Qualifizierten auf {groups} Zielgruppen werden die Gruppen vermutlich sehr klein.",
"stageFlowPreviewRuleWithCount": "{base} (voraussichtlich {count} Qualifizierte)",
"stageFlowReadinessNoGroups": "Noch keine passenden Gruppen vorhanden",
"stageFlowReadinessIncompleteGroups": "{count} Gruppe(n) sind noch nicht vollständig entschieden",
"stageFlowReadinessReady": "Übergang ist fachlich startklar",
"stageFlowQualifiedPreviewTitle": "Aktuell qualifiziert",
"stageFlowQualifiedPreviewEntry": "G{group} · Platz {position} · {name}"
}
}

View File

@@ -304,7 +304,62 @@
"phone": "Telefon",
"generatingPDF": "PDF wird erstellt...",
"page": "Seite",
"dataNotRecorded": "Noch nicht erfasst"
"dataNotRecorded": "Noch nicht erfasst",
"stageValidationTitle": "Review configuration",
"stageValidationDescription": "Fix these points before saving or moving players into the next round.",
"stageValidationGroupCountRequired": "Please set at least one valid group count.",
"stageValidationNoRules": "There is no advancement rule yet for {stage}.",
"stageValidationRulePlacesRequired": "Enter at least one place that should advance.",
"stageValidationRulePlacesInvalid": "The place definition is invalid. Only positive whole numbers such as 1 or 1,2 are allowed.",
"stageValidationRulePlacesDuplicate": "A single rule must not contain the same place more than once.",
"stageValidationRulesOverlap": "Place {place} is assigned twice, in rule {first} and rule {second}.",
"stageValidationTargetGroupsRequired": "Please enter a valid number of target groups.",
"stageValidationKnockoutTargetOnly": "For a knockout final round, only the knockout bracket is allowed as the target here.",
"stageValidationSaveBlocked": "Please fix the highlighted configuration errors first.",
"stageValidationSuggestion": "Vorschlag: {value}",
"stageValidationRulePlacesGap": "Die Platzangabe hat Lücken. Meist ist eine zusammenhängende Reihenfolge wie 1,2 oder 1,2,3 gemeint.",
"stageValidationTargetGroupCountMismatch": "Die Anzahl der Zielgruppen muss zur Zielrunde passen. Erwartet: {expected}.",
"stageValidationTargetTypeMismatch": "Das Ziel dieser Regel muss zur Zielrunde passen. Erwartet: {target}.",
"stageValidationApplyFix": "Übernehmen",
"stageValidationApplyTargetTypeFix": "Auf {target} umstellen",
"stageValidationApplyTargetGroupFix": "Auf {count} Zielgruppen setzen",
"ruleStatusOk": "Passt",
"ruleStatusReview": "Prüfen",
"ruleStatusBlocked": "Blockiert",
"ruleStatusOkDescription": "Diese Regel passt zur aktuellen Zielrunde und kann so verwendet werden.",
"stageFlowHealthOk": "Rundenlogik passt",
"stageFlowHealthOkDescription": "Die Übergänge zwischen den Runden sind vollständig konfiguriert und aktuell stimmig.",
"stageFlowHealthIncomplete": "Rundenlogik unvollständig",
"stageFlowHealthMissingRules": "Mindestens ein Übergang hat noch keine vollständige Weiterkommens-Regel.",
"stageFlowHealthReviewDescription": "Die Rundenlogik ist grundsätzlich nutzbar, sollte aber wegen offener Hinweise noch geprüft werden.",
"stageFlowHealthBlocked": "Rundenlogik widersprüchlich",
"stageFlowHealthBlockedDescription": "Mindestens eine Regel passt aktuell nicht zur Zielrunde oder enthält blockierende Konfigurationsfehler.",
"stageFlowBreakdownOk": "Konfiguration passt",
"stageFlowBreakdownWarnings": "{count} Hinweis(e) offen",
"stageFlowBreakdownErrors": "{count} Fehler blockieren",
"stageFlowBreakdownMissingRule": "Noch keine vollständige Regel angelegt",
"stageFlowBreakdownActionAddRule": "Regel anlegen",
"stageFlowBreakdownActionReview": "Fehler prüfen",
"stageFlowBreakdownActionOpen": "Regel öffnen",
"stageValidationApplyAllFixes": "{count} Schnellkorrektur(en) anwenden",
"stageFlowRecommendationTitle": "Empfohlener nächster Schritt",
"stageFlowRecommendationFixes": "Die typischen Konfigurationsprobleme können direkt automatisch bereinigt werden.",
"stageFlowRecommendationMissingRule": "Für {label} fehlt noch eine vollständige Regel. Lege am besten damit los.",
"stageFlowRecommendationError": "Prüfe zuerst den ersten blockierenden Fehler in der Rundenlogik.",
"stageFlowRecommendationWarnings": "Es gibt noch Hinweise, die du vor dem Speichern prüfen solltest.",
"stageFlowRecommendationSave": "Die Rundenlogik ist stimmig. Du kannst die Konfiguration jetzt speichern.",
"stageFlowPreviewMissing": "Noch keine Regel konfiguriert.",
"stageFlowPreviewRule": "Plätze {places} gehen in {target}",
"stageValidationKnockoutNeedsTwoPlayers": "Für ein K.-o.-Feld werden mindestens 2 Qualifizierte benötigt.",
"stageValidationKnockoutByesLikely": "Mit voraussichtlich {count} Qualifizierten wird das K.-o.-Feld sehr wahrscheinlich Freilose enthalten.",
"stageValidationNotEnoughQualifiersForGroups": "Mit nur {count} Qualifizierten für {groups} Zielgruppen würden leere Gruppen entstehen.",
"stageValidationThinGroupsLikely": "Mit {count} Qualifizierten auf {groups} Zielgruppen werden die Gruppen vermutlich sehr klein.",
"stageFlowPreviewRuleWithCount": "{base} (voraussichtlich {count} Qualifizierte)",
"stageFlowReadinessNoGroups": "Noch keine passenden Gruppen vorhanden",
"stageFlowReadinessIncompleteGroups": "{count} Gruppe(n) sind noch nicht vollständig entschieden",
"stageFlowReadinessReady": "Übergang ist fachlich startklar",
"stageFlowQualifiedPreviewTitle": "Aktuell qualifiziert",
"stageFlowQualifiedPreviewEntry": "G{group} · Platz {position} · {name}"
},
"permissions": {
"title": "Berechtigungsverwaltung",
@@ -395,5 +450,3 @@
"recipients": "Empfänger"
}
}

View File

@@ -6,6 +6,7 @@
"common": {
"loading": "Lade...",
"save": "Speichern",
"saved": "Gespeichert",
"cancel": "Abbrechen",
"delete": "Löschen",
"edit": "Bearbeiten",
@@ -38,7 +39,6 @@
"view": "Anzeigen",
"name": "Name",
"date": "Datum",
"time": "Zeit",
"status": "Status",
"type": "Typ",
"description": "Beschreibung",
@@ -65,6 +65,8 @@
"approvals": "Freigaben",
"statistics": "Trainings-Statistik",
"tournaments": "Turniere",
"clubTournaments": "Vereinsturniere",
"tournamentParticipations": "Turnierteilnahmen",
"schedule": "Spielpläne",
"clubSettings": "Vereinseinstellungen",
"predefinedActivities": "Vordefinierte Aktivitäten",
@@ -243,8 +245,6 @@
"name": "Name, Vorname",
"ttrQttr": "TTR / QTTR",
"address": "Adresse",
"birthdate": "Geburtsdatum",
"gender": "Geschlecht",
"active": "Aktiv",
"actions": "Aktionen",
"phoneNumberShort": "Telefon-Nr.",
@@ -308,7 +308,7 @@
"createGroups": "Gruppen erstellen",
"trainingPlan": "Trainingsplan",
"startTime": "Startzeit",
"group": "Gruppe",
"group": "Gruppe...",
"timeblock": "Zeitblock",
"assignParticipants": "Teilnehmer zuordnen",
"addTimeblock": "Zeitblock",
@@ -327,11 +327,9 @@
"addGroup": "Gruppe hinzufügen",
"activityOrTimeblock": "Aktivität / Zeitblock",
"durationMinutes": "Dauer (Min)",
"assignParticipants": "Teilnehmer zuordnen",
"addGroupActivity": "Gruppen-Aktivität hinzufügen",
"addGroupButton": "+ Gruppe",
"all": "Alle",
"group": "Gruppe...",
"selectGroup": "Gruppe auswählen...",
"activityPlaceholder": "Aktivität",
"showImage": "Bild/Zeichnung anzeigen",
@@ -482,12 +480,360 @@
}
},
"tournaments": {
"title": "Turniere",
"internalTournaments": "Interne Turniere",
"openTournaments": "Offene Turniere",
"miniChampionships": "Minimeisterschaften",
"newMiniChampionship": "Neue Minimeisterschaft",
"miniChampionshipYear": "Jahr",
"miniChampionshipYearHint": "12 = in diesem Jahr 12 oder 13 Jahre, 10 = 10 oder 11, 8 = 9 und jünger",
"miniChampionshipLocation": "Ort",
"tournamentParticipations": "Turnierteilnahmen",
"date": "Datum",
"newTournament": "Neues Turnier",
"unknownDate": "Unbekanntes Datum",
"name": "Name",
"tournamentName": "Turniername",
"events": "Veranstaltungen",
"participations": "Turnierbeteiligungen",
"showEvents": "Gespeicherte Veranstaltungen anzeigen",
"showParticipations": "Turnierbeteiligungen anzeigen"
"winningSets": "Gewinnsätze",
"numberOfTables": "Anzahl Tische",
"table": "Tisch",
"playerOne": "Spieler 1",
"playerTwo": "Spieler 2",
"groupsLabel": "Gruppen",
"knockoutLabel": "KO",
"distributeTables": "Freie Tische verteilen",
"distributeTablesResult": "Tischverteilung",
"noFreeTables": "Keine freien Tische verfügbar.",
"noAssignableMatches": "Keine Spiele verfügbar, bei denen beide Spieler frei sind.",
"tablesDistributed": "Tische wurden verteilt.",
"errorDistributingTables": "Fehler beim Verteilen der Tische.",
"missingDataPDF": "Fehlende Daten als PDF",
"missingDataPDFTitle": "Fehlende Teilnehmerdaten Minimeisterschaft",
"missingDataPDFSubtitle": "Bitte die fehlenden Daten (markiert mit ____) bei den Teilnehmern erfragen und hier notieren.",
"allDataComplete": "Alle Teilnehmerdaten sind vollständig.",
"allDataCompleteTop3": "Alle Daten der Top-3-Platzierten sind vollständig.",
"noTop3Yet": "Es stehen noch keine Top-3-Platzierungen fest.",
"missingDataPDFTitleTop3": "Fehlende Daten Top 3 Minimeisterschaft",
"missingDataPDFSubtitleTop3": "Fehlende Daten der ersten 3 Plätze (markiert mit ____) bitte erfragen und hier notieren.",
"address": "Adresse",
"phone": "Telefon",
"generatingPDF": "PDF wird erstellt...",
"page": "Seite",
"create": "Erstellen",
"exportPDF": "PDF exportieren",
"playInGroups": "Spielen in Gruppen",
"classes": "Klassen",
"save": "Speichern",
"cancel": "Abbrechen",
"edit": "Bearbeiten",
"delete": "Löschen",
"className": "Klassenname",
"addClass": "Klasse hinzufügen",
"noClassesYet": "Noch keine Klassen vorhanden. Fügen Sie eine neue Klasse hinzu.",
"singles": "Einzel",
"doubles": "Doppel",
"genderAll": "Alle",
"genderMixed": "Mixed",
"minBirthYear": "Geboren im Jahr oder später",
"selectClass": "Klasse auswählen",
"tabConfig": "Konfiguration",
"tabGroups": "Gruppen",
"tabParticipants": "Teilnehmer",
"tabResults": "Ergebnisse",
"tabPlacements": "Platzierungen",
"finalPlacements": "Endplatzierungen",
"groupPlacements": "Gruppenplatzierungen",
"noPlacementsYet": "Noch keine Platzierungen vorhanden.",
"participants": "Teilnehmer",
"seeded": "Gesetzt",
"gaveUp": "Aufgegeben",
"statusOpen": "Offen",
"statusLive": "Live",
"statusFinished": "Fertig",
"gaveUpHint": "Spieler hat aufgegeben alle Spiele zählen für den Gegner (11:0) bzw. 0:0 bei beiden aufgegeben.",
"club": "Verein",
"class": "Klasse",
"group": "Gruppe",
"action": "Aktion",
"unknown": "Unbekannt",
"clubMember": "(Vereinsmitglied)",
"selectParticipant": "-- Teilnehmer auswählen --",
"add": "Hinzufügen",
"loadFromTraining": "Aus Trainingstag laden",
"addExternalParticipant": "Externen Teilnehmer hinzufügen",
"firstName": "Vorname",
"lastName": "Nachname",
"optional": "optional",
"birthdate": "Geburtsdatum",
"addClubMember": "Vereinsmitglied hinzufügen",
"advancersPerGroup": "Aufsteiger pro Gruppe",
"maxGroupSize": "Maximale Gruppengröße",
"groupsPerClass": "Gruppen",
"groupsPerClassHint": "Geben Sie für jede Klasse die Anzahl der Gruppen ein (0 = keine Gruppen für diese Klasse):",
"showClass": "Klasse anzeigen",
"allClasses": "Alle Klassen",
"withoutClass": "Ohne Klasse",
"currentClass": "Aktive Klasse",
"selectClassPrompt": "Bitte wählen Sie oben eine Klasse aus.",
"numberOfGroups": "Anzahl Gruppen",
"createGroups": "Gruppen erstellen",
"randomizeGroups": "Zufällig verteilen",
"resetGroups": "Gruppen zurücksetzen",
"groupsOverview": "Gruppenübersicht",
"groupNumber": "Gruppe",
"index": "Index",
"position": "Platz",
"player": "Spieler",
"points": "Punkte",
"sets": "Sätze",
"diff": "Diff",
"pointsRatio": "Spielpunkte",
"livePosition": "Live-Platz",
"pairings": "Doppel-Paarungen",
"addPairing": "Paarung hinzufügen",
"selectPlayer": "Spieler auswählen",
"external": "Extern",
"randomPairings": "Zufällige Doppel-Paarungen",
"errorMoreSeededThanUnseeded": "Es gibt mehr gesetzte als nicht gesetzte Spieler. Zufällige Paarungen können nicht erstellt werden.",
"randomPairingsCreated": "Zufällige Paarungen wurden erstellt.",
"resetGroupMatches": "Gruppenspiele",
"cleanupOrphanedMatches": "Verwaiste Spiele aufräumen",
"groupMatches": "Gruppenspiele",
"round": "Runde",
"encounter": "Begegnung",
"result": "Ergebnis",
"setDiff": "Satzdifferenz",
"createMatches": "Spiele erstellen",
"startKORound": "K.o.-Runde starten",
"deleteKORound": "K.o.-Runde",
"email": "E-Mail",
"forForwarding": "für Weitermeldung",
"showPlayerDetails": "Spielerdetails anzeigen",
"noPlayerDataAvailable": "Keine Spielerdaten verfügbar",
"dataNotRecorded": "Noch nicht erfasst",
"resultsRanking": "Rangliste",
"newSetPlaceholder": "Neuen Satz, z. B. 11:7",
"finishMatch": "Abschließen",
"correctMatch": "Korrigieren",
"markMatchLive": "Als laufend markieren",
"unmarkMatchLive": "Laufend-Markierung entfernen",
"stageConfigTitle": "Zwischenrunde & Endrunde",
"stageConfigLoading": "Lade Zwischenrunden …",
"stageConfigIntro": "Zwischenrunde ist optional. Wenn du sie aktivierst, gibt es danach immer eine Endrunde. KO-Endrunde wird als ein einziges Feld erzeugt.",
"stageConfigCurrentSetup": "Aktuelle Struktur",
"stageFlowWithIntermediate": "Vorrunde -> Zwischenrunde ({stage2}) -> Endrunde ({final})",
"stageFlowDirectFinal": "Vorrunde -> Endrunde ({final})",
"stageStep1": "Schritt 1",
"stageStep2": "Schritt 2",
"stageStep3": "Schritt 3",
"stageStep1Title": "Zwischenrunde festlegen",
"stageStep1Description": "Entscheide zuerst, ob nach der Vorrunde noch eine zusätzliche Runde gespielt werden soll.",
"stageStep2Description": "Lege fest, wie die Zwischenrunde aussieht und wie viele Gruppen dort gebraucht werden.",
"stageFinalDescription": "Bestimme, ob die Endrunde als Gruppenphase oder direkt als K.-o.-Feld gespielt wird.",
"useIntermediateStage": "Zwischenrunde verwenden",
"intermediateRound": "Zwischenrunde (Runde 2)",
"finalRound": "Endrunde",
"playThirdPlace": "Platz 3 ausspielen",
"roundMode": "Modus",
"roundGroupCount": "Anzahl Gruppen",
"promotionRule12": "Weiterkommen: Vorrunde → Zwischenrunde (1→2)",
"promotionRuleFinal": "Weiterkommen: Runde {from} → Runde {to}",
"promotionRuleDescription12": "Hier legst du fest, welche Plätze aus jeder Vorrundengruppe in die Zwischenrunde kommen.",
"promotionRuleDescriptionFinalKnockout": "Hier legst du fest, welche Plätze aus der vorherigen Runde in das K.-o.-Feld kommen.",
"promotionRuleDescriptionFinalGroups": "Hier legst du fest, welche Plätze aus der vorherigen Runde in die Gruppen der Endrunde kommen.",
"poolRuleLabel": "Regel {number}",
"poolRuleSummary": "Plätze {places} gehen in {target}",
"poolRulePreview": "Aus jeder Gruppe gehen Platz {places} weiter in {target}.",
"poolRulePlacesLabel": "Welche Plätze kommen weiter?",
"poolRulePlacesHelp": "Beispiel: 1 oder 1,2",
"poolRuleTargetLabel": "Wohin gehen diese Plätze?",
"poolRuleQuickExamples": "Schnellbeispiele:",
"targetGroupsLabel": "{count} Gruppen",
"poolRuleTargetKnockout": "das K.-o.-Feld",
"poolRuleTargetGroupsDetailed": "{count} Zielgruppen",
"addPoolRule": "Pool-Regel hinzufügen",
"noPoolRulesYet12": "Noch keine Regeln. Beispiel: Plätze 1 und 2 -> obere Runde-2-Gruppen.",
"noPoolRulesYetFinal": "Noch keine Regeln. Beispiel: Plätze 1 und 2 -> Endrunde.",
"placesFromEachGroup": "Plätze aus jeder Gruppe (z. B. 1,2)",
"target": "Ziel",
"targetGroupCount": "Ziel-Gruppenanzahl",
"saveRounds": "Runden speichern",
"createFinalFromPreliminary": "Endrunde aus Vorrunde erstellen",
"createIntermediateFromPreliminary": "Zwischenrunde aus Vorrunde erstellen",
"createFinalFromIntermediate": "Endrunde aus Zwischenrunde erstellen",
"transferQualifiedToFinalFromPreliminary": "Qualifizierte Spieler direkt in die Endrunde übernehmen",
"transferQualifiedToIntermediate": "Qualifizierte Spieler in die Zwischenrunde übernehmen",
"transferQualifiedToFinalFromIntermediate": "Qualifizierte Spieler in die Endrunde übernehmen",
"transferQualifiedToFinalFromPreliminaryDesc": "Nutzt die Regeln Vorrunde -> Endrunde und erzeugt daraus direkt die qualifizierten Teilnehmer.",
"transferQualifiedToIntermediateDesc": "Nutzt die Regeln Vorrunde -> Zwischenrunde und übernimmt die qualifizierten Teilnehmer.",
"transferQualifiedToFinalFromIntermediateDesc": "Nutzt die Regeln Zwischenrunde -> Endrunde und übernimmt die qualifizierten Teilnehmer.",
"stageActionsTitle": "Nächste Aktionen",
"stageActionsDescription": "Diese Schritte übernehmen qualifizierte Spieler in die nächste Runde.",
"stageActionRun": "Jetzt ausführen",
"stageActionMissingRulesHint": "Lege zuerst mindestens eine Weiterkommens-Regel an, bevor du Spieler in die nächste Runde übernimmst.",
"stageValidationTitle": "Konfiguration prüfen",
"stageValidationDescription": "Diese Punkte solltest du vor dem Speichern oder Übernehmen der nächsten Runde korrigieren.",
"stageValidationGroupCountRequired": "Bitte mindestens eine gültige Gruppenanzahl festlegen.",
"stageValidationNoRules": "Für {stage} ist noch keine Weiterkommens-Regel angelegt.",
"stageValidationRulePlacesRequired": "Trage mindestens einen Platz ein, der weiterkommen soll.",
"stageValidationRulePlacesInvalid": "Die Platzangabe ist ungültig. Erlaubt sind nur positive ganze Zahlen wie 1 oder 1,2.",
"stageValidationRulePlacesDuplicate": "Eine Regel darf denselben Platz nicht mehrfach enthalten.",
"stageValidationRulePlacesGap": "Die Platzangabe hat Lücken. Meist ist eine zusammenhängende Reihenfolge wie 1,2 oder 1,2,3 gemeint.",
"stageValidationRulesOverlap": "Platz {place} ist doppelt vergeben, nämlich in Regel {first} und Regel {second}.",
"stageValidationTargetGroupsRequired": "Bitte eine gültige Anzahl von Zielgruppen angeben.",
"stageValidationTargetGroupCountMismatch": "Die Anzahl der Zielgruppen muss zur Zielrunde passen. Erwartet: {expected}.",
"stageValidationTargetTypeMismatch": "Das Ziel dieser Regel muss zur Zielrunde passen. Erwartet: {target}.",
"stageValidationKnockoutTargetOnly": "Bei einer K.-o.-Endrunde ist hier nur das K.-o.-Feld als Ziel erlaubt.",
"stageValidationKnockoutNeedsTwoPlayers": "Für ein K.-o.-Feld werden mindestens 2 Qualifizierte benötigt.",
"stageValidationKnockoutByesLikely": "Mit voraussichtlich {count} Qualifizierten wird das K.-o.-Feld sehr wahrscheinlich Freilose enthalten.",
"stageValidationNotEnoughQualifiersForGroups": "Mit nur {count} Qualifizierten für {groups} Zielgruppen würden leere Gruppen entstehen.",
"stageValidationThinGroupsLikely": "Mit {count} Qualifizierten auf {groups} Zielgruppen werden die Gruppen vermutlich sehr klein.",
"stageValidationSuggestion": "Vorschlag: {value}",
"stageValidationApplyFix": "Übernehmen",
"stageValidationApplyAllFixes": "{count} Schnellkorrektur(en) anwenden",
"stageValidationApplyTargetTypeFix": "Auf {target} umstellen",
"stageValidationApplyTargetGroupFix": "Auf {count} Zielgruppen setzen",
"stageValidationSaveBlocked": "Bitte zuerst die markierten Konfigurationsfehler korrigieren.",
"ruleStatusOk": "Passt",
"ruleStatusReview": "Prüfen",
"ruleStatusBlocked": "Blockiert",
"ruleStatusOkDescription": "Diese Regel passt zur aktuellen Zielrunde und kann so verwendet werden.",
"stageFlowHealthOk": "Rundenlogik passt",
"stageFlowHealthOkDescription": "Die Übergänge zwischen den Runden sind vollständig konfiguriert und aktuell stimmig.",
"stageFlowHealthIncomplete": "Rundenlogik unvollständig",
"stageFlowHealthMissingRules": "Mindestens ein Übergang hat noch keine vollständige Weiterkommens-Regel.",
"stageFlowHealthReviewDescription": "Die Rundenlogik ist grundsätzlich nutzbar, sollte aber wegen offener Hinweise noch geprüft werden.",
"stageFlowHealthBlocked": "Rundenlogik widersprüchlich",
"stageFlowHealthBlockedDescription": "Mindestens eine Regel passt aktuell nicht zur Zielrunde oder enthält blockierende Konfigurationsfehler.",
"stageFlowBreakdownOk": "Konfiguration passt",
"stageFlowBreakdownWarnings": "{count} Hinweis(e) offen",
"stageFlowBreakdownErrors": "{count} Fehler blockieren",
"stageFlowBreakdownMissingRule": "Noch keine vollständige Regel angelegt",
"stageFlowBreakdownActionAddRule": "Regel anlegen",
"stageFlowBreakdownActionReview": "Fehler prüfen",
"stageFlowBreakdownActionOpen": "Regel öffnen",
"stageFlowReadinessNoGroups": "Noch keine passenden Gruppen vorhanden",
"stageFlowReadinessIncompleteGroups": "{count} Gruppe(n) sind noch nicht vollständig entschieden",
"stageFlowReadinessReady": "Übergang ist fachlich startklar",
"stageFlowRecommendationTitle": "Empfohlener nächster Schritt",
"stageFlowRecommendationFixes": "Die typischen Konfigurationsprobleme können direkt automatisch bereinigt werden.",
"stageFlowRecommendationMissingRule": "Für {label} fehlt noch eine vollständige Regel. Lege am besten damit los.",
"stageFlowRecommendationError": "Prüfe zuerst den ersten blockierenden Fehler in der Rundenlogik.",
"stageFlowRecommendationWarnings": "Es gibt noch Hinweise, die du vor dem Speichern prüfen solltest.",
"stageFlowRecommendationSave": "Die Rundenlogik ist stimmig. Du kannst die Konfiguration jetzt speichern.",
"stageFlowPreviewMissing": "Noch keine Regel konfiguriert.",
"stageFlowPreviewRule": "Plätze {places} gehen in {target}",
"stageFlowPreviewRuleWithCount": "{base} (voraussichtlich {count} Qualifizierte)",
"stageFlowQualifiedPreviewTitle": "Aktuell qualifiziert",
"stageFlowQualifiedPreviewEntry": "G{group} · Platz {position} · {name}",
"stageConfigMissingIds": "Kann nicht speichern: Vereins- oder Turnier-ID fehlt.",
"stageConfigLoadError": "Fehler beim Laden der Rundenkonfiguration.",
"stageConfigBadServerResponse": "Fehlerhafte Antwort vom Server.",
"stageCreated": "Runde {round} wurde erstellt.",
"stageParticipantsTransferred": "Qualifizierte Spieler wurden in {round} übernommen.",
"atLeastOnePoolRule": "Bitte mindestens eine Pool-Regel für {label} anlegen (z. B. Plätze 1,2).",
"enterMiniLocation": "Bitte geben Sie einen Ort ein.",
"selectTournamentFirst": "Bitte wählen Sie zuerst ein Turnier aus.",
"errorGeneratingPdf": "Fehler beim Generieren des PDFs.",
"orphanedMatchesRemoved": "{count} verwaiste Spiele entfernt.",
"noOrphanedMatchesFound": "Keine verwaisten Spiele gefunden.",
"enterClassName": "Bitte geben Sie einen Klassennamen ein.",
"deleteClassTitle": "Klasse löschen",
"deleteClassConfirm": "Möchten Sie die Klasse \"{name}\" wirklich löschen?",
"deleteClassParticipantsDetached": "Alle Teilnehmer werden von dieser Klasse entfernt.",
"enterExternalParticipantName": "Bitte geben Sie mindestens Vorname und Nachname ein.",
"noTournamentDate": "Kein Turnierdatum vorhanden.",
"noValidTrainingParticipants": "Keine gültigen Teilnehmer im Trainingstag für dieses Datum gefunden.",
"noTrainingParticipants": "Keine Teilnehmer im Trainingstag für dieses Datum gefunden.",
"noTrainingOnDate": "Kein Trainingstag für {date} gefunden.",
"selectTwoDifferentPlayers": "Bitte wählen Sie zwei verschiedene Spieler aus.",
"pairingAlreadyExists": "Diese Paarung existiert bereits.",
"participantNotFound": "Teilnehmer nicht gefunden.",
"minimumParticipantsForPairings": "Mindestens 2 Teilnehmer für Paarungen erforderlich.",
"deleteExistingPairingsConfirm": "Bestehende Paarungen werden gelöscht. Fortfahren?",
"pairingsCreatedWithErrors": "{successCount} Paarungen erstellt, {errorCount} Fehler.",
"koRound": "K.-o.-Runde",
"errorUpdatingTournament": "Fehler beim Aktualisieren des Turniers.",
"pleaseEnterDate": "Bitte geben Sie ein Datum ein!",
"errorCreatingTournament": "Fehler beim Erstellen des Turniers.",
"pleaseSelectParticipant": "Bitte wählen Sie einen Teilnehmer aus!",
"errorCreatingGroups": "Fehler beim Erstellen der Gruppen.",
"errorResettingKORound": "Fehler beim Zurücksetzen der K.o.-Runde.",
"mergeClasses": "Klassen zusammenlegen (gemeinsame Gruppenphase)",
"sourceClass": "Quelle",
"targetClass": "Ziel",
"strategy": "Strategie",
"mergeSingleGroup": "Alle Spieler aus A in eine Gruppe (bei B)",
"mergeDistribute": "Spieler aus A auf alle Gruppen (von B) verteilen",
"outOfCompetition": "Spieler aus A außer Konkurrenz",
"errorMergingClasses": "Fehler beim Zusammenlegen der Klassen.",
"apply": "Übernehmen",
"participantsNeedClassAssignment": "{count} Teilnehmer sind noch keiner Klasse zugeordnet. Diese sollten zuerst zugeordnet werden.",
"autoAssignableParticipantsHint": "{count} davon koennen automatisch zugeordnet werden.",
"assignmentReviewTitle": "{count} Teilnehmer brauchen eine Auswahl:",
"conflictSuggestionLabel": "Vorschläge:",
"workspaceProblemsTitle": "{count} offene Punkte",
"problemConfigTitle": "Konfiguration unvollständig",
"problemConfigDescription": "Prüfe Datum, Name, Gewinnsätze und mindestens eine Klasse.",
"problemUnassignedTitle": "{count} Teilnehmer ohne Klasse",
"problemUnassignedDescription": "Diese Teilnehmer brauchen noch eine manuelle Klassenzuordnung.",
"problemUnassignedAutoDescription": "{count} davon können direkt automatisch zugeordnet werden.",
"problemConflictsTitle": "{count} Teilnehmer mit Konflikten",
"problemConflictsDescription": "Prüfe unpassende Klassen, fehlende Daten oder unklare Zuordnungen.",
"problemDoublesTitle": "{count} offene Doppelpartner",
"problemDoublesDescription": "Ein oder mehrere Doppelklassen brauchen noch Partnerzuordnungen.",
"problemDoublesAutoDescription": "Die offene Doppelklasse kann direkt automatisch gepaart werden.",
"problemGroupsMissingTitle": "Gruppen noch nicht erstellt",
"problemGroupsMissingDescription": "Für das Gruppenturnier müssen zuerst Gruppen angelegt werden.",
"problemGroupMatchesTitle": "Gruppenspiele noch nicht erzeugt",
"problemGroupMatchesDescription": "Die Gruppen sind bereit, aber die Spiele wurden noch nicht erstellt.",
"problemKnockoutReadyTitle": "K.-o.-Runde kann gestartet werden",
"problemKnockoutReadyDescription": "Die Gruppenphase ist abgeschlossen und die Endrunde kann jetzt erzeugt werden.",
"autoAssignEligible": "Automatisch zuweisen",
"autoAssignEligibleNone": "Aktuell gibt es keine Teilnehmer mit eindeutiger automatischer Klassenzuweisung.",
"autoAssignEligibleDone": "{count} Teilnehmer wurden automatisch zugeordnet.",
"autoAssignEligiblePartial": "{successCount} Teilnehmer wurden automatisch zugeordnet, {errorCount} nicht.",
"doublesPairingHint": "{count} Teilnehmer in dieser Doppelklasse haben noch keinen Partner.",
"createSuggestedPairings": "Partner bilden",
"placementsPendingTitle": "Platzierungen noch nicht verfügbar",
"placementsPendingSelectedClass": "Für die aktuell gewählte Klasse gibt es noch keine Platzierungen.",
"placementsPendingNoGroups": "Sobald Gruppenspiele oder eine Endrunde vorhanden sind, erscheinen hier die Platzierungen.",
"placementsPendingGroups": "Gruppenplatzierungen erscheinen, sobald Gruppenspiele vorhanden sind.",
"placementsPendingFinals": "Endplatzierungen erscheinen, sobald mehrere Gruppen oder eine Endrunde vorhanden sind.",
"showingTournamentCount": "{visible} von {total}",
"noMatchingTournamentsTitle": "Keine passenden Turniere",
"noTournamentsCreatedYet": "Es wurden noch keine Turniere angelegt.",
"adjustTournamentSearch": "Passe den Suchbegriff an oder lege ein neues Turnier an.",
"selectedTournament": "Aktives Turnier",
"createGroupMatches": "Gruppenspiele berechnen",
"participantConflicts": "Konflikte",
"unpairedDoubles": "Doppel ohne Partner",
"noEligibleClass": "Keine passende Klasse gefunden.",
"eligibleClassesHint": "Passt zu: {classes}",
"warningClassMissing": "Klasse nicht gefunden",
"warningGenderMismatch": "Geschlecht passt nicht zur Klasse",
"warningGenderMissing": "Geschlecht fehlt für diese Klasse",
"warningBirthDateMissing": "Geburtsdatum fehlt für diese Klasse",
"warningTooOldForClass": "Zu alt für diese Klasse",
"warningTooYoungForClass": "Zu jung für diese Klasse",
"warningMissingPairing": "Doppelpartner fehlt",
"statusConfigReady": "Konfiguration bereit",
"statusConfigIncomplete": "Konfiguration unvollständig",
"statusParticipantsUnassigned": "{count} ohne Klasse",
"statusParticipantsConflicts": "{count} Teilnehmer mit Konflikten",
"statusParticipantsReady": "{count} Teilnehmer konfliktfrei",
"statusUnpairedDoubles": "{count} Doppel ohne Partner",
"statusGroupsMissing": "Gruppen noch nicht erstellt",
"statusGroupsReady": "Gruppen bereit",
"statusGroupsRunning": "Gruppenphase läuft",
"statusKnockoutReady": "K.-o.-Runde startklar",
"statusKnockoutRunning": "K.-o.-Runde läuft",
"statusTournamentCompleted": "Turnier abgeschlossen",
"statusActionAssign": "Zuweisen",
"statusActionReview": "Prüfen",
"statusActionPair": "Paaren",
"statusActionCreate": "Erstellen",
"statusActionGenerate": "Erzeugen",
"statusActionStart": "Starten"
},
"myTischtennis": {
"title": "myTischtennis-Account",
@@ -573,148 +919,6 @@
"actions": "Aktionen",
"showDetails": "Details anzeigen"
},
"tournaments": {
"internalTournaments": "Interne Turniere",
"openTournaments": "Offene Turniere",
"miniChampionships": "Minimeisterschaften",
"newMiniChampionship": "Neue Minimeisterschaft",
"miniChampionshipYear": "Jahr",
"miniChampionshipYearHint": "12 = in diesem Jahr 12 oder 13 Jahre, 10 = 10 oder 11, 8 = 9 und jünger",
"miniChampionshipLocation": "Ort",
"tournamentParticipations": "Turnierteilnahmen",
"date": "Datum",
"newTournament": "Neues Turnier",
"unknownDate": "Unbekanntes Datum",
"name": "Name",
"tournamentName": "Turniername",
"winningSets": "Gewinnsätze",
"numberOfTables": "Anzahl Tische",
"table": "Tisch",
"distributeTables": "Freie Tische verteilen",
"distributeTablesResult": "Tischverteilung",
"noFreeTables": "Keine freien Tische verfügbar.",
"noAssignableMatches": "Keine Spiele verfügbar, bei denen beide Spieler frei sind.",
"tablesDistributed": "Tische wurden verteilt.",
"missingDataPDF": "Fehlende Daten als PDF",
"missingDataPDFTitle": "Fehlende Teilnehmerdaten Minimeisterschaft",
"missingDataPDFSubtitle": "Bitte die fehlenden Daten (markiert mit ____) bei den Teilnehmern erfragen und hier notieren.",
"allDataComplete": "Alle Teilnehmerdaten sind vollständig.",
"allDataCompleteTop3": "Alle Daten der Top-3-Platzierten sind vollständig.",
"noTop3Yet": "Es stehen noch keine Top-3-Platzierungen fest.",
"missingDataPDFTitleTop3": "Fehlende Daten Top 3 Minimeisterschaft",
"missingDataPDFSubtitleTop3": "Fehlende Daten der ersten 3 Plätze (markiert mit ____) bitte erfragen und hier notieren.",
"address": "Adresse",
"phone": "Telefon",
"generatingPDF": "PDF wird erstellt...",
"page": "Seite",
"create": "Erstellen",
"exportPDF": "PDF exportieren",
"playInGroups": "Spielen in Gruppen",
"classes": "Klassen",
"save": "Speichern",
"cancel": "Abbrechen",
"edit": "Bearbeiten",
"delete": "Löschen",
"className": "Klassenname",
"addClass": "Klasse hinzufügen",
"noClassesYet": "Noch keine Klassen vorhanden. Fügen Sie eine neue Klasse hinzu.",
"singles": "Einzel",
"doubles": "Doppel",
"genderAll": "Alle",
"genderMixed": "Mixed",
"minBirthYear": "Geboren im Jahr oder später",
"selectClass": "Klasse auswählen",
"tabConfig": "Konfiguration",
"tabGroups": "Gruppen",
"tabParticipants": "Teilnehmer",
"tabResults": "Ergebnisse",
"tabPlacements": "Platzierungen",
"finalPlacements": "Endplatzierungen",
"groupPlacements": "Gruppenplatzierungen",
"noPlacementsYet": "Noch keine Platzierungen vorhanden.",
"participants": "Teilnehmer",
"seeded": "Gesetzt",
"gaveUp": "Aufgegeben",
"gaveUpHint": "Spieler hat aufgegeben alle Spiele zählen für den Gegner (11:0) bzw. 0:0 bei beiden aufgegeben.",
"club": "Verein",
"class": "Klasse",
"group": "Gruppe",
"action": "Aktion",
"unknown": "Unbekannt",
"clubMember": "(Vereinsmitglied)",
"selectParticipant": "-- Teilnehmer auswählen --",
"add": "Hinzufügen",
"loadFromTraining": "Aus Trainingstag laden",
"addExternalParticipant": "Externen Teilnehmer hinzufügen",
"firstName": "Vorname",
"lastName": "Nachname",
"optional": "optional",
"birthdate": "Geburtsdatum",
"addClubMember": "Vereinsmitglied hinzufügen",
"advancersPerGroup": "Aufsteiger pro Gruppe",
"maxGroupSize": "Maximale Gruppengröße",
"groupsPerClass": "Gruppen",
"groupsPerClassHint": "Geben Sie für jede Klasse die Anzahl der Gruppen ein (0 = keine Gruppen für diese Klasse):",
"showClass": "Klasse anzeigen",
"allClasses": "Alle Klassen",
"withoutClass": "Ohne Klasse",
"currentClass": "Aktive Klasse",
"selectClassPrompt": "Bitte wählen Sie oben eine Klasse aus.",
"numberOfGroups": "Anzahl Gruppen",
"createGroups": "Gruppen erstellen",
"randomizeGroups": "Zufällig verteilen",
"resetGroups": "Gruppen zurücksetzen",
"groupsOverview": "Gruppenübersicht",
"groupNumber": "Gruppe",
"index": "Index",
"position": "Platz",
"player": "Spieler",
"points": "Punkte",
"sets": "Satz",
"diff": "Diff",
"pointsRatio": "Spielpunkte",
"livePosition": "Live-Platz",
"pairings": "Doppel-Paarungen",
"addPairing": "Paarung hinzufügen",
"selectPlayer": "Spieler auswählen",
"external": "Extern",
"randomPairings": "Zufällige Doppel-Paarungen",
"errorMoreSeededThanUnseeded": "Es gibt mehr gesetzte als nicht gesetzte Spieler. Zufällige Paarungen können nicht erstellt werden.",
"randomPairingsCreated": "Zufällige Paarungen wurden erstellt.",
"resetGroupMatches": "Gruppenspiele",
"cleanupOrphanedMatches": "Verwaiste Spiele aufräumen",
"groupMatches": "Gruppenspiele",
"round": "Runde",
"encounter": "Begegnung",
"result": "Ergebnis",
"sets": "Sätze",
"setDiff": "Satzdifferenz",
"createMatches": "Spiele erstellen",
"startKORound": "K.o.-Runde starten",
"deleteKORound": "K.o.-Runde",
"address": "Adresse",
"email": "E-Mail",
"forForwarding": "für Weitermeldung",
"showPlayerDetails": "Spielerdetails anzeigen",
"noPlayerDataAvailable": "Keine Spielerdaten verfügbar",
"dataNotRecorded": "Noch nicht erfasst",
"koRound": "K.-o.-Runde",
"errorUpdatingTournament": "Fehler beim Aktualisieren des Turniers.",
"pleaseEnterDate": "Bitte geben Sie ein Datum ein!",
"errorCreatingTournament": "Fehler beim Erstellen des Turniers.",
"pleaseSelectParticipant": "Bitte wählen Sie einen Teilnehmer aus!",
"errorCreatingGroups": "Fehler beim Erstellen der Gruppen.",
"errorResettingKORound": "Fehler beim Zurücksetzen der K.o.-Runde.",
"mergeClasses": "Klassen zusammenlegen (gemeinsame Gruppenphase)",
"sourceClass": "Quelle",
"targetClass": "Ziel",
"strategy": "Strategie",
"mergeSingleGroup": "Alle Spieler aus A in eine Gruppe (bei B)",
"mergeDistribute": "Spieler aus A auf alle Gruppen (von B) verteilen",
"outOfCompetition": "Spieler aus A außer Konkurrenz",
"errorMergingClasses": "Fehler beim Zusammenlegen der Klassen.",
"apply": "Übernehmen"
},
"tournament": {
"apply": "Übernehmen"
},
@@ -1739,4 +1943,3 @@
"competitionName": "Konkurrenz"
}
}

View File

@@ -6,6 +6,7 @@
"common": {
"loading": "Loading...",
"save": "Save",
"saved": "Saved",
"cancel": "Cancel",
"delete": "Delete",
"edit": "Edit",
@@ -30,6 +31,8 @@
"approvals": "Approvals",
"statistics": "Training Statistics",
"tournaments": "Tournaments",
"clubTournaments": "Club Tournaments",
"tournamentParticipations": "Tournament Participations",
"schedule": "Schedules",
"clubSettings": "Club Settings",
"predefinedActivities": "Predefined Activities",
@@ -120,11 +123,16 @@
"tournaments": {
"numberOfTables": "Number of tables",
"table": "Table",
"playerOne": "Player 1",
"playerTwo": "Player 2",
"groupsLabel": "Groups",
"knockoutLabel": "KO",
"distributeTables": "Distribute free tables",
"distributeTablesResult": "Table distribution",
"noFreeTables": "No free tables available.",
"noAssignableMatches": "No matches available where both players are free.",
"tablesDistributed": "Tables have been distributed.",
"errorDistributingTables": "Error distributing tables.",
"missingDataPDF": "Missing data as PDF",
"missingDataPDFTitle": "Missing participant data Mini championship",
"missingDataPDFSubtitle": "Please collect the missing data (marked with ____) from participants and note them here.",
@@ -137,7 +145,217 @@
"phone": "Phone",
"generatingPDF": "Generating PDF...",
"page": "Page",
"dataNotRecorded": "Not yet recorded"
"statusOpen": "Open",
"statusLive": "Live",
"statusFinished": "Finished",
"dataNotRecorded": "Not yet recorded",
"resultsRanking": "Ranking",
"newSetPlaceholder": "New set, e.g. 11:7",
"finishMatch": "Finish",
"correctMatch": "Correct",
"markMatchLive": "Mark as live",
"unmarkMatchLive": "Remove live marker",
"stageConfigTitle": "Intermediate & Final Round",
"stageConfigLoading": "Loading round configuration…",
"stageConfigIntro": "The intermediate round is optional. If you activate it, a final round follows afterwards. A knockout final round is created as a single bracket.",
"useIntermediateStage": "Use intermediate round",
"intermediateRound": "Intermediate round (round 2)",
"finalRound": "Final round",
"playThirdPlace": "Play third place match",
"roundMode": "Mode",
"roundGroupCount": "Number of groups",
"promotionRule12": "Advance: preliminary round → intermediate round (1→2)",
"promotionRuleFinal": "Advance: round {from} → round {to}",
"addPoolRule": "Add pool rule",
"noPoolRulesYet12": "No rules yet. Example: places 1 and 2 -> top round-2 groups.",
"noPoolRulesYetFinal": "No rules yet. Example: places 1 and 2 -> final round.",
"placesFromEachGroup": "Places from each group (e.g. 1,2)",
"target": "Target",
"targetGroupCount": "Target number of groups",
"saveRounds": "Save rounds",
"createFinalFromPreliminary": "Create final round from preliminary round",
"createIntermediateFromPreliminary": "Create intermediate round from preliminary round",
"createFinalFromIntermediate": "Create final round from intermediate round",
"stageConfigMissingIds": "Cannot save: club or tournament ID is missing.",
"stageConfigLoadError": "Error loading round configuration.",
"stageConfigBadServerResponse": "Invalid server response.",
"stageCreated": "Round {round} was created.",
"atLeastOnePoolRule": "Please add at least one pool rule for {label} (e.g. places 1,2).",
"enterMiniLocation": "Please enter a location.",
"selectTournamentFirst": "Please select a tournament first.",
"errorGeneratingPdf": "Error generating the PDF.",
"orphanedMatchesRemoved": "{count} orphaned matches removed.",
"noOrphanedMatchesFound": "No orphaned matches found.",
"enterClassName": "Please enter a class name.",
"deleteClassTitle": "Delete class",
"deleteClassConfirm": "Do you really want to delete class \"{name}\"?",
"deleteClassParticipantsDetached": "All participants will be detached from this class.",
"enterExternalParticipantName": "Please enter at least first name and last name.",
"noTournamentDate": "No tournament date available.",
"noValidTrainingParticipants": "No valid participants were found in the training session for this date.",
"noTrainingParticipants": "No participants were found in the training session for this date.",
"noTrainingOnDate": "No training session found for {date}.",
"selectTwoDifferentPlayers": "Please select two different players.",
"pairingAlreadyExists": "This pairing already exists.",
"participantNotFound": "Participant not found.",
"minimumParticipantsForPairings": "At least 2 participants are required for pairings.",
"deleteExistingPairingsConfirm": "Existing pairings will be deleted. Continue?",
"pairingsCreatedWithErrors": "{successCount} pairings created, {errorCount} errors.",
"participantsNeedClassAssignment": "{count} participants are not assigned to a class yet. These should be assigned first.",
"autoAssignableParticipantsHint": "{count} of them can be assigned automatically.",
"assignmentReviewTitle": "{count} participants need a choice:",
"conflictSuggestionLabel": "Suggestions:",
"workspaceProblemsTitle": "{count} open issues",
"problemConfigTitle": "Configuration incomplete",
"problemConfigDescription": "Check date, name, winning sets and at least one class.",
"problemUnassignedTitle": "{count} participants without class",
"problemUnassignedDescription": "These participants still need a manual class assignment.",
"problemUnassignedAutoDescription": "{count} of them can be assigned automatically right away.",
"problemConflictsTitle": "{count} participants with conflicts",
"problemConflictsDescription": "Review invalid classes, missing data or unclear assignments.",
"problemDoublesTitle": "{count} open doubles partners",
"problemDoublesDescription": "One or more doubles classes still need partner assignments.",
"problemDoublesAutoDescription": "The open doubles class can be paired automatically right away.",
"problemGroupsMissingTitle": "Groups not created yet",
"problemGroupsMissingDescription": "The group tournament needs groups to be created first.",
"problemGroupMatchesTitle": "Group matches not generated yet",
"problemGroupMatchesDescription": "The groups are ready, but the matches have not been created yet.",
"problemKnockoutReadyTitle": "Knockout can be started",
"problemKnockoutReadyDescription": "The group stage is complete and the final round can be generated now.",
"autoAssignEligible": "Assign automatically",
"autoAssignEligibleNone": "There are currently no participants with a single automatic class assignment.",
"autoAssignEligibleDone": "{count} participants were assigned automatically.",
"autoAssignEligiblePartial": "{successCount} participants were assigned automatically, {errorCount} failed.",
"doublesPairingHint": "{count} participants in this doubles class still need a partner.",
"createSuggestedPairings": "Create pairings",
"placementsPendingTitle": "Placings not available yet",
"placementsPendingSelectedClass": "There are no placings for the currently selected class yet.",
"placementsPendingNoGroups": "Placings will appear here once group matches or a final round exist.",
"placementsPendingGroups": "Group placings will appear once group matches exist.",
"placementsPendingFinals": "Final placings will appear once multiple groups or a final round exist.",
"showingTournamentCount": "{visible} of {total}",
"noMatchingTournamentsTitle": "No matching tournaments",
"noTournamentsCreatedYet": "No tournaments have been created yet.",
"adjustTournamentSearch": "Adjust the search term or create a new tournament.",
"selectedTournament": "Active tournament",
"createGroupMatches": "Create group matches",
"participantConflicts": "Conflicts",
"unpairedDoubles": "Doubles without partner",
"noEligibleClass": "No suitable class found.",
"eligibleClassesHint": "Suitable for: {classes}",
"warningClassMissing": "Class not found",
"warningGenderMismatch": "Gender does not match class",
"warningGenderMissing": "Gender is missing for this class",
"warningBirthDateMissing": "Birth date is missing for this class",
"warningTooOldForClass": "Too old for this class",
"warningTooYoungForClass": "Too young for this class",
"warningMissingPairing": "Doubles partner missing",
"statusConfigReady": "Configuration ready",
"statusConfigIncomplete": "Configuration incomplete",
"statusParticipantsUnassigned": "{count} without class",
"statusParticipantsConflicts": "{count} participants with conflicts",
"statusParticipantsReady": "{count} participants conflict-free",
"statusUnpairedDoubles": "{count} doubles without partner",
"statusGroupsMissing": "Groups not created yet",
"statusGroupsReady": "Groups ready",
"statusGroupsRunning": "Group stage in progress",
"statusKnockoutReady": "Knockout ready to start",
"statusKnockoutRunning": "Knockout in progress",
"statusTournamentCompleted": "Tournament completed",
"statusActionAssign": "Assign",
"statusActionReview": "Review",
"statusActionPair": "Pair",
"statusActionCreate": "Create",
"statusActionGenerate": "Generate",
"statusActionStart": "Start",
"stageConfigCurrentSetup": "Current structure",
"stageFlowWithIntermediate": "Preliminary round -> intermediate round ({stage2}) -> final round ({final})",
"stageFlowDirectFinal": "Preliminary round -> final round ({final})",
"stageStep1": "Step 1",
"stageStep2": "Step 2",
"stageStep3": "Step 3",
"stageStep1Title": "Decide on an intermediate round",
"stageStep1Description": "First decide whether there should be an additional round after the preliminary round.",
"stageStep2Description": "Define what the intermediate round should look like and how many groups it needs.",
"stageFinalDescription": "Decide whether the final round should be played as groups or directly as a knockout bracket.",
"promotionRuleDescription12": "Define which places from each preliminary group advance to the intermediate round.",
"promotionRuleDescriptionFinalKnockout": "Define which places from the previous round advance to the knockout bracket.",
"promotionRuleDescriptionFinalGroups": "Define which places from the previous round advance to the final-round groups.",
"poolRuleLabel": "Rule {number}",
"poolRuleSummary": "Places {places} go to {target}",
"poolRulePlacesLabel": "Which places advance?",
"poolRulePlacesHelp": "Example: 1 or 1,2",
"poolRuleTargetLabel": "Where do these places go?",
"poolRuleQuickExamples": "Quick examples:",
"targetGroupsLabel": "{count} groups",
"transferQualifiedToFinalFromPreliminary": "Move qualified players straight into the final round",
"transferQualifiedToIntermediate": "Move qualified players into the intermediate round",
"transferQualifiedToFinalFromIntermediate": "Move qualified players into the final round",
"stageParticipantsTransferred": "Qualified players were moved into {round}.",
"transferQualifiedToFinalFromPreliminaryDesc": "Uses the preliminary-round to final-round rules and creates the qualified players directly there.",
"transferQualifiedToIntermediateDesc": "Uses the preliminary-round to intermediate-round rules and moves the qualified players across.",
"transferQualifiedToFinalFromIntermediateDesc": "Uses the intermediate-round to final-round rules and moves the qualified players across.",
"stageActionsTitle": "Next actions",
"stageActionsDescription": "These steps move qualified players into the next round.",
"stageActionRun": "Run now",
"stageActionMissingRulesHint": "Create at least one advancement rule first before moving players into the next round.",
"poolRulePreview": "From each group, places {places} advance to {target}.",
"poolRuleTargetKnockout": "the knockout bracket",
"poolRuleTargetGroupsDetailed": "{count} target groups",
"stageValidationTitle": "Review configuration",
"stageValidationDescription": "Fix these points before saving or moving players into the next round.",
"stageValidationGroupCountRequired": "Please set at least one valid group count.",
"stageValidationNoRules": "There is no advancement rule yet for {stage}.",
"stageValidationRulePlacesRequired": "Enter at least one place that should advance.",
"stageValidationRulePlacesInvalid": "The place definition is invalid. Only positive whole numbers such as 1 or 1,2 are allowed.",
"stageValidationRulePlacesDuplicate": "A single rule must not contain the same place more than once.",
"stageValidationRulesOverlap": "Place {place} is assigned twice, in rule {first} and rule {second}.",
"stageValidationTargetGroupsRequired": "Please enter a valid number of target groups.",
"stageValidationKnockoutTargetOnly": "For a knockout final round, only the knockout bracket is allowed as the target here.",
"stageValidationSaveBlocked": "Please fix the highlighted configuration errors first.",
"stageValidationSuggestion": "Suggestion: {value}",
"stageValidationRulePlacesGap": "The place definition has gaps. Usually a continuous order such as 1,2 or 1,2,3 is intended.",
"stageValidationTargetGroupCountMismatch": "The number of target groups must match the target round. Expected: {expected}.",
"stageValidationTargetTypeMismatch": "The target of this rule must match the target round. Expected: {target}.",
"stageValidationApplyFix": "Apply",
"stageValidationApplyTargetTypeFix": "Switch to {target}",
"stageValidationApplyTargetGroupFix": "Set to {count} target groups",
"ruleStatusOk": "Looks good",
"ruleStatusReview": "Review",
"ruleStatusBlocked": "Blocked",
"ruleStatusOkDescription": "This rule matches the current target round and can be used as-is.",
"stageFlowHealthOk": "Round logic looks good",
"stageFlowHealthOkDescription": "The transitions between rounds are fully configured and currently coherent.",
"stageFlowHealthIncomplete": "Round logic is incomplete",
"stageFlowHealthMissingRules": "At least one transition does not yet have a complete advancement rule.",
"stageFlowHealthReviewDescription": "The round logic is basically usable, but should still be reviewed because of open hints.",
"stageFlowHealthBlocked": "Round logic is contradictory",
"stageFlowHealthBlockedDescription": "At least one rule currently does not match the target round or contains blocking configuration errors.",
"stageFlowBreakdownOk": "Configuration looks good",
"stageFlowBreakdownWarnings": "{count} warning(s) open",
"stageFlowBreakdownErrors": "{count} blocking error(s)",
"stageFlowBreakdownMissingRule": "No complete rule has been added yet",
"stageFlowBreakdownActionAddRule": "Add rule",
"stageFlowBreakdownActionReview": "Review issues",
"stageFlowBreakdownActionOpen": "Open rule",
"stageValidationApplyAllFixes": "Apply {count} quick fix(es)",
"stageFlowRecommendationTitle": "Recommended next step",
"stageFlowRecommendationFixes": "The typical configuration problems can be cleaned up automatically right away.",
"stageFlowRecommendationMissingRule": "A complete rule is still missing for {label}. That is the best place to continue.",
"stageFlowRecommendationError": "Review the first blocking error in the round logic first.",
"stageFlowRecommendationWarnings": "There are still warnings that should be reviewed before saving.",
"stageFlowRecommendationSave": "The round logic is coherent. You can save the configuration now.",
"stageFlowPreviewMissing": "No rule has been configured yet.",
"stageFlowPreviewRule": "Places {places} go to {target}",
"stageValidationKnockoutNeedsTwoPlayers": "A knockout bracket needs at least 2 qualified players.",
"stageValidationKnockoutByesLikely": "With an expected {count} qualifiers, the knockout bracket will very likely include byes.",
"stageValidationNotEnoughQualifiersForGroups": "With only {count} qualifiers for {groups} target groups, empty groups would be created.",
"stageValidationThinGroupsLikely": "With {count} qualifiers across {groups} target groups, the groups will likely be very small.",
"stageFlowPreviewRuleWithCount": "{base} (expected qualifiers: {count})",
"stageFlowReadinessNoGroups": "No matching groups exist yet",
"stageFlowReadinessIncompleteGroups": "{count} group(s) are not fully decided yet",
"stageFlowReadinessReady": "Transition is ready to start",
"stageFlowQualifiedPreviewTitle": "Currently qualified",
"stageFlowQualifiedPreviewEntry": "G{group} · Place {position} · {name}"
}
}

View File

@@ -6,6 +6,7 @@
"common": {
"loading": "Loading...",
"save": "Save",
"saved": "Saved",
"cancel": "Cancel",
"delete": "Delete",
"edit": "Edit",
@@ -30,6 +31,8 @@
"approvals": "Approvals",
"statistics": "Training Statistics",
"tournaments": "Tournaments",
"clubTournaments": "Club Tournaments",
"tournamentParticipations": "Tournament Participations",
"schedule": "Schedules",
"clubSettings": "Club Settings",
"predefinedActivities": "Predefined Activities",
@@ -126,11 +129,16 @@
"tournaments": {
"numberOfTables": "Number of tables",
"table": "Table",
"playerOne": "Player 1",
"playerTwo": "Player 2",
"groupsLabel": "Groups",
"knockoutLabel": "KO",
"distributeTables": "Distribute free tables",
"distributeTablesResult": "Table distribution",
"noFreeTables": "No free tables available.",
"noAssignableMatches": "No matches available where both players are free.",
"tablesDistributed": "Tables have been distributed.",
"errorDistributingTables": "Error distributing tables.",
"missingDataPDF": "Missing data as PDF",
"missingDataPDFTitle": "Missing participant data Mini championship",
"missingDataPDFSubtitle": "Please collect the missing data (marked with ____) from participants and note them here.",
@@ -143,7 +151,217 @@
"phone": "Phone",
"generatingPDF": "Generating PDF...",
"page": "Page",
"dataNotRecorded": "Not yet recorded"
"statusOpen": "Open",
"statusLive": "Live",
"statusFinished": "Finished",
"dataNotRecorded": "Not yet recorded",
"resultsRanking": "Ranking",
"newSetPlaceholder": "New set, e.g. 11:7",
"finishMatch": "Finish",
"correctMatch": "Correct",
"markMatchLive": "Mark as live",
"unmarkMatchLive": "Remove live marker",
"stageConfigTitle": "Intermediate & Final Round",
"stageConfigLoading": "Loading round configuration…",
"stageConfigIntro": "The intermediate round is optional. If you activate it, a final round follows afterwards. A knockout final round is created as a single bracket.",
"stageConfigCurrentSetup": "Current structure",
"stageFlowWithIntermediate": "Preliminary round -> intermediate round ({stage2}) -> final round ({final})",
"stageFlowDirectFinal": "Preliminary round -> final round ({final})",
"stageStep1": "Step 1",
"stageStep2": "Step 2",
"stageStep3": "Step 3",
"stageStep1Title": "Decide on an intermediate round",
"stageStep1Description": "First decide whether there should be an additional round after the preliminary round.",
"stageStep2Description": "Define what the intermediate round should look like and how many groups it needs.",
"stageFinalDescription": "Decide whether the final round should be played as groups or directly as a knockout bracket.",
"useIntermediateStage": "Use intermediate round",
"intermediateRound": "Intermediate round (round 2)",
"finalRound": "Final round",
"playThirdPlace": "Play third place match",
"roundMode": "Mode",
"roundGroupCount": "Number of groups",
"promotionRule12": "Advance: preliminary round → intermediate round (1→2)",
"promotionRuleFinal": "Advance: round {from} → round {to}",
"promotionRuleDescription12": "Define which places from each preliminary group advance to the intermediate round.",
"promotionRuleDescriptionFinalKnockout": "Define which places from the previous round advance to the knockout bracket.",
"promotionRuleDescriptionFinalGroups": "Define which places from the previous round advance to the final-round groups.",
"poolRuleLabel": "Rule {number}",
"poolRuleSummary": "Places {places} go to {target}",
"poolRulePreview": "From each group, places {places} advance to {target}.",
"poolRulePlacesLabel": "Which places advance?",
"poolRulePlacesHelp": "Example: 1 or 1,2",
"poolRuleTargetLabel": "Where do these places go?",
"poolRuleQuickExamples": "Quick examples:",
"targetGroupsLabel": "{count} groups",
"poolRuleTargetKnockout": "the knockout bracket",
"poolRuleTargetGroupsDetailed": "{count} target groups",
"addPoolRule": "Add pool rule",
"noPoolRulesYet12": "No rules yet. Example: places 1 and 2 -> top round-2 groups.",
"noPoolRulesYetFinal": "No rules yet. Example: places 1 and 2 -> final round.",
"placesFromEachGroup": "Places from each group (e.g. 1,2)",
"target": "Target",
"targetGroupCount": "Target number of groups",
"saveRounds": "Save rounds",
"createFinalFromPreliminary": "Create final round from preliminary round",
"createIntermediateFromPreliminary": "Create intermediate round from preliminary round",
"createFinalFromIntermediate": "Create final round from intermediate round",
"transferQualifiedToFinalFromPreliminary": "Move qualified players straight into the final round",
"transferQualifiedToIntermediate": "Move qualified players into the intermediate round",
"transferQualifiedToFinalFromIntermediate": "Move qualified players into the final round",
"transferQualifiedToFinalFromPreliminaryDesc": "Uses the preliminary-round to final-round rules and creates the qualified players directly there.",
"transferQualifiedToIntermediateDesc": "Uses the preliminary-round to intermediate-round rules and moves the qualified players across.",
"transferQualifiedToFinalFromIntermediateDesc": "Uses the intermediate-round to final-round rules and moves the qualified players across.",
"stageActionsTitle": "Next actions",
"stageActionsDescription": "These steps move qualified players into the next round.",
"stageActionRun": "Run now",
"stageActionMissingRulesHint": "Create at least one advancement rule first before moving players into the next round.",
"stageValidationTitle": "Review configuration",
"stageValidationDescription": "Fix these points before saving or moving players into the next round.",
"stageValidationGroupCountRequired": "Please set at least one valid group count.",
"stageValidationNoRules": "There is no advancement rule yet for {stage}.",
"stageValidationRulePlacesRequired": "Enter at least one place that should advance.",
"stageValidationRulePlacesInvalid": "The place definition is invalid. Only positive whole numbers such as 1 or 1,2 are allowed.",
"stageValidationRulePlacesDuplicate": "A single rule must not contain the same place more than once.",
"stageValidationRulePlacesGap": "The place definition has gaps. Usually a continuous order such as 1,2 or 1,2,3 is intended.",
"stageValidationRulesOverlap": "Place {place} is assigned twice, in rule {first} and rule {second}.",
"stageValidationTargetGroupsRequired": "Please enter a valid number of target groups.",
"stageValidationTargetGroupCountMismatch": "The number of target groups must match the target round. Expected: {expected}.",
"stageValidationTargetTypeMismatch": "The target of this rule must match the target round. Expected: {target}.",
"stageValidationKnockoutTargetOnly": "For a knockout final round, only the knockout bracket is allowed as the target here.",
"stageValidationKnockoutNeedsTwoPlayers": "A knockout bracket needs at least 2 qualified players.",
"stageValidationKnockoutByesLikely": "With an expected {count} qualifiers, the knockout bracket will very likely include byes.",
"stageValidationNotEnoughQualifiersForGroups": "With only {count} qualifiers for {groups} target groups, empty groups would be created.",
"stageValidationThinGroupsLikely": "With {count} qualifiers across {groups} target groups, the groups will likely be very small.",
"stageValidationSuggestion": "Suggestion: {value}",
"stageValidationApplyFix": "Apply",
"stageValidationApplyAllFixes": "Apply {count} quick fix(es)",
"stageValidationApplyTargetTypeFix": "Switch to {target}",
"stageValidationApplyTargetGroupFix": "Set to {count} target groups",
"stageValidationSaveBlocked": "Please fix the highlighted configuration errors first.",
"ruleStatusOk": "Looks good",
"ruleStatusReview": "Review",
"ruleStatusBlocked": "Blocked",
"ruleStatusOkDescription": "This rule matches the current target round and can be used as-is.",
"stageFlowHealthOk": "Round logic looks good",
"stageFlowHealthOkDescription": "The transitions between rounds are fully configured and currently coherent.",
"stageFlowHealthIncomplete": "Round logic is incomplete",
"stageFlowHealthMissingRules": "At least one transition does not yet have a complete advancement rule.",
"stageFlowHealthReviewDescription": "The round logic is basically usable, but should still be reviewed because of open hints.",
"stageFlowHealthBlocked": "Round logic is contradictory",
"stageFlowHealthBlockedDescription": "At least one rule currently does not match the target round or contains blocking configuration errors.",
"stageFlowBreakdownOk": "Configuration looks good",
"stageFlowBreakdownWarnings": "{count} warning(s) open",
"stageFlowBreakdownErrors": "{count} blocking error(s)",
"stageFlowBreakdownMissingRule": "No complete rule has been added yet",
"stageFlowBreakdownActionAddRule": "Add rule",
"stageFlowBreakdownActionReview": "Review issues",
"stageFlowBreakdownActionOpen": "Open rule",
"stageFlowReadinessNoGroups": "No matching groups exist yet",
"stageFlowReadinessIncompleteGroups": "{count} group(s) are not fully decided yet",
"stageFlowReadinessReady": "Transition is ready to start",
"stageFlowRecommendationTitle": "Recommended next step",
"stageFlowRecommendationFixes": "The typical configuration problems can be cleaned up automatically right away.",
"stageFlowRecommendationMissingRule": "A complete rule is still missing for {label}. That is the best place to continue.",
"stageFlowRecommendationError": "Review the first blocking error in the round logic first.",
"stageFlowRecommendationWarnings": "There are still warnings that should be reviewed before saving.",
"stageFlowRecommendationSave": "The round logic is coherent. You can save the configuration now.",
"stageFlowPreviewMissing": "No rule has been configured yet.",
"stageFlowPreviewRule": "Places {places} go to {target}",
"stageFlowPreviewRuleWithCount": "{base} (expected qualifiers: {count})",
"stageFlowQualifiedPreviewTitle": "Currently qualified",
"stageFlowQualifiedPreviewEntry": "G{group} · Place {position} · {name}",
"stageConfigMissingIds": "Cannot save: club or tournament ID is missing.",
"stageConfigLoadError": "Error loading round configuration.",
"stageConfigBadServerResponse": "Invalid server response.",
"stageCreated": "Round {round} was created.",
"stageParticipantsTransferred": "Qualified players were moved into {round}.",
"atLeastOnePoolRule": "Please add at least one pool rule for {label} (e.g. places 1,2).",
"enterMiniLocation": "Please enter a location.",
"selectTournamentFirst": "Please select a tournament first.",
"errorGeneratingPdf": "Error generating the PDF.",
"orphanedMatchesRemoved": "{count} orphaned matches removed.",
"noOrphanedMatchesFound": "No orphaned matches found.",
"enterClassName": "Please enter a class name.",
"deleteClassTitle": "Delete class",
"deleteClassConfirm": "Do you really want to delete class \"{name}\"?",
"deleteClassParticipantsDetached": "All participants will be detached from this class.",
"enterExternalParticipantName": "Please enter at least first name and last name.",
"noTournamentDate": "No tournament date available.",
"noValidTrainingParticipants": "No valid participants were found in the training session for this date.",
"noTrainingParticipants": "No participants were found in the training session for this date.",
"noTrainingOnDate": "No training session found for {date}.",
"selectTwoDifferentPlayers": "Please select two different players.",
"pairingAlreadyExists": "This pairing already exists.",
"participantNotFound": "Participant not found.",
"minimumParticipantsForPairings": "At least 2 participants are required for pairings.",
"deleteExistingPairingsConfirm": "Existing pairings will be deleted. Continue?",
"pairingsCreatedWithErrors": "{successCount} pairings created, {errorCount} errors.",
"participantsNeedClassAssignment": "{count} participants are not assigned to a class yet. These should be assigned first.",
"autoAssignableParticipantsHint": "{count} of them can be assigned automatically.",
"assignmentReviewTitle": "{count} participants need a choice:",
"conflictSuggestionLabel": "Suggestions:",
"workspaceProblemsTitle": "{count} open issues",
"problemConfigTitle": "Configuration incomplete",
"problemConfigDescription": "Check date, name, winning sets and at least one class.",
"problemUnassignedTitle": "{count} participants without class",
"problemUnassignedDescription": "These participants still need a manual class assignment.",
"problemUnassignedAutoDescription": "{count} of them can be assigned automatically right away.",
"problemConflictsTitle": "{count} participants with conflicts",
"problemConflictsDescription": "Review invalid classes, missing data or unclear assignments.",
"problemDoublesTitle": "{count} open doubles partners",
"problemDoublesDescription": "One or more doubles classes still need partner assignments.",
"problemDoublesAutoDescription": "The open doubles class can be paired automatically right away.",
"problemGroupsMissingTitle": "Groups not created yet",
"problemGroupsMissingDescription": "The group tournament needs groups to be created first.",
"problemGroupMatchesTitle": "Group matches not generated yet",
"problemGroupMatchesDescription": "The groups are ready, but the matches have not been created yet.",
"problemKnockoutReadyTitle": "Knockout can be started",
"problemKnockoutReadyDescription": "The group stage is complete and the final round can be generated now.",
"autoAssignEligible": "Assign automatically",
"autoAssignEligibleNone": "There are currently no participants with a single automatic class assignment.",
"autoAssignEligibleDone": "{count} participants were assigned automatically.",
"autoAssignEligiblePartial": "{successCount} participants were assigned automatically, {errorCount} failed.",
"doublesPairingHint": "{count} participants in this doubles class still need a partner.",
"createSuggestedPairings": "Create pairings",
"placementsPendingTitle": "Placings not available yet",
"placementsPendingSelectedClass": "There are no placings for the currently selected class yet.",
"placementsPendingNoGroups": "Placings will appear here once group matches or a final round exist.",
"placementsPendingGroups": "Group placings will appear once group matches exist.",
"placementsPendingFinals": "Final placings will appear once multiple groups or a final round exist.",
"showingTournamentCount": "{visible} of {total}",
"noMatchingTournamentsTitle": "No matching tournaments",
"noTournamentsCreatedYet": "No tournaments have been created yet.",
"adjustTournamentSearch": "Adjust the search term or create a new tournament.",
"selectedTournament": "Active tournament",
"createGroupMatches": "Create group matches",
"participantConflicts": "Conflicts",
"unpairedDoubles": "Doubles without partner",
"noEligibleClass": "No suitable class found.",
"eligibleClassesHint": "Suitable for: {classes}",
"warningClassMissing": "Class not found",
"warningGenderMismatch": "Gender does not match class",
"warningGenderMissing": "Gender is missing for this class",
"warningBirthDateMissing": "Birth date is missing for this class",
"warningTooOldForClass": "Too old for this class",
"warningTooYoungForClass": "Too young for this class",
"warningMissingPairing": "Doubles partner missing",
"statusConfigReady": "Configuration ready",
"statusConfigIncomplete": "Configuration incomplete",
"statusParticipantsUnassigned": "{count} without class",
"statusParticipantsConflicts": "{count} participants with conflicts",
"statusParticipantsReady": "{count} participants conflict-free",
"statusUnpairedDoubles": "{count} doubles without partner",
"statusGroupsMissing": "Groups not created yet",
"statusGroupsReady": "Groups ready",
"statusGroupsRunning": "Group stage in progress",
"statusKnockoutReady": "Knockout ready to start",
"statusKnockoutRunning": "Knockout in progress",
"statusTournamentCompleted": "Tournament completed",
"statusActionAssign": "Assign",
"statusActionReview": "Review",
"statusActionPair": "Pair",
"statusActionCreate": "Create",
"statusActionGenerate": "Generate",
"statusActionStart": "Start"
}
}

View File

@@ -6,6 +6,7 @@
"common": {
"loading": "Loading...",
"save": "Save",
"saved": "Saved",
"cancel": "Cancel",
"delete": "Delete",
"edit": "Edit",
@@ -30,6 +31,8 @@
"approvals": "Approvals",
"statistics": "Training Statistics",
"tournaments": "Tournaments",
"clubTournaments": "Club Tournaments",
"tournamentParticipations": "Tournament Participations",
"schedule": "Schedules",
"clubSettings": "Club Settings",
"predefinedActivities": "Predefined Activities",
@@ -120,11 +123,16 @@
"tournaments": {
"numberOfTables": "Number of tables",
"table": "Table",
"playerOne": "Player 1",
"playerTwo": "Player 2",
"groupsLabel": "Groups",
"knockoutLabel": "KO",
"distributeTables": "Distribute free tables",
"distributeTablesResult": "Table distribution",
"noFreeTables": "No free tables available.",
"noAssignableMatches": "No matches available where both players are free.",
"tablesDistributed": "Tables have been distributed.",
"errorDistributingTables": "Error distributing tables.",
"missingDataPDF": "Missing data as PDF",
"missingDataPDFTitle": "Missing participant data Mini championship",
"missingDataPDFSubtitle": "Please collect the missing data (marked with ____) from participants and note them here.",
@@ -137,7 +145,217 @@
"phone": "Phone",
"generatingPDF": "Generating PDF...",
"page": "Page",
"dataNotRecorded": "Not yet recorded"
"statusOpen": "Open",
"statusLive": "Live",
"statusFinished": "Finished",
"dataNotRecorded": "Not yet recorded",
"resultsRanking": "Ranking",
"newSetPlaceholder": "New set, e.g. 11:7",
"finishMatch": "Finish",
"correctMatch": "Correct",
"markMatchLive": "Mark as live",
"unmarkMatchLive": "Remove live marker",
"stageConfigTitle": "Intermediate & Final Round",
"stageConfigLoading": "Loading round configuration…",
"stageConfigIntro": "The intermediate round is optional. If you activate it, a final round follows afterwards. A knockout final round is created as a single bracket.",
"useIntermediateStage": "Use intermediate round",
"intermediateRound": "Intermediate round (round 2)",
"finalRound": "Final round",
"playThirdPlace": "Play third place match",
"roundMode": "Mode",
"roundGroupCount": "Number of groups",
"promotionRule12": "Advance: preliminary round → intermediate round (1→2)",
"promotionRuleFinal": "Advance: round {from} → round {to}",
"addPoolRule": "Add pool rule",
"noPoolRulesYet12": "No rules yet. Example: places 1 and 2 -> top round-2 groups.",
"noPoolRulesYetFinal": "No rules yet. Example: places 1 and 2 -> final round.",
"placesFromEachGroup": "Places from each group (e.g. 1,2)",
"target": "Target",
"targetGroupCount": "Target number of groups",
"saveRounds": "Save rounds",
"createFinalFromPreliminary": "Create final round from preliminary round",
"createIntermediateFromPreliminary": "Create intermediate round from preliminary round",
"createFinalFromIntermediate": "Create final round from intermediate round",
"stageConfigMissingIds": "Cannot save: club or tournament ID is missing.",
"stageConfigLoadError": "Error loading round configuration.",
"stageConfigBadServerResponse": "Invalid server response.",
"stageCreated": "Round {round} was created.",
"atLeastOnePoolRule": "Please add at least one pool rule for {label} (e.g. places 1,2).",
"enterMiniLocation": "Please enter a location.",
"selectTournamentFirst": "Please select a tournament first.",
"errorGeneratingPdf": "Error generating the PDF.",
"orphanedMatchesRemoved": "{count} orphaned matches removed.",
"noOrphanedMatchesFound": "No orphaned matches found.",
"enterClassName": "Please enter a class name.",
"deleteClassTitle": "Delete class",
"deleteClassConfirm": "Do you really want to delete class \"{name}\"?",
"deleteClassParticipantsDetached": "All participants will be detached from this class.",
"enterExternalParticipantName": "Please enter at least first name and last name.",
"noTournamentDate": "No tournament date available.",
"noValidTrainingParticipants": "No valid participants were found in the training session for this date.",
"noTrainingParticipants": "No participants were found in the training session for this date.",
"noTrainingOnDate": "No training session found for {date}.",
"selectTwoDifferentPlayers": "Please select two different players.",
"pairingAlreadyExists": "This pairing already exists.",
"participantNotFound": "Participant not found.",
"minimumParticipantsForPairings": "At least 2 participants are required for pairings.",
"deleteExistingPairingsConfirm": "Existing pairings will be deleted. Continue?",
"pairingsCreatedWithErrors": "{successCount} pairings created, {errorCount} errors.",
"participantsNeedClassAssignment": "{count} participants are not assigned to a class yet. These should be assigned first.",
"autoAssignableParticipantsHint": "{count} of them can be assigned automatically.",
"assignmentReviewTitle": "{count} participants need a choice:",
"conflictSuggestionLabel": "Suggestions:",
"workspaceProblemsTitle": "{count} open issues",
"problemConfigTitle": "Configuration incomplete",
"problemConfigDescription": "Check date, name, winning sets and at least one class.",
"problemUnassignedTitle": "{count} participants without class",
"problemUnassignedDescription": "These participants still need a manual class assignment.",
"problemUnassignedAutoDescription": "{count} of them can be assigned automatically right away.",
"problemConflictsTitle": "{count} participants with conflicts",
"problemConflictsDescription": "Review invalid classes, missing data or unclear assignments.",
"problemDoublesTitle": "{count} open doubles partners",
"problemDoublesDescription": "One or more doubles classes still need partner assignments.",
"problemDoublesAutoDescription": "The open doubles class can be paired automatically right away.",
"problemGroupsMissingTitle": "Groups not created yet",
"problemGroupsMissingDescription": "The group tournament needs groups to be created first.",
"problemGroupMatchesTitle": "Group matches not generated yet",
"problemGroupMatchesDescription": "The groups are ready, but the matches have not been created yet.",
"problemKnockoutReadyTitle": "Knockout can be started",
"problemKnockoutReadyDescription": "The group stage is complete and the final round can be generated now.",
"autoAssignEligible": "Assign automatically",
"autoAssignEligibleNone": "There are currently no participants with a single automatic class assignment.",
"autoAssignEligibleDone": "{count} participants were assigned automatically.",
"autoAssignEligiblePartial": "{successCount} participants were assigned automatically, {errorCount} failed.",
"doublesPairingHint": "{count} participants in this doubles class still need a partner.",
"createSuggestedPairings": "Create pairings",
"placementsPendingTitle": "Placings not available yet",
"placementsPendingSelectedClass": "There are no placings for the currently selected class yet.",
"placementsPendingNoGroups": "Placings will appear here once group matches or a final round exist.",
"placementsPendingGroups": "Group placings will appear once group matches exist.",
"placementsPendingFinals": "Final placings will appear once multiple groups or a final round exist.",
"showingTournamentCount": "{visible} of {total}",
"noMatchingTournamentsTitle": "No matching tournaments",
"noTournamentsCreatedYet": "No tournaments have been created yet.",
"adjustTournamentSearch": "Adjust the search term or create a new tournament.",
"selectedTournament": "Active tournament",
"createGroupMatches": "Create group matches",
"participantConflicts": "Conflicts",
"unpairedDoubles": "Doubles without partner",
"noEligibleClass": "No suitable class found.",
"eligibleClassesHint": "Suitable for: {classes}",
"warningClassMissing": "Class not found",
"warningGenderMismatch": "Gender does not match class",
"warningGenderMissing": "Gender is missing for this class",
"warningBirthDateMissing": "Birth date is missing for this class",
"warningTooOldForClass": "Too old for this class",
"warningTooYoungForClass": "Too young for this class",
"warningMissingPairing": "Doubles partner missing",
"statusConfigReady": "Configuration ready",
"statusConfigIncomplete": "Configuration incomplete",
"statusParticipantsUnassigned": "{count} without class",
"statusParticipantsConflicts": "{count} participants with conflicts",
"statusParticipantsReady": "{count} participants conflict-free",
"statusUnpairedDoubles": "{count} doubles without partner",
"statusGroupsMissing": "Groups not created yet",
"statusGroupsReady": "Groups ready",
"statusGroupsRunning": "Group stage in progress",
"statusKnockoutReady": "Knockout ready to start",
"statusKnockoutRunning": "Knockout in progress",
"statusTournamentCompleted": "Tournament completed",
"statusActionAssign": "Assign",
"statusActionReview": "Review",
"statusActionPair": "Pair",
"statusActionCreate": "Create",
"statusActionGenerate": "Generate",
"statusActionStart": "Start",
"stageConfigCurrentSetup": "Current structure",
"stageFlowWithIntermediate": "Preliminary round -> intermediate round ({stage2}) -> final round ({final})",
"stageFlowDirectFinal": "Preliminary round -> final round ({final})",
"stageStep1": "Step 1",
"stageStep2": "Step 2",
"stageStep3": "Step 3",
"stageStep1Title": "Decide on an intermediate round",
"stageStep1Description": "First decide whether there should be an additional round after the preliminary round.",
"stageStep2Description": "Define what the intermediate round should look like and how many groups it needs.",
"stageFinalDescription": "Decide whether the final round should be played as groups or directly as a knockout bracket.",
"promotionRuleDescription12": "Define which places from each preliminary group advance to the intermediate round.",
"promotionRuleDescriptionFinalKnockout": "Define which places from the previous round advance to the knockout bracket.",
"promotionRuleDescriptionFinalGroups": "Define which places from the previous round advance to the final-round groups.",
"poolRuleLabel": "Rule {number}",
"poolRuleSummary": "Places {places} go to {target}",
"poolRulePlacesLabel": "Which places advance?",
"poolRulePlacesHelp": "Example: 1 or 1,2",
"poolRuleTargetLabel": "Where do these places go?",
"poolRuleQuickExamples": "Quick examples:",
"targetGroupsLabel": "{count} groups",
"transferQualifiedToFinalFromPreliminary": "Move qualified players straight into the final round",
"transferQualifiedToIntermediate": "Move qualified players into the intermediate round",
"transferQualifiedToFinalFromIntermediate": "Move qualified players into the final round",
"stageParticipantsTransferred": "Qualified players were moved into {round}.",
"transferQualifiedToFinalFromPreliminaryDesc": "Uses the preliminary-round to final-round rules and creates the qualified players directly there.",
"transferQualifiedToIntermediateDesc": "Uses the preliminary-round to intermediate-round rules and moves the qualified players across.",
"transferQualifiedToFinalFromIntermediateDesc": "Uses the intermediate-round to final-round rules and moves the qualified players across.",
"stageActionsTitle": "Next actions",
"stageActionsDescription": "These steps move qualified players into the next round.",
"stageActionRun": "Run now",
"stageActionMissingRulesHint": "Create at least one advancement rule first before moving players into the next round.",
"poolRulePreview": "From each group, places {places} advance to {target}.",
"poolRuleTargetKnockout": "the knockout bracket",
"poolRuleTargetGroupsDetailed": "{count} target groups",
"stageValidationTitle": "Review configuration",
"stageValidationDescription": "Fix these points before saving or moving players into the next round.",
"stageValidationGroupCountRequired": "Please set at least one valid group count.",
"stageValidationNoRules": "There is no advancement rule yet for {stage}.",
"stageValidationRulePlacesRequired": "Enter at least one place that should advance.",
"stageValidationRulePlacesInvalid": "The place definition is invalid. Only positive whole numbers such as 1 or 1,2 are allowed.",
"stageValidationRulePlacesDuplicate": "A single rule must not contain the same place more than once.",
"stageValidationRulesOverlap": "Place {place} is assigned twice, in rule {first} and rule {second}.",
"stageValidationTargetGroupsRequired": "Please enter a valid number of target groups.",
"stageValidationKnockoutTargetOnly": "For a knockout final round, only the knockout bracket is allowed as the target here.",
"stageValidationSaveBlocked": "Please fix the highlighted configuration errors first.",
"stageValidationSuggestion": "Suggestion: {value}",
"stageValidationRulePlacesGap": "The place definition has gaps. Usually a continuous order such as 1,2 or 1,2,3 is intended.",
"stageValidationTargetGroupCountMismatch": "The number of target groups must match the target round. Expected: {expected}.",
"stageValidationTargetTypeMismatch": "The target of this rule must match the target round. Expected: {target}.",
"stageValidationApplyFix": "Apply",
"stageValidationApplyTargetTypeFix": "Switch to {target}",
"stageValidationApplyTargetGroupFix": "Set to {count} target groups",
"ruleStatusOk": "Looks good",
"ruleStatusReview": "Review",
"ruleStatusBlocked": "Blocked",
"ruleStatusOkDescription": "This rule matches the current target round and can be used as-is.",
"stageFlowHealthOk": "Round logic looks good",
"stageFlowHealthOkDescription": "The transitions between rounds are fully configured and currently coherent.",
"stageFlowHealthIncomplete": "Round logic is incomplete",
"stageFlowHealthMissingRules": "At least one transition does not yet have a complete advancement rule.",
"stageFlowHealthReviewDescription": "The round logic is basically usable, but should still be reviewed because of open hints.",
"stageFlowHealthBlocked": "Round logic is contradictory",
"stageFlowHealthBlockedDescription": "At least one rule currently does not match the target round or contains blocking configuration errors.",
"stageFlowBreakdownOk": "Configuration looks good",
"stageFlowBreakdownWarnings": "{count} warning(s) open",
"stageFlowBreakdownErrors": "{count} blocking error(s)",
"stageFlowBreakdownMissingRule": "No complete rule has been added yet",
"stageFlowBreakdownActionAddRule": "Add rule",
"stageFlowBreakdownActionReview": "Review issues",
"stageFlowBreakdownActionOpen": "Open rule",
"stageValidationApplyAllFixes": "Apply {count} quick fix(es)",
"stageFlowRecommendationTitle": "Recommended next step",
"stageFlowRecommendationFixes": "The typical configuration problems can be cleaned up automatically right away.",
"stageFlowRecommendationMissingRule": "A complete rule is still missing for {label}. That is the best place to continue.",
"stageFlowRecommendationError": "Review the first blocking error in the round logic first.",
"stageFlowRecommendationWarnings": "There are still warnings that should be reviewed before saving.",
"stageFlowRecommendationSave": "The round logic is coherent. You can save the configuration now.",
"stageFlowPreviewMissing": "No rule has been configured yet.",
"stageFlowPreviewRule": "Places {places} go to {target}",
"stageValidationKnockoutNeedsTwoPlayers": "A knockout bracket needs at least 2 qualified players.",
"stageValidationKnockoutByesLikely": "With an expected {count} qualifiers, the knockout bracket will very likely include byes.",
"stageValidationNotEnoughQualifiersForGroups": "With only {count} qualifiers for {groups} target groups, empty groups would be created.",
"stageValidationThinGroupsLikely": "With {count} qualifiers across {groups} target groups, the groups will likely be very small.",
"stageFlowPreviewRuleWithCount": "{base} (expected qualifiers: {count})",
"stageFlowReadinessNoGroups": "No matching groups exist yet",
"stageFlowReadinessIncompleteGroups": "{count} group(s) are not fully decided yet",
"stageFlowReadinessReady": "Transition is ready to start",
"stageFlowQualifiedPreviewTitle": "Currently qualified",
"stageFlowQualifiedPreviewEntry": "G{group} · Place {position} · {name}"
}
}

View File

@@ -6,6 +6,7 @@
"common": {
"loading": "Cargando...",
"save": "Guardar",
"saved": "Guardado",
"cancel": "Cancelar",
"delete": "Eliminar",
"edit": "Editar",
@@ -30,6 +31,8 @@
"approvals": "Aprobaciones",
"statistics": "Estadísticas de entrenamiento",
"tournaments": "Torneos",
"clubTournaments": "Torneos del club",
"tournamentParticipations": "Participaciones en torneos",
"schedule": "Calendarios",
"clubSettings": "Configuración del club",
"predefinedActivities": "Actividades predefinidas",
@@ -120,11 +123,16 @@
"tournaments": {
"numberOfTables": "Número de mesas",
"table": "Mesa",
"playerOne": "Jugador 1",
"playerTwo": "Jugador 2",
"groupsLabel": "Grupos",
"knockoutLabel": "KO",
"distributeTables": "Distribuir mesas libres",
"distributeTablesResult": "Distribución de mesas",
"noFreeTables": "No hay mesas libres disponibles.",
"noAssignableMatches": "No hay partidos disponibles en los que ambos jugadores estén libres.",
"tablesDistributed": "Las mesas han sido distribuidas.",
"errorDistributingTables": "Error al distribuir las mesas.",
"missingDataPDF": "Datos faltantes como PDF",
"missingDataPDFTitle": "Datos de participantes faltantes Mini campeonato",
"missingDataPDFSubtitle": "Por favor, recopile los datos faltantes (marcados con ____) de los participantes y anótelos aquí.",
@@ -137,7 +145,189 @@
"phone": "Teléfono",
"generatingPDF": "Generando PDF...",
"page": "Página",
"dataNotRecorded": "Aún no registrado"
"statusOpen": "Abierto",
"statusLive": "En juego",
"statusFinished": "Terminado",
"dataNotRecorded": "Aún no registrado",
"resultsRanking": "Clasificación",
"newSetPlaceholder": "Nuevo set, p. ej. 11:7",
"finishMatch": "Finalizar",
"correctMatch": "Corregir",
"markMatchLive": "Marcar como en vivo",
"unmarkMatchLive": "Quitar marca en vivo",
"stageConfigTitle": "Ronda intermedia y final",
"stageConfigLoading": "Cargando configuración de rondas…",
"stageConfigIntro": "La ronda intermedia es opcional. Si la activas, después habrá siempre una ronda final. Una ronda final eliminatoria se crea como un único cuadro.",
"useIntermediateStage": "Usar ronda intermedia",
"intermediateRound": "Ronda intermedia (ronda 2)",
"finalRound": "Ronda final",
"playThirdPlace": "Jugar por el tercer puesto",
"roundMode": "Modo",
"roundGroupCount": "Número de grupos",
"promotionRule12": "Avance: ronda preliminar → ronda intermedia (1→2)",
"promotionRuleFinal": "Avance: ronda {from} → ronda {to}",
"addPoolRule": "Añadir regla de grupos",
"noPoolRulesYet12": "Aún no hay reglas. Ejemplo: puestos 1 y 2 -> grupos superiores de la ronda 2.",
"noPoolRulesYetFinal": "Aún no hay reglas. Ejemplo: puestos 1 y 2 -> ronda final.",
"placesFromEachGroup": "Puestos de cada grupo (p. ej. 1,2)",
"target": "Destino",
"targetGroupCount": "Número de grupos destino",
"saveRounds": "Guardar rondas",
"createFinalFromPreliminary": "Crear ronda final desde la preliminar",
"createIntermediateFromPreliminary": "Crear ronda intermedia desde la preliminar",
"createFinalFromIntermediate": "Crear ronda final desde la intermedia",
"stageConfigMissingIds": "No se puede guardar: falta el ID del club o del torneo.",
"stageConfigLoadError": "Error al cargar la configuración de rondas.",
"stageConfigBadServerResponse": "Respuesta del servidor no válida.",
"stageCreated": "Se creó la ronda {round}.",
"atLeastOnePoolRule": "Añade al menos una regla de grupos para {label} (p. ej. puestos 1,2).",
"enterMiniLocation": "Introduce una ubicación.",
"selectTournamentFirst": "Selecciona primero un torneo.",
"errorGeneratingPdf": "Error al generar el PDF.",
"orphanedMatchesRemoved": "Se eliminaron {count} partidos huérfanos.",
"noOrphanedMatchesFound": "No se encontraron partidos huérfanos.",
"enterClassName": "Introduce un nombre de clase.",
"deleteClassTitle": "Eliminar clase",
"deleteClassConfirm": "¿De verdad quieres eliminar la clase \"{name}\"?",
"deleteClassParticipantsDetached": "Todos los participantes se desvincularán de esta clase.",
"enterExternalParticipantName": "Introduce al menos el nombre y el apellido.",
"noTournamentDate": "No hay fecha de torneo disponible.",
"noValidTrainingParticipants": "No se encontraron participantes válidos en la sesión de entrenamiento para esta fecha.",
"noTrainingParticipants": "No se encontraron participantes en la sesión de entrenamiento para esta fecha.",
"noTrainingOnDate": "No se encontró ninguna sesión de entrenamiento para {date}.",
"selectTwoDifferentPlayers": "Selecciona dos jugadores distintos.",
"pairingAlreadyExists": "Esta pareja ya existe.",
"participantNotFound": "Participante no encontrado.",
"minimumParticipantsForPairings": "Se requieren al menos 2 participantes para formar parejas.",
"deleteExistingPairingsConfirm": "Se eliminarán las parejas existentes. ¿Continuar?",
"pairingsCreatedWithErrors": "Se crearon {successCount} parejas, {errorCount} errores.",
"participantConflicts": "Conflictos",
"unpairedDoubles": "Dobles sin pareja",
"noEligibleClass": "No se encontró una categoría adecuada.",
"eligibleClassesHint": "Adecuado para: {classes}",
"warningClassMissing": "Categoría no encontrada",
"warningGenderMismatch": "El género no coincide con la categoría",
"warningGenderMissing": "Falta el género para esta categoría",
"warningBirthDateMissing": "Falta la fecha de nacimiento para esta categoría",
"warningTooOldForClass": "Demasiado mayor para esta categoría",
"warningTooYoungForClass": "Demasiado joven para esta categoría",
"warningMissingPairing": "Falta la pareja de dobles",
"autoAssignableParticipantsHint": "{count} of them can be assigned automatically.",
"assignmentReviewTitle": "{count} participants need a choice:",
"conflictSuggestionLabel": "Suggestions:",
"workspaceProblemsTitle": "{count} open issues",
"problemConfigTitle": "Configuration incomplete",
"problemConfigDescription": "Check date, name, winning sets and at least one class.",
"problemUnassignedTitle": "{count} participants without class",
"problemUnassignedDescription": "These participants still need a manual class assignment.",
"problemUnassignedAutoDescription": "{count} of them can be assigned automatically right away.",
"problemConflictsTitle": "{count} participants with conflicts",
"problemConflictsDescription": "Review invalid classes, missing data or unclear assignments.",
"problemDoublesTitle": "{count} open doubles partners",
"problemDoublesDescription": "One or more doubles classes still need partner assignments.",
"problemDoublesAutoDescription": "The open doubles class can be paired automatically right away.",
"problemGroupsMissingTitle": "Groups not created yet",
"problemGroupsMissingDescription": "The group tournament needs groups to be created first.",
"problemGroupMatchesTitle": "Group matches not generated yet",
"problemGroupMatchesDescription": "The groups are ready, but the matches have not been created yet.",
"problemKnockoutReadyTitle": "Knockout can be started",
"problemKnockoutReadyDescription": "The group stage is complete and the final round can be generated now.",
"autoAssignEligible": "Assign automatically",
"autoAssignEligibleNone": "There are currently no participants with a single automatic class assignment.",
"autoAssignEligibleDone": "{count} participants were assigned automatically.",
"autoAssignEligiblePartial": "{successCount} participants were assigned automatically, {errorCount} failed.",
"doublesPairingHint": "{count} participants in this doubles class still need a partner.",
"createSuggestedPairings": "Create pairings",
"statusUnpairedDoubles": "{count} doubles without partner",
"statusActionPair": "Pair",
"stageConfigCurrentSetup": "Current structure",
"stageFlowWithIntermediate": "Preliminary round -> intermediate round ({stage2}) -> final round ({final})",
"stageFlowDirectFinal": "Preliminary round -> final round ({final})",
"stageStep1": "Step 1",
"stageStep2": "Step 2",
"stageStep3": "Step 3",
"stageStep1Title": "Decide on an intermediate round",
"stageStep1Description": "First decide whether there should be an additional round after the preliminary round.",
"stageStep2Description": "Define what the intermediate round should look like and how many groups it needs.",
"stageFinalDescription": "Decide whether the final round should be played as groups or directly as a knockout bracket.",
"promotionRuleDescription12": "Define which places from each preliminary group advance to the intermediate round.",
"promotionRuleDescriptionFinalKnockout": "Define which places from the previous round advance to the knockout bracket.",
"promotionRuleDescriptionFinalGroups": "Define which places from the previous round advance to the final-round groups.",
"poolRuleLabel": "Rule {number}",
"poolRuleSummary": "Places {places} go to {target}",
"poolRulePlacesLabel": "Which places advance?",
"poolRulePlacesHelp": "Example: 1 or 1,2",
"poolRuleTargetLabel": "Where do these places go?",
"poolRuleQuickExamples": "Quick examples:",
"targetGroupsLabel": "{count} groups",
"transferQualifiedToFinalFromPreliminary": "Move qualified players straight into the final round",
"transferQualifiedToIntermediate": "Move qualified players into the intermediate round",
"transferQualifiedToFinalFromIntermediate": "Move qualified players into the final round",
"stageParticipantsTransferred": "Qualified players were moved into {round}.",
"transferQualifiedToFinalFromPreliminaryDesc": "Uses the preliminary-round to final-round rules and creates the qualified players directly there.",
"transferQualifiedToIntermediateDesc": "Uses the preliminary-round to intermediate-round rules and moves the qualified players across.",
"transferQualifiedToFinalFromIntermediateDesc": "Uses the intermediate-round to final-round rules and moves the qualified players across.",
"stageActionsTitle": "Next actions",
"stageActionsDescription": "These steps move qualified players into the next round.",
"stageActionRun": "Run now",
"stageActionMissingRulesHint": "Create at least one advancement rule first before moving players into the next round.",
"poolRulePreview": "From each group, places {places} advance to {target}.",
"poolRuleTargetKnockout": "the knockout bracket",
"poolRuleTargetGroupsDetailed": "{count} target groups",
"stageValidationTitle": "Review configuration",
"stageValidationDescription": "Fix these points before saving or moving players into the next round.",
"stageValidationGroupCountRequired": "Please set at least one valid group count.",
"stageValidationNoRules": "There is no advancement rule yet for {stage}.",
"stageValidationRulePlacesRequired": "Enter at least one place that should advance.",
"stageValidationRulePlacesInvalid": "The place definition is invalid. Only positive whole numbers such as 1 or 1,2 are allowed.",
"stageValidationRulePlacesDuplicate": "A single rule must not contain the same place more than once.",
"stageValidationRulesOverlap": "Place {place} is assigned twice, in rule {first} and rule {second}.",
"stageValidationTargetGroupsRequired": "Please enter a valid number of target groups.",
"stageValidationKnockoutTargetOnly": "For a knockout final round, only the knockout bracket is allowed as the target here.",
"stageValidationSaveBlocked": "Please fix the highlighted configuration errors first.",
"stageValidationSuggestion": "Suggestion: {value}",
"stageValidationRulePlacesGap": "The place definition has gaps. Usually a continuous order such as 1,2 or 1,2,3 is intended.",
"stageValidationTargetGroupCountMismatch": "The number of target groups must match the target round. Expected: {expected}.",
"stageValidationTargetTypeMismatch": "The target of this rule must match the target round. Expected: {target}.",
"stageValidationApplyFix": "Apply",
"stageValidationApplyTargetTypeFix": "Switch to {target}",
"stageValidationApplyTargetGroupFix": "Set to {count} target groups",
"ruleStatusOk": "Looks good",
"ruleStatusReview": "Review",
"ruleStatusBlocked": "Blocked",
"ruleStatusOkDescription": "This rule matches the current target round and can be used as-is.",
"stageFlowHealthOk": "Round logic looks good",
"stageFlowHealthOkDescription": "The transitions between rounds are fully configured and currently coherent.",
"stageFlowHealthIncomplete": "Round logic is incomplete",
"stageFlowHealthMissingRules": "At least one transition does not yet have a complete advancement rule.",
"stageFlowHealthReviewDescription": "The round logic is basically usable, but should still be reviewed because of open hints.",
"stageFlowHealthBlocked": "Round logic is contradictory",
"stageFlowHealthBlockedDescription": "At least one rule currently does not match the target round or contains blocking configuration errors.",
"stageFlowBreakdownOk": "Configuration looks good",
"stageFlowBreakdownWarnings": "{count} warning(s) open",
"stageFlowBreakdownErrors": "{count} blocking error(s)",
"stageFlowBreakdownMissingRule": "No complete rule has been added yet",
"stageFlowBreakdownActionAddRule": "Add rule",
"stageFlowBreakdownActionReview": "Review issues",
"stageFlowBreakdownActionOpen": "Open rule",
"stageValidationApplyAllFixes": "Apply {count} quick fix(es)",
"stageFlowRecommendationTitle": "Recommended next step",
"stageFlowRecommendationFixes": "The typical configuration problems can be cleaned up automatically right away.",
"stageFlowRecommendationMissingRule": "A complete rule is still missing for {label}. That is the best place to continue.",
"stageFlowRecommendationError": "Review the first blocking error in the round logic first.",
"stageFlowRecommendationWarnings": "There are still warnings that should be reviewed before saving.",
"stageFlowRecommendationSave": "The round logic is coherent. You can save the configuration now.",
"stageFlowPreviewMissing": "No rule has been configured yet.",
"stageFlowPreviewRule": "Places {places} go to {target}",
"stageValidationKnockoutNeedsTwoPlayers": "A knockout bracket needs at least 2 qualified players.",
"stageValidationKnockoutByesLikely": "With an expected {count} qualifiers, the knockout bracket will very likely include byes.",
"stageValidationNotEnoughQualifiersForGroups": "With only {count} qualifiers for {groups} target groups, empty groups would be created.",
"stageValidationThinGroupsLikely": "With {count} qualifiers across {groups} target groups, the groups will likely be very small.",
"stageFlowPreviewRuleWithCount": "{base} (expected qualifiers: {count})",
"stageFlowReadinessNoGroups": "No matching groups exist yet",
"stageFlowReadinessIncompleteGroups": "{count} group(s) are not fully decided yet",
"stageFlowReadinessReady": "Transition is ready to start",
"stageFlowQualifiedPreviewTitle": "Currently qualified",
"stageFlowQualifiedPreviewEntry": "G{group} · Place {position} · {name}"
}
}

View File

@@ -6,6 +6,7 @@
"common": {
"loading": "Naglo-load...",
"save": "I-save",
"saved": "Na-save",
"cancel": "Kanselahin",
"delete": "Tanggalin",
"edit": "I-edit",
@@ -30,6 +31,8 @@
"approvals": "Mga pag-apruba",
"statistics": "Mga istatistika ng pagsasanay",
"tournaments": "Mga paligsahan",
"clubTournaments": "Mga paligsahan ng club",
"tournamentParticipations": "Mga paglahok sa paligsahan",
"schedule": "Mga iskedyul",
"clubSettings": "Mga setting ng club",
"predefinedActivities": "Mga paunang natukoy na aktibidad",
@@ -120,11 +123,16 @@
"tournaments": {
"numberOfTables": "Bilang ng mesa",
"table": "Mesa",
"playerOne": "Manlalaro 1",
"playerTwo": "Manlalaro 2",
"groupsLabel": "Mga grupo",
"knockoutLabel": "KO",
"distributeTables": "Ipamahagi ang mga bakanteng mesa",
"distributeTablesResult": "Pamamahagi ng mesa",
"noFreeTables": "Walang bakanteng mesa.",
"noAssignableMatches": "Walang laban kung saan pareho ang mga manlalaro ay bakante.",
"tablesDistributed": "Ang mga mesa ay naipamahagi na.",
"errorDistributingTables": "May error sa pamamahagi ng mga mesa.",
"missingDataPDF": "Nawawalang datos bilang PDF",
"missingDataPDFTitle": "Nawawalang datos ng mga kalahok Mini championship",
"missingDataPDFSubtitle": "Pakikolekta ang nawawalang datos (may markang ____) mula sa mga kalahok at itala dito.",
@@ -137,7 +145,189 @@
"phone": "Telepono",
"generatingPDF": "Gumagawa ng PDF...",
"page": "Pahina",
"dataNotRecorded": "Hindi pa naitala"
"statusOpen": "Bukas",
"statusLive": "Live",
"statusFinished": "Tapos",
"dataNotRecorded": "Hindi pa naitala",
"resultsRanking": "Ranggo",
"newSetPlaceholder": "Bagong set, hal. 11:7",
"finishMatch": "Tapusin",
"correctMatch": "Itama",
"markMatchLive": "Markahan bilang live",
"unmarkMatchLive": "Alisin ang live marker",
"stageConfigTitle": "Intermediate at Final Round",
"stageConfigLoading": "Naglo-load ng round configuration…",
"stageConfigIntro": "Opsyonal ang intermediate round. Kapag in-activate mo ito, laging may susunod na final round. Ang knockout final round ay gagawin bilang isang bracket.",
"useIntermediateStage": "Gamitin ang intermediate round",
"intermediateRound": "Intermediate round (round 2)",
"finalRound": "Final round",
"playThirdPlace": "Maglaro para sa ikatlong puwesto",
"roundMode": "Mode",
"roundGroupCount": "Bilang ng mga grupo",
"promotionRule12": "Advance: preliminary round → intermediate round (1→2)",
"promotionRuleFinal": "Advance: round {from} → round {to}",
"addPoolRule": "Magdagdag ng pool rule",
"noPoolRulesYet12": "Wala pang rules. Halimbawa: places 1 at 2 -> top round-2 groups.",
"noPoolRulesYetFinal": "Wala pang rules. Halimbawa: places 1 at 2 -> final round.",
"placesFromEachGroup": "Mga puwesto mula sa bawat grupo (hal. 1,2)",
"target": "Target",
"targetGroupCount": "Target na bilang ng grupo",
"saveRounds": "I-save ang rounds",
"createFinalFromPreliminary": "Gumawa ng final round mula sa preliminary round",
"createIntermediateFromPreliminary": "Gumawa ng intermediate round mula sa preliminary round",
"createFinalFromIntermediate": "Gumawa ng final round mula sa intermediate round",
"stageConfigMissingIds": "Hindi ma-save: kulang ang club o tournament ID.",
"stageConfigLoadError": "Error sa pag-load ng round configuration.",
"stageConfigBadServerResponse": "Hindi wastong tugon mula sa server.",
"stageCreated": "Nagawa ang round {round}.",
"atLeastOnePoolRule": "Mangyaring magdagdag ng kahit isang pool rule para sa {label} (hal. places 1,2).",
"enterMiniLocation": "Mangyaring maglagay ng lokasyon.",
"selectTournamentFirst": "Mangyaring pumili muna ng tournament.",
"errorGeneratingPdf": "Error sa pagbuo ng PDF.",
"orphanedMatchesRemoved": "{count} orphaned matches ang naalis.",
"noOrphanedMatchesFound": "Walang nahanap na orphaned matches.",
"enterClassName": "Mangyaring maglagay ng pangalan ng class.",
"deleteClassTitle": "Burahin ang class",
"deleteClassConfirm": "Sigurado ka bang buburahin ang class na \"{name}\"?",
"deleteClassParticipantsDetached": "Aalisin ang lahat ng participants sa class na ito.",
"enterExternalParticipantName": "Mangyaring ilagay kahit first name at last name.",
"noTournamentDate": "Walang tournament date.",
"noValidTrainingParticipants": "Walang valid na participants na nakita sa training session para sa petsang ito.",
"noTrainingParticipants": "Walang participants na nakita sa training session para sa petsang ito.",
"noTrainingOnDate": "Walang training session para sa {date}.",
"selectTwoDifferentPlayers": "Mangyaring pumili ng dalawang magkaibang player.",
"pairingAlreadyExists": "May ganitong pairing na.",
"participantNotFound": "Hindi nakita ang participant.",
"minimumParticipantsForPairings": "Kailangan ng hindi bababa sa 2 participants para sa pairings.",
"deleteExistingPairingsConfirm": "Buburahin ang mga kasalukuyang pairing. Magpatuloy?",
"pairingsCreatedWithErrors": "{successCount} pairings ang nagawa, {errorCount} errors.",
"participantConflicts": "Conflicts",
"unpairedDoubles": "Doubles without partner",
"noEligibleClass": "No suitable class found.",
"eligibleClassesHint": "Suitable for: {classes}",
"warningClassMissing": "Class not found",
"warningGenderMismatch": "Gender does not match class",
"warningGenderMissing": "Gender is missing for this class",
"warningBirthDateMissing": "Birth date is missing for this class",
"warningTooOldForClass": "Too old for this class",
"warningTooYoungForClass": "Too young for this class",
"warningMissingPairing": "Doubles partner missing",
"autoAssignableParticipantsHint": "{count} of them can be assigned automatically.",
"assignmentReviewTitle": "{count} participants need a choice:",
"conflictSuggestionLabel": "Suggestions:",
"workspaceProblemsTitle": "{count} open issues",
"problemConfigTitle": "Configuration incomplete",
"problemConfigDescription": "Check date, name, winning sets and at least one class.",
"problemUnassignedTitle": "{count} participants without class",
"problemUnassignedDescription": "These participants still need a manual class assignment.",
"problemUnassignedAutoDescription": "{count} of them can be assigned automatically right away.",
"problemConflictsTitle": "{count} participants with conflicts",
"problemConflictsDescription": "Review invalid classes, missing data or unclear assignments.",
"problemDoublesTitle": "{count} open doubles partners",
"problemDoublesDescription": "One or more doubles classes still need partner assignments.",
"problemDoublesAutoDescription": "The open doubles class can be paired automatically right away.",
"problemGroupsMissingTitle": "Groups not created yet",
"problemGroupsMissingDescription": "The group tournament needs groups to be created first.",
"problemGroupMatchesTitle": "Group matches not generated yet",
"problemGroupMatchesDescription": "The groups are ready, but the matches have not been created yet.",
"problemKnockoutReadyTitle": "Knockout can be started",
"problemKnockoutReadyDescription": "The group stage is complete and the final round can be generated now.",
"autoAssignEligible": "Assign automatically",
"autoAssignEligibleNone": "There are currently no participants with a single automatic class assignment.",
"autoAssignEligibleDone": "{count} participants were assigned automatically.",
"autoAssignEligiblePartial": "{successCount} participants were assigned automatically, {errorCount} failed.",
"doublesPairingHint": "{count} participants in this doubles class still need a partner.",
"createSuggestedPairings": "Create pairings",
"statusUnpairedDoubles": "{count} doubles without partner",
"statusActionPair": "Pair",
"stageConfigCurrentSetup": "Current structure",
"stageFlowWithIntermediate": "Preliminary round -> intermediate round ({stage2}) -> final round ({final})",
"stageFlowDirectFinal": "Preliminary round -> final round ({final})",
"stageStep1": "Step 1",
"stageStep2": "Step 2",
"stageStep3": "Step 3",
"stageStep1Title": "Decide on an intermediate round",
"stageStep1Description": "First decide whether there should be an additional round after the preliminary round.",
"stageStep2Description": "Define what the intermediate round should look like and how many groups it needs.",
"stageFinalDescription": "Decide whether the final round should be played as groups or directly as a knockout bracket.",
"promotionRuleDescription12": "Define which places from each preliminary group advance to the intermediate round.",
"promotionRuleDescriptionFinalKnockout": "Define which places from the previous round advance to the knockout bracket.",
"promotionRuleDescriptionFinalGroups": "Define which places from the previous round advance to the final-round groups.",
"poolRuleLabel": "Rule {number}",
"poolRuleSummary": "Places {places} go to {target}",
"poolRulePlacesLabel": "Which places advance?",
"poolRulePlacesHelp": "Example: 1 or 1,2",
"poolRuleTargetLabel": "Where do these places go?",
"poolRuleQuickExamples": "Quick examples:",
"targetGroupsLabel": "{count} groups",
"transferQualifiedToFinalFromPreliminary": "Move qualified players straight into the final round",
"transferQualifiedToIntermediate": "Move qualified players into the intermediate round",
"transferQualifiedToFinalFromIntermediate": "Move qualified players into the final round",
"stageParticipantsTransferred": "Qualified players were moved into {round}.",
"transferQualifiedToFinalFromPreliminaryDesc": "Uses the preliminary-round to final-round rules and creates the qualified players directly there.",
"transferQualifiedToIntermediateDesc": "Uses the preliminary-round to intermediate-round rules and moves the qualified players across.",
"transferQualifiedToFinalFromIntermediateDesc": "Uses the intermediate-round to final-round rules and moves the qualified players across.",
"stageActionsTitle": "Next actions",
"stageActionsDescription": "These steps move qualified players into the next round.",
"stageActionRun": "Run now",
"stageActionMissingRulesHint": "Create at least one advancement rule first before moving players into the next round.",
"poolRulePreview": "From each group, places {places} advance to {target}.",
"poolRuleTargetKnockout": "the knockout bracket",
"poolRuleTargetGroupsDetailed": "{count} target groups",
"stageValidationTitle": "Review configuration",
"stageValidationDescription": "Fix these points before saving or moving players into the next round.",
"stageValidationGroupCountRequired": "Please set at least one valid group count.",
"stageValidationNoRules": "There is no advancement rule yet for {stage}.",
"stageValidationRulePlacesRequired": "Enter at least one place that should advance.",
"stageValidationRulePlacesInvalid": "The place definition is invalid. Only positive whole numbers such as 1 or 1,2 are allowed.",
"stageValidationRulePlacesDuplicate": "A single rule must not contain the same place more than once.",
"stageValidationRulesOverlap": "Place {place} is assigned twice, in rule {first} and rule {second}.",
"stageValidationTargetGroupsRequired": "Please enter a valid number of target groups.",
"stageValidationKnockoutTargetOnly": "For a knockout final round, only the knockout bracket is allowed as the target here.",
"stageValidationSaveBlocked": "Please fix the highlighted configuration errors first.",
"stageValidationSuggestion": "Suggestion: {value}",
"stageValidationRulePlacesGap": "The place definition has gaps. Usually a continuous order such as 1,2 or 1,2,3 is intended.",
"stageValidationTargetGroupCountMismatch": "The number of target groups must match the target round. Expected: {expected}.",
"stageValidationTargetTypeMismatch": "The target of this rule must match the target round. Expected: {target}.",
"stageValidationApplyFix": "Apply",
"stageValidationApplyTargetTypeFix": "Switch to {target}",
"stageValidationApplyTargetGroupFix": "Set to {count} target groups",
"ruleStatusOk": "Looks good",
"ruleStatusReview": "Review",
"ruleStatusBlocked": "Blocked",
"ruleStatusOkDescription": "This rule matches the current target round and can be used as-is.",
"stageFlowHealthOk": "Round logic looks good",
"stageFlowHealthOkDescription": "The transitions between rounds are fully configured and currently coherent.",
"stageFlowHealthIncomplete": "Round logic is incomplete",
"stageFlowHealthMissingRules": "At least one transition does not yet have a complete advancement rule.",
"stageFlowHealthReviewDescription": "The round logic is basically usable, but should still be reviewed because of open hints.",
"stageFlowHealthBlocked": "Round logic is contradictory",
"stageFlowHealthBlockedDescription": "At least one rule currently does not match the target round or contains blocking configuration errors.",
"stageFlowBreakdownOk": "Configuration looks good",
"stageFlowBreakdownWarnings": "{count} warning(s) open",
"stageFlowBreakdownErrors": "{count} blocking error(s)",
"stageFlowBreakdownMissingRule": "No complete rule has been added yet",
"stageFlowBreakdownActionAddRule": "Add rule",
"stageFlowBreakdownActionReview": "Review issues",
"stageFlowBreakdownActionOpen": "Open rule",
"stageValidationApplyAllFixes": "Apply {count} quick fix(es)",
"stageFlowRecommendationTitle": "Recommended next step",
"stageFlowRecommendationFixes": "The typical configuration problems can be cleaned up automatically right away.",
"stageFlowRecommendationMissingRule": "A complete rule is still missing for {label}. That is the best place to continue.",
"stageFlowRecommendationError": "Review the first blocking error in the round logic first.",
"stageFlowRecommendationWarnings": "There are still warnings that should be reviewed before saving.",
"stageFlowRecommendationSave": "The round logic is coherent. You can save the configuration now.",
"stageFlowPreviewMissing": "No rule has been configured yet.",
"stageFlowPreviewRule": "Places {places} go to {target}",
"stageValidationKnockoutNeedsTwoPlayers": "A knockout bracket needs at least 2 qualified players.",
"stageValidationKnockoutByesLikely": "With an expected {count} qualifiers, the knockout bracket will very likely include byes.",
"stageValidationNotEnoughQualifiersForGroups": "With only {count} qualifiers for {groups} target groups, empty groups would be created.",
"stageValidationThinGroupsLikely": "With {count} qualifiers across {groups} target groups, the groups will likely be very small.",
"stageFlowPreviewRuleWithCount": "{base} (expected qualifiers: {count})",
"stageFlowReadinessNoGroups": "No matching groups exist yet",
"stageFlowReadinessIncompleteGroups": "{count} group(s) are not fully decided yet",
"stageFlowReadinessReady": "Transition is ready to start",
"stageFlowQualifiedPreviewTitle": "Currently qualified",
"stageFlowQualifiedPreviewEntry": "G{group} · Place {position} · {name}"
}
}

View File

@@ -6,6 +6,7 @@
"common": {
"loading": "Chargement...",
"save": "Enregistrer",
"saved": "Enregistré",
"cancel": "Annuler",
"delete": "Supprimer",
"edit": "Modifier",
@@ -30,6 +31,8 @@
"approvals": "Approbations",
"statistics": "Statistiques d'entraînement",
"tournaments": "Tournois",
"clubTournaments": "Tournois du club",
"tournamentParticipations": "Participations aux tournois",
"schedule": "Calendriers",
"clubSettings": "Paramètres du club",
"predefinedActivities": "Activités prédéfinies",
@@ -120,11 +123,16 @@
"tournaments": {
"numberOfTables": "Nombre de tables",
"table": "Table",
"playerOne": "Joueur 1",
"playerTwo": "Joueur 2",
"groupsLabel": "Groupes",
"knockoutLabel": "KO",
"distributeTables": "Distribuer les tables libres",
"distributeTablesResult": "Distribution des tables",
"noFreeTables": "Aucune table libre disponible.",
"noAssignableMatches": "Aucun match disponible où les deux joueurs sont libres.",
"tablesDistributed": "Les tables ont été distribuées.",
"errorDistributingTables": "Erreur lors de la répartition des tables.",
"missingDataPDF": "Données manquantes en PDF",
"missingDataPDFTitle": "Données manquantes des participants Mini-championnat",
"missingDataPDFSubtitle": "Veuillez collecter les données manquantes (marquées ____) auprès des participants et les noter ici.",
@@ -137,7 +145,189 @@
"phone": "Téléphone",
"generatingPDF": "Génération du PDF...",
"page": "Page",
"dataNotRecorded": "Pas encore enregistré"
"statusOpen": "Ouvert",
"statusLive": "En direct",
"statusFinished": "Terminé",
"dataNotRecorded": "Pas encore enregistré",
"resultsRanking": "Classement",
"newSetPlaceholder": "Nouveau set, p. ex. 11:7",
"finishMatch": "Terminer",
"correctMatch": "Corriger",
"markMatchLive": "Marquer en direct",
"unmarkMatchLive": "Retirer le marquage en direct",
"stageConfigTitle": "Tour intermédiaire et finale",
"stageConfigLoading": "Chargement de la configuration des tours…",
"stageConfigIntro": "Le tour intermédiaire est facultatif. Si vous l'activez, un tour final suit toujours. Une phase finale à élimination directe est créée sous forme d'un tableau unique.",
"useIntermediateStage": "Utiliser un tour intermédiaire",
"intermediateRound": "Tour intermédiaire (tour 2)",
"finalRound": "Tour final",
"playThirdPlace": "Jouer la troisième place",
"roundMode": "Mode",
"roundGroupCount": "Nombre de groupes",
"promotionRule12": "Qualification : tour préliminaire → tour intermédiaire (1→2)",
"promotionRuleFinal": "Qualification : tour {from} → tour {to}",
"addPoolRule": "Ajouter une règle de groupes",
"noPoolRulesYet12": "Aucune règle pour le moment. Exemple : places 1 et 2 -> groupes supérieurs du tour 2.",
"noPoolRulesYetFinal": "Aucune règle pour le moment. Exemple : places 1 et 2 -> tour final.",
"placesFromEachGroup": "Places de chaque groupe (p. ex. 1,2)",
"target": "Cible",
"targetGroupCount": "Nombre de groupes cibles",
"saveRounds": "Enregistrer les tours",
"createFinalFromPreliminary": "Créer le tour final depuis le tour préliminaire",
"createIntermediateFromPreliminary": "Créer le tour intermédiaire depuis le tour préliminaire",
"createFinalFromIntermediate": "Créer le tour final depuis le tour intermédiaire",
"stageConfigMissingIds": "Impossible d'enregistrer : l'identifiant du club ou du tournoi est manquant.",
"stageConfigLoadError": "Erreur lors du chargement de la configuration des tours.",
"stageConfigBadServerResponse": "Réponse serveur invalide.",
"stageCreated": "Le tour {round} a été créé.",
"atLeastOnePoolRule": "Veuillez ajouter au moins une règle de groupes pour {label} (p. ex. places 1,2).",
"enterMiniLocation": "Veuillez saisir un lieu.",
"selectTournamentFirst": "Veuillez d'abord sélectionner un tournoi.",
"errorGeneratingPdf": "Erreur lors de la génération du PDF.",
"orphanedMatchesRemoved": "{count} matchs orphelins supprimés.",
"noOrphanedMatchesFound": "Aucun match orphelin trouvé.",
"enterClassName": "Veuillez saisir un nom de classe.",
"deleteClassTitle": "Supprimer la classe",
"deleteClassConfirm": "Voulez-vous vraiment supprimer la classe \"{name}\" ?",
"deleteClassParticipantsDetached": "Tous les participants seront dissociés de cette classe.",
"enterExternalParticipantName": "Veuillez saisir au moins le prénom et le nom.",
"noTournamentDate": "Aucune date de tournoi disponible.",
"noValidTrainingParticipants": "Aucun participant valide n'a été trouvé dans la séance d'entraînement pour cette date.",
"noTrainingParticipants": "Aucun participant n'a été trouvé dans la séance d'entraînement pour cette date.",
"noTrainingOnDate": "Aucune séance d'entraînement trouvée pour le {date}.",
"selectTwoDifferentPlayers": "Veuillez sélectionner deux joueurs différents.",
"pairingAlreadyExists": "Cette paire existe déjà.",
"participantNotFound": "Participant introuvable.",
"minimumParticipantsForPairings": "Au moins 2 participants sont nécessaires pour créer des paires.",
"deleteExistingPairingsConfirm": "Les paires existantes seront supprimées. Continuer ?",
"pairingsCreatedWithErrors": "{successCount} paires créées, {errorCount} erreurs.",
"participantConflicts": "Conflits",
"unpairedDoubles": "Double sans partenaire",
"noEligibleClass": "Aucune catégorie appropriée trouvée.",
"eligibleClassesHint": "Convient pour : {classes}",
"warningClassMissing": "Catégorie introuvable",
"warningGenderMismatch": "Le sexe ne correspond pas à la catégorie",
"warningGenderMissing": "Le sexe manque pour cette catégorie",
"warningBirthDateMissing": "La date de naissance manque pour cette catégorie",
"warningTooOldForClass": "Trop âgé pour cette catégorie",
"warningTooYoungForClass": "Trop jeune pour cette catégorie",
"warningMissingPairing": "Partenaire de double manquant",
"autoAssignableParticipantsHint": "{count} of them can be assigned automatically.",
"assignmentReviewTitle": "{count} participants need a choice:",
"conflictSuggestionLabel": "Suggestions:",
"workspaceProblemsTitle": "{count} open issues",
"problemConfigTitle": "Configuration incomplete",
"problemConfigDescription": "Check date, name, winning sets and at least one class.",
"problemUnassignedTitle": "{count} participants without class",
"problemUnassignedDescription": "These participants still need a manual class assignment.",
"problemUnassignedAutoDescription": "{count} of them can be assigned automatically right away.",
"problemConflictsTitle": "{count} participants with conflicts",
"problemConflictsDescription": "Review invalid classes, missing data or unclear assignments.",
"problemDoublesTitle": "{count} open doubles partners",
"problemDoublesDescription": "One or more doubles classes still need partner assignments.",
"problemDoublesAutoDescription": "The open doubles class can be paired automatically right away.",
"problemGroupsMissingTitle": "Groups not created yet",
"problemGroupsMissingDescription": "The group tournament needs groups to be created first.",
"problemGroupMatchesTitle": "Group matches not generated yet",
"problemGroupMatchesDescription": "The groups are ready, but the matches have not been created yet.",
"problemKnockoutReadyTitle": "Knockout can be started",
"problemKnockoutReadyDescription": "The group stage is complete and the final round can be generated now.",
"autoAssignEligible": "Assign automatically",
"autoAssignEligibleNone": "There are currently no participants with a single automatic class assignment.",
"autoAssignEligibleDone": "{count} participants were assigned automatically.",
"autoAssignEligiblePartial": "{successCount} participants were assigned automatically, {errorCount} failed.",
"doublesPairingHint": "{count} participants in this doubles class still need a partner.",
"createSuggestedPairings": "Create pairings",
"statusUnpairedDoubles": "{count} doubles without partner",
"statusActionPair": "Pair",
"stageConfigCurrentSetup": "Current structure",
"stageFlowWithIntermediate": "Preliminary round -> intermediate round ({stage2}) -> final round ({final})",
"stageFlowDirectFinal": "Preliminary round -> final round ({final})",
"stageStep1": "Step 1",
"stageStep2": "Step 2",
"stageStep3": "Step 3",
"stageStep1Title": "Decide on an intermediate round",
"stageStep1Description": "First decide whether there should be an additional round after the preliminary round.",
"stageStep2Description": "Define what the intermediate round should look like and how many groups it needs.",
"stageFinalDescription": "Decide whether the final round should be played as groups or directly as a knockout bracket.",
"promotionRuleDescription12": "Define which places from each preliminary group advance to the intermediate round.",
"promotionRuleDescriptionFinalKnockout": "Define which places from the previous round advance to the knockout bracket.",
"promotionRuleDescriptionFinalGroups": "Define which places from the previous round advance to the final-round groups.",
"poolRuleLabel": "Rule {number}",
"poolRuleSummary": "Places {places} go to {target}",
"poolRulePlacesLabel": "Which places advance?",
"poolRulePlacesHelp": "Example: 1 or 1,2",
"poolRuleTargetLabel": "Where do these places go?",
"poolRuleQuickExamples": "Quick examples:",
"targetGroupsLabel": "{count} groups",
"transferQualifiedToFinalFromPreliminary": "Move qualified players straight into the final round",
"transferQualifiedToIntermediate": "Move qualified players into the intermediate round",
"transferQualifiedToFinalFromIntermediate": "Move qualified players into the final round",
"stageParticipantsTransferred": "Qualified players were moved into {round}.",
"transferQualifiedToFinalFromPreliminaryDesc": "Uses the preliminary-round to final-round rules and creates the qualified players directly there.",
"transferQualifiedToIntermediateDesc": "Uses the preliminary-round to intermediate-round rules and moves the qualified players across.",
"transferQualifiedToFinalFromIntermediateDesc": "Uses the intermediate-round to final-round rules and moves the qualified players across.",
"stageActionsTitle": "Next actions",
"stageActionsDescription": "These steps move qualified players into the next round.",
"stageActionRun": "Run now",
"stageActionMissingRulesHint": "Create at least one advancement rule first before moving players into the next round.",
"poolRulePreview": "From each group, places {places} advance to {target}.",
"poolRuleTargetKnockout": "the knockout bracket",
"poolRuleTargetGroupsDetailed": "{count} target groups",
"stageValidationTitle": "Review configuration",
"stageValidationDescription": "Fix these points before saving or moving players into the next round.",
"stageValidationGroupCountRequired": "Please set at least one valid group count.",
"stageValidationNoRules": "There is no advancement rule yet for {stage}.",
"stageValidationRulePlacesRequired": "Enter at least one place that should advance.",
"stageValidationRulePlacesInvalid": "The place definition is invalid. Only positive whole numbers such as 1 or 1,2 are allowed.",
"stageValidationRulePlacesDuplicate": "A single rule must not contain the same place more than once.",
"stageValidationRulesOverlap": "Place {place} is assigned twice, in rule {first} and rule {second}.",
"stageValidationTargetGroupsRequired": "Please enter a valid number of target groups.",
"stageValidationKnockoutTargetOnly": "For a knockout final round, only the knockout bracket is allowed as the target here.",
"stageValidationSaveBlocked": "Please fix the highlighted configuration errors first.",
"stageValidationSuggestion": "Suggestion: {value}",
"stageValidationRulePlacesGap": "The place definition has gaps. Usually a continuous order such as 1,2 or 1,2,3 is intended.",
"stageValidationTargetGroupCountMismatch": "The number of target groups must match the target round. Expected: {expected}.",
"stageValidationTargetTypeMismatch": "The target of this rule must match the target round. Expected: {target}.",
"stageValidationApplyFix": "Apply",
"stageValidationApplyTargetTypeFix": "Switch to {target}",
"stageValidationApplyTargetGroupFix": "Set to {count} target groups",
"ruleStatusOk": "Looks good",
"ruleStatusReview": "Review",
"ruleStatusBlocked": "Blocked",
"ruleStatusOkDescription": "This rule matches the current target round and can be used as-is.",
"stageFlowHealthOk": "Round logic looks good",
"stageFlowHealthOkDescription": "The transitions between rounds are fully configured and currently coherent.",
"stageFlowHealthIncomplete": "Round logic is incomplete",
"stageFlowHealthMissingRules": "At least one transition does not yet have a complete advancement rule.",
"stageFlowHealthReviewDescription": "The round logic is basically usable, but should still be reviewed because of open hints.",
"stageFlowHealthBlocked": "Round logic is contradictory",
"stageFlowHealthBlockedDescription": "At least one rule currently does not match the target round or contains blocking configuration errors.",
"stageFlowBreakdownOk": "Configuration looks good",
"stageFlowBreakdownWarnings": "{count} warning(s) open",
"stageFlowBreakdownErrors": "{count} blocking error(s)",
"stageFlowBreakdownMissingRule": "No complete rule has been added yet",
"stageFlowBreakdownActionAddRule": "Add rule",
"stageFlowBreakdownActionReview": "Review issues",
"stageFlowBreakdownActionOpen": "Open rule",
"stageValidationApplyAllFixes": "Apply {count} quick fix(es)",
"stageFlowRecommendationTitle": "Recommended next step",
"stageFlowRecommendationFixes": "The typical configuration problems can be cleaned up automatically right away.",
"stageFlowRecommendationMissingRule": "A complete rule is still missing for {label}. That is the best place to continue.",
"stageFlowRecommendationError": "Review the first blocking error in the round logic first.",
"stageFlowRecommendationWarnings": "There are still warnings that should be reviewed before saving.",
"stageFlowRecommendationSave": "The round logic is coherent. You can save the configuration now.",
"stageFlowPreviewMissing": "No rule has been configured yet.",
"stageFlowPreviewRule": "Places {places} go to {target}",
"stageValidationKnockoutNeedsTwoPlayers": "A knockout bracket needs at least 2 qualified players.",
"stageValidationKnockoutByesLikely": "With an expected {count} qualifiers, the knockout bracket will very likely include byes.",
"stageValidationNotEnoughQualifiersForGroups": "With only {count} qualifiers for {groups} target groups, empty groups would be created.",
"stageValidationThinGroupsLikely": "With {count} qualifiers across {groups} target groups, the groups will likely be very small.",
"stageFlowPreviewRuleWithCount": "{base} (expected qualifiers: {count})",
"stageFlowReadinessNoGroups": "No matching groups exist yet",
"stageFlowReadinessIncompleteGroups": "{count} group(s) are not fully decided yet",
"stageFlowReadinessReady": "Transition is ready to start",
"stageFlowQualifiedPreviewTitle": "Currently qualified",
"stageFlowQualifiedPreviewEntry": "G{group} · Place {position} · {name}"
}
}

View File

@@ -6,6 +6,7 @@
"common": {
"loading": "Caricamento...",
"save": "Salva",
"saved": "Salvato",
"cancel": "Annulla",
"delete": "Elimina",
"edit": "Modifica",
@@ -30,6 +31,8 @@
"approvals": "Approvazioni",
"statistics": "Statistiche di allenamento",
"tournaments": "Tornei",
"clubTournaments": "Tornei del club",
"tournamentParticipations": "Partecipazioni ai tornei",
"schedule": "Calendari",
"clubSettings": "Impostazioni club",
"predefinedActivities": "Attività predefinite",
@@ -120,11 +123,16 @@
"tournaments": {
"numberOfTables": "Numero di tavoli",
"table": "Tavolo",
"playerOne": "Giocatore 1",
"playerTwo": "Giocatore 2",
"groupsLabel": "Gruppi",
"knockoutLabel": "KO",
"distributeTables": "Distribuire tavoli liberi",
"distributeTablesResult": "Distribuzione dei tavoli",
"noFreeTables": "Nessun tavolo libero disponibile.",
"noAssignableMatches": "Nessuna partita disponibile in cui entrambi i giocatori sono liberi.",
"tablesDistributed": "I tavoli sono stati distribuiti.",
"errorDistributingTables": "Errore durante la distribuzione dei tavoli.",
"missingDataPDF": "Dati mancanti come PDF",
"missingDataPDFTitle": "Dati mancanti dei partecipanti Mini campionato",
"missingDataPDFSubtitle": "Si prega di raccogliere i dati mancanti (contrassegnati con ____) dai partecipanti e annotarli qui.",
@@ -137,7 +145,189 @@
"phone": "Telefono",
"generatingPDF": "Generazione PDF...",
"page": "Pagina",
"dataNotRecorded": "Non ancora registrato"
"statusOpen": "Aperto",
"statusLive": "Live",
"statusFinished": "Finito",
"dataNotRecorded": "Non ancora registrato",
"resultsRanking": "Classifica",
"newSetPlaceholder": "Nuovo set, es. 11:7",
"finishMatch": "Concludi",
"correctMatch": "Correggi",
"markMatchLive": "Segna come live",
"unmarkMatchLive": "Rimuovi indicatore live",
"stageConfigTitle": "Turno intermedio e finale",
"stageConfigLoading": "Caricamento configurazione dei turni…",
"stageConfigIntro": "Il turno intermedio è facoltativo. Se lo attivi, dopo ci sarà sempre un turno finale. Una fase finale a eliminazione diretta viene creata come un unico tabellone.",
"useIntermediateStage": "Usa turno intermedio",
"intermediateRound": "Turno intermedio (turno 2)",
"finalRound": "Turno finale",
"playThirdPlace": "Giocare la finale per il terzo posto",
"roundMode": "Modalità",
"roundGroupCount": "Numero di gruppi",
"promotionRule12": "Avanzamento: turno preliminare → turno intermedio (1→2)",
"promotionRuleFinal": "Avanzamento: turno {from} → turno {to}",
"addPoolRule": "Aggiungi regola del girone",
"noPoolRulesYet12": "Nessuna regola ancora. Esempio: posizioni 1 e 2 -> gruppi superiori del turno 2.",
"noPoolRulesYetFinal": "Nessuna regola ancora. Esempio: posizioni 1 e 2 -> turno finale.",
"placesFromEachGroup": "Posizioni da ogni gruppo (es. 1,2)",
"target": "Destinazione",
"targetGroupCount": "Numero di gruppi di destinazione",
"saveRounds": "Salva turni",
"createFinalFromPreliminary": "Crea il turno finale dal preliminare",
"createIntermediateFromPreliminary": "Crea il turno intermedio dal preliminare",
"createFinalFromIntermediate": "Crea il turno finale dall'intermedio",
"stageConfigMissingIds": "Impossibile salvare: manca l'ID del club o del torneo.",
"stageConfigLoadError": "Errore durante il caricamento della configurazione dei turni.",
"stageConfigBadServerResponse": "Risposta del server non valida.",
"stageCreated": "Il turno {round} è stato creato.",
"atLeastOnePoolRule": "Aggiungi almeno una regola del girone per {label} (es. posizioni 1,2).",
"enterMiniLocation": "Inserisci una località.",
"selectTournamentFirst": "Seleziona prima un torneo.",
"errorGeneratingPdf": "Errore durante la generazione del PDF.",
"orphanedMatchesRemoved": "{count} partite orfane rimosse.",
"noOrphanedMatchesFound": "Nessuna partita orfana trovata.",
"enterClassName": "Inserisci un nome classe.",
"deleteClassTitle": "Elimina classe",
"deleteClassConfirm": "Vuoi davvero eliminare la classe \"{name}\"?",
"deleteClassParticipantsDetached": "Tutti i partecipanti saranno scollegati da questa classe.",
"enterExternalParticipantName": "Inserisci almeno nome e cognome.",
"noTournamentDate": "Nessuna data torneo disponibile.",
"noValidTrainingParticipants": "Nessun partecipante valido trovato nella sessione di allenamento per questa data.",
"noTrainingParticipants": "Nessun partecipante trovato nella sessione di allenamento per questa data.",
"noTrainingOnDate": "Nessuna sessione di allenamento trovata per {date}.",
"selectTwoDifferentPlayers": "Seleziona due giocatori diversi.",
"pairingAlreadyExists": "Questa coppia esiste già.",
"participantNotFound": "Partecipante non trovato.",
"minimumParticipantsForPairings": "Sono necessari almeno 2 partecipanti per le coppie.",
"deleteExistingPairingsConfirm": "Le coppie esistenti verranno eliminate. Continuare?",
"pairingsCreatedWithErrors": "{successCount} coppie create, {errorCount} errori.",
"participantConflicts": "Conflitti",
"unpairedDoubles": "Doppi senza partner",
"noEligibleClass": "Nessuna classe adatta trovata.",
"eligibleClassesHint": "Adatto per: {classes}",
"warningClassMissing": "Classe non trovata",
"warningGenderMismatch": "Il genere non corrisponde alla classe",
"warningGenderMissing": "Manca il genere per questa classe",
"warningBirthDateMissing": "Manca la data di nascita per questa classe",
"warningTooOldForClass": "Troppo grande per questa classe",
"warningTooYoungForClass": "Troppo giovane per questa classe",
"warningMissingPairing": "Manca il partner di doppio",
"autoAssignableParticipantsHint": "{count} of them can be assigned automatically.",
"assignmentReviewTitle": "{count} participants need a choice:",
"conflictSuggestionLabel": "Suggestions:",
"workspaceProblemsTitle": "{count} open issues",
"problemConfigTitle": "Configuration incomplete",
"problemConfigDescription": "Check date, name, winning sets and at least one class.",
"problemUnassignedTitle": "{count} participants without class",
"problemUnassignedDescription": "These participants still need a manual class assignment.",
"problemUnassignedAutoDescription": "{count} of them can be assigned automatically right away.",
"problemConflictsTitle": "{count} participants with conflicts",
"problemConflictsDescription": "Review invalid classes, missing data or unclear assignments.",
"problemDoublesTitle": "{count} open doubles partners",
"problemDoublesDescription": "One or more doubles classes still need partner assignments.",
"problemDoublesAutoDescription": "The open doubles class can be paired automatically right away.",
"problemGroupsMissingTitle": "Groups not created yet",
"problemGroupsMissingDescription": "The group tournament needs groups to be created first.",
"problemGroupMatchesTitle": "Group matches not generated yet",
"problemGroupMatchesDescription": "The groups are ready, but the matches have not been created yet.",
"problemKnockoutReadyTitle": "Knockout can be started",
"problemKnockoutReadyDescription": "The group stage is complete and the final round can be generated now.",
"autoAssignEligible": "Assign automatically",
"autoAssignEligibleNone": "There are currently no participants with a single automatic class assignment.",
"autoAssignEligibleDone": "{count} participants were assigned automatically.",
"autoAssignEligiblePartial": "{successCount} participants were assigned automatically, {errorCount} failed.",
"doublesPairingHint": "{count} participants in this doubles class still need a partner.",
"createSuggestedPairings": "Create pairings",
"statusUnpairedDoubles": "{count} doubles without partner",
"statusActionPair": "Pair",
"stageConfigCurrentSetup": "Current structure",
"stageFlowWithIntermediate": "Preliminary round -> intermediate round ({stage2}) -> final round ({final})",
"stageFlowDirectFinal": "Preliminary round -> final round ({final})",
"stageStep1": "Step 1",
"stageStep2": "Step 2",
"stageStep3": "Step 3",
"stageStep1Title": "Decide on an intermediate round",
"stageStep1Description": "First decide whether there should be an additional round after the preliminary round.",
"stageStep2Description": "Define what the intermediate round should look like and how many groups it needs.",
"stageFinalDescription": "Decide whether the final round should be played as groups or directly as a knockout bracket.",
"promotionRuleDescription12": "Define which places from each preliminary group advance to the intermediate round.",
"promotionRuleDescriptionFinalKnockout": "Define which places from the previous round advance to the knockout bracket.",
"promotionRuleDescriptionFinalGroups": "Define which places from the previous round advance to the final-round groups.",
"poolRuleLabel": "Rule {number}",
"poolRuleSummary": "Places {places} go to {target}",
"poolRulePlacesLabel": "Which places advance?",
"poolRulePlacesHelp": "Example: 1 or 1,2",
"poolRuleTargetLabel": "Where do these places go?",
"poolRuleQuickExamples": "Quick examples:",
"targetGroupsLabel": "{count} groups",
"transferQualifiedToFinalFromPreliminary": "Move qualified players straight into the final round",
"transferQualifiedToIntermediate": "Move qualified players into the intermediate round",
"transferQualifiedToFinalFromIntermediate": "Move qualified players into the final round",
"stageParticipantsTransferred": "Qualified players were moved into {round}.",
"transferQualifiedToFinalFromPreliminaryDesc": "Uses the preliminary-round to final-round rules and creates the qualified players directly there.",
"transferQualifiedToIntermediateDesc": "Uses the preliminary-round to intermediate-round rules and moves the qualified players across.",
"transferQualifiedToFinalFromIntermediateDesc": "Uses the intermediate-round to final-round rules and moves the qualified players across.",
"stageActionsTitle": "Next actions",
"stageActionsDescription": "These steps move qualified players into the next round.",
"stageActionRun": "Run now",
"stageActionMissingRulesHint": "Create at least one advancement rule first before moving players into the next round.",
"poolRulePreview": "From each group, places {places} advance to {target}.",
"poolRuleTargetKnockout": "the knockout bracket",
"poolRuleTargetGroupsDetailed": "{count} target groups",
"stageValidationTitle": "Review configuration",
"stageValidationDescription": "Fix these points before saving or moving players into the next round.",
"stageValidationGroupCountRequired": "Please set at least one valid group count.",
"stageValidationNoRules": "There is no advancement rule yet for {stage}.",
"stageValidationRulePlacesRequired": "Enter at least one place that should advance.",
"stageValidationRulePlacesInvalid": "The place definition is invalid. Only positive whole numbers such as 1 or 1,2 are allowed.",
"stageValidationRulePlacesDuplicate": "A single rule must not contain the same place more than once.",
"stageValidationRulesOverlap": "Place {place} is assigned twice, in rule {first} and rule {second}.",
"stageValidationTargetGroupsRequired": "Please enter a valid number of target groups.",
"stageValidationKnockoutTargetOnly": "For a knockout final round, only the knockout bracket is allowed as the target here.",
"stageValidationSaveBlocked": "Please fix the highlighted configuration errors first.",
"stageValidationSuggestion": "Suggestion: {value}",
"stageValidationRulePlacesGap": "The place definition has gaps. Usually a continuous order such as 1,2 or 1,2,3 is intended.",
"stageValidationTargetGroupCountMismatch": "The number of target groups must match the target round. Expected: {expected}.",
"stageValidationTargetTypeMismatch": "The target of this rule must match the target round. Expected: {target}.",
"stageValidationApplyFix": "Apply",
"stageValidationApplyTargetTypeFix": "Switch to {target}",
"stageValidationApplyTargetGroupFix": "Set to {count} target groups",
"ruleStatusOk": "Looks good",
"ruleStatusReview": "Review",
"ruleStatusBlocked": "Blocked",
"ruleStatusOkDescription": "This rule matches the current target round and can be used as-is.",
"stageFlowHealthOk": "Round logic looks good",
"stageFlowHealthOkDescription": "The transitions between rounds are fully configured and currently coherent.",
"stageFlowHealthIncomplete": "Round logic is incomplete",
"stageFlowHealthMissingRules": "At least one transition does not yet have a complete advancement rule.",
"stageFlowHealthReviewDescription": "The round logic is basically usable, but should still be reviewed because of open hints.",
"stageFlowHealthBlocked": "Round logic is contradictory",
"stageFlowHealthBlockedDescription": "At least one rule currently does not match the target round or contains blocking configuration errors.",
"stageFlowBreakdownOk": "Configuration looks good",
"stageFlowBreakdownWarnings": "{count} warning(s) open",
"stageFlowBreakdownErrors": "{count} blocking error(s)",
"stageFlowBreakdownMissingRule": "No complete rule has been added yet",
"stageFlowBreakdownActionAddRule": "Add rule",
"stageFlowBreakdownActionReview": "Review issues",
"stageFlowBreakdownActionOpen": "Open rule",
"stageValidationApplyAllFixes": "Apply {count} quick fix(es)",
"stageFlowRecommendationTitle": "Recommended next step",
"stageFlowRecommendationFixes": "The typical configuration problems can be cleaned up automatically right away.",
"stageFlowRecommendationMissingRule": "A complete rule is still missing for {label}. That is the best place to continue.",
"stageFlowRecommendationError": "Review the first blocking error in the round logic first.",
"stageFlowRecommendationWarnings": "There are still warnings that should be reviewed before saving.",
"stageFlowRecommendationSave": "The round logic is coherent. You can save the configuration now.",
"stageFlowPreviewMissing": "No rule has been configured yet.",
"stageFlowPreviewRule": "Places {places} go to {target}",
"stageValidationKnockoutNeedsTwoPlayers": "A knockout bracket needs at least 2 qualified players.",
"stageValidationKnockoutByesLikely": "With an expected {count} qualifiers, the knockout bracket will very likely include byes.",
"stageValidationNotEnoughQualifiersForGroups": "With only {count} qualifiers for {groups} target groups, empty groups would be created.",
"stageValidationThinGroupsLikely": "With {count} qualifiers across {groups} target groups, the groups will likely be very small.",
"stageFlowPreviewRuleWithCount": "{base} (expected qualifiers: {count})",
"stageFlowReadinessNoGroups": "No matching groups exist yet",
"stageFlowReadinessIncompleteGroups": "{count} group(s) are not fully decided yet",
"stageFlowReadinessReady": "Transition is ready to start",
"stageFlowQualifiedPreviewTitle": "Currently qualified",
"stageFlowQualifiedPreviewEntry": "G{group} · Place {position} · {name}"
}
}

View File

@@ -6,6 +6,7 @@
"common": {
"loading": "読み込み中...",
"save": "保存",
"saved": "保存済み",
"cancel": "キャンセル",
"delete": "削除",
"edit": "編集",
@@ -30,6 +31,8 @@
"approvals": "承認",
"statistics": "トレーニング統計",
"tournaments": "トーナメント",
"clubTournaments": "クラブ大会",
"tournamentParticipations": "大会参加",
"schedule": "スケジュール",
"clubSettings": "クラブ設定",
"predefinedActivities": "事前定義された活動",
@@ -120,11 +123,16 @@
"tournaments": {
"numberOfTables": "卓数",
"table": "卓",
"playerOne": "選手 1",
"playerTwo": "選手 2",
"groupsLabel": "グループ",
"knockoutLabel": "KO",
"distributeTables": "空き卓を割り当て",
"distributeTablesResult": "卓の割り当て",
"noFreeTables": "空き卓がありません。",
"noAssignableMatches": "両選手が空いている試合がありません。",
"tablesDistributed": "卓が割り当てられました。",
"errorDistributingTables": "卓の割り当て中にエラーが発生しました。",
"missingDataPDF": "不足データをPDFで",
"missingDataPDFTitle": "参加者の不足データ ミニ選手権",
"missingDataPDFSubtitle": "不足しているデータ____で表示を参加者から収集し、ここに記入してください。",
@@ -137,7 +145,189 @@
"phone": "電話",
"generatingPDF": "PDF作成中...",
"page": "ページ",
"dataNotRecorded": "未登録"
"statusOpen": "未開始",
"statusLive": "進行中",
"statusFinished": "終了",
"dataNotRecorded": "未登録",
"resultsRanking": "順位",
"newSetPlaceholder": "新しいセット 例: 11:7",
"finishMatch": "終了",
"correctMatch": "修正",
"markMatchLive": "進行中としてマーク",
"unmarkMatchLive": "進行中マークを解除",
"stageConfigTitle": "中間ラウンドと決勝ラウンド",
"stageConfigLoading": "ラウンド設定を読み込み中…",
"stageConfigIntro": "中間ラウンドは任意です。有効にすると、その後に必ず決勝ラウンドがあります。ノックアウト決勝ラウンドは単一ブラケットとして作成されます。",
"useIntermediateStage": "中間ラウンドを使用",
"intermediateRound": "中間ラウンドラウンド2",
"finalRound": "決勝ラウンド",
"playThirdPlace": "3位決定戦を行う",
"roundMode": "モード",
"roundGroupCount": "グループ数",
"promotionRule12": "進出: 予選 → 中間ラウンド (1→2)",
"promotionRuleFinal": "進出: ラウンド {from} → ラウンド {to}",
"addPoolRule": "プールルールを追加",
"noPoolRulesYet12": "まだルールがありません。例: 1位と2位 -> ラウンド2上位グループ。",
"noPoolRulesYetFinal": "まだルールがありません。例: 1位と2位 -> 決勝ラウンド。",
"placesFromEachGroup": "各グループからの順位(例: 1,2",
"target": "対象",
"targetGroupCount": "対象グループ数",
"saveRounds": "ラウンドを保存",
"createFinalFromPreliminary": "予選から決勝ラウンドを作成",
"createIntermediateFromPreliminary": "予選から中間ラウンドを作成",
"createFinalFromIntermediate": "中間ラウンドから決勝ラウンドを作成",
"stageConfigMissingIds": "保存できません: クラブIDまたは大会IDがありません。",
"stageConfigLoadError": "ラウンド設定の読み込みエラー。",
"stageConfigBadServerResponse": "サーバー応答が無効です。",
"stageCreated": "ラウンド {round} を作成しました。",
"atLeastOnePoolRule": "{label} に対して少なくとも1つのプールルールを追加してください例: 1,2位。",
"enterMiniLocation": "場所を入力してください。",
"selectTournamentFirst": "先に大会を選択してください。",
"errorGeneratingPdf": "PDF生成エラー。",
"orphanedMatchesRemoved": "孤立した試合を {count} 件削除しました。",
"noOrphanedMatchesFound": "孤立した試合は見つかりませんでした。",
"enterClassName": "クラス名を入力してください。",
"deleteClassTitle": "クラスを削除",
"deleteClassConfirm": "クラス「{name}」を本当に削除しますか?",
"deleteClassParticipantsDetached": "すべての参加者がこのクラスから外されます。",
"enterExternalParticipantName": "少なくとも名と姓を入力してください。",
"noTournamentDate": "大会日がありません。",
"noValidTrainingParticipants": "この日付の練習セッションに有効な参加者が見つかりませんでした。",
"noTrainingParticipants": "この日付の練習セッションに参加者が見つかりませんでした。",
"noTrainingOnDate": "{date} の練習セッションが見つかりませんでした。",
"selectTwoDifferentPlayers": "異なる2人の選手を選択してください。",
"pairingAlreadyExists": "このペアは既に存在します。",
"participantNotFound": "参加者が見つかりません。",
"minimumParticipantsForPairings": "ペアリングには少なくとも2人の参加者が必要です。",
"deleteExistingPairingsConfirm": "既存のペアリングは削除されます。続行しますか?",
"pairingsCreatedWithErrors": "{successCount} 件のペアリングを作成、{errorCount} 件のエラー。",
"participantConflicts": "競合",
"unpairedDoubles": "ペアのいないダブルス",
"noEligibleClass": "適切なクラスが見つかりません。",
"eligibleClassesHint": "参加可能: {classes}",
"warningClassMissing": "クラスが見つかりません",
"warningGenderMismatch": "性別がクラス条件と一致しません",
"warningGenderMissing": "このクラスには性別情報が必要です",
"warningBirthDateMissing": "このクラスには生年月日が必要です",
"warningTooOldForClass": "このクラスには年齢が高すぎます",
"warningTooYoungForClass": "このクラスには年齢が低すぎます",
"warningMissingPairing": "ダブルスのパートナーがいません",
"autoAssignableParticipantsHint": "{count} of them can be assigned automatically.",
"assignmentReviewTitle": "{count} participants need a choice:",
"conflictSuggestionLabel": "Suggestions:",
"workspaceProblemsTitle": "{count} open issues",
"problemConfigTitle": "Configuration incomplete",
"problemConfigDescription": "Check date, name, winning sets and at least one class.",
"problemUnassignedTitle": "{count} participants without class",
"problemUnassignedDescription": "These participants still need a manual class assignment.",
"problemUnassignedAutoDescription": "{count} of them can be assigned automatically right away.",
"problemConflictsTitle": "{count} participants with conflicts",
"problemConflictsDescription": "Review invalid classes, missing data or unclear assignments.",
"problemDoublesTitle": "{count} open doubles partners",
"problemDoublesDescription": "One or more doubles classes still need partner assignments.",
"problemDoublesAutoDescription": "The open doubles class can be paired automatically right away.",
"problemGroupsMissingTitle": "Groups not created yet",
"problemGroupsMissingDescription": "The group tournament needs groups to be created first.",
"problemGroupMatchesTitle": "Group matches not generated yet",
"problemGroupMatchesDescription": "The groups are ready, but the matches have not been created yet.",
"problemKnockoutReadyTitle": "Knockout can be started",
"problemKnockoutReadyDescription": "The group stage is complete and the final round can be generated now.",
"autoAssignEligible": "Assign automatically",
"autoAssignEligibleNone": "There are currently no participants with a single automatic class assignment.",
"autoAssignEligibleDone": "{count} participants were assigned automatically.",
"autoAssignEligiblePartial": "{successCount} participants were assigned automatically, {errorCount} failed.",
"doublesPairingHint": "{count} participants in this doubles class still need a partner.",
"createSuggestedPairings": "Create pairings",
"statusUnpairedDoubles": "{count} doubles without partner",
"statusActionPair": "Pair",
"stageConfigCurrentSetup": "Current structure",
"stageFlowWithIntermediate": "Preliminary round -> intermediate round ({stage2}) -> final round ({final})",
"stageFlowDirectFinal": "Preliminary round -> final round ({final})",
"stageStep1": "Step 1",
"stageStep2": "Step 2",
"stageStep3": "Step 3",
"stageStep1Title": "Decide on an intermediate round",
"stageStep1Description": "First decide whether there should be an additional round after the preliminary round.",
"stageStep2Description": "Define what the intermediate round should look like and how many groups it needs.",
"stageFinalDescription": "Decide whether the final round should be played as groups or directly as a knockout bracket.",
"promotionRuleDescription12": "Define which places from each preliminary group advance to the intermediate round.",
"promotionRuleDescriptionFinalKnockout": "Define which places from the previous round advance to the knockout bracket.",
"promotionRuleDescriptionFinalGroups": "Define which places from the previous round advance to the final-round groups.",
"poolRuleLabel": "Rule {number}",
"poolRuleSummary": "Places {places} go to {target}",
"poolRulePlacesLabel": "Which places advance?",
"poolRulePlacesHelp": "Example: 1 or 1,2",
"poolRuleTargetLabel": "Where do these places go?",
"poolRuleQuickExamples": "Quick examples:",
"targetGroupsLabel": "{count} groups",
"transferQualifiedToFinalFromPreliminary": "Move qualified players straight into the final round",
"transferQualifiedToIntermediate": "Move qualified players into the intermediate round",
"transferQualifiedToFinalFromIntermediate": "Move qualified players into the final round",
"stageParticipantsTransferred": "Qualified players were moved into {round}.",
"transferQualifiedToFinalFromPreliminaryDesc": "Uses the preliminary-round to final-round rules and creates the qualified players directly there.",
"transferQualifiedToIntermediateDesc": "Uses the preliminary-round to intermediate-round rules and moves the qualified players across.",
"transferQualifiedToFinalFromIntermediateDesc": "Uses the intermediate-round to final-round rules and moves the qualified players across.",
"stageActionsTitle": "Next actions",
"stageActionsDescription": "These steps move qualified players into the next round.",
"stageActionRun": "Run now",
"stageActionMissingRulesHint": "Create at least one advancement rule first before moving players into the next round.",
"poolRulePreview": "From each group, places {places} advance to {target}.",
"poolRuleTargetKnockout": "the knockout bracket",
"poolRuleTargetGroupsDetailed": "{count} target groups",
"stageValidationTitle": "Review configuration",
"stageValidationDescription": "Fix these points before saving or moving players into the next round.",
"stageValidationGroupCountRequired": "Please set at least one valid group count.",
"stageValidationNoRules": "There is no advancement rule yet for {stage}.",
"stageValidationRulePlacesRequired": "Enter at least one place that should advance.",
"stageValidationRulePlacesInvalid": "The place definition is invalid. Only positive whole numbers such as 1 or 1,2 are allowed.",
"stageValidationRulePlacesDuplicate": "A single rule must not contain the same place more than once.",
"stageValidationRulesOverlap": "Place {place} is assigned twice, in rule {first} and rule {second}.",
"stageValidationTargetGroupsRequired": "Please enter a valid number of target groups.",
"stageValidationKnockoutTargetOnly": "For a knockout final round, only the knockout bracket is allowed as the target here.",
"stageValidationSaveBlocked": "Please fix the highlighted configuration errors first.",
"stageValidationSuggestion": "Suggestion: {value}",
"stageValidationRulePlacesGap": "The place definition has gaps. Usually a continuous order such as 1,2 or 1,2,3 is intended.",
"stageValidationTargetGroupCountMismatch": "The number of target groups must match the target round. Expected: {expected}.",
"stageValidationTargetTypeMismatch": "The target of this rule must match the target round. Expected: {target}.",
"stageValidationApplyFix": "Apply",
"stageValidationApplyTargetTypeFix": "Switch to {target}",
"stageValidationApplyTargetGroupFix": "Set to {count} target groups",
"ruleStatusOk": "Looks good",
"ruleStatusReview": "Review",
"ruleStatusBlocked": "Blocked",
"ruleStatusOkDescription": "This rule matches the current target round and can be used as-is.",
"stageFlowHealthOk": "Round logic looks good",
"stageFlowHealthOkDescription": "The transitions between rounds are fully configured and currently coherent.",
"stageFlowHealthIncomplete": "Round logic is incomplete",
"stageFlowHealthMissingRules": "At least one transition does not yet have a complete advancement rule.",
"stageFlowHealthReviewDescription": "The round logic is basically usable, but should still be reviewed because of open hints.",
"stageFlowHealthBlocked": "Round logic is contradictory",
"stageFlowHealthBlockedDescription": "At least one rule currently does not match the target round or contains blocking configuration errors.",
"stageFlowBreakdownOk": "Configuration looks good",
"stageFlowBreakdownWarnings": "{count} warning(s) open",
"stageFlowBreakdownErrors": "{count} blocking error(s)",
"stageFlowBreakdownMissingRule": "No complete rule has been added yet",
"stageFlowBreakdownActionAddRule": "Add rule",
"stageFlowBreakdownActionReview": "Review issues",
"stageFlowBreakdownActionOpen": "Open rule",
"stageValidationApplyAllFixes": "Apply {count} quick fix(es)",
"stageFlowRecommendationTitle": "Recommended next step",
"stageFlowRecommendationFixes": "The typical configuration problems can be cleaned up automatically right away.",
"stageFlowRecommendationMissingRule": "A complete rule is still missing for {label}. That is the best place to continue.",
"stageFlowRecommendationError": "Review the first blocking error in the round logic first.",
"stageFlowRecommendationWarnings": "There are still warnings that should be reviewed before saving.",
"stageFlowRecommendationSave": "The round logic is coherent. You can save the configuration now.",
"stageFlowPreviewMissing": "No rule has been configured yet.",
"stageFlowPreviewRule": "Places {places} go to {target}",
"stageValidationKnockoutNeedsTwoPlayers": "A knockout bracket needs at least 2 qualified players.",
"stageValidationKnockoutByesLikely": "With an expected {count} qualifiers, the knockout bracket will very likely include byes.",
"stageValidationNotEnoughQualifiersForGroups": "With only {count} qualifiers for {groups} target groups, empty groups would be created.",
"stageValidationThinGroupsLikely": "With {count} qualifiers across {groups} target groups, the groups will likely be very small.",
"stageFlowPreviewRuleWithCount": "{base} (expected qualifiers: {count})",
"stageFlowReadinessNoGroups": "No matching groups exist yet",
"stageFlowReadinessIncompleteGroups": "{count} group(s) are not fully decided yet",
"stageFlowReadinessReady": "Transition is ready to start",
"stageFlowQualifiedPreviewTitle": "Currently qualified",
"stageFlowQualifiedPreviewEntry": "G{group} · Place {position} · {name}"
}
}

View File

@@ -6,6 +6,7 @@
"common": {
"loading": "Ładowanie...",
"save": "Zapisz",
"saved": "Zapisano",
"cancel": "Anuluj",
"delete": "Usuń",
"edit": "Edytuj",
@@ -30,6 +31,8 @@
"approvals": "Zatwierdzenia",
"statistics": "Statystyki treningowe",
"tournaments": "Turnieje",
"clubTournaments": "Turnieje klubowe",
"tournamentParticipations": "Udziały w turniejach",
"schedule": "Harmonogramy",
"clubSettings": "Ustawienia klubu",
"predefinedActivities": "Predefiniowane aktywności",
@@ -120,11 +123,16 @@
"tournaments": {
"numberOfTables": "Liczba stołów",
"table": "Stół",
"playerOne": "Zawodnik 1",
"playerTwo": "Zawodnik 2",
"groupsLabel": "Grupy",
"knockoutLabel": "KO",
"distributeTables": "Rozdziel wolne stoły",
"distributeTablesResult": "Podział stołów",
"noFreeTables": "Brak wolnych stołów.",
"noAssignableMatches": "Brak meczów, w których obaj gracze są wolni.",
"tablesDistributed": "Stoły zostały rozdzielone.",
"errorDistributingTables": "Błąd podczas rozdzielania stołów.",
"missingDataPDF": "Brakujące dane jako PDF",
"missingDataPDFTitle": "Brakujące dane uczestników Mini mistrzostwa",
"missingDataPDFSubtitle": "Proszę zebrać brakujące dane (oznaczone ____) od uczestników i zanotować je tutaj.",
@@ -137,7 +145,189 @@
"phone": "Telefon",
"generatingPDF": "Generowanie PDF...",
"page": "Strona",
"dataNotRecorded": "Jeszcze nie wprowadzono"
"statusOpen": "Otwarte",
"statusLive": "Na żywo",
"statusFinished": "Zakończone",
"dataNotRecorded": "Jeszcze nie wprowadzono",
"resultsRanking": "Ranking",
"newSetPlaceholder": "Nowy set, np. 11:7",
"finishMatch": "Zakończ",
"correctMatch": "Popraw",
"markMatchLive": "Oznacz jako na żywo",
"unmarkMatchLive": "Usuń oznaczenie na żywo",
"stageConfigTitle": "Runda pośrednia i finałowa",
"stageConfigLoading": "Ładowanie konfiguracji rund…",
"stageConfigIntro": "Runda pośrednia jest opcjonalna. Jeśli ją włączysz, później zawsze nastąpi runda finałowa. Finałowa runda pucharowa jest tworzona jako pojedyncza drabinka.",
"useIntermediateStage": "Użyj rundy pośredniej",
"intermediateRound": "Runda pośrednia (runda 2)",
"finalRound": "Runda finałowa",
"playThirdPlace": "Rozegraj mecz o trzecie miejsce",
"roundMode": "Tryb",
"roundGroupCount": "Liczba grup",
"promotionRule12": "Awans: runda wstępna → runda pośrednia (1→2)",
"promotionRuleFinal": "Awans: runda {from} → runda {to}",
"addPoolRule": "Dodaj regułę puli",
"noPoolRulesYet12": "Brak reguł. Przykład: miejsca 1 i 2 -> górne grupy rundy 2.",
"noPoolRulesYetFinal": "Brak reguł. Przykład: miejsca 1 i 2 -> runda finałowa.",
"placesFromEachGroup": "Miejsca z każdej grupy (np. 1,2)",
"target": "Cel",
"targetGroupCount": "Docelowa liczba grup",
"saveRounds": "Zapisz rundy",
"createFinalFromPreliminary": "Utwórz rundę finałową z rundy wstępnej",
"createIntermediateFromPreliminary": "Utwórz rundę pośrednią z rundy wstępnej",
"createFinalFromIntermediate": "Utwórz rundę finałową z rundy pośredniej",
"stageConfigMissingIds": "Nie można zapisać: brak identyfikatora klubu lub turnieju.",
"stageConfigLoadError": "Błąd podczas ładowania konfiguracji rund.",
"stageConfigBadServerResponse": "Nieprawidłowa odpowiedź serwera.",
"stageCreated": "Utworzono rundę {round}.",
"atLeastOnePoolRule": "Dodaj co najmniej jedną regułę puli dla {label} (np. miejsca 1,2).",
"enterMiniLocation": "Wprowadź lokalizację.",
"selectTournamentFirst": "Najpierw wybierz turniej.",
"errorGeneratingPdf": "Błąd podczas generowania PDF.",
"orphanedMatchesRemoved": "Usunięto {count} osieroconych meczów.",
"noOrphanedMatchesFound": "Nie znaleziono osieroconych meczów.",
"enterClassName": "Wprowadź nazwę klasy.",
"deleteClassTitle": "Usuń klasę",
"deleteClassConfirm": "Czy na pewno chcesz usunąć klasę „{name}”?",
"deleteClassParticipantsDetached": "Wszyscy uczestnicy zostaną odłączeni od tej klasy.",
"enterExternalParticipantName": "Wprowadź co najmniej imię i nazwisko.",
"noTournamentDate": "Brak daty turnieju.",
"noValidTrainingParticipants": "Nie znaleziono prawidłowych uczestników treningu dla tej daty.",
"noTrainingParticipants": "Nie znaleziono uczestników treningu dla tej daty.",
"noTrainingOnDate": "Nie znaleziono treningu dla {date}.",
"selectTwoDifferentPlayers": "Wybierz dwóch różnych zawodników.",
"pairingAlreadyExists": "Ta para już istnieje.",
"participantNotFound": "Nie znaleziono uczestnika.",
"minimumParticipantsForPairings": "Do parowań wymaganych jest co najmniej 2 uczestników.",
"deleteExistingPairingsConfirm": "Istniejące pary zostaną usunięte. Kontynuować?",
"pairingsCreatedWithErrors": "Utworzono {successCount} par, błędy: {errorCount}.",
"participantConflicts": "Konflikty",
"unpairedDoubles": "Debel bez partnera",
"noEligibleClass": "Nie znaleziono odpowiedniej klasy.",
"eligibleClassesHint": "Pasuje do: {classes}",
"warningClassMissing": "Nie znaleziono klasy",
"warningGenderMismatch": "Płeć nie pasuje do klasy",
"warningGenderMissing": "Brakuje płci dla tej klasy",
"warningBirthDateMissing": "Brakuje daty urodzenia dla tej klasy",
"warningTooOldForClass": "Za stary do tej klasy",
"warningTooYoungForClass": "Za młody do tej klasy",
"warningMissingPairing": "Brakuje partnera do debla",
"autoAssignableParticipantsHint": "{count} of them can be assigned automatically.",
"assignmentReviewTitle": "{count} participants need a choice:",
"conflictSuggestionLabel": "Suggestions:",
"workspaceProblemsTitle": "{count} open issues",
"problemConfigTitle": "Configuration incomplete",
"problemConfigDescription": "Check date, name, winning sets and at least one class.",
"problemUnassignedTitle": "{count} participants without class",
"problemUnassignedDescription": "These participants still need a manual class assignment.",
"problemUnassignedAutoDescription": "{count} of them can be assigned automatically right away.",
"problemConflictsTitle": "{count} participants with conflicts",
"problemConflictsDescription": "Review invalid classes, missing data or unclear assignments.",
"problemDoublesTitle": "{count} open doubles partners",
"problemDoublesDescription": "One or more doubles classes still need partner assignments.",
"problemDoublesAutoDescription": "The open doubles class can be paired automatically right away.",
"problemGroupsMissingTitle": "Groups not created yet",
"problemGroupsMissingDescription": "The group tournament needs groups to be created first.",
"problemGroupMatchesTitle": "Group matches not generated yet",
"problemGroupMatchesDescription": "The groups are ready, but the matches have not been created yet.",
"problemKnockoutReadyTitle": "Knockout can be started",
"problemKnockoutReadyDescription": "The group stage is complete and the final round can be generated now.",
"autoAssignEligible": "Assign automatically",
"autoAssignEligibleNone": "There are currently no participants with a single automatic class assignment.",
"autoAssignEligibleDone": "{count} participants were assigned automatically.",
"autoAssignEligiblePartial": "{successCount} participants were assigned automatically, {errorCount} failed.",
"doublesPairingHint": "{count} participants in this doubles class still need a partner.",
"createSuggestedPairings": "Create pairings",
"statusUnpairedDoubles": "{count} doubles without partner",
"statusActionPair": "Pair",
"stageConfigCurrentSetup": "Current structure",
"stageFlowWithIntermediate": "Preliminary round -> intermediate round ({stage2}) -> final round ({final})",
"stageFlowDirectFinal": "Preliminary round -> final round ({final})",
"stageStep1": "Step 1",
"stageStep2": "Step 2",
"stageStep3": "Step 3",
"stageStep1Title": "Decide on an intermediate round",
"stageStep1Description": "First decide whether there should be an additional round after the preliminary round.",
"stageStep2Description": "Define what the intermediate round should look like and how many groups it needs.",
"stageFinalDescription": "Decide whether the final round should be played as groups or directly as a knockout bracket.",
"promotionRuleDescription12": "Define which places from each preliminary group advance to the intermediate round.",
"promotionRuleDescriptionFinalKnockout": "Define which places from the previous round advance to the knockout bracket.",
"promotionRuleDescriptionFinalGroups": "Define which places from the previous round advance to the final-round groups.",
"poolRuleLabel": "Rule {number}",
"poolRuleSummary": "Places {places} go to {target}",
"poolRulePlacesLabel": "Which places advance?",
"poolRulePlacesHelp": "Example: 1 or 1,2",
"poolRuleTargetLabel": "Where do these places go?",
"poolRuleQuickExamples": "Quick examples:",
"targetGroupsLabel": "{count} groups",
"transferQualifiedToFinalFromPreliminary": "Move qualified players straight into the final round",
"transferQualifiedToIntermediate": "Move qualified players into the intermediate round",
"transferQualifiedToFinalFromIntermediate": "Move qualified players into the final round",
"stageParticipantsTransferred": "Qualified players were moved into {round}.",
"transferQualifiedToFinalFromPreliminaryDesc": "Uses the preliminary-round to final-round rules and creates the qualified players directly there.",
"transferQualifiedToIntermediateDesc": "Uses the preliminary-round to intermediate-round rules and moves the qualified players across.",
"transferQualifiedToFinalFromIntermediateDesc": "Uses the intermediate-round to final-round rules and moves the qualified players across.",
"stageActionsTitle": "Next actions",
"stageActionsDescription": "These steps move qualified players into the next round.",
"stageActionRun": "Run now",
"stageActionMissingRulesHint": "Create at least one advancement rule first before moving players into the next round.",
"poolRulePreview": "From each group, places {places} advance to {target}.",
"poolRuleTargetKnockout": "the knockout bracket",
"poolRuleTargetGroupsDetailed": "{count} target groups",
"stageValidationTitle": "Review configuration",
"stageValidationDescription": "Fix these points before saving or moving players into the next round.",
"stageValidationGroupCountRequired": "Please set at least one valid group count.",
"stageValidationNoRules": "There is no advancement rule yet for {stage}.",
"stageValidationRulePlacesRequired": "Enter at least one place that should advance.",
"stageValidationRulePlacesInvalid": "The place definition is invalid. Only positive whole numbers such as 1 or 1,2 are allowed.",
"stageValidationRulePlacesDuplicate": "A single rule must not contain the same place more than once.",
"stageValidationRulesOverlap": "Place {place} is assigned twice, in rule {first} and rule {second}.",
"stageValidationTargetGroupsRequired": "Please enter a valid number of target groups.",
"stageValidationKnockoutTargetOnly": "For a knockout final round, only the knockout bracket is allowed as the target here.",
"stageValidationSaveBlocked": "Please fix the highlighted configuration errors first.",
"stageValidationSuggestion": "Suggestion: {value}",
"stageValidationRulePlacesGap": "The place definition has gaps. Usually a continuous order such as 1,2 or 1,2,3 is intended.",
"stageValidationTargetGroupCountMismatch": "The number of target groups must match the target round. Expected: {expected}.",
"stageValidationTargetTypeMismatch": "The target of this rule must match the target round. Expected: {target}.",
"stageValidationApplyFix": "Apply",
"stageValidationApplyTargetTypeFix": "Switch to {target}",
"stageValidationApplyTargetGroupFix": "Set to {count} target groups",
"ruleStatusOk": "Looks good",
"ruleStatusReview": "Review",
"ruleStatusBlocked": "Blocked",
"ruleStatusOkDescription": "This rule matches the current target round and can be used as-is.",
"stageFlowHealthOk": "Round logic looks good",
"stageFlowHealthOkDescription": "The transitions between rounds are fully configured and currently coherent.",
"stageFlowHealthIncomplete": "Round logic is incomplete",
"stageFlowHealthMissingRules": "At least one transition does not yet have a complete advancement rule.",
"stageFlowHealthReviewDescription": "The round logic is basically usable, but should still be reviewed because of open hints.",
"stageFlowHealthBlocked": "Round logic is contradictory",
"stageFlowHealthBlockedDescription": "At least one rule currently does not match the target round or contains blocking configuration errors.",
"stageFlowBreakdownOk": "Configuration looks good",
"stageFlowBreakdownWarnings": "{count} warning(s) open",
"stageFlowBreakdownErrors": "{count} blocking error(s)",
"stageFlowBreakdownMissingRule": "No complete rule has been added yet",
"stageFlowBreakdownActionAddRule": "Add rule",
"stageFlowBreakdownActionReview": "Review issues",
"stageFlowBreakdownActionOpen": "Open rule",
"stageValidationApplyAllFixes": "Apply {count} quick fix(es)",
"stageFlowRecommendationTitle": "Recommended next step",
"stageFlowRecommendationFixes": "The typical configuration problems can be cleaned up automatically right away.",
"stageFlowRecommendationMissingRule": "A complete rule is still missing for {label}. That is the best place to continue.",
"stageFlowRecommendationError": "Review the first blocking error in the round logic first.",
"stageFlowRecommendationWarnings": "There are still warnings that should be reviewed before saving.",
"stageFlowRecommendationSave": "The round logic is coherent. You can save the configuration now.",
"stageFlowPreviewMissing": "No rule has been configured yet.",
"stageFlowPreviewRule": "Places {places} go to {target}",
"stageValidationKnockoutNeedsTwoPlayers": "A knockout bracket needs at least 2 qualified players.",
"stageValidationKnockoutByesLikely": "With an expected {count} qualifiers, the knockout bracket will very likely include byes.",
"stageValidationNotEnoughQualifiersForGroups": "With only {count} qualifiers for {groups} target groups, empty groups would be created.",
"stageValidationThinGroupsLikely": "With {count} qualifiers across {groups} target groups, the groups will likely be very small.",
"stageFlowPreviewRuleWithCount": "{base} (expected qualifiers: {count})",
"stageFlowReadinessNoGroups": "No matching groups exist yet",
"stageFlowReadinessIncompleteGroups": "{count} group(s) are not fully decided yet",
"stageFlowReadinessReady": "Transition is ready to start",
"stageFlowQualifiedPreviewTitle": "Currently qualified",
"stageFlowQualifiedPreviewEntry": "G{group} · Place {position} · {name}"
}
}

View File

@@ -6,6 +6,7 @@
"common": {
"loading": "กำลังโหลด...",
"save": "บันทึก",
"saved": "บันทึกแล้ว",
"cancel": "ยกเลิก",
"delete": "ลบ",
"edit": "แก้ไข",
@@ -30,6 +31,8 @@
"approvals": "การอนุมัติ",
"statistics": "สถิติการฝึกซ้อม",
"tournaments": "การแข่งขัน",
"clubTournaments": "การแข่งขันของสโมสร",
"tournamentParticipations": "การเข้าร่วมการแข่งขัน",
"schedule": "ตารางเวลา",
"clubSettings": "การตั้งค่าสโมสร",
"predefinedActivities": "กิจกรรมที่กำหนดไว้ล่วงหน้า",
@@ -120,11 +123,16 @@
"tournaments": {
"numberOfTables": "จำนวนโต๊ะ",
"table": "โต๊ะ",
"playerOne": "ผู้เล่น 1",
"playerTwo": "ผู้เล่น 2",
"groupsLabel": "กลุ่ม",
"knockoutLabel": "KO",
"distributeTables": "จัดสรรโต๊ะว่าง",
"distributeTablesResult": "การจัดสรรโต๊ะ",
"noFreeTables": "ไม่มีโต๊ะว่าง",
"noAssignableMatches": "ไม่มีแมตช์ที่ผู้เล่นทั้งสองว่าง",
"tablesDistributed": "จัดสรรโต๊ะเรียบร้อยแล้ว",
"errorDistributingTables": "เกิดข้อผิดพลาดในการจัดสรรโต๊ะ",
"missingDataPDF": "ข้อมูลที่ขาดหายเป็น PDF",
"missingDataPDFTitle": "ข้อมูลผู้เข้าร่วมที่ขาดหาย มินิแชมเปี้ยนชิพ",
"missingDataPDFSubtitle": "กรุณาเก็บข้อมูลที่ขาดหาย (ทำเครื่องหมาย ____) จากผู้เข้าร่วมและจดบันทึกที่นี่",
@@ -137,7 +145,189 @@
"phone": "โทรศัพท์",
"generatingPDF": "กำลังสร้าง PDF...",
"page": "หน้า",
"dataNotRecorded": "ยังไม่ได้บันทึก"
"statusOpen": "เปิด",
"statusLive": "กำลังแข่ง",
"statusFinished": "เสร็จแล้ว",
"dataNotRecorded": "ยังไม่ได้บันทึก",
"resultsRanking": "อันดับ",
"newSetPlaceholder": "เซตใหม่ เช่น 11:7",
"finishMatch": "จบการแข่งขัน",
"correctMatch": "แก้ไข",
"markMatchLive": "ทำเครื่องหมายว่ากำลังแข่ง",
"unmarkMatchLive": "ลบเครื่องหมายกำลังแข่ง",
"stageConfigTitle": "รอบกลางและรอบชิง",
"stageConfigLoading": "กำลังโหลดการตั้งค่ารอบ…",
"stageConfigIntro": "รอบกลางเป็นตัวเลือก หากเปิดใช้งาน จะมีรอบชิงตามมาเสมอ รอบชิงแบบน็อกเอาต์จะถูกสร้างเป็นสายเดียว",
"useIntermediateStage": "ใช้รอบกลาง",
"intermediateRound": "รอบกลาง (รอบ 2)",
"finalRound": "รอบชิง",
"playThirdPlace": "แข่งชิงอันดับสาม",
"roundMode": "โหมด",
"roundGroupCount": "จำนวนกลุ่ม",
"promotionRule12": "ผ่านเข้ารอบ: รอบคัดเลือก → รอบกลาง (1→2)",
"promotionRuleFinal": "ผ่านเข้ารอบ: รอบ {from} → รอบ {to}",
"addPoolRule": "เพิ่มกฎกลุ่ม",
"noPoolRulesYet12": "ยังไม่มีกฎ ตัวอย่าง: อันดับ 1 และ 2 -> กลุ่มบนของรอบ 2",
"noPoolRulesYetFinal": "ยังไม่มีกฎ ตัวอย่าง: อันดับ 1 และ 2 -> รอบชิง",
"placesFromEachGroup": "อันดับจากแต่ละกลุ่ม (เช่น 1,2)",
"target": "เป้าหมาย",
"targetGroupCount": "จำนวนกลุ่มปลายทาง",
"saveRounds": "บันทึกรอบ",
"createFinalFromPreliminary": "สร้างรอบชิงจากรอบคัดเลือก",
"createIntermediateFromPreliminary": "สร้างรอบกลางจากรอบคัดเลือก",
"createFinalFromIntermediate": "สร้างรอบชิงจากรอบกลาง",
"stageConfigMissingIds": "ไม่สามารถบันทึกได้: ไม่มีรหัสสโมสรหรือรหัสทัวร์นาเมนต์",
"stageConfigLoadError": "เกิดข้อผิดพลาดในการโหลดการตั้งค่ารอบ",
"stageConfigBadServerResponse": "การตอบกลับจากเซิร์ฟเวอร์ไม่ถูกต้อง",
"stageCreated": "สร้างรอบ {round} แล้ว",
"atLeastOnePoolRule": "กรุณาเพิ่มกฎกลุ่มอย่างน้อยหนึ่งกฎสำหรับ {label} (เช่น อันดับ 1,2)",
"enterMiniLocation": "กรุณากรอกสถานที่",
"selectTournamentFirst": "กรุณาเลือกทัวร์นาเมนต์ก่อน",
"errorGeneratingPdf": "เกิดข้อผิดพลาดในการสร้าง PDF",
"orphanedMatchesRemoved": "ลบแมตช์กำพร้า {count} รายการแล้ว",
"noOrphanedMatchesFound": "ไม่พบแมตช์กำพร้า",
"enterClassName": "กรุณากรอกชื่อคลาส",
"deleteClassTitle": "ลบคลาส",
"deleteClassConfirm": "คุณต้องการลบคลาส \"{name}\" จริงหรือไม่?",
"deleteClassParticipantsDetached": "ผู้เข้าร่วมทั้งหมดจะถูกเอาออกจากคลาสนี้",
"enterExternalParticipantName": "กรุณากรอกอย่างน้อยชื่อและนามสกุล",
"noTournamentDate": "ไม่มีวันที่ของทัวร์นาเมนต์",
"noValidTrainingParticipants": "ไม่พบผู้เข้าร่วมการฝึกที่ถูกต้องสำหรับวันที่นี้",
"noTrainingParticipants": "ไม่พบผู้เข้าร่วมการฝึกสำหรับวันที่นี้",
"noTrainingOnDate": "ไม่พบวันฝึกสำหรับ {date}",
"selectTwoDifferentPlayers": "กรุณาเลือกผู้เล่นสองคนที่แตกต่างกัน",
"pairingAlreadyExists": "คู่นี้มีอยู่แล้ว",
"participantNotFound": "ไม่พบผู้เข้าร่วม",
"minimumParticipantsForPairings": "ต้องมีผู้เข้าร่วมอย่างน้อย 2 คนสำหรับการจับคู่",
"deleteExistingPairingsConfirm": "คู่ที่มีอยู่จะถูกลบ ดำเนินการต่อหรือไม่?",
"pairingsCreatedWithErrors": "สร้างคู่แล้ว {successCount} คู่, มีข้อผิดพลาด {errorCount} รายการ",
"participantConflicts": "ความขัดแย้ง",
"unpairedDoubles": "ประเภทคู่ที่ยังไม่มีคู่",
"noEligibleClass": "ไม่พบคลาสที่เหมาะสม",
"eligibleClassesHint": "เหมาะกับ: {classes}",
"warningClassMissing": "ไม่พบคลาส",
"warningGenderMismatch": "เพศไม่ตรงกับคลาส",
"warningGenderMissing": "คลาสนี้ต้องมีข้อมูลเพศ",
"warningBirthDateMissing": "คลาสนี้ต้องมีวันเกิด",
"warningTooOldForClass": "อายุมากเกินไปสำหรับคลาสนี้",
"warningTooYoungForClass": "อายุน้อยเกินไปสำหรับคลาสนี้",
"warningMissingPairing": "ยังไม่มีคู่สำหรับประเภทคู่",
"autoAssignableParticipantsHint": "{count} of them can be assigned automatically.",
"assignmentReviewTitle": "{count} participants need a choice:",
"conflictSuggestionLabel": "Suggestions:",
"workspaceProblemsTitle": "{count} open issues",
"problemConfigTitle": "Configuration incomplete",
"problemConfigDescription": "Check date, name, winning sets and at least one class.",
"problemUnassignedTitle": "{count} participants without class",
"problemUnassignedDescription": "These participants still need a manual class assignment.",
"problemUnassignedAutoDescription": "{count} of them can be assigned automatically right away.",
"problemConflictsTitle": "{count} participants with conflicts",
"problemConflictsDescription": "Review invalid classes, missing data or unclear assignments.",
"problemDoublesTitle": "{count} open doubles partners",
"problemDoublesDescription": "One or more doubles classes still need partner assignments.",
"problemDoublesAutoDescription": "The open doubles class can be paired automatically right away.",
"problemGroupsMissingTitle": "Groups not created yet",
"problemGroupsMissingDescription": "The group tournament needs groups to be created first.",
"problemGroupMatchesTitle": "Group matches not generated yet",
"problemGroupMatchesDescription": "The groups are ready, but the matches have not been created yet.",
"problemKnockoutReadyTitle": "Knockout can be started",
"problemKnockoutReadyDescription": "The group stage is complete and the final round can be generated now.",
"autoAssignEligible": "Assign automatically",
"autoAssignEligibleNone": "There are currently no participants with a single automatic class assignment.",
"autoAssignEligibleDone": "{count} participants were assigned automatically.",
"autoAssignEligiblePartial": "{successCount} participants were assigned automatically, {errorCount} failed.",
"doublesPairingHint": "{count} participants in this doubles class still need a partner.",
"createSuggestedPairings": "Create pairings",
"statusUnpairedDoubles": "{count} doubles without partner",
"statusActionPair": "Pair",
"stageConfigCurrentSetup": "Current structure",
"stageFlowWithIntermediate": "Preliminary round -> intermediate round ({stage2}) -> final round ({final})",
"stageFlowDirectFinal": "Preliminary round -> final round ({final})",
"stageStep1": "Step 1",
"stageStep2": "Step 2",
"stageStep3": "Step 3",
"stageStep1Title": "Decide on an intermediate round",
"stageStep1Description": "First decide whether there should be an additional round after the preliminary round.",
"stageStep2Description": "Define what the intermediate round should look like and how many groups it needs.",
"stageFinalDescription": "Decide whether the final round should be played as groups or directly as a knockout bracket.",
"promotionRuleDescription12": "Define which places from each preliminary group advance to the intermediate round.",
"promotionRuleDescriptionFinalKnockout": "Define which places from the previous round advance to the knockout bracket.",
"promotionRuleDescriptionFinalGroups": "Define which places from the previous round advance to the final-round groups.",
"poolRuleLabel": "Rule {number}",
"poolRuleSummary": "Places {places} go to {target}",
"poolRulePlacesLabel": "Which places advance?",
"poolRulePlacesHelp": "Example: 1 or 1,2",
"poolRuleTargetLabel": "Where do these places go?",
"poolRuleQuickExamples": "Quick examples:",
"targetGroupsLabel": "{count} groups",
"transferQualifiedToFinalFromPreliminary": "Move qualified players straight into the final round",
"transferQualifiedToIntermediate": "Move qualified players into the intermediate round",
"transferQualifiedToFinalFromIntermediate": "Move qualified players into the final round",
"stageParticipantsTransferred": "Qualified players were moved into {round}.",
"transferQualifiedToFinalFromPreliminaryDesc": "Uses the preliminary-round to final-round rules and creates the qualified players directly there.",
"transferQualifiedToIntermediateDesc": "Uses the preliminary-round to intermediate-round rules and moves the qualified players across.",
"transferQualifiedToFinalFromIntermediateDesc": "Uses the intermediate-round to final-round rules and moves the qualified players across.",
"stageActionsTitle": "Next actions",
"stageActionsDescription": "These steps move qualified players into the next round.",
"stageActionRun": "Run now",
"stageActionMissingRulesHint": "Create at least one advancement rule first before moving players into the next round.",
"poolRulePreview": "From each group, places {places} advance to {target}.",
"poolRuleTargetKnockout": "the knockout bracket",
"poolRuleTargetGroupsDetailed": "{count} target groups",
"stageValidationTitle": "Review configuration",
"stageValidationDescription": "Fix these points before saving or moving players into the next round.",
"stageValidationGroupCountRequired": "Please set at least one valid group count.",
"stageValidationNoRules": "There is no advancement rule yet for {stage}.",
"stageValidationRulePlacesRequired": "Enter at least one place that should advance.",
"stageValidationRulePlacesInvalid": "The place definition is invalid. Only positive whole numbers such as 1 or 1,2 are allowed.",
"stageValidationRulePlacesDuplicate": "A single rule must not contain the same place more than once.",
"stageValidationRulesOverlap": "Place {place} is assigned twice, in rule {first} and rule {second}.",
"stageValidationTargetGroupsRequired": "Please enter a valid number of target groups.",
"stageValidationKnockoutTargetOnly": "For a knockout final round, only the knockout bracket is allowed as the target here.",
"stageValidationSaveBlocked": "Please fix the highlighted configuration errors first.",
"stageValidationSuggestion": "Suggestion: {value}",
"stageValidationRulePlacesGap": "The place definition has gaps. Usually a continuous order such as 1,2 or 1,2,3 is intended.",
"stageValidationTargetGroupCountMismatch": "The number of target groups must match the target round. Expected: {expected}.",
"stageValidationTargetTypeMismatch": "The target of this rule must match the target round. Expected: {target}.",
"stageValidationApplyFix": "Apply",
"stageValidationApplyTargetTypeFix": "Switch to {target}",
"stageValidationApplyTargetGroupFix": "Set to {count} target groups",
"ruleStatusOk": "Looks good",
"ruleStatusReview": "Review",
"ruleStatusBlocked": "Blocked",
"ruleStatusOkDescription": "This rule matches the current target round and can be used as-is.",
"stageFlowHealthOk": "Round logic looks good",
"stageFlowHealthOkDescription": "The transitions between rounds are fully configured and currently coherent.",
"stageFlowHealthIncomplete": "Round logic is incomplete",
"stageFlowHealthMissingRules": "At least one transition does not yet have a complete advancement rule.",
"stageFlowHealthReviewDescription": "The round logic is basically usable, but should still be reviewed because of open hints.",
"stageFlowHealthBlocked": "Round logic is contradictory",
"stageFlowHealthBlockedDescription": "At least one rule currently does not match the target round or contains blocking configuration errors.",
"stageFlowBreakdownOk": "Configuration looks good",
"stageFlowBreakdownWarnings": "{count} warning(s) open",
"stageFlowBreakdownErrors": "{count} blocking error(s)",
"stageFlowBreakdownMissingRule": "No complete rule has been added yet",
"stageFlowBreakdownActionAddRule": "Add rule",
"stageFlowBreakdownActionReview": "Review issues",
"stageFlowBreakdownActionOpen": "Open rule",
"stageValidationApplyAllFixes": "Apply {count} quick fix(es)",
"stageFlowRecommendationTitle": "Recommended next step",
"stageFlowRecommendationFixes": "The typical configuration problems can be cleaned up automatically right away.",
"stageFlowRecommendationMissingRule": "A complete rule is still missing for {label}. That is the best place to continue.",
"stageFlowRecommendationError": "Review the first blocking error in the round logic first.",
"stageFlowRecommendationWarnings": "There are still warnings that should be reviewed before saving.",
"stageFlowRecommendationSave": "The round logic is coherent. You can save the configuration now.",
"stageFlowPreviewMissing": "No rule has been configured yet.",
"stageFlowPreviewRule": "Places {places} go to {target}",
"stageValidationKnockoutNeedsTwoPlayers": "A knockout bracket needs at least 2 qualified players.",
"stageValidationKnockoutByesLikely": "With an expected {count} qualifiers, the knockout bracket will very likely include byes.",
"stageValidationNotEnoughQualifiersForGroups": "With only {count} qualifiers for {groups} target groups, empty groups would be created.",
"stageValidationThinGroupsLikely": "With {count} qualifiers across {groups} target groups, the groups will likely be very small.",
"stageFlowPreviewRuleWithCount": "{base} (expected qualifiers: {count})",
"stageFlowReadinessNoGroups": "No matching groups exist yet",
"stageFlowReadinessIncompleteGroups": "{count} group(s) are not fully decided yet",
"stageFlowReadinessReady": "Transition is ready to start",
"stageFlowQualifiedPreviewTitle": "Currently qualified",
"stageFlowQualifiedPreviewEntry": "G{group} · Place {position} · {name}"
}
}

View File

@@ -6,6 +6,7 @@
"common": {
"loading": "Naglo-load...",
"save": "I-save",
"saved": "Na-save",
"cancel": "Kanselahin",
"delete": "Tanggalin",
"edit": "I-edit",
@@ -30,6 +31,8 @@
"approvals": "Mga pag-apruba",
"statistics": "Mga istatistika ng pagsasanay",
"tournaments": "Mga paligsahan",
"clubTournaments": "Mga paligsahan ng club",
"tournamentParticipations": "Mga paglahok sa paligsahan",
"schedule": "Mga iskedyul",
"clubSettings": "Mga setting ng club",
"predefinedActivities": "Mga paunang natukoy na aktibidad",
@@ -120,11 +123,16 @@
"tournaments": {
"numberOfTables": "Bilang ng mesa",
"table": "Mesa",
"playerOne": "Manlalaro 1",
"playerTwo": "Manlalaro 2",
"groupsLabel": "Mga grupo",
"knockoutLabel": "KO",
"distributeTables": "Ipamahagi ang mga bakanteng mesa",
"distributeTablesResult": "Pamamahagi ng mesa",
"noFreeTables": "Walang bakanteng mesa.",
"noAssignableMatches": "Walang laban kung saan pareho ang mga manlalaro ay bakante.",
"tablesDistributed": "Ang mga mesa ay naipamahagi na.",
"errorDistributingTables": "May error sa pamamahagi ng mga mesa.",
"missingDataPDF": "Nawawalang datos bilang PDF",
"missingDataPDFTitle": "Nawawalang datos ng mga kalahok Mini championship",
"missingDataPDFSubtitle": "Pakikolekta ang nawawalang datos (may markang ____) mula sa mga kalahok at itala dito.",
@@ -137,7 +145,189 @@
"phone": "Telepono",
"generatingPDF": "Gumagawa ng PDF...",
"page": "Pahina",
"dataNotRecorded": "Hindi pa naitala"
"statusOpen": "Bukas",
"statusLive": "Live",
"statusFinished": "Tapos",
"dataNotRecorded": "Hindi pa naitala",
"resultsRanking": "Ranggo",
"newSetPlaceholder": "Bagong set, hal. 11:7",
"finishMatch": "Tapusin",
"correctMatch": "Itama",
"markMatchLive": "Markahan bilang live",
"unmarkMatchLive": "Alisin ang live marker",
"stageConfigTitle": "Intermediate at Final Round",
"stageConfigLoading": "Naglo-load ng round configuration…",
"stageConfigIntro": "Opsyonal ang intermediate round. Kapag in-activate mo ito, laging may susunod na final round. Ang knockout final round ay gagawin bilang isang bracket.",
"useIntermediateStage": "Gamitin ang intermediate round",
"intermediateRound": "Intermediate round (round 2)",
"finalRound": "Final round",
"playThirdPlace": "Maglaro para sa ikatlong puwesto",
"roundMode": "Mode",
"roundGroupCount": "Bilang ng mga grupo",
"promotionRule12": "Advance: preliminary round → intermediate round (1→2)",
"promotionRuleFinal": "Advance: round {from} → round {to}",
"addPoolRule": "Magdagdag ng pool rule",
"noPoolRulesYet12": "Wala pang rules. Halimbawa: places 1 at 2 -> top round-2 groups.",
"noPoolRulesYetFinal": "Wala pang rules. Halimbawa: places 1 at 2 -> final round.",
"placesFromEachGroup": "Mga puwesto mula sa bawat grupo (hal. 1,2)",
"target": "Target",
"targetGroupCount": "Target na bilang ng grupo",
"saveRounds": "I-save ang rounds",
"createFinalFromPreliminary": "Gumawa ng final round mula sa preliminary round",
"createIntermediateFromPreliminary": "Gumawa ng intermediate round mula sa preliminary round",
"createFinalFromIntermediate": "Gumawa ng final round mula sa intermediate round",
"stageConfigMissingIds": "Hindi ma-save: kulang ang club o tournament ID.",
"stageConfigLoadError": "Error sa pag-load ng round configuration.",
"stageConfigBadServerResponse": "Hindi wastong tugon mula sa server.",
"stageCreated": "Nagawa ang round {round}.",
"atLeastOnePoolRule": "Mangyaring magdagdag ng kahit isang pool rule para sa {label} (hal. places 1,2).",
"enterMiniLocation": "Mangyaring maglagay ng lokasyon.",
"selectTournamentFirst": "Mangyaring pumili muna ng tournament.",
"errorGeneratingPdf": "Error sa pagbuo ng PDF.",
"orphanedMatchesRemoved": "{count} orphaned matches ang naalis.",
"noOrphanedMatchesFound": "Walang nahanap na orphaned matches.",
"enterClassName": "Mangyaring maglagay ng pangalan ng class.",
"deleteClassTitle": "Burahin ang class",
"deleteClassConfirm": "Sigurado ka bang buburahin ang class na \"{name}\"?",
"deleteClassParticipantsDetached": "Aalisin ang lahat ng participants sa class na ito.",
"enterExternalParticipantName": "Mangyaring ilagay kahit first name at last name.",
"noTournamentDate": "Walang tournament date.",
"noValidTrainingParticipants": "Walang valid na participants na nakita sa training session para sa petsang ito.",
"noTrainingParticipants": "Walang participants na nakita sa training session para sa petsang ito.",
"noTrainingOnDate": "Walang training session para sa {date}.",
"selectTwoDifferentPlayers": "Mangyaring pumili ng dalawang magkaibang player.",
"pairingAlreadyExists": "May ganitong pairing na.",
"participantNotFound": "Hindi nakita ang participant.",
"minimumParticipantsForPairings": "Kailangan ng hindi bababa sa 2 participants para sa pairings.",
"deleteExistingPairingsConfirm": "Buburahin ang mga kasalukuyang pairing. Magpatuloy?",
"pairingsCreatedWithErrors": "{successCount} pairings ang nagawa, {errorCount} errors.",
"participantConflicts": "Conflicts",
"unpairedDoubles": "Doubles without partner",
"noEligibleClass": "No suitable class found.",
"eligibleClassesHint": "Suitable for: {classes}",
"warningClassMissing": "Class not found",
"warningGenderMismatch": "Gender does not match class",
"warningGenderMissing": "Gender is missing for this class",
"warningBirthDateMissing": "Birth date is missing for this class",
"warningTooOldForClass": "Too old for this class",
"warningTooYoungForClass": "Too young for this class",
"warningMissingPairing": "Doubles partner missing",
"autoAssignableParticipantsHint": "{count} of them can be assigned automatically.",
"assignmentReviewTitle": "{count} participants need a choice:",
"conflictSuggestionLabel": "Suggestions:",
"workspaceProblemsTitle": "{count} open issues",
"problemConfigTitle": "Configuration incomplete",
"problemConfigDescription": "Check date, name, winning sets and at least one class.",
"problemUnassignedTitle": "{count} participants without class",
"problemUnassignedDescription": "These participants still need a manual class assignment.",
"problemUnassignedAutoDescription": "{count} of them can be assigned automatically right away.",
"problemConflictsTitle": "{count} participants with conflicts",
"problemConflictsDescription": "Review invalid classes, missing data or unclear assignments.",
"problemDoublesTitle": "{count} open doubles partners",
"problemDoublesDescription": "One or more doubles classes still need partner assignments.",
"problemDoublesAutoDescription": "The open doubles class can be paired automatically right away.",
"problemGroupsMissingTitle": "Groups not created yet",
"problemGroupsMissingDescription": "The group tournament needs groups to be created first.",
"problemGroupMatchesTitle": "Group matches not generated yet",
"problemGroupMatchesDescription": "The groups are ready, but the matches have not been created yet.",
"problemKnockoutReadyTitle": "Knockout can be started",
"problemKnockoutReadyDescription": "The group stage is complete and the final round can be generated now.",
"autoAssignEligible": "Assign automatically",
"autoAssignEligibleNone": "There are currently no participants with a single automatic class assignment.",
"autoAssignEligibleDone": "{count} participants were assigned automatically.",
"autoAssignEligiblePartial": "{successCount} participants were assigned automatically, {errorCount} failed.",
"doublesPairingHint": "{count} participants in this doubles class still need a partner.",
"createSuggestedPairings": "Create pairings",
"statusUnpairedDoubles": "{count} doubles without partner",
"statusActionPair": "Pair",
"stageConfigCurrentSetup": "Current structure",
"stageFlowWithIntermediate": "Preliminary round -> intermediate round ({stage2}) -> final round ({final})",
"stageFlowDirectFinal": "Preliminary round -> final round ({final})",
"stageStep1": "Step 1",
"stageStep2": "Step 2",
"stageStep3": "Step 3",
"stageStep1Title": "Decide on an intermediate round",
"stageStep1Description": "First decide whether there should be an additional round after the preliminary round.",
"stageStep2Description": "Define what the intermediate round should look like and how many groups it needs.",
"stageFinalDescription": "Decide whether the final round should be played as groups or directly as a knockout bracket.",
"promotionRuleDescription12": "Define which places from each preliminary group advance to the intermediate round.",
"promotionRuleDescriptionFinalKnockout": "Define which places from the previous round advance to the knockout bracket.",
"promotionRuleDescriptionFinalGroups": "Define which places from the previous round advance to the final-round groups.",
"poolRuleLabel": "Rule {number}",
"poolRuleSummary": "Places {places} go to {target}",
"poolRulePlacesLabel": "Which places advance?",
"poolRulePlacesHelp": "Example: 1 or 1,2",
"poolRuleTargetLabel": "Where do these places go?",
"poolRuleQuickExamples": "Quick examples:",
"targetGroupsLabel": "{count} groups",
"transferQualifiedToFinalFromPreliminary": "Move qualified players straight into the final round",
"transferQualifiedToIntermediate": "Move qualified players into the intermediate round",
"transferQualifiedToFinalFromIntermediate": "Move qualified players into the final round",
"stageParticipantsTransferred": "Qualified players were moved into {round}.",
"transferQualifiedToFinalFromPreliminaryDesc": "Uses the preliminary-round to final-round rules and creates the qualified players directly there.",
"transferQualifiedToIntermediateDesc": "Uses the preliminary-round to intermediate-round rules and moves the qualified players across.",
"transferQualifiedToFinalFromIntermediateDesc": "Uses the intermediate-round to final-round rules and moves the qualified players across.",
"stageActionsTitle": "Next actions",
"stageActionsDescription": "These steps move qualified players into the next round.",
"stageActionRun": "Run now",
"stageActionMissingRulesHint": "Create at least one advancement rule first before moving players into the next round.",
"poolRulePreview": "From each group, places {places} advance to {target}.",
"poolRuleTargetKnockout": "the knockout bracket",
"poolRuleTargetGroupsDetailed": "{count} target groups",
"stageValidationTitle": "Review configuration",
"stageValidationDescription": "Fix these points before saving or moving players into the next round.",
"stageValidationGroupCountRequired": "Please set at least one valid group count.",
"stageValidationNoRules": "There is no advancement rule yet for {stage}.",
"stageValidationRulePlacesRequired": "Enter at least one place that should advance.",
"stageValidationRulePlacesInvalid": "The place definition is invalid. Only positive whole numbers such as 1 or 1,2 are allowed.",
"stageValidationRulePlacesDuplicate": "A single rule must not contain the same place more than once.",
"stageValidationRulesOverlap": "Place {place} is assigned twice, in rule {first} and rule {second}.",
"stageValidationTargetGroupsRequired": "Please enter a valid number of target groups.",
"stageValidationKnockoutTargetOnly": "For a knockout final round, only the knockout bracket is allowed as the target here.",
"stageValidationSaveBlocked": "Please fix the highlighted configuration errors first.",
"stageValidationSuggestion": "Suggestion: {value}",
"stageValidationRulePlacesGap": "The place definition has gaps. Usually a continuous order such as 1,2 or 1,2,3 is intended.",
"stageValidationTargetGroupCountMismatch": "The number of target groups must match the target round. Expected: {expected}.",
"stageValidationTargetTypeMismatch": "The target of this rule must match the target round. Expected: {target}.",
"stageValidationApplyFix": "Apply",
"stageValidationApplyTargetTypeFix": "Switch to {target}",
"stageValidationApplyTargetGroupFix": "Set to {count} target groups",
"ruleStatusOk": "Looks good",
"ruleStatusReview": "Review",
"ruleStatusBlocked": "Blocked",
"ruleStatusOkDescription": "This rule matches the current target round and can be used as-is.",
"stageFlowHealthOk": "Round logic looks good",
"stageFlowHealthOkDescription": "The transitions between rounds are fully configured and currently coherent.",
"stageFlowHealthIncomplete": "Round logic is incomplete",
"stageFlowHealthMissingRules": "At least one transition does not yet have a complete advancement rule.",
"stageFlowHealthReviewDescription": "The round logic is basically usable, but should still be reviewed because of open hints.",
"stageFlowHealthBlocked": "Round logic is contradictory",
"stageFlowHealthBlockedDescription": "At least one rule currently does not match the target round or contains blocking configuration errors.",
"stageFlowBreakdownOk": "Configuration looks good",
"stageFlowBreakdownWarnings": "{count} warning(s) open",
"stageFlowBreakdownErrors": "{count} blocking error(s)",
"stageFlowBreakdownMissingRule": "No complete rule has been added yet",
"stageFlowBreakdownActionAddRule": "Add rule",
"stageFlowBreakdownActionReview": "Review issues",
"stageFlowBreakdownActionOpen": "Open rule",
"stageValidationApplyAllFixes": "Apply {count} quick fix(es)",
"stageFlowRecommendationTitle": "Recommended next step",
"stageFlowRecommendationFixes": "The typical configuration problems can be cleaned up automatically right away.",
"stageFlowRecommendationMissingRule": "A complete rule is still missing for {label}. That is the best place to continue.",
"stageFlowRecommendationError": "Review the first blocking error in the round logic first.",
"stageFlowRecommendationWarnings": "There are still warnings that should be reviewed before saving.",
"stageFlowRecommendationSave": "The round logic is coherent. You can save the configuration now.",
"stageFlowPreviewMissing": "No rule has been configured yet.",
"stageFlowPreviewRule": "Places {places} go to {target}",
"stageValidationKnockoutNeedsTwoPlayers": "A knockout bracket needs at least 2 qualified players.",
"stageValidationKnockoutByesLikely": "With an expected {count} qualifiers, the knockout bracket will very likely include byes.",
"stageValidationNotEnoughQualifiersForGroups": "With only {count} qualifiers for {groups} target groups, empty groups would be created.",
"stageValidationThinGroupsLikely": "With {count} qualifiers across {groups} target groups, the groups will likely be very small.",
"stageFlowPreviewRuleWithCount": "{base} (expected qualifiers: {count})",
"stageFlowReadinessNoGroups": "No matching groups exist yet",
"stageFlowReadinessIncompleteGroups": "{count} group(s) are not fully decided yet",
"stageFlowReadinessReady": "Transition is ready to start",
"stageFlowQualifiedPreviewTitle": "Currently qualified",
"stageFlowQualifiedPreviewEntry": "G{group} · Place {position} · {name}"
}
}

View File

@@ -6,6 +6,7 @@
"common": {
"loading": "加载中...",
"save": "保存",
"saved": "已保存",
"cancel": "取消",
"delete": "删除",
"edit": "编辑",
@@ -30,6 +31,8 @@
"approvals": "审批",
"statistics": "训练统计",
"tournaments": "锦标赛",
"clubTournaments": "俱乐部锦标赛",
"tournamentParticipations": "锦标赛参赛记录",
"schedule": "赛程",
"clubSettings": "俱乐部设置",
"predefinedActivities": "预定义活动",
@@ -120,11 +123,16 @@
"tournaments": {
"numberOfTables": "球台数量",
"table": "球台",
"playerOne": "选手 1",
"playerTwo": "选手 2",
"groupsLabel": "分组",
"knockoutLabel": "KO",
"distributeTables": "分配空闲球台",
"distributeTablesResult": "球台分配",
"noFreeTables": "没有可用的空闲球台。",
"noAssignableMatches": "没有两位选手都空闲的比赛。",
"tablesDistributed": "球台已分配。",
"errorDistributingTables": "分配球台时出错。",
"missingDataPDF": "缺失数据导出为PDF",
"missingDataPDFTitle": "参赛者缺失数据 迷你锦标赛",
"missingDataPDFSubtitle": "请向参赛者收集缺失数据标记为____并在此记录。",
@@ -137,7 +145,189 @@
"phone": "电话",
"generatingPDF": "正在生成PDF...",
"page": "页",
"dataNotRecorded": "尚未录入"
"statusOpen": "未开始",
"statusLive": "进行中",
"statusFinished": "已完成",
"dataNotRecorded": "尚未录入",
"resultsRanking": "排名",
"newSetPlaceholder": "新一局,例如 11:7",
"finishMatch": "结束比赛",
"correctMatch": "更正",
"markMatchLive": "标记为进行中",
"unmarkMatchLive": "移除进行中标记",
"stageConfigTitle": "中间轮与决赛轮",
"stageConfigLoading": "正在加载轮次配置…",
"stageConfigIntro": "中间轮是可选的。如果启用,之后总会有一个决赛轮。淘汰赛决赛轮会生成为单一对阵表。",
"useIntermediateStage": "使用中间轮",
"intermediateRound": "中间轮第2轮",
"finalRound": "决赛轮",
"playThirdPlace": "进行三四名决赛",
"roundMode": "模式",
"roundGroupCount": "小组数量",
"promotionRule12": "晋级:预赛 → 中间轮 (1→2)",
"promotionRuleFinal": "晋级:第 {from} 轮 → 第 {to} 轮",
"addPoolRule": "添加分组规则",
"noPoolRulesYet12": "还没有规则。示例第1和第2名 -> 第2轮上层小组。",
"noPoolRulesYetFinal": "还没有规则。示例第1和第2名 -> 决赛轮。",
"placesFromEachGroup": "每组名次(例如 1,2",
"target": "目标",
"targetGroupCount": "目标小组数量",
"saveRounds": "保存轮次",
"createFinalFromPreliminary": "从预赛创建决赛轮",
"createIntermediateFromPreliminary": "从预赛创建中间轮",
"createFinalFromIntermediate": "从中间轮创建决赛轮",
"stageConfigMissingIds": "无法保存:缺少俱乐部或赛事 ID。",
"stageConfigLoadError": "加载轮次配置时出错。",
"stageConfigBadServerResponse": "服务器响应无效。",
"stageCreated": "第 {round} 轮已创建。",
"atLeastOnePoolRule": "请至少为 {label} 添加一条分组规则(例如名次 1,2。",
"enterMiniLocation": "请输入地点。",
"selectTournamentFirst": "请先选择一个赛事。",
"errorGeneratingPdf": "生成 PDF 时出错。",
"orphanedMatchesRemoved": "已移除 {count} 场孤立比赛。",
"noOrphanedMatchesFound": "未找到孤立比赛。",
"enterClassName": "请输入组别名称。",
"deleteClassTitle": "删除组别",
"deleteClassConfirm": "确定要删除组别“{name}”吗?",
"deleteClassParticipantsDetached": "所有参赛者将从该组别中移除。",
"enterExternalParticipantName": "请至少输入名字和姓氏。",
"noTournamentDate": "没有赛事日期。",
"noValidTrainingParticipants": "在该日期的训练中未找到有效参赛者。",
"noTrainingParticipants": "在该日期的训练中未找到参赛者。",
"noTrainingOnDate": "未找到 {date} 的训练。",
"selectTwoDifferentPlayers": "请选择两位不同的选手。",
"pairingAlreadyExists": "该配对已存在。",
"participantNotFound": "未找到参赛者。",
"minimumParticipantsForPairings": "配对至少需要 2 名参赛者。",
"deleteExistingPairingsConfirm": "现有配对将被删除。是否继续?",
"pairingsCreatedWithErrors": "已创建 {successCount} 个配对,错误 {errorCount} 个。",
"participantConflicts": "冲突",
"unpairedDoubles": "双打未配对",
"noEligibleClass": "未找到合适的组别。",
"eligibleClassesHint": "适合: {classes}",
"warningClassMissing": "未找到组别",
"warningGenderMismatch": "性别与组别不匹配",
"warningGenderMissing": "该组别缺少性别信息",
"warningBirthDateMissing": "该组别缺少出生日期",
"warningTooOldForClass": "对该组别来说年龄过大",
"warningTooYoungForClass": "对该组别来说年龄过小",
"warningMissingPairing": "缺少双打搭档",
"autoAssignableParticipantsHint": "{count} of them can be assigned automatically.",
"assignmentReviewTitle": "{count} participants need a choice:",
"conflictSuggestionLabel": "Suggestions:",
"workspaceProblemsTitle": "{count} open issues",
"problemConfigTitle": "Configuration incomplete",
"problemConfigDescription": "Check date, name, winning sets and at least one class.",
"problemUnassignedTitle": "{count} participants without class",
"problemUnassignedDescription": "These participants still need a manual class assignment.",
"problemUnassignedAutoDescription": "{count} of them can be assigned automatically right away.",
"problemConflictsTitle": "{count} participants with conflicts",
"problemConflictsDescription": "Review invalid classes, missing data or unclear assignments.",
"problemDoublesTitle": "{count} open doubles partners",
"problemDoublesDescription": "One or more doubles classes still need partner assignments.",
"problemDoublesAutoDescription": "The open doubles class can be paired automatically right away.",
"problemGroupsMissingTitle": "Groups not created yet",
"problemGroupsMissingDescription": "The group tournament needs groups to be created first.",
"problemGroupMatchesTitle": "Group matches not generated yet",
"problemGroupMatchesDescription": "The groups are ready, but the matches have not been created yet.",
"problemKnockoutReadyTitle": "Knockout can be started",
"problemKnockoutReadyDescription": "The group stage is complete and the final round can be generated now.",
"autoAssignEligible": "Assign automatically",
"autoAssignEligibleNone": "There are currently no participants with a single automatic class assignment.",
"autoAssignEligibleDone": "{count} participants were assigned automatically.",
"autoAssignEligiblePartial": "{successCount} participants were assigned automatically, {errorCount} failed.",
"doublesPairingHint": "{count} participants in this doubles class still need a partner.",
"createSuggestedPairings": "Create pairings",
"statusUnpairedDoubles": "{count} doubles without partner",
"statusActionPair": "Pair",
"stageConfigCurrentSetup": "Current structure",
"stageFlowWithIntermediate": "Preliminary round -> intermediate round ({stage2}) -> final round ({final})",
"stageFlowDirectFinal": "Preliminary round -> final round ({final})",
"stageStep1": "Step 1",
"stageStep2": "Step 2",
"stageStep3": "Step 3",
"stageStep1Title": "Decide on an intermediate round",
"stageStep1Description": "First decide whether there should be an additional round after the preliminary round.",
"stageStep2Description": "Define what the intermediate round should look like and how many groups it needs.",
"stageFinalDescription": "Decide whether the final round should be played as groups or directly as a knockout bracket.",
"promotionRuleDescription12": "Define which places from each preliminary group advance to the intermediate round.",
"promotionRuleDescriptionFinalKnockout": "Define which places from the previous round advance to the knockout bracket.",
"promotionRuleDescriptionFinalGroups": "Define which places from the previous round advance to the final-round groups.",
"poolRuleLabel": "Rule {number}",
"poolRuleSummary": "Places {places} go to {target}",
"poolRulePlacesLabel": "Which places advance?",
"poolRulePlacesHelp": "Example: 1 or 1,2",
"poolRuleTargetLabel": "Where do these places go?",
"poolRuleQuickExamples": "Quick examples:",
"targetGroupsLabel": "{count} groups",
"transferQualifiedToFinalFromPreliminary": "Move qualified players straight into the final round",
"transferQualifiedToIntermediate": "Move qualified players into the intermediate round",
"transferQualifiedToFinalFromIntermediate": "Move qualified players into the final round",
"stageParticipantsTransferred": "Qualified players were moved into {round}.",
"transferQualifiedToFinalFromPreliminaryDesc": "Uses the preliminary-round to final-round rules and creates the qualified players directly there.",
"transferQualifiedToIntermediateDesc": "Uses the preliminary-round to intermediate-round rules and moves the qualified players across.",
"transferQualifiedToFinalFromIntermediateDesc": "Uses the intermediate-round to final-round rules and moves the qualified players across.",
"stageActionsTitle": "Next actions",
"stageActionsDescription": "These steps move qualified players into the next round.",
"stageActionRun": "Run now",
"stageActionMissingRulesHint": "Create at least one advancement rule first before moving players into the next round.",
"poolRulePreview": "From each group, places {places} advance to {target}.",
"poolRuleTargetKnockout": "the knockout bracket",
"poolRuleTargetGroupsDetailed": "{count} target groups",
"stageValidationTitle": "Review configuration",
"stageValidationDescription": "Fix these points before saving or moving players into the next round.",
"stageValidationGroupCountRequired": "Please set at least one valid group count.",
"stageValidationNoRules": "There is no advancement rule yet for {stage}.",
"stageValidationRulePlacesRequired": "Enter at least one place that should advance.",
"stageValidationRulePlacesInvalid": "The place definition is invalid. Only positive whole numbers such as 1 or 1,2 are allowed.",
"stageValidationRulePlacesDuplicate": "A single rule must not contain the same place more than once.",
"stageValidationRulesOverlap": "Place {place} is assigned twice, in rule {first} and rule {second}.",
"stageValidationTargetGroupsRequired": "Please enter a valid number of target groups.",
"stageValidationKnockoutTargetOnly": "For a knockout final round, only the knockout bracket is allowed as the target here.",
"stageValidationSaveBlocked": "Please fix the highlighted configuration errors first.",
"stageValidationSuggestion": "Suggestion: {value}",
"stageValidationRulePlacesGap": "The place definition has gaps. Usually a continuous order such as 1,2 or 1,2,3 is intended.",
"stageValidationTargetGroupCountMismatch": "The number of target groups must match the target round. Expected: {expected}.",
"stageValidationTargetTypeMismatch": "The target of this rule must match the target round. Expected: {target}.",
"stageValidationApplyFix": "Apply",
"stageValidationApplyTargetTypeFix": "Switch to {target}",
"stageValidationApplyTargetGroupFix": "Set to {count} target groups",
"ruleStatusOk": "Looks good",
"ruleStatusReview": "Review",
"ruleStatusBlocked": "Blocked",
"ruleStatusOkDescription": "This rule matches the current target round and can be used as-is.",
"stageFlowHealthOk": "Round logic looks good",
"stageFlowHealthOkDescription": "The transitions between rounds are fully configured and currently coherent.",
"stageFlowHealthIncomplete": "Round logic is incomplete",
"stageFlowHealthMissingRules": "At least one transition does not yet have a complete advancement rule.",
"stageFlowHealthReviewDescription": "The round logic is basically usable, but should still be reviewed because of open hints.",
"stageFlowHealthBlocked": "Round logic is contradictory",
"stageFlowHealthBlockedDescription": "At least one rule currently does not match the target round or contains blocking configuration errors.",
"stageFlowBreakdownOk": "Configuration looks good",
"stageFlowBreakdownWarnings": "{count} warning(s) open",
"stageFlowBreakdownErrors": "{count} blocking error(s)",
"stageFlowBreakdownMissingRule": "No complete rule has been added yet",
"stageFlowBreakdownActionAddRule": "Add rule",
"stageFlowBreakdownActionReview": "Review issues",
"stageFlowBreakdownActionOpen": "Open rule",
"stageValidationApplyAllFixes": "Apply {count} quick fix(es)",
"stageFlowRecommendationTitle": "Recommended next step",
"stageFlowRecommendationFixes": "The typical configuration problems can be cleaned up automatically right away.",
"stageFlowRecommendationMissingRule": "A complete rule is still missing for {label}. That is the best place to continue.",
"stageFlowRecommendationError": "Review the first blocking error in the round logic first.",
"stageFlowRecommendationWarnings": "There are still warnings that should be reviewed before saving.",
"stageFlowRecommendationSave": "The round logic is coherent. You can save the configuration now.",
"stageFlowPreviewMissing": "No rule has been configured yet.",
"stageFlowPreviewRule": "Places {places} go to {target}",
"stageValidationKnockoutNeedsTwoPlayers": "A knockout bracket needs at least 2 qualified players.",
"stageValidationKnockoutByesLikely": "With an expected {count} qualifiers, the knockout bracket will very likely include byes.",
"stageValidationNotEnoughQualifiersForGroups": "With only {count} qualifiers for {groups} target groups, empty groups would be created.",
"stageValidationThinGroupsLikely": "With {count} qualifiers across {groups} target groups, the groups will likely be very small.",
"stageFlowPreviewRuleWithCount": "{base} (expected qualifiers: {count})",
"stageFlowReadinessNoGroups": "No matching groups exist yet",
"stageFlowReadinessIncompleteGroups": "{count} group(s) are not fully decided yet",
"stageFlowReadinessReady": "Transition is ready to start",
"stageFlowQualifiedPreviewTitle": "Currently qualified",
"stageFlowQualifiedPreviewEntry": "G{group} · Place {position} · {name}"
}
}

View File

@@ -12,6 +12,7 @@ import DiaryView from './views/DiaryView.vue';
import PendingApprovalsView from './views/PendingApprovalsView.vue';
import ScheduleView from './views/ScheduleView.vue';
import TournamentsView from './views/TournamentsView.vue';
import OfficialTournaments from './views/OfficialTournaments.vue';
import TrainingStatsView from './views/TrainingStatsView.vue';
import ClubSettings from './views/ClubSettings.vue';
import PredefinedActivities from './views/PredefinedActivities.vue';
@@ -40,6 +41,7 @@ const routes = [
{ path: '/pending-approvals', component: PendingApprovalsView},
{ path: '/schedule', component: ScheduleView},
{ path: '/tournaments', component: TournamentsView },
{ path: '/tournament-participations', component: OfficialTournaments },
{ path: '/training-stats', component: TrainingStatsView },
{ path: '/club-settings', component: ClubSettings },
{ path: '/predefined-activities', component: PredefinedActivities },

View File

@@ -13,123 +13,135 @@
</div>
<div class="admin-panel">
<div class="tabs">
<button :class="['tab', topActiveTab==='events' ? 'active' : '']" @click="switchTopTab('events')" :title="$t('officialTournaments.showEvents')">{{ $t('officialTournaments.events') }}</button>
<button :class="['tab', topActiveTab==='participations' ? 'active' : '']" @click="switchTopTab('participations')" :title="$t('officialTournaments.showParticipations')">{{ $t('officialTournaments.participations') }}</button>
<div class="panel-header">
<h3>{{ $t('officialTournaments.savedEvents') }}</h3>
<p>Turnier auswählen, umbenennen oder löschen.</p>
</div>
<div v-if="topActiveTab==='events'">
<div class="panel-header">
<h3>{{ $t('officialTournaments.savedEvents') }}</h3>
<p>Turnier auswählen, umbenennen oder löschen.</p>
</div>
<ul v-if="list && list.length > 0" class="event-list">
<li v-for="t in list" :key="t.id" class="event-item" :class="{ selected: String(uploadedId) === String(t.id) }">
<template v-if="editingTournamentId === t.id">
<input
ref="titleEditInput"
v-model="editingTitle"
class="title-input"
@blur="saveTournamentTitle(t)"
@keydown.enter="saveTournamentTitle(t)"
@keydown.esc="cancelTitleEdit"
/>
</template>
<a
v-else
href="#"
class="event-title"
@click.prevent="uploadedId = String(t.id); reload();"
<div class="panel-toolbar">
<input
v-model.trim="tournamentSearch"
type="text"
class="title-input"
placeholder="Turnier suchen"
/>
<label class="toolbar-checkbox">
<input v-model="showOlderTournaments" type="checkbox" />
Vergangene anzeigen
</label>
<span class="toolbar-meta">{{ filteredTournamentList.length }} / {{ list.length || 0 }}</span>
</div>
<ul v-if="filteredTournamentList.length > 0" class="event-list">
<li
v-for="t in filteredTournamentList"
:key="t.id"
class="event-item"
:class="{
selected: String(uploadedId) === String(t.id),
'is-past': tournamentListStatus(t).className === 'is-past'
}">
<template v-if="editingTournamentId === t.id">
<input
ref="titleEditInput"
v-model="editingTitle"
class="title-input"
@blur="saveTournamentTitle(t)"
@keydown.enter="saveTournamentTitle(t)"
@keydown.esc="cancelTitleEdit"
/>
</template>
<a
v-else
href="#"
class="event-title"
@click.prevent="uploadedId = String(t.id); reload();"
>
{{ t.title || ($t('officialTournaments.tournament') + ' #' + t.id) }}
</a>
<span v-if="editingTournamentId !== t.id && (t.termin || t.eventDate)" class="event-date"> {{ t.termin || t.eventDate }}</span>
<button
v-if="editingTournamentId !== t.id"
class="btn-icon"
:title="$t('officialTournaments.editTitle')"
@click.prevent="startTitleEdit(t)"
>
</button>
<button class="btn-secondary" @click.prevent="removeTournament(t)" :title="$t('officialTournaments.delete')">🗑</button>
</li>
</ul>
<div v-else class="empty-state compact">
<p>{{ $t('officialTournaments.noEvents') }}</p>
</div>
</div>
<div v-else-if="topActiveTab==='participations'">
<div class="panel-header">
<h3>{{ $t('officialTournaments.participations') }}</h3>
<p>Vergangene Turnierteilnahmen des Vereins im Überblick.</p>
</div>
<div class="filters">
<label for="participationRange">{{ $t('officialTournaments.timeRange') }}</label>
<select id="participationRange" v-model="participationRange" @change="onParticipationRangeChange">
<option value="3m">{{ $t('officialTournaments.last3Months') }}</option>
<option value="6m">{{ $t('officialTournaments.last6Months') }}</option>
<option value="12m">{{ $t('officialTournaments.last12Months') }}</option>
<option value="2y">{{ $t('officialTournaments.last2Years') }}</option>
<option value="prev">{{ $t('officialTournaments.previousSeason') }}</option>
<option value="all">{{ $t('officialTournaments.all') }}</option>
</select>
</div>
<table>
<thead>
<tr>
<th>{{ $t('officialTournaments.member') }}</th>
<th>{{ $t('officialTournaments.tournament') }}</th>
<th>{{ $t('officialTournaments.competition') }}</th>
<th>{{ $t('officialTournaments.date') }}</th>
<th>{{ $t('officialTournaments.placement') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="row in clubParticipationRows()" :key="row.key">
<td>{{ row.memberName }}</td>
<td>{{ row.tournamentName }}</td>
<td>{{ row.competitionName }}</td>
<td>{{ row.date }}</td>
<td>{{ row.placement || '' }}</td>
</tr>
</tbody>
</table>
<span v-if="editingTournamentId !== t.id && (t.termin || t.eventDate)" class="event-date"> {{ t.termin || t.eventDate }}</span>
<span v-if="editingTournamentId !== t.id" :class="['list-status-badge', tournamentListStatus(t).className]">
{{ tournamentListStatus(t).label }}
</span>
<button
v-if="editingTournamentId !== t.id"
class="btn-icon"
:title="$t('officialTournaments.editTitle')"
@click.prevent="startTitleEdit(t)"
>
</button>
<button class="btn-secondary" @click.prevent="removeTournament(t)" :title="$t('officialTournaments.delete')">🗑</button>
</li>
</ul>
<div v-else class="empty-state compact">
<p>{{ tournamentSearch ? 'Keine passenden Turniere gefunden.' : $t('officialTournaments.noEvents') }}</p>
</div>
</div>
</div>
<div v-if="parsed" class="selected-tournament-strip">
<div class="selected-tournament-main">
<span class="selected-tournament-label">Aktives Turnier</span>
<strong>{{ parsed.parsedData.title || '' }}</strong>
<div class="admin-panel history-panel">
<div class="history-toggle">
<div class="panel-header compact">
<h3>{{ $t('officialTournaments.participations') }}</h3>
<p>{{ historySummaryText }}</p>
</div>
<button class="btn-secondary" @click="showHistory = !showHistory">
{{ showHistory ? 'Ausblenden' : 'Einblenden' }}
</button>
</div>
<div class="selected-tournament-meta">
<span>{{ parsed.parsedData.termin || '' }}</span>
<span v-if="tournamentLocationLabel">{{ tournamentLocationLabel }}</span>
<span>{{ registrationDeadlineInfo.label }}</span>
<div v-if="showHistory" class="filters">
<label for="participationRange">{{ $t('officialTournaments.timeRange') }}</label>
<select id="participationRange" v-model="participationRange" @change="onParticipationRangeChange">
<option value="3m">{{ $t('officialTournaments.last3Months') }}</option>
<option value="6m">{{ $t('officialTournaments.last6Months') }}</option>
<option value="12m">{{ $t('officialTournaments.last12Months') }}</option>
<option value="2y">{{ $t('officialTournaments.last2Years') }}</option>
<option value="prev">{{ $t('officialTournaments.previousSeason') }}</option>
<option value="all">{{ $t('officialTournaments.all') }}</option>
</select>
</div>
<table v-if="showHistory">
<thead>
<tr>
<th>{{ $t('officialTournaments.member') }}</th>
<th>{{ $t('officialTournaments.tournament') }}</th>
<th>{{ $t('officialTournaments.competition') }}</th>
<th>{{ $t('officialTournaments.date') }}</th>
<th>{{ $t('officialTournaments.placement') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="row in clubParticipationRows()" :key="row.key">
<td>{{ row.memberName }}</td>
<td>{{ row.tournamentName }}</td>
<td>{{ row.competitionName }}</td>
<td>{{ row.date }}</td>
<td>{{ row.placement || '' }}</td>
</tr>
</tbody>
</table>
</div>
<div v-if="parsed && parsed.parsedData.competitions && parsed.parsedData.competitions.length">
<div v-if="parsed && parsed.parsedData && parsed.parsedData.competitions && parsed.parsedData.competitions.length" class="workspace-detail">
<div class="selected-tournament-strip">
<div class="selected-tournament-main">
<span class="selected-tournament-label">Aktives Turnier</span>
<strong>{{ parsed.parsedData.title || '' }}</strong>
</div>
<div class="selected-tournament-meta">
<span>{{ parsed.parsedData.termin || '' }}</span>
<span v-if="tournamentLocationLabel">{{ tournamentLocationLabel }}</span>
<span>{{ registrationDeadlineInfo.label }}</span>
</div>
</div>
<div class="overview-header">
<div class="overview-main">
<div class="tournament-headline">
<h2 class="tournament-title">{{ parsed.parsedData.title || 'Turnier' }}</h2>
<div class="tournament-meta">
<span v-if="parsed.parsedData.termin">{{ parsed.parsedData.termin }}</span>
<span v-if="tournamentLocationLabel">{{ tournamentLocationLabel }}</span>
</div>
<div class="workflow-badges">
<span :class="['workflow-badge', registrationDeadlineInfo.statusClass]">
{{ registrationDeadlineInfo.label }}
</span>
<span
v-for="notice in workflowNotices"
:key="notice.label"
:class="['workflow-badge', notice.statusClass]">
{{ notice.label }}
</span>
</div>
<div class="workflow-badges">
<span
v-for="notice in workflowNotices"
:key="notice.label"
:class="['workflow-badge', notice.statusClass]">
{{ notice.label }}
</span>
</div>
<div class="overview-stats">
<div class="stat-card">
@@ -154,7 +166,7 @@
<button
v-if="primaryHeaderAction === 'auto-register'"
class="btn-primary"
:disabled="!uploadedId || !pendingAutoRegistrationCount || autoRegistering"
:disabled="!canAutoRegister || autoRegistering"
@click="autoRegisterParticipants">
{{ autoRegistering ? 'Anmeldung läuft...' : 'Automatisch anmelden' }}
</button>
@@ -187,7 +199,7 @@
<button
v-if="primaryHeaderAction !== 'auto-register'"
class="btn-secondary"
:disabled="!uploadedId || !pendingAutoRegistrationCount || autoRegistering"
:disabled="!canAutoRegister || autoRegistering"
@click="autoRegisterParticipants">
{{ autoRegistering ? 'Anmeldung läuft...' : 'Automatisch anmelden' }}
</button>
@@ -458,6 +470,12 @@
</table>
</div>
</div>
<div v-else class="workspace-detail empty-workspace">
<div class="empty-workspace-card">
<h3>Kein aktives Turnier ausgewählt</h3>
<p>Wähle links ein gespeichertes Turnier aus oder importiere zuerst eine Ausschreibung als PDF.</p>
</div>
</div>
<!-- Member Selection Dialog -->
<MemberSelectionDialog
@@ -549,10 +567,12 @@ export default {
collator: new Intl.Collator('de', { sensitivity: 'base' }),
activeTab: 'overview',
participantsFilter: 'all',
topActiveTab: 'events',
showHistory: false,
loadingClubParticipations: false,
clubParticipationRowsData: [],
participationRange: 'all',
tournamentSearch: '',
showOlderTournaments: false,
editingTournamentId: null,
editingTitle: '',
autoRegistering: false,
@@ -576,6 +596,51 @@ export default {
if (!m) return [];
return this.competitionsForMember(m);
},
filteredTournamentList() {
const search = String(this.tournamentSearch || '').trim().toLowerCase();
const searchDate = this.parseDateFlexible(search);
const today = new Date();
today.setHours(0, 0, 0, 0);
const cutoff = new Date(today);
cutoff.setDate(cutoff.getDate() - 14);
const base = (Array.isArray(this.list) ? this.list.slice() : [])
.filter((tournament) => {
const isSelected = String(this.uploadedId) === String(tournament.id);
const range = this.tournamentDateRange(tournament);
const end = range.end || range.start;
if (!this.showOlderTournaments && end && end.getTime() < cutoff.getTime() && !isSelected) {
return false;
}
if (!search) return true;
if (searchDate) {
if (!range.start) return false;
const endDate = range.end || range.start;
return searchDate.getTime() >= range.start.getTime() && searchDate.getTime() <= endDate.getTime();
}
const haystack = [
tournament.title,
tournament.termin,
tournament.eventDate,
].filter(Boolean).join(' ').toLowerCase();
return haystack.includes(search);
});
return base.sort((a, b) => {
const rangeA = this.tournamentDateRange(a);
const rangeB = this.tournamentDateRange(b);
const timeA = (rangeA.start || rangeA.end)?.getTime?.() ?? 0;
const timeB = (rangeB.start || rangeB.end)?.getTime?.() ?? 0;
return timeB - timeA;
});
},
historySummaryText() {
const rows = this.clubParticipationRows();
if (this.loadingClubParticipations) return 'Turnierbeteiligungen werden geladen';
if (!rows.length) return 'Keine Turnierbeteiligungen im gewählten Zeitraum';
const uniqueMembers = new Set(rows.map((row) => row.memberName)).size;
return `${rows.length} Einträge von ${uniqueMembers} Mitgliedern im Zeitraum ${this.participationRange}`;
},
resultsRows() {
const comps = (this.parsed?.parsedData?.competitions) || [];
const compById = Object.fromEntries(comps.map(c => [String(c.id), c]));
@@ -804,7 +869,7 @@ export default {
const placeLine = places.find((entry) => /^PLZ\s*\/\s*Ort:/i.test(entry || ''));
return placeLine ? placeLine.replace(/^PLZ\s*\/\s*Ort:/i, '').trim() : '';
},
registrationDeadlineInfo() {
registrationDeadlineAt() {
const deadlines = [
...(this.parsed?.parsedData?.meldeschluesse || []),
...(((this.parsed?.parsedData?.competitions) || []).flatMap((competition) => [
@@ -816,26 +881,49 @@ export default {
].filter(Boolean);
let earliest = null;
let earliestText = '';
for (const entry of deadlines) {
const parsed = this.parseDateTimeFlexible(entry);
if (!parsed) continue;
if (!earliest || parsed.getTime() < earliest.getTime()) {
earliest = parsed;
}
}
return earliest;
},
registrationDeadlineInfo() {
let earliestText = '';
const deadlines = [
...(this.parsed?.parsedData?.meldeschluesse || []),
...(((this.parsed?.parsedData?.competitions) || []).flatMap((competition) => [
competition.registrationDeadlineDate,
competition.registrationDeadlineOnline,
competition.meldeschlussDatum,
competition.meldeschlussOnline,
])),
].filter(Boolean);
for (const entry of deadlines) {
const parsed = this.parseDateTimeFlexible(entry);
if (parsed && this.registrationDeadlineAt && parsed.getTime() === this.registrationDeadlineAt.getTime()) {
earliestText = String(entry);
break;
}
}
if (!earliest) {
if (!this.registrationDeadlineAt) {
return { label: 'Meldeschluss unbekannt', statusClass: 'is-neutral' };
}
if (earliest.getTime() < Date.now()) {
if (this.registrationDeadlineAt.getTime() < Date.now()) {
return { label: `Meldeschluss abgelaufen: ${earliestText}`, statusClass: 'is-danger' };
}
return { label: `Meldeschluss offen: ${earliestText}`, statusClass: 'is-success' };
},
canAutoRegister() {
if (!this.uploadedId || !this.pendingAutoRegistrationCount) return false;
if (!this.registrationDeadlineAt) return true;
return this.registrationDeadlineAt.getTime() >= Date.now();
},
workflowNotices() {
const notices = [];
if (this.pendingAutoRegistrationCount > 0) {
@@ -896,7 +984,6 @@ export default {
this.confirmDialog.isOpen = false;
},
switchTopTab(tab) { this.topActiveTab = tab; if (tab === 'participations') this.loadClubParticipations(); },
compareMembers(a, b) {
const fnA = String(a.firstName || '');
const fnB = String(b.firstName || '');
@@ -906,6 +993,34 @@ export default {
if (byFirst !== 0) return byFirst;
return this.collator.compare(lnA, lnB);
},
tournamentDateRange(tournament) {
const raw = String(tournament?.termin || tournament?.eventDate || '').trim();
if (!raw) return { start: null, end: null };
const matches = raw.match(/(\d{1,2}\.\d{1,2}\.\d{4}|\d{4}-\d{2}-\d{2})/g) || [];
const dates = matches
.map((entry) => this.parseDateFlexible(entry))
.filter(Boolean)
.sort((a, b) => a.getTime() - b.getTime());
if (!dates.length) return { start: null, end: null };
return {
start: dates[0],
end: dates[dates.length - 1],
};
},
tournamentListStatus(tournament) {
if (String(this.uploadedId) === String(tournament.id)) {
return { label: 'Aktiv', className: 'is-active' };
}
const range = this.tournamentDateRange(tournament);
const date = range.end || range.start;
if (!date) return { label: 'Ohne Datum', className: 'is-neutral' };
const today = new Date();
today.setHours(0, 0, 0, 0);
if (date.getTime() < today.getTime()) {
return { label: 'Vergangen', className: 'is-past' };
}
return { label: 'Kommend', className: 'is-upcoming' };
},
competitionStatusSummary(c) {
const summary = { interested: 0, registered: 0, participated: 0 };
for (const member of this.eligibleMembers(c)) {
@@ -1218,7 +1333,7 @@ export default {
}
},
async autoRegisterParticipants() {
if (!this.uploadedId || !this.pendingAutoRegistrationCount || this.autoRegistering) return;
if (!this.canAutoRegister || this.autoRegistering) return;
this.autoRegistering = true;
try {
const response = await apiClient.post(
@@ -1775,6 +1890,7 @@ export default {
},
async mounted() {
await this.loadList();
await this.loadClubParticipations();
}
};
</script>
@@ -1783,26 +1899,42 @@ export default {
.official-tournaments { display: flex; flex-direction: column; gap: 0.75rem; }
.workspace-admin { display: grid; grid-template-columns: minmax(280px, 360px) minmax(0, 1fr); gap: 1rem; align-items: start; }
.admin-panel { background: #fff; border: 1px solid #dbe3f0; border-radius: 12px; padding: .9rem 1rem; box-shadow: 0 4px 18px rgba(15, 35, 95, 0.05); }
.history-panel { margin-top: .25rem; }
.history-toggle { display: flex; justify-content: space-between; gap: 1rem; align-items: center; margin-bottom: .35rem; }
.panel-header { display: flex; flex-direction: column; gap: .2rem; margin-bottom: .75rem; }
.panel-header.compact { margin-bottom: 0; }
.panel-header h3 { margin: 0; color: #17366d; font-size: 1rem; }
.panel-header p { margin: 0; color: #64748b; font-size: .9rem; }
.panel-toolbar { display: flex; align-items: center; gap: .6rem; margin-bottom: .75rem; }
.toolbar-checkbox { display: inline-flex; align-items: center; gap: .35rem; color: #556070; font-size: .9rem; white-space: nowrap; }
.toolbar-meta { color: #64748b; font-size: .88rem; white-space: nowrap; }
.event-list { list-style: none; padding: 0; margin: 0; }
.event-item { display: flex; align-items: center; gap: 0.5rem; padding: .45rem .5rem; border-radius: 8px; }
.event-item { display: flex; align-items: center; gap: 0.4rem; padding: .3rem .4rem; border-radius: 8px; }
.event-item.selected { background: #eef4ff; border: 1px solid #d1defd; }
.event-item.is-past { color: #8c96a5; }
.event-item.is-past .event-title,
.event-item.is-past .event-date { color: #8c96a5; }
.event-title { flex: 1; }
.event-date { flex-shrink: 0; }
.list-status-badge { display: inline-flex; align-items: center; padding: .16rem .45rem; border-radius: 999px; font-size: .78rem; font-weight: 600; border: 1px solid transparent; white-space: nowrap; }
.list-status-badge.is-active { background: #e8f1ff; color: #2453a6; border-color: #bfd2ff; }
.list-status-badge.is-upcoming { background: #e6f6ea; color: #1e6b34; border-color: #b9e2c4; }
.list-status-badge.is-past { background: #f2f4f7; color: #556070; border-color: #d8dee8; }
.list-status-badge.is-neutral { background: #fff4db; color: #8a5a00; border-color: #f2d08a; }
.title-input { flex: 1; min-width: 0; padding: 0.25rem 0.4rem; font-size: inherit; }
.btn-icon { background: none; border: none; cursor: pointer; padding: 0.25rem; opacity: 0.7; }
.btn-icon:hover { opacity: 1; }
.workspace-detail { background: #fff; border: 1px solid #dbe3f0; border-radius: 14px; padding: 1rem; box-shadow: 0 4px 18px rgba(15, 35, 95, 0.05); }
.selected-tournament-strip { display: flex; justify-content: space-between; gap: 1rem; align-items: center; flex-wrap: wrap; background: linear-gradient(180deg, #f9fbff 0%, #eef4ff 100%); border: 1px solid #dbe6ff; border-radius: 12px; padding: .8rem 1rem; }
.selected-tournament-main { display: flex; flex-direction: column; gap: .15rem; }
.selected-tournament-label { font-size: .78rem; text-transform: uppercase; letter-spacing: .04em; color: #5f6b85; }
.selected-tournament-meta { display: flex; flex-wrap: wrap; gap: .5rem 1rem; color: #49566c; font-size: .92rem; }
.empty-workspace { display: flex; align-items: center; justify-content: center; min-height: 220px; }
.empty-workspace-card { max-width: 540px; text-align: center; color: #516074; }
.empty-workspace-card h3 { margin: 0 0 .45rem; color: #17366d; }
.empty-workspace-card p { margin: 0; }
.overview-header { display: flex; justify-content: space-between; gap: 1rem; align-items: flex-start; flex-wrap: wrap; }
.overview-main { display: flex; flex-direction: column; gap: .9rem; flex: 1; min-width: min(100%, 520px); }
.tournament-headline { display: flex; flex-direction: column; gap: .35rem; }
.tournament-title { margin: 0; font-size: 1.35rem; line-height: 1.2; color: #17366d; }
.tournament-meta { display: flex; flex-wrap: wrap; gap: .5rem 1rem; color: #5f6b85; font-size: .95rem; }
.workflow-badges { display: flex; flex-wrap: wrap; gap: .45rem; }
.workflow-badge { display: inline-flex; align-items: center; padding: .28rem .55rem; border-radius: 999px; font-size: .82rem; font-weight: 600; border: 1px solid transparent; }
.workflow-badge.is-success { background: #e6f6ea; color: #1e6b34; border-color: #b9e2c4; }
@@ -2024,13 +2156,18 @@ th, td { border-bottom: 1px solid var(--border-color); padding: 0.5rem; text-ali
grid-template-columns: 1fr;
}
.overview-stats {
grid-template-columns: repeat(2, minmax(120px, 1fr));
.history-toggle {
flex-direction: column;
align-items: stretch;
}
.tournament-meta {
.panel-toolbar {
flex-direction: column;
gap: .2rem;
align-items: stretch;
}
.overview-stats {
grid-template-columns: repeat(2, minmax(120px, 1fr));
}
.competition-detail-grid {

File diff suppressed because it is too large Load Diff

View File

@@ -1,69 +1,68 @@
<template>
<div class="tournaments-container">
<!-- Tab Navigation -->
<div class="tab-navigation">
<button
:class="['tab-button', { active: activeTab === 'internal' }]"
@click="switchTab('internal')"
>
🏆 {{ $t('tournaments.internalTournaments') }}
</button>
<button
:class="['tab-button', { active: activeTab === 'external' }]"
@click="switchTab('external')"
>
🌐 {{ $t('tournaments.openTournaments') }}
</button>
<button
:class="['tab-button', { active: activeTab === 'mini' }]"
@click="switchTab('mini')"
>
🏅 {{ $t('tournaments.miniChampionships') }}
</button>
<button
:class="['tab-button', { active: activeTab === 'official' }]"
@click="switchTab('official')"
>
📄 {{ $t('tournaments.tournamentParticipations') }}
</button>
<div class="workspace-header">
<div>
<h2 class="workspace-title">{{ $t('navigation.clubTournaments') }}</h2>
</div>
<div class="mode-switcher">
<button
:class="['mode-button', { active: activeMode === 'internal' }]"
@click="switchMode('internal')"
>
🏆 {{ $t('tournaments.internalTournaments') }}
</button>
<button
:class="['mode-button', { active: activeMode === 'external' }]"
@click="switchMode('external')"
>
🌐 {{ $t('tournaments.openTournaments') }}
</button>
<button
:class="['mode-button', { active: activeMode === 'mini' }]"
@click="switchMode('mini')"
>
🏅 {{ $t('tournaments.miniChampionships') }}
</button>
</div>
</div>
<div class="workspace-summary">
{{ currentModeDescription }}
</div>
<!-- Tab Content -->
<div class="tab-content">
<div v-if="activeTab === 'internal'">
<TournamentTab :allowsExternal="false" />
</div>
<div v-else-if="activeTab === 'external'">
<TournamentTab :allowsExternal="true" />
</div>
<div v-else-if="activeTab === 'mini'">
<TournamentTab :allowsExternal="true" :isMiniChampionship="true" />
</div>
<div v-else-if="activeTab === 'official'">
<OfficialTournaments />
</div>
<TournamentTab
:key="activeMode"
:allowsExternal="activeMode !== 'internal'"
:isMiniChampionship="activeMode === 'mini'"
/>
</div>
</div>
</template>
<script>
import TournamentTab from './TournamentTab.vue';
import OfficialTournaments from './OfficialTournaments.vue';
export default {
name: 'TournamentsView',
components: {
TournamentTab,
OfficialTournaments,
},
data() {
return {
activeTab: 'internal',
activeMode: 'internal',
};
},
computed: {
currentModeDescription() {
if (this.activeMode === 'mini') return this.$t('tournaments.miniChampionships');
if (this.activeMode === 'external') return this.$t('tournaments.openTournaments');
return this.$t('tournaments.internalTournaments');
},
},
methods: {
switchTab(tab) {
this.activeTab = tab;
switchMode(mode) {
this.activeMode = mode;
},
},
};
@@ -74,38 +73,55 @@ export default {
padding: 20px;
}
.tab-navigation {
.workspace-header {
display: flex;
gap: 0;
border-bottom: 2px solid #e0e0e0;
margin-bottom: 20px;
align-items: flex-end;
justify-content: space-between;
gap: 16px;
margin-bottom: 10px;
}
.tab-button {
background: none;
border: none;
padding: 12px 24px;
font-size: 16px;
font-weight: 500;
color: #666;
.workspace-title {
margin: 0;
font-size: 1.6rem;
color: #1f2937;
}
.mode-switcher {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.mode-button {
background: #f3f4f6;
border: 1px solid #d1d5db;
border-radius: 999px;
padding: 10px 16px;
font-size: 0.95rem;
font-weight: 600;
color: #4b5563;
cursor: pointer;
border-bottom: 3px solid transparent;
transition: all 0.3s ease;
margin-bottom: -2px;
transition: all 0.2s ease;
}
.tab-button:hover {
color: #333;
background-color: #f8f9fa;
.mode-button:hover {
background: #e5e7eb;
color: #1f2937;
}
.tab-button.active {
color: #28a745;
border-bottom-color: #28a745;
.mode-button.active {
background: #dcfce7;
border-color: #16a34a;
color: #166534;
}
.workspace-summary {
margin-bottom: 20px;
color: #6b7280;
}
.tab-content {
margin-top: 20px;
min-height: 200px;
position: relative;
display: block !important;
@@ -117,6 +133,13 @@ export default {
width: 100%;
display: block;
}
@media (max-width: 900px) {
.workspace-header {
align-items: flex-start;
flex-direction: column;
}
}
</style>
<style>
@@ -137,4 +160,3 @@ export default {
height: auto !important;
}
</style>